Statistics
| Branch: | Tag: | Revision:

root / lib / utils / process.py @ 1ce03fb1

History | View | Annotate | Download (28.7 kB)

1 a4ccecf6 Michael Hanselmann
#
2 a4ccecf6 Michael Hanselmann
#
3 a4ccecf6 Michael Hanselmann
4 a4ccecf6 Michael Hanselmann
# Copyright (C) 2006, 2007, 2010, 2011 Google Inc.
5 a4ccecf6 Michael Hanselmann
#
6 a4ccecf6 Michael Hanselmann
# This program is free software; you can redistribute it and/or modify
7 a4ccecf6 Michael Hanselmann
# it under the terms of the GNU General Public License as published by
8 a4ccecf6 Michael Hanselmann
# the Free Software Foundation; either version 2 of the License, or
9 a4ccecf6 Michael Hanselmann
# (at your option) any later version.
10 a4ccecf6 Michael Hanselmann
#
11 a4ccecf6 Michael Hanselmann
# This program is distributed in the hope that it will be useful, but
12 a4ccecf6 Michael Hanselmann
# WITHOUT ANY WARRANTY; without even the implied warranty of
13 a4ccecf6 Michael Hanselmann
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 a4ccecf6 Michael Hanselmann
# General Public License for more details.
15 a4ccecf6 Michael Hanselmann
#
16 a4ccecf6 Michael Hanselmann
# You should have received a copy of the GNU General Public License
17 a4ccecf6 Michael Hanselmann
# along with this program; if not, write to the Free Software
18 a4ccecf6 Michael Hanselmann
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 a4ccecf6 Michael Hanselmann
# 02110-1301, USA.
20 a4ccecf6 Michael Hanselmann
21 a4ccecf6 Michael Hanselmann
"""Utility functions for processes.
22 a4ccecf6 Michael Hanselmann

23 a4ccecf6 Michael Hanselmann
"""
24 a4ccecf6 Michael Hanselmann
25 a4ccecf6 Michael Hanselmann
26 a4ccecf6 Michael Hanselmann
import os
27 a4ccecf6 Michael Hanselmann
import sys
28 a4ccecf6 Michael Hanselmann
import subprocess
29 a4ccecf6 Michael Hanselmann
import errno
30 a4ccecf6 Michael Hanselmann
import select
31 a4ccecf6 Michael Hanselmann
import logging
32 a4ccecf6 Michael Hanselmann
import signal
33 a4ccecf6 Michael Hanselmann
import resource
34 a4ccecf6 Michael Hanselmann
35 a4ccecf6 Michael Hanselmann
from cStringIO import StringIO
36 a4ccecf6 Michael Hanselmann
37 a4ccecf6 Michael Hanselmann
from ganeti import errors
38 a4ccecf6 Michael Hanselmann
from ganeti import constants
39 110f49ef Michael Hanselmann
from ganeti import compat
40 a4ccecf6 Michael Hanselmann
41 a4ccecf6 Michael Hanselmann
from ganeti.utils import retry as utils_retry
42 a4ccecf6 Michael Hanselmann
from ganeti.utils import wrapper as utils_wrapper
43 a4ccecf6 Michael Hanselmann
from ganeti.utils import text as utils_text
44 a4ccecf6 Michael Hanselmann
from ganeti.utils import io as utils_io
45 a4ccecf6 Michael Hanselmann
from ganeti.utils import algo as utils_algo
46 a4ccecf6 Michael Hanselmann
47 a4ccecf6 Michael Hanselmann
48 a4ccecf6 Michael Hanselmann
#: when set to True, L{RunCmd} is disabled
49 a4ccecf6 Michael Hanselmann
_no_fork = False
50 a4ccecf6 Michael Hanselmann
51 a4ccecf6 Michael Hanselmann
(_TIMEOUT_NONE,
52 a4ccecf6 Michael Hanselmann
 _TIMEOUT_TERM,
53 a4ccecf6 Michael Hanselmann
 _TIMEOUT_KILL) = range(3)
54 a4ccecf6 Michael Hanselmann
55 a4ccecf6 Michael Hanselmann
56 a4ccecf6 Michael Hanselmann
def DisableFork():
57 a4ccecf6 Michael Hanselmann
  """Disables the use of fork(2).
58 a4ccecf6 Michael Hanselmann

59 a4ccecf6 Michael Hanselmann
  """
60 a4ccecf6 Michael Hanselmann
  global _no_fork # pylint: disable-msg=W0603
61 a4ccecf6 Michael Hanselmann
62 a4ccecf6 Michael Hanselmann
  _no_fork = True
63 a4ccecf6 Michael Hanselmann
64 a4ccecf6 Michael Hanselmann
65 a4ccecf6 Michael Hanselmann
class RunResult(object):
66 a4ccecf6 Michael Hanselmann
  """Holds the result of running external programs.
67 a4ccecf6 Michael Hanselmann

68 a4ccecf6 Michael Hanselmann
  @type exit_code: int
69 a4ccecf6 Michael Hanselmann
  @ivar exit_code: the exit code of the program, or None (if the program
70 a4ccecf6 Michael Hanselmann
      didn't exit())
71 a4ccecf6 Michael Hanselmann
  @type signal: int or None
72 a4ccecf6 Michael Hanselmann
  @ivar signal: the signal that caused the program to finish, or None
73 a4ccecf6 Michael Hanselmann
      (if the program wasn't terminated by a signal)
74 a4ccecf6 Michael Hanselmann
  @type stdout: str
75 a4ccecf6 Michael Hanselmann
  @ivar stdout: the standard output of the program
76 a4ccecf6 Michael Hanselmann
  @type stderr: str
77 a4ccecf6 Michael Hanselmann
  @ivar stderr: the standard error of the program
78 a4ccecf6 Michael Hanselmann
  @type failed: boolean
79 a4ccecf6 Michael Hanselmann
  @ivar failed: True in case the program was
80 a4ccecf6 Michael Hanselmann
      terminated by a signal or exited with a non-zero exit code
81 a4ccecf6 Michael Hanselmann
  @ivar fail_reason: a string detailing the termination reason
82 a4ccecf6 Michael Hanselmann

83 a4ccecf6 Michael Hanselmann
  """
84 a4ccecf6 Michael Hanselmann
  __slots__ = ["exit_code", "signal", "stdout", "stderr",
85 a4ccecf6 Michael Hanselmann
               "failed", "fail_reason", "cmd"]
86 a4ccecf6 Michael Hanselmann
87 a4ccecf6 Michael Hanselmann
88 a4ccecf6 Michael Hanselmann
  def __init__(self, exit_code, signal_, stdout, stderr, cmd, timeout_action,
89 a4ccecf6 Michael Hanselmann
               timeout):
90 a4ccecf6 Michael Hanselmann
    self.cmd = cmd
91 a4ccecf6 Michael Hanselmann
    self.exit_code = exit_code
92 a4ccecf6 Michael Hanselmann
    self.signal = signal_
93 a4ccecf6 Michael Hanselmann
    self.stdout = stdout
94 a4ccecf6 Michael Hanselmann
    self.stderr = stderr
95 a4ccecf6 Michael Hanselmann
    self.failed = (signal_ is not None or exit_code != 0)
96 a4ccecf6 Michael Hanselmann
97 a4ccecf6 Michael Hanselmann
    fail_msgs = []
98 a4ccecf6 Michael Hanselmann
    if self.signal is not None:
99 a4ccecf6 Michael Hanselmann
      fail_msgs.append("terminated by signal %s" % self.signal)
100 a4ccecf6 Michael Hanselmann
    elif self.exit_code is not None:
101 a4ccecf6 Michael Hanselmann
      fail_msgs.append("exited with exit code %s" % self.exit_code)
102 a4ccecf6 Michael Hanselmann
    else:
103 a4ccecf6 Michael Hanselmann
      fail_msgs.append("unable to determine termination reason")
104 a4ccecf6 Michael Hanselmann
105 a4ccecf6 Michael Hanselmann
    if timeout_action == _TIMEOUT_TERM:
106 a4ccecf6 Michael Hanselmann
      fail_msgs.append("terminated after timeout of %.2f seconds" % timeout)
107 a4ccecf6 Michael Hanselmann
    elif timeout_action == _TIMEOUT_KILL:
108 a4ccecf6 Michael Hanselmann
      fail_msgs.append(("force termination after timeout of %.2f seconds"
109 a4ccecf6 Michael Hanselmann
                        " and linger for another %.2f seconds") %
110 a4ccecf6 Michael Hanselmann
                       (timeout, constants.CHILD_LINGER_TIMEOUT))
111 a4ccecf6 Michael Hanselmann
112 a4ccecf6 Michael Hanselmann
    if fail_msgs and self.failed:
113 a4ccecf6 Michael Hanselmann
      self.fail_reason = utils_text.CommaJoin(fail_msgs)
114 a4ccecf6 Michael Hanselmann
115 a4ccecf6 Michael Hanselmann
    if self.failed:
116 a4ccecf6 Michael Hanselmann
      logging.debug("Command '%s' failed (%s); output: %s",
117 a4ccecf6 Michael Hanselmann
                    self.cmd, self.fail_reason, self.output)
118 a4ccecf6 Michael Hanselmann
119 a4ccecf6 Michael Hanselmann
  def _GetOutput(self):
120 a4ccecf6 Michael Hanselmann
    """Returns the combined stdout and stderr for easier usage.
121 a4ccecf6 Michael Hanselmann

122 a4ccecf6 Michael Hanselmann
    """
123 a4ccecf6 Michael Hanselmann
    return self.stdout + self.stderr
124 a4ccecf6 Michael Hanselmann
125 a4ccecf6 Michael Hanselmann
  output = property(_GetOutput, None, None, "Return full output")
126 a4ccecf6 Michael Hanselmann
127 a4ccecf6 Michael Hanselmann
128 a4ccecf6 Michael Hanselmann
def _BuildCmdEnvironment(env, reset):
129 a4ccecf6 Michael Hanselmann
  """Builds the environment for an external program.
130 a4ccecf6 Michael Hanselmann

131 a4ccecf6 Michael Hanselmann
  """
132 a4ccecf6 Michael Hanselmann
  if reset:
133 a4ccecf6 Michael Hanselmann
    cmd_env = {}
134 a4ccecf6 Michael Hanselmann
  else:
135 a4ccecf6 Michael Hanselmann
    cmd_env = os.environ.copy()
136 a4ccecf6 Michael Hanselmann
    cmd_env["LC_ALL"] = "C"
137 a4ccecf6 Michael Hanselmann
138 a4ccecf6 Michael Hanselmann
  if env is not None:
139 a4ccecf6 Michael Hanselmann
    cmd_env.update(env)
140 a4ccecf6 Michael Hanselmann
141 a4ccecf6 Michael Hanselmann
  return cmd_env
142 a4ccecf6 Michael Hanselmann
143 a4ccecf6 Michael Hanselmann
144 a4ccecf6 Michael Hanselmann
def RunCmd(cmd, env=None, output=None, cwd="/", reset_env=False,
145 d6491981 Renรฉ Nussbaumer
           interactive=False, timeout=None, noclose_fds=None,
146 d6491981 Renรฉ Nussbaumer
           _postfork_fn=None):
147 a4ccecf6 Michael Hanselmann
  """Execute a (shell) command.
148 a4ccecf6 Michael Hanselmann

149 a4ccecf6 Michael Hanselmann
  The command should not read from its standard input, as it will be
150 a4ccecf6 Michael Hanselmann
  closed.
151 a4ccecf6 Michael Hanselmann

152 a4ccecf6 Michael Hanselmann
  @type cmd: string or list
153 a4ccecf6 Michael Hanselmann
  @param cmd: Command to run
154 a4ccecf6 Michael Hanselmann
  @type env: dict
155 a4ccecf6 Michael Hanselmann
  @param env: Additional environment variables
156 a4ccecf6 Michael Hanselmann
  @type output: str
157 a4ccecf6 Michael Hanselmann
  @param output: if desired, the output of the command can be
158 a4ccecf6 Michael Hanselmann
      saved in a file instead of the RunResult instance; this
159 a4ccecf6 Michael Hanselmann
      parameter denotes the file name (if not None)
160 a4ccecf6 Michael Hanselmann
  @type cwd: string
161 a4ccecf6 Michael Hanselmann
  @param cwd: if specified, will be used as the working
162 a4ccecf6 Michael Hanselmann
      directory for the command; the default will be /
163 a4ccecf6 Michael Hanselmann
  @type reset_env: boolean
164 a4ccecf6 Michael Hanselmann
  @param reset_env: whether to reset or keep the default os environment
165 a4ccecf6 Michael Hanselmann
  @type interactive: boolean
166 a4ccecf6 Michael Hanselmann
  @param interactive: weather we pipe stdin, stdout and stderr
167 a4ccecf6 Michael Hanselmann
                      (default behaviour) or run the command interactive
168 a4ccecf6 Michael Hanselmann
  @type timeout: int
169 a4ccecf6 Michael Hanselmann
  @param timeout: If not None, timeout in seconds until child process gets
170 a4ccecf6 Michael Hanselmann
                  killed
171 7b0bf9cd Apollon Oikonomopoulos
  @type noclose_fds: list
172 7b0bf9cd Apollon Oikonomopoulos
  @param noclose_fds: list of additional (fd >=3) file descriptors to leave
173 7b0bf9cd Apollon Oikonomopoulos
                      open for the child process
174 d6491981 Renรฉ Nussbaumer
  @param _postfork_fn: Callback run after fork but before timeout (unittest)
175 a4ccecf6 Michael Hanselmann
  @rtype: L{RunResult}
176 a4ccecf6 Michael Hanselmann
  @return: RunResult instance
177 a4ccecf6 Michael Hanselmann
  @raise errors.ProgrammerError: if we call this when forks are disabled
178 a4ccecf6 Michael Hanselmann

179 a4ccecf6 Michael Hanselmann
  """
180 a4ccecf6 Michael Hanselmann
  if _no_fork:
181 a4ccecf6 Michael Hanselmann
    raise errors.ProgrammerError("utils.RunCmd() called with fork() disabled")
182 a4ccecf6 Michael Hanselmann
183 a4ccecf6 Michael Hanselmann
  if output and interactive:
184 a4ccecf6 Michael Hanselmann
    raise errors.ProgrammerError("Parameters 'output' and 'interactive' can"
185 a4ccecf6 Michael Hanselmann
                                 " not be provided at the same time")
186 a4ccecf6 Michael Hanselmann
187 a4ccecf6 Michael Hanselmann
  if isinstance(cmd, basestring):
188 a4ccecf6 Michael Hanselmann
    strcmd = cmd
189 a4ccecf6 Michael Hanselmann
    shell = True
190 a4ccecf6 Michael Hanselmann
  else:
191 a4ccecf6 Michael Hanselmann
    cmd = [str(val) for val in cmd]
192 a4ccecf6 Michael Hanselmann
    strcmd = utils_text.ShellQuoteArgs(cmd)
193 a4ccecf6 Michael Hanselmann
    shell = False
194 a4ccecf6 Michael Hanselmann
195 a4ccecf6 Michael Hanselmann
  if output:
196 a4ccecf6 Michael Hanselmann
    logging.debug("RunCmd %s, output file '%s'", strcmd, output)
197 a4ccecf6 Michael Hanselmann
  else:
198 a4ccecf6 Michael Hanselmann
    logging.debug("RunCmd %s", strcmd)
199 a4ccecf6 Michael Hanselmann
200 a4ccecf6 Michael Hanselmann
  cmd_env = _BuildCmdEnvironment(env, reset_env)
201 a4ccecf6 Michael Hanselmann
202 a4ccecf6 Michael Hanselmann
  try:
203 a4ccecf6 Michael Hanselmann
    if output is None:
204 a4ccecf6 Michael Hanselmann
      out, err, status, timeout_action = _RunCmdPipe(cmd, cmd_env, shell, cwd,
205 7b0bf9cd Apollon Oikonomopoulos
                                                     interactive, timeout,
206 d6491981 Renรฉ Nussbaumer
                                                     noclose_fds,
207 d6491981 Renรฉ Nussbaumer
                                                     _postfork_fn=_postfork_fn)
208 a4ccecf6 Michael Hanselmann
    else:
209 d6491981 Renรฉ Nussbaumer
      assert _postfork_fn is None, \
210 d6491981 Renรฉ Nussbaumer
          "_postfork_fn not supported if output provided"
211 a4ccecf6 Michael Hanselmann
      timeout_action = _TIMEOUT_NONE
212 7b0bf9cd Apollon Oikonomopoulos
      status = _RunCmdFile(cmd, cmd_env, shell, output, cwd, noclose_fds)
213 a4ccecf6 Michael Hanselmann
      out = err = ""
214 a4ccecf6 Michael Hanselmann
  except OSError, err:
215 a4ccecf6 Michael Hanselmann
    if err.errno == errno.ENOENT:
216 a4ccecf6 Michael Hanselmann
      raise errors.OpExecError("Can't execute '%s': not found (%s)" %
217 a4ccecf6 Michael Hanselmann
                               (strcmd, err))
218 a4ccecf6 Michael Hanselmann
    else:
219 a4ccecf6 Michael Hanselmann
      raise
220 a4ccecf6 Michael Hanselmann
221 a4ccecf6 Michael Hanselmann
  if status >= 0:
222 a4ccecf6 Michael Hanselmann
    exitcode = status
223 a4ccecf6 Michael Hanselmann
    signal_ = None
224 a4ccecf6 Michael Hanselmann
  else:
225 a4ccecf6 Michael Hanselmann
    exitcode = None
226 a4ccecf6 Michael Hanselmann
    signal_ = -status
227 a4ccecf6 Michael Hanselmann
228 a4ccecf6 Michael Hanselmann
  return RunResult(exitcode, signal_, out, err, strcmd, timeout_action, timeout)
229 a4ccecf6 Michael Hanselmann
230 a4ccecf6 Michael Hanselmann
231 a4ccecf6 Michael Hanselmann
def SetupDaemonEnv(cwd="/", umask=077):
232 a4ccecf6 Michael Hanselmann
  """Setup a daemon's environment.
233 a4ccecf6 Michael Hanselmann

234 a4ccecf6 Michael Hanselmann
  This should be called between the first and second fork, due to
235 a4ccecf6 Michael Hanselmann
  setsid usage.
236 a4ccecf6 Michael Hanselmann

237 a4ccecf6 Michael Hanselmann
  @param cwd: the directory to which to chdir
238 a4ccecf6 Michael Hanselmann
  @param umask: the umask to setup
239 a4ccecf6 Michael Hanselmann

240 a4ccecf6 Michael Hanselmann
  """
241 a4ccecf6 Michael Hanselmann
  os.chdir(cwd)
242 a4ccecf6 Michael Hanselmann
  os.umask(umask)
243 a4ccecf6 Michael Hanselmann
  os.setsid()
244 a4ccecf6 Michael Hanselmann
245 a4ccecf6 Michael Hanselmann
246 a4ccecf6 Michael Hanselmann
def SetupDaemonFDs(output_file, output_fd):
247 a4ccecf6 Michael Hanselmann
  """Setups up a daemon's file descriptors.
248 a4ccecf6 Michael Hanselmann

249 a4ccecf6 Michael Hanselmann
  @param output_file: if not None, the file to which to redirect
250 a4ccecf6 Michael Hanselmann
      stdout/stderr
251 a4ccecf6 Michael Hanselmann
  @param output_fd: if not None, the file descriptor for stdout/stderr
252 a4ccecf6 Michael Hanselmann

253 a4ccecf6 Michael Hanselmann
  """
254 a4ccecf6 Michael Hanselmann
  # check that at most one is defined
255 a4ccecf6 Michael Hanselmann
  assert [output_file, output_fd].count(None) >= 1
256 a4ccecf6 Michael Hanselmann
257 a4ccecf6 Michael Hanselmann
  # Open /dev/null (read-only, only for stdin)
258 a4ccecf6 Michael Hanselmann
  devnull_fd = os.open(os.devnull, os.O_RDONLY)
259 a4ccecf6 Michael Hanselmann
260 638ac34b Michael Hanselmann
  output_close = True
261 638ac34b Michael Hanselmann
262 a4ccecf6 Michael Hanselmann
  if output_fd is not None:
263 638ac34b Michael Hanselmann
    output_close = False
264 a4ccecf6 Michael Hanselmann
  elif output_file is not None:
265 a4ccecf6 Michael Hanselmann
    # Open output file
266 a4ccecf6 Michael Hanselmann
    try:
267 a4ccecf6 Michael Hanselmann
      output_fd = os.open(output_file,
268 a4ccecf6 Michael Hanselmann
                          os.O_WRONLY | os.O_CREAT | os.O_APPEND, 0600)
269 a4ccecf6 Michael Hanselmann
    except EnvironmentError, err:
270 a4ccecf6 Michael Hanselmann
      raise Exception("Opening output file failed: %s" % err)
271 a4ccecf6 Michael Hanselmann
  else:
272 a4ccecf6 Michael Hanselmann
    output_fd = os.open(os.devnull, os.O_WRONLY)
273 a4ccecf6 Michael Hanselmann
274 a4ccecf6 Michael Hanselmann
  # Redirect standard I/O
275 a4ccecf6 Michael Hanselmann
  os.dup2(devnull_fd, 0)
276 a4ccecf6 Michael Hanselmann
  os.dup2(output_fd, 1)
277 a4ccecf6 Michael Hanselmann
  os.dup2(output_fd, 2)
278 a4ccecf6 Michael Hanselmann
279 638ac34b Michael Hanselmann
  if devnull_fd > 2:
280 638ac34b Michael Hanselmann
    utils_wrapper.CloseFdNoError(devnull_fd)
281 638ac34b Michael Hanselmann
282 638ac34b Michael Hanselmann
  if output_close and output_fd > 2:
283 638ac34b Michael Hanselmann
    utils_wrapper.CloseFdNoError(output_fd)
284 638ac34b Michael Hanselmann
285 a4ccecf6 Michael Hanselmann
286 a4ccecf6 Michael Hanselmann
def StartDaemon(cmd, env=None, cwd="/", output=None, output_fd=None,
287 a4ccecf6 Michael Hanselmann
                pidfile=None):
288 a4ccecf6 Michael Hanselmann
  """Start a daemon process after forking twice.
289 a4ccecf6 Michael Hanselmann

290 a4ccecf6 Michael Hanselmann
  @type cmd: string or list
291 a4ccecf6 Michael Hanselmann
  @param cmd: Command to run
292 a4ccecf6 Michael Hanselmann
  @type env: dict
293 a4ccecf6 Michael Hanselmann
  @param env: Additional environment variables
294 a4ccecf6 Michael Hanselmann
  @type cwd: string
295 a4ccecf6 Michael Hanselmann
  @param cwd: Working directory for the program
296 a4ccecf6 Michael Hanselmann
  @type output: string
297 a4ccecf6 Michael Hanselmann
  @param output: Path to file in which to save the output
298 a4ccecf6 Michael Hanselmann
  @type output_fd: int
299 a4ccecf6 Michael Hanselmann
  @param output_fd: File descriptor for output
300 a4ccecf6 Michael Hanselmann
  @type pidfile: string
301 a4ccecf6 Michael Hanselmann
  @param pidfile: Process ID file
302 a4ccecf6 Michael Hanselmann
  @rtype: int
303 a4ccecf6 Michael Hanselmann
  @return: Daemon process ID
304 a4ccecf6 Michael Hanselmann
  @raise errors.ProgrammerError: if we call this when forks are disabled
305 a4ccecf6 Michael Hanselmann

306 a4ccecf6 Michael Hanselmann
  """
307 a4ccecf6 Michael Hanselmann
  if _no_fork:
308 a4ccecf6 Michael Hanselmann
    raise errors.ProgrammerError("utils.StartDaemon() called with fork()"
309 a4ccecf6 Michael Hanselmann
                                 " disabled")
310 a4ccecf6 Michael Hanselmann
311 a4ccecf6 Michael Hanselmann
  if output and not (bool(output) ^ (output_fd is not None)):
312 a4ccecf6 Michael Hanselmann
    raise errors.ProgrammerError("Only one of 'output' and 'output_fd' can be"
313 a4ccecf6 Michael Hanselmann
                                 " specified")
314 a4ccecf6 Michael Hanselmann
315 a4ccecf6 Michael Hanselmann
  if isinstance(cmd, basestring):
316 a4ccecf6 Michael Hanselmann
    cmd = ["/bin/sh", "-c", cmd]
317 a4ccecf6 Michael Hanselmann
318 a4ccecf6 Michael Hanselmann
  strcmd = utils_text.ShellQuoteArgs(cmd)
319 a4ccecf6 Michael Hanselmann
320 a4ccecf6 Michael Hanselmann
  if output:
321 a4ccecf6 Michael Hanselmann
    logging.debug("StartDaemon %s, output file '%s'", strcmd, output)
322 a4ccecf6 Michael Hanselmann
  else:
323 a4ccecf6 Michael Hanselmann
    logging.debug("StartDaemon %s", strcmd)
324 a4ccecf6 Michael Hanselmann
325 a4ccecf6 Michael Hanselmann
  cmd_env = _BuildCmdEnvironment(env, False)
326 a4ccecf6 Michael Hanselmann
327 a4ccecf6 Michael Hanselmann
  # Create pipe for sending PID back
328 a4ccecf6 Michael Hanselmann
  (pidpipe_read, pidpipe_write) = os.pipe()
329 a4ccecf6 Michael Hanselmann
  try:
330 a4ccecf6 Michael Hanselmann
    try:
331 a4ccecf6 Michael Hanselmann
      # Create pipe for sending error messages
332 a4ccecf6 Michael Hanselmann
      (errpipe_read, errpipe_write) = os.pipe()
333 a4ccecf6 Michael Hanselmann
      try:
334 a4ccecf6 Michael Hanselmann
        try:
335 a4ccecf6 Michael Hanselmann
          # First fork
336 a4ccecf6 Michael Hanselmann
          pid = os.fork()
337 a4ccecf6 Michael Hanselmann
          if pid == 0:
338 a4ccecf6 Michael Hanselmann
            try:
339 a4ccecf6 Michael Hanselmann
              # Child process, won't return
340 a4ccecf6 Michael Hanselmann
              _StartDaemonChild(errpipe_read, errpipe_write,
341 a4ccecf6 Michael Hanselmann
                                pidpipe_read, pidpipe_write,
342 a4ccecf6 Michael Hanselmann
                                cmd, cmd_env, cwd,
343 a4ccecf6 Michael Hanselmann
                                output, output_fd, pidfile)
344 a4ccecf6 Michael Hanselmann
            finally:
345 a4ccecf6 Michael Hanselmann
              # Well, maybe child process failed
346 a4ccecf6 Michael Hanselmann
              os._exit(1) # pylint: disable-msg=W0212
347 a4ccecf6 Michael Hanselmann
        finally:
348 a4ccecf6 Michael Hanselmann
          utils_wrapper.CloseFdNoError(errpipe_write)
349 a4ccecf6 Michael Hanselmann
350 a4ccecf6 Michael Hanselmann
        # Wait for daemon to be started (or an error message to
351 a4ccecf6 Michael Hanselmann
        # arrive) and read up to 100 KB as an error message
352 a4ccecf6 Michael Hanselmann
        errormsg = utils_wrapper.RetryOnSignal(os.read, errpipe_read,
353 a4ccecf6 Michael Hanselmann
                                               100 * 1024)
354 a4ccecf6 Michael Hanselmann
      finally:
355 a4ccecf6 Michael Hanselmann
        utils_wrapper.CloseFdNoError(errpipe_read)
356 a4ccecf6 Michael Hanselmann
    finally:
357 a4ccecf6 Michael Hanselmann
      utils_wrapper.CloseFdNoError(pidpipe_write)
358 a4ccecf6 Michael Hanselmann
359 a4ccecf6 Michael Hanselmann
    # Read up to 128 bytes for PID
360 a4ccecf6 Michael Hanselmann
    pidtext = utils_wrapper.RetryOnSignal(os.read, pidpipe_read, 128)
361 a4ccecf6 Michael Hanselmann
  finally:
362 a4ccecf6 Michael Hanselmann
    utils_wrapper.CloseFdNoError(pidpipe_read)
363 a4ccecf6 Michael Hanselmann
364 a4ccecf6 Michael Hanselmann
  # Try to avoid zombies by waiting for child process
365 a4ccecf6 Michael Hanselmann
  try:
366 a4ccecf6 Michael Hanselmann
    os.waitpid(pid, 0)
367 a4ccecf6 Michael Hanselmann
  except OSError:
368 a4ccecf6 Michael Hanselmann
    pass
369 a4ccecf6 Michael Hanselmann
370 a4ccecf6 Michael Hanselmann
  if errormsg:
371 a4ccecf6 Michael Hanselmann
    raise errors.OpExecError("Error when starting daemon process: %r" %
372 a4ccecf6 Michael Hanselmann
                             errormsg)
373 a4ccecf6 Michael Hanselmann
374 a4ccecf6 Michael Hanselmann
  try:
375 a4ccecf6 Michael Hanselmann
    return int(pidtext)
376 a4ccecf6 Michael Hanselmann
  except (ValueError, TypeError), err:
377 a4ccecf6 Michael Hanselmann
    raise errors.OpExecError("Error while trying to parse PID %r: %s" %
378 a4ccecf6 Michael Hanselmann
                             (pidtext, err))
379 a4ccecf6 Michael Hanselmann
380 a4ccecf6 Michael Hanselmann
381 a4ccecf6 Michael Hanselmann
def _StartDaemonChild(errpipe_read, errpipe_write,
382 a4ccecf6 Michael Hanselmann
                      pidpipe_read, pidpipe_write,
383 a4ccecf6 Michael Hanselmann
                      args, env, cwd,
384 a4ccecf6 Michael Hanselmann
                      output, fd_output, pidfile):
385 a4ccecf6 Michael Hanselmann
  """Child process for starting daemon.
386 a4ccecf6 Michael Hanselmann

387 a4ccecf6 Michael Hanselmann
  """
388 a4ccecf6 Michael Hanselmann
  try:
389 a4ccecf6 Michael Hanselmann
    # Close parent's side
390 a4ccecf6 Michael Hanselmann
    utils_wrapper.CloseFdNoError(errpipe_read)
391 a4ccecf6 Michael Hanselmann
    utils_wrapper.CloseFdNoError(pidpipe_read)
392 a4ccecf6 Michael Hanselmann
393 a4ccecf6 Michael Hanselmann
    # First child process
394 a4ccecf6 Michael Hanselmann
    SetupDaemonEnv()
395 a4ccecf6 Michael Hanselmann
396 a4ccecf6 Michael Hanselmann
    # And fork for the second time
397 a4ccecf6 Michael Hanselmann
    pid = os.fork()
398 a4ccecf6 Michael Hanselmann
    if pid != 0:
399 a4ccecf6 Michael Hanselmann
      # Exit first child process
400 a4ccecf6 Michael Hanselmann
      os._exit(0) # pylint: disable-msg=W0212
401 a4ccecf6 Michael Hanselmann
402 a4ccecf6 Michael Hanselmann
    # Make sure pipe is closed on execv* (and thereby notifies
403 a4ccecf6 Michael Hanselmann
    # original process)
404 a4ccecf6 Michael Hanselmann
    utils_wrapper.SetCloseOnExecFlag(errpipe_write, True)
405 a4ccecf6 Michael Hanselmann
406 a4ccecf6 Michael Hanselmann
    # List of file descriptors to be left open
407 a4ccecf6 Michael Hanselmann
    noclose_fds = [errpipe_write]
408 a4ccecf6 Michael Hanselmann
409 a4ccecf6 Michael Hanselmann
    # Open PID file
410 a4ccecf6 Michael Hanselmann
    if pidfile:
411 a4ccecf6 Michael Hanselmann
      fd_pidfile = utils_io.WritePidFile(pidfile)
412 a4ccecf6 Michael Hanselmann
413 a4ccecf6 Michael Hanselmann
      # Keeping the file open to hold the lock
414 a4ccecf6 Michael Hanselmann
      noclose_fds.append(fd_pidfile)
415 a4ccecf6 Michael Hanselmann
416 a4ccecf6 Michael Hanselmann
      utils_wrapper.SetCloseOnExecFlag(fd_pidfile, False)
417 a4ccecf6 Michael Hanselmann
    else:
418 a4ccecf6 Michael Hanselmann
      fd_pidfile = None
419 a4ccecf6 Michael Hanselmann
420 a4ccecf6 Michael Hanselmann
    SetupDaemonFDs(output, fd_output)
421 a4ccecf6 Michael Hanselmann
422 a4ccecf6 Michael Hanselmann
    # Send daemon PID to parent
423 a4ccecf6 Michael Hanselmann
    utils_wrapper.RetryOnSignal(os.write, pidpipe_write, str(os.getpid()))
424 a4ccecf6 Michael Hanselmann
425 a4ccecf6 Michael Hanselmann
    # Close all file descriptors except stdio and error message pipe
426 a4ccecf6 Michael Hanselmann
    CloseFDs(noclose_fds=noclose_fds)
427 a4ccecf6 Michael Hanselmann
428 a4ccecf6 Michael Hanselmann
    # Change working directory
429 a4ccecf6 Michael Hanselmann
    os.chdir(cwd)
430 a4ccecf6 Michael Hanselmann
431 a4ccecf6 Michael Hanselmann
    if env is None:
432 a4ccecf6 Michael Hanselmann
      os.execvp(args[0], args)
433 a4ccecf6 Michael Hanselmann
    else:
434 a4ccecf6 Michael Hanselmann
      os.execvpe(args[0], args, env)
435 a4ccecf6 Michael Hanselmann
  except: # pylint: disable-msg=W0702
436 a4ccecf6 Michael Hanselmann
    try:
437 a4ccecf6 Michael Hanselmann
      # Report errors to original process
438 a4ccecf6 Michael Hanselmann
      WriteErrorToFD(errpipe_write, str(sys.exc_info()[1]))
439 a4ccecf6 Michael Hanselmann
    except: # pylint: disable-msg=W0702
440 a4ccecf6 Michael Hanselmann
      # Ignore errors in error handling
441 a4ccecf6 Michael Hanselmann
      pass
442 a4ccecf6 Michael Hanselmann
443 a4ccecf6 Michael Hanselmann
  os._exit(1) # pylint: disable-msg=W0212
444 a4ccecf6 Michael Hanselmann
445 a4ccecf6 Michael Hanselmann
446 a4ccecf6 Michael Hanselmann
def WriteErrorToFD(fd, err):
447 a4ccecf6 Michael Hanselmann
  """Possibly write an error message to a fd.
448 a4ccecf6 Michael Hanselmann

449 a4ccecf6 Michael Hanselmann
  @type fd: None or int (file descriptor)
450 a4ccecf6 Michael Hanselmann
  @param fd: if not None, the error will be written to this fd
451 a4ccecf6 Michael Hanselmann
  @param err: string, the error message
452 a4ccecf6 Michael Hanselmann

453 a4ccecf6 Michael Hanselmann
  """
454 a4ccecf6 Michael Hanselmann
  if fd is None:
455 a4ccecf6 Michael Hanselmann
    return
456 a4ccecf6 Michael Hanselmann
457 a4ccecf6 Michael Hanselmann
  if not err:
458 a4ccecf6 Michael Hanselmann
    err = "<unknown error>"
459 a4ccecf6 Michael Hanselmann
460 a4ccecf6 Michael Hanselmann
  utils_wrapper.RetryOnSignal(os.write, fd, err)
461 a4ccecf6 Michael Hanselmann
462 a4ccecf6 Michael Hanselmann
463 a4ccecf6 Michael Hanselmann
def _CheckIfAlive(child):
464 a4ccecf6 Michael Hanselmann
  """Raises L{utils_retry.RetryAgain} if child is still alive.
465 a4ccecf6 Michael Hanselmann

466 a4ccecf6 Michael Hanselmann
  @raises utils_retry.RetryAgain: If child is still alive
467 a4ccecf6 Michael Hanselmann

468 a4ccecf6 Michael Hanselmann
  """
469 a4ccecf6 Michael Hanselmann
  if child.poll() is None:
470 a4ccecf6 Michael Hanselmann
    raise utils_retry.RetryAgain()
471 a4ccecf6 Michael Hanselmann
472 a4ccecf6 Michael Hanselmann
473 a4ccecf6 Michael Hanselmann
def _WaitForProcess(child, timeout):
474 a4ccecf6 Michael Hanselmann
  """Waits for the child to terminate or until we reach timeout.
475 a4ccecf6 Michael Hanselmann

476 a4ccecf6 Michael Hanselmann
  """
477 a4ccecf6 Michael Hanselmann
  try:
478 a4ccecf6 Michael Hanselmann
    utils_retry.Retry(_CheckIfAlive, (1.0, 1.2, 5.0), max(0, timeout),
479 a4ccecf6 Michael Hanselmann
                      args=[child])
480 a4ccecf6 Michael Hanselmann
  except utils_retry.RetryTimeout:
481 a4ccecf6 Michael Hanselmann
    pass
482 a4ccecf6 Michael Hanselmann
483 a4ccecf6 Michael Hanselmann
484 7b0bf9cd Apollon Oikonomopoulos
def _RunCmdPipe(cmd, env, via_shell, cwd, interactive, timeout, noclose_fds,
485 d6491981 Renรฉ Nussbaumer
                _linger_timeout=constants.CHILD_LINGER_TIMEOUT,
486 d6491981 Renรฉ Nussbaumer
                _postfork_fn=None):
487 a4ccecf6 Michael Hanselmann
  """Run a command and return its output.
488 a4ccecf6 Michael Hanselmann

489 a4ccecf6 Michael Hanselmann
  @type  cmd: string or list
490 a4ccecf6 Michael Hanselmann
  @param cmd: Command to run
491 a4ccecf6 Michael Hanselmann
  @type env: dict
492 a4ccecf6 Michael Hanselmann
  @param env: The environment to use
493 a4ccecf6 Michael Hanselmann
  @type via_shell: bool
494 a4ccecf6 Michael Hanselmann
  @param via_shell: if we should run via the shell
495 a4ccecf6 Michael Hanselmann
  @type cwd: string
496 a4ccecf6 Michael Hanselmann
  @param cwd: the working directory for the program
497 a4ccecf6 Michael Hanselmann
  @type interactive: boolean
498 a4ccecf6 Michael Hanselmann
  @param interactive: Run command interactive (without piping)
499 a4ccecf6 Michael Hanselmann
  @type timeout: int
500 a4ccecf6 Michael Hanselmann
  @param timeout: Timeout after the programm gets terminated
501 7b0bf9cd Apollon Oikonomopoulos
  @type noclose_fds: list
502 7b0bf9cd Apollon Oikonomopoulos
  @param noclose_fds: list of additional (fd >=3) file descriptors to leave
503 7b0bf9cd Apollon Oikonomopoulos
                      open for the child process
504 d6491981 Renรฉ Nussbaumer
  @param _postfork_fn: Function run after fork but before timeout (unittest)
505 a4ccecf6 Michael Hanselmann
  @rtype: tuple
506 a4ccecf6 Michael Hanselmann
  @return: (out, err, status)
507 a4ccecf6 Michael Hanselmann

508 a4ccecf6 Michael Hanselmann
  """
509 a4ccecf6 Michael Hanselmann
  poller = select.poll()
510 a4ccecf6 Michael Hanselmann
511 a4ccecf6 Michael Hanselmann
  stderr = subprocess.PIPE
512 a4ccecf6 Michael Hanselmann
  stdout = subprocess.PIPE
513 a4ccecf6 Michael Hanselmann
  stdin = subprocess.PIPE
514 a4ccecf6 Michael Hanselmann
515 a4ccecf6 Michael Hanselmann
  if interactive:
516 a4ccecf6 Michael Hanselmann
    stderr = stdout = stdin = None
