import os
import signal
import subprocess
import threading
import time
import errors
import logger
_abort_on_error = False
def SetAbortOnError(abort=True):
"""Sets behavior of RunCommand to throw AbortError if command process returns
a negative error code"""
global _abort_on_error
_abort_on_error = abort
def RunCommand(cmd, timeout_time=None, retry_count=3, return_output=True,
stdin_input=None):
"""Spawn and retry a subprocess to run the given shell command.
Args:
cmd: shell command to run
timeout_time: time in seconds to wait for command to run before aborting.
retry_count: number of times to retry command
return_output: if True return output of command as string. Otherwise,
direct output of command to stdout.
stdin_input: data to feed to stdin
Returns:
output of command
"""
result = None
while True:
try:
result = RunOnce(cmd, timeout_time=timeout_time,
return_output=return_output, stdin_input=stdin_input)
except errors.WaitForResponseTimedOutError:
if retry_count == 0:
raise
retry_count -= 1
logger.Log("No response for %s, retrying" % cmd)
else:
return result
def RunOnce(cmd, timeout_time=None, return_output=True, stdin_input=None):
"""Spawns a subprocess to run the given shell command.
Args:
cmd: shell command to run
timeout_time: time in seconds to wait for command to run before aborting.
return_output: if True return output of command as string. Otherwise,
direct output of command to stdout.
stdin_input: data to feed to stdin
Returns:
output of command
Raises:
errors.WaitForResponseTimedOutError if command did not complete within
timeout_time seconds.
errors.AbortError is command returned error code and SetAbortOnError is on.
"""
start_time = time.time()
so = []
pid = []
global _abort_on_error, error_occurred
error_occurred = False
def Run():
global error_occurred
if return_output:
output_dest = subprocess.PIPE
else:
output_dest = None
if stdin_input:
stdin_dest = subprocess.PIPE
else:
stdin_dest = None
pipe = subprocess.Popen(
cmd,
executable='/bin/bash',
stdin=stdin_dest,
stdout=output_dest,
stderr=subprocess.STDOUT,
shell=True)
pid.append(pipe.pid)
try:
output = pipe.communicate(input=stdin_input)[0]
if output is not None and len(output) > 0:
so.append(output)
except OSError, e:
logger.SilentLog("failed to retrieve stdout from: %s" % cmd)
logger.Log(e)
so.append("ERROR")
error_occurred = True
if pipe.returncode:
logger.SilentLog("Error: %s returned %d error code" %(cmd,
pipe.returncode))
error_occurred = True
t = threading.Thread(target=Run)
t.start()
break_loop = False
while not break_loop:
if not t.isAlive():
break_loop = True
if (not break_loop and timeout_time is not None
and time.time() > start_time + timeout_time):
try:
os.kill(pid[0], signal.SIGKILL)
except OSError:
pass
logger.SilentLog("about to raise a timeout for: %s" % cmd)
raise errors.WaitForResponseTimedOutError
if not break_loop:
time.sleep(0.1)
t.join()
output = "".join(so)
if _abort_on_error and error_occurred:
raise errors.AbortError(msg=output)
return "".join(so)
def RunHostCommand(binary, valgrind=False):
"""Run a command on the host (opt using valgrind).
Runs the host binary and returns the exit code.
If successfull, the output (stdout and stderr) are discarded,
but printed in case of error.
The command can be run under valgrind in which case all the
output are always discarded.
Args:
binary: full path of the file to be run.
valgrind: If True the command will be run under valgrind.
Returns:
The command exit code (int)
"""
if not valgrind:
subproc = subprocess.Popen(binary, stdout=subprocess.PIPE,
stderr=subprocess.STDOUT)
subproc.wait()
if subproc.returncode != 0:
print subproc.communicate()[0]
return subproc.returncode
else:
subproc = subprocess.Popen(["/usr/bin/valgrind", "--tool=memcheck",
"--leak-check=yes", "-q", binary],
stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
valgrind_out = subproc.communicate()[0].strip()
if valgrind_out:
print valgrind_out
return 1
else:
return 0
def HasValgrind():
"""Check that /usr/bin/valgrind exists.
We look for the fullpath to avoid picking up 'alternative' valgrind
on the system.
Returns:
True if a system valgrind was found.
"""
return os.path.exists("/usr/bin/valgrind")