1 //===-- CommandObjectPlatform.cpp -------------------------------*- C++ -*-===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include <mutex>
10 #include "CommandObjectPlatform.h"
11 #include "lldb/Core/Debugger.h"
12 #include "lldb/Core/Module.h"
13 #include "lldb/Core/PluginManager.h"
14 #include "lldb/Host/OptionParser.h"
15 #include "lldb/Host/StringConvert.h"
16 #include "lldb/Interpreter/CommandInterpreter.h"
17 #include "lldb/Interpreter/CommandOptionValidators.h"
18 #include "lldb/Interpreter/CommandReturnObject.h"
19 #include "lldb/Interpreter/OptionGroupFile.h"
20 #include "lldb/Interpreter/OptionGroupPlatform.h"
21 #include "lldb/Target/ExecutionContext.h"
22 #include "lldb/Target/Platform.h"
23 #include "lldb/Target/Process.h"
24 #include "lldb/Utility/Args.h"
25 #include "lldb/Utility/DataExtractor.h"
26 
27 #include "llvm/ADT/SmallString.h"
28 #include "llvm/Support/Threading.h"
29 
30 using namespace lldb;
31 using namespace lldb_private;
32 
33 static mode_t ParsePermissionString(const char *) = delete;
34 
35 static mode_t ParsePermissionString(llvm::StringRef permissions) {
36   if (permissions.size() != 9)
37     return (mode_t)(-1);
38   bool user_r, user_w, user_x, group_r, group_w, group_x, world_r, world_w,
39       world_x;
40 
41   user_r = (permissions[0] == 'r');
42   user_w = (permissions[1] == 'w');
43   user_x = (permissions[2] == 'x');
44 
45   group_r = (permissions[3] == 'r');
46   group_w = (permissions[4] == 'w');
47   group_x = (permissions[5] == 'x');
48 
49   world_r = (permissions[6] == 'r');
50   world_w = (permissions[7] == 'w');
51   world_x = (permissions[8] == 'x');
52 
53   mode_t user, group, world;
54   user = (user_r ? 4 : 0) | (user_w ? 2 : 0) | (user_x ? 1 : 0);
55   group = (group_r ? 4 : 0) | (group_w ? 2 : 0) | (group_x ? 1 : 0);
56   world = (world_r ? 4 : 0) | (world_w ? 2 : 0) | (world_x ? 1 : 0);
57 
58   return user | group | world;
59 }
60 
61 static constexpr OptionDefinition g_permissions_options[] = {
62     // clang-format off
63   {LLDB_OPT_SET_ALL, false, "permissions-value",   'v', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypePermissionsNumber, "Give out the numeric value for permissions (e.g. 757)"},
64   {LLDB_OPT_SET_ALL, false, "permissions-string",  's', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypePermissionsString, "Give out the string value for permissions (e.g. rwxr-xr--)."},
65   {LLDB_OPT_SET_ALL, false, "user-read",           'r', OptionParser::eNoArgument,       nullptr, {}, 0, eArgTypeNone,              "Allow user to read."},
66   {LLDB_OPT_SET_ALL, false, "user-write",          'w', OptionParser::eNoArgument,       nullptr, {}, 0, eArgTypeNone,              "Allow user to write."},
67   {LLDB_OPT_SET_ALL, false, "user-exec",           'x', OptionParser::eNoArgument,       nullptr, {}, 0, eArgTypeNone,              "Allow user to execute."},
68   {LLDB_OPT_SET_ALL, false, "group-read",          'R', OptionParser::eNoArgument,       nullptr, {}, 0, eArgTypeNone,              "Allow group to read."},
69   {LLDB_OPT_SET_ALL, false, "group-write",         'W', OptionParser::eNoArgument,       nullptr, {}, 0, eArgTypeNone,              "Allow group to write."},
70   {LLDB_OPT_SET_ALL, false, "group-exec",          'X', OptionParser::eNoArgument,       nullptr, {}, 0, eArgTypeNone,              "Allow group to execute."},
71   {LLDB_OPT_SET_ALL, false, "world-read",          'd', OptionParser::eNoArgument,       nullptr, {}, 0, eArgTypeNone,              "Allow world to read."},
72   {LLDB_OPT_SET_ALL, false, "world-write",         't', OptionParser::eNoArgument,       nullptr, {}, 0, eArgTypeNone,              "Allow world to write."},
73   {LLDB_OPT_SET_ALL, false, "world-exec",          'e', OptionParser::eNoArgument,       nullptr, {}, 0, eArgTypeNone,              "Allow world to execute."},
74     // clang-format on
75 };
76 
77 class OptionPermissions : public OptionGroup {
78 public:
79   OptionPermissions() {}
80 
81   ~OptionPermissions() override = default;
82 
83   lldb_private::Status
84   SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
85                  ExecutionContext *execution_context) override {
86     Status error;
87     char short_option = (char)GetDefinitions()[option_idx].short_option;
88     switch (short_option) {
89     case 'v': {
90       if (option_arg.getAsInteger(8, m_permissions)) {
91         m_permissions = 0777;
92         error.SetErrorStringWithFormat("invalid value for permissions: %s",
93                                        option_arg.str().c_str());
94       }
95 
96     } break;
97     case 's': {
98       mode_t perms = ParsePermissionString(option_arg);
99       if (perms == (mode_t)-1)
100         error.SetErrorStringWithFormat("invalid value for permissions: %s",
101                                        option_arg.str().c_str());
102       else
103         m_permissions = perms;
104     } break;
105     case 'r':
106       m_permissions |= lldb::eFilePermissionsUserRead;
107       break;
108     case 'w':
109       m_permissions |= lldb::eFilePermissionsUserWrite;
110       break;
111     case 'x':
112       m_permissions |= lldb::eFilePermissionsUserExecute;
113       break;
114     case 'R':
115       m_permissions |= lldb::eFilePermissionsGroupRead;
116       break;
117     case 'W':
118       m_permissions |= lldb::eFilePermissionsGroupWrite;
119       break;
120     case 'X':
121       m_permissions |= lldb::eFilePermissionsGroupExecute;
122       break;
123     case 'd':
124       m_permissions |= lldb::eFilePermissionsWorldRead;
125       break;
126     case 't':
127       m_permissions |= lldb::eFilePermissionsWorldWrite;
128       break;
129     case 'e':
130       m_permissions |= lldb::eFilePermissionsWorldExecute;
131       break;
132     default:
133       error.SetErrorStringWithFormat("unrecognized option '%c'", short_option);
134       break;
135     }
136 
137     return error;
138   }
139 
140   void OptionParsingStarting(ExecutionContext *execution_context) override {
141     m_permissions = 0;
142   }
143 
144   llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
145     return llvm::makeArrayRef(g_permissions_options);
146   }
147 
148   // Instance variables to hold the values for command options.
149 
150   uint32_t m_permissions;
151 
152 private:
153   DISALLOW_COPY_AND_ASSIGN(OptionPermissions);
154 };
155 
156 //----------------------------------------------------------------------
157 // "platform select <platform-name>"
158 //----------------------------------------------------------------------
159 class CommandObjectPlatformSelect : public CommandObjectParsed {
160 public:
161   CommandObjectPlatformSelect(CommandInterpreter &interpreter)
162       : CommandObjectParsed(interpreter, "platform select",
163                             "Create a platform if needed and select it as the "
164                             "current platform.",
165                             "platform select <platform-name>", 0),
166         m_option_group(),
167         m_platform_options(
168             false) // Don't include the "--platform" option by passing false
169   {
170     m_option_group.Append(&m_platform_options, LLDB_OPT_SET_ALL, 1);
171     m_option_group.Finalize();
172   }
173 
174   ~CommandObjectPlatformSelect() override = default;
175 
176   int HandleCompletion(CompletionRequest &request) override {
177     CommandCompletions::PlatformPluginNames(GetCommandInterpreter(), request,
178                                             nullptr);
179     return request.GetNumberOfMatches();
180   }
181 
182   Options *GetOptions() override { return &m_option_group; }
183 
184 protected:
185   bool DoExecute(Args &args, CommandReturnObject &result) override {
186     if (args.GetArgumentCount() == 1) {
187       const char *platform_name = args.GetArgumentAtIndex(0);
188       if (platform_name && platform_name[0]) {
189         const bool select = true;
190         m_platform_options.SetPlatformName(platform_name);
191         Status error;
192         ArchSpec platform_arch;
193         PlatformSP platform_sp(m_platform_options.CreatePlatformWithOptions(
194             m_interpreter, ArchSpec(), select, error, platform_arch));
195         if (platform_sp) {
196           m_interpreter.GetDebugger().GetPlatformList().SetSelectedPlatform(
197               platform_sp);
198 
199           platform_sp->GetStatus(result.GetOutputStream());
200           result.SetStatus(eReturnStatusSuccessFinishResult);
201         } else {
202           result.AppendError(error.AsCString());
203           result.SetStatus(eReturnStatusFailed);
204         }
205       } else {
206         result.AppendError("invalid platform name");
207         result.SetStatus(eReturnStatusFailed);
208       }
209     } else {
210       result.AppendError(
211           "platform create takes a platform name as an argument\n");
212       result.SetStatus(eReturnStatusFailed);
213     }
214     return result.Succeeded();
215   }
216 
217   OptionGroupOptions m_option_group;
218   OptionGroupPlatform m_platform_options;
219 };
220 
221 //----------------------------------------------------------------------
222 // "platform list"
223 //----------------------------------------------------------------------
224 class CommandObjectPlatformList : public CommandObjectParsed {
225 public:
226   CommandObjectPlatformList(CommandInterpreter &interpreter)
227       : CommandObjectParsed(interpreter, "platform list",
228                             "List all platforms that are available.", nullptr,
229                             0) {}
230 
231   ~CommandObjectPlatformList() override = default;
232 
233 protected:
234   bool DoExecute(Args &args, CommandReturnObject &result) override {
235     Stream &ostrm = result.GetOutputStream();
236     ostrm.Printf("Available platforms:\n");
237 
238     PlatformSP host_platform_sp(Platform::GetHostPlatform());
239     ostrm.Printf("%s: %s\n", host_platform_sp->GetPluginName().GetCString(),
240                  host_platform_sp->GetDescription());
241 
242     uint32_t idx;
243     for (idx = 0; 1; ++idx) {
244       const char *plugin_name =
245           PluginManager::GetPlatformPluginNameAtIndex(idx);
246       if (plugin_name == nullptr)
247         break;
248       const char *plugin_desc =
249           PluginManager::GetPlatformPluginDescriptionAtIndex(idx);
250       if (plugin_desc == nullptr)
251         break;
252       ostrm.Printf("%s: %s\n", plugin_name, plugin_desc);
253     }
254 
255     if (idx == 0) {
256       result.AppendError("no platforms are available\n");
257       result.SetStatus(eReturnStatusFailed);
258     } else
259       result.SetStatus(eReturnStatusSuccessFinishResult);
260     return result.Succeeded();
261   }
262 };
263 
264 //----------------------------------------------------------------------
265 // "platform status"
266 //----------------------------------------------------------------------
267 class CommandObjectPlatformStatus : public CommandObjectParsed {
268 public:
269   CommandObjectPlatformStatus(CommandInterpreter &interpreter)
270       : CommandObjectParsed(interpreter, "platform status",
271                             "Display status for the current platform.", nullptr,
272                             0) {}
273 
274   ~CommandObjectPlatformStatus() override = default;
275 
276 protected:
277   bool DoExecute(Args &args, CommandReturnObject &result) override {
278     Stream &ostrm = result.GetOutputStream();
279 
280     Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get();
281     PlatformSP platform_sp;
282     if (target) {
283       platform_sp = target->GetPlatform();
284     }
285     if (!platform_sp) {
286       platform_sp =
287           m_interpreter.GetDebugger().GetPlatformList().GetSelectedPlatform();
288     }
289     if (platform_sp) {
290       platform_sp->GetStatus(ostrm);
291       result.SetStatus(eReturnStatusSuccessFinishResult);
292     } else {
293       result.AppendError("no platform is currently selected\n");
294       result.SetStatus(eReturnStatusFailed);
295     }
296     return result.Succeeded();
297   }
298 };
299 
300 //----------------------------------------------------------------------
301 // "platform connect <connect-url>"
302 //----------------------------------------------------------------------
303 class CommandObjectPlatformConnect : public CommandObjectParsed {
304 public:
305   CommandObjectPlatformConnect(CommandInterpreter &interpreter)
306       : CommandObjectParsed(
307             interpreter, "platform connect",
308             "Select the current platform by providing a connection URL.",
309             "platform connect <connect-url>", 0) {}
310 
311   ~CommandObjectPlatformConnect() override = default;
312 
313 protected:
314   bool DoExecute(Args &args, CommandReturnObject &result) override {
315     Stream &ostrm = result.GetOutputStream();
316 
317     PlatformSP platform_sp(
318         m_interpreter.GetDebugger().GetPlatformList().GetSelectedPlatform());
319     if (platform_sp) {
320       Status error(platform_sp->ConnectRemote(args));
321       if (error.Success()) {
322         platform_sp->GetStatus(ostrm);
323         result.SetStatus(eReturnStatusSuccessFinishResult);
324 
325         platform_sp->ConnectToWaitingProcesses(m_interpreter.GetDebugger(),
326                                                error);
327         if (error.Fail()) {
328           result.AppendError(error.AsCString());
329           result.SetStatus(eReturnStatusFailed);
330         }
331       } else {
332         result.AppendErrorWithFormat("%s\n", error.AsCString());
333         result.SetStatus(eReturnStatusFailed);
334       }
335     } else {
336       result.AppendError("no platform is currently selected\n");
337       result.SetStatus(eReturnStatusFailed);
338     }
339     return result.Succeeded();
340   }
341 
342   Options *GetOptions() override {
343     PlatformSP platform_sp(
344         m_interpreter.GetDebugger().GetPlatformList().GetSelectedPlatform());
345     OptionGroupOptions *m_platform_options = nullptr;
346     if (platform_sp) {
347       m_platform_options = platform_sp->GetConnectionOptions(m_interpreter);
348       if (m_platform_options != nullptr && !m_platform_options->m_did_finalize)
349         m_platform_options->Finalize();
350     }
351     return m_platform_options;
352   }
353 };
354 
355 //----------------------------------------------------------------------
356 // "platform disconnect"
357 //----------------------------------------------------------------------
358 class CommandObjectPlatformDisconnect : public CommandObjectParsed {
359 public:
360   CommandObjectPlatformDisconnect(CommandInterpreter &interpreter)
361       : CommandObjectParsed(interpreter, "platform disconnect",
362                             "Disconnect from the current platform.",
363                             "platform disconnect", 0) {}
364 
365   ~CommandObjectPlatformDisconnect() override = default;
366 
367 protected:
368   bool DoExecute(Args &args, CommandReturnObject &result) override {
369     PlatformSP platform_sp(
370         m_interpreter.GetDebugger().GetPlatformList().GetSelectedPlatform());
371     if (platform_sp) {
372       if (args.GetArgumentCount() == 0) {
373         Status error;
374 
375         if (platform_sp->IsConnected()) {
376           // Cache the instance name if there is one since we are about to
377           // disconnect and the name might go with it.
378           const char *hostname_cstr = platform_sp->GetHostname();
379           std::string hostname;
380           if (hostname_cstr)
381             hostname.assign(hostname_cstr);
382 
383           error = platform_sp->DisconnectRemote();
384           if (error.Success()) {
385             Stream &ostrm = result.GetOutputStream();
386             if (hostname.empty())
387               ostrm.Printf("Disconnected from \"%s\"\n",
388                            platform_sp->GetPluginName().GetCString());
389             else
390               ostrm.Printf("Disconnected from \"%s\"\n", hostname.c_str());
391             result.SetStatus(eReturnStatusSuccessFinishResult);
392           } else {
393             result.AppendErrorWithFormat("%s", error.AsCString());
394             result.SetStatus(eReturnStatusFailed);
395           }
396         } else {
397           // Not connected...
398           result.AppendErrorWithFormat(
399               "not connected to '%s'",
400               platform_sp->GetPluginName().GetCString());
401           result.SetStatus(eReturnStatusFailed);
402         }
403       } else {
404         // Bad args
405         result.AppendError(
406             "\"platform disconnect\" doesn't take any arguments");
407         result.SetStatus(eReturnStatusFailed);
408       }
409     } else {
410       result.AppendError("no platform is currently selected");
411       result.SetStatus(eReturnStatusFailed);
412     }
413     return result.Succeeded();
414   }
415 };
416 
417 //----------------------------------------------------------------------
418 // "platform settings"
419 //----------------------------------------------------------------------
420 class CommandObjectPlatformSettings : public CommandObjectParsed {
421 public:
422   CommandObjectPlatformSettings(CommandInterpreter &interpreter)
423       : CommandObjectParsed(interpreter, "platform settings",
424                             "Set settings for the current target's platform, "
425                             "or for a platform by name.",
426                             "platform settings", 0),
427         m_options(),
428         m_option_working_dir(LLDB_OPT_SET_1, false, "working-dir", 'w', 0,
429                              eArgTypePath,
430                              "The working directory for the platform.") {
431     m_options.Append(&m_option_working_dir, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1);
432   }
433 
434   ~CommandObjectPlatformSettings() override = default;
435 
436 protected:
437   bool DoExecute(Args &args, CommandReturnObject &result) override {
438     PlatformSP platform_sp(
439         m_interpreter.GetDebugger().GetPlatformList().GetSelectedPlatform());
440     if (platform_sp) {
441       if (m_option_working_dir.GetOptionValue().OptionWasSet())
442         platform_sp->SetWorkingDirectory(
443             m_option_working_dir.GetOptionValue().GetCurrentValue());
444     } else {
445       result.AppendError("no platform is currently selected");
446       result.SetStatus(eReturnStatusFailed);
447     }
448     return result.Succeeded();
449   }
450 
451   Options *GetOptions() override {
452     if (!m_options.DidFinalize())
453       m_options.Finalize();
454     return &m_options;
455   }
456 
457 protected:
458   OptionGroupOptions m_options;
459   OptionGroupFile m_option_working_dir;
460 };
461 
462 //----------------------------------------------------------------------
463 // "platform mkdir"
464 //----------------------------------------------------------------------
465 class CommandObjectPlatformMkDir : public CommandObjectParsed {
466 public:
467   CommandObjectPlatformMkDir(CommandInterpreter &interpreter)
468       : CommandObjectParsed(interpreter, "platform mkdir",
469                             "Make a new directory on the remote end.", nullptr,
470                             0),
471         m_options() {}
472 
473   ~CommandObjectPlatformMkDir() override = default;
474 
475   bool DoExecute(Args &args, CommandReturnObject &result) override {
476     PlatformSP platform_sp(
477         m_interpreter.GetDebugger().GetPlatformList().GetSelectedPlatform());
478     if (platform_sp) {
479       std::string cmd_line;
480       args.GetCommandString(cmd_line);
481       uint32_t mode;
482       const OptionPermissions *options_permissions =
483           (const OptionPermissions *)m_options.GetGroupWithOption('r');
484       if (options_permissions)
485         mode = options_permissions->m_permissions;
486       else
487         mode = lldb::eFilePermissionsUserRWX | lldb::eFilePermissionsGroupRWX |
488                lldb::eFilePermissionsWorldRX;
489       Status error = platform_sp->MakeDirectory(FileSpec(cmd_line), mode);
490       if (error.Success()) {
491         result.SetStatus(eReturnStatusSuccessFinishResult);
492       } else {
493         result.AppendError(error.AsCString());
494         result.SetStatus(eReturnStatusFailed);
495       }
496     } else {
497       result.AppendError("no platform currently selected\n");
498       result.SetStatus(eReturnStatusFailed);
499     }
500     return result.Succeeded();
501   }
502 
503   Options *GetOptions() override {
504     if (!m_options.DidFinalize()) {
505       m_options.Append(new OptionPermissions());
506       m_options.Finalize();
507     }
508     return &m_options;
509   }
510 
511   OptionGroupOptions m_options;
512 };
513 
514 //----------------------------------------------------------------------
515 // "platform fopen"
516 //----------------------------------------------------------------------
517 class CommandObjectPlatformFOpen : public CommandObjectParsed {
518 public:
519   CommandObjectPlatformFOpen(CommandInterpreter &interpreter)
520       : CommandObjectParsed(interpreter, "platform file open",
521                             "Open a file on the remote end.", nullptr, 0),
522         m_options() {}
523 
524   ~CommandObjectPlatformFOpen() override = default;
525 
526   bool DoExecute(Args &args, CommandReturnObject &result) override {
527     PlatformSP platform_sp(
528         m_interpreter.GetDebugger().GetPlatformList().GetSelectedPlatform());
529     if (platform_sp) {
530       Status error;
531       std::string cmd_line;
532       args.GetCommandString(cmd_line);
533       mode_t perms;
534       const OptionPermissions *options_permissions =
535           (const OptionPermissions *)m_options.GetGroupWithOption('r');
536       if (options_permissions)
537         perms = options_permissions->m_permissions;
538       else
539         perms = lldb::eFilePermissionsUserRW | lldb::eFilePermissionsGroupRW |
540                 lldb::eFilePermissionsWorldRead;
541       lldb::user_id_t fd = platform_sp->OpenFile(
542           FileSpec(cmd_line),
543           File::eOpenOptionRead | File::eOpenOptionWrite |
544               File::eOpenOptionAppend | File::eOpenOptionCanCreate,
545           perms, error);
546       if (error.Success()) {
547         result.AppendMessageWithFormat("File Descriptor = %" PRIu64 "\n", fd);
548         result.SetStatus(eReturnStatusSuccessFinishResult);
549       } else {
550         result.AppendError(error.AsCString());
551         result.SetStatus(eReturnStatusFailed);
552       }
553     } else {
554       result.AppendError("no platform currently selected\n");
555       result.SetStatus(eReturnStatusFailed);
556     }
557     return result.Succeeded();
558   }
559 
560   Options *GetOptions() override {
561     if (!m_options.DidFinalize()) {
562       m_options.Append(new OptionPermissions());
563       m_options.Finalize();
564     }
565     return &m_options;
566   }
567 
568   OptionGroupOptions m_options;
569 };
570 
571 //----------------------------------------------------------------------
572 // "platform fclose"
573 //----------------------------------------------------------------------
574 class CommandObjectPlatformFClose : public CommandObjectParsed {
575 public:
576   CommandObjectPlatformFClose(CommandInterpreter &interpreter)
577       : CommandObjectParsed(interpreter, "platform file close",
578                             "Close a file on the remote end.", nullptr, 0) {}
579 
580   ~CommandObjectPlatformFClose() override = default;
581 
582   bool DoExecute(Args &args, CommandReturnObject &result) override {
583     PlatformSP platform_sp(
584         m_interpreter.GetDebugger().GetPlatformList().GetSelectedPlatform());
585     if (platform_sp) {
586       std::string cmd_line;
587       args.GetCommandString(cmd_line);
588       const lldb::user_id_t fd =
589           StringConvert::ToUInt64(cmd_line.c_str(), UINT64_MAX);
590       Status error;
591       bool success = platform_sp->CloseFile(fd, error);
592       if (success) {
593         result.AppendMessageWithFormat("file %" PRIu64 " closed.\n", fd);
594         result.SetStatus(eReturnStatusSuccessFinishResult);
595       } else {
596         result.AppendError(error.AsCString());
597         result.SetStatus(eReturnStatusFailed);
598       }
599     } else {
600       result.AppendError("no platform currently selected\n");
601       result.SetStatus(eReturnStatusFailed);
602     }
603     return result.Succeeded();
604   }
605 };
606 
607 //----------------------------------------------------------------------
608 // "platform fread"
609 //----------------------------------------------------------------------
610 
611 static constexpr OptionDefinition g_platform_fread_options[] = {
612     // clang-format off
613   { LLDB_OPT_SET_1, false, "offset", 'o', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeIndex, "Offset into the file at which to start reading." },
614   { LLDB_OPT_SET_1, false, "count",  'c', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeCount, "Number of bytes to read from the file." },
615     // clang-format on
616 };
617 
618 class CommandObjectPlatformFRead : public CommandObjectParsed {
619 public:
620   CommandObjectPlatformFRead(CommandInterpreter &interpreter)
621       : CommandObjectParsed(interpreter, "platform file read",
622                             "Read data from a file on the remote end.", nullptr,
623                             0),
624         m_options() {}
625 
626   ~CommandObjectPlatformFRead() override = default;
627 
628   bool DoExecute(Args &args, CommandReturnObject &result) override {
629     PlatformSP platform_sp(
630         m_interpreter.GetDebugger().GetPlatformList().GetSelectedPlatform());
631     if (platform_sp) {
632       std::string cmd_line;
633       args.GetCommandString(cmd_line);
634       const lldb::user_id_t fd =
635           StringConvert::ToUInt64(cmd_line.c_str(), UINT64_MAX);
636       std::string buffer(m_options.m_count, 0);
637       Status error;
638       uint32_t retcode = platform_sp->ReadFile(
639           fd, m_options.m_offset, &buffer[0], m_options.m_count, error);
640       result.AppendMessageWithFormat("Return = %d\n", retcode);
641       result.AppendMessageWithFormat("Data = \"%s\"\n", buffer.c_str());
642       result.SetStatus(eReturnStatusSuccessFinishResult);
643     } else {
644       result.AppendError("no platform currently selected\n");
645       result.SetStatus(eReturnStatusFailed);
646     }
647     return result.Succeeded();
648   }
649 
650   Options *GetOptions() override { return &m_options; }
651 
652 protected:
653   class CommandOptions : public Options {
654   public:
655     CommandOptions() : Options() {}
656 
657     ~CommandOptions() override = default;
658 
659     Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
660                           ExecutionContext *execution_context) override {
661       Status error;
662       char short_option = (char)m_getopt_table[option_idx].val;
663 
664       switch (short_option) {
665       case 'o':
666         if (option_arg.getAsInteger(0, m_offset))
667           error.SetErrorStringWithFormat("invalid offset: '%s'",
668                                          option_arg.str().c_str());
669         break;
670       case 'c':
671         if (option_arg.getAsInteger(0, m_count))
672           error.SetErrorStringWithFormat("invalid offset: '%s'",
673                                          option_arg.str().c_str());
674         break;
675       default:
676         error.SetErrorStringWithFormat("unrecognized option '%c'",
677                                        short_option);
678         break;
679       }
680 
681       return error;
682     }
683 
684     void OptionParsingStarting(ExecutionContext *execution_context) override {
685       m_offset = 0;
686       m_count = 1;
687     }
688 
689     llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
690       return llvm::makeArrayRef(g_platform_fread_options);
691     }
692 
693     // Instance variables to hold the values for command options.
694 
695     uint32_t m_offset;
696     uint32_t m_count;
697   };
698 
699   CommandOptions m_options;
700 };
701 
702 //----------------------------------------------------------------------
703 // "platform fwrite"
704 //----------------------------------------------------------------------
705 
706 static constexpr OptionDefinition g_platform_fwrite_options[] = {
707     // clang-format off
708   { LLDB_OPT_SET_1, false, "offset", 'o', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeIndex, "Offset into the file at which to start reading." },
709   { LLDB_OPT_SET_1, false, "data",   'd', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeValue, "Text to write to the file." },
710     // clang-format on
711 };
712 
713 class CommandObjectPlatformFWrite : public CommandObjectParsed {
714 public:
715   CommandObjectPlatformFWrite(CommandInterpreter &interpreter)
716       : CommandObjectParsed(interpreter, "platform file write",
717                             "Write data to a file on the remote end.", nullptr,
718                             0),
719         m_options() {}
720 
721   ~CommandObjectPlatformFWrite() override = default;
722 
723   bool DoExecute(Args &args, CommandReturnObject &result) override {
724     PlatformSP platform_sp(
725         m_interpreter.GetDebugger().GetPlatformList().GetSelectedPlatform());
726     if (platform_sp) {
727       std::string cmd_line;
728       args.GetCommandString(cmd_line);
729       Status error;
730       const lldb::user_id_t fd =
731           StringConvert::ToUInt64(cmd_line.c_str(), UINT64_MAX);
732       uint32_t retcode =
733           platform_sp->WriteFile(fd, m_options.m_offset, &m_options.m_data[0],
734                                  m_options.m_data.size(), error);
735       result.AppendMessageWithFormat("Return = %d\n", retcode);
736       result.SetStatus(eReturnStatusSuccessFinishResult);
737     } else {
738       result.AppendError("no platform currently selected\n");
739       result.SetStatus(eReturnStatusFailed);
740     }
741     return result.Succeeded();
742   }
743 
744   Options *GetOptions() override { return &m_options; }
745 
746 protected:
747   class CommandOptions : public Options {
748   public:
749     CommandOptions() : Options() {}
750 
751     ~CommandOptions() override = default;
752 
753     Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
754                           ExecutionContext *execution_context) override {
755       Status error;
756       char short_option = (char)m_getopt_table[option_idx].val;
757 
758       switch (short_option) {
759       case 'o':
760         if (option_arg.getAsInteger(0, m_offset))
761           error.SetErrorStringWithFormat("invalid offset: '%s'",
762                                          option_arg.str().c_str());
763         break;
764       case 'd':
765         m_data.assign(option_arg);
766         break;
767       default:
768         error.SetErrorStringWithFormat("unrecognized option '%c'",
769                                        short_option);
770         break;
771       }
772 
773       return error;
774     }
775 
776     void OptionParsingStarting(ExecutionContext *execution_context) override {
777       m_offset = 0;
778       m_data.clear();
779     }
780 
781     llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
782       return llvm::makeArrayRef(g_platform_fwrite_options);
783     }
784 
785     // Instance variables to hold the values for command options.
786 
787     uint32_t m_offset;
788     std::string m_data;
789   };
790 
791   CommandOptions m_options;
792 };
793 
794 class CommandObjectPlatformFile : public CommandObjectMultiword {
795 public:
796   //------------------------------------------------------------------
797   // Constructors and Destructors
798   //------------------------------------------------------------------
799   CommandObjectPlatformFile(CommandInterpreter &interpreter)
800       : CommandObjectMultiword(
801             interpreter, "platform file",
802             "Commands to access files on the current platform.",
803             "platform file [open|close|read|write] ...") {
804     LoadSubCommand(
805         "open", CommandObjectSP(new CommandObjectPlatformFOpen(interpreter)));
806     LoadSubCommand(
807         "close", CommandObjectSP(new CommandObjectPlatformFClose(interpreter)));
808     LoadSubCommand(
809         "read", CommandObjectSP(new CommandObjectPlatformFRead(interpreter)));
810     LoadSubCommand(
811         "write", CommandObjectSP(new CommandObjectPlatformFWrite(interpreter)));
812   }
813 
814   ~CommandObjectPlatformFile() override = default;
815 
816 private:
817   //------------------------------------------------------------------
818   // For CommandObjectPlatform only
819   //------------------------------------------------------------------
820   DISALLOW_COPY_AND_ASSIGN(CommandObjectPlatformFile);
821 };
822 
823 //----------------------------------------------------------------------
824 // "platform get-file remote-file-path host-file-path"
825 //----------------------------------------------------------------------
826 class CommandObjectPlatformGetFile : public CommandObjectParsed {
827 public:
828   CommandObjectPlatformGetFile(CommandInterpreter &interpreter)
829       : CommandObjectParsed(
830             interpreter, "platform get-file",
831             "Transfer a file from the remote end to the local host.",
832             "platform get-file <remote-file-spec> <local-file-spec>", 0) {
833     SetHelpLong(
834         R"(Examples:
835 
836 (lldb) platform get-file /the/remote/file/path /the/local/file/path
837 
838     Transfer a file from the remote end with file path /the/remote/file/path to the local host.)");
839 
840     CommandArgumentEntry arg1, arg2;
841     CommandArgumentData file_arg_remote, file_arg_host;
842 
843     // Define the first (and only) variant of this arg.
844     file_arg_remote.arg_type = eArgTypeFilename;
845     file_arg_remote.arg_repetition = eArgRepeatPlain;
846     // There is only one variant this argument could be; put it into the
847     // argument entry.
848     arg1.push_back(file_arg_remote);
849 
850     // Define the second (and only) variant of this arg.
851     file_arg_host.arg_type = eArgTypeFilename;
852     file_arg_host.arg_repetition = eArgRepeatPlain;
853     // There is only one variant this argument could be; put it into the
854     // argument entry.
855     arg2.push_back(file_arg_host);
856 
857     // Push the data for the first and the second arguments into the
858     // m_arguments vector.
859     m_arguments.push_back(arg1);
860     m_arguments.push_back(arg2);
861   }
862 
863   ~CommandObjectPlatformGetFile() override = default;
864 
865   bool DoExecute(Args &args, CommandReturnObject &result) override {
866     // If the number of arguments is incorrect, issue an error message.
867     if (args.GetArgumentCount() != 2) {
868       result.GetErrorStream().Printf("error: required arguments missing; "
869                                      "specify both the source and destination "
870                                      "file paths\n");
871       result.SetStatus(eReturnStatusFailed);
872       return false;
873     }
874 
875     PlatformSP platform_sp(
876         m_interpreter.GetDebugger().GetPlatformList().GetSelectedPlatform());
877     if (platform_sp) {
878       const char *remote_file_path = args.GetArgumentAtIndex(0);
879       const char *local_file_path = args.GetArgumentAtIndex(1);
880       Status error = platform_sp->GetFile(FileSpec(remote_file_path),
881                                           FileSpec(local_file_path));
882       if (error.Success()) {
883         result.AppendMessageWithFormat(
884             "successfully get-file from %s (remote) to %s (host)\n",
885             remote_file_path, local_file_path);
886         result.SetStatus(eReturnStatusSuccessFinishResult);
887       } else {
888         result.AppendMessageWithFormat("get-file failed: %s\n",
889                                        error.AsCString());
890         result.SetStatus(eReturnStatusFailed);
891       }
892     } else {
893       result.AppendError("no platform currently selected\n");
894       result.SetStatus(eReturnStatusFailed);
895     }
896     return result.Succeeded();
897   }
898 };
899 
900 //----------------------------------------------------------------------
901 // "platform get-size remote-file-path"
902 //----------------------------------------------------------------------
903 class CommandObjectPlatformGetSize : public CommandObjectParsed {
904 public:
905   CommandObjectPlatformGetSize(CommandInterpreter &interpreter)
906       : CommandObjectParsed(interpreter, "platform get-size",
907                             "Get the file size from the remote end.",
908                             "platform get-size <remote-file-spec>", 0) {
909     SetHelpLong(
910         R"(Examples:
911 
912 (lldb) platform get-size /the/remote/file/path
913 
914     Get the file size from the remote end with path /the/remote/file/path.)");
915 
916     CommandArgumentEntry arg1;
917     CommandArgumentData file_arg_remote;
918 
919     // Define the first (and only) variant of this arg.
920     file_arg_remote.arg_type = eArgTypeFilename;
921     file_arg_remote.arg_repetition = eArgRepeatPlain;
922     // There is only one variant this argument could be; put it into the
923     // argument entry.
924     arg1.push_back(file_arg_remote);
925 
926     // Push the data for the first argument into the m_arguments vector.
927     m_arguments.push_back(arg1);
928   }
929 
930   ~CommandObjectPlatformGetSize() override = default;
931 
932   bool DoExecute(Args &args, CommandReturnObject &result) override {
933     // If the number of arguments is incorrect, issue an error message.
934     if (args.GetArgumentCount() != 1) {
935       result.GetErrorStream().Printf("error: required argument missing; "
936                                      "specify the source file path as the only "
937                                      "argument\n");
938       result.SetStatus(eReturnStatusFailed);
939       return false;
940     }
941 
942     PlatformSP platform_sp(
943         m_interpreter.GetDebugger().GetPlatformList().GetSelectedPlatform());
944     if (platform_sp) {
945       std::string remote_file_path(args.GetArgumentAtIndex(0));
946       user_id_t size = platform_sp->GetFileSize(FileSpec(remote_file_path));
947       if (size != UINT64_MAX) {
948         result.AppendMessageWithFormat("File size of %s (remote): %" PRIu64
949                                        "\n",
950                                        remote_file_path.c_str(), size);
951         result.SetStatus(eReturnStatusSuccessFinishResult);
952       } else {
953         result.AppendMessageWithFormat(
954             "Error getting file size of %s (remote)\n",
955             remote_file_path.c_str());
956         result.SetStatus(eReturnStatusFailed);
957       }
958     } else {
959       result.AppendError("no platform currently selected\n");
960       result.SetStatus(eReturnStatusFailed);
961     }
962     return result.Succeeded();
963   }
964 };
965 
966 //----------------------------------------------------------------------
967 // "platform put-file"
968 //----------------------------------------------------------------------
969 class CommandObjectPlatformPutFile : public CommandObjectParsed {
970 public:
971   CommandObjectPlatformPutFile(CommandInterpreter &interpreter)
972       : CommandObjectParsed(
973             interpreter, "platform put-file",
974             "Transfer a file from this system to the remote end.", nullptr, 0) {
975   }
976 
977   ~CommandObjectPlatformPutFile() override = default;
978 
979   bool DoExecute(Args &args, CommandReturnObject &result) override {
980     const char *src = args.GetArgumentAtIndex(0);
981     const char *dst = args.GetArgumentAtIndex(1);
982 
983     FileSpec src_fs(src);
984     FileSystem::Instance().Resolve(src_fs);
985     FileSpec dst_fs(dst ? dst : src_fs.GetFilename().GetCString());
986 
987     PlatformSP platform_sp(
988         m_interpreter.GetDebugger().GetPlatformList().GetSelectedPlatform());
989     if (platform_sp) {
990       Status error(platform_sp->PutFile(src_fs, dst_fs));
991       if (error.Success()) {
992         result.SetStatus(eReturnStatusSuccessFinishNoResult);
993       } else {
994         result.AppendError(error.AsCString());
995         result.SetStatus(eReturnStatusFailed);
996       }
997     } else {
998       result.AppendError("no platform currently selected\n");
999       result.SetStatus(eReturnStatusFailed);
1000     }
1001     return result.Succeeded();
1002   }
1003 };
1004 
1005 //----------------------------------------------------------------------
1006 // "platform process launch"
1007 //----------------------------------------------------------------------
1008 class CommandObjectPlatformProcessLaunch : public CommandObjectParsed {
1009 public:
1010   CommandObjectPlatformProcessLaunch(CommandInterpreter &interpreter)
1011       : CommandObjectParsed(interpreter, "platform process launch",
1012                             "Launch a new process on a remote platform.",
1013                             "platform process launch program",
1014                             eCommandRequiresTarget | eCommandTryTargetAPILock),
1015         m_options() {}
1016 
1017   ~CommandObjectPlatformProcessLaunch() override = default;
1018 
1019   Options *GetOptions() override { return &m_options; }
1020 
1021 protected:
1022   bool DoExecute(Args &args, CommandReturnObject &result) override {
1023     Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get();
1024     PlatformSP platform_sp;
1025     if (target) {
1026       platform_sp = target->GetPlatform();
1027     }
1028     if (!platform_sp) {
1029       platform_sp =
1030           m_interpreter.GetDebugger().GetPlatformList().GetSelectedPlatform();
1031     }
1032 
1033     if (platform_sp) {
1034       Status error;
1035       const size_t argc = args.GetArgumentCount();
1036       Target *target = m_exe_ctx.GetTargetPtr();
1037       Module *exe_module = target->GetExecutableModulePointer();
1038       if (exe_module) {
1039         m_options.launch_info.GetExecutableFile() = exe_module->GetFileSpec();
1040         llvm::SmallString<128> exe_path;
1041         m_options.launch_info.GetExecutableFile().GetPath(exe_path);
1042         if (!exe_path.empty())
1043           m_options.launch_info.GetArguments().AppendArgument(exe_path);
1044         m_options.launch_info.GetArchitecture() = exe_module->GetArchitecture();
1045       }
1046 
1047       if (argc > 0) {
1048         if (m_options.launch_info.GetExecutableFile()) {
1049           // We already have an executable file, so we will use this and all
1050           // arguments to this function are extra arguments
1051           m_options.launch_info.GetArguments().AppendArguments(args);
1052         } else {
1053           // We don't have any file yet, so the first argument is our
1054           // executable, and the rest are program arguments
1055           const bool first_arg_is_executable = true;
1056           m_options.launch_info.SetArguments(args, first_arg_is_executable);
1057         }
1058       }
1059 
1060       if (m_options.launch_info.GetExecutableFile()) {
1061         Debugger &debugger = m_interpreter.GetDebugger();
1062 
1063         if (argc == 0)
1064           target->GetRunArguments(m_options.launch_info.GetArguments());
1065 
1066         ProcessSP process_sp(platform_sp->DebugProcess(
1067             m_options.launch_info, debugger, target, error));
1068         if (process_sp && process_sp->IsAlive()) {
1069           result.SetStatus(eReturnStatusSuccessFinishNoResult);
1070           return true;
1071         }
1072 
1073         if (error.Success())
1074           result.AppendError("process launch failed");
1075         else
1076           result.AppendError(error.AsCString());
1077         result.SetStatus(eReturnStatusFailed);
1078       } else {
1079         result.AppendError("'platform process launch' uses the current target "
1080                            "file and arguments, or the executable and its "
1081                            "arguments can be specified in this command");
1082         result.SetStatus(eReturnStatusFailed);
1083         return false;
1084       }
1085     } else {
1086       result.AppendError("no platform is selected\n");
1087     }
1088     return result.Succeeded();
1089   }
1090 
1091 protected:
1092   ProcessLaunchCommandOptions m_options;
1093 };
1094 
1095 //----------------------------------------------------------------------
1096 // "platform process list"
1097 //----------------------------------------------------------------------
1098 
1099 static OptionDefinition g_platform_process_list_options[] = {
1100     // clang-format off
1101   { LLDB_OPT_SET_1,             false, "pid",         'p', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypePid,               "List the process info for a specific process ID." },
1102   { LLDB_OPT_SET_2,             true,  "name",        'n', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeProcessName,       "Find processes with executable basenames that match a string." },
1103   { LLDB_OPT_SET_3,             true,  "ends-with",   'e', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeProcessName,       "Find processes with executable basenames that end with a string." },
1104   { LLDB_OPT_SET_4,             true,  "starts-with", 's', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeProcessName,       "Find processes with executable basenames that start with a string." },
1105   { LLDB_OPT_SET_5,             true,  "contains",    'c', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeProcessName,       "Find processes with executable basenames that contain a string." },
1106   { LLDB_OPT_SET_6,             true,  "regex",       'r', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeRegularExpression, "Find processes with executable basenames that match a regular expression." },
1107   { LLDB_OPT_SET_FROM_TO(2, 6), false, "parent",      'P', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypePid,               "Find processes that have a matching parent process ID." },
1108   { LLDB_OPT_SET_FROM_TO(2, 6), false, "uid",         'u', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeUnsignedInteger,   "Find processes that have a matching user ID." },
1109   { LLDB_OPT_SET_FROM_TO(2, 6), false, "euid",        'U', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeUnsignedInteger,   "Find processes that have a matching effective user ID." },
1110   { LLDB_OPT_SET_FROM_TO(2, 6), false, "gid",         'g', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeUnsignedInteger,   "Find processes that have a matching group ID." },
1111   { LLDB_OPT_SET_FROM_TO(2, 6), false, "egid",        'G', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeUnsignedInteger,   "Find processes that have a matching effective group ID." },
1112   { LLDB_OPT_SET_FROM_TO(2, 6), false, "arch",        'a', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeArchitecture,      "Find processes that have a matching architecture." },
1113   { LLDB_OPT_SET_FROM_TO(1, 6), false, "show-args",   'A', OptionParser::eNoArgument,       nullptr, {}, 0, eArgTypeNone,              "Show process arguments instead of the process executable basename." },
1114   { LLDB_OPT_SET_FROM_TO(1, 6), false, "verbose",     'v', OptionParser::eNoArgument,       nullptr, {}, 0, eArgTypeNone,              "Enable verbose output." },
1115     // clang-format on
1116 };
1117 
1118 class CommandObjectPlatformProcessList : public CommandObjectParsed {
1119 public:
1120   CommandObjectPlatformProcessList(CommandInterpreter &interpreter)
1121       : CommandObjectParsed(interpreter, "platform process list",
1122                             "List processes on a remote platform by name, pid, "
1123                             "or many other matching attributes.",
1124                             "platform process list", 0),
1125         m_options() {}
1126 
1127   ~CommandObjectPlatformProcessList() override = default;
1128 
1129   Options *GetOptions() override { return &m_options; }
1130 
1131 protected:
1132   bool DoExecute(Args &args, CommandReturnObject &result) override {
1133     Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get();
1134     PlatformSP platform_sp;
1135     if (target) {
1136       platform_sp = target->GetPlatform();
1137     }
1138     if (!platform_sp) {
1139       platform_sp =
1140           m_interpreter.GetDebugger().GetPlatformList().GetSelectedPlatform();
1141     }
1142 
1143     if (platform_sp) {
1144       Status error;
1145       if (args.GetArgumentCount() == 0) {
1146         if (platform_sp) {
1147           Stream &ostrm = result.GetOutputStream();
1148 
1149           lldb::pid_t pid =
1150               m_options.match_info.GetProcessInfo().GetProcessID();
1151           if (pid != LLDB_INVALID_PROCESS_ID) {
1152             ProcessInstanceInfo proc_info;
1153             if (platform_sp->GetProcessInfo(pid, proc_info)) {
1154               ProcessInstanceInfo::DumpTableHeader(ostrm, m_options.show_args,
1155                                                    m_options.verbose);
1156               proc_info.DumpAsTableRow(ostrm, platform_sp->GetUserIDResolver(),
1157                                        m_options.show_args, m_options.verbose);
1158               result.SetStatus(eReturnStatusSuccessFinishResult);
1159             } else {
1160               result.AppendErrorWithFormat(
1161                   "no process found with pid = %" PRIu64 "\n", pid);
1162               result.SetStatus(eReturnStatusFailed);
1163             }
1164           } else {
1165             ProcessInstanceInfoList proc_infos;
1166             const uint32_t matches =
1167                 platform_sp->FindProcesses(m_options.match_info, proc_infos);
1168             const char *match_desc = nullptr;
1169             const char *match_name =
1170                 m_options.match_info.GetProcessInfo().GetName();
1171             if (match_name && match_name[0]) {
1172               switch (m_options.match_info.GetNameMatchType()) {
1173               case NameMatch::Ignore:
1174                 break;
1175               case NameMatch::Equals:
1176                 match_desc = "matched";
1177                 break;
1178               case NameMatch::Contains:
1179                 match_desc = "contained";
1180                 break;
1181               case NameMatch::StartsWith:
1182                 match_desc = "started with";
1183                 break;
1184               case NameMatch::EndsWith:
1185                 match_desc = "ended with";
1186                 break;
1187               case NameMatch::RegularExpression:
1188                 match_desc = "matched the regular expression";
1189                 break;
1190               }
1191             }
1192 
1193             if (matches == 0) {
1194               if (match_desc)
1195                 result.AppendErrorWithFormat(
1196                     "no processes were found that %s \"%s\" on the \"%s\" "
1197                     "platform\n",
1198                     match_desc, match_name,
1199                     platform_sp->GetPluginName().GetCString());
1200               else
1201                 result.AppendErrorWithFormat(
1202                     "no processes were found on the \"%s\" platform\n",
1203                     platform_sp->GetPluginName().GetCString());
1204               result.SetStatus(eReturnStatusFailed);
1205             } else {
1206               result.AppendMessageWithFormat(
1207                   "%u matching process%s found on \"%s\"", matches,
1208                   matches > 1 ? "es were" : " was",
1209                   platform_sp->GetName().GetCString());
1210               if (match_desc)
1211                 result.AppendMessageWithFormat(" whose name %s \"%s\"",
1212                                                match_desc, match_name);
1213               result.AppendMessageWithFormat("\n");
1214               ProcessInstanceInfo::DumpTableHeader(ostrm, m_options.show_args,
1215                                                    m_options.verbose);
1216               for (uint32_t i = 0; i < matches; ++i) {
1217                 proc_infos.GetProcessInfoAtIndex(i).DumpAsTableRow(
1218                     ostrm, platform_sp->GetUserIDResolver(),
1219                     m_options.show_args, m_options.verbose);
1220               }
1221             }
1222           }
1223         }
1224       } else {
1225         result.AppendError("invalid args: process list takes only options\n");
1226         result.SetStatus(eReturnStatusFailed);
1227       }
1228     } else {
1229       result.AppendError("no platform is selected\n");
1230       result.SetStatus(eReturnStatusFailed);
1231     }
1232     return result.Succeeded();
1233   }
1234 
1235   class CommandOptions : public Options {
1236   public:
1237     CommandOptions()
1238         : Options(), match_info(), show_args(false), verbose(false) {
1239       static llvm::once_flag g_once_flag;
1240       llvm::call_once(g_once_flag, []() {
1241         PosixPlatformCommandOptionValidator *posix_validator =
1242             new PosixPlatformCommandOptionValidator();
1243         for (auto &Option : g_platform_process_list_options) {
1244           switch (Option.short_option) {
1245           case 'u':
1246           case 'U':
1247           case 'g':
1248           case 'G':
1249             Option.validator = posix_validator;
1250             break;
1251           default:
1252             break;
1253           }
1254         }
1255       });
1256     }
1257 
1258     ~CommandOptions() override = default;
1259 
1260     Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
1261                           ExecutionContext *execution_context) override {
1262       Status error;
1263       const int short_option = m_getopt_table[option_idx].val;
1264       bool success = false;
1265 
1266       uint32_t id = LLDB_INVALID_PROCESS_ID;
1267       success = !option_arg.getAsInteger(0, id);
1268       switch (short_option) {
1269       case 'p': {
1270         match_info.GetProcessInfo().SetProcessID(id);
1271         if (!success)
1272           error.SetErrorStringWithFormat("invalid process ID string: '%s'",
1273                                          option_arg.str().c_str());
1274         break;
1275       }
1276       case 'P':
1277         match_info.GetProcessInfo().SetParentProcessID(id);
1278         if (!success)
1279           error.SetErrorStringWithFormat(
1280               "invalid parent process ID string: '%s'",
1281               option_arg.str().c_str());
1282         break;
1283 
1284       case 'u':
1285         match_info.GetProcessInfo().SetUserID(success ? id : UINT32_MAX);
1286         if (!success)
1287           error.SetErrorStringWithFormat("invalid user ID string: '%s'",
1288                                          option_arg.str().c_str());
1289         break;
1290 
1291       case 'U':
1292         match_info.GetProcessInfo().SetEffectiveUserID(success ? id
1293                                                                : UINT32_MAX);
1294         if (!success)
1295           error.SetErrorStringWithFormat(
1296               "invalid effective user ID string: '%s'",
1297               option_arg.str().c_str());
1298         break;
1299 
1300       case 'g':
1301         match_info.GetProcessInfo().SetGroupID(success ? id : UINT32_MAX);
1302         if (!success)
1303           error.SetErrorStringWithFormat("invalid group ID string: '%s'",
1304                                          option_arg.str().c_str());
1305         break;
1306 
1307       case 'G':
1308         match_info.GetProcessInfo().SetEffectiveGroupID(success ? id
1309                                                                 : UINT32_MAX);
1310         if (!success)
1311           error.SetErrorStringWithFormat(
1312               "invalid effective group ID string: '%s'",
1313               option_arg.str().c_str());
1314         break;
1315 
1316       case 'a': {
1317         TargetSP target_sp =
1318             execution_context ? execution_context->GetTargetSP() : TargetSP();
1319         DebuggerSP debugger_sp =
1320             target_sp ? target_sp->GetDebugger().shared_from_this()
1321                       : DebuggerSP();
1322         PlatformSP platform_sp =
1323             debugger_sp ? debugger_sp->GetPlatformList().GetSelectedPlatform()
1324                         : PlatformSP();
1325         match_info.GetProcessInfo().GetArchitecture() =
1326             Platform::GetAugmentedArchSpec(platform_sp.get(), option_arg);
1327       } break;
1328 
1329       case 'n':
1330         match_info.GetProcessInfo().GetExecutableFile().SetFile(
1331             option_arg, FileSpec::Style::native);
1332         match_info.SetNameMatchType(NameMatch::Equals);
1333         break;
1334 
1335       case 'e':
1336         match_info.GetProcessInfo().GetExecutableFile().SetFile(
1337             option_arg, FileSpec::Style::native);
1338         match_info.SetNameMatchType(NameMatch::EndsWith);
1339         break;
1340 
1341       case 's':
1342         match_info.GetProcessInfo().GetExecutableFile().SetFile(
1343             option_arg, FileSpec::Style::native);
1344         match_info.SetNameMatchType(NameMatch::StartsWith);
1345         break;
1346 
1347       case 'c':
1348         match_info.GetProcessInfo().GetExecutableFile().SetFile(
1349             option_arg, FileSpec::Style::native);
1350         match_info.SetNameMatchType(NameMatch::Contains);
1351         break;
1352 
1353       case 'r':
1354         match_info.GetProcessInfo().GetExecutableFile().SetFile(
1355             option_arg, FileSpec::Style::native);
1356         match_info.SetNameMatchType(NameMatch::RegularExpression);
1357         break;
1358 
1359       case 'A':
1360         show_args = true;
1361         break;
1362 
1363       case 'v':
1364         verbose = true;
1365         break;
1366 
1367       default:
1368         error.SetErrorStringWithFormat("unrecognized option '%c'",
1369                                        short_option);
1370         break;
1371       }
1372 
1373       return error;
1374     }
1375 
1376     void OptionParsingStarting(ExecutionContext *execution_context) override {
1377       match_info.Clear();
1378       show_args = false;
1379       verbose = false;
1380     }
1381 
1382     llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
1383       return llvm::makeArrayRef(g_platform_process_list_options);
1384     }
1385 
1386     // Instance variables to hold the values for command options.
1387 
1388     ProcessInstanceInfoMatch match_info;
1389     bool show_args;
1390     bool verbose;
1391   };
1392 
1393   CommandOptions m_options;
1394 };
1395 
1396 //----------------------------------------------------------------------
1397 // "platform process info"
1398 //----------------------------------------------------------------------
1399 class CommandObjectPlatformProcessInfo : public CommandObjectParsed {
1400 public:
1401   CommandObjectPlatformProcessInfo(CommandInterpreter &interpreter)
1402       : CommandObjectParsed(
1403             interpreter, "platform process info",
1404             "Get detailed information for one or more process by process ID.",
1405             "platform process info <pid> [<pid> <pid> ...]", 0) {
1406     CommandArgumentEntry arg;
1407     CommandArgumentData pid_args;
1408 
1409     // Define the first (and only) variant of this arg.
1410     pid_args.arg_type = eArgTypePid;
1411     pid_args.arg_repetition = eArgRepeatStar;
1412 
1413     // There is only one variant this argument could be; put it into the
1414     // argument entry.
1415     arg.push_back(pid_args);
1416 
1417     // Push the data for the first argument into the m_arguments vector.
1418     m_arguments.push_back(arg);
1419   }
1420 
1421   ~CommandObjectPlatformProcessInfo() override = default;
1422 
1423 protected:
1424   bool DoExecute(Args &args, CommandReturnObject &result) override {
1425     Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get();
1426     PlatformSP platform_sp;
1427     if (target) {
1428       platform_sp = target->GetPlatform();
1429     }
1430     if (!platform_sp) {
1431       platform_sp =
1432           m_interpreter.GetDebugger().GetPlatformList().GetSelectedPlatform();
1433     }
1434 
1435     if (platform_sp) {
1436       const size_t argc = args.GetArgumentCount();
1437       if (argc > 0) {
1438         Status error;
1439 
1440         if (platform_sp->IsConnected()) {
1441           Stream &ostrm = result.GetOutputStream();
1442           for (auto &entry : args.entries()) {
1443             lldb::pid_t pid;
1444             if (entry.ref.getAsInteger(0, pid)) {
1445               result.AppendErrorWithFormat("invalid process ID argument '%s'",
1446                                            entry.ref.str().c_str());
1447               result.SetStatus(eReturnStatusFailed);
1448               break;
1449             } else {
1450               ProcessInstanceInfo proc_info;
1451               if (platform_sp->GetProcessInfo(pid, proc_info)) {
1452                 ostrm.Printf("Process information for process %" PRIu64 ":\n",
1453                              pid);
1454                 proc_info.Dump(ostrm, platform_sp->GetUserIDResolver());
1455               } else {
1456                 ostrm.Printf("error: no process information is available for "
1457                              "process %" PRIu64 "\n",
1458                              pid);
1459               }
1460               ostrm.EOL();
1461             }
1462           }
1463         } else {
1464           // Not connected...
1465           result.AppendErrorWithFormat(
1466               "not connected to '%s'",
1467               platform_sp->GetPluginName().GetCString());
1468           result.SetStatus(eReturnStatusFailed);
1469         }
1470       } else {
1471         // No args
1472         result.AppendError("one or more process id(s) must be specified");
1473         result.SetStatus(eReturnStatusFailed);
1474       }
1475     } else {
1476       result.AppendError("no platform is currently selected");
1477       result.SetStatus(eReturnStatusFailed);
1478     }
1479     return result.Succeeded();
1480   }
1481 };
1482 
1483 static constexpr OptionDefinition g_platform_process_attach_options[] = {
1484     // clang-format off
1485   { LLDB_OPT_SET_ALL, false, "plugin",  'P', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypePlugin,      "Name of the process plugin you want to use." },
1486   { LLDB_OPT_SET_1,   false, "pid",     'p', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypePid,         "The process ID of an existing process to attach to." },
1487   { LLDB_OPT_SET_2,   false, "name",    'n', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeProcessName, "The name of the process to attach to." },
1488   { LLDB_OPT_SET_2,   false, "waitfor", 'w', OptionParser::eNoArgument,       nullptr, {}, 0, eArgTypeNone,        "Wait for the process with <process-name> to launch." },
1489     // clang-format on
1490 };
1491 
1492 class CommandObjectPlatformProcessAttach : public CommandObjectParsed {
1493 public:
1494   class CommandOptions : public Options {
1495   public:
1496     CommandOptions() : Options() {
1497       // Keep default values of all options in one place: OptionParsingStarting
1498       // ()
1499       OptionParsingStarting(nullptr);
1500     }
1501 
1502     ~CommandOptions() override = default;
1503 
1504     Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
1505                           ExecutionContext *execution_context) override {
1506       Status error;
1507       char short_option = (char)m_getopt_table[option_idx].val;
1508       switch (short_option) {
1509       case 'p': {
1510         lldb::pid_t pid = LLDB_INVALID_PROCESS_ID;
1511         if (option_arg.getAsInteger(0, pid)) {
1512           error.SetErrorStringWithFormat("invalid process ID '%s'",
1513                                          option_arg.str().c_str());
1514         } else {
1515           attach_info.SetProcessID(pid);
1516         }
1517       } break;
1518 
1519       case 'P':
1520         attach_info.SetProcessPluginName(option_arg);
1521         break;
1522 
1523       case 'n':
1524         attach_info.GetExecutableFile().SetFile(option_arg,
1525                                                 FileSpec::Style::native);
1526         break;
1527 
1528       case 'w':
1529         attach_info.SetWaitForLaunch(true);
1530         break;
1531 
1532       default:
1533         error.SetErrorStringWithFormat("invalid short option character '%c'",
1534                                        short_option);
1535         break;
1536       }
1537       return error;
1538     }
1539 
1540     void OptionParsingStarting(ExecutionContext *execution_context) override {
1541       attach_info.Clear();
1542     }
1543 
1544     llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
1545       return llvm::makeArrayRef(g_platform_process_attach_options);
1546     }
1547 
1548     bool HandleOptionArgumentCompletion(
1549         CompletionRequest &request, OptionElementVector &opt_element_vector,
1550         int opt_element_index, CommandInterpreter &interpreter) override {
1551       int opt_arg_pos = opt_element_vector[opt_element_index].opt_arg_pos;
1552       int opt_defs_index = opt_element_vector[opt_element_index].opt_defs_index;
1553 
1554       // We are only completing the name option for now...
1555 
1556       if (GetDefinitions()[opt_defs_index].short_option == 'n') {
1557         // Are we in the name?
1558 
1559         // Look to see if there is a -P argument provided, and if so use that
1560         // plugin, otherwise use the default plugin.
1561 
1562         const char *partial_name = nullptr;
1563         partial_name = request.GetParsedLine().GetArgumentAtIndex(opt_arg_pos);
1564 
1565         PlatformSP platform_sp(interpreter.GetPlatform(true));
1566         if (platform_sp) {
1567           ProcessInstanceInfoList process_infos;
1568           ProcessInstanceInfoMatch match_info;
1569           if (partial_name) {
1570             match_info.GetProcessInfo().GetExecutableFile().SetFile(
1571                 partial_name, FileSpec::Style::native);
1572             match_info.SetNameMatchType(NameMatch::StartsWith);
1573           }
1574           platform_sp->FindProcesses(match_info, process_infos);
1575           const uint32_t num_matches = process_infos.GetSize();
1576           if (num_matches > 0) {
1577             for (uint32_t i = 0; i < num_matches; ++i) {
1578               request.AddCompletion(llvm::StringRef(
1579                   process_infos.GetProcessNameAtIndex(i),
1580                   process_infos.GetProcessNameLengthAtIndex(i)));
1581             }
1582           }
1583         }
1584       }
1585 
1586       return false;
1587     }
1588 
1589     // Options table: Required for subclasses of Options.
1590 
1591     static OptionDefinition g_option_table[];
1592 
1593     // Instance variables to hold the values for command options.
1594 
1595     ProcessAttachInfo attach_info;
1596   };
1597 
1598   CommandObjectPlatformProcessAttach(CommandInterpreter &interpreter)
1599       : CommandObjectParsed(interpreter, "platform process attach",
1600                             "Attach to a process.",
1601                             "platform process attach <cmd-options>"),
1602         m_options() {}
1603 
1604   ~CommandObjectPlatformProcessAttach() override = default;
1605 
1606   bool DoExecute(Args &command, CommandReturnObject &result) override {
1607     PlatformSP platform_sp(
1608         m_interpreter.GetDebugger().GetPlatformList().GetSelectedPlatform());
1609     if (platform_sp) {
1610       Status err;
1611       ProcessSP remote_process_sp = platform_sp->Attach(
1612           m_options.attach_info, m_interpreter.GetDebugger(), nullptr, err);
1613       if (err.Fail()) {
1614         result.AppendError(err.AsCString());
1615         result.SetStatus(eReturnStatusFailed);
1616       } else if (!remote_process_sp) {
1617         result.AppendError("could not attach: unknown reason");
1618         result.SetStatus(eReturnStatusFailed);
1619       } else
1620         result.SetStatus(eReturnStatusSuccessFinishResult);
1621     } else {
1622       result.AppendError("no platform is currently selected");
1623       result.SetStatus(eReturnStatusFailed);
1624     }
1625     return result.Succeeded();
1626   }
1627 
1628   Options *GetOptions() override { return &m_options; }
1629 
1630 protected:
1631   CommandOptions m_options;
1632 };
1633 
1634 class CommandObjectPlatformProcess : public CommandObjectMultiword {
1635 public:
1636   //------------------------------------------------------------------
1637   // Constructors and Destructors
1638   //------------------------------------------------------------------
1639   CommandObjectPlatformProcess(CommandInterpreter &interpreter)
1640       : CommandObjectMultiword(interpreter, "platform process",
1641                                "Commands to query, launch and attach to "
1642                                "processes on the current platform.",
1643                                "platform process [attach|launch|list] ...") {
1644     LoadSubCommand(
1645         "attach",
1646         CommandObjectSP(new CommandObjectPlatformProcessAttach(interpreter)));
1647     LoadSubCommand(
1648         "launch",
1649         CommandObjectSP(new CommandObjectPlatformProcessLaunch(interpreter)));
1650     LoadSubCommand("info", CommandObjectSP(new CommandObjectPlatformProcessInfo(
1651                                interpreter)));
1652     LoadSubCommand("list", CommandObjectSP(new CommandObjectPlatformProcessList(
1653                                interpreter)));
1654   }
1655 
1656   ~CommandObjectPlatformProcess() override = default;
1657 
1658 private:
1659   //------------------------------------------------------------------
1660   // For CommandObjectPlatform only
1661   //------------------------------------------------------------------
1662   DISALLOW_COPY_AND_ASSIGN(CommandObjectPlatformProcess);
1663 };
1664 
1665 //----------------------------------------------------------------------
1666 // "platform shell"
1667 //----------------------------------------------------------------------
1668 static constexpr OptionDefinition g_platform_shell_options[] = {
1669     // clang-format off
1670   { LLDB_OPT_SET_ALL, false, "timeout", 't', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeValue, "Seconds to wait for the remote host to finish running the command." },
1671     // clang-format on
1672 };
1673 
1674 class CommandObjectPlatformShell : public CommandObjectRaw {
1675 public:
1676   class CommandOptions : public Options {
1677   public:
1678     CommandOptions() : Options() {}
1679 
1680     ~CommandOptions() override = default;
1681 
1682     llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
1683       return llvm::makeArrayRef(g_platform_shell_options);
1684     }
1685 
1686     Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
1687                           ExecutionContext *execution_context) override {
1688       Status error;
1689 
1690       const char short_option = (char)GetDefinitions()[option_idx].short_option;
1691 
1692       switch (short_option) {
1693       case 't':
1694         uint32_t timeout_sec;
1695         if (option_arg.getAsInteger(10, timeout_sec))
1696           error.SetErrorStringWithFormat(
1697               "could not convert \"%s\" to a numeric value.",
1698               option_arg.str().c_str());
1699         else
1700           timeout = std::chrono::seconds(timeout_sec);
1701         break;
1702       default:
1703         error.SetErrorStringWithFormat("invalid short option character '%c'",
1704                                        short_option);
1705         break;
1706       }
1707 
1708       return error;
1709     }
1710 
1711     void OptionParsingStarting(ExecutionContext *execution_context) override {}
1712 
1713     Timeout<std::micro> timeout = std::chrono::seconds(10);
1714   };
1715 
1716   CommandObjectPlatformShell(CommandInterpreter &interpreter)
1717       : CommandObjectRaw(interpreter, "platform shell",
1718                          "Run a shell command on the current platform.",
1719                          "platform shell <shell-command>", 0),
1720         m_options() {}
1721 
1722   ~CommandObjectPlatformShell() override = default;
1723 
1724   Options *GetOptions() override { return &m_options; }
1725 
1726   bool DoExecute(llvm::StringRef raw_command_line,
1727                  CommandReturnObject &result) override {
1728     ExecutionContext exe_ctx = GetCommandInterpreter().GetExecutionContext();
1729     m_options.NotifyOptionParsingStarting(&exe_ctx);
1730 
1731 
1732     // Print out an usage syntax on an empty command line.
1733     if (raw_command_line.empty()) {
1734       result.GetOutputStream().Printf("%s\n", this->GetSyntax().str().c_str());
1735       return true;
1736     }
1737 
1738     OptionsWithRaw args(raw_command_line);
1739     const char *expr = args.GetRawPart().c_str();
1740 
1741     if (args.HasArgs())
1742       if (!ParseOptions(args.GetArgs(), result))
1743         return false;
1744 
1745     PlatformSP platform_sp(
1746         m_interpreter.GetDebugger().GetPlatformList().GetSelectedPlatform());
1747     Status error;
1748     if (platform_sp) {
1749       FileSpec working_dir{};
1750       std::string output;
1751       int status = -1;
1752       int signo = -1;
1753       error = (platform_sp->RunShellCommand(expr, working_dir, &status, &signo,
1754                                             &output, m_options.timeout));
1755       if (!output.empty())
1756         result.GetOutputStream().PutCString(output);
1757       if (status > 0) {
1758         if (signo > 0) {
1759           const char *signo_cstr = Host::GetSignalAsCString(signo);
1760           if (signo_cstr)
1761             result.GetOutputStream().Printf(
1762                 "error: command returned with status %i and signal %s\n",
1763                 status, signo_cstr);
1764           else
1765             result.GetOutputStream().Printf(
1766                 "error: command returned with status %i and signal %i\n",
1767                 status, signo);
1768         } else
1769           result.GetOutputStream().Printf(
1770               "error: command returned with status %i\n", status);
1771       }
1772     } else {
1773       result.GetOutputStream().Printf(
1774           "error: cannot run remote shell commands without a platform\n");
1775       error.SetErrorString(
1776           "error: cannot run remote shell commands without a platform");
1777     }
1778 
1779     if (error.Fail()) {
1780       result.AppendError(error.AsCString());
1781       result.SetStatus(eReturnStatusFailed);
1782     } else {
1783       result.SetStatus(eReturnStatusSuccessFinishResult);
1784     }
1785     return true;
1786   }
1787 
1788   CommandOptions m_options;
1789 };
1790 
1791 //----------------------------------------------------------------------
1792 // "platform install" - install a target to a remote end
1793 //----------------------------------------------------------------------
1794 class CommandObjectPlatformInstall : public CommandObjectParsed {
1795 public:
1796   CommandObjectPlatformInstall(CommandInterpreter &interpreter)
1797       : CommandObjectParsed(
1798             interpreter, "platform target-install",
1799             "Install a target (bundle or executable file) to the remote end.",
1800             "platform target-install <local-thing> <remote-sandbox>", 0) {}
1801 
1802   ~CommandObjectPlatformInstall() override = default;
1803 
1804   bool DoExecute(Args &args, CommandReturnObject &result) override {
1805     if (args.GetArgumentCount() != 2) {
1806       result.AppendError("platform target-install takes two arguments");
1807       result.SetStatus(eReturnStatusFailed);
1808       return false;
1809     }
1810     // TODO: move the bulk of this code over to the platform itself
1811     FileSpec src(args.GetArgumentAtIndex(0));
1812     FileSystem::Instance().Resolve(src);
1813     FileSpec dst(args.GetArgumentAtIndex(1));
1814     if (!FileSystem::Instance().Exists(src)) {
1815       result.AppendError("source location does not exist or is not accessible");
1816       result.SetStatus(eReturnStatusFailed);
1817       return false;
1818     }
1819     PlatformSP platform_sp(
1820         m_interpreter.GetDebugger().GetPlatformList().GetSelectedPlatform());
1821     if (!platform_sp) {
1822       result.AppendError("no platform currently selected");
1823       result.SetStatus(eReturnStatusFailed);
1824       return false;
1825     }
1826 
1827     Status error = platform_sp->Install(src, dst);
1828     if (error.Success()) {
1829       result.SetStatus(eReturnStatusSuccessFinishNoResult);
1830     } else {
1831       result.AppendErrorWithFormat("install failed: %s", error.AsCString());
1832       result.SetStatus(eReturnStatusFailed);
1833     }
1834     return result.Succeeded();
1835   }
1836 };
1837 
1838 CommandObjectPlatform::CommandObjectPlatform(CommandInterpreter &interpreter)
1839     : CommandObjectMultiword(
1840           interpreter, "platform", "Commands to manage and create platforms.",
1841           "platform [connect|disconnect|info|list|status|select] ...") {
1842   LoadSubCommand("select",
1843                  CommandObjectSP(new CommandObjectPlatformSelect(interpreter)));
1844   LoadSubCommand("list",
1845                  CommandObjectSP(new CommandObjectPlatformList(interpreter)));
1846   LoadSubCommand("status",
1847                  CommandObjectSP(new CommandObjectPlatformStatus(interpreter)));
1848   LoadSubCommand("connect", CommandObjectSP(
1849                                 new CommandObjectPlatformConnect(interpreter)));
1850   LoadSubCommand(
1851       "disconnect",
1852       CommandObjectSP(new CommandObjectPlatformDisconnect(interpreter)));
1853   LoadSubCommand("settings", CommandObjectSP(new CommandObjectPlatformSettings(
1854                                  interpreter)));
1855   LoadSubCommand("mkdir",
1856                  CommandObjectSP(new CommandObjectPlatformMkDir(interpreter)));
1857   LoadSubCommand("file",
1858                  CommandObjectSP(new CommandObjectPlatformFile(interpreter)));
1859   LoadSubCommand("get-file", CommandObjectSP(new CommandObjectPlatformGetFile(
1860                                  interpreter)));
1861   LoadSubCommand("get-size", CommandObjectSP(new CommandObjectPlatformGetSize(
1862                                  interpreter)));
1863   LoadSubCommand("put-file", CommandObjectSP(new CommandObjectPlatformPutFile(
1864                                  interpreter)));
1865   LoadSubCommand("process", CommandObjectSP(
1866                                 new CommandObjectPlatformProcess(interpreter)));
1867   LoadSubCommand("shell",
1868                  CommandObjectSP(new CommandObjectPlatformShell(interpreter)));
1869   LoadSubCommand(
1870       "target-install",
1871       CommandObjectSP(new CommandObjectPlatformInstall(interpreter)));
1872 }
1873 
1874 CommandObjectPlatform::~CommandObjectPlatform() = default;
1875