Statistics
| Branch: | Tag: | Revision:

root / lib / utils / process.py @ 5f2725c1

History | View | Annotate | Download (27.8 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 a4ccecf6 Michael Hanselmann
40 a4ccecf6 Michael Hanselmann
from ganeti.utils import retry as utils_retry
41 a4ccecf6 Michael Hanselmann
from ganeti.utils import wrapper as utils_wrapper
42 a4ccecf6 Michael Hanselmann
from ganeti.utils import text as utils_text
43 a4ccecf6 Michael Hanselmann
from ganeti.utils import io as utils_io
44 a4ccecf6 Michael Hanselmann
from ganeti.utils import algo as utils_algo
45 a4ccecf6 Michael Hanselmann
46 a4ccecf6 Michael Hanselmann
47 a4ccecf6 Michael Hanselmann
#: when set to True, L{RunCmd} is disabled
48 a4ccecf6 Michael Hanselmann
_no_fork = False
49 a4ccecf6 Michael Hanselmann
50 a4ccecf6 Michael Hanselmann
(_TIMEOUT_NONE,
51 a4ccecf6 Michael Hanselmann
 _TIMEOUT_TERM,
52 a4ccecf6 Michael Hanselmann
 _TIMEOUT_KILL) = range(3)
53 a4ccecf6 Michael Hanselmann
54 a4ccecf6 Michael Hanselmann
55 a4ccecf6 Michael Hanselmann
def DisableFork():
56 a4ccecf6 Michael Hanselmann
  """Disables the use of fork(2).
57 a4ccecf6 Michael Hanselmann

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

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

82 a4ccecf6 Michael Hanselmann
  """
83 a4ccecf6 Michael Hanselmann
  __slots__ = ["exit_code", "signal", "stdout", "stderr",
84 a4ccecf6 Michael Hanselmann
               "failed", "fail_reason", "cmd"]
85 a4ccecf6 Michael Hanselmann
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 7b0bf9cd Apollon Oikonomopoulos
           interactive=False, timeout=None, noclose_fds=None):
145 a4ccecf6 Michael Hanselmann
  """Execute a (shell) command.
146 a4ccecf6 Michael Hanselmann

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

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

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

228 a4ccecf6 Michael Hanselmann
  This should be called between the first and second fork, due to
229 a4ccecf6 Michael Hanselmann
  setsid usage.
230 a4ccecf6 Michael Hanselmann

231 a4ccecf6 Michael Hanselmann
  @param cwd: the directory to which to chdir
232 a4ccecf6 Michael Hanselmann
  @param umask: the umask to setup
233 a4ccecf6 Michael Hanselmann

234 a4ccecf6 Michael Hanselmann
  """
235 a4ccecf6 Michael Hanselmann
  os.chdir(cwd)
236 a4ccecf6 Michael Hanselmann
  os.umask(umask)
237 a4ccecf6 Michael Hanselmann
  os.setsid()
238 a4ccecf6 Michael Hanselmann
239 a4ccecf6 Michael Hanselmann
240 a4ccecf6 Michael Hanselmann
def SetupDaemonFDs(output_file, output_fd):
241 a4ccecf6 Michael Hanselmann
  """Setups up a daemon's file descriptors.
242 a4ccecf6 Michael Hanselmann

243 a4ccecf6 Michael Hanselmann
  @param output_file: if not None, the file to which to redirect
244 a4ccecf6 Michael Hanselmann
      stdout/stderr
245 a4ccecf6 Michael Hanselmann
  @param output_fd: if not None, the file descriptor for stdout/stderr
246 a4ccecf6 Michael Hanselmann

247 a4ccecf6 Michael Hanselmann
  """
248 a4ccecf6 Michael Hanselmann
  # check that at most one is defined
249 a4ccecf6 Michael Hanselmann
  assert [output_file, output_fd].count(None) >= 1
250 a4ccecf6 Michael Hanselmann
251 a4ccecf6 Michael Hanselmann
  # Open /dev/null (read-only, only for stdin)
252 a4ccecf6 Michael Hanselmann
  devnull_fd = os.open(os.devnull, os.O_RDONLY)
253 a4ccecf6 Michael Hanselmann
254 a4ccecf6 Michael Hanselmann
  if output_fd is not None:
255 a4ccecf6 Michael Hanselmann
    pass
256 a4ccecf6 Michael Hanselmann
  elif output_file is not None:
257 a4ccecf6 Michael Hanselmann
    # Open output file
258 a4ccecf6 Michael Hanselmann
    try:
259 a4ccecf6 Michael Hanselmann
      output_fd = os.open(output_file,
260 a4ccecf6 Michael Hanselmann
                          os.O_WRONLY | os.O_CREAT | os.O_APPEND, 0600)
261 a4ccecf6 Michael Hanselmann
    except EnvironmentError, err:
262 a4ccecf6 Michael Hanselmann
      raise Exception("Opening output file failed: %s" % err)
263 a4ccecf6 Michael Hanselmann
  else:
264 a4ccecf6 Michael Hanselmann
    output_fd = os.open(os.devnull, os.O_WRONLY)
265 a4ccecf6 Michael Hanselmann
266 a4ccecf6 Michael Hanselmann
  # Redirect standard I/O
267 a4ccecf6 Michael Hanselmann
  os.dup2(devnull_fd, 0)
268 a4ccecf6 Michael Hanselmann
  os.dup2(output_fd, 1)
269 a4ccecf6 Michael Hanselmann
  os.dup2(output_fd, 2)
270 a4ccecf6 Michael Hanselmann
271 a4ccecf6 Michael Hanselmann
272 a4ccecf6 Michael Hanselmann
def StartDaemon(cmd, env=None, cwd="/", output=None, output_fd=None,
273 a4ccecf6 Michael Hanselmann
                pidfile=None):
274 a4ccecf6 Michael Hanselmann
  """Start a daemon process after forking twice.
275 a4ccecf6 Michael Hanselmann

276 a4ccecf6 Michael Hanselmann
  @type cmd: string or list
277 a4ccecf6 Michael Hanselmann
  @param cmd: Command to run
278 a4ccecf6 Michael Hanselmann
  @type env: dict
279 a4ccecf6 Michael Hanselmann
  @param env: Additional environment variables
280 a4ccecf6 Michael Hanselmann
  @type cwd: string
281 a4ccecf6 Michael Hanselmann
  @param cwd: Working directory for the program
282 a4ccecf6 Michael Hanselmann
  @type output: string
283 a4ccecf6 Michael Hanselmann
  @param output: Path to file in which to save the output
284 a4ccecf6 Michael Hanselmann
  @type output_fd: int
285 a4ccecf6 Michael Hanselmann
  @param output_fd: File descriptor for output
286 a4ccecf6 Michael Hanselmann
  @type pidfile: string
287 a4ccecf6 Michael Hanselmann
  @param pidfile: Process ID file
288 a4ccecf6 Michael Hanselmann
  @rtype: int
289 a4ccecf6 Michael Hanselmann
  @return: Daemon process ID
290 a4ccecf6 Michael Hanselmann
  @raise errors.ProgrammerError: if we call this when forks are disabled
291 a4ccecf6 Michael Hanselmann

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

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

435 a4ccecf6 Michael Hanselmann
  @type fd: None or int (file descriptor)
436 a4ccecf6 Michael Hanselmann
  @param fd: if not None, the error will be written to this fd
437 a4ccecf6 Michael Hanselmann
  @param err: string, the error message
438 a4ccecf6 Michael Hanselmann

439 a4ccecf6 Michael Hanselmann
  """
440 a4ccecf6 Michael Hanselmann
  if fd is None:
441 a4ccecf6 Michael Hanselmann
    return
442 a4ccecf6 Michael Hanselmann
443 a4ccecf6 Michael Hanselmann
  if not err:
444 a4ccecf6 Michael Hanselmann
    err = "<unknown error>"
445 a4ccecf6 Michael Hanselmann
446 a4ccecf6 Michael Hanselmann
  utils_wrapper.RetryOnSignal(os.write, fd, err)
447 a4ccecf6 Michael Hanselmann
448 a4ccecf6 Michael Hanselmann
449 a4ccecf6 Michael Hanselmann
def _CheckIfAlive(child):
450 a4ccecf6 Michael Hanselmann
  """Raises L{utils_retry.RetryAgain} if child is still alive.
451 a4ccecf6 Michael Hanselmann

452 a4ccecf6 Michael Hanselmann
  @raises utils_retry.RetryAgain: If child is still alive
453 a4ccecf6 Michael Hanselmann

454 a4ccecf6 Michael Hanselmann
  """
455 a4ccecf6 Michael Hanselmann
  if child.poll() is None:
456 a4ccecf6 Michael Hanselmann
    raise utils_retry.RetryAgain()
457 a4ccecf6 Michael Hanselmann
458 a4ccecf6 Michael Hanselmann
459 a4ccecf6 Michael Hanselmann
def _WaitForProcess(child, timeout):
460 a4ccecf6 Michael Hanselmann
  """Waits for the child to terminate or until we reach timeout.
461 a4ccecf6 Michael Hanselmann

462 a4ccecf6 Michael Hanselmann
  """
463 a4ccecf6 Michael Hanselmann
  try:
464 a4ccecf6 Michael Hanselmann
    utils_retry.Retry(_CheckIfAlive, (1.0, 1.2, 5.0), max(0, timeout),
465 a4ccecf6 Michael Hanselmann
                      args=[child])
466 a4ccecf6 Michael Hanselmann
  except utils_retry.RetryTimeout:
467 a4ccecf6 Michael Hanselmann
    pass
468 a4ccecf6 Michael Hanselmann
469 a4ccecf6 Michael Hanselmann
470 7b0bf9cd Apollon Oikonomopoulos
def _RunCmdPipe(cmd, env, via_shell, cwd, interactive, timeout, noclose_fds,
471 a4ccecf6 Michael Hanselmann
                _linger_timeout=constants.CHILD_LINGER_TIMEOUT):