517 a4ccecf6 Michael Hanselmann
518 7b0bf9cd Apollon Oikonomopoulos
  if noclose_fds:
519 7b0bf9cd Apollon Oikonomopoulos
    preexec_fn = lambda: CloseFDs(noclose_fds)
520 7b0bf9cd Apollon Oikonomopoulos
    close_fds = False
521 7b0bf9cd Apollon Oikonomopoulos
  else:
522 7b0bf9cd Apollon Oikonomopoulos
    preexec_fn = None
523 7b0bf9cd Apollon Oikonomopoulos
    close_fds = True
524 7b0bf9cd Apollon Oikonomopoulos
525 a4ccecf6 Michael Hanselmann
  child = subprocess.Popen(cmd, shell=via_shell,
526 a4ccecf6 Michael Hanselmann
                           stderr=stderr,
527 a4ccecf6 Michael Hanselmann
                           stdout=stdout,
528 a4ccecf6 Michael Hanselmann
                           stdin=stdin,
529 7b0bf9cd Apollon Oikonomopoulos
                           close_fds=close_fds, env=env,
530 7b0bf9cd Apollon Oikonomopoulos
                           cwd=cwd,
531 7b0bf9cd Apollon Oikonomopoulos
                           preexec_fn=preexec_fn)
532 a4ccecf6 Michael Hanselmann
533 d6491981 Renรฉ Nussbaumer
  if _postfork_fn:
534 d6491981 Renรฉ Nussbaumer
    _postfork_fn(child.pid)
535 d6491981 Renรฉ Nussbaumer
536 a4ccecf6 Michael Hanselmann
  out = StringIO()
537 a4ccecf6 Michael Hanselmann
  err = StringIO()
538 a4ccecf6 Michael Hanselmann
539 a4ccecf6 Michael Hanselmann
  linger_timeout = None
540 a4ccecf6 Michael Hanselmann
541 a4ccecf6 Michael Hanselmann
  if timeout is None:
542 a4ccecf6 Michael Hanselmann
    poll_timeout = None
543 a4ccecf6 Michael Hanselmann
  else:
544 a4ccecf6 Michael Hanselmann
    poll_timeout = utils_algo.RunningTimeout(timeout, True).Remaining
545 a4ccecf6 Michael Hanselmann
546 a4ccecf6 Michael Hanselmann
  msg_timeout = ("Command %s (%d) run into execution timeout, terminating" %
547 a4ccecf6 Michael Hanselmann
                 (cmd, child.pid))
548 a4ccecf6 Michael Hanselmann
  msg_linger = ("Command %s (%d) run into linger timeout, killing" %
549 a4ccecf6 Michael Hanselmann
                (cmd, child.pid))
550 a4ccecf6 Michael Hanselmann
551 a4ccecf6 Michael Hanselmann
  timeout_action = _TIMEOUT_NONE
552 a4ccecf6 Michael Hanselmann
553 a4ccecf6 Michael Hanselmann
  if not interactive:
554 a4ccecf6 Michael Hanselmann
    child.stdin.close()
555 a4ccecf6 Michael Hanselmann
    poller.register(child.stdout, select.POLLIN)
556 a4ccecf6 Michael Hanselmann
    poller.register(child.stderr, select.POLLIN)
557 a4ccecf6 Michael Hanselmann
    fdmap = {
558 a4ccecf6 Michael Hanselmann
      child.stdout.fileno(): (out, child.stdout),
559 a4ccecf6 Michael Hanselmann
      child.stderr.fileno(): (err, child.stderr),
560 a4ccecf6 Michael Hanselmann
      }
561 a4ccecf6 Michael Hanselmann
    for fd in fdmap:
562 a4ccecf6 Michael Hanselmann
      utils_wrapper.SetNonblockFlag(fd, True)
563 a4ccecf6 Michael Hanselmann
564 a4ccecf6 Michael Hanselmann
    while fdmap:
565 a4ccecf6 Michael Hanselmann
      if poll_timeout:
566 a4ccecf6 Michael Hanselmann
        pt = poll_timeout() * 1000
567 a4ccecf6 Michael Hanselmann
        if pt < 0:
568 a4ccecf6 Michael Hanselmann
          if linger_timeout is None:
569 a4ccecf6 Michael Hanselmann
            logging.warning(msg_timeout)
570 a4ccecf6 Michael Hanselmann
            if child.poll() is None:
571 a4ccecf6 Michael Hanselmann
              timeout_action = _TIMEOUT_TERM
572 a4ccecf6 Michael Hanselmann
              utils_wrapper.IgnoreProcessNotFound(os.kill, child.pid,
573 a4ccecf6 Michael Hanselmann
                                                  signal.SIGTERM)
574 a4ccecf6 Michael Hanselmann
            linger_timeout = \
575 a4ccecf6 Michael Hanselmann
              utils_algo.RunningTimeout(_linger_timeout, True).Remaining
576 a4ccecf6 Michael Hanselmann
          pt = linger_timeout() * 1000
577 a4ccecf6 Michael Hanselmann
          if pt < 0:
578 a4ccecf6 Michael Hanselmann
            break
579 a4ccecf6 Michael Hanselmann
      else:
580 a4ccecf6 Michael Hanselmann
        pt = None
581 a4ccecf6 Michael Hanselmann
582 a4ccecf6 Michael Hanselmann
      pollresult = utils_wrapper.RetryOnSignal(poller.poll, pt)
583 a4ccecf6 Michael Hanselmann
584 a4ccecf6 Michael Hanselmann
      for fd, event in pollresult:
585 a4ccecf6 Michael Hanselmann
        if event & select.POLLIN or event & select.POLLPRI:
586 a4ccecf6 Michael Hanselmann
          data = fdmap[fd][1].read()
587 a4ccecf6 Michael Hanselmann
          # no data from read signifies EOF (the same as POLLHUP)
588 a4ccecf6 Michael Hanselmann
          if not data:
589 a4ccecf6 Michael Hanselmann
            poller.unregister(fd)
590 a4ccecf6 Michael Hanselmann
            del fdmap[fd]
591 a4ccecf6 Michael Hanselmann
            continue
592 a4ccecf6 Michael Hanselmann
          fdmap[fd][0].write(data)
593 a4ccecf6 Michael Hanselmann
        if (event & select.POLLNVAL or event & select.POLLHUP or
594 a4ccecf6 Michael Hanselmann
            event & select.POLLERR):
595 a4ccecf6 Michael Hanselmann
          poller.unregister(fd)
596 a4ccecf6 Michael Hanselmann
          del fdmap[fd]
597 a4ccecf6 Michael Hanselmann
598 a4ccecf6 Michael Hanselmann
  if timeout is not None:
599 a4ccecf6 Michael Hanselmann
    assert callable(poll_timeout)
600 a4ccecf6 Michael Hanselmann
601 a4ccecf6 Michael Hanselmann
    # We have no I/O left but it might still run
602 a4ccecf6 Michael Hanselmann
    if child.poll() is None:
603 a4ccecf6 Michael Hanselmann
      _WaitForProcess(child, poll_timeout())
604 a4ccecf6 Michael Hanselmann
605 a4ccecf6 Michael Hanselmann
    # Terminate if still alive after timeout
606 a4ccecf6 Michael Hanselmann
    if child.poll() is None:
607 a4ccecf6 Michael Hanselmann
      if linger_timeout is None:
608 a4ccecf6 Michael Hanselmann
        logging.warning(msg_timeout)
609 a4ccecf6 Michael Hanselmann
        timeout_action = _TIMEOUT_TERM
610 a4ccecf6 Michael Hanselmann
        utils_wrapper.IgnoreProcessNotFound(os.kill, child.pid, signal.SIGTERM)
611 a4ccecf6 Michael Hanselmann
        lt = _linger_timeout
612 a4ccecf6 Michael Hanselmann
      else:
613 a4ccecf6 Michael Hanselmann
        lt = linger_timeout()
614 a4ccecf6 Michael Hanselmann
      _WaitForProcess(child, lt)
615 a4ccecf6 Michael Hanselmann
616 a4ccecf6 Michael Hanselmann
    # Okay, still alive after timeout and linger timeout? Kill it!
617 a4ccecf6 Michael Hanselmann
    if child.poll() is None:
618 a4ccecf6 Michael Hanselmann
      timeout_action = _TIMEOUT_KILL
619 a4ccecf6 Michael Hanselmann
      logging.warning(msg_linger)
620 a4ccecf6 Michael Hanselmann
      utils_wrapper.IgnoreProcessNotFound(os.kill, child.pid, signal.SIGKILL)
621 a4ccecf6 Michael Hanselmann
622 a4ccecf6 Michael Hanselmann
  out = out.getvalue()
623 a4ccecf6 Michael Hanselmann
  err = err.getvalue()
624 a4ccecf6 Michael Hanselmann
625 a4ccecf6 Michael Hanselmann
  status = child.wait()
626 a4ccecf6 Michael Hanselmann
  return out, err, status, timeout_action
627 a4ccecf6 Michael Hanselmann
628 a4ccecf6 Michael Hanselmann
629 7b0bf9cd Apollon Oikonomopoulos
def _RunCmdFile(cmd, env, via_shell, output, cwd, noclose_fds):
630 a4ccecf6 Michael Hanselmann
  """Run a command and save its output to a file.
631 a4ccecf6 Michael Hanselmann

632 a4ccecf6 Michael Hanselmann
  @type  cmd: string or list
633 a4ccecf6 Michael Hanselmann
  @param cmd: Command to run
634 a4ccecf6 Michael Hanselmann
  @type env: dict
635 a4ccecf6 Michael Hanselmann
  @param env: The environment to use
636 a4ccecf6 Michael Hanselmann
  @type via_shell: bool
637 a4ccecf6 Michael Hanselmann
  @param via_shell: if we should run via the shell
638 a4ccecf6 Michael Hanselmann
  @type output: str
639 a4ccecf6 Michael Hanselmann
  @param output: the filename in which to save the output
640 a4ccecf6 Michael Hanselmann
  @type cwd: string
641 a4ccecf6 Michael Hanselmann
  @param cwd: the working directory for the program
642 7b0bf9cd Apollon Oikonomopoulos
  @type noclose_fds: list
643 7b0bf9cd Apollon Oikonomopoulos
  @param noclose_fds: list of additional (fd >=3) file descriptors to leave
644 7b0bf9cd Apollon Oikonomopoulos
                      open for the child process
645 a4ccecf6 Michael Hanselmann
  @rtype: int
646 a4ccecf6 Michael Hanselmann
  @return: the exit status
647 a4ccecf6 Michael Hanselmann

648 a4ccecf6 Michael Hanselmann
  """
649 a4ccecf6 Michael Hanselmann
  fh = open(output, "a")
650 7b0bf9cd Apollon Oikonomopoulos
651 7b0bf9cd Apollon Oikonomopoulos
  if noclose_fds:
652 7b0bf9cd Apollon Oikonomopoulos
    preexec_fn = lambda: CloseFDs(noclose_fds + [fh.fileno()])
653 7b0bf9cd Apollon Oikonomopoulos
    close_fds = False
654 7b0bf9cd Apollon Oikonomopoulos
  else:
655 7b0bf9cd Apollon Oikonomopoulos
    preexec_fn = None
656 7b0bf9cd Apollon Oikonomopoulos
    close_fds = True
657 7b0bf9cd Apollon Oikonomopoulos
658 a4ccecf6 Michael Hanselmann
  try:
659 a4ccecf6 Michael Hanselmann
    child = subprocess.Popen(cmd, shell=via_shell,
660 a4ccecf6 Michael Hanselmann
                             stderr=subprocess.STDOUT,
661 a4ccecf6 Michael Hanselmann
                             stdout=fh,
662 a4ccecf6 Michael Hanselmann
                             stdin=subprocess.PIPE,
663 7b0bf9cd Apollon Oikonomopoulos
                             close_fds=close_fds, env=env,
664 7b0bf9cd Apollon Oikonomopoulos
                             cwd=cwd,
665 7b0bf9cd Apollon Oikonomopoulos
                             preexec_fn=preexec_fn)
666 a4ccecf6 Michael Hanselmann
667 a4ccecf6 Michael Hanselmann
    child.stdin.close()
668 a4ccecf6 Michael Hanselmann
    status = child.wait()
669 a4ccecf6 Michael Hanselmann
  finally:
670 a4ccecf6 Michael Hanselmann
    fh.close()
671 a4ccecf6 Michael Hanselmann
  return status
672 a4ccecf6 Michael Hanselmann
673 a4ccecf6 Michael Hanselmann
674 a4ccecf6 Michael Hanselmann
def RunParts(dir_name, env=None, reset_env=False):
675 a4ccecf6 Michael Hanselmann
  """Run Scripts or programs in a directory
676 a4ccecf6 Michael Hanselmann

677 a4ccecf6 Michael Hanselmann
  @type dir_name: string
678 a4ccecf6 Michael Hanselmann
  @param dir_name: absolute path to a directory
679 a4ccecf6 Michael Hanselmann
  @type env: dict
680 a4ccecf6 Michael Hanselmann
  @param env: The environment to use
681 a4ccecf6 Michael Hanselmann
  @type reset_env: boolean
682 a4ccecf6 Michael Hanselmann
  @param reset_env: whether to reset or keep the default os environment
683 a4ccecf6 Michael Hanselmann
  @rtype: list of tuples
684 a4ccecf6 Michael Hanselmann
  @return: list of (name, (one of RUNDIR_STATUS), RunResult)
685 a4ccecf6 Michael Hanselmann

686 a4ccecf6 Michael Hanselmann
  """
687 a4ccecf6 Michael Hanselmann
  rr = []
688 a4ccecf6 Michael Hanselmann
689 a4ccecf6 Michael Hanselmann
  try:
690 a4ccecf6 Michael Hanselmann
    dir_contents = utils_io.ListVisibleFiles(dir_name)
691 a4ccecf6 Michael Hanselmann
  except OSError, err:
692 a4ccecf6 Michael Hanselmann
    logging.warning("RunParts: skipping %s (cannot list: %s)", dir_name, err)
693 a4ccecf6 Michael Hanselmann
    return rr
694 a4ccecf6 Michael Hanselmann
695 a4ccecf6 Michael Hanselmann
  for relname in sorted(dir_contents):
696 a4ccecf6 Michael Hanselmann
    fname = utils_io.PathJoin(dir_name, relname)
697 a4ccecf6 Michael Hanselmann
    if not (os.path.isfile(fname) and os.access(fname, os.X_OK) and
698 a4ccecf6 Michael Hanselmann
            constants.EXT_PLUGIN_MASK.match(relname) is not None):
699 a4ccecf6 Michael Hanselmann
      rr.append((relname, constants.RUNPARTS_SKIP, None))
700 a4ccecf6 Michael Hanselmann
    else:
701 a4ccecf6 Michael Hanselmann
      try:
702 a4ccecf6 Michael Hanselmann
        result = RunCmd([fname], env=env, reset_env=reset_env)
703 a4ccecf6 Michael Hanselmann
      except Exception, err: # pylint: disable-msg=W0703
704 a4ccecf6 Michael Hanselmann
        rr.append((relname, constants.RUNPARTS_ERR, str(err)))
705 a4ccecf6 Michael Hanselmann
      else:
706 a4ccecf6 Michael Hanselmann
        rr.append((relname, constants.RUNPARTS_RUN, result))
707 a4ccecf6 Michael Hanselmann
708 a4ccecf6 Michael Hanselmann
  return rr
709 a4ccecf6 Michael Hanselmann
710 a4ccecf6 Michael Hanselmann
711 a4ccecf6 Michael Hanselmann
def _GetProcStatusPath(pid):
712 a4ccecf6 Michael Hanselmann
  """Returns the path for a PID's proc status file.
713 a4ccecf6 Michael Hanselmann

714 a4ccecf6 Michael Hanselmann
  @type pid: int
715 a4ccecf6 Michael Hanselmann
  @param pid: Process ID
716 a4ccecf6 Michael Hanselmann
  @rtype: string
717 a4ccecf6 Michael Hanselmann

718 a4ccecf6 Michael Hanselmann
  """
719 a4ccecf6 Michael Hanselmann
  return "/proc/%d/status" % pid
720 a4ccecf6 Michael Hanselmann
721 a4ccecf6 Michael Hanselmann
722 a4ccecf6 Michael Hanselmann
def IsProcessAlive(pid):
723 a4ccecf6 Michael Hanselmann
  """Check if a given pid exists on the system.
724 a4ccecf6 Michael Hanselmann

725 a4ccecf6 Michael Hanselmann
  @note: zombie status is not handled, so zombie processes
726 a4ccecf6 Michael Hanselmann
      will be returned as alive
727 a4ccecf6 Michael Hanselmann
  @type pid: int
728 a4ccecf6 Michael Hanselmann
  @param pid: the process ID to check
729 a4ccecf6 Michael Hanselmann
  @rtype: boolean
730 a4ccecf6 Michael Hanselmann
  @return: True if the process exists
731 a4ccecf6 Michael Hanselmann

732 a4ccecf6 Michael Hanselmann
  """
733 a4ccecf6 Michael Hanselmann
  def _TryStat(name):
734 a4ccecf6 Michael Hanselmann
    try:
735 a4ccecf6 Michael Hanselmann
      os.stat(name)
736 a4ccecf6 Michael Hanselmann
      return True
737 a4ccecf6 Michael Hanselmann
    except EnvironmentError, err:
738 a4ccecf6 Michael Hanselmann
      if err.errno in (errno.ENOENT, errno.ENOTDIR):
739 a4ccecf6 Michael Hanselmann
        return False
740 a4ccecf6 Michael Hanselmann
      elif err.errno == errno.EINVAL:
741 a4ccecf6 Michael Hanselmann
        raise utils_retry.RetryAgain(err)
742 a4ccecf6 Michael Hanselmann
      raise
743 a4ccecf6 Michael Hanselmann
744 a4ccecf6 Michael Hanselmann
  assert isinstance(pid, int), "pid must be an integer"
745 a4ccecf6 Michael Hanselmann
  if pid <= 0:
746 a4ccecf6 Michael Hanselmann
    return False
747 a4ccecf6 Michael Hanselmann
748 a4ccecf6 Michael Hanselmann
  # /proc in a multiprocessor environment can have strange behaviors.
749 a4ccecf6 Michael Hanselmann
  # Retry the os.stat a few times until we get a good result.
750 a4ccecf6 Michael Hanselmann
  try:
751 a4ccecf6 Michael Hanselmann
    return utils_retry.Retry(_TryStat, (0.01, 1.5, 0.1), 0.5,
752 a4ccecf6 Michael Hanselmann
                             args=[_GetProcStatusPath(pid)])
753 a4ccecf6 Michael Hanselmann
  except utils_retry.RetryTimeout, err:
754 a4ccecf6 Michael Hanselmann
    err.RaiseInner()
755 a4ccecf6 Michael Hanselmann
756 a4ccecf6 Michael Hanselmann
757 a4ccecf6 Michael Hanselmann
def _ParseSigsetT(sigset):
758 a4ccecf6 Michael Hanselmann
  """Parse a rendered sigset_t value.
759 a4ccecf6 Michael Hanselmann

760 a4ccecf6 Michael Hanselmann
  This is the opposite of the Linux kernel's fs/proc/array.c:render_sigset_t
761 a4ccecf6 Michael Hanselmann
  function.
762 a4ccecf6 Michael Hanselmann

763 a4ccecf6 Michael Hanselmann
  @type sigset: string
764 a4ccecf6 Michael Hanselmann
  @param sigset: Rendered signal set from /proc/$pid/status
765 a4ccecf6 Michael Hanselmann
  @rtype: set
766 a4ccecf6 Michael Hanselmann
  @return: Set of all enabled signal numbers
767 a4ccecf6 Michael Hanselmann

768 a4ccecf6 Michael Hanselmann
  """
769 a4ccecf6 Michael Hanselmann
  result = set()
770 a4ccecf6 Michael Hanselmann
771 a4ccecf6 Michael Hanselmann
  signum = 0
772 a4ccecf6 Michael Hanselmann
  for ch in reversed(sigset):
