def listen(self, listenip="", listenport=DEF_TFTP_PORT,
               timeout=SOCK_TIMEOUT):
        """Start a server listening on the supplied interface and port. This
        defaults to INADDR_ANY (all interfaces) and UDP port 69. You can also
        supply a different socket timeout value, if desired."""
        tftp_factory = TftpPacketFactory()

        # Don't use new 2.5 ternary operator yet
        # listenip = listenip if listenip else '0.0.0.0'
        if not listenip: listenip = '0.0.0.0'
        log.info("Server requested on ip %s, port %s" % (listenip, listenport))
        try:
            # FIXME - sockets should be non-blocking
            self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
            self.sock.bind((listenip, listenport))
            _, self.listenport = self.sock.getsockname()
        except socket.error as err:
            # Reraise it for now.
            raise err

        self.is_running.set()

        log.info("Starting receive loop...")
        while True:
            log.debug("shutdown_immediately is %s" % self.shutdown_immediately)
            log.debug("shutdown_gracefully is %s" % self.shutdown_gracefully)
            if self.shutdown_immediately:
                log.warning("Shutting down now. Session count: %d" %
                         len(self.sessions))
                self.sock.close()
                for key in self.sessions:
                    self.sessions[key].end()
                self.sessions = []
                break

            elif self.shutdown_gracefully:
                if not self.sessions:
                    log.warning("In graceful shutdown mode and all "
                             "sessions complete.")
                    self.sock.close()
                    break

            # Build the inputlist array of sockets to select() on.
            inputlist = []
            inputlist.append(self.sock)
            for key in self.sessions:
                inputlist.append(self.sessions[key].sock)

            # Block until some socket has input on it.
            log.debug("Performing select on this inputlist: %s", inputlist)
            try:
                readyinput, readyoutput, readyspecial = \
                        select.select(inputlist, [], [], SOCK_TIMEOUT)
            except select.error as err:
                if err[0] == EINTR:
                    # Interrupted system call
                    log.debug("Interrupted syscall, retrying")
                    continue
                else:
                    raise

            deletion_list = []

            # Handle the available data, if any. Maybe we timed-out.
            for readysock in readyinput:
                # Is the traffic on the main server socket? ie. new session?
                if readysock == self.sock:
                    log.debug("Data ready on our main socket")
                    buffer, (raddress, rport) = self.sock.recvfrom(MAX_BLKSIZE)

                    log.debug("Read %d bytes", len(buffer))

                    if self.shutdown_gracefully:
                        log.warning("Discarding data on main port, "
                                 "in graceful shutdown mode")
                        continue

                    # Forge a session key based on the client's IP and port,
                    # which should safely work through NAT.
                    key = "%s:%s" % (raddress, rport)

                    if not key in self.sessions:
                        log.debug("Creating new server context for "
                                     "session key = %s" % key)
                        self.sessions[key] = TftpContextServer(raddress,
                                                               rport,
                                                               timeout,
                                                               self.root,
                                                               self.dyn_file_func,
                                                               self.upload_open)
                        try:
                            self.sessions[key].start(buffer)
                        except TftpException as err:
                            deletion_list.append(key)
                            log.error("Fatal exception thrown from "
                                      "session %s: %s" % (key, str(err)))
                    else:
                        log.warning("received traffic on main socket for "
                                 "existing session??")
                    log.info("Currently handling these sessions:")
                    for session_key, session in list(self.sessions.items()):
                        log.info("    %s" % session)

                else:
                    # Must find the owner of this traffic.
                    for key in self.sessions:
                        if readysock == self.sessions[key].sock:
                            log.debug("Matched input to session key %s"
                                % key)
                            try:
                                self.sessions[key].cycle()
                                if self.sessions[key].state == None:
                                    log.info("Successful transfer.")
                                    deletion_list.append(key)
                            except TftpException as err:
                                deletion_list.append(key)
                                log.error("Fatal exception thrown from "
                                          "session %s: %s"
                                          % (key, str(err)))
                            # Break out of for loop since we found the correct
                            # session.
                            break
                    else:
                        log.error("Can't find the owner for this packet. "
                                  "Discarding.")

            log.debug("Looping on all sessions to check for timeouts")
            now = time.time()
            for key in self.sessions:
                try:
                    self.sessions[key].checkTimeout(now)
                except TftpTimeout as err:
                    log.error(str(err))
                    self.sessions[key].retry_count += 1
                    if self.sessions[key].retry_count >= TIMEOUT_RETRIES:
                        log.debug("hit max retries on %s, giving up" %
                            self.sessions[key])
                        deletion_list.append(key)
                    else:
                        log.debug("resending on session %s" % self.sessions[key])
                        self.sessions[key].state.resendLast()

            log.debug("Iterating deletion list.")
            for key in deletion_list:
                log.info('')
                log.info("Session %s complete" % key)
                if key in self.sessions:
                    log.debug("Gathering up metrics from session before deleting")
                    self.sessions[key].end()
                    metrics = self.sessions[key].metrics
                    if metrics.duration == 0:
                        log.info("Duration too short, rate undetermined")
                    else:
                        log.info("Transferred %d bytes in %.2f seconds"
                            % (metrics.bytes, metrics.duration))
                        log.info("Average rate: %.2f kbps" % metrics.kbps)
                    log.info("%.2f bytes in resent data" % metrics.resent_bytes)
                    log.info("%d duplicate packets" % metrics.dupcount)
                    log.debug("Deleting session %s" % key)
                    del self.sessions[key]
                    log.debug("Session list is now %s" % self.sessions)
                else:
                    log.warning(
                        "Strange, session %s is not on the deletion list" % key)

        self.is_running.clear()

        log.debug("server returning from while loop")
        self.shutdown_gracefully = self.shutdown_immediately = False