472 a4ccecf6 Michael Hanselmann
  """Run a command and return its output.
473 a4ccecf6 Michael Hanselmann

474 a4ccecf6 Michael Hanselmann
  @type  cmd: string or list
475 a4ccecf6 Michael Hanselmann
  @param cmd: Command to run
476 a4ccecf6 Michael Hanselmann
  @type env: dict
477 a4ccecf6 Michael Hanselmann
  @param env: The environment to use
478 a4ccecf6 Michael Hanselmann
  @type via_shell: bool
479 a4ccecf6 Michael Hanselmann
  @param via_shell: if we should run via the shell
480 a4ccecf6 Michael Hanselmann
  @type cwd: string
481 a4ccecf6 Michael Hanselmann
  @param cwd: the working directory for the program
482 a4ccecf6 Michael Hanselmann
  @type interactive: boolean
483 a4ccecf6 Michael Hanselmann
  @param interactive: Run command interactive (without piping)
484 a4ccecf6 Michael Hanselmann
  @type timeout: int
485 a4ccecf6 Michael Hanselmann
  @param timeout: Timeout after the programm gets terminated
486 7b0bf9cd Apollon Oikonomopoulos
  @type noclose_fds: list
487 7b0bf9cd Apollon Oikonomopoulos
  @param noclose_fds: list of additional (fd >=3) file descriptors to leave
488 7b0bf9cd Apollon Oikonomopoulos
                      open for the child process
489 a4ccecf6 Michael Hanselmann
  @rtype: tuple
490 a4ccecf6 Michael Hanselmann
  @return: (out, err, status)
491 a4ccecf6 Michael Hanselmann

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

613 a4ccecf6 Michael Hanselmann
  @type  cmd: string or list
614 a4ccecf6 Michael Hanselmann
  @param cmd: Command to run
615 a4ccecf6 Michael Hanselmann
  @type env: dict
616 a4ccecf6 Michael Hanselmann
  @param env: The environment to use
617 a4ccecf6 Michael Hanselmann
  @type via_shell: bool
618 a4ccecf6 Michael Hanselmann
  @param via_shell: if we should run via the shell
619 a4ccecf6 Michael Hanselmann
  @type output: str
620 a4ccecf6 Michael Hanselmann
  @param output: the filename in which to save the output
621 a4ccecf6 Michael Hanselmann
  @type cwd: string
622 a4ccecf6 Michael Hanselmann
  @param cwd: the working directory for the program
623 7b0bf9cd Apollon Oikonomopoulos
  @type noclose_fds: list
624 7b0bf9cd Apollon Oikonomopoulos
  @param noclose_fds: list of additional (fd >=3) file descriptors to leave
625 7b0bf9cd Apollon Oikonomopoulos
                      open for the child process
626 a4ccecf6 Michael Hanselmann
  @rtype: int
627 a4ccecf6 Michael Hanselmann
  @return: the exit status
628 a4ccecf6 Michael Hanselmann

629 a4ccecf6 Michael Hanselmann
  """
630 a4ccecf6 Michael Hanselmann
  fh = open(output, "a")
631 7b0bf9cd Apollon Oikonomopoulos
632 7b0bf9cd Apollon Oikonomopoulos
  if noclose_fds:
633 7b0bf9cd Apollon Oikonomopoulos
    preexec_fn = lambda: CloseFDs(noclose_fds + [fh.fileno()])
634 7b0bf9cd Apollon Oikonomopoulos
    close_fds = False
635 7b0bf9cd Apollon Oikonomopoulos
  else:
636 7b0bf9cd Apollon Oikonomopoulos
    preexec_fn = None
637 7b0bf9cd Apollon Oikonomopoulos
    close_fds = True
638 7b0bf9cd Apollon Oikonomopoulos
639 a4ccecf6 Michael Hanselmann
  try:
640 a4ccecf6 Michael Hanselmann
    child = subprocess.Popen(cmd, shell=via_shell,
641 a4ccecf6 Michael Hanselmann
                             stderr=subprocess.STDOUT,
642 a4ccecf6 Michael Hanselmann
                             stdout=fh,
643 a4ccecf6 Michael Hanselmann
                             stdin=subprocess.PIPE,
644 7b0bf9cd Apollon Oikonomopoulos
                             close_fds=close_fds, env=env,
645 7b0bf9cd Apollon Oikonomopoulos
                             cwd=cwd,
646 7b0bf9cd Apollon Oikonomopoulos
                             preexec_fn=preexec_fn)
647 a4ccecf6 Michael Hanselmann
648 a4ccecf6 Michael Hanselmann
    child.stdin.close()
649 a4ccecf6 Michael Hanselmann
    status = child.wait()
650 a4ccecf6 Michael Hanselmann
  finally:
651 a4ccecf6 Michael Hanselmann
    fh.close()
652 a4ccecf6 Michael Hanselmann
  return status
653 a4ccecf6 Michael Hanselmann
654 a4ccecf6 Michael Hanselmann
655 a4ccecf6 Michael Hanselmann
def RunParts(dir_name, env=None, reset_env=False):
656 a4ccecf6 Michael Hanselmann
  """Run Scripts or programs in a directory
657 a4ccecf6 Michael Hanselmann

658 a4ccecf6 Michael Hanselmann
  @type dir_name: string
659 a4ccecf6 Michael Hanselmann
  @param dir_name: absolute path to a directory
660 a4ccecf6 Michael Hanselmann
  @type env: dict
661 a4ccecf6 Michael Hanselmann
  @param env: The environment to use
662 a4ccecf6 Michael Hanselmann
  @type reset_env: boolean
663 a4ccecf6 Michael Hanselmann
  @param reset_env: whether to reset or keep the default os environment
664 a4ccecf6 Michael Hanselmann
  @rtype: list of tuples
665 a4ccecf6 Michael Hanselmann
  @return: list of (name, (one of RUNDIR_STATUS), RunResult)
666 a4ccecf6 Michael Hanselmann

667 a4ccecf6 Michael Hanselmann
  """
