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