def listen ( self , listenip = "" , listenport = DEF_TFTP_PORT , timeout = SOCK_TIMEOUT ) : tftp_factory = TftpPacketFactory ( ) if not listenip : listenip = '0.0.0.0' log . info ( "Server requested on ip %s, port %s" % ( listenip , listenport ) ) try : self . sock = socket . socket ( socket . AF_INET , socket . SOCK_DGRAM ) self . sock . bind ( ( listenip , listenport ) ) _ , self . listenport = self . sock . getsockname ( ) except socket . error as err : raise err self . is_running . set ( ) log . info ( "Starting receive loop..." ) while True : log . debug ( "shutdown_immediately is %s" % self . shutdown_immediately ) log . debug ( "shutdown_gracefully is %s" % self . shutdown_gracefully ) if self . shutdown_immediately : log . warning ( "Shutting down now. Session count: %d" % len ( self . sessions ) ) self . sock . close ( ) for key in self . sessions : self . sessions [ key ] . end ( ) self . sessions = [ ] break elif self . shutdown_gracefully : if not self . sessions : log . warning ( "In graceful shutdown mode and all " "sessions complete." ) self . sock . close ( ) break inputlist = [ ] inputlist . append ( self . sock ) for key in self . sessions : inputlist . append ( self . sessions [ key ] . sock ) log . debug ( "Performing select on this inputlist: %s" , inputlist ) try : readyinput , readyoutput , readyspecial = select . select ( inputlist , [ ] , [ ] , SOCK_TIMEOUT ) except select . error as err : if err [ 0 ] == EINTR : log . debug ( "Interrupted syscall, retrying" ) continue else : raise deletion_list = [ ] for readysock in readyinput : if readysock == self . sock : log . debug ( "Data ready on our main socket" ) buffer , ( raddress , rport ) = self . sock . recvfrom ( MAX_BLKSIZE ) log . debug ( "Read %d bytes" , len ( buffer ) ) if self . shutdown_gracefully : log . warning ( "Discarding data on main port, " "in graceful shutdown mode" ) continue key = "%s:%s" % ( raddress , rport ) if not key in self . sessions : log . debug ( "Creating new server context for " "session key = %s" % key ) self . sessions [ key ] = TftpContextServer ( raddress , rport , timeout , self . root , self . dyn_file_func , self . upload_open ) try : self . sessions [ key ] . start ( buffer ) except TftpException as err : deletion_list . append ( key ) log . error ( "Fatal exception thrown from " "session %s: %s" % ( key , str ( err ) ) ) else : log . warning ( "received traffic on main socket for " "existing session??" ) log . info ( "Currently handling these sessions:" ) for session_key , session in list ( self . sessions . items ( ) ) : log . info ( "    %s" % session ) else : for key in self . sessions : if readysock == self . sessions [ key ] . sock : log . debug ( "Matched input to session key %s" % key ) try : self . sessions [ key ] . cycle ( ) if self . sessions [ key ] . state == None : log . info ( "Successful transfer." ) deletion_list . append ( key ) except TftpException as err : deletion_list . append ( key ) log . error ( "Fatal exception thrown from " "session %s: %s" % ( key , str ( err ) ) ) break else : log . error ( "Can't find the owner for this packet. " "Discarding." ) log . debug ( "Looping on all sessions to check for timeouts" ) now = time . time ( ) for key in self . sessions : try : self . sessions [ key ] . checkTimeout ( now ) except TftpTimeout as err : log . error ( str ( err ) ) self . sessions [ key ] . retry_count += 1 if self . sessions [ key ] . retry_count >= TIMEOUT_RETRIES : log . debug ( "hit max retries on %s, giving up" % self . sessions [ key ] ) deletion_list . append ( key ) else : log . debug ( "resending on session %s" % self . sessions [ key ] ) self . sessions [ key ] . state . resendLast ( ) log . debug ( "Iterating deletion list." ) for key in deletion_list : log . info ( '' ) log . info ( "Session %s complete" % key ) if key in self . sessions : log . debug ( "Gathering up metrics from session before deleting" ) self . sessions [ key ] . end ( ) metrics = self . sessions [ key ] . metrics if metrics . duration == 0 : log . info ( "Duration too short, rate undetermined" ) else : log . info ( "Transferred %d bytes in %.2f seconds" % ( metrics . bytes , metrics . duration ) ) log . info ( "Average rate: %.2f kbps" % metrics . kbps ) log . info ( "%.2f bytes in resent data" % metrics . resent_bytes ) log . info ( "%d duplicate packets" % metrics . dupcount ) log . debug ( "Deleting session %s" % key ) del self . sessions [ key ] log . debug ( "Session list is now %s" % self . sessions ) else : log . warning ( "Strange, session %s is not on the deletion list" % key ) self . is_running . clear ( ) log . debug ( "server returning from while loop" ) self . shutdown_gracefully = self . shutdown_immediately = False
Start a server listening on the supplied interface and port. This
        defaults to INADDR_ANY (all interfaces) and UDP port 69. You can also
        supply a different socket timeout value, if desired.