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