def read(self, fd): if not self.watcher: return w = self.watcher # doRead can cause self.shutdown to be called so keep # a reference to self.watcher def _read(): #Don't call me again, until the data has been read self.notifier.setEnabled(False) why = None try: why = w.doRead() inRead = True except: inRead = False log.err() why = sys.exc_info()[1] if why: self.reactor._disconnectSelectable(w, why, inRead) elif self.watcher: self.notifier.setEnabled(True) # Re enable notification following sucessfull read self.reactor._iterate(fromqt=True) log.callWithLogger(w, _read)
def doWaitForMultipleEvents(self, timeout, reads=reads, writes=writes): log.msg(channel="system", event="iteration", reactor=self) if timeout is None: # timeout = INFINITE timeout = 100 else: timeout = int(timeout * 1000) if not (events or writes): # sleep so we don't suck up CPU time time.sleep(timeout / 1000.0) return canDoMoreWrites = 0 for fd in writes.keys(): if log.callWithLogger(fd, self._runWrite, fd): canDoMoreWrites = 1 if canDoMoreWrites: timeout = 0 handles = events.keys() or [self.dummyEvent] val = MsgWaitForMultipleObjects(handles, 0, timeout, QS_ALLINPUT | QS_ALLEVENTS) if val == WAIT_TIMEOUT: return elif val == WAIT_OBJECT_0 + len(handles): exit = win32gui.PumpWaitingMessages() if exit: self.callLater(0, self.stop) return elif val >= WAIT_OBJECT_0 and val < WAIT_OBJECT_0 + len(handles): fd, action = events[handles[val - WAIT_OBJECT_0]] log.callWithLogger(fd, self._runAction, action, fd)
def ssh_CHANNEL_OPEN(self, packet): channelType, rest = common.getNS(packet) senderChannel, windowSize, maxPacket = struct.unpack('>3L', rest[: 12]) packet = rest[12:] try: channel = self.getChannel(channelType, windowSize, maxPacket, packet) localChannel = self.localChannelID self.localChannelID+=1 channel.id = localChannel self.channels[localChannel] = channel self.channelsToRemoteChannel[channel] = senderChannel self.localToRemoteChannel[localChannel] = senderChannel self.transport.sendPacket(MSG_CHANNEL_OPEN_CONFIRMATION, struct.pack('>4L', senderChannel, localChannel, channel.localWindowSize, channel.localMaxPacket)+channel.specificData) log.callWithLogger(channel, channel.channelOpen, '') except Exception, e: log.msg('channel open failed') log.err(e) if isinstance(e, error.ConchError): reason, textualInfo = e.args[0], e.data else: reason = OPEN_CONNECT_FAILED textualInfo = "unknown failure" self.transport.sendPacket(MSG_CHANNEL_OPEN_FAILURE, struct.pack('>2L', senderChannel, reason)+ \ common.NS(textualInfo)+common.NS(''))
def doRead(self): """ Some data is available for reading on ZeroMQ descriptor. ZeroMQ is signalling that we should process some events, we're starting to receive incoming messages. Implementation of :tm:`IReadDescriptor <internet.interfaces.IReadDescriptor>`. """ if self.read_scheduled is not None: if not self.read_scheduled.called: self.read_scheduled.cancel() self.read_scheduled = None while True: if self.factory is None: # disconnected return events = self.socket.get(constants.EVENTS) if (events & constants.POLLIN) != constants.POLLIN: return try: message = self._readMultipart() except error.ZMQError as e: if e.errno == constants.EAGAIN: continue raise e log.callWithLogger(self, self.messageReceived, message)
def ssh_CHANNEL_FAILURE(self, packet): localChannel = struct.unpack('>L', packet[: 4])[0] if self.deferreds.get(localChannel): d = self.deferreds[localChannel].pop(0) log.callWithLogger(self.channels[localChannel], d.errback, error.ConchError('channel request failed'))
def doKEvent(self, timeout): """ Poll the kqueue for new events. """ if timeout is None: timeout = 1 try: events = self._kq.control([], len(self._selectables), timeout) except OSError as e: # Since this command blocks for potentially a while, it's possible # EINTR can be raised for various reasons (for example, if the user # hits ^C). if e.errno == errno.EINTR: return else: raise _drdw = self._doWriteOrRead for event in events: fd = event.ident try: selectable = self._selectables[fd] except KeyError: # Handles the infrequent case where one selectable's # handler disconnects another. continue else: log.callWithLogger(selectable, _drdw, selectable, fd, event)
def doPoll(self, timeout): """ Poll the poller for new events. """ if timeout is None: timeout = -1 # Wait indefinitely. try: # Limit the number of events to the number of io objects we're # currently tracking (because that's maybe a good heuristic) and # the amount of time we block to the value specified by our # caller. l = self._poller.poll(timeout, len(self._selectables)) except IOError as err: if err.errno == errno.EINTR: return # See epoll_wait(2) for documentation on the other conditions # under which this can fail. They can only be due to a serious # programming error on our part, so let's just announce them # loudly. raise _drdw = self._doReadOrWrite for fd, event in l: try: selectable = self._selectables[fd] except KeyError: pass else: log.callWithLogger(selectable, _drdw, selectable, fd, event)
def dataReceived(self, data): self.buf = self.buf+data if not self.gotVersion: parts = self.buf.split('\n') for p in parts: if p[: 4] == 'SSH-': self.gotVersion = 1 self.otherVersionString = p.strip() if p.split('-')[1]not in('1.99', '2.0'): # bad version self.sendDisconnect(DISCONNECT_PROTOCOL_VERSION_NOT_SUPPORTED, 'bad version %s'%p.split('-')[1]) return i = parts.index(p) self.buf = '\n'.join(parts[i+1:]) packet = self.getPacket() while packet: messageNum = ord(packet[0]) if messageNum < 50: messageType = messages[messageNum][4:] f = getattr(self, 'ssh_%s'%messageType, None) if f: f(packet[1:]) else: log.msg("couldn't handle %s"%messageType) log.msg(repr(packet[1:])) self.sendUnimplemented() elif self.service: log.callWithLogger(self.service, self.service.packetReceived, ord(packet[0]), packet[1:]) else: log.msg("couldn't handle %s"%messageNum) log.msg(repr(packet[1:])) self.sendUnimplemented() packet = self.getPacket()
def disconnectAll(self): """Disconnect every reader, and writer in the system. """ selectables = self.removeAll() for reader in selectables: log.callWithLogger(reader, reader.connectionLost, failure.Failure(main.CONNECTION_LOST))
def doKEvent(self, timeout): """ Poll the kqueue for new events. """ if timeout is None: timeout = 1 try: l = self._kq.control([], len(self._selectables), timeout) except OSError as e: if e[0] == errno.EINTR: return else: raise _drdw = self._doWriteOrRead for event in l: fd = event.ident try: selectable = self._selectables[fd] except KeyError: # Handles the infrequent case where one selectable's # handler disconnects another. continue else: log.callWithLogger(selectable, _drdw, selectable, fd, event)
def ssh_CHANNEL_DATA(self, packet): """ The other side is sending us data. Payload:: uint32 local channel number string data Check to make sure the other side hasn't sent too much data (more than what's in the window, or more than the maximum packet size). If they have, close the channel. Otherwise, decrease the available window and pass the data to the channel's dataReceived(). """ localChannel, dataLength = struct.unpack('>2L', packet[:8]) channel = self.channels[localChannel] # XXX should this move to dataReceived to put client in charge? if (dataLength > channel.localWindowLeft or dataLength > channel.localMaxPacket): # more data than we want log.callWithLogger(channel, log.msg, 'too much data') self.sendClose(channel) return #packet = packet[:channel.localWindowLeft+4] data = common.getNS(packet[4:])[0] channel.localWindowLeft -= dataLength if channel.localWindowLeft < channel.localWindowSize / 2: self.adjustWindow(channel, channel.localWindowSize - \ channel.localWindowLeft) #log.msg('local window left: %s/%s' % (channel.localWindowLeft, # channel.localWindowSize)) log.callWithLogger(channel, channel.dataReceived, data)
def ssh_CHANNEL_EXTENDED_DATA(self, packet): """ The other side is sending us exteneded data. Payload:: uint32 local channel number uint32 type code string data Check to make sure the other side hasn't sent too much data (more than what's in the window, or or than the maximum packet size). If they have, close the channel. Otherwise, decrease the available window and pass the data and type code to the channel's extReceived(). """ localChannel, typeCode, dataLength = struct.unpack('>3L', packet[:12]) channel = self.channels[localChannel] if (dataLength > channel.localWindowLeft or dataLength > channel.localMaxPacket): log.callWithLogger(channel, log.msg, 'too much extdata') self.sendClose(channel) return data = common.getNS(packet[8:])[0] channel.localWindowLeft -= dataLength if channel.localWindowLeft < channel.localWindowSize / 2: self.adjustWindow(channel, channel.localWindowSize - channel.localWindowLeft) log.callWithLogger(channel, channel.extReceived, typeCode, data)
def _ioEventCallback(self, source, condition): """ Called by event loop when an I/O event occurs. """ log.callWithLogger( source, self._doReadOrWrite, source, source, condition) return True # True = don't auto-remove the source
def _invoke_callback(self, fd, events): if fd not in self._fds: return (reader, writer) = self._fds[fd] if reader: err = None if reader.fileno() == -1: err = error.ConnectionLost() elif events & IOLoop.READ: err = log.callWithLogger(reader, reader.doRead) if err is None and events & IOLoop.ERROR: err = error.ConnectionLost() if err is not None: self.removeReader(reader) reader.readConnectionLost(failure.Failure(err)) if writer: err = None if writer.fileno() == -1: err = error.ConnectionLost() elif events & IOLoop.WRITE: err = log.callWithLogger(writer, writer.doWrite) if err is None and events & IOLoop.ERROR: err = error.ConnectionLost() if err is not None: self.removeWriter(writer) writer.writeConnectionLost(failure.Failure(err))
def ssh_CHANNEL_OPEN_FAILURE(self, packet): localChannel, reasonCode = struct.unpack('>2L', packet[: 8]) reasonDesc = common.getNS(packet[8:])[0] channel = self.channels[localChannel] del self.channels[localChannel] channel.conn = self reason = error.ConchError(reasonDesc, reasonCode) log.callWithLogger(channel, channel.openFailed, reason)
def _runPendingEvents(self, pending_events): # pending_events is a list of (fd, mode) pairs. while pending_events: fd, mode = pending_events.pop() if fd in self._selectables: selectable = self._selectables[fd] log.callWithLogger(selectable, self._doReadOrWrite, fd, mode, selectable)
def ssh_CHANNEL_CLOSE(self, packet): localChannel = struct.unpack('>L', packet[: 4])[0] channel = self.channels[localChannel] if channel.remoteClosed: return log.callWithLogger(channel, channel.closeReceived) channel.remoteClosed = 1 if channel.localClosed and channel.remoteClosed: self.channelClosed(channel)
def ssh_CHANNEL_OPEN_CONFIRMATION(self, packet): localChannel, remoteChannel, windowSize, maxPacket = struct.unpack('>4L', packet[: 16]) specificData = packet[16:] channel = self.channels[localChannel] channel.conn = self self.localToRemoteChannel[localChannel] = remoteChannel self.channelsToRemoteChannel[channel] = remoteChannel channel.remoteWindowLeft = windowSize channel.remoteMaxPacket = maxPacket log.callWithLogger(channel, channel.channelOpen, specificData)
def ssh_CHANNEL_EOF(self, packet): """ The other side is not sending any more data. Payload:: uint32 local channel number Notify the channel by calling its eofReceived() method. """ localChannel = struct.unpack('>L', packet[:4])[0] channel = self.channels[localChannel] log.callWithLogger(channel, channel.eofReceived)
def resolve_cb(result): if add_service: method = protocol.addService else: # remove the service from the list before calling back the proto _registered_services.remove(info) method = protocol.removeService # call it with the reader instance to make things cleaner log.callWithLogger(reader, method, info)
def _cbGotChannel(self, channel, senderChannel, packet): localChannel = self.localChannelID self.localChannelID += 1 channel.id = localChannel self.channels[localChannel] = channel self.channelsToRemoteChannel[channel] = senderChannel self.localToRemoteChannel[localChannel] = senderChannel self.transport.sendPacket(connection.MSG_CHANNEL_OPEN_CONFIRMATION, struct.pack('>4L', senderChannel, localChannel, channel.localWindowSize, channel.localMaxPacket)+channel.specificData) log.callWithLogger(channel, channel.channelOpen, packet)
def ssh_CHANNEL_WINDOW_ADJUST(self, packet): """ The other side is adding bytes to its window. Payload:: uint32 local channel number uint32 bytes to add Call the channel's addWindowBytes() method to add new bytes to the remote window. """ localChannel, bytesToAdd = struct.unpack('>2L', packet[:8]) channel = self.channels[localChannel] log.callWithLogger(channel, channel.addWindowBytes, bytesToAdd)
def ssh_CHANNEL_SUCCESS(self, packet): """ Our channel request to the other other side succeeded. Payload:: uint32 local channel number Get the C{Deferred} out of self.deferreds and call it back. """ localChannel = struct.unpack('>L', packet[:4])[0] if self.deferreds.get(localChannel): d = self.deferreds[localChannel].pop(0) log.callWithLogger(self.channels[localChannel], d.callback, '')
def read(self, sock): w = self.watcher def _read(): why = None try: why = w.doRead() except: log.err() why = exc_info()[1] if why: self.reactor._disconnectSelectable(w, why, True) log.callWithLogger(w, _read) self.reactor.simulate()
def write(self, sock): w = self.watcher def _write(): why = None try: why = w.doWrite() except: log.err() why = exc_info()[1] if why: self.reactor._disconnectSelectable(w, why, False) log.callWithLogger(w, _write) self.reactor.simulate()
def ssh_CHANNEL_EXTENDED_DATA(self, packet): localChannel, typeCode, dataLength = struct.unpack('>3L', packet[: 12]) channel = self.channels[localChannel] if dataLength > channel.localWindowLeft or \ dataLength > channel.localMaxPacket: log.callWithLogger(channel, lambda s=self,c=channel: log.msg('too much extdata') and s.sendClose(c)) return data = common.getNS(packet[8:])[0] channel.localWindowLeft -= dataLength if channel.localWindowLeft < channel.localWindowSize/2: self.adjustWindow(channel, channel.localWindowSize - \ channel.localWindowLeft) log.callWithLogger(channel, channel.extReceived, typeCode, data)
def channelClosed(self, channel): """ Called when a channel is closed. It clears the local state related to the channel, and calls channel.closed(). MAKE SURE YOU CALL THIS METHOD, even if you subclass L{SSHConnection}. If you don't, things will break mysteriously. """ channel.localClosed = channel.remoteClosed = 1 del self.localToRemoteChannel[channel.id] del self.channels[channel.id] del self.channelsToRemoteChannel[channel] self.deferreds[channel.id] = [] log.callWithLogger(channel, channel.closed)
def ssh_CHANNEL_FAILURE(self, packet): """ Our channel request to the other side failed. Payload:: uint32 local channel number Get the C{Deferred} out of self.deferreds and errback it with a C{error.ConchError}. """ localChannel = struct.unpack('>L', packet[:4])[0] if self.deferreds.get(localChannel): d = self.deferreds[localChannel].pop(0) log.callWithLogger(self.channels[localChannel], d.errback, error.ConchError('channel request failed'))
def disconnectAll(self): """Disconnect every reader, and writer in the system. """ selectables = self.removeAll() for reader in selectables: log.callWithLogger(reader, reader.connectionLost, failure.Failure(main.CONNECTION_LOST)) mdata = { 'event':'disconnectAll' , 'msg' : 'Disconnect every reader, and writer in the system.' , 'level' : 'critical' } sender.send(str(mdata) , MONITOR_ADDR)
def ssh_CHANNEL_CLOSE(self, packet): """ The other side is closing its end; it does not want to receive any more data. Payload:: uint32 local channel number Notify the channnel by calling its closeReceived() method. If the channel has also sent a close message, call self.channelClosed(). """ localChannel = struct.unpack('>L', packet[:4])[0] channel = self.channels[localChannel] log.callWithLogger(channel, channel.closeReceived) channel.remoteClosed = True if channel.localClosed and channel.remoteClosed: self.channelClosed(channel)
def doRead(self): """ Some data is available for reading on your descriptor. ZeroMQ is signalling that we should process some events, we're starting to send queued messages and to receive incoming messages. Note that the ZeroMQ FD is used in an edge-triggered manner. Consequently, this function must read all pending messages before returning. Part of L{IReadDescriptor}. """ if self._ctx is None: # disconnected return while self._queue and self._zsock is not None: try: self._zsock.send_multipart(self._queue[0], constants.NOBLOCK) self._queue.popleft() except error.ZMQError as e: if e.errno == constants.EAGAIN: break raise e while self._zsock is not None: try: msg_list = self._zsock.recv_multipart(constants.NOBLOCK) log.callWithLogger(self, self.messageReceived, msg_list) except error.ZMQError as e: if e.errno == constants.EAGAIN: break # This exception can be thrown during socket closing process if e.errno == 156384763 or str( e ) == 'Operation cannot be accomplished in current state': break # Seen in 3.2 for an unknown reason if e.errno == 95: break raise e
def ssh_CHANNEL_OPEN(self, packet): """ The other side wants to get a channel. Payload:: string channel name uint32 remote channel number uint32 remote window size uint32 remote maximum packet size <channel specific data> We get a channel from self.getChannel(), give it a local channel number and notify the other side. Then notify the channel by calling its channelOpen method. """ channelType, rest = common.getNS(packet) senderChannel, windowSize, maxPacket = struct.unpack('>3L', rest[:12]) packet = rest[12:] try: channel = self.getChannel(channelType, windowSize, maxPacket, packet) localChannel = self.localChannelID self.localChannelID += 1 channel.id = localChannel self.channels[localChannel] = channel self.channelsToRemoteChannel[channel] = senderChannel self.localToRemoteChannel[localChannel] = senderChannel self.transport.sendPacket( MSG_CHANNEL_OPEN_CONFIRMATION, struct.pack('>4L', senderChannel, localChannel, channel.localWindowSize, channel.localMaxPacket) + channel.specificData) log.callWithLogger(channel, channel.channelOpen, packet) except Exception as e: log.err(e, 'channel open failed') if isinstance(e, error.ConchError): textualInfo, reason = e.args if isinstance(textualInfo, (int, long)): # See #3657 and #3071 textualInfo, reason = reason, textualInfo else: reason = OPEN_CONNECT_FAILED textualInfo = "unknown failure" self.transport.sendPacket( MSG_CHANNEL_OPEN_FAILURE, struct.pack('>2L', senderChannel, reason) + common.NS(networkString(textualInfo)) + common.NS(b''))
def channelClosed(self, channel): """ Called when a channel is closed. It clears the local state related to the channel, and calls channel.closed(). MAKE SURE YOU CALL THIS METHOD, even if you subclass L{SSHConnection}. If you don't, things will break mysteriously. @type channel: L{SSHChannel} """ if channel in self.channelsToRemoteChannel: # actually open channel.localClosed = channel.remoteClosed = True del self.localToRemoteChannel[channel.id] del self.channels[channel.id] del self.channelsToRemoteChannel[channel] for d in self.deferreds.pop(channel.id, []): d.errback(error.ConchError("Channel closed.")) log.callWithLogger(channel, channel.closed)
def write(self, sock): w = self.watcher self.setEnabled(False) def _write(): why = None try: why = w.doWrite() except: log.err() why = sys.exc_info()[1] if why: self.reactor._disconnectSelectable(w, why, False) elif self.watcher: self.setEnabled(True) log.callWithLogger(w, _write) self.reactor.reactorInvocation()
def ssh_CHANNEL_DATA(self, packet): localChannel, dataLength = struct.unpack('>2L', packet[:8]) channel = self.channels[localChannel] # XXX should this move to dataReceived to put client in charge? if dataLength > channel.localWindowLeft or \ dataLength > channel.localMaxPacket: # more data than we want log.callWithLogger(channel, lambda s=self, c=channel: log.msg( 'too much data') and s.sendClose(c)) return #packet = packet[:channel.localWindowLeft+4] data = common.getNS(packet[4:])[0] channel.localWindowLeft -= dataLength if channel.localWindowLeft < channel.localWindowSize / 2: self.adjustWindow(channel, channel.localWindowSize - \ channel.localWindowLeft) #log.msg('local window left: %s/%s' % (channel.localWindowLeft, # channel.localWindowSize)) log.callWithLogger(channel, channel.dataReceived, data)
def write(self, sock): if not self.watcher: return w = self.watcher def _write(): why = None self.notifier.setEnabled(False) try: why = w.doWrite() except: log.err() why = sys.exc_info()[1] if why: self.reactor._disconnectSelectable(w, why, False) elif self.watcher: self.notifier.setEnabled(True) self.reactor._iterate(fromqt=True) log.callWithLogger(w, _write)
def write(self, sock): self.notifier.setEnabled(False) if not self.watcher: return w = self.watcher def _write(): try: data = w.doWrite() if data: self.qt_reactor._disconnectSelectable(w, data, False) elif self.watcher: self.notifier.setEnabled(True) except: log.err() self.qt_reactor._disconnectSelectable(w, sys.exc_info()[1], False) self.qt_reactor._iterate(None, fromqt=True) log.callWithLogger(w, _write)
def doIteration(self, timeout): """ Poll the IO completion port for new events. """ # This function sits and waits for an IO completion event. # # There are two requirements: process IO events as soon as they arrive # and process ctrl-break from the user in a reasonable amount of time. # # There are three kinds of waiting. # 1) GetQueuedCompletionStatus (self.port.getEvent) to wait for IO # events only. # 2) Msg* family of wait functions that can stop waiting when # ctrl-break is detected (then, I think, Python converts it into a # KeyboardInterrupt) # 3) *Ex family of wait functions that put the thread into an # "alertable" wait state which is supposedly triggered by IO completion # # 2) and 3) can be combined. Trouble is, my IO completion is not # causing 3) to trigger, possibly because I do not use an IO completion # callback. Windows is weird. # There are two ways to handle this. I could use MsgWaitForSingleObject # here and GetQueuedCompletionStatus in a thread. Or I could poll with # a reasonable interval. Guess what! Threads are hard. processed_events = 0 if timeout is None: timeout = MAX_TIMEOUT else: timeout = min(MAX_TIMEOUT, int(1000 * timeout)) rc, numBytes, key, evt = self.port.getEvent(timeout) while 1: if rc == WAIT_TIMEOUT: break if key != KEY_WAKEUP: assert key == KEY_NORMAL log.callWithLogger( evt.owner, self._callEventCallback, rc, numBytes, evt ) processed_events += 1 if processed_events >= EVENTS_PER_LOOP: break rc, numBytes, key, evt = self.port.getEvent(0)
def setup(reactor, latency_func): import sys if sys.platform != 'linux2': return from twisted.internet import epollreactor from twisted.python import log import errno, time global g_latency_func g_latency_func = latency_func assert isinstance(reactor, epollreactor.EPollReactor) if 1: print 'installing EPollReactor latency measurement wrapper' def mypoll(self, timeout): if timeout is None: timeout = -1 # Wait indefinitely. try: if hasattr(self._poller, 'poll'): # NEW python API l = self._poller.poll(timeout, len(self._selectables)) else: # OLD twisted implementation l = self._poller.wait(len(self._selectables), int(1000*timeout)) except IOError, err: if err.errno == errno.EINTR: return raise _drdw = self._doReadOrWrite start_time = time.time() for fd, event in l: try: selectable = self._selectables[fd] except KeyError: pass else: log.callWithLogger(selectable, _drdw, selectable, fd, event) end_time = time.time() g_latency_func('ALL', end_time - start_time) epollreactor.EPollReactor.doIteration = mypoll
def read(self, sock): w = self.watcher #self.setEnabled(False) # ??? do I need this? def _read(): why = None try: why = w.doRead() except: log.err() why = sys.exc_info()[1] if why: self.reactor._disconnectSelectable(w, why, True) elif self.watcher: pass #self.setEnabled(True) log.callWithLogger(w, _read) self.reactor.reactorInvocation()
def _invoke_callback(self, handle, events, poll_error): reader, writer = self._fds[handle.fileno()] if reader: err = None if reader.fileno() == -1: err = error.ConnectionLost() elif events & pyuv.UV_READABLE or poll_error is not None: err = log.callWithLogger(reader, reader.doRead) if err is not None: self.removeReader(reader) reader.readConnectionLost(failure.Failure(err)) if writer: err = None if writer.fileno() == -1: err = error.ConnectionLost() elif events & pyuv.UV_WRITABLE or poll_error is not None: err = log.callWithLogger(writer, writer.doWrite) if err is not None: self.removeWriter(writer) writer.writeConnectionLost(failure.Failure(err))
def run(self, save=1, installSignalHandlers=1): """run(save=1, installSignalHandlers=1) Run this application, running the main loop if necessary. If 'save' is true, then when this Application is shut down, it will be persisted to a pickle. 'installSignalHandlers' is passed through to reactor.run(), the function that starts the mainloop. """ from twisted.internet import reactor if not self._boundPorts: self.bindPorts() self._save = save reactor.addSystemEventTrigger('before', 'shutdown', self._beforeShutDown) reactor.addSystemEventTrigger('after', 'shutdown', self._afterShutDown) global theApplication theApplication = self log.callWithLogger(self, reactor.run, installSignalHandlers=installSignalHandlers)
def doPoll(self, timeout): """Poll the poller for new events.""" if timeout is not None: timeout = int(timeout * 1000) # convert seconds to milliseconds try: l = self._poller.poll(timeout) except SelectError as e: if e.args[0] == errno.EINTR: return else: raise _drdw = self._doReadOrWrite for fd, event in l: try: selectable = self._selectables[fd] except KeyError: # Handles the infrequent case where one selectable's # handler disconnects another. continue log.callWithLogger(selectable, _drdw, selectable, fd, event)
def read(self, fd): self.notifier.setEnabled(False) if not self.watcher: return w = self.watcher # doRead can cause self.shutdown to be called so keep a # reference to self.watcher def _read(): try: data = w.doRead() if data: self.qt_reactor._disconnectSelectable(w, data, True) elif self.watcher: self.notifier.setEnabled(True) except: log.err() self.qt_reactor._disconnectSelectable(w, sys.exc_info()[1], False) self.qt_reactor._iterate(None, fromqt=True) log.callWithLogger(w, _read)
def doRead(self): """ Some data is available for reading on your descriptor. ZeroMQ is signalling that we should process some events. Part of L{IReadDescriptor}. """ events = self.socket.getsockopt(constants.EVENTS) if (events & constants.POLLIN) == constants.POLLIN: while True: try: message = self._readMultipart() except error.ZMQError as e: if e.errno == constants.EAGAIN: break raise e log.callWithLogger(self, self.messageReceived, message) if (events & constants.POLLOUT) == constants.POLLOUT: self._startWriting()
def read(self, fd): if not self.watcher: return w = self.watcher # doRead can cause self.shutdown to be called so keep a reference to self.watcher def _read(): #Don't call me again, until the data has been read self.notifier.setEnabled(False) why = None try: why = w.doRead() inRead = True except: inRead = False log.err() why = sys.exc_info()[1] if why: self.reactor._disconnectSelectable(w, why, inRead) elif self.watcher: self.notifier.setEnabled(True) # Re enable notification following sucessfull read self.reactor._iterate(fromqt=True) log.callWithLogger(w, _read)
def ssh_CHANNEL_OPEN_CONFIRMATION(self, packet): """ The other side accepted our MSG_CHANNEL_OPEN request. Payload:: uint32 local channel number uint32 remote channel number uint32 remote window size uint32 remote maximum packet size <channel specific data> Find the channel using the local channel number and notify its channelOpen method. """ (localChannel, remoteChannel, windowSize, maxPacket) = struct.unpack('>4L', packet[:16]) specificData = packet[16:] channel = self.channels[localChannel] channel.conn = self self.localToRemoteChannel[localChannel] = remoteChannel self.channelsToRemoteChannel[channel] = remoteChannel channel.remoteWindowLeft = windowSize channel.remoteMaxPacket = maxPacket log.callWithLogger(channel, channel.channelOpen, specificData)
def doRead(self): """ Some data is available for reading on ZeroMQ descriptor. ZeroMQ is signalling that we should process some events, we're starting to receive incoming messages. Implementation of :class:`IReadDescriptor <twisted.internet.interfaces.IReadDescriptor>`. """ if self.shutted_down: return if self.read_scheduled is not None: if not self.read_scheduled.called: self.read_scheduled.cancel() self.read_scheduled = None unpickler = ZmqConnection.Unpickler() while True: if self.factory is None: # disconnected return events = self.socket.get(constants.EVENTS) if (events & constants.POLLIN) != constants.POLLIN: return timestamp = time.time() try: message = self._readMultipart(unpickler) except error.ZMQError as e: if e.errno == constants.EAGAIN: continue raise e finally: self._last_read_time = time.time() - timestamp log.callWithLogger(self, self.messageReceived, message)
def dispatchMessage(self, messageNum, payload): """ Send a received message to the appropriate method. @type messageNum: C{int} @type payload: c{str} """ if messageNum < 50 and messageNum in messages: messageType = messages[messageNum][4:] f = getattr(self, 'ssh_%s' % messageType, None) if f is not None: f(payload) else: log.msg("couldn't handle %s" % messageType) log.msg(repr(payload)) self.sendUnimplemented() elif self.service: log.callWithLogger(self.service, self.service.packetReceived, messageNum, payload) else: log.msg("couldn't handle %s" % messageNum) log.msg(repr(payload)) self.sendUnimplemented()
def _invoke_callback(self, fd, events): (reader, writer) = self._fds[fd] if reader: err = None if reader.fileno() == -1: err = error.ConnectionLost() elif events & IOLoop.READ: err = log.callWithLogger(reader, reader.doRead) if err is None and events & IOLoop.ERROR: err = error.ConnectionLost() if err is not None: self.removeReader(reader) reader.readConnectionLost(failure.Failure(err)) if writer: err = None if writer.fileno() == -1: err = error.ConnectionLost() elif events & IOLoop.WRITE: err = log.callWithLogger(writer, writer.doWrite) if err is None and events & IOLoop.ERROR: err = error.ConnectionLost() if err is not None: self.removeWriter(writer) writer.writeConnectionLost(failure.Failure(err))
def doPoll(self, timeout, reads=reads, writes=writes, selectables=selectables, select=select, log=log, POLLIN=select.POLLIN, POLLOUT=select.POLLOUT): """Poll the poller for new events.""" if timeout is not None: timeout = int(timeout * 1000 + 1) # convert seconds to milliseconds try: l = poller.poll(timeout) if l is None: if self.running: self.stop() l = [] except select.error as e: if e[0] == errno.EINTR: return else: raise _drdw = self._doReadOrWrite for fd, event in l: try: selectable = selectables[fd] except KeyError: # Handles the infrequent case where one selectable's # handler disconnects another. continue log.callWithLogger(selectable, _drdw, selectable, fd, event, POLLIN, POLLOUT, log)
def ssh_CHANNEL_REQUEST(self, packet): localChannel = struct.unpack('>L', packet[:4])[0] requestType, rest = common.getNS(packet[4:]) wantReply = ord(rest[0]) channel = self.channels[localChannel] d = log.callWithLogger(channel, channel.requestReceived, requestType, rest[1:]) if wantReply: if isinstance(d, defer.Deferred): d.addCallback(self._cbChannelRequest, localChannel) d.addErrback(self._ebChannelRequest, localChannel) elif d: self._cbChannelRequest(None, localChannel) else: self._ebChannelRequest(None, localChannel)
def log(message): if len(tlog.theLogPublisher.observers) == 1: print '[status.py] %s' % message else: tlog.callWithLogger(StatusLogger, tlog.msg, message)
def callback(self, source, condition): log.callWithLogger(source, self._doReadOrWrite, source, condition) self.simulate() # fire Twisted timers return 1 # 1=don't auto-remove the source
class PollReactor(posixbase.PosixReactorBase): """ A reactor that uses poll(2). @ivar _poller: A L{poll} which will be used to check for I/O readiness. @ivar _selectables: A dictionary mapping integer file descriptors to instances of L{FileDescriptor} which have been registered with the reactor. All L{FileDescriptors} which are currently receiving read or write readiness notifications will be present as values in this dictionary. @ivar _reads: A dictionary mapping integer file descriptors to arbitrary values (this is essentially a set). Keys in this dictionary will be registered with C{_poller} for read readiness notifications which will be dispatched to the corresponding L{FileDescriptor} instances in C{_selectables}. @ivar _writes: A dictionary mapping integer file descriptors to arbitrary values (this is essentially a set). Keys in this dictionary will be registered with C{_poller} for write readiness notifications which will be dispatched to the corresponding L{FileDescriptor} instances in C{_selectables}. """ implements(IReactorFDSet) def __init__(self): """ Initialize polling object, file descriptor tracking dictionaries, and the base class. """ self._poller = poll() self._selectables = {} self._reads = {} self._writes = {} posixbase.PosixReactorBase.__init__(self) def _updateRegistration(self, fd): """Register/unregister an fd with the poller.""" try: self._poller.unregister(fd) except KeyError: pass mask = 0 if fd in self._reads: mask = mask | POLLIN if fd in self._writes: mask = mask | POLLOUT if mask != 0: self._poller.register(fd, mask) else: if fd in self._selectables: del self._selectables[fd] def _dictRemove(self, selectable, mdict): try: # the easy way fd = selectable.fileno() # make sure the fd is actually real. In some situations we can get # -1 here. mdict[fd] except: # the hard way: necessary because fileno() may disappear at any # moment, thanks to python's underlying sockets impl for fd, fdes in self._selectables.items(): if selectable is fdes: break else: # Hmm, maybe not the right course of action? This method can't # fail, because it happens inside error detection... return if fd in mdict: del mdict[fd] self._updateRegistration(fd) def addReader(self, reader): """Add a FileDescriptor for notification of data available to read. """ fd = reader.fileno() if fd not in self._reads: self._selectables[fd] = reader self._reads[fd] = 1 self._updateRegistration(fd) def addWriter(self, writer): """Add a FileDescriptor for notification of data available to write. """ fd = writer.fileno() if fd not in self._writes: self._selectables[fd] = writer self._writes[fd] = 1 self._updateRegistration(fd) def removeReader(self, reader): """Remove a Selectable for notification of data available to read. """ return self._dictRemove(reader, self._reads) def removeWriter(self, writer): """Remove a Selectable for notification of data available to write. """ return self._dictRemove(writer, self._writes) def removeAll(self): """ Remove all selectables, and return a list of them. """ return self._removeAll([self._selectables[fd] for fd in self._reads], [self._selectables[fd] for fd in self._writes]) def doPoll(self, timeout): """Poll the poller for new events.""" if timeout is not None: timeout = int(timeout * 1000) # convert seconds to milliseconds try: l = self._poller.poll(timeout) except SelectError, e: if e[0] == errno.EINTR: return else: raise _drdw = self._doReadOrWrite for fd, event in l: try: selectable = self._selectables[fd] except KeyError: # Handles the infrequent case where one selectable's # handler disconnects another. continue log.callWithLogger(selectable, _drdw, selectable, fd, event)
def _socketCallback( self, cfSocket, callbackType, ignoredAddress, ignoredData, context ): """ The socket callback issued by CFRunLoop. This will issue C{doRead} or C{doWrite} calls to the L{IReadDescriptor} and L{IWriteDescriptor} registered with the file descriptor that we are being notified of. @param cfSocket: The C{CFSocket} which has got some activity. @param callbackType: The type of activity that we are being notified of. Either C{kCFSocketReadCallBack} or C{kCFSocketWriteCallBack}. @param ignoredAddress: Unused, because this is not used for either of the callback types we register for. @param ignoredData: Unused, because this is not used for either of the callback types we register for. @param context: The data associated with this callback by C{CFSocketCreateWithNative} (in C{CFReactor._watchFD}). A 2-tuple of C{(int, CFRunLoopSource)}. """ (fd, smugglesrc) = context if fd not in self._fdmap: # Spurious notifications seem to be generated sometimes if you # CFSocketDisableCallBacks in the middle of an event. I don't know # about this FD, any more, so let's get rid of it. CFRunLoopRemoveSource(self._cfrunloop, smugglesrc, kCFRunLoopCommonModes) return src, skt, readWriteDescriptor, rw = self._fdmap[fd] def _drdw(): why = None isRead = False try: if readWriteDescriptor.fileno() == -1: why = _NO_FILEDESC else: isRead = callbackType == kCFSocketReadCallBack # CFSocket seems to deliver duplicate read/write # notifications sometimes, especially a duplicate # writability notification when first registering the # socket. This bears further investigation, since I may # have been mis-interpreting the behavior I was seeing. # (Running the full Twisted test suite, while thorough, is # not always entirely clear.) Until this has been more # thoroughly investigated , we consult our own # reading/writing state flags to determine whether we # should actually attempt a doRead/doWrite first. -glyph if isRead: if rw[_READ]: why = readWriteDescriptor.doRead() else: if rw[_WRITE]: why = readWriteDescriptor.doWrite() except BaseException: why = sys.exc_info()[1] log.err() if why: self._disconnectSelectable(readWriteDescriptor, why, isRead) log.callWithLogger(readWriteDescriptor, _drdw)
def doWaitForMultipleEvents(self, timeout): log.msg(channel='system', event='iteration', reactor=self) if timeout is None: timeout = 100 # Keep track of whether we run any application code before we get to the # MsgWaitForMultipleObjects. If so, there's a chance it will schedule a # new timed call or stop the reactor or do something else that means we # shouldn't block in MsgWaitForMultipleObjects for the full timeout. ranUserCode = False # If any descriptors are trying to close, try to get them out of the way # first. for reader in self._closedAndReading.keys(): ranUserCode = True self._runAction('doRead', reader) for fd in self._writes.keys(): ranUserCode = True log.callWithLogger(fd, self._runWrite, fd) if ranUserCode: # If application code *might* have scheduled an event, assume it # did. If we're wrong, we'll get back here shortly anyway. If # we're right, we'll be sure to handle the event (including reactor # shutdown) in a timely manner. timeout = 0 if not (self._events or self._writes): # sleep so we don't suck up CPU time time.sleep(timeout) return handles = self._events.keys() or [self.dummyEvent] timeout = int(timeout * 1000) val = MsgWaitForMultipleObjects(handles, 0, timeout, QS_ALLINPUT) if val == WAIT_TIMEOUT: return elif val == WAIT_OBJECT_0 + len(handles): exit = win32gui.PumpWaitingMessages() if exit: self.callLater(0, self.stop) return elif val >= WAIT_OBJECT_0 and val < WAIT_OBJECT_0 + len(handles): event = handles[val - WAIT_OBJECT_0] fd, action = self._events[event] if fd in self._reads: # Before anything, make sure it's still a valid file descriptor. fileno = fd.fileno() if fileno == -1: self._disconnectSelectable(fd, posixbase._NO_FILEDESC, False) return # Since it's a socket (not another arbitrary event added via # addEvent) and we asked for FD_READ | FD_CLOSE, check to see if # we actually got FD_CLOSE. This needs a special check because # it only gets delivered once. If we miss it, it's gone forever # and we'll never know that the connection is closed. events = WSAEnumNetworkEvents(fileno, event) if FD_CLOSE in events: self._closedAndReading[fd] = True log.callWithLogger(fd, self._runAction, action, fd)
class KQueueReactor(posixbase.PosixReactorBase): """ A reactor that uses kqueue(2)/kevent(2) and relies on Python 2.6 or higher which has built in support for kqueue in the select module. @ivar _kq: A L{kqueue} which will be used to check for I/O readiness. @ivar _selectables: A dictionary mapping integer file descriptors to instances of L{FileDescriptor} which have been registered with the reactor. All L{FileDescriptors} which are currently receiving read or write readiness notifications will be present as values in this dictionary. @ivar _reads: A set containing integer file descriptors. Values in this set will be registered with C{_kq} for read readiness notifications which will be dispatched to the corresponding L{FileDescriptor} instances in C{_selectables}. @ivar _writes: A set containing integer file descriptors. Values in this set will be registered with C{_kq} for write readiness notifications which will be dispatched to the corresponding L{FileDescriptor} instances in C{_selectables}. """ implements(IReactorFDSet, IReactorDaemonize) def __init__(self): """ Initialize kqueue object, file descriptor tracking dictionaries, and the base class. See: - http://docs.python.org/library/select.html - www.freebsd.org/cgi/man.cgi?query=kqueue - people.freebsd.org/~jlemon/papers/kqueue.pdf """ self._kq = kqueue() self._reads = set() self._writes = set() self._selectables = {} posixbase.PosixReactorBase.__init__(self) def _updateRegistration(self, fd, filter, op): """ Private method for changing kqueue registration on a given FD filtering for events given filter/op. This will never block and returns nothing. """ self._kq.control([kevent(fd, filter, op)], 0, 0) def beforeDaemonize(self): """ Implement L{IReactorDaemonize.beforeDaemonize}. """ # Twisted-internal method called during daemonization (when application # is started via twistd). This is called right before the magic double # forking done for daemonization. We cleanly close the kqueue() and later # recreate it. This is needed since a) kqueue() are not inherited across # forks and b) twistd will create the reactor already before daemonization # (and will also add at least 1 reader to the reactor, an instance of # twisted.internet.posixbase._UnixWaker). # # See: twisted.scripts._twistd_unix.daemonize() self._kq.close() self._kq = None def afterDaemonize(self): """ Implement L{IReactorDaemonize.afterDaemonize}. """ # Twisted-internal method called during daemonization. This is called right # after daemonization and recreates the kqueue() and any readers/writers # that were added before. Note that you MUST NOT call any reactor methods # in between beforeDaemonize() and afterDaemonize()! self._kq = kqueue() for fd in self._reads: self._updateRegistration(fd, KQ_FILTER_READ, KQ_EV_ADD) for fd in self._writes: self._updateRegistration(fd, KQ_FILTER_WRITE, KQ_EV_ADD) def addReader(self, reader): """ Implement L{IReactorFDSet.addReader}. """ fd = reader.fileno() if fd not in self._reads: try: self._updateRegistration(fd, KQ_FILTER_READ, KQ_EV_ADD) except OSError: pass finally: self._selectables[fd] = reader self._reads.add(fd) def addWriter(self, writer): """ Implement L{IReactorFDSet.addWriter}. """ fd = writer.fileno() if fd not in self._writes: try: self._updateRegistration(fd, KQ_FILTER_WRITE, KQ_EV_ADD) except OSError: pass finally: self._selectables[fd] = writer self._writes.add(fd) def removeReader(self, reader): """ Implement L{IReactorFDSet.removeReader}. """ wasLost = False try: fd = reader.fileno() except: fd = -1 if fd == -1: for fd, fdes in self._selectables.items(): if reader is fdes: wasLost = True break else: return if fd in self._reads: self._reads.remove(fd) if fd not in self._writes: del self._selectables[fd] if not wasLost: try: self._updateRegistration(fd, KQ_FILTER_READ, KQ_EV_DELETE) except OSError: pass def removeWriter(self, writer): """ Implement L{IReactorFDSet.removeWriter}. """ wasLost = False try: fd = writer.fileno() except: fd = -1 if fd == -1: for fd, fdes in self._selectables.items(): if writer is fdes: wasLost = True break else: return if fd in self._writes: self._writes.remove(fd) if fd not in self._reads: del self._selectables[fd] if not wasLost: try: self._updateRegistration(fd, KQ_FILTER_WRITE, KQ_EV_DELETE) except OSError: pass def removeAll(self): """ Implement L{IReactorFDSet.removeAll}. """ return self._removeAll([self._selectables[fd] for fd in self._reads], [self._selectables[fd] for fd in self._writes]) def getReaders(self): """ Implement L{IReactorFDSet.getReaders}. """ return [self._selectables[fd] for fd in self._reads] def getWriters(self): """ Implement L{IReactorFDSet.getWriters}. """ return [self._selectables[fd] for fd in self._writes] def doKEvent(self, timeout): """ Poll the kqueue for new events. """ if timeout is None: timeout = 1 try: l = self._kq.control([], len(self._selectables), timeout) except OSError, e: if e[0] == errno.EINTR: return else: raise _drdw = self._doWriteOrRead for event in l: fd = event.ident try: selectable = self._selectables[fd] except KeyError: # Handles the infrequent case where one selectable's # handler disconnects another. continue else: log.callWithLogger(selectable, _drdw, selectable, fd, event)
class PollReactor(posixbase.PosixReactorBase): """A reactor that uses poll(2).""" def _updateRegistration(self, fd): """Register/unregister an fd with the poller.""" try: poller.unregister(fd) except KeyError: pass mask = 0 if fd in reads: mask = mask | select.POLLIN if fd in writes: mask = mask | select.POLLOUT if mask != 0: poller.register(fd, mask) else: if fd in selectables: del selectables[fd] poller.eApp.interruptPoll() def _dictRemove(self, selectable, mdict): try: # the easy way fd = selectable.fileno() # make sure the fd is actually real. In some situations we can get # -1 here. mdict[fd] except: # the hard way: necessary because fileno() may disappear at any # moment, thanks to python's underlying sockets impl for fd, fdes in selectables.items(): if selectable is fdes: break else: # Hmm, maybe not the right course of action? This method can't # fail, because it happens inside error detection... return if fd in mdict: del mdict[fd] self._updateRegistration(fd) def addReader(self, reader): """Add a FileDescriptor for notification of data available to read. """ fd = reader.fileno() if fd not in reads: selectables[fd] = reader reads[fd] = 1 self._updateRegistration(fd) def addWriter(self, writer, writes=writes, selectables=selectables): """Add a FileDescriptor for notification of data available to write. """ fd = writer.fileno() if fd not in writes: selectables[fd] = writer writes[fd] = 1 self._updateRegistration(fd) def removeReader(self, reader, reads=reads): """Remove a Selectable for notification of data available to read. """ return self._dictRemove(reader, reads) def removeWriter(self, writer, writes=writes): """Remove a Selectable for notification of data available to write. """ return self._dictRemove(writer, writes) def removeAll(self, reads=reads, writes=writes, selectables=selectables): """Remove all selectables, and return a list of them.""" if self.waker is not None: self.removeReader(self.waker) result = selectables.values() fds = selectables.keys() reads.clear() writes.clear() selectables.clear() for fd in fds: poller.unregister(fd) if self.waker is not None: self.addReader(self.waker) return result def doPoll(self, timeout, reads=reads, writes=writes, selectables=selectables, select=select, log=log, POLLIN=select.POLLIN, POLLOUT=select.POLLOUT): """Poll the poller for new events.""" if timeout is not None: timeout = int(timeout * 1000) # convert seconds to milliseconds try: l = poller.poll(timeout) if l is None: if self.running: self.stop() l = [] except select.error, e: if e[0] == errno.EINTR: return else: raise _drdw = self._doReadOrWrite for fd, event in l: try: selectable = selectables[fd] except KeyError: # Handles the infrequent case where one selectable's # handler disconnects another. continue log.callWithLogger(selectable, _drdw, selectable, fd, event, POLLIN, POLLOUT, log)
class EPollReactor(posixbase.PosixReactorBase): """ A reactor that uses epoll(4). @ivar _poller: A L{poll} which will be used to check for I/O readiness. @ivar _selectables: A dictionary mapping integer file descriptors to instances of L{FileDescriptor} which have been registered with the reactor. All L{FileDescriptors} which are currently receiving read or write readiness notifications will be present as values in this dictionary. @ivar _reads: A dictionary mapping integer file descriptors to arbitrary values (this is essentially a set). Keys in this dictionary will be registered with C{_poller} for read readiness notifications which will be dispatched to the corresponding L{FileDescriptor} instances in C{_selectables}. @ivar _writes: A dictionary mapping integer file descriptors to arbitrary values (this is essentially a set). Keys in this dictionary will be registered with C{_poller} for write readiness notifications which will be dispatched to the corresponding L{FileDescriptor} instances in C{_selectables}. """ implements(IReactorFDSet) def __init__(self): """ Initialize epoll object, file descriptor tracking dictionaries, and the base class. """ # Create the poller we're going to use. The 1024 here is just a hint # to the kernel, it is not a hard maximum. self._poller = _epoll.epoll(1024) self._reads = {} self._writes = {} self._selectables = {} posixbase.PosixReactorBase.__init__(self) def _add(self, xer, primary, other, selectables, event, antievent): """ Private method for adding a descriptor from the event loop. It takes care of adding it if new or modifying it if already added for another state (read -> read/write for example). """ fd = xer.fileno() if fd not in primary: cmd = _epoll.CTL_ADD flags = event if fd in other: flags |= antievent cmd = _epoll.CTL_MOD primary[fd] = 1 selectables[fd] = xer # epoll_ctl can raise all kinds of IOErrors, and every one # indicates a bug either in the reactor or application-code. # Let them all through so someone sees a traceback and fixes # something. We'll do the same thing for every other call to # this method in this file. self._poller._control(cmd, fd, flags) def addReader(self, reader): """ Add a FileDescriptor for notification of data available to read. """ self._add(reader, self._reads, self._writes, self._selectables, _epoll.IN, _epoll.OUT) def addWriter(self, writer): """ Add a FileDescriptor for notification of data available to write. """ self._add(writer, self._writes, self._reads, self._selectables, _epoll.OUT, _epoll.IN) def _remove(self, xer, primary, other, selectables, event, antievent): """ Private method for removing a descriptor from the event loop. It does the inverse job of _add, and also add a check in case of the fd has gone away. """ fd = xer.fileno() if fd == -1: for fd, fdes in selectables.items(): if xer is fdes: break else: return if fd in primary: cmd = _epoll.CTL_DEL flags = event if fd in other: flags = antievent cmd = _epoll.CTL_MOD else: del selectables[fd] del primary[fd] # See comment above _control call in _add. self._poller._control(cmd, fd, flags) def removeReader(self, reader): """ Remove a Selectable for notification of data available to read. """ self._remove(reader, self._reads, self._writes, self._selectables, _epoll.IN, _epoll.OUT) def removeWriter(self, writer): """ Remove a Selectable for notification of data available to write. """ self._remove(writer, self._writes, self._reads, self._selectables, _epoll.OUT, _epoll.IN) def removeAll(self): """ Remove all selectables, and return a list of them. """ if self.waker is not None: fd = self.waker.fileno() if fd in self._reads: del self._reads[fd] del self._selectables[fd] result = self._selectables.values() fds = self._selectables.keys() self._reads.clear() self._writes.clear() self._selectables.clear() for fd in fds: try: # Actually, we'll ignore all errors from this, since it's # just last-chance cleanup. self._poller._control(_epoll.CTL_DEL, fd, 0) except IOError: pass if self.waker is not None: fd = self.waker.fileno() self._reads[fd] = 1 self._selectables[fd] = self.waker return result def getReaders(self): return [self._selectables[fd] for fd in self._reads] def getWriters(self): return [self._selectables[fd] for fd in self._writes] def doPoll(self, timeout): """ Poll the poller for new events. """ if timeout is None: timeout = 1 timeout = int(timeout * 1000) # convert seconds to milliseconds try: # Limit the number of events to the number of io objects we're # currently tracking (because that's maybe a good heuristic) and # the amount of time we block to the value specified by our # caller. l = self._poller.wait(len(self._selectables), timeout) except IOError, err: if err.errno == errno.EINTR: return # See epoll_wait(2) for documentation on the other conditions # under which this can fail. They can only be due to a serious # programming error on our part, so let's just announce them # loudly. raise _drdw = self._doReadOrWrite for fd, event in l: try: selectable = self._selectables[fd] except KeyError: pass else: log.callWithLogger(selectable, _drdw, selectable, fd, event)