From 799e0d966e2f091e0b66a3ef2c3f6d20311475e4 Mon Sep 17 00:00:00 2001
From: "Bryn M. Reeves" <bmr@redhat.com>
Date: Thu, 31 Jul 2014 12:16:03 +0100
Subject: [PATCH 1/4] [helpers] backport isExecutable()

---
 sos/helpers.py | 16 +++++++---------
 1 file changed, 7 insertions(+), 9 deletions(-)

diff --git a/sos/helpers.py b/sos/helpers.py
index 34e403c..d4e189e 100755
--- a/sos/helpers.py
+++ b/sos/helpers.py
@@ -38,18 +38,16 @@ def importPlugin(pluginname, name):
         return None
     return getattr(plugin, name)
 
+def isExecutable(command):
+    """Returns True if a command matches an executable on the PATH"""
+    paths = os.environ.get("PATH", "").split(os.path.pathsep)
+    candidates = [command] + [os.path.join(p, command) for p in paths]
+    return any(os.access(path, os.X_OK) for path in candidates)
+
 def sosGetCommandOutput(command, timeout = 300):
     """ Execute a command and gather stdin, stdout, and return status.
     """
-    for path in os.environ["PATH"].split(":"):
-        exists = False
-        cmdfile = command.strip("(").split()[0]
-        # handle both absolute or relative paths
-        if ( ( not os.path.isabs(cmdfile) and os.access(os.path.join(path,cmdfile), os.X_OK) ) or \
-           ( os.path.isabs(cmdfile) and os.access(cmdfile, os.X_OK) ) ):
-            exists = True
-            break
-    if not exists:
+    if not isExecutable(command.split()[0]):
         return (127, "", 0)
 
     cmd_env = os.environ
-- 
1.9.3


From e32772b8f380e32f4a0f2cafa872dd0c8b856eac Mon Sep 17 00:00:00 2001
From: "Bryn M. Reeves" <bmr@redhat.com>
Date: Thu, 31 Jul 2014 12:43:23 +0100
Subject: [PATCH 2/4] [ds] Fix addForbiddenPath() usage

---
 sos/plugins/ds.py | 14 +++++++-------
 1 file changed, 7 insertions(+), 7 deletions(-)

diff --git a/sos/plugins/ds.py b/sos/plugins/ds.py
index 7648498..72cc1b8 100644
--- a/sos/plugins/ds.py
+++ b/sos/plugins/ds.py
@@ -41,13 +41,13 @@ class ds(sos.plugintools.PluginBase):
         return False
 
     def setup(self):
-        self.add_forbidden_path("/etc/dirsrv/slapd*/pin.txt")
-        self.add_forbidden_path("/etc/dirsrv/slapd*/key3.db")
-        self.add_forbidden_path("/etc/dirsrv/slapd*/pwfile.txt")
-        self.add_forbidden_path("/etc/dirsrv/slapd*/*passw*")
-        self.add_forbidden_path("/etc/dirsrv/admin-serv/key3.db")
-        self.add_forbidden_path("/etc/dirsrv/admin-serv/admpw")
-        self.add_forbidden_path("/etc/dirsrv/admin-serv/password.conf")
+        self.addForbiddenPath("/etc/dirsrv/slapd*/pin.txt")
+        self.addForbiddenPath("/etc/dirsrv/slapd*/key3.db")
+        self.addForbiddenPath("/etc/dirsrv/slapd*/pwfile.txt")
+        self.addForbiddenPath("/etc/dirsrv/slapd*/*passw*")
+        self.addForbiddenPath("/etc/dirsrv/admin-serv/key3.db")
+        self.addForbiddenPath("/etc/dirsrv/admin-serv/admpw")
+        self.addForbiddenPath("/etc/dirsrv/admin-serv/password.conf")
         try:
             for d in os.listdir("/etc/dirsrv"):
                 if d[0:5] == 'slapd':
-- 
1.9.3


From bb3830d8eb0bbb061cc971892de781d2118e8df3 Mon Sep 17 00:00:00 2001
From: "Bryn M. Reeves" <bmr@redhat.com>
Date: Thu, 31 Jul 2014 14:37:50 +0100
Subject: [PATCH 3/4] [backport][plugin] timeout support

---
 sos/helpers.py     |  6 +++++-
 sos/plugintools.py | 19 +++++++++++++------
 2 files changed, 18 insertions(+), 7 deletions(-)

diff --git a/sos/helpers.py b/sos/helpers.py
index d4e189e..07bfeaa 100755
--- a/sos/helpers.py
+++ b/sos/helpers.py
@@ -44,18 +44,22 @@ def isExecutable(command):
     candidates = [command] + [os.path.join(p, command) for p in paths]
     return any(os.access(path, os.X_OK) for path in candidates)
 
-def sosGetCommandOutput(command, timeout = 300):
+def sosGetCommandOutput(command, timeout=30):
     """ Execute a command and gather stdin, stdout, and return status.
     """
     if not isExecutable(command.split()[0]):
         return (127, "", 0)
 
+    if timeout and isExecutable("timeout"):
+        command = "timeout %ds %s" % (timeout, command)
+
     cmd_env = os.environ
     cmd_env['LC_ALL'] = 'C'
     p = Popen(command, shell = True,
               stdout = PIPE, stderr = STDOUT,
               bufsize = -1, env = cmd_env)
     stdout, stderr = p.communicate()
