Statistics
| Branch: | Tag: | Revision:

root / lib / utils / __init__.py @ c50645c0

History | View | Annotate | Download (52.7 kB)

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

24 58885d79 Iustin Pop
This module holds functions that can be used in both daemons (all) and
25 58885d79 Iustin Pop
the command line scripts.
26 899d2a81 Michael Hanselmann

27 a8083063 Iustin Pop
"""
28 a8083063 Iustin Pop
29 a8083063 Iustin Pop
30 a8083063 Iustin Pop
import os
31 c1dd99d4 Michael Hanselmann
import sys
32 a8083063 Iustin Pop
import time
33 113b55aa Iustin Pop
import subprocess
34 a8083063 Iustin Pop
import re
35 a8083063 Iustin Pop
import socket
36 a8083063 Iustin Pop
import tempfile
37 a8083063 Iustin Pop
import shutil
38 4ca1b175 Alexander Schreiber
import errno
39 2f8b60b3 Iustin Pop
import pwd
40 78feb6fb Guido Trotter
import itertools
41 9c233417 Iustin Pop
import select
42 9c233417 Iustin Pop
import fcntl
43 8f765069 Iustin Pop
import resource
44 bb698c1f Iustin Pop
import logging
45 de499029 Michael Hanselmann
import signal
46 27e46076 Michael Hanselmann
import datetime
47 27e46076 Michael Hanselmann
import calendar
48 9c233417 Iustin Pop
49 9c233417 Iustin Pop
from cStringIO import StringIO
50 a8083063 Iustin Pop
51 a8083063 Iustin Pop
from ganeti import errors
52 3aecd2c7 Iustin Pop
from ganeti import constants
53 716a32cb Guido Trotter
from ganeti import compat
54 a8083063 Iustin Pop
55 4fd029cf Michael Hanselmann
from ganeti.utils.algo import * # pylint: disable-msg=W0401
56 79d22269 Michael Hanselmann
from ganeti.utils.retry import * # pylint: disable-msg=W0401
57 7fcffe27 Michael Hanselmann
from ganeti.utils.text import * # pylint: disable-msg=W0401
58 36a4acd4 Michael Hanselmann
from ganeti.utils.mlock import * # pylint: disable-msg=W0401
59 1ae4c5a1 Michael Hanselmann
from ganeti.utils.log import * # pylint: disable-msg=W0401
60 f21bb4b7 Michael Hanselmann
from ganeti.utils.hash import * # pylint: disable-msg=W0401
61 7831fc5f Michael Hanselmann
from ganeti.utils.wrapper import * # pylint: disable-msg=W0401
62 9d1b963f Michael Hanselmann
from ganeti.utils.filelock import * # pylint: disable-msg=W0401
63 3865ca48 Michael Hanselmann
from ganeti.utils.io import * # pylint: disable-msg=W0401
64 c50645c0 Michael Hanselmann
from ganeti.utils.x509 import * # pylint: disable-msg=W0401
65 16abfbc2 Alexander Schreiber
66 58885d79 Iustin Pop
67 58885d79 Iustin Pop
#: when set to True, L{RunCmd} is disabled
68 7b4baeb1 Michael Hanselmann
_no_fork = False
69 f362096f Iustin Pop
70 13998ef2 Michael Hanselmann
_RANDOM_UUID_FILE = "/proc/sys/kernel/random/uuid"
71 13998ef2 Michael Hanselmann
72 28f34048 Michael Hanselmann
_VALID_SERVICE_NAME_RE = re.compile("^[-_.a-zA-Z0-9]{1,128}$")
73 28f34048 Michael Hanselmann
74 05636402 Guido Trotter
UUID_RE = re.compile('^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-'
75 05636402 Guido Trotter
                     '[a-f0-9]{4}-[a-f0-9]{12}$')
76 05636402 Guido Trotter
77 c74cda62 René Nussbaumer
(_TIMEOUT_NONE,
78 c74cda62 René Nussbaumer
 _TIMEOUT_TERM,
79 c74cda62 René Nussbaumer
 _TIMEOUT_KILL) = range(3)
80 c74cda62 René Nussbaumer
81 0b5303da Iustin Pop
#: Shell param checker regexp
82 0b5303da Iustin Pop
_SHELLPARAM_REGEX = re.compile(r"^[-a-zA-Z0-9._+/:%@]+$")
83 0b5303da Iustin Pop
84 7c0d6283 Michael Hanselmann
85 7b4baeb1 Michael Hanselmann
def DisableFork():
86 7b4baeb1 Michael Hanselmann
  """Disables the use of fork(2).
87 7b4baeb1 Michael Hanselmann

88 7b4baeb1 Michael Hanselmann
  """
89 7b4baeb1 Michael Hanselmann
  global _no_fork # pylint: disable-msg=W0603
90 7b4baeb1 Michael Hanselmann
91 7b4baeb1 Michael Hanselmann
  _no_fork = True
92 7b4baeb1 Michael Hanselmann
93 7b4baeb1 Michael Hanselmann
94 a8083063 Iustin Pop
class RunResult(object):
95 58885d79 Iustin Pop
  """Holds the result of running external programs.
96 58885d79 Iustin Pop

97 58885d79 Iustin Pop
  @type exit_code: int
