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