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