mib created this revision.
mib added reviewers: labath, JDevlieghere, jingham.
mib added a project: LLDB.
Herald added subscribers: lldb-commits, dang.
mib requested review of this revision.

This patch adds the ability to use a custom interpreter with the
`platform shell` command. If the user enables the `-i|--interpreter`
option, and passes it the path to a binary, lldb will prepend that path
before the rest of the command and set a flag so the host interpreter
isn't fetched.

The `Platform::RunShellCommand` method already had a default parameter
`m_run_in_default_shell`, so it was mostly a matter of hooking up the
the CommandObject flag to the method parameter.

rdar://67759256

Signed-off-by: Med Ismail Bennani <medismail.benn...@gmail.com>


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D86667

Files:
  lldb/bindings/interface/SBPlatform.i
  lldb/include/lldb/API/SBPlatform.h
  lldb/include/lldb/Target/Platform.h
  lldb/include/lldb/Target/RemoteAwarePlatform.h
  lldb/source/API/SBPlatform.cpp
  lldb/source/Commands/CommandObjectPlatform.cpp
  lldb/source/Commands/Options.td
  lldb/source/Plugins/Platform/gdb-server/PlatformRemoteGDBServer.cpp
  lldb/source/Plugins/Platform/gdb-server/PlatformRemoteGDBServer.h
  lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp
  lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h
  lldb/source/Target/Platform.cpp
  lldb/source/Target/RemoteAwarePlatform.cpp
  lldb/test/API/commands/platform/basic/TestPlatformCommand.py

Index: lldb/test/API/commands/platform/basic/TestPlatformCommand.py
===================================================================
--- lldb/test/API/commands/platform/basic/TestPlatformCommand.py
+++ lldb/test/API/commands/platform/basic/TestPlatformCommand.py
@@ -92,3 +92,24 @@
                     "error: timed out waiting for shell command to complete"])
         self.expect("shell -t 1 --  sleep 3", error=True, substrs=[
                     "error: timed out waiting for shell command to complete"])
+
+    @expectedFailureAll(oslist=["windows"])
+    @no_debug_info_test
+    def test_shell_interpreter(self):
+        """ Test a shell with a different interpreter """
+        platform = self.dbg.GetSelectedPlatform()
+        self.assertTrue(platform.IsValid())
+
+        sh_cmd = lldb.SBPlatformShellCommand('/bin/sh', 'echo $0')
+        self.assertIn('/bin/sh', sh_cmd.GetCommand())
+        self.assertIn('echo $0', sh_cmd.GetCommand())
+
+        err = platform.Run(sh_cmd)
+        self.assertTrue(err.Success())
+        self.assertIn("/bin/sh", sh_cmd.GetOutput())
+
+    @expectedFailureAll(oslist=["windows"])
+    @no_debug_info_test
+    def test_shell_interpreter(self):
+        """ Test a shell with a different interpreter """
+        self.expect("platform shell -h -i /bin/sh -- 'echo $0'", substrs=['/bin/sh'])
Index: lldb/source/Target/RemoteAwarePlatform.cpp
===================================================================
--- lldb/source/Target/RemoteAwarePlatform.cpp
+++ lldb/source/Target/RemoteAwarePlatform.cpp
@@ -170,16 +170,19 @@
   return error;
 }
 
-Status RemoteAwarePlatform::RunShellCommand(
-    const char *command, const FileSpec &working_dir, int *status_ptr,
-    int *signo_ptr, std::string *command_output,
-    const Timeout<std::micro> &timeout) {
+Status RemoteAwarePlatform::RunShellCommand(const char *command,
+                                            const FileSpec &working_dir,
+                                            int *status_ptr, int *signo_ptr,
+                                            std::string *command_output,
+                                            const Timeout<std::micro> &timeout,
+                                            const bool run_in_default_shell) {
   if (IsHost())
     return Host::RunShellCommand(command, working_dir, status_ptr, signo_ptr,
-                                 command_output, timeout);
+                                 command_output, timeout, run_in_default_shell);
   if (m_remote_platform_sp)
     return m_remote_platform_sp->RunShellCommand(
-        command, working_dir, status_ptr, signo_ptr, command_output, timeout);
+        command, working_dir, status_ptr, signo_ptr, command_output, timeout,
+        run_in_default_shell);
   return Status("unable to run a remote command without a platform");
 }
 
