From 3b215e5c7a79c588776ba1fe439a6cfa9faa3e0f Mon Sep 17 00:00:00 2001 From: "Bryn M. Reeves" 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 --- 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