Exemplo n.º 1
0
 def _helper(pid, signal_, wait):
   """Simple helper to encapsulate the kill/waitpid sequence"""
   if utils_wrapper.IgnoreProcessNotFound(os.kill, pid, signal_) and wait:
     try:
       os.waitpid(pid, os.WNOHANG)
     except OSError:
       pass
Exemplo n.º 2
0
def _RunCmdPipe(cmd, env, via_shell, cwd, interactive, timeout, noclose_fds,
                input_fd, postfork_fn=None,
                _linger_timeout=constants.CHILD_LINGER_TIMEOUT):
  """Run a command and return its output.

  @type  cmd: string or list
  @param cmd: Command to run
  @type env: dict
  @param env: The environment to use
  @type via_shell: bool
  @param via_shell: if we should run via the shell
  @type cwd: string
  @param cwd: the working directory for the program
  @type interactive: boolean
  @param interactive: Run command interactive (without piping)
  @type timeout: int
  @param timeout: Timeout after the programm gets terminated
  @type noclose_fds: list
  @param noclose_fds: list of additional (fd >=3) file descriptors to leave
                      open for the child process
  @type input_fd: C{file}-like object or numeric file descriptor
  @param input_fd: File descriptor for process' standard input
  @type postfork_fn: Callable receiving PID as parameter
  @param postfork_fn: Function run after fork but before timeout
  @rtype: tuple
  @return: (out, err, status)

  """
  poller = select.poll()

  if interactive:
    stderr = None
    stdout = None
  else:
    stderr = subprocess.PIPE
    stdout = subprocess.PIPE

  if input_fd:
    stdin = input_fd
  elif interactive:
    stdin = None
  else:
    stdin = subprocess.PIPE

  if noclose_fds:
    preexec_fn = lambda: CloseFDs(noclose_fds)
    close_fds = False
  else:
    preexec_fn = None
    close_fds = True

  child = subprocess.Popen(cmd, shell=via_shell,
                           stderr=stderr,
                           stdout=stdout,
                           stdin=stdin,
                           close_fds=close_fds, env=env,
                           cwd=cwd,
                           preexec_fn=preexec_fn)

  if postfork_fn:
    postfork_fn(child.pid)

  out = StringIO()
  err = StringIO()

  linger_timeout = None

  if timeout is None:
    poll_timeout = None
  else:
    poll_timeout = utils_algo.RunningTimeout(timeout, True).Remaining

  msg_timeout = ("Command %s (%d) run into execution timeout, terminating" %
                 (cmd, child.pid))
  msg_linger = ("Command %s (%d) run into linger timeout, killing" %
                (cmd, child.pid))

  timeout_action = _TIMEOUT_NONE

  # subprocess: "If the stdin argument is PIPE, this attribute is a file object
  # that provides input to the child process. Otherwise, it is None."
  assert (stdin == subprocess.PIPE) ^ (child.stdin is None), \
    "subprocess' stdin did not behave as documented"

  if not interactive:
    if child.stdin is not None:
      child.stdin.close()
    poller.register(child.stdout, select.POLLIN)
    poller.register(child.stderr, select.POLLIN)
    fdmap = {
      child.stdout.fileno(): (out, child.stdout),
      child.stderr.fileno(): (err, child.stderr),
      }
    for fd in fdmap:
      utils_wrapper.SetNonblockFlag(fd, True)

    while fdmap:
      if poll_timeout:
        pt = poll_timeout() * 1000
        if pt < 0:
          if linger_timeout is None:
            logging.warning(msg_timeout)
            if child.poll() is None:
              timeout_action = _TIMEOUT_TERM
              utils_wrapper.IgnoreProcessNotFound(os.kill, child.pid,
                                                  signal.SIGTERM)
            linger_timeout = \
              utils_algo.RunningTimeout(_linger_timeout, True).Remaining
          pt = linger_timeout() * 1000
          if pt < 0:
            break
      else:
        pt = None

      pollresult = utils_wrapper.RetryOnSignal(poller.poll, pt)

      for fd, event in pollresult:
        if event & select.POLLIN or event & select.POLLPRI:
          data = fdmap[fd][1].read()
          # no data from read signifies EOF (the same as POLLHUP)
          if not data:
            poller.unregister(fd)
            del fdmap[fd]
            continue
          fdmap[fd][0].write(data)
        if (event & select.POLLNVAL or event & select.POLLHUP or
            event & select.POLLERR):
          poller.unregister(fd)
          del fdmap[fd]

  if timeout is not None:
    assert callable(poll_timeout)

    # We have no I/O left but it might still run
    if child.poll() is None:
      _WaitForProcess(child, poll_timeout())

    # Terminate if still alive after timeout
    if child.poll() is None:
      if linger_timeout is None:
        logging.warning(msg_timeout)
        timeout_action = _TIMEOUT_TERM
        utils_wrapper.IgnoreProcessNotFound(os.kill, child.pid, signal.SIGTERM)
        lt = _linger_timeout
      else:
        lt = linger_timeout()
      _WaitForProcess(child, lt)

    # Okay, still alive after timeout and linger timeout? Kill it!
    if child.poll() is None:
      timeout_action = _TIMEOUT_KILL
      logging.warning(msg_linger)
      utils_wrapper.IgnoreProcessNotFound(os.kill, child.pid, signal.SIGKILL)

  out = out.getvalue()
  err = err.getvalue()

  status = child.wait()
  return out, err, status, timeout_action