Index: lldb/source/Target/Platform.cpp
===================================================================
--- lldb/source/Target/Platform.cpp
+++ lldb/source/Target/Platform.cpp
@@ -1327,10 +1327,10 @@
                     // process to exit
     std::string
         *command_output, // Pass nullptr if you don't want the command output
-    const Timeout<std::micro> &timeout) {
+    const Timeout<std::micro> &timeout, const bool run_in_default_shell) {
   if (IsHost())
     return Host::RunShellCommand(command, working_dir, status_ptr, signo_ptr,
-                                 command_output, timeout);
+                                 command_output, timeout, run_in_default_shell);
   else
     return Status("unimplemented");
 }
Index: lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h
===================================================================
--- lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h
+++ lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h
@@ -407,7 +407,8 @@
                        // the process to exit
       std::string
           *command_output, // Pass nullptr if you don't want the command output
-      const Timeout<std::micro> &timeout);
+      const Timeout<std::micro> &timeout,
+      const bool run_in_default_shell = true);
 
   bool CalculateMD5(const FileSpec &file_spec, uint64_t &high, uint64_t &low);
 
Index: lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp
===================================================================
--- lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp
+++ lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp
@@ -2825,7 +2825,7 @@
                      // process to exit
     std::string
         *command_output, // Pass NULL if you don't want the command output
-    const Timeout<std::micro> &timeout) {
+    const Timeout<std::micro> &timeout, const bool run_in_default_shell) {
   lldb_private::StreamString stream;
   stream.PutCString("qPlatform_shell:");
   stream.PutBytesAsRawHex8(command, strlen(command));
Index: lldb/source/Plugins/Platform/gdb-server/PlatformRemoteGDBServer.h
===================================================================
--- lldb/source/Plugins/Platform/gdb-server/PlatformRemoteGDBServer.h
+++ lldb/source/Plugins/Platform/gdb-server/PlatformRemoteGDBServer.h
@@ -148,7 +148,8 @@
                        // process to exit
       std::string
           *command_output, // Pass NULL if you don't want the command output
-      const lldb_private::Timeout<std::micro> &timeout) override;
+      const lldb_private::Timeout<std::micro> &timeout,
+      const bool run_in_default_shell = true) override;
 
   void CalculateTrapHandlerSymbolNames() override;
 
Index: lldb/source/Plugins/Platform/gdb-server/PlatformRemoteGDBServer.cpp
===================================================================
--- lldb/source/Plugins/Platform/gdb-server/PlatformRemoteGDBServer.cpp
+++ lldb/source/Plugins/Platform/gdb-server/PlatformRemoteGDBServer.cpp
@@ -719,9 +719,10 @@
                      // process to exit
     std::string
         *command_output, // Pass NULL if you don't want the command output
-    const Timeout<std::micro> &timeout) {
+    const Timeout<std::micro> &timeout, const bool run_in_default_shell) {
   return m_gdb_client.RunShellCommand(command, working_dir, status_ptr,
-                                      signo_ptr, command_output, timeout);
+                                      signo_ptr, command_output, timeout,
+                                      run_in_default_shell);
 }
 
 void PlatformRemoteGDBServer::CalculateTrapHandlerSymbolNames() {
Index: lldb/source/Commands/Options.td
===================================================================
--- lldb/source/Commands/Options.td
+++ lldb/source/Commands/Options.td
@@ -631,6 +631,8 @@
     Desc<"Run the commands on the host shell when enabled.">;
   def platform_shell_timeout : Option<"timeout", "t">, Arg<"Value">,
     Desc<"Seconds to wait for the remote host to finish running the command.">;
+  def platform_shell_interpreter : Option<"interpreter", "i">, Arg<"Path">,
+    Desc<"Shell interpreter path. This is the binary used to run the command.">;
 }
 
 let Command = "process attach" in {
Index: lldb/source/Commands/CommandObjectPlatform.cpp
===================================================================
--- lldb/source/Commands/CommandObjectPlatform.cpp
+++ lldb/source/Commands/CommandObjectPlatform.cpp
@@ -1611,6 +1611,29 @@
         else
           m_timeout = std::chrono::seconds(timeout_sec);
         break;
+      case 'i': {
+        if (option_arg.empty()) {
+          error.SetErrorStringWithFormat(
+              "missing shell interpreter path for option -i|--interpreter.");
+          return error;
+        }
+
+        if (!FileSystem::Instance().Exists(option_arg)) {
+          error.SetErrorStringWithFormat("File \"%s\" doesn't exist.",
+                                         option_arg.str().c_str());
+          return error;
+        }
+
+        if (!FileSystem::Instance().Readable(option_arg)) {
+          error.SetErrorStringWithFormat("File \"%s\" is not readable.",
+                                         option_arg.str().c_str());
+          return error;
+        }
+
+        m_shell_interpreter.SetFile(option_arg, FileSpec::Style::native);
+
+        break;
+      }
       default:
         llvm_unreachable("Unimplemented option");
       }
