#!/usr/bin/env python # XXX This isn't actually very reliable because if the nonohup process # dies unexpectedly (for example, by receiving a signal), there's # really nothing it can do to kill the child process. It would be # great if we could use process groups/sessions to kill off the child, # but that only appears to be capable of delivering SIGHUP. Another # possibility is to record the child PID (and process name?) and the # nonohup PID somewhere and to reap children without controlling # nonohups, perhaps when we nonohup something else. import os, signal, subprocess, sys DEBUG=True def wait(expect=None): if DEBUG: print "Waiting for %d (expecting signal %s)" % (p.pid, expect) res = p.wait() if DEBUG: print "Child %d exited with code %d" % (p.pid, res) if expect and res < 0 and -res == expect: # We sent it this signal. Die normally. sys.exit(0) if res < 0: # It died with a signal on its own. signal.signal(-res, signal.SIG_DFL) os.kill(os.getpid(), -res) # It exited normally. sys.exit(res) def kill(level=0): sig = [signal.SIGTERM, signal.SIGINT, signal.SIGQUIT, signal.SIGKILL][level] # Kill the process harder if we wait for more than 10 seconds signal.signal(signal.SIGALRM, lambda signo,frame: kill(level+1)) signal.alarm(10) if DEBUG: print "Killing %d with %d" % (p.pid, sig) os.kill(p.pid, sig) wait(expect = sig) # Start the child process p = subprocess.Popen(args = sys.argv[1:], stdin = subprocess.PIPE) if DEBUG: print "Started %d" % p.pid # If the child exits, clean up the bits signal.signal(signal.SIGCHLD, lambda signo,frame: wait()) if p.poll(): # Child exited between us starting it and setting up SIGCHLD if DEBUG: print "Child %d exited early" % p.pid wait() # Feed data over stdin pipe until EOF or child terminates on its own while 1: line = sys.stdin.readline() if not len(line): if DEBUG: print "EOF" break p.stdin.write(line) p.stdin.flush() # Re-enable usual wait() handling signal.signal(signal.SIGCHLD, signal.SIG_DFL) if p.poll(): # Already dead. Clean up the bits if DEBUG: print "Child %d exited after EOF" % p.pid wait() # Process still running. Kill it kill()