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