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