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