def test_registerReapProcessHandler(self): process.registerReapProcessHandler(None, None) [error] = self.flushLoggedErrors() self.assertEqual( type(error.value), self.expected_type, "Wrong error type logged", ) self.assertEqual( str(error.value), self.expected_message, "Wrong error message logged", )
def __init__(self, reactor, executable, args, environment, path, protocol, name): abstract.FileDescriptor.__init__(self, reactor) process._BaseProcess.__init__(self, protocol) masterfd, slavefd = pty.openpty() ttyname = os.ttyname(slavefd) uid=None gid=None try: self._fork(path, uid, gid, executable, args, environment, masterfd=masterfd, slavefd=slavefd) except: os.close(masterfd) os.close(slavefd) raise self._masterfd = masterfd self._slavefd = slavefd self._args = args self._environment = environment self._path = path self._protocol = protocol self._ttyname = ttyname self._executable = executable # we are now in parent process: os.close(slavefd) fdesc.setNonBlocking(masterfd) self.fd = masterfd self.startReading() self.connected = 1 self.status = -1 self._info = info.Info(pid=self.pid, name=name, type='proc', executable=executable) try: self.proto.makeConnection(self) except: log.err() process.registerReapProcessHandler(self.pid, self)
def __init__(self, reactor, command, args, environment, path, proto, uid=None, gid=None, childFDs=None, sessionLeader=False, timeout=None): """Spawn an operating-system process. This is where the hard work of disconnecting all currently open files / forking / executing the new process happens. (This is executed automatically when a Process is instantiated.) This will also run the subprocess as a given user ID and group ID, if specified. (Implementation Note: this doesn't support all the arcane nuances of setXXuid on UNIX: it will assume that either your effective or real UID is 0.) """ if not proto: assert 'r' not in childFDs.values() assert 'w' not in childFDs.values() if not signal.getsignal(signal.SIGCHLD): log.msg("spawnProcess called, but the SIGCHLD handler is not " "installed. This probably means you have not yet " "called reactor.run, or called " "reactor.run(installSignalHandler=0). You will probably " "never see this process finish, and it may become a " "zombie process.") # if you see this message during a unit test, look in # test-standard.xhtml or twisted.test.test_process.SignalMixin # for a workaround self.lostProcess = False settingUID = (uid is not None) or (gid is not None) if settingUID: curegid = os.getegid() currgid = os.getgid() cureuid = os.geteuid() curruid = os.getuid() if uid is None: uid = cureuid if gid is None: gid = curegid # prepare to change UID in subprocess os.setuid(0) os.setgid(0) self.pipes = {} # keys are childFDs, we can sense them closing # values are ProcessReader/ProcessWriters helpers = {} # keys are childFDs # values are parentFDs if childFDs is None: childFDs = { 0: "w", # we write to the child's stdin 1: "r", # we read from their stdout 2: "r", # and we read from their stderr } debug = self.debug if debug: print "childFDs", childFDs # fdmap.keys() are filenos of pipes that are used by the child. fdmap = {} # maps childFD to parentFD for childFD, target in childFDs.items(): if debug: print "[%d]" % childFD, target if target == "r": # we need a pipe that the parent can read from readFD, writeFD = os.pipe() if debug: print "readFD=%d, writeFD%d" % (readFD, writeFD) fdmap[childFD] = writeFD # child writes to this helpers[childFD] = readFD # parent reads from this elif target == "w": # we need a pipe that the parent can write to readFD, writeFD = os.pipe() if debug: print "readFD=%d, writeFD=%d" % (readFD, writeFD) fdmap[childFD] = readFD # child reads from this helpers[childFD] = writeFD # parent writes to this else: assert type(target) == int, '%r should be an int' % (target, ) fdmap[childFD] = target # parent ignores this if debug: print "fdmap", fdmap if debug: print "helpers", helpers # the child only cares about fdmap.values() self.pid = os.fork() if self.pid == 0: # pid is 0 in the child process # do not put *ANY* code outside the try block. The child process # must either exec or _exit. If it gets outside this block (due # to an exception that is not handled here, but which might be # handled higher up), there will be two copies of the parent # running in parallel, doing all kinds of damage. # After each change to this code, review it to make sure there # are no exit paths. try: # stop debugging, if I am! I don't care anymore! sys.settrace(None) # close all parent-side pipes self._setupChild(fdmap) # Make a session/process group leader if requested if sessionLeader: self._setupSession() self._execChild(path, settingUID, uid, gid, command, args, environment) except: # If there are errors, bail and try to write something # descriptive to stderr. # XXX: The parent's stderr isn't necessarily fd 2 anymore, or # even still available # XXXX: however even libc assumes write(2,err) is a useful # thing to attempt try: stderr = os.fdopen(2, 'w') stderr.write( "Upon execvpe %s %s in environment %s\n:" % (command, str(args), "id %s" % id(environment))) traceback.print_exc(file=stderr) stderr.flush() for fd in range(3): os.close(fd) except: pass # make *sure* the child terminates # Did you read the comment about not adding code here? os._exit(1) # we are the parent if settingUID: os.setregid(currgid, curegid) os.setreuid(curruid, cureuid) self.status = -1 # this records the exit status of the child if timeout: self.timeoutCall = reactor.callLater(timeout, self._processTimeout) self.proto = proto # arrange for the parent-side pipes to be read and written for childFD, parentFD in helpers.items(): os.close(fdmap[childFD]) if childFDs[childFD] == "r": reader = process.ProcessReader(reactor, self, childFD, parentFD) self.pipes[childFD] = reader if childFDs[childFD] == "w": writer = process.ProcessWriter(reactor, self, childFD, parentFD, forceReadHack=True) self.pipes[childFD] = writer try: # the 'transport' is used for some compatibility methods if self.proto is not None: self.proto.makeConnection(self) except: log.err() process.registerReapProcessHandler(self.pid, self)