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