96
def run_bounded(seconds, command, **kwargs):
97
# Fork first, to make sure that the controlling process only has a
98
# single child to worry about.
99
control_pid = os.fork()
101
waitpid_retry(control_pid, 0)
104
def sigchld_handler(signum, frame):
105
_, status = os.waitpid(-1, 0)
106
if os.WIFEXITED(status):
109
logger.error("child exited with signal %d" % os.WTERMSIG(status))
110
os._exit(os.WTERMSIG(status) + 128)
112
old_sigchld = signal.signal(signal.SIGCHLD, sigchld_handler)
115
signal.signal(signal.SIGCHLD, old_sigchld)
118
subp = subprocess.Popen(command, preexec_fn=preexec, **kwargs)
120
# prevent race by setting process group on either side; cf. Stevens 1993
122
os.setpgid(subp.pid, 0)
124
pass # may fail if the child has already execed
126
def sigalrm_handler(signum, frame):
127
logger.error("%s took too long, terminating ..." % command[0])
128
os.kill(-subp.pid, signal.SIGTERM)
130
signal.signal(signal.SIGALRM, sigalrm_handler)
131
signal.setitimer(signal.ITIMER_REAL, seconds)
137
97
class FetchError(Exception):
138
98
"""An attempt to fetch a file from a remote system failed."""
167
127
raise FetchError("%s returned %d" % (command_str, ret))
170
def shell_escape(arg):
171
if re.match(r"^[a-zA-Z0-9+,./:=@_-]+$", arg):
174
return "'%s'" % arg.replace("'", "'\\''")
177
130
def _read_nullsep_output(command):
178
131
raw = subprocess.Popen(
179
132
command, stdout=subprocess.PIPE,
191
144
def read_shell_config(config_path=None, whitelisted_keys=[]):
193
146
if config_path is not None:
194
commands.append(". %s" % shell_escape(config_path))
147
commands.append(". %s" % shell_quote(config_path))
195
148
commands.append("cat /proc/self/environ")
196
149
for key in whitelisted_keys:
200
153
env = _read_nullsep_output(["sh", "-c", "; ".join(commands)])
201
154
for key, value in env.items():
163
if e.errno == errno.ESRCH: