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