668 a4ccecf6 Michael Hanselmann
  rr = []
669 a4ccecf6 Michael Hanselmann
670 a4ccecf6 Michael Hanselmann
  try:
671 a4ccecf6 Michael Hanselmann
    dir_contents = utils_io.ListVisibleFiles(dir_name)
672 a4ccecf6 Michael Hanselmann
  except OSError, err:
673 a4ccecf6 Michael Hanselmann
    logging.warning("RunParts: skipping %s (cannot list: %s)", dir_name, err)
674 a4ccecf6 Michael Hanselmann
    return rr
675 a4ccecf6 Michael Hanselmann
676 a4ccecf6 Michael Hanselmann
  for relname in sorted(dir_contents):
677 a4ccecf6 Michael Hanselmann
    fname = utils_io.PathJoin(dir_name, relname)
678 a4ccecf6 Michael Hanselmann
    if not (os.path.isfile(fname) and os.access(fname, os.X_OK) and
679 a4ccecf6 Michael Hanselmann
            constants.EXT_PLUGIN_MASK.match(relname) is not None):
680 a4ccecf6 Michael Hanselmann
      rr.append((relname, constants.RUNPARTS_SKIP, None))
681 a4ccecf6 Michael Hanselmann
    else:
682 a4ccecf6 Michael Hanselmann
      try:
683 a4ccecf6 Michael Hanselmann
        result = RunCmd([fname], env=env, reset_env=reset_env)
684 a4ccecf6 Michael Hanselmann
      except Exception, err: # pylint: disable-msg=W0703
685 a4ccecf6 Michael Hanselmann
        rr.append((relname, constants.RUNPARTS_ERR, str(err)))
686 a4ccecf6 Michael Hanselmann
      else:
687 a4ccecf6 Michael Hanselmann
        rr.append((relname, constants.RUNPARTS_RUN, result))
688 a4ccecf6 Michael Hanselmann
689 a4ccecf6 Michael Hanselmann
  return rr
690 a4ccecf6 Michael Hanselmann
691 a4ccecf6 Michael Hanselmann
692 a4ccecf6 Michael Hanselmann
def _GetProcStatusPath(pid):
693 a4ccecf6 Michael Hanselmann
  """Returns the path for a PID's proc status file.
694 a4ccecf6 Michael Hanselmann

695 a4ccecf6 Michael Hanselmann
  @type pid: int
696 a4ccecf6 Michael Hanselmann
  @param pid: Process ID
697 a4ccecf6 Michael Hanselmann
  @rtype: string
698 a4ccecf6 Michael Hanselmann

699 a4ccecf6 Michael Hanselmann
  """
700 a4ccecf6 Michael Hanselmann
  return "/proc/%d/status" % pid
701 a4ccecf6 Michael Hanselmann
702 a4ccecf6 Michael Hanselmann
703 a4ccecf6 Michael Hanselmann
def IsProcessAlive(pid):
704 a4ccecf6 Michael Hanselmann
  """Check if a given pid exists on the system.
705 a4ccecf6 Michael Hanselmann

706 a4ccecf6 Michael Hanselmann
  @note: zombie status is not handled, so zombie processes
707 a4ccecf6 Michael Hanselmann
      will be returned as alive
708 a4ccecf6 Michael Hanselmann
  @type pid: int
709 a4ccecf6 Michael Hanselmann
  @param pid: the process ID to check
710 a4ccecf6 Michael Hanselmann
  @rtype: boolean
711 a4ccecf6 Michael Hanselmann
  @return: True if the process exists
712 a4ccecf6 Michael Hanselmann

713 a4ccecf6 Michael Hanselmann
  """
714 a4ccecf6 Michael Hanselmann
  def _TryStat(name):
715 a4ccecf6 Michael Hanselmann
    try:
716 a4ccecf6 Michael Hanselmann
      os.stat(name)
717 a4ccecf6 Michael Hanselmann
      return True
718 a4ccecf6 Michael Hanselmann
    except EnvironmentError, err:
719 a4ccecf6 Michael Hanselmann
      if err.errno in (errno.ENOENT, errno.ENOTDIR):
720 a4ccecf6 Michael Hanselmann
        return False
721 a4ccecf6 Michael Hanselmann
      elif err.errno == errno.EINVAL:
722 a4ccecf6 Michael Hanselmann
        raise utils_retry.RetryAgain(err)
723 a4ccecf6 Michael Hanselmann
      raise
724 a4ccecf6 Michael Hanselmann
725 a4ccecf6 Michael Hanselmann
  assert isinstance(pid, int), "pid must be an integer"
726 a4ccecf6 Michael Hanselmann
  if pid <= 0:
727 a4ccecf6 Michael Hanselmann
    return False
728 a4ccecf6 Michael Hanselmann
729 a4ccecf6 Michael Hanselmann
  # /proc in a multiprocessor environment can have strange behaviors.
730 a4ccecf6 Michael Hanselmann
  # Retry the os.stat a few times until we get a good result.
731 a4ccecf6 Michael Hanselmann
  try:
732 a4ccecf6 Michael Hanselmann
    return utils_retry.Retry(_TryStat, (0.01, 1.5, 0.1), 0.5,
733 a4ccecf6 Michael Hanselmann
                             args=[_GetProcStatusPath(pid)])
734 a4ccecf6 Michael Hanselmann
  except utils_retry.RetryTimeout, err:
