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