773 a4ccecf6 Michael Hanselmann
    chv = int(ch, 16)
774 a4ccecf6 Michael Hanselmann
775 a4ccecf6 Michael Hanselmann
    # The following could be done in a loop, but it's easier to read and
776 a4ccecf6 Michael Hanselmann
    # understand in the unrolled form
777 a4ccecf6 Michael Hanselmann
    if chv & 1:
778 a4ccecf6 Michael Hanselmann
      result.add(signum + 1)
779 a4ccecf6 Michael Hanselmann
    if chv & 2:
780 a4ccecf6 Michael Hanselmann
      result.add(signum + 2)
781 a4ccecf6 Michael Hanselmann
    if chv & 4:
782 a4ccecf6 Michael Hanselmann
      result.add(signum + 3)
783 a4ccecf6 Michael Hanselmann
    if chv & 8:
784 a4ccecf6 Michael Hanselmann
      result.add(signum + 4)
785 a4ccecf6 Michael Hanselmann
786 a4ccecf6 Michael Hanselmann
    signum += 4
787 a4ccecf6 Michael Hanselmann
788 a4ccecf6 Michael Hanselmann
  return result
789 a4ccecf6 Michael Hanselmann
790 a4ccecf6 Michael Hanselmann
791 a4ccecf6 Michael Hanselmann
def _GetProcStatusField(pstatus, field):
792 a4ccecf6 Michael Hanselmann
  """Retrieves a field from the contents of a proc status file.
793 a4ccecf6 Michael Hanselmann

794 a4ccecf6 Michael Hanselmann
  @type pstatus: string
795 a4ccecf6 Michael Hanselmann
  @param pstatus: Contents of /proc/$pid/status
796 a4ccecf6 Michael Hanselmann
  @type field: string
797 a4ccecf6 Michael Hanselmann
  @param field: Name of field whose value should be returned
798 a4ccecf6 Michael Hanselmann
  @rtype: string
799 a4ccecf6 Michael Hanselmann

800 a4ccecf6 Michael Hanselmann
  """
801 a4ccecf6 Michael Hanselmann
  for line in pstatus.splitlines():
802 a4ccecf6 Michael Hanselmann
    parts = line.split(":", 1)
803 a4ccecf6 Michael Hanselmann
804 a4ccecf6 Michael Hanselmann
    if len(parts) < 2 or parts[0] != field:
805 a4ccecf6 Michael Hanselmann
      continue
806 a4ccecf6 Michael Hanselmann
807 a4ccecf6 Michael Hanselmann
    return parts[1].strip()
808 a4ccecf6 Michael Hanselmann
809 a4ccecf6 Michael Hanselmann
  return None
810 a4ccecf6 Michael Hanselmann
811 a4ccecf6 Michael Hanselmann
812 a4ccecf6 Michael Hanselmann
def IsProcessHandlingSignal(pid, signum, status_path=None):
813 a4ccecf6 Michael Hanselmann
  """Checks whether a process is handling a signal.
814 a4ccecf6 Michael Hanselmann

815 a4ccecf6 Michael Hanselmann
  @type pid: int
816 a4ccecf6 Michael Hanselmann
  @param pid: Process ID
817 a4ccecf6 Michael Hanselmann
  @type signum: int
818 a4ccecf6 Michael Hanselmann
  @param signum: Signal number
819 a4ccecf6 Michael Hanselmann
  @rtype: bool
820 a4ccecf6 Michael Hanselmann

821 a4ccecf6 Michael Hanselmann
  """
822 a4ccecf6 Michael Hanselmann
  if status_path is None:
823 a4ccecf6 Michael Hanselmann
    status_path = _GetProcStatusPath(pid)
824 a4ccecf6 Michael Hanselmann
825 a4ccecf6 Michael Hanselmann
  try:
826 a4ccecf6 Michael Hanselmann
    proc_status = utils_io.ReadFile(status_path)
827 a4ccecf6 Michael Hanselmann
  except EnvironmentError, err:
828 a4ccecf6 Michael Hanselmann
    # In at least one case, reading /proc/$pid/status failed with ESRCH.
829 a4ccecf6 Michael Hanselmann
    if err.errno in (errno.ENOENT, errno.ENOTDIR, errno.EINVAL, errno.ESRCH):
830 a4ccecf6 Michael Hanselmann
      return False
831 a4ccecf6 Michael Hanselmann
    raise
832 a4ccecf6 Michael Hanselmann
833 a4ccecf6 Michael Hanselmann
  sigcgt = _GetProcStatusField(proc_status, "SigCgt")
834 a4ccecf6 Michael Hanselmann
  if sigcgt is None:
835 a4ccecf6 Michael Hanselmann
    raise RuntimeError("%s is missing 'SigCgt' field" % status_path)
836 a4ccecf6 Michael Hanselmann
837 a4ccecf6 Michael Hanselmann
  # Now check whether signal is handled
838 a4ccecf6 Michael Hanselmann
  return signum in _ParseSigsetT(sigcgt)
839 a4ccecf6 Michael Hanselmann
840 a4ccecf6 Michael Hanselmann
841 a4ccecf6 Michael Hanselmann
def Daemonize(logfile):
842 a4ccecf6 Michael Hanselmann
  """Daemonize the current process.
843 a4ccecf6 Michael Hanselmann

844 a4ccecf6 Michael Hanselmann
  This detaches the current process from the controlling terminal and
845 a4ccecf6 Michael Hanselmann
  runs it in the background as a daemon.
846 a4ccecf6 Michael Hanselmann

847 a4ccecf6 Michael Hanselmann
  @type logfile: str
848 a4ccecf6 Michael Hanselmann
  @param logfile: the logfile to which we should redirect stdout/stderr
849 110f49ef Michael Hanselmann
  @rtype: tuple; (int, callable)
850 110f49ef Michael Hanselmann
  @return: File descriptor of pipe(2) which must be closed to notify parent
851 110f49ef Michael Hanselmann
    process and a callable to reopen log files
852 a4ccecf6 Michael Hanselmann

853 a4ccecf6 Michael Hanselmann
  """
854 a4ccecf6 Michael Hanselmann
  # pylint: disable-msg=W0212
855 a4ccecf6 Michael Hanselmann
  # yes, we really want os._exit
856 a4ccecf6 Michael Hanselmann
857 a4ccecf6 Michael Hanselmann
  # TODO: do another attempt to merge Daemonize and StartDaemon, or at
858 a4ccecf6 Michael Hanselmann
  # least abstract the pipe functionality between them
859 a4ccecf6 Michael Hanselmann
860 a4ccecf6 Michael Hanselmann
  # Create pipe for sending error messages
861 a4ccecf6 Michael Hanselmann
  (rpipe, wpipe) = os.pipe()
862 a4ccecf6 Michael Hanselmann
863 a4ccecf6 Michael Hanselmann
  # this might fail
864 a4ccecf6 Michael Hanselmann
  pid = os.fork()
865 a4ccecf6 Michael Hanselmann
  if (pid == 0):  # The first child.
866 a4ccecf6 Michael Hanselmann
    SetupDaemonEnv()
867 a4ccecf6 Michael Hanselmann
868 a4ccecf6 Michael Hanselmann
    # this might fail
869 a4ccecf6 Michael Hanselmann
    pid = os.fork() # Fork a second child.
870 a4ccecf6 Michael Hanselmann
    if (pid == 0):  # The second child.
871 a4ccecf6 Michael Hanselmann
      utils_wrapper.CloseFdNoError(rpipe)
872 a4ccecf6 Michael Hanselmann
    else:
873 a4ccecf6 Michael Hanselmann
      # exit() or _exit()?  See below.
874 a4ccecf6 Michael Hanselmann
      os._exit(0) # Exit parent (the first child) of the second child.
875 a4ccecf6 Michael Hanselmann
  else:
876 a4ccecf6 Michael Hanselmann
    utils_wrapper.CloseFdNoError(wpipe)
877 a4ccecf6 Michael Hanselmann
    # Wait for daemon to be started (or an error message to
878 a4ccecf6 Michael Hanselmann
    # arrive) and read up to 100 KB as an error message
879 a4ccecf6 Michael Hanselmann
    errormsg = utils_wrapper.RetryOnSignal(os.read, rpipe, 100 * 1024)
880 a4ccecf6 Michael Hanselmann
    if errormsg:
881 a4ccecf6 Michael Hanselmann
      sys.stderr.write("Error when starting daemon process: %r\n" % errormsg)
882 a4ccecf6 Michael Hanselmann
      rcode = 1
883 a4ccecf6 Michael Hanselmann
    else:
884 a4ccecf6 Michael Hanselmann
      rcode = 0
885 a4ccecf6 Michael Hanselmann
    os._exit(rcode) # Exit parent of the first child.
886 a4ccecf6 Michael Hanselmann
887 110f49ef Michael Hanselmann
  reopen_fn = compat.partial(SetupDaemonFDs, logfile, None)
888 110f49ef Michael Hanselmann
889 110f49ef Michael Hanselmann
  # Open logs for the first time
890 110f49ef Michael Hanselmann
  reopen_fn()
891 110f49ef Michael Hanselmann
892 110f49ef Michael Hanselmann
  return (wpipe, reopen_fn)
893 a4ccecf6 Michael Hanselmann
894 a4ccecf6 Michael Hanselmann
895 a4ccecf6 Michael Hanselmann
def KillProcess(pid, signal_=signal.SIGTERM, timeout=30,
896 a4ccecf6 Michael Hanselmann
                waitpid=False):