735 a4ccecf6 Michael Hanselmann
    err.RaiseInner()
736 a4ccecf6 Michael Hanselmann
737 a4ccecf6 Michael Hanselmann
738 a4ccecf6 Michael Hanselmann
def _ParseSigsetT(sigset):
739 a4ccecf6 Michael Hanselmann
  """Parse a rendered sigset_t value.
740 a4ccecf6 Michael Hanselmann

741 a4ccecf6 Michael Hanselmann
  This is the opposite of the Linux kernel's fs/proc/array.c:render_sigset_t
742 a4ccecf6 Michael Hanselmann
  function.
743 a4ccecf6 Michael Hanselmann

744 a4ccecf6 Michael Hanselmann
  @type sigset: string
745 a4ccecf6 Michael Hanselmann
  @param sigset: Rendered signal set from /proc/$pid/status
746 a4ccecf6 Michael Hanselmann
  @rtype: set
747 a4ccecf6 Michael Hanselmann
  @return: Set of all enabled signal numbers
748 a4ccecf6 Michael Hanselmann

749 a4ccecf6 Michael Hanselmann
  """
750 a4ccecf6 Michael Hanselmann
  result = set()
751 a4ccecf6 Michael Hanselmann
752 a4ccecf6 Michael Hanselmann
  signum = 0
753 a4ccecf6 Michael Hanselmann
  for ch in reversed(sigset):
754 a4ccecf6 Michael Hanselmann
    chv = int(ch, 16)
755 a4ccecf6 Michael Hanselmann
756 a4ccecf6 Michael Hanselmann
    # The following could be done in a loop, but it's easier to read and
757 a4ccecf6 Michael Hanselmann
    # understand in the unrolled form
758 a4ccecf6 Michael Hanselmann
    if chv & 1:
759 a4ccecf6 Michael Hanselmann
      result.add(signum + 1)
760 a4ccecf6 Michael Hanselmann
    if chv & 2:
761 a4ccecf6 Michael Hanselmann
      result.add(signum + 2)
762 a4ccecf6 Michael Hanselmann
    if chv & 4:
763 a4ccecf6 Michael Hanselmann
      result.add(signum + 3)
764 a4ccecf6 Michael Hanselmann
    if chv & 8:
765 a4ccecf6 Michael Hanselmann
      result.add(signum + 4)
766 a4ccecf6 Michael Hanselmann
767 a4ccecf6 Michael Hanselmann
    signum += 4
768 a4ccecf6 Michael Hanselmann
769 a4ccecf6 Michael Hanselmann
  return result
770 a4ccecf6 Michael Hanselmann
771 a4ccecf6 Michael Hanselmann
772 a4ccecf6 Michael Hanselmann
def _GetProcStatusField(pstatus, field):
773 a4ccecf6 Michael Hanselmann
  """Retrieves a field from the contents of a proc status file.
774 a4ccecf6 Michael Hanselmann

775 a4ccecf6 Michael Hanselmann
  @type pstatus: string
776 a4ccecf6 Michael Hanselmann
  @param pstatus: Contents of /proc/$pid/status
777 a4ccecf6 Michael Hanselmann
  @type field: string
778 a4ccecf6 Michael Hanselmann
  @param field: Name of field whose value should be returned
779 a4ccecf6 Michael Hanselmann
  @rtype: string
780 a4ccecf6 Michael Hanselmann

781 a4ccecf6 Michael Hanselmann
  """
782 a4ccecf6 Michael Hanselmann
  for line in pstatus.splitlines():
783 a4ccecf6 Michael Hanselmann
    parts = line.split(":", 1)
784 a4ccecf6 Michael Hanselmann
785 a4ccecf6 Michael Hanselmann
    if len(parts) < 2 or parts[0] != field:
786 a4ccecf6 Michael Hanselmann
      continue
787 a4ccecf6 Michael Hanselmann
788 a4ccecf6 Michael Hanselmann
    return parts[1].strip()
789 a4ccecf6 Michael Hanselmann
790 a4ccecf6 Michael Hanselmann
  return None
791 a4ccecf6 Michael Hanselmann
792 a4ccecf6 Michael Hanselmann
793 a4ccecf6 Michael Hanselmann
def IsProcessHandlingSignal(pid, signum, status_path=None):
794 a4ccecf6 Michael Hanselmann
  """Checks whether a process is handling a signal.
795 a4ccecf6 Michael Hanselmann

796 a4ccecf6 Michael Hanselmann
  @type pid: int
797 a4ccecf6 Michael Hanselmann
  @param pid: Process ID
798 a4ccecf6 Michael Hanselmann
  @type signum: int
799 a4ccecf6 Michael Hanselmann
  @param signum: Signal number
800 a4ccecf6 Michael Hanselmann
  @rtype: bool
801 a4ccecf6 Michael Hanselmann

802 a4ccecf6 Michael Hanselmann
  """
803 a4ccecf6 Michael Hanselmann
  if status_path is None:
804 a4ccecf6 Michael Hanselmann
    status_path = _GetProcStatusPath(pid)
805 a4ccecf6 Michael Hanselmann
806 a4ccecf6 Michael Hanselmann
  try:
807 a4ccecf6 Michael Hanselmann
    proc_status = utils_io.ReadFile(status_path)
808 a4ccecf6 Michael Hanselmann
  except EnvironmentError, err:
