netlink_monitor.py
Go to the documentation of this file.
00001 #! /usr/bin/env python
00002 
00003 import command_with_output
00004 import state_publisher
00005 import threading
00006 import time
00007 import traceback
00008 import pythonwifi.iwlibs
00009 import async_helpers
00010 from twisted.internet.defer import inlineCallbacks
00011 
00012 # TODO 
00013 # Make this autoshutdown when there are no references.
00014 
00015 # FIXME Move this elsewhere.
00016 import subprocess
00017 class RunCommand:
00018     def __init__(self, *args):
00019         proc = subprocess.Popen(list(args), stdout = subprocess.PIPE, stderr = subprocess.PIPE, close_fds = True)
00020         (self.stdout, self.stderr) = proc.communicate()
00021 
00022 class IFSTATE:
00023     PLUGGED    = 0
00024     UP         = 1
00025     LINK       = 2
00026     LINK_ADDR  = 3
00027     ADDR       = 4
00028     NUM_STATES = 5
00029 
00030 class NetlinkMonitor(command_with_output.CommandWithOutput):
00031     def __init__(self):
00032         self.lock = threading.RLock()
00033         self.raw_state_publishers = [ {} for i in range(0, IFSTATE.NUM_STATES)]
00034         self.state_publishers = [ {} for i in range(0, IFSTATE.NUM_STATES)]
00035         self.status_publishers = {}
00036         self.cur_iface = None
00037         self.deleted = None
00038         command_with_output.CommandWithOutput.__init__(self, ['ip', 'monitor', 'link', 'addr'], 'ip_monitor')
00039 
00040     @inlineCallbacks
00041     def child_restart(self):
00042         yield async_helpers.async_sleep(0.2) # Limit race conditions on getting the startup state.
00043         current_state = RunCommand('ip', 'addr')
00044         with self.lock:
00045             old_cur_iface = self.cur_iface
00046             old_deleted = self.deleted
00047             for line in current_state.stdout.split('\n'):
00048                 self.got_line(line)
00049             self.deleted = old_deleted
00050             self.cur_iface = old_cur_iface
00051 
00052     def get_state_publisher(self, interface, level):
00053         if level == 0:
00054             return self.get_raw_state_publisher(interface, level)
00055         pubs =  self.state_publishers[level]
00056         if not interface in pubs:
00057             pubs[interface] = state_publisher.CompositeStatePublisher(lambda l: l[0] and l[1], [
00058                     self.get_state_publisher(interface, level - 1),
00059                     self.get_raw_state_publisher(interface, level), 
00060                     ])
00061         return pubs[interface]
00062     
00063     def get_raw_state_publisher(self, interface, level):
00064         pubs =  self.raw_state_publishers[level]
00065         if not interface in pubs:
00066             pubs[interface] = state_publisher.StatePublisher(False)
00067             #pubs[interface].subscribe(self.debug_print, interface, level)
00068         return pubs[interface]
00069 
00070     def debug_print(self, interface, level, old_state, new_state):
00071         print "Netlink transition", interface, level, "from", old_state, "to", new_state
00072 
00073     def get_status_publisher(self, interface):
00074         if not interface in self.status_publishers:
00075             def lowest_nonzero(args):
00076                 best = -1
00077                 for arg in args:
00078                     if not arg:
00079                         break
00080                     else:
00081                         best += 1
00082                 return best
00083             raw_pubs = [ self.get_raw_state_publisher(interface, level) for level in range(IFSTATE.NUM_STATES) ]
00084             self.status_publishers[interface] = state_publisher.CompositeStatePublisher(lowest_nonzero, raw_pubs)
00085         return self.status_publishers[interface]
00086 
00087     def got_line(self, line):
00088         with self.lock:
00089             try:
00090                 # Figure out the interface, and whether this is a delete
00091                 # event.
00092                 
00093                 if len(line) == 0 or (line[0] == ' ' and self.cur_iface == None):
00094                     return
00095                 tokens = line.rstrip().split()
00096                 link_info = False
00097                 if line[0] != ' ':
00098                     if tokens[0] == 'Deleted':
00099                         self.deleted = True
00100                         tokens.pop(0)
00101                     else:
00102                         self.deleted = False
00103                     self.cur_iface = tokens[1].rstrip(':')
00104                     if tokens[1][-1] == ':':
00105                         link_info = True
00106                     tokens.pop(0)
00107                     tokens.pop(0)
00108 
00109                 if link_info:
00110                     # Plugged or not?
00111                     self.get_raw_state_publisher(self.cur_iface, IFSTATE.PLUGGED).set(not self.deleted)
00112                     
00113                     # Up or not?
00114                     flags = tokens[0].strip('<>').split(',')
00115                     self.get_raw_state_publisher(self.cur_iface, IFSTATE.UP).set('UP' in flags)
00116 
00117                     # Have a link?
00118                     try:
00119                         state_idx = tokens.index('state')
00120                         state = tokens[state_idx + 1]
00121                         if state != 'DOWN':
00122                             try:
00123                                 link_state = pythonwifi.iwlibs.Wireless(self.cur_iface).getAPaddr()
00124                             except IOError, e:
00125                                 if e.errno == 95 or e.errno == 22:
00126                                     link_state = 'Wired'
00127                                 else:
00128                                     raise
00129                         else:
00130                             link_state = False
00131                         self.get_raw_state_publisher(self.cur_iface, IFSTATE.LINK).set(link_state)
00132                     except ValueError:
00133                         pass # Sometimes state is not listed.
00134                 
00135                 else:
00136                     # Find the address.
00137                     if tokens[0] == 'inet':
00138                         if self.deleted:
00139                             addr_state = False
00140                         else:
00141                             addr_state = tokens[1].split('/')
00142                         self.get_raw_state_publisher(self.cur_iface, IFSTATE.ADDR).set(addr_state)
00143 
00144                     if tokens[0].startswith('link/') and len(tokens) > 1:
00145                         if self.deleted:
00146                             addr_state = False
00147                         else:
00148                             addr_state = tokens[1]
00149                         self.get_raw_state_publisher(self.cur_iface, IFSTATE.LINK_ADDR).set(addr_state)
00150 
00151             except Exception, e:
00152                 print "Caught exception in NetlinkMonitor.run:", e
00153                 traceback.print_exc(10)
00154                 print
00155 
00156 monitor = netlink_monitor = NetlinkMonitor()
00157 
00158 def print_status(iface):
00159     from twisted.internet import reactor
00160     print monitor.get_status_publisher(iface).get(), ' : ',
00161     for i in range(0,IFSTATE.NUM_STATES):
00162         print monitor.get_raw_state_publisher(iface, i).get(),
00163         print monitor.get_state_publisher(iface, i).get(), '  /  ',
00164     print
00165     reactor.callLater(1, print_status, iface)
00166 
00167 def main():
00168     from twisted.internet import reactor
00169     iface = 'wlan0'
00170     try:
00171         print_status(iface)
00172         reactor.run()
00173     except KeyboardInterrupt:
00174         print "Shutting down on CTRL+C"
00175         #monitor.shutdown()
00176 
00177 if __name__ == "__main__":
00178     main()


multi_interface_roam
Author(s): Blaise Gassend
autogenerated on Thu Apr 24 2014 15:34:18