Blob Blame History Raw
From 3b215e5c7a79c588776ba1fe439a6cfa9faa3e0f Mon Sep 17 00:00:00 2001
From: "Bryn M. Reeves" <bmr@redhat.com>
Date: Sun, 25 Jan 2015 14:20:10 +0000
Subject: [PATCH 56/93] [utilities] add chroot support to
 sos_get_command_output()

Allow callers of sos_get_command_output() to specify a path to
chroot into before executing command.

Signed-off-by: Bryn M. Reeves <bmr@redhat.com>
---
 sos/plugins/__init__.py  |  2 +-
 sos/utilities.py         | 26 +++++++++++++++-----------
 tests/utilities_tests.py |  5 +++++
 3 files changed, 21 insertions(+), 12 deletions(-)

diff --git a/sos/plugins/__init__.py b/sos/plugins/__init__.py
index 14136b4..3f8f628 100644
--- a/sos/plugins/__init__.py
+++ b/sos/plugins/__init__.py
@@ -493,7 +493,7 @@ class Plugin(object):
             self._log_info("added copyspec '%s'" % copy_paths)
 
     def get_command_output(self, prog, timeout=300, runat=None):
-        result = sos_get_command_output(prog, timeout=timeout, runat=runat)
+        result = sos_get_command_output(prog, timeout=timeout, chdir=runat)
         if result['status'] == 124:
             self._log_warn("command '%s' timed out after %ds"
                            % (prog, timeout))
diff --git a/sos/utilities.py b/sos/utilities.py
index 51909c6..cc25a8f 100644
--- a/sos/utilities.py
+++ b/sos/utilities.py
@@ -120,15 +120,19 @@ def is_executable(command):
     return any(os.access(path, os.X_OK) for path in candidates)
 
 
-def sos_get_command_output(command, timeout=300, runat=None):
-    """Execute a command through the system shell. First checks to see if the
-    requested command is executable. Returns (returncode, stdout, 0)"""
-    def _child_chdir():
-        if(runat):
-            try:
-                os.chdir(runat)
-            except:
-                self.log_error("failed to chdir to '%s'" % runat)
+def sos_get_command_output(command, timeout=300, chroot=None, chdir=None):
+    """Execute a command and return a dictionary of status and output,
+    optionally changing root or current working directory before
+    executing command.
+    """
+    # Change root or cwd for child only. Exceptions in the prexec_fn
+    # closure are caught in the parent (chroot and chdir are bound from
+    # the enclosing scope).
+    def _child_prep_fn():
+        if (chroot):
+                os.chroot(chroot)
+        if (chdir):
+                os.chdir(chdir)
 
     cmd_env = os.environ
     # ensure consistent locale for collected command output
@@ -144,7 +148,7 @@ def sos_get_command_output(command, timeout=300, runat=None):
     try:
         p = Popen(args, shell=False, stdout=PIPE, stderr=STDOUT,
                   bufsize=-1, env=cmd_env, close_fds=True,
-                  preexec_fn=_child_chdir)
+                  preexec_fn=_child_prep_fn)
     except OSError as e:
         if e.errno == errno.ENOENT:
             return {'status': 127, 'output': ""}
@@ -184,7 +188,7 @@ def shell_out(cmd, runat=None):
     """Shell out to an external command and return the output or the empty
     string in case of error.
     """
-    return sos_get_command_output(cmd, runat=runat)['output']
+    return sos_get_command_output(cmd, chdir=runat)['output']
 
 
 class ImporterHelper(object):
diff --git a/tests/utilities_tests.py b/tests/utilities_tests.py
index 60087c1..def3aed 100644
--- a/tests/utilities_tests.py
+++ b/tests/utilities_tests.py
@@ -68,6 +68,11 @@ class ExecutableTest(unittest.TestCase):
         self.assertEquals(result['status'], 127)
         self.assertEquals(result['output'], "")
 
+    def test_output_chdir(self):
+        result = sos_get_command_output("/usr/bin/pwd", chdir=TEST_DIR)
+        self.assertEquals(result['status'], 0)
+        self.assertEquals(result['output'].strip(), TEST_DIR)
+
     def test_shell_out(self):
         path = os.path.join(TEST_DIR, 'test_exe.py')
         self.assertEquals("executed\n", shell_out(path))
-- 
1.9.3