Revision 4ca693ca daemons/import-export

b/daemons/import-export
524 524
  return (status_file_path, mode)
525 525

  
526 526

  
527
class ChildProcess(subprocess.Popen):
528
  def __init__(self, cmd, noclose_fds):
529
    """Initializes this class.
530

  
531
    """
532
    self._noclose_fds = noclose_fds
533

  
534
    # Not using close_fds because doing so would also close the socat stderr
535
    # pipe, which we still need.
536
    subprocess.Popen.__init__(self, cmd, shell=False, close_fds=False,
537
                              stderr=subprocess.PIPE, stdout=None, stdin=None,
538
                              preexec_fn=self._ChildPreexec)
539
    self._SetProcessGroup()
540

  
541
  def _ChildPreexec(self):
542
    """Called before child executable is execve'd.
543

  
544
    """
545
    # Move to separate process group. By sending a signal to its process group
546
    # we can kill the child process and all grandchildren.
547
    os.setpgid(0, 0)
548

  
549
    # Close almost all file descriptors
550
    utils.CloseFDs(noclose_fds=self._noclose_fds)
551

  
552
  def _SetProcessGroup(self):
553
    """Sets the child's process group.
554

  
555
    """
556
    assert self.pid, "Can't be called in child process"
557

  
558
    # Avoid race condition by setting child's process group (as good as
559
    # possible in Python) before sending signals to child. For an
560
    # explanation, see preexec function for child.
561
    try:
562
      os.setpgid(self.pid, self.pid)
563
    except EnvironmentError, err:
564
      # If the child process was faster we receive EPERM or EACCES
565
      if err.errno not in (errno.EPERM, errno.EACCES):
566
        raise
567

  
568
  def Kill(self, signum):
569
    """Sends signal to child process.
570

  
571
    """
572
    logging.info("Sending signal %s to child process", signum)
573
    os.killpg(self.pid, signum)
574

  
575
  def ForceQuit(self):
576
    """Ensure child process is no longer running.
577

  
578
    """
579
    # Final check if child process is still alive
580
    if utils.RetryOnSignal(self.poll) is None:
581
      logging.error("Child process still alive, sending SIGKILL")
582
      self.Kill(signal.SIGKILL)
583
      utils.RetryOnSignal(self.wait)
584

  
585

  
527 586
def main():
528 587
  """Main function.
529 588

  
......
564 623

  
565 624
      logging.debug("Starting command %r", cmd)
566 625

  
567
      def _ChildPreexec():
568
        # Move child to separate process group. By sending a signal to its
569
        # process group we can kill the child process and all its own
570
        # child-processes.
571
        os.setpgid(0, 0)
572

  
573
        # Close almost all file descriptors
574
        utils.CloseFDs(noclose_fds=[socat_stderr_write_fd])
575

  
576
      # Not using close_fds because doing so would also close the socat stderr
577
      # pipe, which we still need.
578
      child = subprocess.Popen(cmd, shell=False, close_fds=False,
579
                               stderr=subprocess.PIPE, stdout=None, stdin=None,
580
                               preexec_fn=_ChildPreexec)
626
      # Start child process
627
      child = ChildProcess(cmd, [socat_stderr_write_fd])
581 628
      try:
582
        # Avoid race condition by setting child's process group (as good as
583
        # possible in Python) before sending signals to child. For an
584
        # explanation, see preexec function for child.
585
        try:
586
          os.setpgid(child.pid, child.pid)
587
        except EnvironmentError, err:
588
          # If the child process was faster we receive EPERM or EACCES
589
          if err.errno not in (errno.EPERM, errno.EACCES):
590
            raise
591

  
592 629
        # Forward signals to child process
593 630
        def _ForwardSignal(signum, _):
594 631
          # Wake up poll(2)
595 632
          os.write(signal_notify_write_fd, "\0")
596 633

  
597 634
          # Send signal to child
598
          os.killpg(child.pid, signum)
635
          child.Kill(signum)
599 636

  
600 637
        # TODO: There is a race condition between starting the child and
601 638
        # handling the signals here. While there might be a way to work around
......
616 653
        finally:
617 654
          signal_handler.Reset()
618 655
      finally:
619
        # Final check if child process is still alive
620
        if utils.RetryOnSignal(child.poll) is None:
621
          logging.error("Child process still alive, sending SIGKILL")
622
          os.killpg(child.pid, signal.SIGKILL)
623
          utils.RetryOnSignal(child.wait)
656
        child.ForceQuit()
624 657

  
625 658
      if child.returncode == 0:
626 659
        errmsg = None

Also available in: Unified diff