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