Statistics
| Branch: | Tag: | Revision:

root / lib / utils / process.py @ 0aee8ee9

History | View | Annotate | Download (28.6 kB)

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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