98 58885d79 Iustin Pop
  @ivar exit_code: the exit code of the program, or None (if the program
99 58885d79 Iustin Pop
      didn't exit())
100 58885d79 Iustin Pop
  @type signal: int or None
101 58885d79 Iustin Pop
  @ivar signal: the signal that caused the program to finish, or None
102 58885d79 Iustin Pop
      (if the program wasn't terminated by a signal)
103 58885d79 Iustin Pop
  @type stdout: str
104 58885d79 Iustin Pop
  @ivar stdout: the standard output of the program
105 58885d79 Iustin Pop
  @type stderr: str
106 58885d79 Iustin Pop
  @ivar stderr: the standard error of the program
107 58885d79 Iustin Pop
  @type failed: boolean
108 58885d79 Iustin Pop
  @ivar failed: True in case the program was
109 58885d79 Iustin Pop
      terminated by a signal or exited with a non-zero exit code
110 58885d79 Iustin Pop
  @ivar fail_reason: a string detailing the termination reason
111 a8083063 Iustin Pop

112 a8083063 Iustin Pop
  """
113 a8083063 Iustin Pop
  __slots__ = ["exit_code", "signal", "stdout", "stderr",
114 a8083063 Iustin Pop
               "failed", "fail_reason", "cmd"]
115 a8083063 Iustin Pop
116 a8083063 Iustin Pop
117 c74cda62 René Nussbaumer
  def __init__(self, exit_code, signal_, stdout, stderr, cmd, timeout_action,
118 c74cda62 René Nussbaumer
               timeout):
119 a8083063 Iustin Pop
    self.cmd = cmd
120 a8083063 Iustin Pop
    self.exit_code = exit_code
121 38206f3c Iustin Pop
    self.signal = signal_
122 a8083063 Iustin Pop
    self.stdout = stdout
123 a8083063 Iustin Pop
    self.stderr = stderr
124 38206f3c Iustin Pop
    self.failed = (signal_ is not None or exit_code != 0)
125 a8083063 Iustin Pop
126 c74cda62 René Nussbaumer
    fail_msgs = []
127 a8083063 Iustin Pop
    if self.signal is not None:
128 c74cda62 René Nussbaumer
      fail_msgs.append("terminated by signal %s" % self.signal)
129 a8083063 Iustin Pop
    elif self.exit_code is not None:
130 c74cda62 René Nussbaumer
      fail_msgs.append("exited with exit code %s" % self.exit_code)
131 a8083063 Iustin Pop
    else:
132 c74cda62 René Nussbaumer
      fail_msgs.append("unable to determine termination reason")
133 c74cda62 René Nussbaumer
134 c74cda62 René Nussbaumer
    if timeout_action == _TIMEOUT_TERM:
135 c74cda62 René Nussbaumer
      fail_msgs.append("terminated after timeout of %.2f seconds" % timeout)
136 c74cda62 René Nussbaumer
    elif timeout_action == _TIMEOUT_KILL:
137 c74cda62 René Nussbaumer
      fail_msgs.append(("force termination after timeout of %.2f seconds"
138 c74cda62 René Nussbaumer
                        " and linger for another %.2f seconds") %
139 c74cda62 René Nussbaumer
                       (timeout, constants.CHILD_LINGER_TIMEOUT))
140 c74cda62 René Nussbaumer
141 c74cda62 René Nussbaumer
    if fail_msgs and self.failed:
142 c74cda62 René Nussbaumer
      self.fail_reason = CommaJoin(fail_msgs)
143 a8083063 Iustin Pop
144 bb698c1f Iustin Pop
    if self.failed:
145 bb698c1f Iustin Pop
      logging.debug("Command '%s' failed (%s); output: %s",
146 bb698c1f Iustin Pop
                    self.cmd, self.fail_reason, self.output)
147 f362096f Iustin Pop
148 a8083063 Iustin Pop
  def _GetOutput(self):
149 a8083063 Iustin Pop
    """Returns the combined stdout and stderr for easier usage.
150 a8083063 Iustin Pop

151 a8083063 Iustin Pop
    """
152 a8083063 Iustin Pop
    return self.stdout + self.stderr
153 a8083063 Iustin Pop
154 a8083063 Iustin Pop
  output = property(_GetOutput, None, None, "Return full output")
155 a8083063 Iustin Pop
156 a8083063 Iustin Pop
157 bb3776b4 Michael Hanselmann
def _BuildCmdEnvironment(env, reset):
158 c1dd99d4 Michael Hanselmann
  """Builds the environment for an external program.
159 c1dd99d4 Michael Hanselmann

160 c1dd99d4 Michael Hanselmann
  """
161 bb3776b4 Michael Hanselmann
  if reset:
162 bb3776b4 Michael Hanselmann
    cmd_env = {}
163 bb3776b4 Michael Hanselmann
  else:
164 bb3776b4 Michael Hanselmann
    cmd_env = os.environ.copy()
165 bb3776b4 Michael Hanselmann
    cmd_env["LC_ALL"] = "C"
166 bb3776b4 Michael Hanselmann
167 c1dd99d4 Michael Hanselmann
  if env is not None:
168 c1dd99d4 Michael Hanselmann
    cmd_env.update(env)
169 bb3776b4 Michael Hanselmann
170 c1dd99d4 Michael Hanselmann
  return cmd_env
171 c1dd99d4 Michael Hanselmann
172 c1dd99d4 Michael Hanselmann
173 0963d545 René Nussbaumer
def RunCmd(cmd, env=None, output=None, cwd="/", reset_env=False,
174 c74cda62 René Nussbaumer
           interactive=False, timeout=None):
175 a8083063 Iustin Pop
  """Execute a (shell) command.
176 a8083063 Iustin Pop

177 a8083063 Iustin Pop
  The command should not read from its standard input, as it will be
178 a8083063 Iustin Pop
  closed.
179 a8083063 Iustin Pop

180 c1dd99d4 Michael Hanselmann
  @type cmd: string or list
181 36117c2b Iustin Pop
  @param cmd: Command to run
182 2557ff82 Guido Trotter
  @type env: dict
183 c1dd99d4 Michael Hanselmann
  @param env: Additional environment variables
184 36117c2b Iustin Pop
  @type output: str
185 58885d79 Iustin Pop
  @param output: if desired, the output of the command can be
186 36117c2b Iustin Pop
      saved in a file instead of the RunResult instance; this
187 36117c2b Iustin Pop
      parameter denotes the file name (if not None)
188 8797df43 Iustin Pop
  @type cwd: string
189 8797df43 Iustin Pop
  @param cwd: if specified, will be used as the working
190 8797df43 Iustin Pop
      directory for the command; the default will be /
191 bf4daac9 Guido Trotter
  @type reset_env: boolean
192 bf4daac9 Guido Trotter
  @param reset_env: whether to reset or keep the default os environment
193 0963d545 René Nussbaumer
  @type interactive: boolean
194 0963d545 René Nussbaumer
  @param interactive: weather we pipe stdin, stdout and stderr
195 0963d545 René Nussbaumer
                      (default behaviour) or run the command interactive
196 c74cda62 René Nussbaumer
  @type timeout: int
197 c74cda62 René Nussbaumer
  @param timeout: If not None, timeout in seconds until child process gets
198 c74cda62 René Nussbaumer
                  killed
199 36117c2b Iustin Pop
  @rtype: L{RunResult}
200 58885d79 Iustin Pop
  @return: RunResult instance
201 5bbd3f7f Michael Hanselmann
  @raise errors.ProgrammerError: if we call this when forks are disabled
202 a8083063 Iustin Pop

203 a8083063 Iustin Pop
  """
204 7b4baeb1 Michael Hanselmann
  if _no_fork:
205 b74159ee Iustin Pop
    raise errors.ProgrammerError("utils.RunCmd() called with fork() disabled")
206 b74159ee Iustin Pop
207 0963d545 René Nussbaumer
  if output and interactive:
208 0963d545 René Nussbaumer
    raise errors.ProgrammerError("Parameters 'output' and 'interactive' can"
209 0963d545 René Nussbaumer
                                 " not be provided at the same time")
210 0963d545 René Nussbaumer
211 c1dd99d4 Michael Hanselmann
  if isinstance(cmd, basestring):
212 c1dd99d4 Michael Hanselmann
    strcmd = cmd
213 c1dd99d4 Michael Hanselmann
    shell = True
214 c1dd99d4 Michael Hanselmann
  else:
215 a8083063 Iustin Pop
    cmd = [str(val) for val in cmd]
216 c1dd99d4 Michael Hanselmann
    strcmd = ShellQuoteArgs(cmd)
217 113b55aa Iustin Pop
    shell = False
218 c1dd99d4 Michael Hanselmann
219 c1dd99d4 Michael Hanselmann
  if output:
220 c1dd99d4 Michael Hanselmann
    logging.debug("RunCmd %s, output file '%s'", strcmd, output)
221 113b55aa Iustin Pop
  else:
222 c1dd99d4 Michael Hanselmann
    logging.debug("RunCmd %s", strcmd)
223 2557ff82 Guido Trotter
224 bb3776b4 Michael Hanselmann
  cmd_env = _BuildCmdEnvironment(env, reset_env)
225 2557ff82 Guido Trotter
226 c803b052 Iustin Pop
  try:
227 c803b052 Iustin Pop
    if output is None:
228 c74cda62 René Nussbaumer
      out, err, status, timeout_action = _RunCmdPipe(cmd, cmd_env, shell, cwd,
229 c74cda62 René Nussbaumer
                                                     interactive, timeout)
230 c803b052 Iustin Pop
    else:
231 c74cda62 René Nussbaumer
      timeout_action = _TIMEOUT_NONE
232 c803b052 Iustin Pop
      status = _RunCmdFile(cmd, cmd_env, shell, output, cwd)
233 c803b052 Iustin Pop
      out = err = ""
234 c803b052 Iustin Pop
  except OSError, err:
235 c803b052 Iustin Pop
    if err.errno == errno.ENOENT:
236 c803b052 Iustin Pop
      raise errors.OpExecError("Can't execute '%s': not found (%s)" %
237 c803b052 Iustin Pop
                               (strcmd, err))
238 c803b052 Iustin Pop
    else:
239 c803b052 Iustin Pop
      raise
240 36117c2b Iustin Pop
241 36117c2b Iustin Pop
  if status >= 0:
242 36117c2b Iustin Pop
    exitcode = status
243 36117c2b Iustin Pop
    signal_ = None
244 36117c2b Iustin Pop
  else:
245 36117c2b Iustin Pop
    exitcode = None
246 36117c2b Iustin Pop
    signal_ = -status
247 36117c2b Iustin Pop
248 c74cda62 René Nussbaumer
  return RunResult(exitcode, signal_, out, err, strcmd, timeout_action, timeout)
249 36117c2b Iustin Pop
250 ae59efea Michael Hanselmann
251 0260032c Iustin Pop
def SetupDaemonEnv(cwd="/", umask=077):
252 0260032c Iustin Pop
  """Setup a daemon's environment.
253 0260032c Iustin Pop

254 0260032c Iustin Pop
  This should be called between the first and second fork, due to
255 0260032c Iustin Pop
  setsid usage.
256 0260032c Iustin Pop

257 0260032c Iustin Pop
  @param cwd: the directory to which to chdir
258 0260032c Iustin Pop
  @param umask: the umask to setup
259 0260032c Iustin Pop

260 0260032c Iustin Pop
  """
261 0260032c Iustin Pop
  os.chdir(cwd)
262 0260032c Iustin Pop
  os.umask(umask)
263 0260032c Iustin Pop
  os.setsid()
264 0260032c Iustin Pop
265 0260032c Iustin Pop
266 79634555 Iustin Pop
def SetupDaemonFDs(output_file, output_fd):
267 79634555 Iustin Pop
  """Setups up a daemon's file descriptors.
268 79634555 Iustin Pop

269 79634555 Iustin Pop
  @param output_file: if not None, the file to which to redirect
270 79634555 Iustin Pop
      stdout/stderr
271 79634555 Iustin Pop
  @param output_fd: if not None, the file descriptor for stdout/stderr
272 79634555 Iustin Pop

273 79634555 Iustin Pop
  """
274 79634555 Iustin Pop
  # check that at most one is defined
275 79634555 Iustin Pop
  assert [output_file, output_fd].count(None) >= 1
276 79634555 Iustin Pop
277 79634555 Iustin Pop
  # Open /dev/null (read-only, only for stdin)
278 79634555 Iustin Pop
  devnull_fd = os.open(os.devnull, os.O_RDONLY)
279 79634555 Iustin Pop
280 79634555 Iustin Pop
  if output_fd is not None:
281 79634555 Iustin Pop
    pass
282 79634555 Iustin Pop
  elif output_file is not None:
283 79634555 Iustin Pop
    # Open output file
284 79634555 Iustin Pop
    try:
285 79634555 Iustin Pop
      output_fd = os.open(output_file,
286 79634555 Iustin Pop
                          os.O_WRONLY | os.O_CREAT | os.O_APPEND, 0600)
287 79634555 Iustin Pop
    except EnvironmentError, err:
288 79634555 Iustin Pop
      raise Exception("Opening output file failed: %s" % err)
289 79634555 Iustin Pop
  else:
290 79634555 Iustin Pop
    output_fd = os.open(os.devnull, os.O_WRONLY)
291 79634555 Iustin Pop
292 79634555 Iustin Pop
  # Redirect standard I/O
293 79634555 Iustin Pop
  os.dup2(devnull_fd, 0)
294 79634555 Iustin Pop
  os.dup2(output_fd, 1)
295 79634555 Iustin Pop
  os.dup2(output_fd, 2)
296 79634555 Iustin Pop
297 79634555 Iustin Pop
298 c1dd99d4 Michael Hanselmann
def StartDaemon(cmd, env=None, cwd="/", output=None, output_fd=None,
299 c1dd99d4 Michael Hanselmann
                pidfile=None):
300 c1dd99d4 Michael Hanselmann
  """Start a daemon process after forking twice.
301 c1dd99d4 Michael Hanselmann

302 c1dd99d4 Michael Hanselmann
  @type cmd: string or list
303 c1dd99d4 Michael Hanselmann
  @param cmd: Command to run
304 c1dd99d4 Michael Hanselmann
  @type env: dict
305 c1dd99d4 Michael Hanselmann
  @param env: Additional environment variables
306 c1dd99d4 Michael Hanselmann
  @type cwd: string
307 c1dd99d4 Michael Hanselmann
  @param cwd: Working directory for the program
308 c1dd99d4 Michael Hanselmann
  @type output: string
309 c1dd99d4 Michael Hanselmann
  @param output: Path to file in which to save the output
310 c1dd99d4 Michael Hanselmann
  @type output_fd: int
311 c1dd99d4 Michael Hanselmann
  @param output_fd: File descriptor for output
312 c1dd99d4 Michael Hanselmann
  @type pidfile: string
313 c1dd99d4 Michael Hanselmann
  @param pidfile: Process ID file
314 c1dd99d4 Michael Hanselmann
  @rtype: int
315 c1dd99d4 Michael Hanselmann
  @return: Daemon process ID
316 c1dd99d4 Michael Hanselmann
  @raise errors.ProgrammerError: if we call this when forks are disabled
317 c1dd99d4 Michael Hanselmann

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

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

460 ed3920e3 Iustin Pop
  @type fd: None or int (file descriptor)
461 ed3920e3 Iustin Pop
  @param fd: if not None, the error will be written to this fd
462 ed3920e3 Iustin Pop
  @param err: string, the error message
463 ed3920e3 Iustin Pop

464 ed3920e3 Iustin Pop
  """
465 ed3920e3 Iustin Pop
  if fd is None:
466 ed3920e3 Iustin Pop
    return
467 ed3920e3 Iustin Pop
468 ed3920e3 Iustin Pop
  if not err:
469 ed3920e3 Iustin Pop
    err = "<unknown error>"
470 ed3920e3 Iustin Pop
471 ed3920e3 Iustin Pop
  RetryOnSignal(os.write, fd, err)
472 ed3920e3 Iustin Pop
473 ed3920e3 Iustin Pop
474 c74cda62 René Nussbaumer
def _CheckIfAlive(child):
475 c74cda62 René Nussbaumer
  """Raises L{RetryAgain} if child is still alive.
476 c74cda62 René Nussbaumer

477 c74cda62 René Nussbaumer
  @raises RetryAgain: If child is still alive
478 c74cda62 René Nussbaumer

479 c74cda62 René Nussbaumer
  """
480 c74cda62 René Nussbaumer
  if child.poll() is None:
481 c74cda62 René Nussbaumer
    raise RetryAgain()
482 c74cda62 René Nussbaumer
483 c74cda62 René Nussbaumer
484 c74cda62 René Nussbaumer
def _WaitForProcess(child, timeout):
485 c74cda62 René Nussbaumer
  """Waits for the child to terminate or until we reach timeout.
486 c74cda62 René Nussbaumer

487 c74cda62 René Nussbaumer
  """
488 c74cda62 René Nussbaumer
  try:
489 c74cda62 René Nussbaumer
    Retry(_CheckIfAlive, (1.0, 1.2, 5.0), max(0, timeout), args=[child])
490 c74cda62 René Nussbaumer
  except RetryTimeout:
491 c74cda62 René Nussbaumer
    pass
492 c74cda62 René Nussbaumer
493 c74cda62 René Nussbaumer
494 c74cda62 René Nussbaumer
def _RunCmdPipe(cmd, env, via_shell, cwd, interactive, timeout,
495 c74cda62 René Nussbaumer
                _linger_timeout=constants.CHILD_LINGER_TIMEOUT):
496 36117c2b Iustin Pop
  """Run a command and return its output.
497 36117c2b Iustin Pop

498 36117c2b Iustin Pop
  @type  cmd: string or list
499 36117c2b Iustin Pop
  @param cmd: Command to run
500 36117c2b Iustin Pop
  @type env: dict
501 36117c2b Iustin Pop
  @param env: The environment to use
502 36117c2b Iustin Pop
  @type via_shell: bool
503 36117c2b Iustin Pop
  @param via_shell: if we should run via the shell
504 8797df43 Iustin Pop
  @type cwd: string
505 8797df43 Iustin Pop
  @param cwd: the working directory for the program
506 0963d545 René Nussbaumer
  @type interactive: boolean
507 0963d545 René Nussbaumer
  @param interactive: Run command interactive (without piping)
508 c74cda62 René Nussbaumer
  @type timeout: int
509 c74cda62 René Nussbaumer
  @param timeout: Timeout after the programm gets terminated
510 36117c2b Iustin Pop
  @rtype: tuple
511 36117c2b Iustin Pop
  @return: (out, err, status)
512 36117c2b Iustin Pop

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

624 36117c2b Iustin Pop
  @type  cmd: string or list
625 36117c2b Iustin Pop
  @param cmd: Command to run
626 36117c2b Iustin Pop
  @type env: dict
627 36117c2b Iustin Pop
  @param env: The environment to use
628 36117c2b Iustin Pop
  @type via_shell: bool
629 36117c2b Iustin Pop
  @param via_shell: if we should run via the shell
630 36117c2b Iustin Pop
  @type output: str
631 36117c2b Iustin Pop
  @param output: the filename in which to save the output
632 8797df43 Iustin Pop
  @type cwd: string
633 8797df43 Iustin Pop
  @param cwd: the working directory for the program
634 36117c2b Iustin Pop
  @rtype: int
635 36117c2b Iustin Pop
  @return: the exit status
636 36117c2b Iustin Pop

637 36117c2b Iustin Pop
  """
638 36117c2b Iustin Pop
  fh = open(output, "a")
639 36117c2b Iustin Pop
  try:
640 36117c2b Iustin Pop
    child = subprocess.Popen(cmd, shell=via_shell,
641 36117c2b Iustin Pop
                             stderr=subprocess.STDOUT,
642 36117c2b Iustin Pop
                             stdout=fh,
643 36117c2b Iustin Pop
                             stdin=subprocess.PIPE,
644 8797df43 Iustin Pop
                             close_fds=True, env=env,
645 8797df43 Iustin Pop
                             cwd=cwd)
646 36117c2b Iustin Pop
647 36117c2b Iustin Pop
    child.stdin.close()
648 36117c2b Iustin Pop
    status = child.wait()
649 36117c2b Iustin Pop
  finally:
650 36117c2b Iustin Pop
    fh.close()
651 36117c2b Iustin Pop
  return status
652 a8083063 Iustin Pop
653 a8083063 Iustin Pop
654 6bb65e3a Guido Trotter
def RunParts(dir_name, env=None, reset_env=False):
655 6bb65e3a Guido Trotter
  """Run Scripts or programs in a directory
656 6bb65e3a Guido Trotter

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

666 6bb65e3a Guido Trotter
  """
667 6bb65e3a Guido Trotter
  rr = []
668 6bb65e3a Guido Trotter
669 6bb65e3a Guido Trotter
  try:
670 6bb65e3a Guido Trotter
    dir_contents = ListVisibleFiles(dir_name)
671 6bb65e3a Guido Trotter
  except OSError, err:
672 6bb65e3a Guido Trotter
    logging.warning("RunParts: skipping %s (cannot list: %s)", dir_name, err)
673 6bb65e3a Guido Trotter
    return rr
674 6bb65e3a Guido Trotter
675 6bb65e3a Guido Trotter
  for relname in sorted(dir_contents):
676 c4feafe8 Iustin Pop
    fname = PathJoin(dir_name, relname)
677 6bb65e3a Guido Trotter
    if not (os.path.isfile(fname) and os.access(fname, os.X_OK) and
678 6bb65e3a Guido Trotter
            constants.EXT_PLUGIN_MASK.match(relname) is not None):
679 6bb65e3a Guido Trotter
      rr.append((relname, constants.RUNPARTS_SKIP, None))
680 6bb65e3a Guido Trotter
    else:
681 6bb65e3a Guido Trotter
      try:
682 6bb65e3a Guido Trotter
        result = RunCmd([fname], env=env, reset_env=reset_env)
683 6bb65e3a Guido Trotter
      except Exception, err: # pylint: disable-msg=W0703
684 6bb65e3a Guido Trotter
        rr.append((relname, constants.RUNPARTS_ERR, str(err)))
685 6bb65e3a Guido Trotter
      else:
686 6bb65e3a Guido Trotter
        rr.append((relname, constants.RUNPARTS_RUN, result))
687 6bb65e3a Guido Trotter
688 6bb65e3a Guido Trotter
  return rr
689 6bb65e3a Guido Trotter
690 6bb65e3a Guido Trotter
691 055f822b Michael Hanselmann
def ResetTempfileModule():
692 055f822b Michael Hanselmann
  """Resets the random name generator of the tempfile module.
693 055f822b Michael Hanselmann

694 055f822b Michael Hanselmann
  This function should be called after C{os.fork} in the child process to
695 055f822b Michael Hanselmann
  ensure it creates a newly seeded random generator. Otherwise it would
696 055f822b Michael Hanselmann
  generate the same random parts as the parent process. If several processes
697 055f822b Michael Hanselmann
  race for the creation of a temporary file, this could lead to one not getting
698 055f822b Michael Hanselmann
  a temporary name.
699 055f822b Michael Hanselmann

700 055f822b Michael Hanselmann
  """
701 055f822b Michael Hanselmann
  # pylint: disable-msg=W0212
702 055f822b Michael Hanselmann
  if hasattr(tempfile, "_once_lock") and hasattr(tempfile, "_name_sequence"):
703 055f822b Michael Hanselmann
    tempfile._once_lock.acquire()
704 055f822b Michael Hanselmann
    try:
705 055f822b Michael Hanselmann
      # Reset random name generator
706 055f822b Michael Hanselmann
      tempfile._name_sequence = None
707 055f822b Michael Hanselmann
    finally:
708 055f822b Michael Hanselmann
      tempfile._once_lock.release()
709 055f822b Michael Hanselmann
  else:
710 055f822b Michael Hanselmann
    logging.critical("The tempfile module misses at least one of the"
711 055f822b Michael Hanselmann
                     " '_once_lock' and '_name_sequence' attributes")
712 055f822b Michael Hanselmann
713 055f822b Michael Hanselmann
714 a5728081 Guido Trotter
def ForceDictType(target, key_types, allowed_values=None):
715 a5728081 Guido Trotter
  """Force the values of a dict to have certain types.
716 a5728081 Guido Trotter

717 a5728081 Guido Trotter
  @type target: dict
718 a5728081 Guido Trotter
  @param target: the dict to update
719 a5728081 Guido Trotter
  @type key_types: dict
720 a5728081 Guido Trotter
  @param key_types: dict mapping target dict keys to types
721 a5728081 Guido Trotter
                    in constants.ENFORCEABLE_TYPES
722 a5728081 Guido Trotter
  @type allowed_values: list
723 a5728081 Guido Trotter
  @keyword allowed_values: list of specially allowed values
724 a5728081 Guido Trotter

725 a5728081 Guido Trotter
  """
726 a5728081 Guido Trotter
  if allowed_values is None:
727 a5728081 Guido Trotter
    allowed_values = []
728 a5728081 Guido Trotter
729 8b46606c Guido Trotter
  if not isinstance(target, dict):
730 8b46606c Guido Trotter
    msg = "Expected dictionary, got '%s'" % target
731 8b46606c Guido Trotter
    raise errors.TypeEnforcementError(msg)
732 8b46606c Guido Trotter
733 a5728081 Guido Trotter
  for key in target:
734 a5728081 Guido Trotter
    if key not in key_types:
735 a5728081 Guido Trotter
      msg = "Unknown key '%s'" % key
736 a5728081 Guido Trotter
      raise errors.TypeEnforcementError(msg)
737 a5728081 Guido Trotter
738 a5728081 Guido Trotter
    if target[key] in allowed_values:
739 a5728081 Guido Trotter
      continue
740 a5728081 Guido Trotter
741 29921401 Iustin Pop
    ktype = key_types[key]
742 29921401 Iustin Pop
    if ktype not in constants.ENFORCEABLE_TYPES:
743 29921401 Iustin Pop
      msg = "'%s' has non-enforceable type %s" % (key, ktype)
744 a5728081 Guido Trotter
      raise errors.ProgrammerError(msg)
745 a5728081 Guido Trotter
746 59525e1f Michael Hanselmann
    if ktype in (constants.VTYPE_STRING, constants.VTYPE_MAYBE_STRING):
747 59525e1f Michael Hanselmann
      if target[key] is None and ktype == constants.VTYPE_MAYBE_STRING:
748 59525e1f Michael Hanselmann
        pass
749 59525e1f Michael Hanselmann
      elif not isinstance(target[key], basestring):
750 a5728081 Guido Trotter
        if isinstance(target[key], bool) and not target[key]:
751 a5728081 Guido Trotter
          target[key] = ''
752 a5728081 Guido Trotter
        else:
753 a5728081 Guido Trotter
          msg = "'%s' (value %s) is not a valid string" % (key, target[key])
754 a5728081 Guido Trotter
          raise errors.TypeEnforcementError(msg)
755 29921401 Iustin Pop
    elif ktype == constants.VTYPE_BOOL:
756 a5728081 Guido Trotter
      if isinstance(target[key], basestring) and target[key]:
757 a5728081 Guido Trotter
        if target[key].lower() == constants.VALUE_FALSE:
758 a5728081 Guido Trotter
          target[key] = False
759 a5728081 Guido Trotter
        elif target[key].lower() == constants.VALUE_TRUE:
760 a5728081 Guido Trotter
          target[key] = True
761 a5728081 Guido Trotter
        else:
762 a5728081 Guido Trotter
          msg = "'%s' (value %s) is not a valid boolean" % (key, target[key])
763 a5728081 Guido Trotter
          raise errors.TypeEnforcementError(msg)
764 a5728081 Guido Trotter
      elif target[key]:
765 a5728081 Guido Trotter
        target[key] = True
766 a5728081 Guido Trotter
      else:
767 a5728081 Guido Trotter
        target[key] = False
768 29921401 Iustin Pop
    elif ktype == constants.VTYPE_SIZE:
769 a5728081 Guido Trotter
      try:
770 a5728081 Guido Trotter
        target[key] = ParseUnit(target[key])
771 a5728081 Guido Trotter
      except errors.UnitParseError, err:
772 a5728081 Guido Trotter
        msg = "'%s' (value %s) is not a valid size. error: %s" % \
773 a5728081 Guido Trotter
              (key, target[key], err)
774 a5728081 Guido Trotter
        raise errors.TypeEnforcementError(msg)
775 29921401 Iustin Pop
    elif ktype == constants.VTYPE_INT:
776 a5728081 Guido Trotter
      try:
777 a5728081 Guido Trotter
        target[key] = int(target[key])
778 a5728081 Guido Trotter
      except (ValueError, TypeError):
779 a5728081 Guido Trotter
        msg = "'%s' (value %s) is not a valid integer" % (key, target[key])
780 a5728081 Guido Trotter
        raise errors.TypeEnforcementError(msg)
781 a5728081 Guido Trotter
782 a5728081 Guido Trotter
783 a01b500b Michael Hanselmann
def _GetProcStatusPath(pid):
784 a01b500b Michael Hanselmann
  """Returns the path for a PID's proc status file.
785 a01b500b Michael Hanselmann

786 a01b500b Michael Hanselmann
  @type pid: int
787 a01b500b Michael Hanselmann
  @param pid: Process ID
788 a01b500b Michael Hanselmann
  @rtype: string
789 a01b500b Michael Hanselmann

790 a01b500b Michael Hanselmann
  """
791 a01b500b Michael Hanselmann
  return "/proc/%d/status" % pid
792 a01b500b Michael Hanselmann
793 a01b500b Michael Hanselmann
794 a8083063 Iustin Pop
def IsProcessAlive(pid):
795 a8083063 Iustin Pop
  """Check if a given pid exists on the system.
796 a8083063 Iustin Pop

797 44bf25ff Iustin Pop
  @note: zombie status is not handled, so zombie processes
798 44bf25ff Iustin Pop
      will be returned as alive
799 58885d79 Iustin Pop
  @type pid: int
800 58885d79 Iustin Pop
  @param pid: the process ID to check
801 58885d79 Iustin Pop
  @rtype: boolean
802 58885d79 Iustin Pop
  @return: True if the process exists
803 a8083063 Iustin Pop

804 a8083063 Iustin Pop
  """
805 5ef5ea45 Guido Trotter
  def _TryStat(name):
806 5ef5ea45 Guido Trotter
    try:
807 5ef5ea45 Guido Trotter
      os.stat(name)
808 5ef5ea45 Guido Trotter
      return True
809 5ef5ea45 Guido Trotter
    except EnvironmentError, err:
810 5ef5ea45 Guido Trotter
      if err.errno in (errno.ENOENT, errno.ENOTDIR):
811 5ef5ea45 Guido Trotter
        return False
812 5ef5ea45 Guido Trotter
      elif err.errno == errno.EINVAL:
813 5ef5ea45 Guido Trotter
        raise RetryAgain(err)
814 5ef5ea45 Guido Trotter
      raise
815 5ef5ea45 Guido Trotter
816 5ef5ea45 Guido Trotter
  assert isinstance(pid, int), "pid must be an integer"
817 d9f311d7 Iustin Pop
  if pid <= 0:
818 d9f311d7 Iustin Pop
    return False
819 d9f311d7 Iustin Pop
820 5ef5ea45 Guido Trotter
  # /proc in a multiprocessor environment can have strange behaviors.
821 5ef5ea45 Guido Trotter
  # Retry the os.stat a few times until we get a good result.
822 a8083063 Iustin Pop
  try:
823 a01b500b Michael Hanselmann
    return Retry(_TryStat, (0.01, 1.5, 0.1), 0.5,
824 a01b500b Michael Hanselmann
                 args=[_GetProcStatusPath(pid)])
825 5ef5ea45 Guido Trotter
  except RetryTimeout, err:
826 5ef5ea45 Guido Trotter
    err.RaiseInner()
827 a8083063 Iustin Pop
828 a8083063 Iustin Pop
829 a01b500b Michael Hanselmann
def _ParseSigsetT(sigset):
830 a01b500b Michael Hanselmann
  """Parse a rendered sigset_t value.
831 a01b500b Michael Hanselmann

832 a01b500b Michael Hanselmann
  This is the opposite of the Linux kernel's fs/proc/array.c:render_sigset_t
833 a01b500b Michael Hanselmann
  function.
834 a01b500b Michael Hanselmann

835 a01b500b Michael Hanselmann
  @type sigset: string
836 a01b500b Michael Hanselmann
  @param sigset: Rendered signal set from /proc/$pid/status
837 a01b500b Michael Hanselmann
  @rtype: set
838 a01b500b Michael Hanselmann
  @return: Set of all enabled signal numbers
839 a01b500b Michael Hanselmann

840 a01b500b Michael Hanselmann
  """
841 a01b500b Michael Hanselmann
  result = set()
842 a01b500b Michael Hanselmann
843 a01b500b Michael Hanselmann
  signum = 0
844 a01b500b Michael Hanselmann
  for ch in reversed(sigset):
845 a01b500b Michael Hanselmann
    chv = int(ch, 16)
846 a01b500b Michael Hanselmann
847 a01b500b Michael Hanselmann
    # The following could be done in a loop, but it's easier to read and
848 a01b500b Michael Hanselmann
    # understand in the unrolled form
849 a01b500b Michael Hanselmann
    if chv & 1:
850 a01b500b Michael Hanselmann
      result.add(signum + 1)
851 a01b500b Michael Hanselmann
    if chv & 2:
852 a01b500b Michael Hanselmann
      result.add(signum + 2)
853 a01b500b Michael Hanselmann
    if chv & 4:
854 a01b500b Michael Hanselmann
      result.add(signum + 3)
855 a01b500b Michael Hanselmann
    if chv & 8:
856 a01b500b Michael Hanselmann
      result.add(signum + 4)
857 a01b500b Michael Hanselmann
858 a01b500b Michael Hanselmann
    signum += 4
859 a01b500b Michael Hanselmann
860 a01b500b Michael Hanselmann
  return result
861 a01b500b Michael Hanselmann
862 a01b500b Michael Hanselmann
863 a01b500b Michael Hanselmann
def _GetProcStatusField(pstatus, field):
864 a01b500b Michael Hanselmann
  """Retrieves a field from the contents of a proc status file.
865 a01b500b Michael Hanselmann

866 a01b500b Michael Hanselmann
  @type pstatus: string
867 a01b500b Michael Hanselmann
  @param pstatus: Contents of /proc/$pid/status
868 a01b500b Michael Hanselmann
  @type field: string
869 a01b500b Michael Hanselmann
  @param field: Name of field whose value should be returned
870 a01b500b Michael Hanselmann
  @rtype: string
871 a01b500b Michael Hanselmann

872 a01b500b Michael Hanselmann
  """
873 a01b500b Michael Hanselmann
  for line in pstatus.splitlines():
874 a01b500b Michael Hanselmann
    parts = line.split(":", 1)
875 a01b500b Michael Hanselmann
876 a01b500b Michael Hanselmann
    if len(parts) < 2 or parts[0] != field:
877 a01b500b Michael Hanselmann
      continue
878 a01b500b Michael Hanselmann
879 a01b500b Michael Hanselmann
    return parts[1].strip()
880 a01b500b Michael Hanselmann
881 a01b500b Michael Hanselmann
  return None
882 a01b500b Michael Hanselmann
883 a01b500b Michael Hanselmann
884 a01b500b Michael Hanselmann
def IsProcessHandlingSignal(pid, signum, status_path=None):
885 a01b500b Michael Hanselmann
  """Checks whether a process is handling a signal.
886 a01b500b Michael Hanselmann

887 a01b500b Michael Hanselmann
  @type pid: int
888 a01b500b Michael Hanselmann
  @param pid: Process ID
889 a01b500b Michael Hanselmann
  @type signum: int
890 a01b500b Michael Hanselmann
  @param signum: Signal number
891 a01b500b Michael Hanselmann
  @rtype: bool
892 a01b500b Michael Hanselmann

893 a01b500b Michael Hanselmann
  """
894 a01b500b Michael Hanselmann
  if status_path is None:
895 a01b500b Michael Hanselmann
    status_path = _GetProcStatusPath(pid)
896 a01b500b Michael Hanselmann
897 a01b500b Michael Hanselmann
  try:
898 a01b500b Michael Hanselmann
    proc_status = ReadFile(status_path)
899 a01b500b Michael Hanselmann
  except EnvironmentError, err:
900 a01b500b Michael Hanselmann
    # In at least one case, reading /proc/$pid/status failed with ESRCH.
901 a01b500b Michael Hanselmann
    if err.errno in (errno.ENOENT, errno.ENOTDIR, errno.EINVAL, errno.ESRCH):
902 a01b500b Michael Hanselmann
      return False
903 a01b500b Michael Hanselmann
    raise
904 a01b500b Michael Hanselmann
905 a01b500b Michael Hanselmann
  sigcgt = _GetProcStatusField(proc_status, "SigCgt")
906 a01b500b Michael Hanselmann
  if sigcgt is None:
907 a01b500b Michael Hanselmann
    raise RuntimeError("%s is missing 'SigCgt' field" % status_path)
908 a01b500b Michael Hanselmann
909 a01b500b Michael Hanselmann
  # Now check whether signal is handled
910 a01b500b Michael Hanselmann
  return signum in _ParseSigsetT(sigcgt)
911 a01b500b Michael Hanselmann
912 a01b500b Michael Hanselmann
913 28f34048 Michael Hanselmann
def ValidateServiceName(name):
914 28f34048 Michael Hanselmann
  """Validate the given service name.
915 28f34048 Michael Hanselmann

916 28f34048 Michael Hanselmann
  @type name: number or string
917 28f34048 Michael Hanselmann
  @param name: Service name or port specification
918 28f34048 Michael Hanselmann

919 28f34048 Michael Hanselmann
  """
920 28f34048 Michael Hanselmann
  try:
921 28f34048 Michael Hanselmann
    numport = int(name)
922 28f34048 Michael Hanselmann
  except (ValueError, TypeError):
923 28f34048 Michael Hanselmann
    # Non-numeric service name
924 28f34048 Michael Hanselmann
    valid = _VALID_SERVICE_NAME_RE.match(name)
925 28f34048 Michael Hanselmann
  else:
926 28f34048 Michael Hanselmann
    # Numeric port (protocols other than TCP or UDP might need adjustments
927 28f34048 Michael Hanselmann
    # here)
928 28f34048 Michael Hanselmann
    valid = (numport >= 0 and numport < (1 << 16))
929 28f34048 Michael Hanselmann
930 28f34048 Michael Hanselmann
  if not valid:
931 28f34048 Michael Hanselmann
    raise errors.OpPrereqError("Invalid service name '%s'" % name,
932 28f34048 Michael Hanselmann
                               errors.ECODE_INVAL)
933 28f34048 Michael Hanselmann
934 28f34048 Michael Hanselmann
  return name
935 28f34048 Michael Hanselmann
936 28f34048 Michael Hanselmann
937 a8083063 Iustin Pop
def ListVolumeGroups():
938 a8083063 Iustin Pop
  """List volume groups and their size
939 a8083063 Iustin Pop

940 58885d79 Iustin Pop
  @rtype: dict
941 58885d79 Iustin Pop
  @return:
942 58885d79 Iustin Pop
       Dictionary with keys volume name and values
943 58885d79 Iustin Pop
       the size of the volume
944 a8083063 Iustin Pop

945 a8083063 Iustin Pop
  """
946 a8083063 Iustin Pop
  command = "vgs --noheadings --units m --nosuffix -o name,size"
947 a8083063 Iustin Pop
  result = RunCmd(command)
948 a8083063 Iustin Pop
  retval = {}
949 a8083063 Iustin Pop
  if result.failed:
950 a8083063 Iustin Pop
    return retval
951 a8083063 Iustin Pop
952 a8083063 Iustin Pop
  for line in result.stdout.splitlines():
953 a8083063 Iustin Pop
    try:
954 a8083063 Iustin Pop
      name, size = line.split()
955 a8083063 Iustin Pop
      size = int(float(size))
956 a8083063 Iustin Pop
    except (IndexError, ValueError), err:
957 bb698c1f Iustin Pop
      logging.error("Invalid output from vgs (%s): %s", err, line)
958 a8083063 Iustin Pop
      continue
959 a8083063 Iustin Pop
960 a8083063 Iustin Pop
    retval[name] = size
961 a8083063 Iustin Pop
962 a8083063 Iustin Pop
  return retval
963 a8083063 Iustin Pop
964 a8083063 Iustin Pop
965 a8083063 Iustin Pop
def BridgeExists(bridge):
966 a8083063 Iustin Pop
  """Check whether the given bridge exists in the system
967 a8083063 Iustin Pop

968 58885d79 Iustin Pop
  @type bridge: str
969 58885d79 Iustin Pop
  @param bridge: the bridge name to check
970 58885d79 Iustin Pop
  @rtype: boolean
971 58885d79 Iustin Pop
  @return: True if it does
972 a8083063 Iustin Pop

973 a8083063 Iustin Pop
  """
974 a8083063 Iustin Pop
  return os.path.isdir("/sys/class/net/%s/bridge" % bridge)
975 a8083063 Iustin Pop
976 a8083063 Iustin Pop
977 a8083063 Iustin Pop
def TryConvert(fn, val):
978 a8083063 Iustin Pop
  """Try to convert a value ignoring errors.
979 a8083063 Iustin Pop

980 58885d79 Iustin Pop
  This function tries to apply function I{fn} to I{val}. If no
981 58885d79 Iustin Pop
  C{ValueError} or C{TypeError} exceptions are raised, it will return
982 58885d79 Iustin Pop
  the result, else it will return the original value. Any other
983 58885d79 Iustin Pop
  exceptions are propagated to the caller.
984 58885d79 Iustin Pop

985 58885d79 Iustin Pop
  @type fn: callable
986 58885d79 Iustin Pop
  @param fn: function to apply to the value
987 58885d79 Iustin Pop
  @param val: the value to be converted
988 58885d79 Iustin Pop
  @return: The converted value if the conversion was successful,
989 58885d79 Iustin Pop
      otherwise the original value.
990 a8083063 Iustin Pop

991 a8083063 Iustin Pop
  """
992 a8083063 Iustin Pop
  try:
993 a8083063 Iustin Pop
    nv = fn(val)
994 7c4d6c7b Michael Hanselmann
  except (ValueError, TypeError):
995 a8083063 Iustin Pop
    nv = val
996 a8083063 Iustin Pop
  return nv
997 a8083063 Iustin Pop
998 a8083063 Iustin Pop
999 a8083063 Iustin Pop
def IsValidShellParam(word):
1000 a8083063 Iustin Pop
  """Verifies is the given word is safe from the shell's p.o.v.
1001 a8083063 Iustin Pop

1002 a8083063 Iustin Pop
  This means that we can pass this to a command via the shell and be
1003 a8083063 Iustin Pop
  sure that it doesn't alter the command line and is passed as such to
1004 a8083063 Iustin Pop
  the actual command.
1005 a8083063 Iustin Pop

1006 a8083063 Iustin Pop
  Note that we are overly restrictive here, in order to be on the safe
1007 a8083063 Iustin Pop
  side.
1008 a8083063 Iustin Pop

1009 58885d79 Iustin Pop
  @type word: str
1010 58885d79 Iustin Pop
  @param word: the word to check
1011 58885d79 Iustin Pop
  @rtype: boolean
1012 58885d79 Iustin Pop
  @return: True if the word is 'safe'
1013 58885d79 Iustin Pop

1014 a8083063 Iustin Pop
  """
1015 0b5303da Iustin Pop
  return bool(_SHELLPARAM_REGEX.match(word))
1016 a8083063 Iustin Pop
1017 a8083063 Iustin Pop
1018 a8083063 Iustin Pop
def BuildShellCmd(template, *args):
1019 a8083063 Iustin Pop
  """Build a safe shell command line from the given arguments.
1020 a8083063 Iustin Pop

1021 a8083063 Iustin Pop
  This function will check all arguments in the args list so that they
1022 a8083063 Iustin Pop
  are valid shell parameters (i.e. they don't contain shell
1023 5bbd3f7f Michael Hanselmann
  metacharacters). If everything is ok, it will return the result of
1024 a8083063 Iustin Pop
  template % args.
1025 a8083063 Iustin Pop

1026 58885d79 Iustin Pop
  @type template: str
1027 58885d79 Iustin Pop
  @param template: the string holding the template for the
1028 58885d79 Iustin Pop
      string formatting
1029 58885d79 Iustin Pop
  @rtype: str
1030 58885d79 Iustin Pop
  @return: the expanded command line
1031 58885d79 Iustin Pop

1032 a8083063 Iustin Pop
  """
1033 a8083063 Iustin Pop
  for word in args:
1034 a8083063 Iustin Pop
    if not IsValidShellParam(word):
1035 3ecf6786 Iustin Pop
      raise errors.ProgrammerError("Shell argument '%s' contains"
1036 3ecf6786 Iustin Pop
                                   " invalid characters" % word)
1037 a8083063 Iustin Pop
  return template % args
1038 a8083063 Iustin Pop
1039 a8083063 Iustin Pop
1040 31155d60 Balazs Lecz
def ParseCpuMask(cpu_mask):
1041 31155d60 Balazs Lecz
  """Parse a CPU mask definition and return the list of CPU IDs.
1042 31155d60 Balazs Lecz

1043 31155d60 Balazs Lecz
  CPU mask format: comma-separated list of CPU IDs
1044 31155d60 Balazs Lecz
  or dash-separated ID ranges
1045 31155d60 Balazs Lecz
  Example: "0-2,5" -> "0,1,2,5"
1046 31155d60 Balazs Lecz

1047 31155d60 Balazs Lecz
  @type cpu_mask: str
1048 31155d60 Balazs Lecz
  @param cpu_mask: CPU mask definition
1049 31155d60 Balazs Lecz
  @rtype: list of int
1050 31155d60 Balazs Lecz
  @return: list of CPU IDs
1051 31155d60 Balazs Lecz

1052 31155d60 Balazs Lecz
  """
1053 31155d60 Balazs Lecz
  if not cpu_mask:
1054 31155d60 Balazs Lecz
    return []
1055 31155d60 Balazs Lecz
  cpu_list = []
1056 31155d60 Balazs Lecz
  for range_def in cpu_mask.split(","):
1057 31155d60 Balazs Lecz
    boundaries = range_def.split("-")
1058 31155d60 Balazs Lecz
    n_elements = len(boundaries)
1059 31155d60 Balazs Lecz
    if n_elements > 2:
1060 31155d60 Balazs Lecz
      raise errors.ParseError("Invalid CPU ID range definition"
1061 31155d60 Balazs Lecz
                              " (only one hyphen allowed): %s" % range_def)
1062 31155d60 Balazs Lecz
    try:
1063 31155d60 Balazs Lecz
      lower = int(boundaries[0])
1064 31155d60 Balazs Lecz
    except (ValueError, TypeError), err:
1065 31155d60 Balazs Lecz
      raise errors.ParseError("Invalid CPU ID value for lower boundary of"
1066 31155d60 Balazs Lecz
                              " CPU ID range: %s" % str(err))
1067 31155d60 Balazs Lecz
    try:
1068 31155d60 Balazs Lecz
      higher = int(boundaries[-1])
1069 31155d60 Balazs Lecz
    except (ValueError, TypeError), err:
1070 31155d60 Balazs Lecz
      raise errors.ParseError("Invalid CPU ID value for higher boundary of"
1071 31155d60 Balazs Lecz
                              " CPU ID range: %s" % str(err))
1072 31155d60 Balazs Lecz
    if lower > higher:
1073 31155d60 Balazs Lecz
      raise errors.ParseError("Invalid CPU ID range definition"
1074 31155d60 Balazs Lecz
                              " (%d > %d): %s" % (lower, higher, range_def))
1075 31155d60 Balazs Lecz
    cpu_list.extend(range(lower, higher + 1))
1076 31155d60 Balazs Lecz
  return cpu_list
1077 31155d60 Balazs Lecz
1078 31155d60 Balazs Lecz
1079 9440aeab Michael Hanselmann
def SetEtcHostsEntry(file_name, ip, hostname, aliases):
1080 9440aeab Michael Hanselmann
  """Sets the name of an IP address and hostname in /etc/hosts.
1081 899d2a81 Michael Hanselmann

1082 58885d79 Iustin Pop
  @type file_name: str
1083 58885d79 Iustin Pop
  @param file_name: path to the file to modify (usually C{/etc/hosts})
1084 58885d79 Iustin Pop
  @type ip: str
1085 58885d79 Iustin Pop
  @param ip: the IP address
1086 58885d79 Iustin Pop
  @type hostname: str
1087 58885d79 Iustin Pop
  @param hostname: the hostname to be added
1088 58885d79 Iustin Pop
  @type aliases: list
1089 58885d79 Iustin Pop
  @param aliases: the list of aliases to add for the hostname
1090 58885d79 Iustin Pop

1091 899d2a81 Michael Hanselmann
  """
1092 7fbb1f65 Michael Hanselmann
  # Ensure aliases are unique
1093 7fbb1f65 Michael Hanselmann
  aliases = UniqueSequence([hostname] + aliases)[1:]
1094 7fbb1f65 Michael Hanselmann
1095 edcd876b Michael Hanselmann
  def _WriteEtcHosts(fd):
1096 edcd876b Michael Hanselmann
    # Duplicating file descriptor because os.fdopen's result will automatically
1097 edcd876b Michael Hanselmann
    # close the descriptor, but we would still like to have its functionality.
1098 edcd876b Michael Hanselmann
    out = os.fdopen(os.dup(fd), "w")
1099 9440aeab Michael Hanselmann
    try:
1100 edcd876b Michael Hanselmann
      for line in ReadFile(file_name).splitlines(True):
1101 edcd876b Michael Hanselmann
        fields = line.split()
1102 edcd876b Michael Hanselmann
        if fields and not fields[0].startswith("#") and ip == fields[0]:
1103 edcd876b Michael Hanselmann
          continue
1104 edcd876b Michael Hanselmann
        out.write(line)
1105 edcd876b Michael Hanselmann
1106 edcd876b Michael Hanselmann
      out.write("%s\t%s" % (ip, hostname))
1107 edcd876b Michael Hanselmann
      if aliases:
1108 edcd876b Michael Hanselmann
        out.write(" %s" % " ".join(aliases))
1109 edcd876b Michael Hanselmann
      out.write("\n")
1110 edcd876b Michael Hanselmann
      out.flush()
1111 9440aeab Michael Hanselmann
    finally:
1112 9440aeab Michael Hanselmann
      out.close()
1113 edcd876b Michael Hanselmann
1114 edcd876b Michael Hanselmann
  WriteFile(file_name, fn=_WriteEtcHosts, mode=0644)
1115 899d2a81 Michael Hanselmann
1116 899d2a81 Michael Hanselmann
1117 ea8ac9c9 René Nussbaumer
def AddHostToEtcHosts(hostname, ip):
1118 d9c02ca6 Michael Hanselmann
  """Wrapper around SetEtcHostsEntry.
1119 d9c02ca6 Michael Hanselmann

1120 58885d79 Iustin Pop
  @type hostname: str
1121 58885d79 Iustin Pop
  @param hostname: a hostname that will be resolved and added to
1122 58885d79 Iustin Pop
      L{constants.ETC_HOSTS}
1123 ea8ac9c9 René Nussbaumer
  @type ip: str
1124 ea8ac9c9 René Nussbaumer
  @param ip: The ip address of the host
1125 58885d79 Iustin Pop

1126 d9c02ca6 Michael Hanselmann
  """
1127 ea8ac9c9 René Nussbaumer
  SetEtcHostsEntry(constants.ETC_HOSTS, ip, hostname, [hostname.split(".")[0]])
1128 d9c02ca6 Michael Hanselmann
1129 d9c02ca6 Michael Hanselmann
1130 899d2a81 Michael Hanselmann
def RemoveEtcHostsEntry(file_name, hostname):
1131 3e1cdf9f Michael Hanselmann
  """Removes a hostname from /etc/hosts.
1132 899d2a81 Michael Hanselmann

1133 9440aeab Michael Hanselmann
  IP addresses without names are removed from the file.
1134 58885d79 Iustin Pop

1135 58885d79 Iustin Pop
  @type file_name: str
1136 58885d79 Iustin Pop
  @param file_name: path to the file to modify (usually C{/etc/hosts})
1137 58885d79 Iustin Pop
  @type hostname: str
1138 58885d79 Iustin Pop
  @param hostname: the hostname to be removed
1139 58885d79 Iustin Pop

1140 899d2a81 Michael Hanselmann
  """
1141 edcd876b Michael Hanselmann
  def _WriteEtcHosts(fd):
1142 edcd876b Michael Hanselmann
    # Duplicating file descriptor because os.fdopen's result will automatically
1143 edcd876b Michael Hanselmann
    # close the descriptor, but we would still like to have its functionality.
1144 edcd876b Michael Hanselmann
    out = os.fdopen(os.dup(fd), "w")
1145 899d2a81 Michael Hanselmann
    try:
1146 edcd876b Michael Hanselmann
      for line in ReadFile(file_name).splitlines(True):
1147 edcd876b Michael Hanselmann
        fields = line.split()
1148 edcd876b Michael Hanselmann
        if len(fields) > 1 and not fields[0].startswith("#"):
1149 edcd876b Michael Hanselmann
          names = fields[1:]
1150 edcd876b Michael Hanselmann
          if hostname in names:
1151 edcd876b Michael Hanselmann
            while hostname in names:
1152 edcd876b Michael Hanselmann
              names.remove(hostname)
1153 edcd876b Michael Hanselmann
            if names:
1154 edcd876b Michael Hanselmann
              out.write("%s %s\n" % (fields[0], " ".join(names)))
1155 edcd876b Michael Hanselmann
            continue
1156 59f82e3f Michael Hanselmann
1157 edcd876b Michael Hanselmann
        out.write(line)
1158 edcd876b Michael Hanselmann
1159 edcd876b Michael Hanselmann
      out.flush()
1160 a8083063 Iustin Pop
    finally:
1161 59f82e3f Michael Hanselmann
      out.close()
1162 edcd876b Michael Hanselmann
1163 edcd876b Michael Hanselmann
  WriteFile(file_name, fn=_WriteEtcHosts, mode=0644)
1164 a8083063 Iustin Pop
1165 a8083063 Iustin Pop
1166 d9c02ca6 Michael Hanselmann
def RemoveHostFromEtcHosts(hostname):
1167 d9c02ca6 Michael Hanselmann
  """Wrapper around RemoveEtcHostsEntry.
1168 d9c02ca6 Michael Hanselmann

1169 58885d79 Iustin Pop
  @type hostname: str
1170 58885d79 Iustin Pop
  @param hostname: hostname that will be resolved and its
1171 58885d79 Iustin Pop
      full and shot name will be removed from
1172 58885d79 Iustin Pop
      L{constants.ETC_HOSTS}
1173 58885d79 Iustin Pop

1174 d9c02ca6 Michael Hanselmann
  """
1175 b705c7a6 Manuel Franceschini
  RemoveEtcHostsEntry(constants.ETC_HOSTS, hostname)
1176 b705c7a6 Manuel Franceschini
  RemoveEtcHostsEntry(constants.ETC_HOSTS, hostname.split(".")[0])
1177 d9c02ca6 Michael Hanselmann
1178 d9c02ca6 Michael Hanselmann
1179 257f4c0a Iustin Pop
def GetHomeDir(user, default=None):
1180 257f4c0a Iustin Pop
  """Try to get the homedir of the given user.
1181 257f4c0a Iustin Pop

1182 257f4c0a Iustin Pop
  The user can be passed either as a string (denoting the name) or as
1183 257f4c0a Iustin Pop
  an integer (denoting the user id). If the user is not found, the
1184 257f4c0a Iustin Pop
  'default' argument is returned, which defaults to None.
1185 2f8b60b3 Iustin Pop

1186 2f8b60b3 Iustin Pop
  """
1187 2f8b60b3 Iustin Pop
  try:
1188 257f4c0a Iustin Pop
    if isinstance(user, basestring):
1189 257f4c0a Iustin Pop
      result = pwd.getpwnam(user)
1190 257f4c0a Iustin Pop
    elif isinstance(user, (int, long)):
1191 257f4c0a Iustin Pop
      result = pwd.getpwuid(user)
1192 257f4c0a Iustin Pop
    else:
1193 257f4c0a Iustin Pop
      raise errors.ProgrammerError("Invalid type passed to GetHomeDir (%s)" %
1194 257f4c0a Iustin Pop
                                   type(user))
1195 2f8b60b3 Iustin Pop
  except KeyError:
1196 2f8b60b3 Iustin Pop
    return default
1197 2f8b60b3 Iustin Pop
  return result.pw_dir
1198 59072e7e Michael Hanselmann
1199 59072e7e Michael Hanselmann
1200 24818e8f Michael Hanselmann
def NewUUID():
1201 59072e7e Michael Hanselmann
  """Returns a random UUID.
1202 59072e7e Michael Hanselmann

1203 58885d79 Iustin Pop
  @note: This is a Linux-specific method as it uses the /proc
1204 58885d79 Iustin Pop
      filesystem.
1205 58885d79 Iustin Pop
  @rtype: str
1206 58885d79 Iustin Pop

1207 59072e7e Michael Hanselmann
  """
1208 13998ef2 Michael Hanselmann
  return ReadFile(_RANDOM_UUID_FILE, size=128).rstrip("\n")
1209 087b34fe Iustin Pop
1210 087b34fe Iustin Pop
1211 7b4126b7 Iustin Pop
def FirstFree(seq, base=0):
1212 7b4126b7 Iustin Pop
  """Returns the first non-existing integer from seq.
1213 7b4126b7 Iustin Pop

1214 7b4126b7 Iustin Pop
  The seq argument should be a sorted list of positive integers. The
1215 7b4126b7 Iustin Pop
  first time the index of an element is smaller than the element
1216 7b4126b7 Iustin Pop
  value, the index will be returned.
1217 7b4126b7 Iustin Pop

1218 7b4126b7 Iustin Pop
  The base argument is used to start at a different offset,
1219 58885d79 Iustin Pop
  i.e. C{[3, 4, 6]} with I{offset=3} will return 5.
1220 58885d79 Iustin Pop

1221 58885d79 Iustin Pop
  Example: C{[0, 1, 3]} will return I{2}.
1222 7b4126b7 Iustin Pop

1223 58885d79 Iustin Pop
  @type seq: sequence
1224 58885d79 Iustin Pop
  @param seq: the sequence to be analyzed.
1225 58885d79 Iustin Pop
  @type base: int
1226 58885d79 Iustin Pop
  @param base: use this value as the base index of the sequence
1227 58885d79 Iustin Pop
  @rtype: int
1228 58885d79 Iustin Pop
  @return: the first non-used index in the sequence
1229 7b4126b7 Iustin Pop

1230 7b4126b7 Iustin Pop
  """
1231 7b4126b7 Iustin Pop
  for idx, elem in enumerate(seq):
1232 7b4126b7 Iustin Pop
    assert elem >= base, "Passed element is higher than base offset"
1233 7b4126b7 Iustin Pop
    if elem > idx + base:
1234 7b4126b7 Iustin Pop
      # idx is not used
1235 7b4126b7 Iustin Pop
      return idx + base
1236 7b4126b7 Iustin Pop
  return None
1237 7b4126b7 Iustin Pop
1238 7b4126b7 Iustin Pop
1239 dfdc4060 Guido Trotter
def SingleWaitForFdCondition(fdobj, event, timeout):
1240 dcd511c8 Guido Trotter
  """Waits for a condition to occur on the socket.
1241 dcd511c8 Guido Trotter

1242 dfdc4060 Guido Trotter
  Immediately returns at the first interruption.
1243 dfdc4060 Guido Trotter

1244 dfdc4060 Guido Trotter
  @type fdobj: integer or object supporting a fileno() method
1245 dfdc4060 Guido Trotter
  @param fdobj: entity to wait for events on
1246 dfdc4060 Guido Trotter
  @type event: integer
1247 dcd511c8 Guido Trotter
  @param event: ORed condition (see select module)
1248 dcd511c8 Guido Trotter
  @type timeout: float or None
1249 dcd511c8 Guido Trotter
  @param timeout: Timeout in seconds
1250 dcd511c8 Guido Trotter
  @rtype: int or None
1251 dcd511c8 Guido Trotter
  @return: None for timeout, otherwise occured conditions
1252 dcd511c8 Guido Trotter

1253 dcd511c8 Guido Trotter
  """
1254 dcd511c8 Guido Trotter
  check = (event | select.POLLPRI |
1255 dcd511c8 Guido Trotter
           select.POLLNVAL | select.POLLHUP | select.POLLERR)
1256 dcd511c8 Guido Trotter
1257 dcd511c8 Guido Trotter
  if timeout is not None:
1258 dcd511c8 Guido Trotter
    # Poller object expects milliseconds
1259 dcd511c8 Guido Trotter
    timeout *= 1000
1260 dcd511c8 Guido Trotter
1261 dcd511c8 Guido Trotter
  poller = select.poll()
1262 dfdc4060 Guido Trotter
  poller.register(fdobj, event)
1263 dcd511c8 Guido Trotter
  try:
1264 dfdc4060 Guido Trotter
    # TODO: If the main thread receives a signal and we have no timeout, we
1265 dfdc4060 Guido Trotter
    # could wait forever. This should check a global "quit" flag or something
1266 dfdc4060 Guido Trotter
    # every so often.
1267 dfdc4060 Guido Trotter
    io_events = poller.poll(timeout)
1268 dfdc4060 Guido Trotter
  except select.error, err:
1269 dfdc4060 Guido Trotter
    if err[0] != errno.EINTR:
1270 dfdc4060 Guido Trotter
      raise
1271 dfdc4060 Guido Trotter
    io_events = []
1272 dfdc4060 Guido Trotter
  if io_events and io_events[0][1] & check:
1273 dfdc4060 Guido Trotter
    return io_events[0][1]
1274 dfdc4060 Guido Trotter
  else:
1275 dfdc4060 Guido Trotter
    return None
1276 dfdc4060 Guido Trotter
1277 dfdc4060 Guido Trotter
1278 dfdc4060 Guido Trotter
class FdConditionWaiterHelper(object):
1279 dfdc4060 Guido Trotter
  """Retry helper for WaitForFdCondition.
1280 dfdc4060 Guido Trotter

1281 dfdc4060 Guido Trotter
  This class contains the retried and wait functions that make sure
1282 dfdc4060 Guido Trotter
  WaitForFdCondition can continue waiting until the timeout is actually
1283 dfdc4060 Guido Trotter
  expired.
1284 dfdc4060 Guido Trotter

1285 dfdc4060 Guido Trotter
  """
1286 dfdc4060 Guido Trotter
1287 dfdc4060 Guido Trotter
  def __init__(self, timeout):
1288 dfdc4060 Guido Trotter
    self.timeout = timeout
1289 dfdc4060 Guido Trotter
1290 dfdc4060 Guido Trotter
  def Poll(self, fdobj, event):
1291 dfdc4060 Guido Trotter
    result = SingleWaitForFdCondition(fdobj, event, self.timeout)
1292 dfdc4060 Guido Trotter
    if result is None:
1293 dfdc4060 Guido Trotter
      raise RetryAgain()
1294 dfdc4060 Guido Trotter
    else:
1295 dfdc4060 Guido Trotter
      return result
1296 dfdc4060 Guido Trotter
1297 dfdc4060 Guido Trotter
  def UpdateTimeout(self, timeout):
1298 dfdc4060 Guido Trotter
    self.timeout = timeout
1299 dfdc4060 Guido Trotter
1300 dfdc4060 Guido Trotter
1301 dfdc4060 Guido Trotter
def WaitForFdCondition(fdobj, event, timeout):
1302 dfdc4060 Guido Trotter
  """Waits for a condition to occur on the socket.
1303 dfdc4060 Guido Trotter

1304 dfdc4060 Guido Trotter
  Retries until the timeout is expired, even if interrupted.
1305 dfdc4060 Guido Trotter

1306 dfdc4060 Guido Trotter
  @type fdobj: integer or object supporting a fileno() method
1307 dfdc4060 Guido Trotter
  @param fdobj: entity to wait for events on
1308 dfdc4060 Guido Trotter
  @type event: integer
1309 dfdc4060 Guido Trotter
  @param event: ORed condition (see select module)
1310 dfdc4060 Guido Trotter
  @type timeout: float or None
1311 dfdc4060 Guido Trotter
  @param timeout: Timeout in seconds
1312 dfdc4060 Guido Trotter
  @rtype: int or None
1313 dfdc4060 Guido Trotter
  @return: None for timeout, otherwise occured conditions
1314 dfdc4060 Guido Trotter

1315 dfdc4060 Guido Trotter
  """
1316 dfdc4060 Guido Trotter
  if timeout is not None:
1317 dfdc4060 Guido Trotter
    retrywaiter = FdConditionWaiterHelper(timeout)
1318 1b429e2a Iustin Pop
    try:
1319 1b429e2a Iustin Pop
      result = Retry(retrywaiter.Poll, RETRY_REMAINING_TIME, timeout,
1320 1b429e2a Iustin Pop
                     args=(fdobj, event), wait_fn=retrywaiter.UpdateTimeout)
1321 1b429e2a Iustin Pop
    except RetryTimeout:
1322 1b429e2a Iustin Pop
      result = None
1323 dfdc4060 Guido Trotter
  else:
1324 dfdc4060 Guido Trotter
    result = None
1325 dfdc4060 Guido Trotter
    while result is None:
1326 dfdc4060 Guido Trotter
      result = SingleWaitForFdCondition(fdobj, event, timeout)
1327 dfdc4060 Guido Trotter
  return result
1328 2de64672 Iustin Pop
1329 2de64672 Iustin Pop
1330 7d88772a Iustin Pop
def CloseFDs(noclose_fds=None):
1331 7d88772a Iustin Pop
  """Close file descriptors.
1332 7d88772a Iustin Pop

1333 7d88772a Iustin Pop
  This closes all file descriptors above 2 (i.e. except
1334 7d88772a Iustin Pop
  stdin/out/err).
1335 8f765069 Iustin Pop

1336 58885d79 Iustin Pop
  @type noclose_fds: list or None
1337 58885d79 Iustin Pop
  @param noclose_fds: if given, it denotes a list of file descriptor
1338 58885d79 Iustin Pop
      that should not be closed
1339 58885d79 Iustin Pop

1340 8f765069 Iustin Pop
  """
1341 8f765069 Iustin Pop
  # Default maximum for the number of available file descriptors.
1342 8f765069 Iustin Pop
  if 'SC_OPEN_MAX' in os.sysconf_names:
1343 8f765069 Iustin Pop
    try:
1344 8f765069 Iustin Pop
      MAXFD = os.sysconf('SC_OPEN_MAX')
1345 8f765069 Iustin Pop
      if MAXFD < 0:
1346 8f765069 Iustin Pop
        MAXFD = 1024
1347 8f765069 Iustin Pop
    except OSError:
1348 8f765069 Iustin Pop
      MAXFD = 1024
1349 8f765069 Iustin Pop
  else:
1350 8f765069 Iustin Pop
    MAXFD = 1024
1351 7d88772a Iustin Pop
  maxfd = resource.getrlimit(resource.RLIMIT_NOFILE)[1]
1352 7d88772a Iustin Pop
  if (maxfd == resource.RLIM_INFINITY):
1353 7d88772a Iustin Pop
    maxfd = MAXFD
1354 7d88772a Iustin Pop
1355 7d88772a Iustin Pop
  # Iterate through and close all file descriptors (except the standard ones)
1356 7d88772a Iustin Pop
  for fd in range(3, maxfd):
1357 7d88772a Iustin Pop
    if noclose_fds and fd in noclose_fds:
1358 7d88772a Iustin Pop
      continue
1359 71ab9dbe Michael Hanselmann
    CloseFdNoError(fd)
1360 7d88772a Iustin Pop
1361 7d88772a Iustin Pop
1362 0070a462 René Nussbaumer
def Daemonize(logfile):
1363 7d88772a Iustin Pop
  """Daemonize the current process.
1364 7d88772a Iustin Pop

1365 7d88772a Iustin Pop
  This detaches the current process from the controlling terminal and
1366 7d88772a Iustin Pop
  runs it in the background as a daemon.
1367 7d88772a Iustin Pop

1368 7d88772a Iustin Pop
  @type logfile: str
1369 7d88772a Iustin Pop
  @param logfile: the logfile to which we should redirect stdout/stderr
1370 7d88772a Iustin Pop
  @rtype: int
1371 5fcc718f Iustin Pop
  @return: the value zero
1372 7d88772a Iustin Pop

1373 7d88772a Iustin Pop
  """
1374 7260cfbe Iustin Pop
  # pylint: disable-msg=W0212
1375 7260cfbe Iustin Pop
  # yes, we really want os._exit
1376 8f765069 Iustin Pop
1377 b78aa8c2 Iustin Pop
  # TODO: do another attempt to merge Daemonize and StartDaemon, or at
1378 b78aa8c2 Iustin Pop
  # least abstract the pipe functionality between them
1379 b78aa8c2 Iustin Pop
1380 b78aa8c2 Iustin Pop
  # Create pipe for sending error messages
1381 b78aa8c2 Iustin Pop
  (rpipe, wpipe) = os.pipe()
1382 b78aa8c2 Iustin Pop
1383 8f765069 Iustin Pop
  # this might fail
1384 8f765069 Iustin Pop
  pid = os.fork()
1385 8f765069 Iustin Pop
  if (pid == 0):  # The first child.
1386 0260032c Iustin Pop
    SetupDaemonEnv()
1387 0260032c Iustin Pop
1388 8f765069 Iustin Pop
    # this might fail
1389 8f765069 Iustin Pop
    pid = os.fork() # Fork a second child.
1390 8f765069 Iustin Pop
    if (pid == 0):  # The second child.
1391 71ab9dbe Michael Hanselmann
      CloseFdNoError(rpipe)
1392 8f765069 Iustin Pop
    else:
1393 8f765069 Iustin Pop
      # exit() or _exit()?  See below.
1394 8f765069 Iustin Pop
      os._exit(0) # Exit parent (the first child) of the second child.
1395 8f765069 Iustin Pop
  else:
1396 71ab9dbe Michael Hanselmann
    CloseFdNoError(wpipe)
1397 b78aa8c2 Iustin Pop
    # Wait for daemon to be started (or an error message to
1398 b78aa8c2 Iustin Pop
    # arrive) and read up to 100 KB as an error message
1399 b78aa8c2 Iustin Pop
    errormsg = RetryOnSignal(os.read, rpipe, 100 * 1024)
1400 b78aa8c2 Iustin Pop
    if errormsg:
1401 b78aa8c2 Iustin Pop
      sys.stderr.write("Error when starting daemon process: %r\n" % errormsg)
1402 b78aa8c2 Iustin Pop
      rcode = 1
1403 b78aa8c2 Iustin Pop
    else:
1404 b78aa8c2 Iustin Pop
      rcode = 0
1405 b78aa8c2 Iustin Pop
    os._exit(rcode) # Exit parent of the first child.
1406 8f765069 Iustin Pop
1407 79634555 Iustin Pop
  SetupDaemonFDs(logfile, None)
1408 b78aa8c2 Iustin Pop
  return wpipe
1409 57c177af Iustin Pop
1410 57c177af Iustin Pop
1411 2826b361 Guido Trotter
def EnsureDaemon(name):
1412 2826b361 Guido Trotter
  """Check for and start daemon if not alive.
1413 2826b361 Guido Trotter

1414 2826b361 Guido Trotter
  """
1415 2826b361 Guido Trotter
  result = RunCmd([constants.DAEMON_UTIL, "check-and-start", name])
1416 2826b361 Guido Trotter
  if result.failed:
1417 2826b361 Guido Trotter
    logging.error("Can't start daemon '%s', failure %s, output: %s",
1418 2826b361 Guido Trotter
                  name, result.fail_reason, result.output)
1419 2826b361 Guido Trotter
    return False
1420 2826b361 Guido Trotter
1421 2826b361 Guido Trotter
  return True
1422 b330ac0b Guido Trotter
1423 b330ac0b Guido Trotter
1424 db147305 Tom Limoncelli
def StopDaemon(name):
1425 db147305 Tom Limoncelli
  """Stop daemon
1426 db147305 Tom Limoncelli

1427 db147305 Tom Limoncelli
  """
1428 db147305 Tom Limoncelli
  result = RunCmd([constants.DAEMON_UTIL, "stop", name])
1429 db147305 Tom Limoncelli
  if result.failed:
1430 db147305 Tom Limoncelli
    logging.error("Can't stop daemon '%s', failure %s, output: %s",
1431 db147305 Tom Limoncelli
                  name, result.fail_reason, result.output)
1432 db147305 Tom Limoncelli
    return False
1433 db147305 Tom Limoncelli
1434 db147305 Tom Limoncelli
  return True
1435 db147305 Tom Limoncelli
1436 db147305 Tom Limoncelli
1437 ff5251bc Iustin Pop
def KillProcess(pid, signal_=signal.SIGTERM, timeout=30,
1438 ff5251bc Iustin Pop
                waitpid=False):
1439 b2a1f511 Iustin Pop
  """Kill a process given by its pid.
1440 b2a1f511 Iustin Pop

1441 b2a1f511 Iustin Pop
  @type pid: int
1442 b2a1f511 Iustin Pop
  @param pid: The PID to terminate.
1443 38206f3c Iustin Pop
  @type signal_: int
1444 38206f3c Iustin Pop
  @param signal_: The signal to send, by default SIGTERM
1445 b2a1f511 Iustin Pop
  @type timeout: int
1446 b2a1f511 Iustin Pop
  @param timeout: The timeout after which, if the process is still alive,
1447 b2a1f511 Iustin Pop
                  a SIGKILL will be sent. If not positive, no such checking
1448 b2a1f511 Iustin Pop
                  will be done
1449 ff5251bc Iustin Pop
  @type waitpid: boolean
1450 ff5251bc Iustin Pop
  @param waitpid: If true, we should waitpid on this process after
1451 ff5251bc Iustin Pop
      sending signals, since it's our own child and otherwise it
1452 ff5251bc Iustin Pop
      would remain as zombie
1453 b2a1f511 Iustin Pop

1454 b2a1f511 Iustin Pop
  """
1455 ff5251bc Iustin Pop
  def _helper(pid, signal_, wait):
1456 ff5251bc Iustin Pop
    """Simple helper to encapsulate the kill/waitpid sequence"""
1457 560cbec1 Michael Hanselmann
    if IgnoreProcessNotFound(os.kill, pid, signal_) and wait:
1458 ff5251bc Iustin Pop
      try:
1459 ff5251bc Iustin Pop
        os.waitpid(pid, os.WNOHANG)
1460 ff5251bc Iustin Pop
      except OSError:
1461 ff5251bc Iustin Pop
        pass
1462 ff5251bc Iustin Pop
1463 b2a1f511 Iustin Pop
  if pid <= 0:
1464 b2a1f511 Iustin Pop
    # kill with pid=0 == suicide
1465 b2a1f511 Iustin Pop
    raise errors.ProgrammerError("Invalid pid given '%s'" % pid)
1466 b2a1f511 Iustin Pop
1467 b2a1f511 Iustin Pop
  if not IsProcessAlive(pid):
1468 b2a1f511 Iustin Pop
    return
1469 31892b4c Michael Hanselmann
1470 ff5251bc Iustin Pop
  _helper(pid, signal_, waitpid)
1471 31892b4c Michael Hanselmann
1472 b2a1f511 Iustin Pop
  if timeout <= 0:
1473 b2a1f511 Iustin Pop
    return
1474 7167159a Michael Hanselmann
1475 31892b4c Michael Hanselmann
  def _CheckProcess():
1476 31892b4c Michael Hanselmann
    if not IsProcessAlive(pid):
1477 31892b4c Michael Hanselmann
      return
1478 31892b4c Michael Hanselmann
1479 7167159a Michael Hanselmann
    try:
1480 7167159a Michael Hanselmann
      (result_pid, _) = os.waitpid(pid, os.WNOHANG)
1481 7167159a Michael Hanselmann
    except OSError:
1482 31892b4c Michael Hanselmann
      raise RetryAgain()
1483 31892b4c Michael Hanselmann
1484 31892b4c Michael Hanselmann
    if result_pid > 0:
1485 31892b4c Michael Hanselmann
      return
1486 31892b4c Michael Hanselmann
1487 31892b4c Michael Hanselmann
    raise RetryAgain()
1488 31892b4c Michael Hanselmann
1489 31892b4c Michael Hanselmann
  try:
1490 31892b4c Michael Hanselmann
    # Wait up to $timeout seconds
1491 31892b4c Michael Hanselmann
    Retry(_CheckProcess, (0.01, 1.5, 0.1), timeout)
1492 31892b4c Michael Hanselmann
  except RetryTimeout:
1493 31892b4c Michael Hanselmann
    pass
1494 7167159a Michael Hanselmann
1495 b2a1f511 Iustin Pop
  if IsProcessAlive(pid):
1496 7167159a Michael Hanselmann
    # Kill process if it's still alive
1497 e1bd0072 Iustin Pop
    _helper(pid, signal.SIGKILL, waitpid)
1498 b2a1f511 Iustin Pop
1499 b2a1f511 Iustin Pop
1500 8d1a2a64 Michael Hanselmann
def CheckVolumeGroupSize(vglist, vgname, minsize):
1501 8d1a2a64 Michael Hanselmann
  """Checks if the volume group list is valid.
1502 8d1a2a64 Michael Hanselmann

1503 58885d79 Iustin Pop
  The function will check if a given volume group is in the list of
1504 58885d79 Iustin Pop
  volume groups and has a minimum size.
1505 58885d79 Iustin Pop

1506 58885d79 Iustin Pop
  @type vglist: dict
1507 58885d79 Iustin Pop
  @param vglist: dictionary of volume group names and their size
1508 58885d79 Iustin Pop
  @type vgname: str
1509 58885d79 Iustin Pop
  @param vgname: the volume group we should check
1510 58885d79 Iustin Pop
  @type minsize: int
1511 58885d79 Iustin Pop
  @param minsize: the minimum size we accept
1512 58885d79 Iustin Pop
  @rtype: None or str
1513 58885d79 Iustin Pop
  @return: None for success, otherwise the error message
1514 8d1a2a64 Michael Hanselmann

1515 8d1a2a64 Michael Hanselmann
  """
1516 8d1a2a64 Michael Hanselmann
  vgsize = vglist.get(vgname, None)
1517 8d1a2a64 Michael Hanselmann
  if vgsize is None:
1518 8d1a2a64 Michael Hanselmann
    return "volume group '%s' missing" % vgname
1519 8d1a2a64 Michael Hanselmann
  elif vgsize < minsize:
1520 8d1a2a64 Michael Hanselmann
    return ("volume group '%s' too small (%s MiB required, %d MiB found)" %
1521 8d1a2a64 Michael Hanselmann
            (vgname, minsize, vgsize))
1522 8d1a2a64 Michael Hanselmann
  return None
1523 7996a135 Iustin Pop
1524 7996a135 Iustin Pop
1525 45bc5e4a Michael Hanselmann
def SplitTime(value):
1526 739be818 Michael Hanselmann
  """Splits time as floating point number into a tuple.
1527 739be818 Michael Hanselmann

1528 45bc5e4a Michael Hanselmann
  @param value: Time in seconds
1529 45bc5e4a Michael Hanselmann
  @type value: int or float
1530 45bc5e4a Michael Hanselmann
  @return: Tuple containing (seconds, microseconds)
1531 739be818 Michael Hanselmann

1532 739be818 Michael Hanselmann
  """
1533 45bc5e4a Michael Hanselmann
  (seconds, microseconds) = divmod(int(value * 1000000), 1000000)
1534 45bc5e4a Michael Hanselmann
1535 45bc5e4a Michael Hanselmann
  assert 0 <= seconds, \
1536 45bc5e4a Michael Hanselmann
    "Seconds must be larger than or equal to 0, but are %s" % seconds
1537 45bc5e4a Michael Hanselmann
  assert 0 <= microseconds <= 999999, \
1538 45bc5e4a Michael Hanselmann
    "Microseconds must be 0-999999, but are %s" % microseconds
1539 45bc5e4a Michael Hanselmann
1540 45bc5e4a Michael Hanselmann
  return (int(seconds), int(microseconds))
1541 739be818 Michael Hanselmann
1542 739be818 Michael Hanselmann
1543 739be818 Michael Hanselmann
def MergeTime(timetuple):
1544 739be818 Michael Hanselmann
  """Merges a tuple into time as a floating point number.
1545 739be818 Michael Hanselmann

1546 45bc5e4a Michael Hanselmann
  @param timetuple: Time as tuple, (seconds, microseconds)
1547 739be818 Michael Hanselmann
  @type timetuple: tuple
1548 739be818 Michael Hanselmann
  @return: Time as a floating point number expressed in seconds
1549 739be818 Michael Hanselmann

1550 739be818 Michael Hanselmann
  """
1551 45bc5e4a Michael Hanselmann
  (seconds, microseconds) = timetuple
1552 739be818 Michael Hanselmann
1553 45bc5e4a Michael Hanselmann
  assert 0 <= seconds, \
1554 45bc5e4a Michael Hanselmann
    "Seconds must be larger than or equal to 0, but are %s" % seconds
1555 45bc5e4a Michael Hanselmann
  assert 0 <= microseconds <= 999999, \
1556 45bc5e4a Michael Hanselmann
    "Microseconds must be 0-999999, but are %s" % microseconds
1557 739be818 Michael Hanselmann
1558 45bc5e4a Michael Hanselmann
  return float(seconds) + (float(microseconds) * 0.000001)
1559 739be818 Michael Hanselmann
1560 739be818 Michael Hanselmann
1561 691c81b7 Michael Hanselmann
def FindMatch(data, name):
1562 691c81b7 Michael Hanselmann
  """Tries to find an item in a dictionary matching a name.
1563 691c81b7 Michael Hanselmann

1564 691c81b7 Michael Hanselmann
  Callers have to ensure the data names aren't contradictory (e.g. a regexp
1565 691c81b7 Michael Hanselmann
  that matches a string). If the name isn't a direct key, all regular
1566 691c81b7 Michael Hanselmann
  expression objects in the dictionary are matched against it.
1567 691c81b7 Michael Hanselmann

1568 691c81b7 Michael Hanselmann
  @type data: dict
1569 691c81b7 Michael Hanselmann
  @param data: Dictionary containing data
1570 691c81b7 Michael Hanselmann
  @type name: string
1571 691c81b7 Michael Hanselmann
  @param name: Name to look for
1572 691c81b7 Michael Hanselmann
  @rtype: tuple; (value in dictionary, matched groups as list)
1573 691c81b7 Michael Hanselmann

1574 691c81b7 Michael Hanselmann
  """
1575 691c81b7 Michael Hanselmann
  if name in data:
1576 691c81b7 Michael Hanselmann
    return (data[name], [])
1577 691c81b7 Michael Hanselmann
1578 691c81b7 Michael Hanselmann
  for key, value in data.items():
1579 691c81b7 Michael Hanselmann
    # Regex objects
1580 691c81b7 Michael Hanselmann
    if hasattr(key, "match"):
1581 691c81b7 Michael Hanselmann
      m = key.match(name)
1582 691c81b7 Michael Hanselmann
      if m:
1583 691c81b7 Michael Hanselmann
        return (value, list(m.groups()))
1584 691c81b7 Michael Hanselmann
1585 691c81b7 Michael Hanselmann
  return None
1586 691c81b7 Michael Hanselmann
1587 691c81b7 Michael Hanselmann
1588 1b045f5d Balazs Lecz
def GetMounts(filename=constants.PROC_MOUNTS):
1589 1b045f5d Balazs Lecz
  """Returns the list of mounted filesystems.
1590 1b045f5d Balazs Lecz

1591 1b045f5d Balazs Lecz
  This function is Linux-specific.
1592 1b045f5d Balazs Lecz

1593 1b045f5d Balazs Lecz
  @param filename: path of mounts file (/proc/mounts by default)
1594 1b045f5d Balazs Lecz
  @rtype: list of tuples
1595 1b045f5d Balazs Lecz
  @return: list of mount entries (device, mountpoint, fstype, options)
1596 1b045f5d Balazs Lecz

1597 1b045f5d Balazs Lecz
  """
1598 1b045f5d Balazs Lecz
  # TODO(iustin): investigate non-Linux options (e.g. via mount output)
1599 1b045f5d Balazs Lecz
  data = []
1600 1b045f5d Balazs Lecz
  mountlines = ReadFile(filename).splitlines()
1601 1b045f5d Balazs Lecz
  for line in mountlines:
1602 1b045f5d Balazs Lecz
    device, mountpoint, fstype, options, _ = line.split(None, 4)
1603 1b045f5d Balazs Lecz
    data.append((device, mountpoint, fstype, options))
1604 1b045f5d Balazs Lecz
1605 1b045f5d Balazs Lecz
  return data
1606 1b045f5d Balazs Lecz
1607 1b045f5d Balazs Lecz
1608 bdefe5dd Michael Hanselmann
def RunInSeparateProcess(fn, *args):
1609 eb58f7bd Michael Hanselmann
  """Runs a function in a separate process.
1610 eb58f7bd Michael Hanselmann

1611 eb58f7bd Michael Hanselmann
  Note: Only boolean return values are supported.
1612 eb58f7bd Michael Hanselmann

1613 eb58f7bd Michael Hanselmann
  @type fn: callable
1614 eb58f7bd Michael Hanselmann
  @param fn: Function to be called
1615 bdefe5dd Michael Hanselmann
  @rtype: bool
1616 bdefe5dd Michael Hanselmann
  @return: Function's result
1617 eb58f7bd Michael Hanselmann

1618 eb58f7bd Michael Hanselmann
  """
1619 eb58f7bd Michael Hanselmann
  pid = os.fork()
1620 eb58f7bd Michael Hanselmann
  if pid == 0:
1621 eb58f7bd Michael Hanselmann
    # Child process
1622 eb58f7bd Michael Hanselmann
    try:
1623 82869978 Michael Hanselmann
      # In case the function uses temporary files
1624 82869978 Michael Hanselmann
      ResetTempfileModule()
1625 82869978 Michael Hanselmann
1626 eb58f7bd Michael Hanselmann
      # Call function
1627 bdefe5dd Michael Hanselmann
      result = int(bool(fn(*args)))
1628 eb58f7bd Michael Hanselmann
      assert result in (0, 1)
1629 eb58f7bd Michael Hanselmann
    except: # pylint: disable-msg=W0702
1630 eb58f7bd Michael Hanselmann
      logging.exception("Error while calling function in separate process")
1631 eb58f7bd Michael Hanselmann
      # 0 and 1 are reserved for the return value
1632 eb58f7bd Michael Hanselmann
      result = 33
1633 eb58f7bd Michael Hanselmann
1634 eb58f7bd Michael Hanselmann
    os._exit(result) # pylint: disable-msg=W0212
1635 eb58f7bd Michael Hanselmann
1636 eb58f7bd Michael Hanselmann
  # Parent process
1637 eb58f7bd Michael Hanselmann
1638 eb58f7bd Michael Hanselmann
  # Avoid zombies and check exit code
1639 eb58f7bd Michael Hanselmann
  (_, status) = os.waitpid(pid, 0)
1640 eb58f7bd Michael Hanselmann
1641 eb58f7bd Michael Hanselmann
  if os.WIFSIGNALED(status):
1642 eb58f7bd Michael Hanselmann
    exitcode = None
1643 eb58f7bd Michael Hanselmann
    signum = os.WTERMSIG(status)
1644 eb58f7bd Michael Hanselmann
  else:
1645 eb58f7bd Michael Hanselmann
    exitcode = os.WEXITSTATUS(status)
1646 eb58f7bd Michael Hanselmann
    signum = None
1647 eb58f7bd Michael Hanselmann
1648 eb58f7bd Michael Hanselmann
  if not (exitcode in (0, 1) and signum is None):
1649 eb58f7bd Michael Hanselmann
    raise errors.GenericError("Child program failed (code=%s, signal=%s)" %
1650 eb58f7bd Michael Hanselmann
                              (exitcode, signum))
1651 eb58f7bd Michael Hanselmann
1652 eb58f7bd Michael Hanselmann
  return bool(exitcode)
1653 eb58f7bd Michael Hanselmann
1654 eb58f7bd Michael Hanselmann
1655 451575de Guido Trotter
def SignalHandled(signums):
1656 451575de Guido Trotter
  """Signal Handled decoration.
1657 451575de Guido Trotter

1658 451575de Guido Trotter
  This special decorator installs a signal handler and then calls the target
1659 451575de Guido Trotter
  function. The function must accept a 'signal_handlers' keyword argument,
1660 451575de Guido Trotter
  which will contain a dict indexed by signal number, with SignalHandler
1661 451575de Guido Trotter
  objects as values.
1662 451575de Guido Trotter

1663 451575de Guido Trotter
  The decorator can be safely stacked with iself, to handle multiple signals
1664 451575de Guido Trotter
  with different handlers.
1665 451575de Guido Trotter

1666 451575de Guido Trotter
  @type signums: list
1667 451575de Guido Trotter
  @param signums: signals to intercept
1668 451575de Guido Trotter

1669 451575de Guido Trotter
  """
1670 451575de Guido Trotter
  def wrap(fn):
1671 451575de Guido Trotter
    def sig_function(*args, **kwargs):
1672 451575de Guido Trotter
      assert 'signal_handlers' not in kwargs or \
1673 451575de Guido Trotter
             kwargs['signal_handlers'] is None or \
1674 451575de Guido Trotter
             isinstance(kwargs['signal_handlers'], dict), \
1675 451575de Guido Trotter
             "Wrong signal_handlers parameter in original function call"
1676 451575de Guido Trotter
      if 'signal_handlers' in kwargs and kwargs['signal_handlers'] is not None:
1677 451575de Guido Trotter
        signal_handlers = kwargs['signal_handlers']
1678 451575de Guido Trotter
      else:
1679 451575de Guido Trotter
        signal_handlers = {}
1680 451575de Guido Trotter
        kwargs['signal_handlers'] = signal_handlers
1681 451575de Guido Trotter
      sighandler = SignalHandler(signums)
1682 451575de Guido Trotter
      try:
1683 451575de Guido Trotter
        for sig in signums:
1684 451575de Guido Trotter
          signal_handlers[sig] = sighandler
1685 451575de Guido Trotter
        return fn(*args, **kwargs)
1686 451575de Guido Trotter
      finally:
1687 451575de Guido Trotter
        sighandler.Reset()
1688 451575de Guido Trotter
    return sig_function
1689 451575de Guido Trotter
  return wrap
1690 451575de Guido Trotter
1691 451575de Guido Trotter
1692 b9768937 Michael Hanselmann
class SignalWakeupFd(object):
1693 b9768937 Michael Hanselmann
  try:
1694 b9768937 Michael Hanselmann
    # This is only supported in Python 2.5 and above (some distributions
1695 b9768937 Michael Hanselmann
    # backported it to Python 2.4)
1696 b9768937 Michael Hanselmann
    _set_wakeup_fd_fn = signal.set_wakeup_fd
1697 b9768937 Michael Hanselmann
  except AttributeError:
1698 b9768937 Michael Hanselmann
    # Not supported
1699 b9768937 Michael Hanselmann
    def _SetWakeupFd(self, _): # pylint: disable-msg=R0201
1700 b9768937 Michael Hanselmann
      return -1
1701 b9768937 Michael Hanselmann
  else:
1702 b9768937 Michael Hanselmann
    def _SetWakeupFd(self, fd):
1703 b9768937 Michael Hanselmann
      return self._set_wakeup_fd_fn(fd)
1704 b9768937 Michael Hanselmann
1705 b9768937 Michael Hanselmann
  def __init__(self):
1706 b9768937 Michael Hanselmann
    """Initializes this class.
1707 b9768937 Michael Hanselmann

1708 b9768937 Michael Hanselmann
    """
1709 b9768937 Michael Hanselmann
    (read_fd, write_fd) = os.pipe()
1710 b9768937 Michael Hanselmann
1711 b9768937 Michael Hanselmann
    # Once these succeeded, the file descriptors will be closed automatically.
1712 b9768937 Michael Hanselmann
    # Buffer size 0 is important, otherwise .read() with a specified length
1713 b9768937 Michael Hanselmann
    # might buffer data and the file descriptors won't be marked readable.
1714 b9768937 Michael Hanselmann
    self._read_fh = os.fdopen(read_fd, "r", 0)
1715 b9768937 Michael Hanselmann
    self._write_fh = os.fdopen(write_fd, "w", 0)
1716 b9768937 Michael Hanselmann
1717 b9768937 Michael Hanselmann
    self._previous = self._SetWakeupFd(self._write_fh.fileno())
1718 b9768937 Michael Hanselmann
1719 b9768937 Michael Hanselmann
    # Utility functions
1720 b9768937 Michael Hanselmann
    self.fileno = self._read_fh.fileno
1721 b9768937 Michael Hanselmann
    self.read = self._read_fh.read
1722 b9768937 Michael Hanselmann
1723 b9768937 Michael Hanselmann
  def Reset(self):
1724 b9768937 Michael Hanselmann
    """Restores the previous wakeup file descriptor.
1725 b9768937 Michael Hanselmann

1726 b9768937 Michael Hanselmann
    """
1727 b9768937 Michael Hanselmann
    if hasattr(self, "_previous") and self._previous is not None:
1728 b9768937 Michael Hanselmann
      self._SetWakeupFd(self._previous)
1729 b9768937 Michael Hanselmann
      self._previous = None
1730 b9768937 Michael Hanselmann
1731 b9768937 Michael Hanselmann
  def Notify(self):
1732 b9768937 Michael Hanselmann
    """Notifies the wakeup file descriptor.
1733 b9768937 Michael Hanselmann

1734 b9768937 Michael Hanselmann
    """
1735 b9768937 Michael Hanselmann
    self._write_fh.write("\0")
1736 b9768937 Michael Hanselmann
1737 b9768937 Michael Hanselmann
  def __del__(self):
1738 b9768937 Michael Hanselmann
    """Called before object deletion.
1739 b9768937 Michael Hanselmann

1740 b9768937 Michael Hanselmann
    """
1741 b9768937 Michael Hanselmann
    self.Reset()
1742 b9768937 Michael Hanselmann
1743 b9768937 Michael Hanselmann
1744 de499029 Michael Hanselmann
class SignalHandler(object):
1745 de499029 Michael Hanselmann
  """Generic signal handler class.
1746 de499029 Michael Hanselmann

1747 58885d79 Iustin Pop
  It automatically restores the original handler when deconstructed or
1748 58885d79 Iustin Pop
  when L{Reset} is called. You can either pass your own handler
1749 58885d79 Iustin Pop
  function in or query the L{called} attribute to detect whether the
1750 58885d79 Iustin Pop
  signal was sent.
1751 58885d79 Iustin Pop

1752 58885d79 Iustin Pop
  @type signum: list
1753 58885d79 Iustin Pop
  @ivar signum: the signals we handle
1754 58885d79 Iustin Pop
  @type called: boolean
1755 58885d79 Iustin Pop
  @ivar called: tracks whether any of the signals have been raised
1756 de499029 Michael Hanselmann

1757 de499029 Michael Hanselmann
  """
1758 b9768937 Michael Hanselmann
  def __init__(self, signum, handler_fn=None, wakeup=None):
1759 de499029 Michael Hanselmann
    """Constructs a new SignalHandler instance.
1760 de499029 Michael Hanselmann

1761 58885d79 Iustin Pop
    @type signum: int or list of ints
1762 de499029 Michael Hanselmann
    @param signum: Single signal number or set of signal numbers
1763 92b61ec7 Michael Hanselmann
    @type handler_fn: callable
1764 92b61ec7 Michael Hanselmann
    @param handler_fn: Signal handling function
1765 de499029 Michael Hanselmann

1766 de499029 Michael Hanselmann
    """
1767 92b61ec7 Michael Hanselmann
    assert handler_fn is None or callable(handler_fn)
1768 92b61ec7 Michael Hanselmann
1769 6c52849e Guido Trotter
    self.signum = set(signum)
1770 de499029 Michael Hanselmann
    self.called = False
1771 de499029 Michael Hanselmann
1772 92b61ec7 Michael Hanselmann
    self._handler_fn = handler_fn
1773 b9768937 Michael Hanselmann
    self._wakeup = wakeup
1774 92b61ec7 Michael Hanselmann
1775 de499029 Michael Hanselmann
    self._previous = {}
1776 de499029 Michael Hanselmann
    try:
1777 de499029 Michael Hanselmann
      for signum in self.signum:
1778 de499029 Michael Hanselmann
        # Setup handler
1779 de499029 Michael Hanselmann
        prev_handler = signal.signal(signum, self._HandleSignal)
1780 de499029 Michael Hanselmann
        try:
1781 de499029 Michael Hanselmann
          self._previous[signum] = prev_handler
1782 de499029 Michael Hanselmann
        except:
1783 de499029 Michael Hanselmann
          # Restore previous handler
1784 de499029 Michael Hanselmann
          signal.signal(signum, prev_handler)
1785 de499029 Michael Hanselmann
          raise
1786 de499029 Michael Hanselmann
    except:
1787 de499029 Michael Hanselmann
      # Reset all handlers
1788 de499029 Michael Hanselmann
      self.Reset()
1789 de499029 Michael Hanselmann
      # Here we have a race condition: a handler may have already been called,
1790 de499029 Michael Hanselmann
      # but there's not much we can do about it at this point.
1791 de499029 Michael Hanselmann
      raise
1792 de499029 Michael Hanselmann
1793 de499029 Michael Hanselmann
  def __del__(self):
1794 de499029 Michael Hanselmann
    self.Reset()
1795 de499029 Michael Hanselmann
1796 de499029 Michael Hanselmann
  def Reset(self):
1797 de499029 Michael Hanselmann
    """Restore previous handler.
1798 de499029 Michael Hanselmann

1799 58885d79 Iustin Pop
    This will reset all the signals to their previous handlers.
1800 58885d79 Iustin Pop

1801 de499029 Michael Hanselmann
    """
1802 de499029 Michael Hanselmann
    for signum, prev_handler in self._previous.items():
1803 de499029 Michael Hanselmann
      signal.signal(signum, prev_handler)
1804 de499029 Michael Hanselmann
      # If successful, remove from dict
1805 de499029 Michael Hanselmann
      del self._previous[signum]
1806 de499029 Michael Hanselmann
1807 de499029 Michael Hanselmann
  def Clear(self):
1808 58885d79 Iustin Pop
    """Unsets the L{called} flag.
1809 de499029 Michael Hanselmann

1810 de499029 Michael Hanselmann
    This function can be used in case a signal may arrive several times.
1811 de499029 Michael Hanselmann

1812 de499029 Michael Hanselmann
    """
1813 de499029 Michael Hanselmann
    self.called = False
1814 de499029 Michael Hanselmann
1815 92b61ec7 Michael Hanselmann
  def _HandleSignal(self, signum, frame):
1816 de499029 Michael Hanselmann
    """Actual signal handling function.
1817 de499029 Michael Hanselmann

1818 de499029 Michael Hanselmann
    """
1819 de499029 Michael Hanselmann
    # This is not nice and not absolutely atomic, but it appears to be the only
1820 de499029 Michael Hanselmann
    # solution in Python -- there are no atomic types.
1821 de499029 Michael Hanselmann
    self.called = True
1822 a2d2e1a7 Iustin Pop
1823 b9768937 Michael Hanselmann
    if self._wakeup:
1824 b9768937 Michael Hanselmann
      # Notify whoever is interested in signals
1825 b9768937 Michael Hanselmann
      self._wakeup.Notify()
1826 b9768937 Michael Hanselmann
1827 92b61ec7 Michael Hanselmann
    if self._handler_fn:
1828 92b61ec7 Michael Hanselmann
      self._handler_fn(signum, frame)
1829 92b61ec7 Michael Hanselmann
1830 a2d2e1a7 Iustin Pop
1831 a2d2e1a7 Iustin Pop
class FieldSet(object):
1832 a2d2e1a7 Iustin Pop
  """A simple field set.
1833 a2d2e1a7 Iustin Pop

1834 a2d2e1a7 Iustin Pop
  Among the features are:
1835 a2d2e1a7 Iustin Pop
    - checking if a string is among a list of static string or regex objects
1836 a2d2e1a7 Iustin Pop
    - checking if a whole list of string matches
1837 a2d2e1a7 Iustin Pop
    - returning the matching groups from a regex match
1838 a2d2e1a7 Iustin Pop

1839 a2d2e1a7 Iustin Pop
  Internally, all fields are held as regular expression objects.
1840 a2d2e1a7 Iustin Pop

1841 a2d2e1a7 Iustin Pop
  """
1842 a2d2e1a7 Iustin Pop
  def __init__(self, *items):
1843 a2d2e1a7 Iustin Pop
    self.items = [re.compile("^%s$" % value) for value in items]
1844 a2d2e1a7 Iustin Pop
1845 a2d2e1a7 Iustin Pop
  def Extend(self, other_set):
1846 a2d2e1a7 Iustin Pop
    """Extend the field set with the items from another one"""
1847 a2d2e1a7 Iustin Pop
    self.items.extend(other_set.items)
1848 a2d2e1a7 Iustin Pop
1849 a2d2e1a7 Iustin Pop
  def Matches(self, field):
1850 a2d2e1a7 Iustin Pop
    """Checks if a field matches the current set
1851 a2d2e1a7 Iustin Pop

1852 a2d2e1a7 Iustin Pop
    @type field: str
1853 a2d2e1a7 Iustin Pop
    @param field: the string to match
1854 6c881c52 Iustin Pop
    @return: either None or a regular expression match object
1855 a2d2e1a7 Iustin Pop

1856 a2d2e1a7 Iustin Pop
    """
1857 a2d2e1a7 Iustin Pop
    for m in itertools.ifilter(None, (val.match(field) for val in self.items)):
1858 a2d2e1a7 Iustin Pop
      return m
1859 6c881c52 Iustin Pop
    return None
1860 a2d2e1a7 Iustin Pop
1861 a2d2e1a7 Iustin Pop
  def NonMatching(self, items):
1862 a2d2e1a7 Iustin Pop
    """Returns the list of fields not matching the current set
1863 a2d2e1a7 Iustin Pop

1864 a2d2e1a7 Iustin Pop
    @type items: list
1865 a2d2e1a7 Iustin Pop
    @param items: the list of fields to check
1866 a2d2e1a7 Iustin Pop
    @rtype: list
1867 a2d2e1a7 Iustin Pop
    @return: list of non-matching fields
1868 a2d2e1a7 Iustin Pop

1869 a2d2e1a7 Iustin Pop
    """
1870 a2d2e1a7 Iustin Pop
    return [val for val in items if not self.Matches(val)]
1871 557838c1 René Nussbaumer
1872 557838c1 René Nussbaumer
1873 557838c1 René Nussbaumer
class RunningTimeout(object):
1874 557838c1 René Nussbaumer
  """Class to calculate remaining timeout when doing several operations.
1875 557838c1 René Nussbaumer

1876 557838c1 René Nussbaumer
  """
1877 557838c1 René Nussbaumer
  __slots__ = [
1878 557838c1 René Nussbaumer
    "_allow_negative",
1879 557838c1 René Nussbaumer
    "_start_time",
1880 557838c1 René Nussbaumer
    "_time_fn",
1881 557838c1 René Nussbaumer
    "_timeout",
1882 557838c1 René Nussbaumer
    ]
1883 557838c1 René Nussbaumer
1884 557838c1 René Nussbaumer
  def __init__(self, timeout, allow_negative, _time_fn=time.time):
1885 557838c1 René Nussbaumer
    """Initializes this class.
1886 557838c1 René Nussbaumer

1887 557838c1 René Nussbaumer
    @type timeout: float
1888 557838c1 René Nussbaumer
    @param timeout: Timeout duration
1889 557838c1 René Nussbaumer
    @type allow_negative: bool
1890 557838c1 René Nussbaumer
    @param allow_negative: Whether to return values below zero
1891 557838c1 René Nussbaumer
    @param _time_fn: Time function for unittests
1892 557838c1 René Nussbaumer

1893 557838c1 René Nussbaumer
    """
1894 557838c1 René Nussbaumer
    object.__init__(self)
1895 557838c1 René Nussbaumer
1896 557838c1 René Nussbaumer
    if timeout is not None and timeout < 0.0:
1897 557838c1 René Nussbaumer
      raise ValueError("Timeout must not be negative")
1898 557838c1 René Nussbaumer
1899 557838c1 René Nussbaumer
    self._timeout = timeout
1900 557838c1 René Nussbaumer
    self._allow_negative = allow_negative
1901 557838c1 René Nussbaumer
    self._time_fn = _time_fn
1902 557838c1 René Nussbaumer
1903 557838c1 René Nussbaumer
    self._start_time = None
1904 557838c1 René Nussbaumer
1905 557838c1 René Nussbaumer
  def Remaining(self):
1906 557838c1 René Nussbaumer
    """Returns the remaining timeout.
1907 557838c1 René Nussbaumer

1908 557838c1 René Nussbaumer
    """
1909 557838c1 René Nussbaumer
    if self._timeout is None:
1910 557838c1 René Nussbaumer
      return None
1911 557838c1 René Nussbaumer
1912 557838c1 René Nussbaumer
    # Get start time on first calculation
1913 557838c1 René Nussbaumer
    if self._start_time is None:
1914 557838c1 René Nussbaumer
      self._start_time = self._time_fn()
1915 557838c1 René Nussbaumer
1916 557838c1 René Nussbaumer
    # Calculate remaining time
1917 557838c1 René Nussbaumer
    remaining_timeout = self._start_time + self._timeout - self._time_fn()
1918 557838c1 René Nussbaumer
1919 557838c1 René Nussbaumer
    if not self._allow_negative:
1920 557838c1 René Nussbaumer
      # Ensure timeout is always >= 0
1921 557838c1 René Nussbaumer
      return max(0.0, remaining_timeout)
1922 557838c1 René Nussbaumer
1923 557838c1 René Nussbaumer
    return remaining_timeout