class StandardIO(object): implements(interfaces.ITransport, interfaces.IProducer, interfaces.IConsumer, interfaces.IHalfCloseableDescriptor) _reader = None _writer = None disconnected = False disconnecting = False def __init__(self, proto, stdin=0, stdout=1): from twisted.internet import reactor self.protocol = proto self._writer = process.ProcessWriter(reactor, self, 'write', stdout) try: self._writer.startReading() except IOError, e: if e.errno == errno.EPERM: # epoll will reject certain file descriptors by raising # EPERM. Most commonly, this means stdout was redirected to # a regular file. raise RuntimeError( "This reactor does not support this type of file " "descriptor (fd %d, mode %d) (for example, epollreactor " "does not support normal files. See #4429)." % (stdout, os.fstat(stdout).st_mode)) raise self._reader = process.ProcessReader(reactor, self, 'read', stdin) self._reader.startReading() self.protocol.makeConnection(self)
def __init__(self, proto, stdin=0, stdout=1, reactor=None): if reactor is None: from twisted.internet import reactor self.protocol = proto self._writer = process.ProcessWriter(reactor, self, "write", stdout) self._reader = process.ProcessReader(reactor, self, "read", stdin) self._reader.startReading() self.protocol.makeConnection(self)
def __init__(self, proto, stdin=0, stdout=1): from twisted.internet import reactor self.protocol = proto self._reader = process.ProcessReader(reactor, self, 'read', stdin) self._reader.startReading() self._writer = process.ProcessWriter(reactor, self, 'write', stdout) self._writer.startReading() self.protocol.makeConnection(self)
def connectionMade(self): debug("outgoing connection made") self.reader = process.ProcessReader(self.reactor, self, "in", 0)
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)