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