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