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