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