809 a4ccecf6 Michael Hanselmann
    # In at least one case, reading /proc/$pid/status failed with ESRCH.
810 a4ccecf6 Michael Hanselmann
    if err.errno in (errno.ENOENT, errno.ENOTDIR, errno.EINVAL, errno.ESRCH):
811 a4ccecf6 Michael Hanselmann
      return False
812 a4ccecf6 Michael Hanselmann
    raise
813 a4ccecf6 Michael Hanselmann
814 a4ccecf6 Michael Hanselmann
  sigcgt = _GetProcStatusField(proc_status, "SigCgt")
815 a4ccecf6 Michael Hanselmann
  if sigcgt is None:
816 a4ccecf6 Michael Hanselmann
    raise RuntimeError("%s is missing 'SigCgt' field" % status_path)
817 a4ccecf6 Michael Hanselmann
818 a4ccecf6 Michael Hanselmann
  # Now check whether signal is handled
819 a4ccecf6 Michael Hanselmann
  return signum in _ParseSigsetT(sigcgt)
820 a4ccecf6 Michael Hanselmann
821 a4ccecf6 Michael Hanselmann
822 a4ccecf6 Michael Hanselmann
def Daemonize(logfile):
823 a4ccecf6 Michael Hanselmann
  """Daemonize the current process.
824 a4ccecf6 Michael Hanselmann

825 a4ccecf6 Michael Hanselmann
  This detaches the current process from the controlling terminal and
826 a4ccecf6 Michael Hanselmann
  runs it in the background as a daemon.
827 a4ccecf6 Michael Hanselmann

828 a4ccecf6 Michael Hanselmann
  @type logfile: str
829 a4ccecf6 Michael Hanselmann
  @param logfile: the logfile to which we should redirect stdout/stderr
830 a4ccecf6 Michael Hanselmann
  @rtype: int
831 a4ccecf6 Michael Hanselmann
  @return: the value zero
832 a4ccecf6 Michael Hanselmann

833 a4ccecf6 Michael Hanselmann
  """
834 a4ccecf6 Michael Hanselmann
  # pylint: disable-msg=W0212
835 a4ccecf6 Michael Hanselmann
  # yes, we really want os._exit
836 a4ccecf6 Michael Hanselmann
837 a4ccecf6 Michael Hanselmann
  # TODO: do another attempt to merge Daemonize and StartDaemon, or at
838 a4ccecf6 Michael Hanselmann
  # least abstract the pipe functionality between them
839 a4ccecf6 Michael Hanselmann
840 a4ccecf6 Michael Hanselmann
  # Create pipe for sending error messages
841 a4ccecf6 Michael Hanselmann
  (rpipe, wpipe) = os.pipe()
842 a4ccecf6 Michael Hanselmann
843 a4ccecf6 Michael Hanselmann
  # this might fail
844 a4ccecf6 Michael Hanselmann
  pid = os.fork()
845 a4ccecf6 Michael Hanselmann
  if (pid == 0):  # The first child.
846 a4ccecf6 Michael Hanselmann
    SetupDaemonEnv()
847 a4ccecf6 Michael Hanselmann
848 a4ccecf6 Michael Hanselmann
    # this might fail
849 a4ccecf6 Michael Hanselmann
    pid = os.fork() # Fork a second child.
850 a4ccecf6 Michael Hanselmann
    if (pid == 0):  # The second child.
851 a4ccecf6 Michael Hanselmann
      utils_wrapper.CloseFdNoError(rpipe)
852 a4ccecf6 Michael Hanselmann
    else:
853 a4ccecf6 Michael Hanselmann
      # exit() or _exit()?  See below.
854 a4ccecf6 Michael Hanselmann
      os._exit(0) # Exit parent (the first child) of the second child.
855 a4ccecf6 Michael Hanselmann
  else:
856 a4ccecf6 Michael Hanselmann
    utils_wrapper.CloseFdNoError(wpipe)
857 a4ccecf6 Michael Hanselmann
    # Wait for daemon to be started (or an error message to
858 a4ccecf6 Michael Hanselmann
    # arrive) and read up to 100 KB as an error message
859 a4ccecf6 Michael Hanselmann
    errormsg = utils_wrapper.RetryOnSignal(os.read, rpipe, 100 * 1024)
860 a4ccecf6 Michael Hanselmann
    if errormsg:
861 a4ccecf6 Michael Hanselmann
      sys.stderr.write("Error when starting daemon process: %r\n" % errormsg)
862 a4ccecf6 Michael Hanselmann
      rcode = 1
863 a4ccecf6 Michael Hanselmann
    else:
864 a4ccecf6 Michael Hanselmann
      rcode = 0
865 a4ccecf6 Michael Hanselmann
    os._exit(rcode) # Exit parent of the first child.
866 a4ccecf6 Michael Hanselmann
867 a4ccecf6 Michael Hanselmann
  SetupDaemonFDs(logfile, None)
868 a4ccecf6 Michael Hanselmann
  return wpipe
869 a4ccecf6 Michael Hanselmann
870 a4ccecf6 Michael Hanselmann
871 a4ccecf6 Michael Hanselmann
def KillProcess(pid, signal_=signal.SIGTERM, timeout=30,
872 a4ccecf6 Michael Hanselmann
                waitpid=False):
