def reapProcess(self): """ Try to reap a process (without blocking) via waitpid. This is called when sigchild is caught or a Process object loses its "connection" (stdout is closed) This ought to result in reaping all zombie processes, since it will be called twice as often as it needs to be. (Unfortunately, this is a slightly experimental approach, since UNIX has no way to be really sure that your process is going to go away w/o blocking. I don't want to block.) """ try: try: pid, status = os.waitpid(self.pid, os.WNOHANG) except OSError, e: if e.errno == errno.ECHILD: # no child process pid = None else: raise except: log.msg('Failed to reap %d:' % self.pid) log.err() pid = None if pid: self.processEnded(status) unregisterReapProcessHandler(pid, self)
def readConnectionLost(self, reason): p = interfaces.IHalfCloseableProtocol(self.protocol, None) if p: try: p.readConnectionLost() except: log.err() self.connectionLost(failure.Failure()) else: self.connectionLost(reason)
def childConnectionLost(self, childFD, reason): # this is called when one of the helpers (ProcessReader or # ProcessWriter) notices their pipe has been closed os.close(self.pipes[childFD].fileno()) del self.pipes[childFD] try: self.proto.childConnectionLost(childFD) except: log.err() self.maybeCallProcessEnded()
def maybeCallProcessEnded(self): """ Call processEnded on protocol after final cleanup. """ if self.proto is not None: reason = self._getReason(self.status) proto = self.proto self.proto = None try: proto.processEnded(Failure(reason)) except: err(None, "unexpected error in processEnded")
def __del__(self): """Print tracebacks and die. If the *last* (and I do mean *last*) callback leaves me in an error state, print a traceback (if said errback is a Failure). """ if self.failResult is not None: log.msg("Unhandled error in Deferred:", isError=True) debugInfo = self._getDebugTracebacks() if debugInfo != '': log.msg("(debug: " + debugInfo + ")", isError=True) log.err(self.failResult)
def _doReadOrWrite(self, selectable, method, dict): try: why = getattr(selectable, method)() handfn = getattr(selectable, 'fileno', None) if not handfn: why = _NO_FILENO elif handfn() == -1: why = _NO_FILEDESC except: why = sys.exc_info()[1] log.err() if why: self._disconnectSelectable(selectable, why, method=="doRead")
def _continueFiring(self, ignored): """ Call the during and after phase triggers for this event. """ self.state = 'BASE' self.finishedBefore = [] for phase in self.during, self.after: while phase: callable, args, kwargs = phase.pop(0) try: callable(*args, **kwargs) except: log.err()
def _closeWriteConnection(self): try: getattr(self.socket, self._socketShutdownMethod)(1) except socket.error: pass p = interfaces.IHalfCloseableProtocol(self.protocol, None) if p: try: p.writeConnectionLost() except: f = failure.Failure() log.err() self.connectionLost(f)
def __init__(self, reactor, executable, args, environment, path, proto, uid=None, gid=None, usePTY=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 pty is None and not isinstance(usePTY, (tuple, list)): # no pty module and we didn't get a pty to use raise NotImplementedError( "cannot use PTYProcess on platforms without the pty module.") abstract.FileDescriptor.__init__(self, reactor) _BaseProcess.__init__(self, proto) if isinstance(usePTY, (tuple, list)): masterfd, slavefd, ttyname = usePTY else: masterfd, slavefd = pty.openpty() ttyname = os.ttyname(slavefd) try: self._fork(path, uid, gid, executable, args, environment, masterfd=masterfd, slavefd=slavefd) except: if not isinstance(usePTY, (tuple, list)): os.close(masterfd) os.close(slavefd) raise # we are now in parent process: os.close(slavefd) fdesc.setNonBlocking(masterfd) self.fd = masterfd self.startReading() self.connected = 1 self.status = -1 try: self.proto.makeConnection(self) except: log.err() registerReapProcessHandler(self.pid, self)
def fireEvent(self): """ Call the triggers added to this event. """ self.state = 'BEFORE' self.finishedBefore = [] beforeResults = [] while self.before: callable, args, kwargs = self.before.pop(0) self.finishedBefore.append((callable, args, kwargs)) try: result = callable(*args, **kwargs) except: log.err() else: if isinstance(result, Deferred): beforeResults.append(result) DeferredList(beforeResults).addCallback(self._continueFiring)
def mainLoop(self): while self._started: try: while self._started: # Advance simulation time in delayed event # processors. self.runUntilCurrent() # XXX f**k this shit.. WHY does twisted's select() # block signals? this is NOT NORMAL self.doIteration(1) #t2 = self.timeout() #t = self.running and t2 #self.doIteration(t) except: log.msg("Unexpected error in main loop.") log.err() else: log.msg('Main loop terminated.')
def registerReapProcessHandler(pid, process): """ Register a process handler for the given pid, in case L{reapAllProcesses} is called. @param pid: the pid of the process. @param process: a process handler. """ if pid in reapProcessHandlers: raise RuntimeError("Try to register an already registered process.") try: auxPID, status = os.waitpid(pid, os.WNOHANG) except: log.msg('Failed to reap %d:' % pid) log.err() auxPID = None if auxPID: process.processEnded(status) else: # if auxPID is 0, there are children but none have exited reapProcessHandlers[pid] = process
def doRead(self): """Called when my socket is ready for reading.""" read = 0 while read < self.maxThroughput: try: data, addr = self.socket.recvfrom(self.maxPacketSize) except socket.error, se: no = se.args[0] if no in (EAGAIN, EINTR, EWOULDBLOCK): return if (no == ECONNREFUSED) or (platformType == "win32" and no == WSAECONNRESET): if self._connectedAddr: self.protocol.connectionRefused() else: raise else: read += len(data) try: self.protocol.datagramReceived(data, addr) except: log.err()
def doSelect(self, timeout): """ Run one iteration of the I/O monitor loop. This will run all selectables who had input or output readiness waiting for them. """ while 1: try: r, w, ignored = _select(self._reads.keys(), self._writes.keys(), [], timeout) break except ValueError, ve: # Possibly a file descriptor has gone negative? log.err() self._preenDescriptors() except TypeError, te: # Something *totally* invalid (object w/o fileno, non-integral # result) was passed log.err() self._preenDescriptors()
def doRead(self): if self.disconnected: # See the comment in the similar check in doWrite below. # Additionally, in order for anything other than returning # CONNECTION_DONE here to make sense, it will probably be necessary # to implement a way to switch back to TCP from TLS (actually, if # we did something other than return CONNECTION_DONE, that would be # a big part of implementing that feature). In other words, the # expectation is that doRead will be called when self.disconnected # is True only when the connection has been lost. It's possible # that the other end could stop speaking TLS and then send us some # non-TLS data. We'll end up ignoring that data and dropping the # connection. There's no unit tests for this check in the cases # where it makes a difference. The test suite only hits this # codepath when it would have otherwise hit the SSL.ZeroReturnError # exception handler below, which has exactly the same behavior as # this conditional. Maybe that's the only case that can ever be # triggered, I'm not sure. -exarkun return main.CONNECTION_DONE if self.writeBlockedOnRead: self.writeBlockedOnRead = 0 self._resetReadWrite() try: return Connection.doRead(self) except SSL.ZeroReturnError: return main.CONNECTION_DONE except SSL.WantReadError: return except SSL.WantWriteError: self.readBlockedOnWrite = 1 Connection.startWriting(self) Connection.stopReading(self) return except SSL.SysCallError, (retval, desc): if ((retval == -1 and desc == 'Unexpected EOF') or retval > 0): return main.CONNECTION_LOST log.err() return main.CONNECTION_LOST
def runUntilCurrent(self): """Run all pending timed calls. """ if self.threadCallQueue: # Keep track of how many calls we actually make, as we're # making them, in case another call is added to the queue # while we're in this loop. count = 0 total = len(self.threadCallQueue) for (f, a, kw) in self.threadCallQueue: try: f(*a, **kw) except: log.err() count += 1 if count == total: break del self.threadCallQueue[:count] if self.threadCallQueue: self.wakeUp() # insert new delayed calls now self._insertNewDelayedCalls() now = self.seconds() while self._pendingTimedCalls and (self._pendingTimedCalls[0].time <= now): call = heappop(self._pendingTimedCalls) if call.cancelled: self._cancellations-=1 continue if call.delayed_time > 0: call.activate_delay() heappush(self._pendingTimedCalls, call) continue try: call.called = 1 call.func(*call.args, **call.kw) except: log.deferr() if hasattr(call, "creator"): e = "\n" e += " C: previous exception occurred in " + \ "a DelayedCall created here:\n" e += " C:" e += "".join(call.creator).rstrip().replace("\n","\n C:") e += "\n" log.msg(e) if (self._cancellations > 50 and self._cancellations > len(self._pendingTimedCalls) >> 1): self._cancellations = 0 self._pendingTimedCalls = [x for x in self._pendingTimedCalls if not x.cancelled] heapify(self._pendingTimedCalls) if self._justStopped: self._justStopped = False self.fireSystemEvent("shutdown")
def __init__(self, reactor, executable, args, environment, path, proto, uid=None, gid=None, childFDs=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() _BaseProcess.__init__(self, proto) 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 _openedPipes = [] def pipe(): r, w = os.pipe() _openedPipes.extend([r, w]) return r, w # fdmap.keys() are filenos of pipes that are used by the child. fdmap = {} # maps childFD to parentFD try: 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 = 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 = 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._fork(path, uid, gid, executable, args, environment, fdmap=fdmap) except: map(os.close, _openedPipes) raise # we are the parent process: 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 = self.processReaderFactory(reactor, self, childFD, parentFD) self.pipes[childFD] = reader if childFDs[childFD] == "w": writer = self.processWriterFactory(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() registerReapProcessHandler(self.pid, self)
def logError(err): log.err(err) return err