@@ -1621,10 +1644,12 @@
     void OptionParsingStarting(ExecutionContext *execution_context) override {
       m_timeout.reset();
       m_use_host_platform = false;
+      m_shell_interpreter.Clear();
     }
 
     Timeout<std::micro> m_timeout = std::chrono::seconds(10);
     bool m_use_host_platform;
+    FileSpec m_shell_interpreter;
   };
 
   CommandObjectPlatformShell(CommandInterpreter &interpreter)
@@ -1650,7 +1675,6 @@
 
     const bool is_alias = !raw_command_line.contains("platform");
     OptionsWithRaw args(raw_command_line);
-    const char *expr = args.GetRawPart().c_str();
 
     if (args.HasArgs())
       if (!ParseOptions(args.GetArgs(), result))
@@ -1662,6 +1686,14 @@
       return false;
     }
 
+    std::string expr;
+    bool use_default_shell = true;
+    if (m_options.m_shell_interpreter) {
+      expr += m_options.m_shell_interpreter.GetPath() + " -c ";
+      use_default_shell = false;
+    }
+    expr += args.GetRawPart();
+
     PlatformSP platform_sp(
         m_options.m_use_host_platform
             ? Platform::GetHostPlatform()
@@ -1672,8 +1704,9 @@
       std::string output;
       int status = -1;
       int signo = -1;
-      error = (platform_sp->RunShellCommand(expr, working_dir, &status, &signo,
-                                            &output, m_options.m_timeout));
+      error = (platform_sp->RunShellCommand(
+          expr.c_str(), working_dir, &status, &signo, &output,
+          m_options.m_timeout, use_default_shell));
       if (!output.empty())
         result.GetOutputStream().PutCString(output);
       if (status > 0) {
Index: lldb/source/API/SBPlatform.cpp
===================================================================
--- lldb/source/API/SBPlatform.cpp
+++ lldb/source/API/SBPlatform.cpp
@@ -50,8 +50,31 @@
 
 // PlatformShellCommand
 struct PlatformShellCommand {
+  PlatformShellCommand(const char *command_interpreter,
+                       const char *shell_command)
+      : m_command(), m_working_dir(), m_status(0), m_signo(0),
+        m_run_in_default_shell(false) {
+    std::string full_command;
+
+    if (command_interpreter && command_interpreter[0]) {
+      full_command += command_interpreter;
+      full_command += " -c ";
+    }
+
+    if (shell_command && shell_command[0]) {
+      full_command += " \"";
+      full_command += shell_command;
+      full_command += "\"";
+    }
+
+    if (!full_command.empty()) {
+      m_command = full_command.c_str();
+    }
+  }
+
   PlatformShellCommand(const char *shell_command = nullptr)
-      : m_command(), m_working_dir(), m_status(0), m_signo(0) {
+      : m_command(), m_working_dir(), m_status(0), m_signo(0),
+        m_run_in_default_shell(true) {
     if (shell_command && shell_command[0])
       m_command = shell_command;
   }
@@ -64,6 +87,7 @@
   int m_status;
   int m_signo;
   Timeout<std::ratio<1>> m_timeout = llvm::None;
+  bool m_run_in_default_shell;
 };
 // SBPlatformConnectOptions
 SBPlatformConnectOptions::SBPlatformConnectOptions(const char *url)
@@ -163,6 +187,13 @@
 }
 
 // SBPlatformShellCommand
+SBPlatformShellCommand::SBPlatformShellCommand(const char *shell_interpreter,
+                                               const char *shell_command)
+    : m_opaque_ptr(new PlatformShellCommand(shell_interpreter, shell_command)) {
+  LLDB_RECORD_CONSTRUCTOR(SBPlatformShellCommand, (const char *, const char *),
+                          shell_interpreter, shell_command);
+}
+
 SBPlatformShellCommand::SBPlatformShellCommand(const char *shell_command)
     : m_opaque_ptr(new PlatformShellCommand(shell_command)) {
   LLDB_RECORD_CONSTRUCTOR(SBPlatformShellCommand, (const char *),
@@ -557,11 +588,12 @@
       if (working_dir)
         shell_command.SetWorkingDirectory(working_dir);
     }
-    return platform_sp->RunShellCommand(command, FileSpec(working_dir),
-                                        &shell_command.m_opaque_ptr->m_status,
-                                        &shell_command.m_opaque_ptr->m_signo,
-                                        &shell_command.m_opaque_ptr->m_output,
-                                        shell_command.m_opaque_ptr->m_timeout);
+    return platform_sp->RunShellCommand(
+        command, FileSpec(working_dir), &shell_command.m_opaque_ptr->m_status,
+        &shell_command.m_opaque_ptr->m_signo,
+        &shell_command.m_opaque_ptr->m_output,
+        shell_command.m_opaque_ptr->m_timeout,
+        shell_command.m_opaque_ptr->m_run_in_default_shell);
   }));
 }
 