873 a4ccecf6 Michael Hanselmann
  """Kill a process given by its pid.
874 a4ccecf6 Michael Hanselmann

875 a4ccecf6 Michael Hanselmann
  @type pid: int
876 a4ccecf6 Michael Hanselmann
  @param pid: The PID to terminate.
877 a4ccecf6 Michael Hanselmann
  @type signal_: int
878 a4ccecf6 Michael Hanselmann
  @param signal_: The signal to send, by default SIGTERM
879 a4ccecf6 Michael Hanselmann
  @type timeout: int
880 a4ccecf6 Michael Hanselmann
  @param timeout: The timeout after which, if the process is still alive,
881 a4ccecf6 Michael Hanselmann
                  a SIGKILL will be sent. If not positive, no such checking
882 a4ccecf6 Michael Hanselmann
                  will be done
883 a4ccecf6 Michael Hanselmann
  @type waitpid: boolean
884 a4ccecf6 Michael Hanselmann
  @param waitpid: If true, we should waitpid on this process after
885 a4ccecf6 Michael Hanselmann
      sending signals, since it's our own child and otherwise it
886 a4ccecf6 Michael Hanselmann
      would remain as zombie
887 a4ccecf6 Michael Hanselmann

888 a4ccecf6 Michael Hanselmann
  """
889 a4ccecf6 Michael Hanselmann
  def _helper(pid, signal_, wait):
890 a4ccecf6 Michael Hanselmann
    """Simple helper to encapsulate the kill/waitpid sequence"""
891 a4ccecf6 Michael Hanselmann
    if utils_wrapper.IgnoreProcessNotFound(os.kill, pid, signal_) and wait:
892 a4ccecf6 Michael Hanselmann
      try:
893 a4ccecf6 Michael Hanselmann
        os.waitpid(pid, os.WNOHANG)
894 a4ccecf6 Michael Hanselmann
      except OSError:
895 a4ccecf6 Michael Hanselmann
        pass
896 a4ccecf6 Michael Hanselmann
897 a4ccecf6 Michael Hanselmann
  if pid <= 0:
898 a4ccecf6 Michael Hanselmann
    # kill with pid=0 == suicide
899 a4ccecf6 Michael Hanselmann
    raise errors.ProgrammerError("Invalid pid given '%s'" % pid)
900 a4ccecf6 Michael Hanselmann
901 a4ccecf6 Michael Hanselmann
  if not IsProcessAlive(pid):
902 a4ccecf6 Michael Hanselmann
    return
903 a4ccecf6 Michael Hanselmann
904 a4ccecf6 Michael Hanselmann
  _helper(pid, signal_, waitpid)
905 a4ccecf6 Michael Hanselmann
906 a4ccecf6 Michael Hanselmann
  if timeout <= 0:
907 a4ccecf6 Michael Hanselmann
    return
908 a4ccecf6 Michael Hanselmann
909 a4ccecf6 Michael Hanselmann
  def _CheckProcess():
910 a4ccecf6 Michael Hanselmann
    if not IsProcessAlive(pid):
911 a4ccecf6 Michael Hanselmann
      return
912 a4ccecf6 Michael Hanselmann
913 a4ccecf6 Michael Hanselmann
    try:
914 a4ccecf6 Michael Hanselmann
      (result_pid, _) = os.waitpid(pid, os.WNOHANG)
915 a4ccecf6 Michael Hanselmann
    except OSError:
916 a4ccecf6 Michael Hanselmann
      raise utils_retry.RetryAgain()
917 a4ccecf6 Michael Hanselmann
918 a4ccecf6 Michael Hanselmann
    if result_pid > 0:
919 a4ccecf6 Michael Hanselmann
      return
920 a4ccecf6 Michael Hanselmann
921 a4ccecf6 Michael Hanselmann
    raise utils_retry.RetryAgain()
922 a4ccecf6 Michael Hanselmann
923 a4ccecf6 Michael Hanselmann
  try:
924 a4ccecf6 Michael Hanselmann
    # Wait up to $timeout seconds
925 a4ccecf6 Michael Hanselmann
    utils_retry.Retry(_CheckProcess, (0.01, 1.5, 0.1), timeout)
926 a4ccecf6 Michael Hanselmann
  except utils_retry.RetryTimeout:
927 a4ccecf6 Michael Hanselmann
    pass
928 a4ccecf6 Michael Hanselmann
929 a4ccecf6 Michael Hanselmann
  if IsProcessAlive(pid):
930 a4ccecf6 Michael Hanselmann
    # Kill process if it's still alive
931 a4ccecf6 Michael Hanselmann
    _helper(pid, signal.SIGKILL, waitpid)
932 a4ccecf6 Michael Hanselmann
933 a4ccecf6 Michael Hanselmann
934 a4ccecf6 Michael Hanselmann
def RunInSeparateProcess(fn, *args):
935 a4ccecf6 Michael Hanselmann
  """Runs a function in a separate process.
936 a4ccecf6 Michael Hanselmann

937 a4ccecf6 Michael Hanselmann
  Note: Only boolean return values are supported.
938 a4ccecf6 Michael Hanselmann

939 a4ccecf6 Michael Hanselmann
  @type fn: callable
940 a4ccecf6 Michael Hanselmann
  @param fn: Function to be called
941 a4ccecf6 Michael Hanselmann
  @rtype: bool
942 a4ccecf6 Michael Hanselmann
  @return: Function's result
943 a4ccecf6 Michael Hanselmann

944 a4ccecf6 Michael Hanselmann
  """
