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