897 a4ccecf6 Michael Hanselmann
  """Kill a process given by its pid.
898 a4ccecf6 Michael Hanselmann

899 a4ccecf6 Michael Hanselmann
  @type pid: int
900 a4ccecf6 Michael Hanselmann
  @param pid: The PID to terminate.
901 a4ccecf6 Michael Hanselmann
  @type signal_: int
902 a4ccecf6 Michael Hanselmann
  @param signal_: The signal to send, by default SIGTERM
903 a4ccecf6 Michael Hanselmann
  @type timeout: int
904 a4ccecf6 Michael Hanselmann
  @param timeout: The timeout after which, if the process is still alive,
905 a4ccecf6 Michael Hanselmann
                  a SIGKILL will be sent. If not positive, no such checking
906 a4ccecf6 Michael Hanselmann
                  will be done
907 a4ccecf6 Michael Hanselmann
  @type waitpid: boolean
908 a4ccecf6 Michael Hanselmann
  @param waitpid: If true, we should waitpid on this process after
909 a4ccecf6 Michael Hanselmann
      sending signals, since it's our own child and otherwise it
910 a4ccecf6 Michael Hanselmann
      would remain as zombie
911 a4ccecf6 Michael Hanselmann

912 a4ccecf6 Michael Hanselmann
  """
913 a4ccecf6 Michael Hanselmann
  def _helper(pid, signal_, wait):
914 a4ccecf6 Michael Hanselmann
    """Simple helper to encapsulate the kill/waitpid sequence"""
915 a4ccecf6 Michael Hanselmann
    if utils_wrapper.IgnoreProcessNotFound(os.kill, pid, signal_) and wait:
916 a4ccecf6 Michael Hanselmann
      try:
917 a4ccecf6 Michael Hanselmann
        os.waitpid(pid, os.WNOHANG)
918 a4ccecf6 Michael Hanselmann
      except OSError:
919 a4ccecf6 Michael Hanselmann
        pass
920 a4ccecf6 Michael Hanselmann
921 a4ccecf6 Michael Hanselmann
  if pid <= 0:
922 a4ccecf6 Michael Hanselmann
    # kill with pid=0 == suicide
923 a4ccecf6 Michael Hanselmann
    raise errors.ProgrammerError("Invalid pid given '%s'" % pid)
924 a4ccecf6 Michael Hanselmann
925 a4ccecf6 Michael Hanselmann
  if not IsProcessAlive(pid):
926 a4ccecf6 Michael Hanselmann
    return
927 a4ccecf6 Michael Hanselmann
928 a4ccecf6 Michael Hanselmann
  _helper(pid, signal_, waitpid)
929 a4ccecf6 Michael Hanselmann
930 a4ccecf6 Michael Hanselmann
  if timeout <= 0:
931 a4ccecf6 Michael Hanselmann
    return
932 a4ccecf6 Michael Hanselmann
933 a4ccecf6 Michael Hanselmann
  def _CheckProcess():
934 a4ccecf6 Michael Hanselmann
    if not IsProcessAlive(pid):
935 a4ccecf6 Michael Hanselmann
      return
936 a4ccecf6 Michael Hanselmann
937 a4ccecf6 Michael Hanselmann
    try:
938 a4ccecf6 Michael Hanselmann
      (result_pid, _) = os.waitpid(pid, os.WNOHANG)
939 a4ccecf6 Michael Hanselmann
    except OSError:
940 a4ccecf6 Michael Hanselmann
      raise utils_retry.RetryAgain()
941 a4ccecf6 Michael Hanselmann
942 a4ccecf6 Michael Hanselmann
    if result_pid > 0:
943 a4ccecf6 Michael Hanselmann
      return
944 a4ccecf6 Michael Hanselmann
945 a4ccecf6 Michael Hanselmann
    raise utils_retry.RetryAgain()
946 a4ccecf6 Michael Hanselmann
947 a4ccecf6 Michael Hanselmann
  try:
948 a4ccecf6 Michael Hanselmann
    # Wait up to $timeout seconds
949 a4ccecf6 Michael Hanselmann
    utils_retry.Retry(_CheckProcess, (0.01, 1.5, 0.1), timeout)
950 a4ccecf6 Michael Hanselmann
  except utils_retry.RetryTimeout:
951 a4ccecf6 Michael Hanselmann
    pass
952 a4ccecf6 Michael Hanselmann
953 a4ccecf6 Michael Hanselmann
  if IsProcessAlive(pid):
954 a4ccecf6 Michael Hanselmann
    # Kill process if it's still alive
955 a4ccecf6 Michael Hanselmann
    _helper(pid, signal.SIGKILL, waitpid)
956 a4ccecf6 Michael Hanselmann
957 a4ccecf6 Michael Hanselmann
958 a4ccecf6 Michael Hanselmann
def RunInSeparateProcess(fn, *args):
959 a4ccecf6 Michael Hanselmann
  """Runs a function in a separate process.
960 a4ccecf6 Michael Hanselmann

961 a4ccecf6 Michael Hanselmann
  Note: Only boolean return values are supported.
962 a4ccecf6 Michael Hanselmann

963 a4ccecf6 Michael Hanselmann
  @type fn: callable
964 a4ccecf6 Michael Hanselmann
  @param fn: Function to be called
965 a4ccecf6 Michael Hanselmann
  @rtype: bool
966 a4ccecf6 Michael Hanselmann
  @return: Function's result
967 a4ccecf6 Michael Hanselmann

968 a4ccecf6 Michael Hanselmann
  """
969 a4ccecf6 Michael Hanselmann
  pid = os.fork()
970 a4ccecf6 Michael Hanselmann
  if pid == 0:
971 a4ccecf6 Michael Hanselmann
    # Child process
972 a4ccecf6 Michael Hanselmann
    try:
973 a4ccecf6 Michael Hanselmann
      # In case the function uses temporary files
974 a4ccecf6 Michael Hanselmann
      utils_wrapper.ResetTempfileModule()
975 a4ccecf6 Michael Hanselmann
976 a4ccecf6 Michael Hanselmann
      # Call function
977 a4ccecf6 Michael Hanselmann
      result = int(bool(fn(*args)))
978 a4ccecf6 Michael Hanselmann
      assert result in (0, 1)
979 a4ccecf6 Michael Hanselmann
    except: # pylint: disable-msg=W0702
980 a4ccecf6 Michael Hanselmann
      logging.exception("Error while calling function in separate process")
981 a4ccecf6 Michael Hanselmann
      # 0 and 1 are reserved for the return value
982 a4ccecf6 Michael Hanselmann
      result = 33
983 a4ccecf6 Michael Hanselmann
984 a4ccecf6 Michael Hanselmann
    os._exit(result) # pylint: disable-msg=W0212
985 a4ccecf6 Michael Hanselmann
986 a4ccecf6 Michael Hanselmann
  # Parent process
987 a4ccecf6 Michael Hanselmann
988 a4ccecf6 Michael Hanselmann
  # Avoid zombies and check exit code
989 a4ccecf6 Michael Hanselmann
  (_, status) = os.waitpid(pid, 0)
990 a4ccecf6 Michael Hanselmann
991 a4ccecf6 Michael Hanselmann
  if os.WIFSIGNALED(status):
992 a4ccecf6 Michael Hanselmann
    exitcode = None
993 a4ccecf6 Michael Hanselmann
    signum = os.WTERMSIG(status)
994 a4ccecf6 Michael Hanselmann
  else:
995 a4ccecf6 Michael Hanselmann
    exitcode = os.WEXITSTATUS(status)
996 a4ccecf6 Michael Hanselmann
    signum = None
997 a4ccecf6 Michael Hanselmann
998 a4ccecf6 Michael Hanselmann
  if not (exitcode in (0, 1) and signum is None):
999 a4ccecf6 Michael Hanselmann
    raise errors.GenericError("Child program failed (code=%s, signal=%s)" %
1000 a4ccecf6 Michael Hanselmann
                              (exitcode, signum))
1001 a4ccecf6 Michael Hanselmann
1002 a4ccecf6 Michael Hanselmann
  return bool(exitcode)
1003 a4ccecf6 Michael Hanselmann
1004 a4ccecf6 Michael Hanselmann
1005 a4ccecf6 Michael Hanselmann
def CloseFDs(noclose_fds=None):
1006 a4ccecf6 Michael Hanselmann
  """Close file descriptors.
1007 a4ccecf6 Michael Hanselmann

1008 a4ccecf6 Michael Hanselmann
  This closes all file descriptors above 2 (i.e. except
1009 a4ccecf6 Michael Hanselmann
  stdin/out/err).
1010 a4ccecf6 Michael Hanselmann

1011 a4ccecf6 Michael Hanselmann
  @type noclose_fds: list or None
1012 a4ccecf6 Michael Hanselmann
  @param noclose_fds: if given, it denotes a list of file descriptor
1013 a4ccecf6 Michael Hanselmann
      that should not be closed
1014 a4ccecf6 Michael Hanselmann

1015 a4ccecf6 Michael Hanselmann
  """
1016 a4ccecf6 Michael Hanselmann
  # Default maximum for the number of available file descriptors.
1017 a4ccecf6 Michael Hanselmann
  if 'SC_OPEN_MAX' in os.sysconf_names:
1018 a4ccecf6 Michael Hanselmann
    try:
1019 a4ccecf6 Michael Hanselmann
      MAXFD = os.sysconf('SC_OPEN_MAX')
1020 a4ccecf6 Michael Hanselmann
      if MAXFD < 0:
1021 a4ccecf6 Michael Hanselmann
        MAXFD = 1024
1022 a4ccecf6 Michael Hanselmann
    except OSError:
1023 a4ccecf6 Michael Hanselmann
      MAXFD = 1024
1024 a4ccecf6 Michael Hanselmann
  else:
1025 a4ccecf6 Michael Hanselmann
    MAXFD = 1024
1026 a4ccecf6 Michael Hanselmann
1027 a4ccecf6 Michael Hanselmann
  maxfd = resource.getrlimit(resource.RLIMIT_NOFILE)[1]
1028 a4ccecf6 Michael Hanselmann
  if (maxfd == resource.RLIM_INFINITY):
1029 a4ccecf6 Michael Hanselmann
    maxfd = MAXFD
1030 a4ccecf6 Michael Hanselmann
1031 a4ccecf6 Michael Hanselmann
  # Iterate through and close all file descriptors (except the standard ones)
1032 a4ccecf6 Michael Hanselmann
  for fd in range(3, maxfd):
1033 a4ccecf6 Michael Hanselmann
    if noclose_fds and fd in noclose_fds:
1034 a4ccecf6 Michael Hanselmann
      continue
1035 a4ccecf6 Michael Hanselmann
    utils_wrapper.CloseFdNoError(fd)