945 a4ccecf6 Michael Hanselmann
  pid = os.fork()
946 a4ccecf6 Michael Hanselmann
  if pid == 0:
947 a4ccecf6 Michael Hanselmann
    # Child process
948 a4ccecf6 Michael Hanselmann
    try:
949 a4ccecf6 Michael Hanselmann
      # In case the function uses temporary files
950 a4ccecf6 Michael Hanselmann
      utils_wrapper.ResetTempfileModule()
951 a4ccecf6 Michael Hanselmann
952 a4ccecf6 Michael Hanselmann
      # Call function
953 a4ccecf6 Michael Hanselmann
      result = int(bool(fn(*args)))
954 a4ccecf6 Michael Hanselmann
      assert result in (0, 1)
955 a4ccecf6 Michael Hanselmann
    except: # pylint: disable-msg=W0702
956 a4ccecf6 Michael Hanselmann
      logging.exception("Error while calling function in separate process")
957 a4ccecf6 Michael Hanselmann
      # 0 and 1 are reserved for the return value
958 a4ccecf6 Michael Hanselmann
      result = 33
959 a4ccecf6 Michael Hanselmann
960 a4ccecf6 Michael Hanselmann
    os._exit(result) # pylint: disable-msg=W0212
961 a4ccecf6 Michael Hanselmann
962 a4ccecf6 Michael Hanselmann
  # Parent process
963 a4ccecf6 Michael Hanselmann
964 a4ccecf6 Michael Hanselmann
  # Avoid zombies and check exit code
965 a4ccecf6 Michael Hanselmann
  (_, status) = os.waitpid(pid, 0)
966 a4ccecf6 Michael Hanselmann
967 a4ccecf6 Michael Hanselmann
  if os.WIFSIGNALED(status):
968 a4ccecf6 Michael Hanselmann
    exitcode = None
969 a4ccecf6 Michael Hanselmann
    signum = os.WTERMSIG(status)
970 a4ccecf6 Michael Hanselmann
  else:
971 a4ccecf6 Michael Hanselmann
    exitcode = os.WEXITSTATUS(status)
972 a4ccecf6 Michael Hanselmann
    signum = None
973 a4ccecf6 Michael Hanselmann
974 a4ccecf6 Michael Hanselmann
  if not (exitcode in (0, 1) and signum is None):
975 a4ccecf6 Michael Hanselmann
    raise errors.GenericError("Child program failed (code=%s, signal=%s)" %
976 a4ccecf6 Michael Hanselmann
                              (exitcode, signum))
977 a4ccecf6 Michael Hanselmann
978 a4ccecf6 Michael Hanselmann
  return bool(exitcode)
979 a4ccecf6 Michael Hanselmann
980 a4ccecf6 Michael Hanselmann
981 a4ccecf6 Michael Hanselmann
def CloseFDs(noclose_fds=None):
982 a4ccecf6 Michael Hanselmann
  """Close file descriptors.
983 a4ccecf6 Michael Hanselmann

984 a4ccecf6 Michael Hanselmann
  This closes all file descriptors above 2 (i.e. except
985 a4ccecf6 Michael Hanselmann
  stdin/out/err).
986 a4ccecf6 Michael Hanselmann

987 a4ccecf6 Michael Hanselmann
  @type noclose_fds: list or None
988 a4ccecf6 Michael Hanselmann
  @param noclose_fds: if given, it denotes a list of file descriptor
989 a4ccecf6 Michael Hanselmann
      that should not be closed
990 a4ccecf6 Michael Hanselmann

991 a4ccecf6 Michael Hanselmann
  """
992 a4ccecf6 Michael Hanselmann
  # Default maximum for the number of available file descriptors.
993 a4ccecf6 Michael Hanselmann
  if 'SC_OPEN_MAX' in os.sysconf_names:
994 a4ccecf6 Michael Hanselmann
    try:
995 a4ccecf6 Michael Hanselmann
      MAXFD = os.sysconf('SC_OPEN_MAX')
996 a4ccecf6 Michael Hanselmann
      if MAXFD < 0:
997 a4ccecf6 Michael Hanselmann
        MAXFD = 1024
998 a4ccecf6 Michael Hanselmann
    except OSError:
999 a4ccecf6 Michael Hanselmann
      MAXFD = 1024
1000 a4ccecf6 Michael Hanselmann
  else:
1001 a4ccecf6 Michael Hanselmann
    MAXFD = 1024
1002 a4ccecf6 Michael Hanselmann
1003 a4ccecf6 Michael Hanselmann
  maxfd = resource.getrlimit(resource.RLIMIT_NOFILE)[1]
1004 a4ccecf6 Michael Hanselmann
  if (maxfd == resource.RLIM_INFINITY):
1005 a4ccecf6 Michael Hanselmann
    maxfd = MAXFD
1006 a4ccecf6 Michael Hanselmann
1007 a4ccecf6 Michael Hanselmann
  # Iterate through and close all file descriptors (except the standard ones)
1008 a4ccecf6 Michael Hanselmann
  for fd in range(3, maxfd):
1009 a4ccecf6 Michael Hanselmann
    if noclose_fds and fd in noclose_fds:
1010 a4ccecf6 Michael Hanselmann
      continue
1011 a4ccecf6 Michael Hanselmann
    utils_wrapper.CloseFdNoError(fd)