+
     # hack to delete trailing '\n' added by p.communicate()
     if stdout[-1:] == '\n': stdout = stdout[:-1]
     return (p.returncode, stdout, 0)
diff --git a/sos/plugintools.py b/sos/plugintools.py
index 81cca18..2fd9fe6 100644
--- a/sos/plugintools.py
+++ b/sos/plugintools.py
@@ -389,14 +389,17 @@ class PluginBase:
             if filespec not in self.copyPaths:
                 self.copyPaths.append(filespec)
 
-    def callExtProg(self, prog):
+    def callExtProg(self, prog, timeout=30):
         """ Execute a command independantly of the output gathering part of
         sosreport
         """
         # pylint: disable-msg = W0612
-        status, shout, runtime = sosGetCommandOutput(prog)
+        status, shout, runtime = sosGetCommandOutput(prog, timeout=timeout)
         if (status == 127):
            self.soslog.info("could not run '%s'" % prog) 
+        if timeout and status == 124:
+           self.soslog.info("timeout waiting for '%s' (%ds)" % (prog, timeout)) 
+
         return (status, shout, runtime)
 
     def collectExtOutputs(self, cmds):
@@ -406,7 +409,7 @@ class PluginBase:
         for cmd in cmds:
             self.collectExtOutput(cmd)
 
-    def collectExtOutput(self, exe, suggest_filename = None, symlink = None, timeout = 300):
+    def collectExtOutput(self, exe, suggest_filename=None, symlink=None, timeout=30):
         """
         Run a program and collect the output
         """
@@ -442,7 +445,7 @@ class PluginBase:
 
         return outfn
 
-    def collectOutputNow(self, exe, suggest_filename = None, symlink = False, timeout = 300):
+    def collectOutputNow(self, exe, suggest_filename=None, symlink=False, timeout=30):
         """ Execute a command and save the output to a file for inclusion in
         the report
         """
@@ -451,7 +454,7 @@ class PluginBase:
             start_time = time()
 
         # pylint: disable-msg = W0612
-        status, shout, runtime = sosGetCommandOutput(exe, timeout = timeout)
+        status, shout, runtime = sosGetCommandOutput(exe, timeout=timeout)
 
         if suggest_filename:
             outfn = self.makeCommandFilename(suggest_filename)
@@ -481,10 +484,14 @@ class PluginBase:
             outfn_strip = outfn[len(self.cInfo['cmddir'])+1:]
 
         else:
-            self.soslog.info("could not run command: %s" % exe)
             outfn = None
             outfn_strip = None
 
+        if timeout and status == 124:
+            self.soslog.info("timeout waiting for '%s' (%ds)" % (exe, timeout))
+        if status == 127:
+            self.soslog.info("could not run command: %s" % exe)
+
         # save info for later
         self.executedCommands.append({'exe': exe, 'file':outfn_strip}) # save in our list
         self.cInfo['xmlreport'].add_command(cmdline=exe,exitcode=status,f_stdout=outfn_strip,runtime=runtime)
-- 
1.9.3


From cb7919131754f416feb498236ed6d8acf0473e3d Mon Sep 17 00:00:00 2001
From: "Bryn M. Reeves" <bmr@redhat.com>
Date: Thu, 31 Jul 2014 14:41:31 +0100
Subject: [PATCH 4/4] [rpm] reduce verify timeout to 180s and -Va timeout to
 600s

The initial rpm verify timeout was set to 3600s (one hour). This
is insane; if a verify has not completed in that time then it's
unlikely that it ever will. With the current set of verify
patterns the total time taken in rpm (including both the verify
and all other runs, e.g. policy initialisation) is under 60s on
all my test hosts (bare metal and virtual).

Allow a factor of three over this figure and set the timeout to
180s.
---
 sos/plugins/rpm.py | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/sos/plugins/rpm.py b/sos/plugins/rpm.py
index 1ff6d25..d6fa637 100644
--- a/sos/plugins/rpm.py
+++ b/sos/plugins/rpm.py
@@ -35,7 +35,7 @@ class rpm(sos.plugintools.PluginBase):
             self.collectExtOutput("/bin/rpm -qa --qf=\"%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}~~%{INSTALLTIME:date}\n\" --nosignature --nodigest|/bin/awk -F '~~' '{printf \"%-59s %s\\n\",$1,$2}'|sort", symlink = "installed-rpms")
 
         if self.getOption("rpmva"):
-            self.collectExtOutput("/bin/rpm -Va", symlink = "rpm-Va", timeout = 3600)
+            self.collectExtOutput("/bin/rpm -Va", symlink = "rpm-Va", timeout=600)
         else:
             pkgs_by_regex = self.policy().allPkgsByNameRegex
             verify_list = map(pkgs_by_regex, self.verify_list)
@@ -45,6 +45,6 @@ class rpm(sos.plugintools.PluginBase):
                     if 'debuginfo' in pkg['name'] or 'devel' in pkg['name']:
                         continue
                     verify_pkgs = "%s %s" % (verify_pkgs, pkg['name'])
-            self.collectExtOutput("rpm -V %s" % verify_pkgs)
+            self.collectExtOutput("rpm -V %s" % verify_pkgs, timeout=180)
         return
 
-- 
1.9.3

