root / lib / utils / process.py @ 1ce03fb1
History | View | Annotate | Download (28.7 kB)
1 | a4ccecf6 | Michael Hanselmann | #
|
---|---|---|---|
2 | a4ccecf6 | Michael Hanselmann | #
|
3 | a4ccecf6 | Michael Hanselmann | |
4 | a4ccecf6 | Michael Hanselmann | # Copyright (C) 2006, 2007, 2010, 2011 Google Inc.
|
5 | a4ccecf6 | Michael Hanselmann | #
|
6 | a4ccecf6 | Michael Hanselmann | # This program is free software; you can redistribute it and/or modify
|
7 | a4ccecf6 | Michael Hanselmann | # it under the terms of the GNU General Public License as published by
|
8 | a4ccecf6 | Michael Hanselmann | # the Free Software Foundation; either version 2 of the License, or
|
9 | a4ccecf6 | Michael Hanselmann | # (at your option) any later version.
|
10 | a4ccecf6 | Michael Hanselmann | #
|
11 | a4ccecf6 | Michael Hanselmann | # This program is distributed in the hope that it will be useful, but
|
12 | a4ccecf6 | Michael Hanselmann | # WITHOUT ANY WARRANTY; without even the implied warranty of
|
13 | a4ccecf6 | Michael Hanselmann | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
14 | a4ccecf6 | Michael Hanselmann | # General Public License for more details.
|
15 | a4ccecf6 | Michael Hanselmann | #
|
16 | a4ccecf6 | Michael Hanselmann | # You should have received a copy of the GNU General Public License
|
17 | a4ccecf6 | Michael Hanselmann | # along with this program; if not, write to the Free Software
|
18 | a4ccecf6 | Michael Hanselmann | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
19 | a4ccecf6 | Michael Hanselmann | # 02110-1301, USA.
|
20 | a4ccecf6 | Michael Hanselmann | |
21 | a4ccecf6 | Michael Hanselmann | """Utility functions for processes.
|
22 | a4ccecf6 | Michael Hanselmann |
|
23 | a4ccecf6 | Michael Hanselmann | """
|
24 | a4ccecf6 | Michael Hanselmann | |
25 | a4ccecf6 | Michael Hanselmann | |
26 | a4ccecf6 | Michael Hanselmann | import os |
27 | a4ccecf6 | Michael Hanselmann | import sys |
28 | a4ccecf6 | Michael Hanselmann | import subprocess |
29 | a4ccecf6 | Michael Hanselmann | import errno |
30 | a4ccecf6 | Michael Hanselmann | import select |
31 | a4ccecf6 | Michael Hanselmann | import logging |
32 | a4ccecf6 | Michael Hanselmann | import signal |
33 | a4ccecf6 | Michael Hanselmann | import resource |
34 | a4ccecf6 | Michael Hanselmann | |
35 | a4ccecf6 | Michael Hanselmann | from cStringIO import StringIO |
36 | a4ccecf6 | Michael Hanselmann | |
37 | a4ccecf6 | Michael Hanselmann | from ganeti import errors |
38 | a4ccecf6 | Michael Hanselmann | from ganeti import constants |
39 | 110f49ef | Michael Hanselmann | from ganeti import compat |
40 | a4ccecf6 | Michael Hanselmann | |
41 | a4ccecf6 | Michael Hanselmann | from ganeti.utils import retry as utils_retry |
42 | a4ccecf6 | Michael Hanselmann | from ganeti.utils import wrapper as utils_wrapper |
43 | a4ccecf6 | Michael Hanselmann | from ganeti.utils import text as utils_text |
44 | a4ccecf6 | Michael Hanselmann | from ganeti.utils import io as utils_io |
45 | a4ccecf6 | Michael Hanselmann | from ganeti.utils import algo as utils_algo |
46 | a4ccecf6 | Michael Hanselmann | |
47 | a4ccecf6 | Michael Hanselmann | |
48 | a4ccecf6 | Michael Hanselmann | #: when set to True, L{RunCmd} is disabled
|
49 | a4ccecf6 | Michael Hanselmann | _no_fork = False
|
50 | a4ccecf6 | Michael Hanselmann | |
51 | a4ccecf6 | Michael Hanselmann | (_TIMEOUT_NONE, |
52 | a4ccecf6 | Michael Hanselmann | _TIMEOUT_TERM, |
53 | a4ccecf6 | Michael Hanselmann | _TIMEOUT_KILL) = range(3) |
54 | a4ccecf6 | Michael Hanselmann | |
55 | a4ccecf6 | Michael Hanselmann | |
56 | a4ccecf6 | Michael Hanselmann | def DisableFork(): |
57 | a4ccecf6 | Michael Hanselmann | """Disables the use of fork(2).
|
58 | a4ccecf6 | Michael Hanselmann |
|
59 | a4ccecf6 | Michael Hanselmann | """
|
60 | a4ccecf6 | Michael Hanselmann | global _no_fork # pylint: disable-msg=W0603 |
61 | a4ccecf6 | Michael Hanselmann | |
62 | a4ccecf6 | Michael Hanselmann | _no_fork = True
|
63 | a4ccecf6 | Michael Hanselmann | |
64 | a4ccecf6 | Michael Hanselmann | |
65 | a4ccecf6 | Michael Hanselmann | class RunResult(object): |
66 | a4ccecf6 | Michael Hanselmann | """Holds the result of running external programs.
|
67 | a4ccecf6 | Michael Hanselmann |
|
68 | a4ccecf6 | Michael Hanselmann | @type exit_code: int
|
69 | a4ccecf6 | Michael Hanselmann | @ivar exit_code: the exit code of the program, or None (if the program
|
70 | a4ccecf6 | Michael Hanselmann | didn't exit())
|
71 | a4ccecf6 | Michael Hanselmann | @type signal: int or None
|
72 | a4ccecf6 | Michael Hanselmann | @ivar signal: the signal that caused the program to finish, or None
|
73 | a4ccecf6 | Michael Hanselmann | (if the program wasn't terminated by a signal)
|
74 | a4ccecf6 | Michael Hanselmann | @type stdout: str
|
75 | a4ccecf6 | Michael Hanselmann | @ivar stdout: the standard output of the program
|
76 | a4ccecf6 | Michael Hanselmann | @type stderr: str
|
77 | a4ccecf6 | Michael Hanselmann | @ivar stderr: the standard error of the program
|
78 | a4ccecf6 | Michael Hanselmann | @type failed: boolean
|
79 | a4ccecf6 | Michael Hanselmann | @ivar failed: True in case the program was
|
80 | a4ccecf6 | Michael Hanselmann | terminated by a signal or exited with a non-zero exit code
|
81 | a4ccecf6 | Michael Hanselmann | @ivar fail_reason: a string detailing the termination reason
|
82 | a4ccecf6 | Michael Hanselmann |
|
83 | a4ccecf6 | Michael Hanselmann | """
|
84 | a4ccecf6 | Michael Hanselmann | __slots__ = ["exit_code", "signal", "stdout", "stderr", |
85 | a4ccecf6 | Michael Hanselmann | "failed", "fail_reason", "cmd"] |
86 | a4ccecf6 | Michael Hanselmann | |
87 | a4ccecf6 | Michael Hanselmann | |
88 | a4ccecf6 | Michael Hanselmann | def __init__(self, exit_code, signal_, stdout, stderr, cmd, timeout_action, |
89 | a4ccecf6 | Michael Hanselmann | timeout): |
90 | a4ccecf6 | Michael Hanselmann | self.cmd = cmd
|
91 | a4ccecf6 | Michael Hanselmann | self.exit_code = exit_code
|
92 | a4ccecf6 | Michael Hanselmann | self.signal = signal_
|
93 | a4ccecf6 | Michael Hanselmann | self.stdout = stdout
|
94 | a4ccecf6 | Michael Hanselmann | self.stderr = stderr
|
95 | a4ccecf6 | Michael Hanselmann | self.failed = (signal_ is not None or exit_code != 0) |
96 | a4ccecf6 | Michael Hanselmann | |
97 | a4ccecf6 | Michael Hanselmann | fail_msgs = [] |
98 | a4ccecf6 | Michael Hanselmann | if self.signal is not None: |
99 | a4ccecf6 | Michael Hanselmann | fail_msgs.append("terminated by signal %s" % self.signal) |
100 | a4ccecf6 | Michael Hanselmann | elif self.exit_code is not None: |
101 | a4ccecf6 | Michael Hanselmann | fail_msgs.append("exited with exit code %s" % self.exit_code) |
102 | a4ccecf6 | Michael Hanselmann | else:
|
103 | a4ccecf6 | Michael Hanselmann | fail_msgs.append("unable to determine termination reason")
|
104 | a4ccecf6 | Michael Hanselmann | |
105 | a4ccecf6 | Michael Hanselmann | if timeout_action == _TIMEOUT_TERM:
|
106 | a4ccecf6 | Michael Hanselmann | fail_msgs.append("terminated after timeout of %.2f seconds" % timeout)
|
107 | a4ccecf6 | Michael Hanselmann | elif timeout_action == _TIMEOUT_KILL:
|
108 | a4ccecf6 | Michael Hanselmann | fail_msgs.append(("force termination after timeout of %.2f seconds"
|
109 | a4ccecf6 | Michael Hanselmann | " and linger for another %.2f seconds") %
|
110 | a4ccecf6 | Michael Hanselmann | (timeout, constants.CHILD_LINGER_TIMEOUT)) |
111 | a4ccecf6 | Michael Hanselmann | |
112 | a4ccecf6 | Michael Hanselmann | if fail_msgs and self.failed: |
113 | a4ccecf6 | Michael Hanselmann | self.fail_reason = utils_text.CommaJoin(fail_msgs)
|
114 | a4ccecf6 | Michael Hanselmann | |
115 | a4ccecf6 | Michael Hanselmann | if self.failed: |
116 | a4ccecf6 | Michael Hanselmann | logging.debug("Command '%s' failed (%s); output: %s",
|
117 | a4ccecf6 | Michael Hanselmann | self.cmd, self.fail_reason, self.output) |
118 | a4ccecf6 | Michael Hanselmann | |
119 | a4ccecf6 | Michael Hanselmann | def _GetOutput(self): |
120 | a4ccecf6 | Michael Hanselmann | """Returns the combined stdout and stderr for easier usage.
|
121 | a4ccecf6 | Michael Hanselmann |
|
122 | a4ccecf6 | Michael Hanselmann | """
|
123 | a4ccecf6 | Michael Hanselmann | return self.stdout + self.stderr |
124 | a4ccecf6 | Michael Hanselmann | |
125 | a4ccecf6 | Michael Hanselmann | output = property(_GetOutput, None, None, "Return full output") |
126 | a4ccecf6 | Michael Hanselmann | |
127 | a4ccecf6 | Michael Hanselmann | |
128 | a4ccecf6 | Michael Hanselmann | def _BuildCmdEnvironment(env, reset): |
129 | a4ccecf6 | Michael Hanselmann | """Builds the environment for an external program.
|
130 | a4ccecf6 | Michael Hanselmann |
|
131 | a4ccecf6 | Michael Hanselmann | """
|
132 | a4ccecf6 | Michael Hanselmann | if reset:
|
133 | a4ccecf6 | Michael Hanselmann | cmd_env = {} |
134 | a4ccecf6 | Michael Hanselmann | else:
|
135 | a4ccecf6 | Michael Hanselmann | cmd_env = os.environ.copy() |
136 | a4ccecf6 | Michael Hanselmann | cmd_env["LC_ALL"] = "C" |
137 | a4ccecf6 | Michael Hanselmann | |
138 | a4ccecf6 | Michael Hanselmann | if env is not None: |
139 | a4ccecf6 | Michael Hanselmann | cmd_env.update(env) |
140 | a4ccecf6 | Michael Hanselmann | |
141 | a4ccecf6 | Michael Hanselmann | return cmd_env
|
142 | a4ccecf6 | Michael Hanselmann | |
143 | a4ccecf6 | Michael Hanselmann | |
144 | a4ccecf6 | Michael Hanselmann | def RunCmd(cmd, env=None, output=None, cwd="/", reset_env=False, |
145 | d6491981 | Renรฉ Nussbaumer | interactive=False, timeout=None, noclose_fds=None, |
146 | d6491981 | Renรฉ Nussbaumer | _postfork_fn=None):
|
147 | a4ccecf6 | Michael Hanselmann | """Execute a (shell) command.
|
148 | a4ccecf6 | Michael Hanselmann |
|
149 | a4ccecf6 | Michael Hanselmann | The command should not read from its standard input, as it will be
|
150 | a4ccecf6 | Michael Hanselmann | closed.
|
151 | a4ccecf6 | Michael Hanselmann |
|
152 | a4ccecf6 | Michael Hanselmann | @type cmd: string or list
|
153 | a4ccecf6 | Michael Hanselmann | @param cmd: Command to run
|
154 | a4ccecf6 | Michael Hanselmann | @type env: dict
|
155 | a4ccecf6 | Michael Hanselmann | @param env: Additional environment variables
|
156 | a4ccecf6 | Michael Hanselmann | @type output: str
|
157 | a4ccecf6 | Michael Hanselmann | @param output: if desired, the output of the command can be
|
158 | a4ccecf6 | Michael Hanselmann | saved in a file instead of the RunResult instance; this
|
159 | a4ccecf6 | Michael Hanselmann | parameter denotes the file name (if not None)
|
160 | a4ccecf6 | Michael Hanselmann | @type cwd: string
|
161 | a4ccecf6 | Michael Hanselmann | @param cwd: if specified, will be used as the working
|
162 | a4ccecf6 | Michael Hanselmann | directory for the command; the default will be /
|
163 | a4ccecf6 | Michael Hanselmann | @type reset_env: boolean
|
164 | a4ccecf6 | Michael Hanselmann | @param reset_env: whether to reset or keep the default os environment
|
165 | a4ccecf6 | Michael Hanselmann | @type interactive: boolean
|
166 | a4ccecf6 | Michael Hanselmann | @param interactive: weather we pipe stdin, stdout and stderr
|
167 | a4ccecf6 | Michael Hanselmann | (default behaviour) or run the command interactive
|
168 | a4ccecf6 | Michael Hanselmann | @type timeout: int
|
169 | a4ccecf6 | Michael Hanselmann | @param timeout: If not None, timeout in seconds until child process gets
|
170 | a4ccecf6 | Michael Hanselmann | killed
|
171 | 7b0bf9cd | Apollon Oikonomopoulos | @type noclose_fds: list
|
172 | 7b0bf9cd | Apollon Oikonomopoulos | @param noclose_fds: list of additional (fd >=3) file descriptors to leave
|
173 | 7b0bf9cd | Apollon Oikonomopoulos | open for the child process
|
174 | d6491981 | Renรฉ Nussbaumer | @param _postfork_fn: Callback run after fork but before timeout (unittest)
|
175 | a4ccecf6 | Michael Hanselmann | @rtype: L{RunResult}
|
176 | a4ccecf6 | Michael Hanselmann | @return: RunResult instance
|
177 | a4ccecf6 | Michael Hanselmann | @raise errors.ProgrammerError: if we call this when forks are disabled
|
178 | a4ccecf6 | Michael Hanselmann |
|
179 | a4ccecf6 | Michael Hanselmann | """
|
180 | a4ccecf6 | Michael Hanselmann | if _no_fork:
|
181 | a4ccecf6 | Michael Hanselmann | raise errors.ProgrammerError("utils.RunCmd() called with fork() disabled") |
182 | a4ccecf6 | Michael Hanselmann | |
183 | a4ccecf6 | Michael Hanselmann | if output and interactive: |
184 | a4ccecf6 | Michael Hanselmann | raise errors.ProgrammerError("Parameters 'output' and 'interactive' can" |
185 | a4ccecf6 | Michael Hanselmann | " not be provided at the same time")
|
186 | a4ccecf6 | Michael Hanselmann | |
187 | a4ccecf6 | Michael Hanselmann | if isinstance(cmd, basestring): |
188 | a4ccecf6 | Michael Hanselmann | strcmd = cmd |
189 | a4ccecf6 | Michael Hanselmann | shell = True
|
190 | a4ccecf6 | Michael Hanselmann | else:
|
191 | a4ccecf6 | Michael Hanselmann | cmd = [str(val) for val in cmd] |
192 | a4ccecf6 | Michael Hanselmann | strcmd = utils_text.ShellQuoteArgs(cmd) |
193 | a4ccecf6 | Michael Hanselmann | shell = False
|
194 | a4ccecf6 | Michael Hanselmann | |
195 | a4ccecf6 | Michael Hanselmann | if output:
|
196 | a4ccecf6 | Michael Hanselmann | logging.debug("RunCmd %s, output file '%s'", strcmd, output)
|
197 | a4ccecf6 | Michael Hanselmann | else:
|
198 | a4ccecf6 | Michael Hanselmann | logging.debug("RunCmd %s", strcmd)
|
199 | a4ccecf6 | Michael Hanselmann | |
200 | a4ccecf6 | Michael Hanselmann | cmd_env = _BuildCmdEnvironment(env, reset_env) |
201 | a4ccecf6 | Michael Hanselmann | |
202 | a4ccecf6 | Michael Hanselmann | try:
|
203 | a4ccecf6 | Michael Hanselmann | if output is None: |
204 | a4ccecf6 | Michael Hanselmann | out, err, status, timeout_action = _RunCmdPipe(cmd, cmd_env, shell, cwd, |
205 | 7b0bf9cd | Apollon Oikonomopoulos | interactive, timeout, |
206 | d6491981 | Renรฉ Nussbaumer | noclose_fds, |
207 | d6491981 | Renรฉ Nussbaumer | _postfork_fn=_postfork_fn) |
208 | a4ccecf6 | Michael Hanselmann | else:
|
209 | d6491981 | Renรฉ Nussbaumer | assert _postfork_fn is None, \ |
210 | d6491981 | Renรฉ Nussbaumer | "_postfork_fn not supported if output provided"
|
211 | a4ccecf6 | Michael Hanselmann | timeout_action = _TIMEOUT_NONE |
212 | 7b0bf9cd | Apollon Oikonomopoulos | status = _RunCmdFile(cmd, cmd_env, shell, output, cwd, noclose_fds) |
213 | a4ccecf6 | Michael Hanselmann | out = err = ""
|
214 | a4ccecf6 | Michael Hanselmann | except OSError, err: |
215 | a4ccecf6 | Michael Hanselmann | if err.errno == errno.ENOENT:
|
216 | a4ccecf6 | Michael Hanselmann | raise errors.OpExecError("Can't execute '%s': not found (%s)" % |
217 | a4ccecf6 | Michael Hanselmann | (strcmd, err)) |
218 | a4ccecf6 | Michael Hanselmann | else:
|
219 | a4ccecf6 | Michael Hanselmann | raise
|
220 | a4ccecf6 | Michael Hanselmann | |
221 | a4ccecf6 | Michael Hanselmann | if status >= 0: |
222 | a4ccecf6 | Michael Hanselmann | exitcode = status |
223 | a4ccecf6 | Michael Hanselmann | signal_ = None
|
224 | a4ccecf6 | Michael Hanselmann | else:
|
225 | a4ccecf6 | Michael Hanselmann | exitcode = None
|
226 | a4ccecf6 | Michael Hanselmann | signal_ = -status |
227 | a4ccecf6 | Michael Hanselmann | |
228 | a4ccecf6 | Michael Hanselmann | return RunResult(exitcode, signal_, out, err, strcmd, timeout_action, timeout)
|
229 | a4ccecf6 | Michael Hanselmann | |
230 | a4ccecf6 | Michael Hanselmann | |
231 | a4ccecf6 | Michael Hanselmann | def SetupDaemonEnv(cwd="/", umask=077): |
232 | a4ccecf6 | Michael Hanselmann | """Setup a daemon's environment.
|
233 | a4ccecf6 | Michael Hanselmann |
|
234 | a4ccecf6 | Michael Hanselmann | This should be called between the first and second fork, due to
|
235 | a4ccecf6 | Michael Hanselmann | setsid usage.
|
236 | a4ccecf6 | Michael Hanselmann |
|
237 | a4ccecf6 | Michael Hanselmann | @param cwd: the directory to which to chdir
|
238 | a4ccecf6 | Michael Hanselmann | @param umask: the umask to setup
|
239 | a4ccecf6 | Michael Hanselmann |
|
240 | a4ccecf6 | Michael Hanselmann | """
|
241 | a4ccecf6 | Michael Hanselmann | os.chdir(cwd) |
242 | a4ccecf6 | Michael Hanselmann | os.umask(umask) |
243 | a4ccecf6 | Michael Hanselmann | os.setsid() |
244 | a4ccecf6 | Michael Hanselmann | |
245 | a4ccecf6 | Michael Hanselmann | |
246 | a4ccecf6 | Michael Hanselmann | def SetupDaemonFDs(output_file, output_fd): |
247 | a4ccecf6 | Michael Hanselmann | """Setups up a daemon's file descriptors.
|
248 | a4ccecf6 | Michael Hanselmann |
|
249 | a4ccecf6 | Michael Hanselmann | @param output_file: if not None, the file to which to redirect
|
250 | a4ccecf6 | Michael Hanselmann | stdout/stderr
|
251 | a4ccecf6 | Michael Hanselmann | @param output_fd: if not None, the file descriptor for stdout/stderr
|
252 | a4ccecf6 | Michael Hanselmann |
|
253 | a4ccecf6 | Michael Hanselmann | """
|
254 | a4ccecf6 | Michael Hanselmann | # check that at most one is defined
|
255 | a4ccecf6 | Michael Hanselmann | assert [output_file, output_fd].count(None) >= 1 |
256 | a4ccecf6 | Michael Hanselmann | |
257 | a4ccecf6 | Michael Hanselmann | # Open /dev/null (read-only, only for stdin)
|
258 | a4ccecf6 | Michael Hanselmann | devnull_fd = os.open(os.devnull, os.O_RDONLY) |
259 | a4ccecf6 | Michael Hanselmann | |
260 | 638ac34b | Michael Hanselmann | output_close = True
|
261 | 638ac34b | Michael Hanselmann | |
262 | a4ccecf6 | Michael Hanselmann | if output_fd is not None: |
263 | 638ac34b | Michael Hanselmann | output_close = False
|
264 | a4ccecf6 | Michael Hanselmann | elif output_file is not None: |
265 | a4ccecf6 | Michael Hanselmann | # Open output file
|
266 | a4ccecf6 | Michael Hanselmann | try:
|
267 | a4ccecf6 | Michael Hanselmann | output_fd = os.open(output_file, |
268 | a4ccecf6 | Michael Hanselmann | os.O_WRONLY | os.O_CREAT | os.O_APPEND, 0600)
|
269 | a4ccecf6 | Michael Hanselmann | except EnvironmentError, err: |
270 | a4ccecf6 | Michael Hanselmann | raise Exception("Opening output file failed: %s" % err) |
271 | a4ccecf6 | Michael Hanselmann | else:
|
272 | a4ccecf6 | Michael Hanselmann | output_fd = os.open(os.devnull, os.O_WRONLY) |
273 | a4ccecf6 | Michael Hanselmann | |
274 | a4ccecf6 | Michael Hanselmann | # Redirect standard I/O
|
275 | a4ccecf6 | Michael Hanselmann | os.dup2(devnull_fd, 0)
|
276 | a4ccecf6 | Michael Hanselmann | os.dup2(output_fd, 1)
|
277 | a4ccecf6 | Michael Hanselmann | os.dup2(output_fd, 2)
|
278 | a4ccecf6 | Michael Hanselmann | |
279 | 638ac34b | Michael Hanselmann | if devnull_fd > 2: |
280 | 638ac34b | Michael Hanselmann | utils_wrapper.CloseFdNoError(devnull_fd) |
281 | 638ac34b | Michael Hanselmann | |
282 | 638ac34b | Michael Hanselmann | if output_close and output_fd > 2: |
283 | 638ac34b | Michael Hanselmann | utils_wrapper.CloseFdNoError(output_fd) |
284 | 638ac34b | Michael Hanselmann | |
285 | a4ccecf6 | Michael Hanselmann | |
286 | a4ccecf6 | Michael Hanselmann | def StartDaemon(cmd, env=None, cwd="/", output=None, output_fd=None, |
287 | a4ccecf6 | Michael Hanselmann | pidfile=None):
|
288 | a4ccecf6 | Michael Hanselmann | """Start a daemon process after forking twice.
|
289 | a4ccecf6 | Michael Hanselmann |
|
290 | a4ccecf6 | Michael Hanselmann | @type cmd: string or list
|
291 | a4ccecf6 | Michael Hanselmann | @param cmd: Command to run
|
292 | a4ccecf6 | Michael Hanselmann | @type env: dict
|
293 | a4ccecf6 | Michael Hanselmann | @param env: Additional environment variables
|
294 | a4ccecf6 | Michael Hanselmann | @type cwd: string
|
295 | a4ccecf6 | Michael Hanselmann | @param cwd: Working directory for the program
|
296 | a4ccecf6 | Michael Hanselmann | @type output: string
|
297 | a4ccecf6 | Michael Hanselmann | @param output: Path to file in which to save the output
|
298 | a4ccecf6 | Michael Hanselmann | @type output_fd: int
|
299 | a4ccecf6 | Michael Hanselmann | @param output_fd: File descriptor for output
|
300 | a4ccecf6 | Michael Hanselmann | @type pidfile: string
|
301 | a4ccecf6 | Michael Hanselmann | @param pidfile: Process ID file
|
302 | a4ccecf6 | Michael Hanselmann | @rtype: int
|
303 | a4ccecf6 | Michael Hanselmann | @return: Daemon process ID
|
304 | a4ccecf6 | Michael Hanselmann | @raise errors.ProgrammerError: if we call this when forks are disabled
|
305 | a4ccecf6 | Michael Hanselmann |
|
306 | a4ccecf6 | Michael Hanselmann | """
|
307 | a4ccecf6 | Michael Hanselmann | if _no_fork:
|
308 | a4ccecf6 | Michael Hanselmann | raise errors.ProgrammerError("utils.StartDaemon() called with fork()" |
309 | a4ccecf6 | Michael Hanselmann | " disabled")
|
310 | a4ccecf6 | Michael Hanselmann | |
311 | a4ccecf6 | Michael Hanselmann | if output and not (bool(output) ^ (output_fd is not None)): |
312 | a4ccecf6 | Michael Hanselmann | raise errors.ProgrammerError("Only one of 'output' and 'output_fd' can be" |
313 | a4ccecf6 | Michael Hanselmann | " specified")
|
314 | a4ccecf6 | Michael Hanselmann | |
315 | a4ccecf6 | Michael Hanselmann | if isinstance(cmd, basestring): |
316 | a4ccecf6 | Michael Hanselmann | cmd = ["/bin/sh", "-c", cmd] |
317 | a4ccecf6 | Michael Hanselmann | |
318 | a4ccecf6 | Michael Hanselmann | strcmd = utils_text.ShellQuoteArgs(cmd) |
319 | a4ccecf6 | Michael Hanselmann | |
320 | a4ccecf6 | Michael Hanselmann | if output:
|
321 | a4ccecf6 | Michael Hanselmann | logging.debug("StartDaemon %s, output file '%s'", strcmd, output)
|
322 | a4ccecf6 | Michael Hanselmann | else:
|
323 | a4ccecf6 | Michael Hanselmann | logging.debug("StartDaemon %s", strcmd)
|
324 | a4ccecf6 | Michael Hanselmann | |
325 | a4ccecf6 | Michael Hanselmann | cmd_env = _BuildCmdEnvironment(env, False)
|
326 | a4ccecf6 | Michael Hanselmann | |
327 | a4ccecf6 | Michael Hanselmann | # Create pipe for sending PID back
|
328 | a4ccecf6 | Michael Hanselmann | (pidpipe_read, pidpipe_write) = os.pipe() |
329 | a4ccecf6 | Michael Hanselmann | try:
|
330 | a4ccecf6 | Michael Hanselmann | try:
|
331 | a4ccecf6 | Michael Hanselmann | # Create pipe for sending error messages
|
332 | a4ccecf6 | Michael Hanselmann | (errpipe_read, errpipe_write) = os.pipe() |
333 | a4ccecf6 | Michael Hanselmann | try:
|
334 | a4ccecf6 | Michael Hanselmann | try:
|
335 | a4ccecf6 | Michael Hanselmann | # First fork
|
336 | a4ccecf6 | Michael Hanselmann | pid = os.fork() |
337 | a4ccecf6 | Michael Hanselmann | if pid == 0: |
338 | a4ccecf6 | Michael Hanselmann | try:
|
339 | a4ccecf6 | Michael Hanselmann | # Child process, won't return
|
340 | a4ccecf6 | Michael Hanselmann | _StartDaemonChild(errpipe_read, errpipe_write, |
341 | a4ccecf6 | Michael Hanselmann | pidpipe_read, pidpipe_write, |
342 | a4ccecf6 | Michael Hanselmann | cmd, cmd_env, cwd, |
343 | a4ccecf6 | Michael Hanselmann | output, output_fd, pidfile) |
344 | a4ccecf6 | Michael Hanselmann | finally:
|
345 | a4ccecf6 | Michael Hanselmann | # Well, maybe child process failed
|
346 | a4ccecf6 | Michael Hanselmann | os._exit(1) # pylint: disable-msg=W0212 |
347 | a4ccecf6 | Michael Hanselmann | finally:
|
348 | a4ccecf6 | Michael Hanselmann | utils_wrapper.CloseFdNoError(errpipe_write) |
349 | a4ccecf6 | Michael Hanselmann | |
350 | a4ccecf6 | Michael Hanselmann | # Wait for daemon to be started (or an error message to
|
351 | a4ccecf6 | Michael Hanselmann | # arrive) and read up to 100 KB as an error message
|
352 | a4ccecf6 | Michael Hanselmann | errormsg = utils_wrapper.RetryOnSignal(os.read, errpipe_read, |
353 | a4ccecf6 | Michael Hanselmann | 100 * 1024) |
354 | a4ccecf6 | Michael Hanselmann | finally:
|
355 | a4ccecf6 | Michael Hanselmann | utils_wrapper.CloseFdNoError(errpipe_read) |
356 | a4ccecf6 | Michael Hanselmann | finally:
|
357 | a4ccecf6 | Michael Hanselmann | utils_wrapper.CloseFdNoError(pidpipe_write) |
358 | a4ccecf6 | Michael Hanselmann | |
359 | a4ccecf6 | Michael Hanselmann | # Read up to 128 bytes for PID
|
360 | a4ccecf6 | Michael Hanselmann | pidtext = utils_wrapper.RetryOnSignal(os.read, pidpipe_read, 128)
|
361 | a4ccecf6 | Michael Hanselmann | finally:
|
362 | a4ccecf6 | Michael Hanselmann | utils_wrapper.CloseFdNoError(pidpipe_read) |
363 | a4ccecf6 | Michael Hanselmann | |
364 | a4ccecf6 | Michael Hanselmann | # Try to avoid zombies by waiting for child process
|
365 | a4ccecf6 | Michael Hanselmann | try:
|
366 | a4ccecf6 | Michael Hanselmann | os.waitpid(pid, 0)
|
367 | a4ccecf6 | Michael Hanselmann | except OSError: |
368 | a4ccecf6 | Michael Hanselmann | pass
|
369 | a4ccecf6 | Michael Hanselmann | |
370 | a4ccecf6 | Michael Hanselmann | if errormsg:
|
371 | a4ccecf6 | Michael Hanselmann | raise errors.OpExecError("Error when starting daemon process: %r" % |
372 | a4ccecf6 | Michael Hanselmann | errormsg) |
373 | a4ccecf6 | Michael Hanselmann | |
374 | a4ccecf6 | Michael Hanselmann | try:
|
375 | a4ccecf6 | Michael Hanselmann | return int(pidtext) |
376 | a4ccecf6 | Michael Hanselmann | except (ValueError, TypeError), err: |
377 | a4ccecf6 | Michael Hanselmann | raise errors.OpExecError("Error while trying to parse PID %r: %s" % |
378 | a4ccecf6 | Michael Hanselmann | (pidtext, err)) |
379 | a4ccecf6 | Michael Hanselmann | |
380 | a4ccecf6 | Michael Hanselmann | |
381 | a4ccecf6 | Michael Hanselmann | def _StartDaemonChild(errpipe_read, errpipe_write, |
382 | a4ccecf6 | Michael Hanselmann | pidpipe_read, pidpipe_write, |
383 | a4ccecf6 | Michael Hanselmann | args, env, cwd, |
384 | a4ccecf6 | Michael Hanselmann | output, fd_output, pidfile): |
385 | a4ccecf6 | Michael Hanselmann | """Child process for starting daemon.
|
386 | a4ccecf6 | Michael Hanselmann |
|
387 | a4ccecf6 | Michael Hanselmann | """
|
388 | a4ccecf6 | Michael Hanselmann | try:
|
389 | a4ccecf6 | Michael Hanselmann | # Close parent's side
|
390 | a4ccecf6 | Michael Hanselmann | utils_wrapper.CloseFdNoError(errpipe_read) |
391 | a4ccecf6 | Michael Hanselmann | utils_wrapper.CloseFdNoError(pidpipe_read) |
392 | a4ccecf6 | Michael Hanselmann | |
393 | a4ccecf6 | Michael Hanselmann | # First child process
|
394 | a4ccecf6 | Michael Hanselmann | SetupDaemonEnv() |
395 | a4ccecf6 | Michael Hanselmann | |
396 | a4ccecf6 | Michael Hanselmann | # And fork for the second time
|
397 | a4ccecf6 | Michael Hanselmann | pid = os.fork() |
398 | a4ccecf6 | Michael Hanselmann | if pid != 0: |
399 | a4ccecf6 | Michael Hanselmann | # Exit first child process
|
400 | a4ccecf6 | Michael Hanselmann | os._exit(0) # pylint: disable-msg=W0212 |
401 | a4ccecf6 | Michael Hanselmann | |
402 | a4ccecf6 | Michael Hanselmann | # Make sure pipe is closed on execv* (and thereby notifies
|
403 | a4ccecf6 | Michael Hanselmann | # original process)
|
404 | a4ccecf6 | Michael Hanselmann | utils_wrapper.SetCloseOnExecFlag(errpipe_write, True)
|
405 | a4ccecf6 | Michael Hanselmann | |
406 | a4ccecf6 | Michael Hanselmann | # List of file descriptors to be left open
|
407 | a4ccecf6 | Michael Hanselmann | noclose_fds = [errpipe_write] |
408 | a4ccecf6 | Michael Hanselmann | |
409 | a4ccecf6 | Michael Hanselmann | # Open PID file
|
410 | a4ccecf6 | Michael Hanselmann | if pidfile:
|
411 | a4ccecf6 | Michael Hanselmann | fd_pidfile = utils_io.WritePidFile(pidfile) |
412 | a4ccecf6 | Michael Hanselmann | |
413 | a4ccecf6 | Michael Hanselmann | # Keeping the file open to hold the lock
|
414 | a4ccecf6 | Michael Hanselmann | noclose_fds.append(fd_pidfile) |
415 | a4ccecf6 | Michael Hanselmann | |
416 | a4ccecf6 | Michael Hanselmann | utils_wrapper.SetCloseOnExecFlag(fd_pidfile, False)
|
417 | a4ccecf6 | Michael Hanselmann | else:
|
418 | a4ccecf6 | Michael Hanselmann | fd_pidfile = None
|
419 | a4ccecf6 | Michael Hanselmann | |
420 | a4ccecf6 | Michael Hanselmann | SetupDaemonFDs(output, fd_output) |
421 | a4ccecf6 | Michael Hanselmann | |
422 | a4ccecf6 | Michael Hanselmann | # Send daemon PID to parent
|
423 | a4ccecf6 | Michael Hanselmann | utils_wrapper.RetryOnSignal(os.write, pidpipe_write, str(os.getpid()))
|
424 | a4ccecf6 | Michael Hanselmann | |
425 | a4ccecf6 | Michael Hanselmann | # Close all file descriptors except stdio and error message pipe
|
426 | a4ccecf6 | Michael Hanselmann | CloseFDs(noclose_fds=noclose_fds) |
427 | a4ccecf6 | Michael Hanselmann | |
428 | a4ccecf6 | Michael Hanselmann | # Change working directory
|
429 | a4ccecf6 | Michael Hanselmann | os.chdir(cwd) |
430 | a4ccecf6 | Michael Hanselmann | |
431 | a4ccecf6 | Michael Hanselmann | if env is None: |
432 | a4ccecf6 | Michael Hanselmann | os.execvp(args[0], args)
|
433 | a4ccecf6 | Michael Hanselmann | else:
|
434 | a4ccecf6 | Michael Hanselmann | os.execvpe(args[0], args, env)
|
435 | a4ccecf6 | Michael Hanselmann | except: # pylint: disable-msg=W0702 |
436 | a4ccecf6 | Michael Hanselmann | try:
|
437 | a4ccecf6 | Michael Hanselmann | # Report errors to original process
|
438 | a4ccecf6 | Michael Hanselmann | WriteErrorToFD(errpipe_write, str(sys.exc_info()[1])) |
439 | a4ccecf6 | Michael Hanselmann | except: # pylint: disable-msg=W0702 |
440 | a4ccecf6 | Michael Hanselmann | # Ignore errors in error handling
|
441 | a4ccecf6 | Michael Hanselmann | pass
|
442 | a4ccecf6 | Michael Hanselmann | |
443 | a4ccecf6 | Michael Hanselmann | os._exit(1) # pylint: disable-msg=W0212 |
444 | a4ccecf6 | Michael Hanselmann | |
445 | a4ccecf6 | Michael Hanselmann | |
446 | a4ccecf6 | Michael Hanselmann | def WriteErrorToFD(fd, err): |
447 | a4ccecf6 | Michael Hanselmann | """Possibly write an error message to a fd.
|
448 | a4ccecf6 | Michael Hanselmann |
|
449 | a4ccecf6 | Michael Hanselmann | @type fd: None or int (file descriptor)
|
450 | a4ccecf6 | Michael Hanselmann | @param fd: if not None, the error will be written to this fd
|
451 | a4ccecf6 | Michael Hanselmann | @param err: string, the error message
|
452 | a4ccecf6 | Michael Hanselmann |
|
453 | a4ccecf6 | Michael Hanselmann | """
|
454 | a4ccecf6 | Michael Hanselmann | if fd is None: |
455 | a4ccecf6 | Michael Hanselmann | return
|
456 | a4ccecf6 | Michael Hanselmann | |
457 | a4ccecf6 | Michael Hanselmann | if not err: |
458 | a4ccecf6 | Michael Hanselmann | err = "<unknown error>"
|
459 | a4ccecf6 | Michael Hanselmann | |
460 | a4ccecf6 | Michael Hanselmann | utils_wrapper.RetryOnSignal(os.write, fd, err) |
461 | a4ccecf6 | Michael Hanselmann | |
462 | a4ccecf6 | Michael Hanselmann | |
463 | a4ccecf6 | Michael Hanselmann | def _CheckIfAlive(child): |
464 | a4ccecf6 | Michael Hanselmann | """Raises L{utils_retry.RetryAgain} if child is still alive.
|
465 | a4ccecf6 | Michael Hanselmann |
|
466 | a4ccecf6 | Michael Hanselmann | @raises utils_retry.RetryAgain: If child is still alive
|
467 | a4ccecf6 | Michael Hanselmann |
|
468 | a4ccecf6 | Michael Hanselmann | """
|
469 | a4ccecf6 | Michael Hanselmann | if child.poll() is None: |
470 | a4ccecf6 | Michael Hanselmann | raise utils_retry.RetryAgain()
|
471 | a4ccecf6 | Michael Hanselmann | |
472 | a4ccecf6 | Michael Hanselmann | |
473 | a4ccecf6 | Michael Hanselmann | def _WaitForProcess(child, timeout): |
474 | a4ccecf6 | Michael Hanselmann | """Waits for the child to terminate or until we reach timeout.
|
475 | a4ccecf6 | Michael Hanselmann |
|
476 | a4ccecf6 | Michael Hanselmann | """
|
477 | a4ccecf6 | Michael Hanselmann | try:
|
478 | a4ccecf6 | Michael Hanselmann | utils_retry.Retry(_CheckIfAlive, (1.0, 1.2, 5.0), max(0, timeout), |
479 | a4ccecf6 | Michael Hanselmann | args=[child]) |
480 | a4ccecf6 | Michael Hanselmann | except utils_retry.RetryTimeout:
|
481 | a4ccecf6 | Michael Hanselmann | pass
|
482 | a4ccecf6 | Michael Hanselmann | |
483 | a4ccecf6 | Michael Hanselmann | |
484 | 7b0bf9cd | Apollon Oikonomopoulos | def _RunCmdPipe(cmd, env, via_shell, cwd, interactive, timeout, noclose_fds, |
485 | d6491981 | Renรฉ Nussbaumer | _linger_timeout=constants.CHILD_LINGER_TIMEOUT, |
486 | d6491981 | Renรฉ Nussbaumer | _postfork_fn=None):
|
487 | a4ccecf6 | Michael Hanselmann | """Run a command and return its output.
|
488 | a4ccecf6 | Michael Hanselmann |
|
489 | a4ccecf6 | Michael Hanselmann | @type cmd: string or list
|
490 | a4ccecf6 | Michael Hanselmann | @param cmd: Command to run
|
491 | a4ccecf6 | Michael Hanselmann | @type env: dict
|
492 | a4ccecf6 | Michael Hanselmann | @param env: The environment to use
|
493 | a4ccecf6 | Michael Hanselmann | @type via_shell: bool
|
494 | a4ccecf6 | Michael Hanselmann | @param via_shell: if we should run via the shell
|
495 | a4ccecf6 | Michael Hanselmann | @type cwd: string
|
496 | a4ccecf6 | Michael Hanselmann | @param cwd: the working directory for the program
|
497 | a4ccecf6 | Michael Hanselmann | @type interactive: boolean
|
498 | a4ccecf6 | Michael Hanselmann | @param interactive: Run command interactive (without piping)
|
499 | a4ccecf6 | Michael Hanselmann | @type timeout: int
|
500 | a4ccecf6 | Michael Hanselmann | @param timeout: Timeout after the programm gets terminated
|
501 | 7b0bf9cd | Apollon Oikonomopoulos | @type noclose_fds: list
|
502 | 7b0bf9cd | Apollon Oikonomopoulos | @param noclose_fds: list of additional (fd >=3) file descriptors to leave
|
503 | 7b0bf9cd | Apollon Oikonomopoulos | open for the child process
|
504 | d6491981 | Renรฉ Nussbaumer | @param _postfork_fn: Function run after fork but before timeout (unittest)
|
505 | a4ccecf6 | Michael Hanselmann | @rtype: tuple
|
506 | a4ccecf6 | Michael Hanselmann | @return: (out, err, status)
|
507 | a4ccecf6 | Michael Hanselmann |
|
508 | a4ccecf6 | Michael Hanselmann | """
|
509 | a4ccecf6 | Michael Hanselmann | poller = select.poll() |
510 | a4ccecf6 | Michael Hanselmann | |
511 | a4ccecf6 | Michael Hanselmann | stderr = subprocess.PIPE |
512 | a4ccecf6 | Michael Hanselmann | stdout = subprocess.PIPE |
513 | a4ccecf6 | Michael Hanselmann | stdin = subprocess.PIPE |
514 | a4ccecf6 | Michael Hanselmann | |
515 | a4ccecf6 | Michael Hanselmann | if interactive:
|
516 | a4ccecf6 | Michael Hanselmann | stderr = stdout = stdin = None
|
517 | a4ccecf6 | Michael Hanselmann | |
518 | 7b0bf9cd | Apollon Oikonomopoulos | if noclose_fds:
|
519 | 7b0bf9cd | Apollon Oikonomopoulos | preexec_fn = lambda: CloseFDs(noclose_fds)
|
520 | 7b0bf9cd | Apollon Oikonomopoulos | close_fds = False
|
521 | 7b0bf9cd | Apollon Oikonomopoulos | else:
|
522 | 7b0bf9cd | Apollon Oikonomopoulos | preexec_fn = None
|
523 | 7b0bf9cd | Apollon Oikonomopoulos | close_fds = True
|
524 | 7b0bf9cd | Apollon Oikonomopoulos | |
525 | a4ccecf6 | Michael Hanselmann | child = subprocess.Popen(cmd, shell=via_shell, |
526 | a4ccecf6 | Michael Hanselmann | stderr=stderr, |
527 | a4ccecf6 | Michael Hanselmann | stdout=stdout, |
528 | a4ccecf6 | Michael Hanselmann | stdin=stdin, |
529 | 7b0bf9cd | Apollon Oikonomopoulos | close_fds=close_fds, env=env, |
530 | 7b0bf9cd | Apollon Oikonomopoulos | cwd=cwd, |
531 | 7b0bf9cd | Apollon Oikonomopoulos | preexec_fn=preexec_fn) |
532 | a4ccecf6 | Michael Hanselmann | |
533 | d6491981 | Renรฉ Nussbaumer | if _postfork_fn:
|
534 | d6491981 | Renรฉ Nussbaumer | _postfork_fn(child.pid) |
535 | d6491981 | Renรฉ Nussbaumer | |
536 | a4ccecf6 | Michael Hanselmann | out = StringIO() |
537 | a4ccecf6 | Michael Hanselmann | err = StringIO() |
538 | a4ccecf6 | Michael Hanselmann | |
539 | a4ccecf6 | Michael Hanselmann | linger_timeout = None
|
540 | a4ccecf6 | Michael Hanselmann | |
541 | a4ccecf6 | Michael Hanselmann | if timeout is None: |
542 | a4ccecf6 | Michael Hanselmann | poll_timeout = None
|
543 | a4ccecf6 | Michael Hanselmann | else:
|
544 | a4ccecf6 | Michael Hanselmann | poll_timeout = utils_algo.RunningTimeout(timeout, True).Remaining
|
545 | a4ccecf6 | Michael Hanselmann | |
546 | a4ccecf6 | Michael Hanselmann | msg_timeout = ("Command %s (%d) run into execution timeout, terminating" %
|
547 | a4ccecf6 | Michael Hanselmann | (cmd, child.pid)) |
548 | a4ccecf6 | Michael Hanselmann | msg_linger = ("Command %s (%d) run into linger timeout, killing" %
|
549 | a4ccecf6 | Michael Hanselmann | (cmd, child.pid)) |
550 | a4ccecf6 | Michael Hanselmann | |
551 | a4ccecf6 | Michael Hanselmann | timeout_action = _TIMEOUT_NONE |
552 | a4ccecf6 | Michael Hanselmann | |
553 | a4ccecf6 | Michael Hanselmann | if not interactive: |
554 | a4ccecf6 | Michael Hanselmann | child.stdin.close() |
555 | a4ccecf6 | Michael Hanselmann | poller.register(child.stdout, select.POLLIN) |
556 | a4ccecf6 | Michael Hanselmann | poller.register(child.stderr, select.POLLIN) |
557 | a4ccecf6 | Michael Hanselmann | fdmap = { |
558 | a4ccecf6 | Michael Hanselmann | child.stdout.fileno(): (out, child.stdout), |
559 | a4ccecf6 | Michael Hanselmann | child.stderr.fileno(): (err, child.stderr), |
560 | a4ccecf6 | Michael Hanselmann | } |
561 | a4ccecf6 | Michael Hanselmann | for fd in fdmap: |
562 | a4ccecf6 | Michael Hanselmann | utils_wrapper.SetNonblockFlag(fd, True)
|
563 | a4ccecf6 | Michael Hanselmann | |
564 | a4ccecf6 | Michael Hanselmann | while fdmap:
|
565 | a4ccecf6 | Michael Hanselmann | if poll_timeout:
|
566 | a4ccecf6 | Michael Hanselmann | pt = poll_timeout() * 1000
|
567 | a4ccecf6 | Michael Hanselmann | if pt < 0: |
568 | a4ccecf6 | Michael Hanselmann | if linger_timeout is None: |
569 | a4ccecf6 | Michael Hanselmann | logging.warning(msg_timeout) |
570 | a4ccecf6 | Michael Hanselmann | if child.poll() is None: |
571 | a4ccecf6 | Michael Hanselmann | timeout_action = _TIMEOUT_TERM |
572 | a4ccecf6 | Michael Hanselmann | utils_wrapper.IgnoreProcessNotFound(os.kill, child.pid, |
573 | a4ccecf6 | Michael Hanselmann | signal.SIGTERM) |
574 | a4ccecf6 | Michael Hanselmann | linger_timeout = \ |
575 | a4ccecf6 | Michael Hanselmann | utils_algo.RunningTimeout(_linger_timeout, True).Remaining
|
576 | a4ccecf6 | Michael Hanselmann | pt = linger_timeout() * 1000
|
577 | a4ccecf6 | Michael Hanselmann | if pt < 0: |
578 | a4ccecf6 | Michael Hanselmann | break
|
579 | a4ccecf6 | Michael Hanselmann | else:
|
580 | a4ccecf6 | Michael Hanselmann | pt = None
|
581 | a4ccecf6 | Michael Hanselmann | |
582 | a4ccecf6 | Michael Hanselmann | pollresult = utils_wrapper.RetryOnSignal(poller.poll, pt) |
583 | a4ccecf6 | Michael Hanselmann | |
584 | a4ccecf6 | Michael Hanselmann | for fd, event in pollresult: |
585 | a4ccecf6 | Michael Hanselmann | if event & select.POLLIN or event & select.POLLPRI: |
586 | a4ccecf6 | Michael Hanselmann | data = fdmap[fd][1].read()
|
587 | a4ccecf6 | Michael Hanselmann | # no data from read signifies EOF (the same as POLLHUP)
|
588 | a4ccecf6 | Michael Hanselmann | if not data: |
589 | a4ccecf6 | Michael Hanselmann | poller.unregister(fd) |
590 | a4ccecf6 | Michael Hanselmann | del fdmap[fd]
|
591 | a4ccecf6 | Michael Hanselmann | continue
|
592 | a4ccecf6 | Michael Hanselmann | fdmap[fd][0].write(data)
|
593 | a4ccecf6 | Michael Hanselmann | if (event & select.POLLNVAL or event & select.POLLHUP or |
594 | a4ccecf6 | Michael Hanselmann | event & select.POLLERR): |
595 | a4ccecf6 | Michael Hanselmann | poller.unregister(fd) |
596 | a4ccecf6 | Michael Hanselmann | del fdmap[fd]
|
597 | a4ccecf6 | Michael Hanselmann | |
598 | a4ccecf6 | Michael Hanselmann | if timeout is not None: |
599 | a4ccecf6 | Michael Hanselmann | assert callable(poll_timeout) |
600 | a4ccecf6 | Michael Hanselmann | |
601 | a4ccecf6 | Michael Hanselmann | # We have no I/O left but it might still run
|
602 | a4ccecf6 | Michael Hanselmann | if child.poll() is None: |
603 | a4ccecf6 | Michael Hanselmann | _WaitForProcess(child, poll_timeout()) |
604 | a4ccecf6 | Michael Hanselmann | |
605 | a4ccecf6 | Michael Hanselmann | # Terminate if still alive after timeout
|
606 | a4ccecf6 | Michael Hanselmann | if child.poll() is None: |
607 | a4ccecf6 | Michael Hanselmann | if linger_timeout is None: |
608 | a4ccecf6 | Michael Hanselmann | logging.warning(msg_timeout) |
609 | a4ccecf6 | Michael Hanselmann | timeout_action = _TIMEOUT_TERM |
610 | a4ccecf6 | Michael Hanselmann | utils_wrapper.IgnoreProcessNotFound(os.kill, child.pid, signal.SIGTERM) |
611 | a4ccecf6 | Michael Hanselmann | lt = _linger_timeout |
612 | a4ccecf6 | Michael Hanselmann | else:
|
613 | a4ccecf6 | Michael Hanselmann | lt = linger_timeout() |
614 | a4ccecf6 | Michael Hanselmann | _WaitForProcess(child, lt) |
615 | a4ccecf6 | Michael Hanselmann | |
616 | a4ccecf6 | Michael Hanselmann | # Okay, still alive after timeout and linger timeout? Kill it!
|
617 | a4ccecf6 | Michael Hanselmann | if child.poll() is None: |
618 | a4ccecf6 | Michael Hanselmann | timeout_action = _TIMEOUT_KILL |
619 | a4ccecf6 | Michael Hanselmann | logging.warning(msg_linger) |
620 | a4ccecf6 | Michael Hanselmann | utils_wrapper.IgnoreProcessNotFound(os.kill, child.pid, signal.SIGKILL) |
621 | a4ccecf6 | Michael Hanselmann | |
622 | a4ccecf6 | Michael Hanselmann | out = out.getvalue() |
623 | a4ccecf6 | Michael Hanselmann | err = err.getvalue() |
624 | a4ccecf6 | Michael Hanselmann | |
625 | a4ccecf6 | Michael Hanselmann | status = child.wait() |
626 | a4ccecf6 | Michael Hanselmann | return out, err, status, timeout_action
|
627 | a4ccecf6 | Michael Hanselmann | |
628 | a4ccecf6 | Michael Hanselmann | |
629 | 7b0bf9cd | Apollon Oikonomopoulos | def _RunCmdFile(cmd, env, via_shell, output, cwd, noclose_fds): |
630 | a4ccecf6 | Michael Hanselmann | """Run a command and save its output to a file.
|
631 | a4ccecf6 | Michael Hanselmann |
|
632 | a4ccecf6 | Michael Hanselmann | @type cmd: string or list
|
633 | a4ccecf6 | Michael Hanselmann | @param cmd: Command to run
|
634 | a4ccecf6 | Michael Hanselmann | @type env: dict
|
635 | a4ccecf6 | Michael Hanselmann | @param env: The environment to use
|
636 | a4ccecf6 | Michael Hanselmann | @type via_shell: bool
|
637 | a4ccecf6 | Michael Hanselmann | @param via_shell: if we should run via the shell
|
638 | a4ccecf6 | Michael Hanselmann | @type output: str
|
639 | a4ccecf6 | Michael Hanselmann | @param output: the filename in which to save the output
|
640 | a4ccecf6 | Michael Hanselmann | @type cwd: string
|
641 | a4ccecf6 | Michael Hanselmann | @param cwd: the working directory for the program
|
642 | 7b0bf9cd | Apollon Oikonomopoulos | @type noclose_fds: list
|
643 | 7b0bf9cd | Apollon Oikonomopoulos | @param noclose_fds: list of additional (fd >=3) file descriptors to leave
|
644 | 7b0bf9cd | Apollon Oikonomopoulos | open for the child process
|
645 | a4ccecf6 | Michael Hanselmann | @rtype: int
|
646 | a4ccecf6 | Michael Hanselmann | @return: the exit status
|
647 | a4ccecf6 | Michael Hanselmann |
|
648 | a4ccecf6 | Michael Hanselmann | """
|
649 | a4ccecf6 | Michael Hanselmann | fh = open(output, "a") |
650 | 7b0bf9cd | Apollon Oikonomopoulos | |
651 | 7b0bf9cd | Apollon Oikonomopoulos | if noclose_fds:
|
652 | 7b0bf9cd | Apollon Oikonomopoulos | preexec_fn = lambda: CloseFDs(noclose_fds + [fh.fileno()])
|
653 | 7b0bf9cd | Apollon Oikonomopoulos | close_fds = False
|
654 | 7b0bf9cd | Apollon Oikonomopoulos | else:
|
655 | 7b0bf9cd | Apollon Oikonomopoulos | preexec_fn = None
|
656 | 7b0bf9cd | Apollon Oikonomopoulos | close_fds = True
|
657 | 7b0bf9cd | Apollon Oikonomopoulos | |
658 | a4ccecf6 | Michael Hanselmann | try:
|
659 | a4ccecf6 | Michael Hanselmann | child = subprocess.Popen(cmd, shell=via_shell, |
660 | a4ccecf6 | Michael Hanselmann | stderr=subprocess.STDOUT, |
661 | a4ccecf6 | Michael Hanselmann | stdout=fh, |
662 | a4ccecf6 | Michael Hanselmann | stdin=subprocess.PIPE, |
663 | 7b0bf9cd | Apollon Oikonomopoulos | close_fds=close_fds, env=env, |
664 | 7b0bf9cd | Apollon Oikonomopoulos | cwd=cwd, |
665 | 7b0bf9cd | Apollon Oikonomopoulos | preexec_fn=preexec_fn) |
666 | a4ccecf6 | Michael Hanselmann | |
667 | a4ccecf6 | Michael Hanselmann | child.stdin.close() |
668 | a4ccecf6 | Michael Hanselmann | status = child.wait() |
669 | a4ccecf6 | Michael Hanselmann | finally:
|
670 | a4ccecf6 | Michael Hanselmann | fh.close() |
671 | a4ccecf6 | Michael Hanselmann | return status
|
672 | a4ccecf6 | Michael Hanselmann | |
673 | a4ccecf6 | Michael Hanselmann | |
674 | a4ccecf6 | Michael Hanselmann | def RunParts(dir_name, env=None, reset_env=False): |
675 | a4ccecf6 | Michael Hanselmann | """Run Scripts or programs in a directory
|
676 | a4ccecf6 | Michael Hanselmann |
|
677 | a4ccecf6 | Michael Hanselmann | @type dir_name: string
|
678 | a4ccecf6 | Michael Hanselmann | @param dir_name: absolute path to a directory
|
679 | a4ccecf6 | Michael Hanselmann | @type env: dict
|
680 | a4ccecf6 | Michael Hanselmann | @param env: The environment to use
|
681 | a4ccecf6 | Michael Hanselmann | @type reset_env: boolean
|
682 | a4ccecf6 | Michael Hanselmann | @param reset_env: whether to reset or keep the default os environment
|
683 | a4ccecf6 | Michael Hanselmann | @rtype: list of tuples
|
684 | a4ccecf6 | Michael Hanselmann | @return: list of (name, (one of RUNDIR_STATUS), RunResult)
|
685 | a4ccecf6 | Michael Hanselmann |
|
686 | a4ccecf6 | Michael Hanselmann | """
|
687 | a4ccecf6 | Michael Hanselmann | rr = [] |
688 | a4ccecf6 | Michael Hanselmann | |
689 | a4ccecf6 | Michael Hanselmann | try:
|
690 | a4ccecf6 | Michael Hanselmann | dir_contents = utils_io.ListVisibleFiles(dir_name) |
691 | a4ccecf6 | Michael Hanselmann | except OSError, err: |
692 | a4ccecf6 | Michael Hanselmann | logging.warning("RunParts: skipping %s (cannot list: %s)", dir_name, err)
|
693 | a4ccecf6 | Michael Hanselmann | return rr
|
694 | a4ccecf6 | Michael Hanselmann | |
695 | a4ccecf6 | Michael Hanselmann | for relname in sorted(dir_contents): |
696 | a4ccecf6 | Michael Hanselmann | fname = utils_io.PathJoin(dir_name, relname) |
697 | a4ccecf6 | Michael Hanselmann | if not (os.path.isfile(fname) and os.access(fname, os.X_OK) and |
698 | a4ccecf6 | Michael Hanselmann | constants.EXT_PLUGIN_MASK.match(relname) is not None): |
699 | a4ccecf6 | Michael Hanselmann | rr.append((relname, constants.RUNPARTS_SKIP, None))
|
700 | a4ccecf6 | Michael Hanselmann | else:
|
701 | a4ccecf6 | Michael Hanselmann | try:
|
702 | a4ccecf6 | Michael Hanselmann | result = RunCmd([fname], env=env, reset_env=reset_env) |
703 | a4ccecf6 | Michael Hanselmann | except Exception, err: # pylint: disable-msg=W0703 |
704 | a4ccecf6 | Michael Hanselmann | rr.append((relname, constants.RUNPARTS_ERR, str(err)))
|
705 | a4ccecf6 | Michael Hanselmann | else:
|
706 | a4ccecf6 | Michael Hanselmann | rr.append((relname, constants.RUNPARTS_RUN, result)) |
707 | a4ccecf6 | Michael Hanselmann | |
708 | a4ccecf6 | Michael Hanselmann | return rr
|
709 | a4ccecf6 | Michael Hanselmann | |
710 | a4ccecf6 | Michael Hanselmann | |
711 | a4ccecf6 | Michael Hanselmann | def _GetProcStatusPath(pid): |
712 | a4ccecf6 | Michael Hanselmann | """Returns the path for a PID's proc status file.
|
713 | a4ccecf6 | Michael Hanselmann |
|
714 | a4ccecf6 | Michael Hanselmann | @type pid: int
|
715 | a4ccecf6 | Michael Hanselmann | @param pid: Process ID
|
716 | a4ccecf6 | Michael Hanselmann | @rtype: string
|
717 | a4ccecf6 | Michael Hanselmann |
|
718 | a4ccecf6 | Michael Hanselmann | """
|
719 | a4ccecf6 | Michael Hanselmann | return "/proc/%d/status" % pid |
720 | a4ccecf6 | Michael Hanselmann | |
721 | a4ccecf6 | Michael Hanselmann | |
722 | a4ccecf6 | Michael Hanselmann | def IsProcessAlive(pid): |
723 | a4ccecf6 | Michael Hanselmann | """Check if a given pid exists on the system.
|
724 | a4ccecf6 | Michael Hanselmann |
|
725 | a4ccecf6 | Michael Hanselmann | @note: zombie status is not handled, so zombie processes
|
726 | a4ccecf6 | Michael Hanselmann | will be returned as alive
|
727 | a4ccecf6 | Michael Hanselmann | @type pid: int
|
728 | a4ccecf6 | Michael Hanselmann | @param pid: the process ID to check
|
729 | a4ccecf6 | Michael Hanselmann | @rtype: boolean
|
730 | a4ccecf6 | Michael Hanselmann | @return: True if the process exists
|
731 | a4ccecf6 | Michael Hanselmann |
|
732 | a4ccecf6 | Michael Hanselmann | """
|
733 | a4ccecf6 | Michael Hanselmann | def _TryStat(name): |
734 | a4ccecf6 | Michael Hanselmann | try:
|
735 | a4ccecf6 | Michael Hanselmann | os.stat(name) |
736 | a4ccecf6 | Michael Hanselmann | return True |
737 | a4ccecf6 | Michael Hanselmann | except EnvironmentError, err: |
738 | a4ccecf6 | Michael Hanselmann | if err.errno in (errno.ENOENT, errno.ENOTDIR): |
739 | a4ccecf6 | Michael Hanselmann | return False |
740 | a4ccecf6 | Michael Hanselmann | elif err.errno == errno.EINVAL:
|
741 | a4ccecf6 | Michael Hanselmann | raise utils_retry.RetryAgain(err)
|
742 | a4ccecf6 | Michael Hanselmann | raise
|
743 | a4ccecf6 | Michael Hanselmann | |
744 | a4ccecf6 | Michael Hanselmann | assert isinstance(pid, int), "pid must be an integer" |
745 | a4ccecf6 | Michael Hanselmann | if pid <= 0: |
746 | a4ccecf6 | Michael Hanselmann | return False |
747 | a4ccecf6 | Michael Hanselmann | |
748 | a4ccecf6 | Michael Hanselmann | # /proc in a multiprocessor environment can have strange behaviors.
|
749 | a4ccecf6 | Michael Hanselmann | # Retry the os.stat a few times until we get a good result.
|
750 | a4ccecf6 | Michael Hanselmann | try:
|
751 | a4ccecf6 | Michael Hanselmann | return utils_retry.Retry(_TryStat, (0.01, 1.5, 0.1), 0.5, |
752 | a4ccecf6 | Michael Hanselmann | args=[_GetProcStatusPath(pid)]) |
753 | a4ccecf6 | Michael Hanselmann | except utils_retry.RetryTimeout, err:
|
754 | a4ccecf6 | Michael Hanselmann | err.RaiseInner() |
755 | a4ccecf6 | Michael Hanselmann | |
756 | a4ccecf6 | Michael Hanselmann | |
757 | a4ccecf6 | Michael Hanselmann | def _ParseSigsetT(sigset): |
758 | a4ccecf6 | Michael Hanselmann | """Parse a rendered sigset_t value.
|
759 | a4ccecf6 | Michael Hanselmann |
|
760 | a4ccecf6 | Michael Hanselmann | This is the opposite of the Linux kernel's fs/proc/array.c:render_sigset_t
|
761 | a4ccecf6 | Michael Hanselmann | function.
|
762 | a4ccecf6 | Michael Hanselmann |
|
763 | a4ccecf6 | Michael Hanselmann | @type sigset: string
|
764 | a4ccecf6 | Michael Hanselmann | @param sigset: Rendered signal set from /proc/$pid/status
|
765 | a4ccecf6 | Michael Hanselmann | @rtype: set
|
766 | a4ccecf6 | Michael Hanselmann | @return: Set of all enabled signal numbers
|
767 | a4ccecf6 | Michael Hanselmann |
|
768 | a4ccecf6 | Michael Hanselmann | """
|
769 | a4ccecf6 | Michael Hanselmann | result = set()
|
770 | a4ccecf6 | Michael Hanselmann | |
771 | a4ccecf6 | Michael Hanselmann | signum = 0
|
772 | a4ccecf6 | Michael Hanselmann | for ch in reversed(sigset): |
773 | a4ccecf6 | Michael Hanselmann | chv = int(ch, 16) |
774 | a4ccecf6 | Michael Hanselmann | |
775 | a4ccecf6 | Michael Hanselmann | # The following could be done in a loop, but it's easier to read and
|
776 | a4ccecf6 | Michael Hanselmann | # understand in the unrolled form
|
777 | a4ccecf6 | Michael Hanselmann | if chv & 1: |
778 | a4ccecf6 | Michael Hanselmann | result.add(signum + 1)
|
779 | a4ccecf6 | Michael Hanselmann | if chv & 2: |
780 | a4ccecf6 | Michael Hanselmann | result.add(signum + 2)
|
781 | a4ccecf6 | Michael Hanselmann | if chv & 4: |
782 | a4ccecf6 | Michael Hanselmann | result.add(signum + 3)
|
783 | a4ccecf6 | Michael Hanselmann | if chv & 8: |
784 | a4ccecf6 | Michael Hanselmann | result.add(signum + 4)
|
785 | a4ccecf6 | Michael Hanselmann | |
786 | a4ccecf6 | Michael Hanselmann | signum += 4
|
787 | a4ccecf6 | Michael Hanselmann | |
788 | a4ccecf6 | Michael Hanselmann | return result
|
789 | a4ccecf6 | Michael Hanselmann | |
790 | a4ccecf6 | Michael Hanselmann | |
791 | a4ccecf6 | Michael Hanselmann | def _GetProcStatusField(pstatus, field): |
792 | a4ccecf6 | Michael Hanselmann | """Retrieves a field from the contents of a proc status file.
|
793 | a4ccecf6 | Michael Hanselmann |
|
794 | a4ccecf6 | Michael Hanselmann | @type pstatus: string
|
795 | a4ccecf6 | Michael Hanselmann | @param pstatus: Contents of /proc/$pid/status
|
796 | a4ccecf6 | Michael Hanselmann | @type field: string
|
797 | a4ccecf6 | Michael Hanselmann | @param field: Name of field whose value should be returned
|
798 | a4ccecf6 | Michael Hanselmann | @rtype: string
|
799 | a4ccecf6 | Michael Hanselmann |
|
800 | a4ccecf6 | Michael Hanselmann | """
|
801 | a4ccecf6 | Michael Hanselmann | for line in pstatus.splitlines(): |
802 | a4ccecf6 | Michael Hanselmann | parts = line.split(":", 1) |
803 | a4ccecf6 | Michael Hanselmann | |
804 | a4ccecf6 | Michael Hanselmann | if len(parts) < 2 or parts[0] != field: |
805 | a4ccecf6 | Michael Hanselmann | continue
|
806 | a4ccecf6 | Michael Hanselmann | |
807 | a4ccecf6 | Michael Hanselmann | return parts[1].strip() |
808 | a4ccecf6 | Michael Hanselmann | |
809 | a4ccecf6 | Michael Hanselmann | return None |
810 | a4ccecf6 | Michael Hanselmann | |
811 | a4ccecf6 | Michael Hanselmann | |
812 | a4ccecf6 | Michael Hanselmann | def IsProcessHandlingSignal(pid, signum, status_path=None): |
813 | a4ccecf6 | Michael Hanselmann | """Checks whether a process is handling a signal.
|
814 | a4ccecf6 | Michael Hanselmann |
|
815 | a4ccecf6 | Michael Hanselmann | @type pid: int
|
816 | a4ccecf6 | Michael Hanselmann | @param pid: Process ID
|
817 | a4ccecf6 | Michael Hanselmann | @type signum: int
|
818 | a4ccecf6 | Michael Hanselmann | @param signum: Signal number
|
819 | a4ccecf6 | Michael Hanselmann | @rtype: bool
|
820 | a4ccecf6 | Michael Hanselmann |
|
821 | a4ccecf6 | Michael Hanselmann | """
|
822 | a4ccecf6 | Michael Hanselmann | if status_path is None: |
823 | a4ccecf6 | Michael Hanselmann | status_path = _GetProcStatusPath(pid) |
824 | a4ccecf6 | Michael Hanselmann | |
825 | a4ccecf6 | Michael Hanselmann | try:
|
826 | a4ccecf6 | Michael Hanselmann | proc_status = utils_io.ReadFile(status_path) |
827 | a4ccecf6 | Michael Hanselmann | except EnvironmentError, err: |
828 | a4ccecf6 | Michael Hanselmann | # In at least one case, reading /proc/$pid/status failed with ESRCH.
|
829 | a4ccecf6 | Michael Hanselmann | if err.errno in (errno.ENOENT, errno.ENOTDIR, errno.EINVAL, errno.ESRCH): |
830 | a4ccecf6 | Michael Hanselmann | return False |
831 | a4ccecf6 | Michael Hanselmann | raise
|
832 | a4ccecf6 | Michael Hanselmann | |
833 | a4ccecf6 | Michael Hanselmann | sigcgt = _GetProcStatusField(proc_status, "SigCgt")
|
834 | a4ccecf6 | Michael Hanselmann | if sigcgt is None: |
835 | a4ccecf6 | Michael Hanselmann | raise RuntimeError("%s is missing 'SigCgt' field" % status_path) |
836 | a4ccecf6 | Michael Hanselmann | |
837 | a4ccecf6 | Michael Hanselmann | # Now check whether signal is handled
|
838 | a4ccecf6 | Michael Hanselmann | return signum in _ParseSigsetT(sigcgt) |
839 | a4ccecf6 | Michael Hanselmann | |
840 | a4ccecf6 | Michael Hanselmann | |
841 | a4ccecf6 | Michael Hanselmann | def Daemonize(logfile): |
842 | a4ccecf6 | Michael Hanselmann | """Daemonize the current process.
|
843 | a4ccecf6 | Michael Hanselmann |
|
844 | a4ccecf6 | Michael Hanselmann | This detaches the current process from the controlling terminal and
|
845 | a4ccecf6 | Michael Hanselmann | runs it in the background as a daemon.
|
846 | a4ccecf6 | Michael Hanselmann |
|
847 | a4ccecf6 | Michael Hanselmann | @type logfile: str
|
848 | a4ccecf6 | Michael Hanselmann | @param logfile: the logfile to which we should redirect stdout/stderr
|
849 | 110f49ef | Michael Hanselmann | @rtype: tuple; (int, callable)
|
850 | 110f49ef | Michael Hanselmann | @return: File descriptor of pipe(2) which must be closed to notify parent
|
851 | 110f49ef | Michael Hanselmann | process and a callable to reopen log files
|
852 | a4ccecf6 | Michael Hanselmann |
|
853 | a4ccecf6 | Michael Hanselmann | """
|
854 | a4ccecf6 | Michael Hanselmann | # pylint: disable-msg=W0212
|
855 | a4ccecf6 | Michael Hanselmann | # yes, we really want os._exit
|
856 | a4ccecf6 | Michael Hanselmann | |
857 | a4ccecf6 | Michael Hanselmann | # TODO: do another attempt to merge Daemonize and StartDaemon, or at
|
858 | a4ccecf6 | Michael Hanselmann | # least abstract the pipe functionality between them
|
859 | a4ccecf6 | Michael Hanselmann | |
860 | a4ccecf6 | Michael Hanselmann | # Create pipe for sending error messages
|
861 | a4ccecf6 | Michael Hanselmann | (rpipe, wpipe) = os.pipe() |
862 | a4ccecf6 | Michael Hanselmann | |
863 | a4ccecf6 | Michael Hanselmann | # this might fail
|
864 | a4ccecf6 | Michael Hanselmann | pid = os.fork() |
865 | a4ccecf6 | Michael Hanselmann | if (pid == 0): # The first child. |
866 | a4ccecf6 | Michael Hanselmann | SetupDaemonEnv() |
867 | a4ccecf6 | Michael Hanselmann | |
868 | a4ccecf6 | Michael Hanselmann | # this might fail
|
869 | a4ccecf6 | Michael Hanselmann | pid = os.fork() # Fork a second child.
|
870 | a4ccecf6 | Michael Hanselmann | if (pid == 0): # The second child. |
871 | a4ccecf6 | Michael Hanselmann | utils_wrapper.CloseFdNoError(rpipe) |
872 | a4ccecf6 | Michael Hanselmann | else:
|
873 | a4ccecf6 | Michael Hanselmann | # exit() or _exit()? See below.
|
874 | a4ccecf6 | Michael Hanselmann | os._exit(0) # Exit parent (the first child) of the second child. |
875 | a4ccecf6 | Michael Hanselmann | else:
|
876 | a4ccecf6 | Michael Hanselmann | utils_wrapper.CloseFdNoError(wpipe) |
877 | a4ccecf6 | Michael Hanselmann | # Wait for daemon to be started (or an error message to
|
878 | a4ccecf6 | Michael Hanselmann | # arrive) and read up to 100 KB as an error message
|
879 | a4ccecf6 | Michael Hanselmann | errormsg = utils_wrapper.RetryOnSignal(os.read, rpipe, 100 * 1024) |
880 | a4ccecf6 | Michael Hanselmann | if errormsg:
|
881 | a4ccecf6 | Michael Hanselmann | sys.stderr.write("Error when starting daemon process: %r\n" % errormsg)
|
882 | a4ccecf6 | Michael Hanselmann | rcode = 1
|
883 | a4ccecf6 | Michael Hanselmann | else:
|
884 | a4ccecf6 | Michael Hanselmann | rcode = 0
|
885 | a4ccecf6 | Michael Hanselmann | os._exit(rcode) # Exit parent of the first child.
|
886 | a4ccecf6 | Michael Hanselmann | |
887 | 110f49ef | Michael Hanselmann | reopen_fn = compat.partial(SetupDaemonFDs, logfile, None)
|
888 | 110f49ef | Michael Hanselmann | |
889 | 110f49ef | Michael Hanselmann | # Open logs for the first time
|
890 | 110f49ef | Michael Hanselmann | reopen_fn() |
891 | 110f49ef | Michael Hanselmann | |
892 | 110f49ef | Michael Hanselmann | return (wpipe, reopen_fn)
|
893 | a4ccecf6 | Michael Hanselmann | |
894 | a4ccecf6 | Michael Hanselmann | |
895 | a4ccecf6 | Michael Hanselmann | def KillProcess(pid, signal_=signal.SIGTERM, timeout=30, |
896 | a4ccecf6 | Michael Hanselmann | waitpid=False):
|
897 | a4ccecf6 | Michael Hanselmann | """Kill a process given by its pid.
|
898 | a4ccecf6 | Michael Hanselmann |
|
899 | a4ccecf6 | Michael Hanselmann | @type pid: int
|
900 | a4ccecf6 | Michael Hanselmann | @param pid: The PID to terminate.
|
901 | a4ccecf6 | Michael Hanselmann | @type signal_: int
|
902 | a4ccecf6 | Michael Hanselmann | @param signal_: The signal to send, by default SIGTERM
|
903 | a4ccecf6 | Michael Hanselmann | @type timeout: int
|
904 | a4ccecf6 | Michael Hanselmann | @param timeout: The timeout after which, if the process is still alive,
|
905 | a4ccecf6 | Michael Hanselmann | a SIGKILL will be sent. If not positive, no such checking
|
906 | a4ccecf6 | Michael Hanselmann | will be done
|
907 | a4ccecf6 | Michael Hanselmann | @type waitpid: boolean
|
908 | a4ccecf6 | Michael Hanselmann | @param waitpid: If true, we should waitpid on this process after
|
909 | a4ccecf6 | Michael Hanselmann | sending signals, since it's our own child and otherwise it
|
910 | a4ccecf6 | Michael Hanselmann | would remain as zombie
|
911 | a4ccecf6 | Michael Hanselmann |
|
912 | a4ccecf6 | Michael Hanselmann | """
|
913 | a4ccecf6 | Michael Hanselmann | def _helper(pid, signal_, wait): |
914 | a4ccecf6 | Michael Hanselmann | """Simple helper to encapsulate the kill/waitpid sequence"""
|
915 | a4ccecf6 | Michael Hanselmann | if utils_wrapper.IgnoreProcessNotFound(os.kill, pid, signal_) and wait: |
916 | a4ccecf6 | Michael Hanselmann | try:
|
917 | a4ccecf6 | Michael Hanselmann | os.waitpid(pid, os.WNOHANG) |
918 | a4ccecf6 | Michael Hanselmann | except OSError: |
919 | a4ccecf6 | Michael Hanselmann | pass
|
920 | a4ccecf6 | Michael Hanselmann | |
921 | a4ccecf6 | Michael Hanselmann | if pid <= 0: |
922 | a4ccecf6 | Michael Hanselmann | # kill with pid=0 == suicide
|
923 | a4ccecf6 | Michael Hanselmann | raise errors.ProgrammerError("Invalid pid given '%s'" % pid) |
924 | a4ccecf6 | Michael Hanselmann | |
925 | a4ccecf6 | Michael Hanselmann | if not IsProcessAlive(pid): |
926 | a4ccecf6 | Michael Hanselmann | return
|
927 | a4ccecf6 | Michael Hanselmann | |
928 | a4ccecf6 | Michael Hanselmann | _helper(pid, signal_, waitpid) |
929 | a4ccecf6 | Michael Hanselmann | |
930 | a4ccecf6 | Michael Hanselmann | if timeout <= 0: |
931 | a4ccecf6 | Michael Hanselmann | return
|
932 | a4ccecf6 | Michael Hanselmann | |
933 | a4ccecf6 | Michael Hanselmann | def _CheckProcess(): |
934 | a4ccecf6 | Michael Hanselmann | if not IsProcessAlive(pid): |
935 | a4ccecf6 | Michael Hanselmann | return
|
936 | a4ccecf6 | Michael Hanselmann | |
937 | a4ccecf6 | Michael Hanselmann | try:
|
938 | a4ccecf6 | Michael Hanselmann | (result_pid, _) = os.waitpid(pid, os.WNOHANG) |
939 | a4ccecf6 | Michael Hanselmann | except OSError: |
940 | a4ccecf6 | Michael Hanselmann | raise utils_retry.RetryAgain()
|
941 | a4ccecf6 | Michael Hanselmann | |
942 | a4ccecf6 | Michael Hanselmann | if result_pid > 0: |
943 | a4ccecf6 | Michael Hanselmann | return
|
944 | a4ccecf6 | Michael Hanselmann | |
945 | a4ccecf6 | Michael Hanselmann | raise utils_retry.RetryAgain()
|
946 | a4ccecf6 | Michael Hanselmann | |
947 | a4ccecf6 | Michael Hanselmann | try:
|
948 | a4ccecf6 | Michael Hanselmann | # Wait up to $timeout seconds
|
949 | a4ccecf6 | Michael Hanselmann | utils_retry.Retry(_CheckProcess, (0.01, 1.5, 0.1), timeout) |
950 | a4ccecf6 | Michael Hanselmann | except utils_retry.RetryTimeout:
|
951 | a4ccecf6 | Michael Hanselmann | pass
|
952 | a4ccecf6 | Michael Hanselmann | |
953 | a4ccecf6 | Michael Hanselmann | if IsProcessAlive(pid):
|
954 | a4ccecf6 | Michael Hanselmann | # Kill process if it's still alive
|
955 | a4ccecf6 | Michael Hanselmann | _helper(pid, signal.SIGKILL, waitpid) |
956 | a4ccecf6 | Michael Hanselmann | |
957 | a4ccecf6 | Michael Hanselmann | |
958 | a4ccecf6 | Michael Hanselmann | def RunInSeparateProcess(fn, *args): |
959 | a4ccecf6 | Michael Hanselmann | """Runs a function in a separate process.
|
960 | a4ccecf6 | Michael Hanselmann |
|
961 | a4ccecf6 | Michael Hanselmann | Note: Only boolean return values are supported.
|
962 | a4ccecf6 | Michael Hanselmann |
|
963 | a4ccecf6 | Michael Hanselmann | @type fn: callable
|
964 | a4ccecf6 | Michael Hanselmann | @param fn: Function to be called
|
965 | a4ccecf6 | Michael Hanselmann | @rtype: bool
|
966 | a4ccecf6 | Michael Hanselmann | @return: Function's result
|
967 | a4ccecf6 | Michael Hanselmann |
|
968 | a4ccecf6 | Michael Hanselmann | """
|
969 | a4ccecf6 | Michael Hanselmann | pid = os.fork() |
970 | a4ccecf6 | Michael Hanselmann | if pid == 0: |
971 | a4ccecf6 | Michael Hanselmann | # Child process
|
972 | a4ccecf6 | Michael Hanselmann | try:
|
973 | a4ccecf6 | Michael Hanselmann | # In case the function uses temporary files
|
974 | a4ccecf6 | Michael Hanselmann | utils_wrapper.ResetTempfileModule() |
975 | a4ccecf6 | Michael Hanselmann | |
976 | a4ccecf6 | Michael Hanselmann | # Call function
|
977 | a4ccecf6 | Michael Hanselmann | result = int(bool(fn(*args))) |
978 | a4ccecf6 | Michael Hanselmann | assert result in (0, 1) |
979 | a4ccecf6 | Michael Hanselmann | except: # pylint: disable-msg=W0702 |
980 | a4ccecf6 | Michael Hanselmann | logging.exception("Error while calling function in separate process")
|
981 | a4ccecf6 | Michael Hanselmann | # 0 and 1 are reserved for the return value
|
982 | a4ccecf6 | Michael Hanselmann | result = 33
|
983 | a4ccecf6 | Michael Hanselmann | |
984 | a4ccecf6 | Michael Hanselmann | os._exit(result) # pylint: disable-msg=W0212
|
985 | a4ccecf6 | Michael Hanselmann | |
986 | a4ccecf6 | Michael Hanselmann | # Parent process
|
987 | a4ccecf6 | Michael Hanselmann | |
988 | a4ccecf6 | Michael Hanselmann | # Avoid zombies and check exit code
|
989 | a4ccecf6 | Michael Hanselmann | (_, status) = os.waitpid(pid, 0)
|
990 | a4ccecf6 | Michael Hanselmann | |
991 | a4ccecf6 | Michael Hanselmann | if os.WIFSIGNALED(status):
|
992 | a4ccecf6 | Michael Hanselmann | exitcode = None
|
993 | a4ccecf6 | Michael Hanselmann | signum = os.WTERMSIG(status) |
994 | a4ccecf6 | Michael Hanselmann | else:
|
995 | a4ccecf6 | Michael Hanselmann | exitcode = os.WEXITSTATUS(status) |
996 | a4ccecf6 | Michael Hanselmann | signum = None
|
997 | a4ccecf6 | Michael Hanselmann | |
998 | a4ccecf6 | Michael Hanselmann | if not (exitcode in (0, 1) and signum is None): |
999 | a4ccecf6 | Michael Hanselmann | raise errors.GenericError("Child program failed (code=%s, signal=%s)" % |
1000 | a4ccecf6 | Michael Hanselmann | (exitcode, signum)) |
1001 | a4ccecf6 | Michael Hanselmann | |
1002 | a4ccecf6 | Michael Hanselmann | return bool(exitcode) |
1003 | a4ccecf6 | Michael Hanselmann | |
1004 | a4ccecf6 | Michael Hanselmann | |
1005 | a4ccecf6 | Michael Hanselmann | def CloseFDs(noclose_fds=None): |
1006 | a4ccecf6 | Michael Hanselmann | """Close file descriptors.
|
1007 | a4ccecf6 | Michael Hanselmann |
|
1008 | a4ccecf6 | Michael Hanselmann | This closes all file descriptors above 2 (i.e. except
|
1009 | a4ccecf6 | Michael Hanselmann | stdin/out/err).
|
1010 | a4ccecf6 | Michael Hanselmann |
|
1011 | a4ccecf6 | Michael Hanselmann | @type noclose_fds: list or None
|
1012 | a4ccecf6 | Michael Hanselmann | @param noclose_fds: if given, it denotes a list of file descriptor
|
1013 | a4ccecf6 | Michael Hanselmann | that should not be closed
|
1014 | a4ccecf6 | Michael Hanselmann |
|
1015 | a4ccecf6 | Michael Hanselmann | """
|
1016 | a4ccecf6 | Michael Hanselmann | # Default maximum for the number of available file descriptors.
|
1017 | a4ccecf6 | Michael Hanselmann | if 'SC_OPEN_MAX' in os.sysconf_names: |
1018 | a4ccecf6 | Michael Hanselmann | try:
|
1019 | a4ccecf6 | Michael Hanselmann | MAXFD = os.sysconf('SC_OPEN_MAX')
|
1020 | a4ccecf6 | Michael Hanselmann | if MAXFD < 0: |
1021 | a4ccecf6 | Michael Hanselmann | MAXFD = 1024
|
1022 | a4ccecf6 | Michael Hanselmann | except OSError: |
1023 | a4ccecf6 | Michael Hanselmann | MAXFD = 1024
|
1024 | a4ccecf6 | Michael Hanselmann | else:
|
1025 | a4ccecf6 | Michael Hanselmann | MAXFD = 1024
|
1026 | a4ccecf6 | Michael Hanselmann | |
1027 | a4ccecf6 | Michael Hanselmann | maxfd = resource.getrlimit(resource.RLIMIT_NOFILE)[1]
|
1028 | a4ccecf6 | Michael Hanselmann | if (maxfd == resource.RLIM_INFINITY):
|
1029 | a4ccecf6 | Michael Hanselmann | maxfd = MAXFD |
1030 | a4ccecf6 | Michael Hanselmann | |
1031 | a4ccecf6 | Michael Hanselmann | # Iterate through and close all file descriptors (except the standard ones)
|
1032 | a4ccecf6 | Michael Hanselmann | for fd in range(3, maxfd): |
1033 | a4ccecf6 | Michael Hanselmann | if noclose_fds and fd in noclose_fds: |
1034 | a4ccecf6 | Michael Hanselmann | continue
|
1035 | a4ccecf6 | Michael Hanselmann | utils_wrapper.CloseFdNoError(fd) |