Index: lldb/include/lldb/Target/RemoteAwarePlatform.h
===================================================================
--- lldb/include/lldb/Target/RemoteAwarePlatform.h
+++ lldb/include/lldb/Target/RemoteAwarePlatform.h
@@ -71,7 +71,8 @@
   Status RunShellCommand(const char *command, const FileSpec &working_dir,
                          int *status_ptr, int *signo_ptr,
                          std::string *command_output,
-                         const Timeout<std::micro> &timeout) override;
+                         const Timeout<std::micro> &timeout,
+                         const bool run_in_default_shell = true) override;
 
   const char *GetHostname() override;
   UserIDResolver &GetUserIDResolver() override;
Index: lldb/include/lldb/Target/Platform.h
===================================================================
--- lldb/include/lldb/Target/Platform.h
+++ lldb/include/lldb/Target/Platform.h
@@ -629,7 +629,8 @@
                        // the process to exit
       std::string
           *command_output, // Pass nullptr if you don't want the command output
-      const Timeout<std::micro> &timeout);
+      const Timeout<std::micro> &timeout,
+      const bool run_in_default_shell = true);
 
   virtual void SetLocalCacheDirectory(const char *local);
 
Index: lldb/include/lldb/API/SBPlatform.h
===================================================================
--- lldb/include/lldb/API/SBPlatform.h
+++ lldb/include/lldb/API/SBPlatform.h
@@ -51,6 +51,8 @@
 
 class LLDB_API SBPlatformShellCommand {
 public:
+  SBPlatformShellCommand(const char *shell_interpreter,
+                         const char *shell_command);
   SBPlatformShellCommand(const char *shell_command);
 
   SBPlatformShellCommand(const SBPlatformShellCommand &rhs);
Index: lldb/bindings/interface/SBPlatform.i
===================================================================
--- lldb/bindings/interface/SBPlatform.i
+++ lldb/bindings/interface/SBPlatform.i
@@ -45,6 +45,7 @@
 class SBPlatformShellCommand
 {
 public:
+    SBPlatformShellCommand (const char *shell_interpreter, const char *shell_command);
     SBPlatformShellCommand (const char *shell_command);
 
     SBPlatformShellCommand (const SBPlatformShellCommand &rhs);
_______________________________________________
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to