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/DataExtractor.h"
17 #include "lldb/Core/Debugger.h"
18 #include "lldb/Core/Module.h"
19 #include "lldb/Core/PluginManager.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/Utils.h"
31 
32 using namespace lldb;
33 using namespace lldb_private;
34 
35 static mode_t ParsePermissionString(const char *permissions) {
36   if (strlen(permissions) != 9)
37     return (mode_t)(-1);
38   bool user_r, user_w, user_x, group_r, group_w, group_x, world_r, world_w,
39       world_x;
40 
41   user_r = (permissions[0] == 'r');
42   user_w = (permissions[1] == 'w');
43   user_x = (permissions[2] == 'x');
44 
45   group_r = (permissions[3] == 'r');
46   group_w = (permissions[4] == 'w');
47   group_x = (permissions[5] == 'x');
48 
49   world_r = (permissions[6] == 'r');
50   world_w = (permissions[7] == 'w');
51   world_x = (permissions[8] == 'x');
52 
53   mode_t user, group, world;
54   user = (user_r ? 4 : 0) | (user_w ? 2 : 0) | (user_x ? 1 : 0);
55   group = (group_r ? 4 : 0) | (group_w ? 2 : 0) | (group_x ? 1 : 0);
56   world = (world_r ? 4 : 0) | (world_w ? 2 : 0) | (world_x ? 1 : 0);
57 
58   return user | group | world;
59 }
60 
61 static OptionDefinition g_permissions_options[] = {
62     // clang-format off
63   {LLDB_OPT_SET_ALL, false, "permissions-value",   'v', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypePermissionsNumber, "Give out the numeric value for permissions (e.g. 757)"},
64   {LLDB_OPT_SET_ALL, false, "permissions-string",  's', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypePermissionsString, "Give out the string value for permissions (e.g. rwxr-xr--)."},
65   {LLDB_OPT_SET_ALL, false, "user-read",           'r', OptionParser::eNoArgument,       nullptr, nullptr, 0, eArgTypeNone,              "Allow user to read."},
66   {LLDB_OPT_SET_ALL, false, "user-write",          'w', OptionParser::eNoArgument,       nullptr, nullptr, 0, eArgTypeNone,              "Allow user to write."},
67   {LLDB_OPT_SET_ALL, false, "user-exec",           'x', OptionParser::eNoArgument,       nullptr, nullptr, 0, eArgTypeNone,              "Allow user to execute."},
68   {LLDB_OPT_SET_ALL, false, "group-read",          'R', OptionParser::eNoArgument,       nullptr, nullptr, 0, eArgTypeNone,              "Allow group to read."},
69   {LLDB_OPT_SET_ALL, false, "group-write",         'W', OptionParser::eNoArgument,       nullptr, nullptr, 0, eArgTypeNone,              "Allow group to write."},
70   {LLDB_OPT_SET_ALL, false, "group-exec",          'X', OptionParser::eNoArgument,       nullptr, nullptr, 0, eArgTypeNone,              "Allow group to execute."},
71   {LLDB_OPT_SET_ALL, false, "world-read",          'd', OptionParser::eNoArgument,       nullptr, nullptr, 0, eArgTypeNone,              "Allow world to read."},
72   {LLDB_OPT_SET_ALL, false, "world-write",         't', OptionParser::eNoArgument,       nullptr, nullptr, 0, eArgTypeNone,              "Allow world to write."},
73   {LLDB_OPT_SET_ALL, false, "world-exec",          'e', OptionParser::eNoArgument,       nullptr, nullptr, 0, eArgTypeNone,              "Allow world to execute."},
74     // clang-format on
75 };
76 
77 class OptionPermissions : public lldb_private::OptionGroup {
78 public:
79   OptionPermissions() {}
80 
81   ~OptionPermissions() override = default;
82 
83   lldb_private::Error
84   SetOptionValue(uint32_t option_idx, const char *option_arg,
85                  ExecutionContext *execution_context) override {
86     Error error;
87     char short_option = (char)GetDefinitions()[option_idx].short_option;
88     switch (short_option) {
89     case 'v': {
90       bool ok;
91       uint32_t perms = StringConvert::ToUInt32(option_arg, 777, 8, &ok);
92       if (!ok)
93         error.SetErrorStringWithFormat("invalid value for permissions: %s",
94                                        option_arg);
95       else
96         m_permissions = perms;
97     } break;
98     case 's': {
99       mode_t perms = ParsePermissionString(option_arg);
100       if (perms == (mode_t)-1)
101         error.SetErrorStringWithFormat("invalid value for permissions: %s",
102                                        option_arg);
103       else
104         m_permissions = perms;
105     } break;
106     case 'r':
107       m_permissions |= lldb::eFilePermissionsUserRead;
108       break;
109     case 'w':
110       m_permissions |= lldb::eFilePermissionsUserWrite;
111       break;
112     case 'x':
113       m_permissions |= lldb::eFilePermissionsUserExecute;
114       break;
115     case 'R':
116       m_permissions |= lldb::eFilePermissionsGroupRead;
117       break;
118     case 'W':
119       m_permissions |= lldb::eFilePermissionsGroupWrite;
120       break;
121     case 'X':
122       m_permissions |= lldb::eFilePermissionsGroupExecute;
123       break;
124     case 'd':
125       m_permissions |= lldb::eFilePermissionsWorldRead;
126       break;
127     case 't':
128       m_permissions |= lldb::eFilePermissionsWorldWrite;
129       break;
130     case 'e':
131       m_permissions |= lldb::eFilePermissionsWorldExecute;
132       break;
133     default:
134       error.SetErrorStringWithFormat("unrecognized option '%c'", short_option);
135       break;
136     }
137 
138     return error;
139   }
140 
141   void OptionParsingStarting(ExecutionContext *execution_context) override {
142     m_permissions = 0;
143   }
144 
145   uint32_t GetNumDefinitions() override {
146     return llvm::array_lengthof(g_permissions_options);
147   }
148 
149   const lldb_private::OptionDefinition *GetDefinitions() override {
150     return 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.c_str(), 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 class CommandObjectPlatformFRead : public CommandObjectParsed {
623 public:
624   CommandObjectPlatformFRead(CommandInterpreter &interpreter)
625       : CommandObjectParsed(interpreter, "platform file read",
626                             "Read data from a file on the remote end.", nullptr,
627                             0),
628         m_options() {}
629 
630   ~CommandObjectPlatformFRead() override = default;
631 
632   bool DoExecute(Args &args, CommandReturnObject &result) override {
633     PlatformSP platform_sp(
634         m_interpreter.GetDebugger().GetPlatformList().GetSelectedPlatform());
635     if (platform_sp) {
636       std::string cmd_line;
637       args.GetCommandString(cmd_line);
638       const lldb::user_id_t fd =
639           StringConvert::ToUInt64(cmd_line.c_str(), UINT64_MAX);
640       std::string buffer(m_options.m_count, 0);
641       Error error;
642       uint32_t retcode = platform_sp->ReadFile(
643           fd, m_options.m_offset, &buffer[0], m_options.m_count, error);
644       result.AppendMessageWithFormat("Return = %d\n", retcode);
645       result.AppendMessageWithFormat("Data = \"%s\"\n", buffer.c_str());
646       result.SetStatus(eReturnStatusSuccessFinishResult);
647     } else {
648       result.AppendError("no platform currently selected\n");
649       result.SetStatus(eReturnStatusFailed);
650     }
651     return result.Succeeded();
652   }
653 
654   Options *GetOptions() override { return &m_options; }
655 
656 protected:
657   class CommandOptions : public Options {
658   public:
659     CommandOptions() : Options() {}
660 
661     ~CommandOptions() override = default;
662 
663     Error SetOptionValue(uint32_t option_idx, const char *option_arg,
664                          ExecutionContext *execution_context) override {
665       Error error;
666       char short_option = (char)m_getopt_table[option_idx].val;
667       bool success = false;
668 
669       switch (short_option) {
670       case 'o':
671         m_offset = StringConvert::ToUInt32(option_arg, 0, 0, &success);
672         if (!success)
673           error.SetErrorStringWithFormat("invalid offset: '%s'", option_arg);
674         break;
675       case 'c':
676         m_count = StringConvert::ToUInt32(option_arg, 0, 0, &success);
677         if (!success)
678           error.SetErrorStringWithFormat("invalid offset: '%s'", option_arg);
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     const OptionDefinition *GetDefinitions() override { return g_option_table; }
695 
696     // Options table: Required for subclasses of Options.
697 
698     static OptionDefinition g_option_table[];
699 
700     // Instance variables to hold the values for command options.
701 
702     uint32_t m_offset;
703     uint32_t m_count;
704   };
705 
706   CommandOptions m_options;
707 };
708 
709 OptionDefinition CommandObjectPlatformFRead::CommandOptions::g_option_table[] =
710     {
711         // clang-format off
712   {LLDB_OPT_SET_1, false, "offset", 'o', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeIndex, "Offset into the file at which to start reading."},
713   {LLDB_OPT_SET_1, false, "count",  'c', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeCount, "Number of bytes to read from the file."},
714   {0, false, nullptr, 0, 0, nullptr, nullptr, 0, eArgTypeNone, nullptr}
715         // clang-format on
716 };
717 
718 //----------------------------------------------------------------------
719 // "platform fwrite"
720 //----------------------------------------------------------------------
721 class CommandObjectPlatformFWrite : public CommandObjectParsed {
722 public:
723   CommandObjectPlatformFWrite(CommandInterpreter &interpreter)
724       : CommandObjectParsed(interpreter, "platform file write",
725                             "Write data to a file on the remote end.", nullptr,
726                             0),
727         m_options() {}
728 
729   ~CommandObjectPlatformFWrite() override = default;
730 
731   bool DoExecute(Args &args, CommandReturnObject &result) override {
732     PlatformSP platform_sp(
733         m_interpreter.GetDebugger().GetPlatformList().GetSelectedPlatform());
734     if (platform_sp) {
735       std::string cmd_line;
736       args.GetCommandString(cmd_line);
737       Error error;
738       const lldb::user_id_t fd =
739           StringConvert::ToUInt64(cmd_line.c_str(), UINT64_MAX);
740       uint32_t retcode =
741           platform_sp->WriteFile(fd, m_options.m_offset, &m_options.m_data[0],
742                                  m_options.m_data.size(), error);
743       result.AppendMessageWithFormat("Return = %d\n", retcode);
744       result.SetStatus(eReturnStatusSuccessFinishResult);
745     } else {
746       result.AppendError("no platform currently selected\n");
747       result.SetStatus(eReturnStatusFailed);
748     }
749     return result.Succeeded();
750   }
751 
752   Options *GetOptions() override { return &m_options; }
753 
754 protected:
755   class CommandOptions : public Options {
756   public:
757     CommandOptions() : Options() {}
758 
759     ~CommandOptions() override = default;
760 
761     Error SetOptionValue(uint32_t option_idx, const char *option_arg,
762                          ExecutionContext *execution_context) override {
763       Error error;
764       char short_option = (char)m_getopt_table[option_idx].val;
765       bool success = false;
766 
767       switch (short_option) {
768       case 'o':
769         m_offset = StringConvert::ToUInt32(option_arg, 0, 0, &success);
770         if (!success)
771           error.SetErrorStringWithFormat("invalid offset: '%s'", option_arg);
772         break;
773       case 'd':
774         m_data.assign(option_arg);
775         break;
776       default:
777         error.SetErrorStringWithFormat("unrecognized option '%c'",
778                                        short_option);
779         break;
780       }
781 
782       return error;
783     }
784 
785     void OptionParsingStarting(ExecutionContext *execution_context) override {
786       m_offset = 0;
787       m_data.clear();
788     }
789 
790     const OptionDefinition *GetDefinitions() override { return g_option_table; }
791 
792     // Options table: Required for subclasses of Options.
793 
794     static OptionDefinition g_option_table[];
795 
796     // Instance variables to hold the values for command options.
797 
798     uint32_t m_offset;
799     std::string m_data;
800   };
801 
802   CommandOptions m_options;
803 };
804 
805 OptionDefinition CommandObjectPlatformFWrite::CommandOptions::g_option_table[] =
806     {
807         // clang-format off
808   {LLDB_OPT_SET_1, false, "offset", 'o', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeIndex, "Offset into the file at which to start reading."},
809   {LLDB_OPT_SET_1, false, "data",   'd', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeValue, "Text to write to the file."},
810   {0, false, nullptr, 0, 0, nullptr, nullptr, 0, eArgTypeNone, nullptr}
811         // clang-format on
812 };
813 
814 class CommandObjectPlatformFile : public CommandObjectMultiword {
815 public:
816   //------------------------------------------------------------------
817   // Constructors and Destructors
818   //------------------------------------------------------------------
819   CommandObjectPlatformFile(CommandInterpreter &interpreter)
820       : CommandObjectMultiword(
821             interpreter, "platform file",
822             "Commands to access files on the current platform.",
823             "platform file [open|close|read|write] ...") {
824     LoadSubCommand(
825         "open", CommandObjectSP(new CommandObjectPlatformFOpen(interpreter)));
826     LoadSubCommand(
827         "close", CommandObjectSP(new CommandObjectPlatformFClose(interpreter)));
828     LoadSubCommand(
829         "read", CommandObjectSP(new CommandObjectPlatformFRead(interpreter)));
830     LoadSubCommand(
831         "write", CommandObjectSP(new CommandObjectPlatformFWrite(interpreter)));
832   }
833 
834   ~CommandObjectPlatformFile() override = default;
835 
836 private:
837   //------------------------------------------------------------------
838   // For CommandObjectPlatform only
839   //------------------------------------------------------------------
840   DISALLOW_COPY_AND_ASSIGN(CommandObjectPlatformFile);
841 };
842 
843 //----------------------------------------------------------------------
844 // "platform get-file remote-file-path host-file-path"
845 //----------------------------------------------------------------------
846 class CommandObjectPlatformGetFile : public CommandObjectParsed {
847 public:
848   CommandObjectPlatformGetFile(CommandInterpreter &interpreter)
849       : CommandObjectParsed(
850             interpreter, "platform get-file",
851             "Transfer a file from the remote end to the local host.",
852             "platform get-file <remote-file-spec> <local-file-spec>", 0) {
853     SetHelpLong(
854         R"(Examples:
855 
856 (lldb) platform get-file /the/remote/file/path /the/local/file/path
857 
858     Transfer a file from the remote end with file path /the/remote/file/path to the local host.)");
859 
860     CommandArgumentEntry arg1, arg2;
861     CommandArgumentData file_arg_remote, file_arg_host;
862 
863     // Define the first (and only) variant of this arg.
864     file_arg_remote.arg_type = eArgTypeFilename;
865     file_arg_remote.arg_repetition = eArgRepeatPlain;
866     // There is only one variant this argument could be; put it into the
867     // argument entry.
868     arg1.push_back(file_arg_remote);
869 
870     // Define the second (and only) variant of this arg.
871     file_arg_host.arg_type = eArgTypeFilename;
872     file_arg_host.arg_repetition = eArgRepeatPlain;
873     // There is only one variant this argument could be; put it into the
874     // argument entry.
875     arg2.push_back(file_arg_host);
876 
877     // Push the data for the first and the second arguments into the m_arguments
878     // vector.
879     m_arguments.push_back(arg1);
880     m_arguments.push_back(arg2);
881   }
882 
883   ~CommandObjectPlatformGetFile() override = default;
884 
885   bool DoExecute(Args &args, CommandReturnObject &result) override {
886     // If the number of arguments is incorrect, issue an error message.
887     if (args.GetArgumentCount() != 2) {
888       result.GetErrorStream().Printf("error: required arguments missing; "
889                                      "specify both the source and destination "
890                                      "file paths\n");
891       result.SetStatus(eReturnStatusFailed);
892       return false;
893     }
894 
895     PlatformSP platform_sp(
896         m_interpreter.GetDebugger().GetPlatformList().GetSelectedPlatform());
897     if (platform_sp) {
898       const char *remote_file_path = args.GetArgumentAtIndex(0);
899       const char *local_file_path = args.GetArgumentAtIndex(1);
900       Error error = platform_sp->GetFile(FileSpec(remote_file_path, false),
901                                          FileSpec(local_file_path, false));
902       if (error.Success()) {
903         result.AppendMessageWithFormat(
904             "successfully get-file from %s (remote) to %s (host)\n",
905             remote_file_path, local_file_path);
906         result.SetStatus(eReturnStatusSuccessFinishResult);
907       } else {
908         result.AppendMessageWithFormat("get-file failed: %s\n",
909                                        error.AsCString());
910         result.SetStatus(eReturnStatusFailed);
911       }
912     } else {
913       result.AppendError("no platform currently selected\n");
914       result.SetStatus(eReturnStatusFailed);
915     }
916     return result.Succeeded();
917   }
918 };
919 
920 //----------------------------------------------------------------------
921 // "platform get-size remote-file-path"
922 //----------------------------------------------------------------------
923 class CommandObjectPlatformGetSize : public CommandObjectParsed {
924 public:
925   CommandObjectPlatformGetSize(CommandInterpreter &interpreter)
926       : CommandObjectParsed(interpreter, "platform get-size",
927                             "Get the file size from the remote end.",
928                             "platform get-size <remote-file-spec>", 0) {
929     SetHelpLong(
930         R"(Examples:
931 
932 (lldb) platform get-size /the/remote/file/path
933 
934     Get the file size from the remote end with path /the/remote/file/path.)");
935 
936     CommandArgumentEntry arg1;
937     CommandArgumentData file_arg_remote;
938 
939     // Define the first (and only) variant of this arg.
940     file_arg_remote.arg_type = eArgTypeFilename;
941     file_arg_remote.arg_repetition = eArgRepeatPlain;
942     // There is only one variant this argument could be; put it into the
943     // argument entry.
944     arg1.push_back(file_arg_remote);
945 
946     // Push the data for the first argument into the m_arguments vector.
947     m_arguments.push_back(arg1);
948   }
949 
950   ~CommandObjectPlatformGetSize() override = default;
951 
952   bool DoExecute(Args &args, CommandReturnObject &result) override {
953     // If the number of arguments is incorrect, issue an error message.
954     if (args.GetArgumentCount() != 1) {
955       result.GetErrorStream().Printf("error: required argument missing; "
956                                      "specify the source file path as the only "
957                                      "argument\n");
958       result.SetStatus(eReturnStatusFailed);
959       return false;
960     }
961 
962     PlatformSP platform_sp(
963         m_interpreter.GetDebugger().GetPlatformList().GetSelectedPlatform());
964     if (platform_sp) {
965       std::string remote_file_path(args.GetArgumentAtIndex(0));
966       user_id_t size =
967           platform_sp->GetFileSize(FileSpec(remote_file_path.c_str(), false));
968       if (size != UINT64_MAX) {
969         result.AppendMessageWithFormat("File size of %s (remote): %" PRIu64
970                                        "\n",
971                                        remote_file_path.c_str(), size);
972         result.SetStatus(eReturnStatusSuccessFinishResult);
973       } else {
974         result.AppendMessageWithFormat(
975             "Error getting file size of %s (remote)\n",
976             remote_file_path.c_str());
977         result.SetStatus(eReturnStatusFailed);
978       }
979     } else {
980       result.AppendError("no platform currently selected\n");
981       result.SetStatus(eReturnStatusFailed);
982     }
983     return result.Succeeded();
984   }
985 };
986 
987 //----------------------------------------------------------------------
988 // "platform put-file"
989 //----------------------------------------------------------------------
990 class CommandObjectPlatformPutFile : public CommandObjectParsed {
991 public:
992   CommandObjectPlatformPutFile(CommandInterpreter &interpreter)
993       : CommandObjectParsed(
994             interpreter, "platform put-file",
995             "Transfer a file from this system to the remote end.", nullptr, 0) {
996   }
997 
998   ~CommandObjectPlatformPutFile() override = default;
999 
1000   bool DoExecute(Args &args, CommandReturnObject &result) override {
1001     const char *src = args.GetArgumentAtIndex(0);
1002     const char *dst = args.GetArgumentAtIndex(1);
1003 
1004     FileSpec src_fs(src, true);
1005     FileSpec dst_fs(dst ? dst : src_fs.GetFilename().GetCString(), false);
1006 
1007     PlatformSP platform_sp(
1008         m_interpreter.GetDebugger().GetPlatformList().GetSelectedPlatform());
1009     if (platform_sp) {
1010       Error error(platform_sp->PutFile(src_fs, dst_fs));
1011       if (error.Success()) {
1012         result.SetStatus(eReturnStatusSuccessFinishNoResult);
1013       } else {
1014         result.AppendError(error.AsCString());
1015         result.SetStatus(eReturnStatusFailed);
1016       }
1017     } else {
1018       result.AppendError("no platform currently selected\n");
1019       result.SetStatus(eReturnStatusFailed);
1020     }
1021     return result.Succeeded();
1022   }
1023 };
1024 
1025 //----------------------------------------------------------------------
1026 // "platform process launch"
1027 //----------------------------------------------------------------------
1028 class CommandObjectPlatformProcessLaunch : public CommandObjectParsed {
1029 public:
1030   CommandObjectPlatformProcessLaunch(CommandInterpreter &interpreter)
1031       : CommandObjectParsed(interpreter, "platform process launch",
1032                             "Launch a new process on a remote platform.",
1033                             "platform process launch program",
1034                             eCommandRequiresTarget | eCommandTryTargetAPILock),
1035         m_options() {}
1036 
1037   ~CommandObjectPlatformProcessLaunch() override = default;
1038 
1039   Options *GetOptions() override { return &m_options; }
1040 
1041 protected:
1042   bool DoExecute(Args &args, CommandReturnObject &result) override {
1043     Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get();
1044     PlatformSP platform_sp;
1045     if (target) {
1046       platform_sp = target->GetPlatform();
1047     }
1048     if (!platform_sp) {
1049       platform_sp =
1050           m_interpreter.GetDebugger().GetPlatformList().GetSelectedPlatform();
1051     }
1052 
1053     if (platform_sp) {
1054       Error error;
1055       const size_t argc = args.GetArgumentCount();
1056       Target *target = m_exe_ctx.GetTargetPtr();
1057       Module *exe_module = target->GetExecutableModulePointer();
1058       if (exe_module) {
1059         m_options.launch_info.GetExecutableFile() = exe_module->GetFileSpec();
1060         char exe_path[PATH_MAX];
1061         if (m_options.launch_info.GetExecutableFile().GetPath(exe_path,
1062                                                               sizeof(exe_path)))
1063           m_options.launch_info.GetArguments().AppendArgument(exe_path);
1064         m_options.launch_info.GetArchitecture() = exe_module->GetArchitecture();
1065       }
1066 
1067       if (argc > 0) {
1068         if (m_options.launch_info.GetExecutableFile()) {
1069           // We already have an executable file, so we will use this
1070           // and all arguments to this function are extra arguments
1071           m_options.launch_info.GetArguments().AppendArguments(args);
1072         } else {
1073           // We don't have any file yet, so the first argument is our
1074           // executable, and the rest are program arguments
1075           const bool first_arg_is_executable = true;
1076           m_options.launch_info.SetArguments(args, first_arg_is_executable);
1077         }
1078       }
1079 
1080       if (m_options.launch_info.GetExecutableFile()) {
1081         Debugger &debugger = m_interpreter.GetDebugger();
1082 
1083         if (argc == 0)
1084           target->GetRunArguments(m_options.launch_info.GetArguments());
1085 
1086         ProcessSP process_sp(platform_sp->DebugProcess(
1087             m_options.launch_info, debugger, target, error));
1088         if (process_sp && process_sp->IsAlive()) {
1089           result.SetStatus(eReturnStatusSuccessFinishNoResult);
1090           return true;
1091         }
1092 
1093         if (error.Success())
1094           result.AppendError("process launch failed");
1095         else
1096           result.AppendError(error.AsCString());
1097         result.SetStatus(eReturnStatusFailed);
1098       } else {
1099         result.AppendError("'platform process launch' uses the current target "
1100                            "file and arguments, or the executable and its "
1101                            "arguments can be specified in this command");
1102         result.SetStatus(eReturnStatusFailed);
1103         return false;
1104       }
1105     } else {
1106       result.AppendError("no platform is selected\n");
1107     }
1108     return result.Succeeded();
1109   }
1110 
1111 protected:
1112   ProcessLaunchCommandOptions m_options;
1113 };
1114 
1115 //----------------------------------------------------------------------
1116 // "platform process list"
1117 //----------------------------------------------------------------------
1118 class CommandObjectPlatformProcessList : public CommandObjectParsed {
1119 public:
1120   CommandObjectPlatformProcessList(CommandInterpreter &interpreter)
1121       : CommandObjectParsed(interpreter, "platform process list",
1122                             "List processes on a remote platform by name, pid, "
1123                             "or many other matching attributes.",
1124                             "platform process list", 0),
1125         m_options() {}
1126 
1127   ~CommandObjectPlatformProcessList() override = default;
1128 
1129   Options *GetOptions() override { return &m_options; }
1130 
1131 protected:
1132   bool DoExecute(Args &args, CommandReturnObject &result) override {
1133     Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get();
1134     PlatformSP platform_sp;
1135     if (target) {
1136       platform_sp = target->GetPlatform();
1137     }
1138     if (!platform_sp) {
1139       platform_sp =
1140           m_interpreter.GetDebugger().GetPlatformList().GetSelectedPlatform();
1141     }
1142 
1143     if (platform_sp) {
1144       Error error;
1145       if (args.GetArgumentCount() == 0) {
1146         if (platform_sp) {
1147           Stream &ostrm = result.GetOutputStream();
1148 
1149           lldb::pid_t pid =
1150               m_options.match_info.GetProcessInfo().GetProcessID();
1151           if (pid != LLDB_INVALID_PROCESS_ID) {
1152             ProcessInstanceInfo proc_info;
1153             if (platform_sp->GetProcessInfo(pid, proc_info)) {
1154               ProcessInstanceInfo::DumpTableHeader(ostrm, platform_sp.get(),
1155                                                    m_options.show_args,
1156                                                    m_options.verbose);
1157               proc_info.DumpAsTableRow(ostrm, platform_sp.get(),
1158                                        m_options.show_args, m_options.verbose);
1159               result.SetStatus(eReturnStatusSuccessFinishResult);
1160             } else {
1161               result.AppendErrorWithFormat(
1162                   "no process found with pid = %" PRIu64 "\n", pid);
1163               result.SetStatus(eReturnStatusFailed);
1164             }
1165           } else {
1166             ProcessInstanceInfoList proc_infos;
1167             const uint32_t matches =
1168                 platform_sp->FindProcesses(m_options.match_info, proc_infos);
1169             const char *match_desc = nullptr;
1170             const char *match_name =
1171                 m_options.match_info.GetProcessInfo().GetName();
1172             if (match_name && match_name[0]) {
1173               switch (m_options.match_info.GetNameMatchType()) {
1174               case eNameMatchIgnore:
1175                 break;
1176               case eNameMatchEquals:
1177                 match_desc = "matched";
1178                 break;
1179               case eNameMatchContains:
1180                 match_desc = "contained";
1181                 break;
1182               case eNameMatchStartsWith:
1183                 match_desc = "started with";
1184                 break;
1185               case eNameMatchEndsWith:
1186                 match_desc = "ended with";
1187                 break;
1188               case eNameMatchRegularExpression:
1189                 match_desc = "matched the regular expression";
1190                 break;
1191               }
1192             }
1193 
1194             if (matches == 0) {
1195               if (match_desc)
1196                 result.AppendErrorWithFormat(
1197                     "no processes were found that %s \"%s\" on the \"%s\" "
1198                     "platform\n",
1199                     match_desc, match_name,
1200                     platform_sp->GetPluginName().GetCString());
1201               else
1202                 result.AppendErrorWithFormat(
1203                     "no processes were found on the \"%s\" platform\n",
1204                     platform_sp->GetPluginName().GetCString());
1205               result.SetStatus(eReturnStatusFailed);
1206             } else {
1207               result.AppendMessageWithFormat(
1208                   "%u matching process%s found on \"%s\"", matches,
1209                   matches > 1 ? "es were" : " was",
1210                   platform_sp->GetName().GetCString());
1211               if (match_desc)
1212                 result.AppendMessageWithFormat(" whose name %s \"%s\"",
1213                                                match_desc, match_name);
1214               result.AppendMessageWithFormat("\n");
1215               ProcessInstanceInfo::DumpTableHeader(ostrm, platform_sp.get(),
1216                                                    m_options.show_args,
1217                                                    m_options.verbose);
1218               for (uint32_t i = 0; i < matches; ++i) {
1219                 proc_infos.GetProcessInfoAtIndex(i).DumpAsTableRow(
1220                     ostrm, platform_sp.get(), m_options.show_args,
1221                     m_options.verbose);
1222               }
1223             }
1224           }
1225         }
1226       } else {
1227         result.AppendError("invalid args: process list takes only options\n");
1228         result.SetStatus(eReturnStatusFailed);
1229       }
1230     } else {
1231       result.AppendError("no platform is selected\n");
1232       result.SetStatus(eReturnStatusFailed);
1233     }
1234     return result.Succeeded();
1235   }
1236 
1237   class CommandOptions : public Options {
1238   public:
1239     CommandOptions()
1240         : Options(), match_info(), show_args(false), verbose(false) {
1241       static std::once_flag g_once_flag;
1242       std::call_once(g_once_flag, []() {
1243         PosixPlatformCommandOptionValidator *posix_validator =
1244             new PosixPlatformCommandOptionValidator();
1245         for (size_t i = 0; g_option_table[i].short_option != 0; ++i) {
1246           switch (g_option_table[i].short_option) {
1247           case 'u':
1248           case 'U':
1249           case 'g':
1250           case 'G':
1251             g_option_table[i].validator = posix_validator;
1252             break;
1253           default:
1254             break;
1255           }
1256         }
1257       });
1258     }
1259 
1260     ~CommandOptions() override = default;
1261 
1262     Error SetOptionValue(uint32_t option_idx, const char *option_arg,
1263                          ExecutionContext *execution_context) override {
1264       Error error;
1265       const int short_option = m_getopt_table[option_idx].val;
1266       bool success = false;
1267 
1268       switch (short_option) {
1269       case 'p':
1270         match_info.GetProcessInfo().SetProcessID(StringConvert::ToUInt32(
1271             option_arg, LLDB_INVALID_PROCESS_ID, 0, &success));
1272         if (!success)
1273           error.SetErrorStringWithFormat("invalid process ID string: '%s'",
1274                                          option_arg);
1275         break;
1276 
1277       case 'P':
1278         match_info.GetProcessInfo().SetParentProcessID(StringConvert::ToUInt32(
1279             option_arg, LLDB_INVALID_PROCESS_ID, 0, &success));
1280         if (!success)
1281           error.SetErrorStringWithFormat(
1282               "invalid parent process ID string: '%s'", option_arg);
1283         break;
1284 
1285       case 'u':
1286         match_info.GetProcessInfo().SetUserID(
1287             StringConvert::ToUInt32(option_arg, UINT32_MAX, 0, &success));
1288         if (!success)
1289           error.SetErrorStringWithFormat("invalid user ID string: '%s'",
1290                                          option_arg);
1291         break;
1292 
1293       case 'U':
1294         match_info.GetProcessInfo().SetEffectiveUserID(
1295             StringConvert::ToUInt32(option_arg, UINT32_MAX, 0, &success));
1296         if (!success)
1297           error.SetErrorStringWithFormat(
1298               "invalid effective user ID string: '%s'", option_arg);
1299         break;
1300 
1301       case 'g':
1302         match_info.GetProcessInfo().SetGroupID(
1303             StringConvert::ToUInt32(option_arg, UINT32_MAX, 0, &success));
1304         if (!success)
1305           error.SetErrorStringWithFormat("invalid group ID string: '%s'",
1306                                          option_arg);
1307         break;
1308 
1309       case 'G':
1310         match_info.GetProcessInfo().SetEffectiveGroupID(
1311             StringConvert::ToUInt32(option_arg, UINT32_MAX, 0, &success));
1312         if (!success)
1313           error.SetErrorStringWithFormat(
1314               "invalid effective group ID string: '%s'", option_arg);
1315         break;
1316 
1317       case 'a': {
1318         TargetSP target_sp =
1319             execution_context ? execution_context->GetTargetSP() : TargetSP();
1320         DebuggerSP debugger_sp =
1321             target_sp ? target_sp->GetDebugger().shared_from_this()
1322                       : DebuggerSP();
1323         PlatformSP platform_sp =
1324             debugger_sp ? debugger_sp->GetPlatformList().GetSelectedPlatform()
1325                         : PlatformSP();
1326         match_info.GetProcessInfo().GetArchitecture().SetTriple(
1327             option_arg, platform_sp.get());
1328       } break;
1329 
1330       case 'n':
1331         match_info.GetProcessInfo().GetExecutableFile().SetFile(option_arg,
1332                                                                 false);
1333         match_info.SetNameMatchType(eNameMatchEquals);
1334         break;
1335 
1336       case 'e':
1337         match_info.GetProcessInfo().GetExecutableFile().SetFile(option_arg,
1338                                                                 false);
1339         match_info.SetNameMatchType(eNameMatchEndsWith);
1340         break;
1341 
1342       case 's':
1343         match_info.GetProcessInfo().GetExecutableFile().SetFile(option_arg,
1344                                                                 false);
1345         match_info.SetNameMatchType(eNameMatchStartsWith);
1346         break;
1347 
1348       case 'c':
1349         match_info.GetProcessInfo().GetExecutableFile().SetFile(option_arg,
1350                                                                 false);
1351         match_info.SetNameMatchType(eNameMatchContains);
1352         break;
1353 
1354       case 'r':
1355         match_info.GetProcessInfo().GetExecutableFile().SetFile(option_arg,
1356                                                                 false);
1357         match_info.SetNameMatchType(eNameMatchRegularExpression);
1358         break;
1359 
1360       case 'A':
1361         show_args = true;
1362         break;
1363 
1364       case 'v':
1365         verbose = true;
1366         break;
1367 
1368       default:
1369         error.SetErrorStringWithFormat("unrecognized option '%c'",
1370                                        short_option);
1371         break;
1372       }
1373 
1374       return error;
1375     }
1376 
1377     void OptionParsingStarting(ExecutionContext *execution_context) override {
1378       match_info.Clear();
1379       show_args = false;
1380       verbose = false;
1381     }
1382 
1383     const OptionDefinition *GetDefinitions() override { return g_option_table; }
1384 
1385     // Options table: Required for subclasses of Options.
1386 
1387     static OptionDefinition g_option_table[];
1388 
1389     // Instance variables to hold the values for command options.
1390 
1391     ProcessInstanceInfoMatch match_info;
1392     bool show_args;
1393     bool verbose;
1394   };
1395 
1396   CommandOptions m_options;
1397 };
1398 
1399 OptionDefinition
1400     CommandObjectPlatformProcessList::CommandOptions::g_option_table[] = {
1401         // clang-format off
1402   {LLDB_OPT_SET_1,             false, "pid",         'p', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypePid,               "List the process info for a specific process ID."},
1403   {LLDB_OPT_SET_2,             true,  "name",        'n', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeProcessName,       "Find processes with executable basenames that match a string."},
1404   {LLDB_OPT_SET_3,             true,  "ends-with",   'e', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeProcessName,       "Find processes with executable basenames that end with a string."},
1405   {LLDB_OPT_SET_4,             true,  "starts-with", 's', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeProcessName,       "Find processes with executable basenames that start with a string."},
1406   {LLDB_OPT_SET_5,             true,  "contains",    'c', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeProcessName,       "Find processes with executable basenames that contain a string."},
1407   {LLDB_OPT_SET_6,             true,  "regex",       'r', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeRegularExpression, "Find processes with executable basenames that match a regular expression."},
1408   {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."},
1409   {LLDB_OPT_SET_FROM_TO(2, 6), false, "uid",         'u', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeUnsignedInteger,   "Find processes that have a matching user ID."},
1410   {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."},
1411   {LLDB_OPT_SET_FROM_TO(2, 6), false, "gid",         'g', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeUnsignedInteger,   "Find processes that have a matching group ID."},
1412   {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."},
1413   {LLDB_OPT_SET_FROM_TO(2, 6), false, "arch",        'a', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeArchitecture,      "Find processes that have a matching architecture."},
1414   {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."},
1415   {LLDB_OPT_SET_FROM_TO(1, 6), false, "verbose",     'v', OptionParser::eNoArgument,       nullptr, nullptr, 0, eArgTypeNone,              "Enable verbose output."},
1416   {0, false, nullptr, 0, 0, nullptr, nullptr, 0, eArgTypeNone, nullptr}
1417         // clang-format on
1418 };
1419 
1420 //----------------------------------------------------------------------
1421 // "platform process info"
1422 //----------------------------------------------------------------------
1423 class CommandObjectPlatformProcessInfo : public CommandObjectParsed {
1424 public:
1425   CommandObjectPlatformProcessInfo(CommandInterpreter &interpreter)
1426       : CommandObjectParsed(
1427             interpreter, "platform process info",
1428             "Get detailed information for one or more process by process ID.",
1429             "platform process info <pid> [<pid> <pid> ...]", 0) {
1430     CommandArgumentEntry arg;
1431     CommandArgumentData pid_args;
1432 
1433     // Define the first (and only) variant of this arg.
1434     pid_args.arg_type = eArgTypePid;
1435     pid_args.arg_repetition = eArgRepeatStar;
1436 
1437     // There is only one variant this argument could be; put it into the
1438     // argument entry.
1439     arg.push_back(pid_args);
1440 
1441     // Push the data for the first argument into the m_arguments vector.
1442     m_arguments.push_back(arg);
1443   }
1444 
1445   ~CommandObjectPlatformProcessInfo() override = default;
1446 
1447 protected:
1448   bool DoExecute(Args &args, CommandReturnObject &result) override {
1449     Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get();
1450     PlatformSP platform_sp;
1451     if (target) {
1452       platform_sp = target->GetPlatform();
1453     }
1454     if (!platform_sp) {
1455       platform_sp =
1456           m_interpreter.GetDebugger().GetPlatformList().GetSelectedPlatform();
1457     }
1458 
1459     if (platform_sp) {
1460       const size_t argc = args.GetArgumentCount();
1461       if (argc > 0) {
1462         Error error;
1463 
1464         if (platform_sp->IsConnected()) {
1465           Stream &ostrm = result.GetOutputStream();
1466           bool success;
1467           for (size_t i = 0; i < argc; ++i) {
1468             const char *arg = args.GetArgumentAtIndex(i);
1469             lldb::pid_t pid = StringConvert::ToUInt32(
1470                 arg, LLDB_INVALID_PROCESS_ID, 0, &success);
1471             if (success) {
1472               ProcessInstanceInfo proc_info;
1473               if (platform_sp->GetProcessInfo(pid, proc_info)) {
1474                 ostrm.Printf("Process information for process %" PRIu64 ":\n",
1475                              pid);
1476                 proc_info.Dump(ostrm, platform_sp.get());
1477               } else {
1478                 ostrm.Printf("error: no process information is available for "
1479                              "process %" PRIu64 "\n",
1480                              pid);
1481               }
1482               ostrm.EOL();
1483             } else {
1484               result.AppendErrorWithFormat("invalid process ID argument '%s'",
1485                                            arg);
1486               result.SetStatus(eReturnStatusFailed);
1487               break;
1488             }
1489           }
1490         } else {
1491           // Not connected...
1492           result.AppendErrorWithFormat(
1493               "not connected to '%s'",
1494               platform_sp->GetPluginName().GetCString());
1495           result.SetStatus(eReturnStatusFailed);
1496         }
1497       } else {
1498         // No args
1499         result.AppendError("one or more process id(s) must be specified");
1500         result.SetStatus(eReturnStatusFailed);
1501       }
1502     } else {
1503       result.AppendError("no platform is currently selected");
1504       result.SetStatus(eReturnStatusFailed);
1505     }
1506     return result.Succeeded();
1507   }
1508 };
1509 
1510 class CommandObjectPlatformProcessAttach : public CommandObjectParsed {
1511 public:
1512   class CommandOptions : public Options {
1513   public:
1514     CommandOptions() : Options() {
1515       // Keep default values of all options in one place: OptionParsingStarting
1516       // ()
1517       OptionParsingStarting(nullptr);
1518     }
1519 
1520     ~CommandOptions() override = default;
1521 
1522     Error SetOptionValue(uint32_t option_idx, const char *option_arg,
1523                          ExecutionContext *execution_context) override {
1524       Error error;
1525       char short_option = (char)m_getopt_table[option_idx].val;
1526       bool success = false;
1527       switch (short_option) {
1528       case 'p': {
1529         lldb::pid_t pid = StringConvert::ToUInt32(
1530             option_arg, LLDB_INVALID_PROCESS_ID, 0, &success);
1531         if (!success || pid == LLDB_INVALID_PROCESS_ID) {
1532           error.SetErrorStringWithFormat("invalid process ID '%s'", option_arg);
1533         } else {
1534           attach_info.SetProcessID(pid);
1535         }
1536       } break;
1537 
1538       case 'P':
1539         attach_info.SetProcessPluginName(option_arg);
1540         break;
1541 
1542       case 'n':
1543         attach_info.GetExecutableFile().SetFile(option_arg, false);
1544         break;
1545 
1546       case 'w':
1547         attach_info.SetWaitForLaunch(true);
1548         break;
1549 
1550       default:
1551         error.SetErrorStringWithFormat("invalid short option character '%c'",
1552                                        short_option);
1553         break;
1554       }
1555       return error;
1556     }
1557 
1558     void OptionParsingStarting(ExecutionContext *execution_context) override {
1559       attach_info.Clear();
1560     }
1561 
1562     const OptionDefinition *GetDefinitions() override { return g_option_table; }
1563 
1564     bool HandleOptionArgumentCompletion(
1565         Args &input, int cursor_index, int char_pos,
1566         OptionElementVector &opt_element_vector, int opt_element_index,
1567         int match_start_point, int max_return_elements,
1568         CommandInterpreter &interpreter, bool &word_complete,
1569         StringList &matches) override {
1570       int opt_arg_pos = opt_element_vector[opt_element_index].opt_arg_pos;
1571       int opt_defs_index = opt_element_vector[opt_element_index].opt_defs_index;
1572 
1573       // We are only completing the name option for now...
1574 
1575       const OptionDefinition *opt_defs = GetDefinitions();
1576       if (opt_defs[opt_defs_index].short_option == 'n') {
1577         // Are we in the name?
1578 
1579         // Look to see if there is a -P argument provided, and if so use that
1580         // plugin, otherwise
1581         // use the default plugin.
1582 
1583         const char *partial_name = nullptr;
1584         partial_name = input.GetArgumentAtIndex(opt_arg_pos);
1585 
1586         PlatformSP platform_sp(interpreter.GetPlatform(true));
1587         if (platform_sp) {
1588           ProcessInstanceInfoList process_infos;
1589           ProcessInstanceInfoMatch match_info;
1590           if (partial_name) {
1591             match_info.GetProcessInfo().GetExecutableFile().SetFile(
1592                 partial_name, false);
1593             match_info.SetNameMatchType(eNameMatchStartsWith);
1594           }
1595           platform_sp->FindProcesses(match_info, process_infos);
1596           const uint32_t num_matches = process_infos.GetSize();
1597           if (num_matches > 0) {
1598             for (uint32_t i = 0; i < num_matches; ++i) {
1599               matches.AppendString(
1600                   process_infos.GetProcessNameAtIndex(i),
1601                   process_infos.GetProcessNameLengthAtIndex(i));
1602             }
1603           }
1604         }
1605       }
1606 
1607       return false;
1608     }
1609 
1610     // Options table: Required for subclasses of Options.
1611 
1612     static OptionDefinition g_option_table[];
1613 
1614     // Instance variables to hold the values for command options.
1615 
1616     ProcessAttachInfo attach_info;
1617   };
1618 
1619   CommandObjectPlatformProcessAttach(CommandInterpreter &interpreter)
1620       : CommandObjectParsed(interpreter, "platform process attach",
1621                             "Attach to a process.",
1622                             "platform process attach <cmd-options>"),
1623         m_options() {}
1624 
1625   ~CommandObjectPlatformProcessAttach() override = default;
1626 
1627   bool DoExecute(Args &command, CommandReturnObject &result) override {
1628     PlatformSP platform_sp(
1629         m_interpreter.GetDebugger().GetPlatformList().GetSelectedPlatform());
1630     if (platform_sp) {
1631       Error err;
1632       ProcessSP remote_process_sp = platform_sp->Attach(
1633           m_options.attach_info, m_interpreter.GetDebugger(), nullptr, err);
1634       if (err.Fail()) {
1635         result.AppendError(err.AsCString());
1636         result.SetStatus(eReturnStatusFailed);
1637       } else if (!remote_process_sp) {
1638         result.AppendError("could not attach: unknown reason");
1639         result.SetStatus(eReturnStatusFailed);
1640       } else
1641         result.SetStatus(eReturnStatusSuccessFinishResult);
1642     } else {
1643       result.AppendError("no platform is currently selected");
1644       result.SetStatus(eReturnStatusFailed);
1645     }
1646     return result.Succeeded();
1647   }
1648 
1649   Options *GetOptions() override { return &m_options; }
1650 
1651 protected:
1652   CommandOptions m_options;
1653 };
1654 
1655 OptionDefinition
1656     CommandObjectPlatformProcessAttach::CommandOptions::g_option_table[] = {
1657         // clang-format off
1658   {LLDB_OPT_SET_ALL, false, "plugin",  'P', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypePlugin,      "Name of the process plugin you want to use."},
1659   {LLDB_OPT_SET_1,   false, "pid",     'p', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypePid,         "The process ID of an existing process to attach to."},
1660   {LLDB_OPT_SET_2,   false, "name",    'n', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeProcessName, "The name of the process to attach to."},
1661   {LLDB_OPT_SET_2,   false, "waitfor", 'w', OptionParser::eNoArgument,       nullptr, nullptr, 0, eArgTypeNone,        "Wait for the process with <process-name> to launch."},
1662   {0, false, nullptr, 0, 0, nullptr, nullptr, 0, eArgTypeNone, nullptr}
1663         // clang-format on
1664 };
1665 
1666 class CommandObjectPlatformProcess : public CommandObjectMultiword {
1667 public:
1668   //------------------------------------------------------------------
1669   // Constructors and Destructors
1670   //------------------------------------------------------------------
1671   CommandObjectPlatformProcess(CommandInterpreter &interpreter)
1672       : CommandObjectMultiword(interpreter, "platform process",
1673                                "Commands to query, launch and attach to "
1674                                "processes on the current platform.",
1675                                "platform process [attach|launch|list] ...") {
1676     LoadSubCommand(
1677         "attach",
1678         CommandObjectSP(new CommandObjectPlatformProcessAttach(interpreter)));
1679     LoadSubCommand(
1680         "launch",
1681         CommandObjectSP(new CommandObjectPlatformProcessLaunch(interpreter)));
1682     LoadSubCommand("info", CommandObjectSP(new CommandObjectPlatformProcessInfo(
1683                                interpreter)));
1684     LoadSubCommand("list", CommandObjectSP(new CommandObjectPlatformProcessList(
1685                                interpreter)));
1686   }
1687 
1688   ~CommandObjectPlatformProcess() override = default;
1689 
1690 private:
1691   //------------------------------------------------------------------
1692   // For CommandObjectPlatform only
1693   //------------------------------------------------------------------
1694   DISALLOW_COPY_AND_ASSIGN(CommandObjectPlatformProcess);
1695 };
1696 
1697 //----------------------------------------------------------------------
1698 // "platform shell"
1699 //----------------------------------------------------------------------
1700 class CommandObjectPlatformShell : public CommandObjectRaw {
1701 public:
1702   class CommandOptions : public Options {
1703   public:
1704     CommandOptions() : Options(), timeout(10) {}
1705 
1706     ~CommandOptions() override = default;
1707 
1708     virtual uint32_t GetNumDefinitions() { return 1; }
1709 
1710     const OptionDefinition *GetDefinitions() override { return g_option_table; }
1711 
1712     Error SetOptionValue(uint32_t option_idx, const char *option_value,
1713                          ExecutionContext *execution_context) override {
1714       Error error;
1715 
1716       const char short_option = (char)g_option_table[option_idx].short_option;
1717 
1718       switch (short_option) {
1719       case 't': {
1720         bool success;
1721         timeout = StringConvert::ToUInt32(option_value, 10, 10, &success);
1722         if (!success)
1723           error.SetErrorStringWithFormat(
1724               "could not convert \"%s\" to a numeric value.", option_value);
1725         break;
1726       }
1727       default:
1728         error.SetErrorStringWithFormat("invalid short option character '%c'",
1729                                        short_option);
1730         break;
1731       }
1732 
1733       return error;
1734     }
1735 
1736     void OptionParsingStarting(ExecutionContext *execution_context) override {}
1737 
1738     // Options table: Required for subclasses of Options.
1739 
1740     static OptionDefinition g_option_table[];
1741     uint32_t timeout;
1742   };
1743 
1744   CommandObjectPlatformShell(CommandInterpreter &interpreter)
1745       : CommandObjectRaw(interpreter, "platform shell",
1746                          "Run a shell command on the current platform.",
1747                          "platform shell <shell-command>", 0),
1748         m_options() {}
1749 
1750   ~CommandObjectPlatformShell() override = default;
1751 
1752   Options *GetOptions() override { return &m_options; }
1753 
1754   bool DoExecute(const char *raw_command_line,
1755                  CommandReturnObject &result) override {
1756     ExecutionContext exe_ctx = GetCommandInterpreter().GetExecutionContext();
1757     m_options.NotifyOptionParsingStarting(&exe_ctx);
1758 
1759     const char *expr = nullptr;
1760 
1761     // Print out an usage syntax on an empty command line.
1762     if (raw_command_line[0] == '\0') {
1763       result.GetOutputStream().Printf("%s\n", this->GetSyntax());
1764       return true;
1765     }
1766 
1767     if (raw_command_line[0] == '-') {
1768       // We have some options and these options MUST end with --.
1769       const char *end_options = nullptr;
1770       const char *s = raw_command_line;
1771       while (s && s[0]) {
1772         end_options = ::strstr(s, "--");
1773         if (end_options) {
1774           end_options += 2; // Get past the "--"
1775           if (::isspace(end_options[0])) {
1776             expr = end_options;
1777             while (::isspace(*expr))
1778               ++expr;
1779             break;
1780           }
1781         }
1782         s = end_options;
1783       }
1784 
1785       if (end_options) {
1786         Args args(
1787             llvm::StringRef(raw_command_line, end_options - raw_command_line));
1788         if (!ParseOptions(args, result))
1789           return false;
1790       }
1791     }
1792 
1793     if (expr == nullptr)
1794       expr = raw_command_line;
1795 
1796     PlatformSP platform_sp(
1797         m_interpreter.GetDebugger().GetPlatformList().GetSelectedPlatform());
1798     Error error;
1799     if (platform_sp) {
1800       FileSpec working_dir{};
1801       std::string output;
1802       int status = -1;
1803       int signo = -1;
1804       error = (platform_sp->RunShellCommand(expr, working_dir, &status, &signo,
1805                                             &output, m_options.timeout));
1806       if (!output.empty())
1807         result.GetOutputStream().PutCString(output.c_str());
1808       if (status > 0) {
1809         if (signo > 0) {
1810           const char *signo_cstr = Host::GetSignalAsCString(signo);
1811           if (signo_cstr)
1812             result.GetOutputStream().Printf(
1813                 "error: command returned with status %i and signal %s\n",
1814                 status, signo_cstr);
1815           else
1816             result.GetOutputStream().Printf(
1817                 "error: command returned with status %i and signal %i\n",
1818                 status, signo);
1819         } else
1820           result.GetOutputStream().Printf(
1821               "error: command returned with status %i\n", status);
1822       }
1823     } else {
1824       result.GetOutputStream().Printf(
1825           "error: cannot run remote shell commands without a platform\n");
1826       error.SetErrorString(
1827           "error: cannot run remote shell commands without a platform");
1828     }
1829 
1830     if (error.Fail()) {
1831       result.AppendError(error.AsCString());
1832       result.SetStatus(eReturnStatusFailed);
1833     } else {
1834       result.SetStatus(eReturnStatusSuccessFinishResult);
1835     }
1836     return true;
1837   }
1838 
1839   CommandOptions m_options;
1840 };
1841 
1842 OptionDefinition CommandObjectPlatformShell::CommandOptions::g_option_table[] =
1843     {
1844         // clang-format off
1845   {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."},
1846   {0, false, nullptr, 0, 0, nullptr, nullptr, 0, eArgTypeNone, nullptr}
1847         // clang-format on
1848 };
1849 
1850 //----------------------------------------------------------------------
1851 // "platform install" - install a target to a remote end
1852 //----------------------------------------------------------------------
1853 class CommandObjectPlatformInstall : public CommandObjectParsed {
1854 public:
1855   CommandObjectPlatformInstall(CommandInterpreter &interpreter)
1856       : CommandObjectParsed(
1857             interpreter, "platform target-install",
1858             "Install a target (bundle or executable file) to the remote end.",
1859             "platform target-install <local-thing> <remote-sandbox>", 0) {}
1860 
1861   ~CommandObjectPlatformInstall() override = default;
1862 
1863   bool DoExecute(Args &args, CommandReturnObject &result) override {
1864     if (args.GetArgumentCount() != 2) {
1865       result.AppendError("platform target-install takes two arguments");
1866       result.SetStatus(eReturnStatusFailed);
1867       return false;
1868     }
1869     // TODO: move the bulk of this code over to the platform itself
1870     FileSpec src(args.GetArgumentAtIndex(0), true);
1871     FileSpec dst(args.GetArgumentAtIndex(1), false);
1872     if (!src.Exists()) {
1873       result.AppendError("source location does not exist or is not accessible");
1874       result.SetStatus(eReturnStatusFailed);
1875       return false;
1876     }
1877     PlatformSP platform_sp(
1878         m_interpreter.GetDebugger().GetPlatformList().GetSelectedPlatform());
1879     if (!platform_sp) {
1880       result.AppendError("no platform currently selected");
1881       result.SetStatus(eReturnStatusFailed);
1882       return false;
1883     }
1884 
1885     Error error = platform_sp->Install(src, dst);
1886     if (error.Success()) {
1887       result.SetStatus(eReturnStatusSuccessFinishNoResult);
1888     } else {
1889       result.AppendErrorWithFormat("install failed: %s", error.AsCString());
1890       result.SetStatus(eReturnStatusFailed);
1891     }
1892     return result.Succeeded();
1893   }
1894 };
1895 
1896 CommandObjectPlatform::CommandObjectPlatform(CommandInterpreter &interpreter)
1897     : CommandObjectMultiword(
1898           interpreter, "platform", "Commands to manage and create platforms.",
1899           "platform [connect|disconnect|info|list|status|select] ...") {
1900   LoadSubCommand("select",
1901                  CommandObjectSP(new CommandObjectPlatformSelect(interpreter)));
1902   LoadSubCommand("list",
1903                  CommandObjectSP(new CommandObjectPlatformList(interpreter)));
1904   LoadSubCommand("status",
1905                  CommandObjectSP(new CommandObjectPlatformStatus(interpreter)));
1906   LoadSubCommand("connect", CommandObjectSP(
1907                                 new CommandObjectPlatformConnect(interpreter)));
1908   LoadSubCommand(
1909       "disconnect",
1910       CommandObjectSP(new CommandObjectPlatformDisconnect(interpreter)));
1911   LoadSubCommand("settings", CommandObjectSP(new CommandObjectPlatformSettings(
1912                                  interpreter)));
1913   LoadSubCommand("mkdir",
1914                  CommandObjectSP(new CommandObjectPlatformMkDir(interpreter)));
1915   LoadSubCommand("file",
1916                  CommandObjectSP(new CommandObjectPlatformFile(interpreter)));
1917   LoadSubCommand("get-file", CommandObjectSP(new CommandObjectPlatformGetFile(
1918                                  interpreter)));
1919   LoadSubCommand("get-size", CommandObjectSP(new CommandObjectPlatformGetSize(
1920                                  interpreter)));
1921   LoadSubCommand("put-file", CommandObjectSP(new CommandObjectPlatformPutFile(
1922                                  interpreter)));
1923   LoadSubCommand("process", CommandObjectSP(
1924                                 new CommandObjectPlatformProcess(interpreter)));
1925   LoadSubCommand("shell",
1926                  CommandObjectSP(new CommandObjectPlatformShell(interpreter)));
1927   LoadSubCommand(
1928       "target-install",
1929       CommandObjectSP(new CommandObjectPlatformInstall(interpreter)));
1930 }
1931 
1932 CommandObjectPlatform::~CommandObjectPlatform() = default;
1933