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