diff --git a/daemon.py b/daemon.py index 49fe5ae..09bb4d8 100644 --- a/daemon.py +++ b/daemon.py @@ -195,11 +195,9 @@ class ConnectionWizard(dbus.service.Object): @dbus.service.method('org.wicd.daemon') def SetWiredInterface(self, interface): """ Sets the wired interface for the daemon to use. """ - print "setting wired interface", str(interface) + print "setting wired interface %s" % (str(interface)) self.wired.wired_interface = interface - self.wired.SetLiface(interface) self.wifi.wired_interface = interface - self.wifi.SetLiface(interface) config = ConfigParser.ConfigParser() config.read(self.app_conf) config.set("Settings","wired_interface", interface) @@ -208,11 +206,9 @@ class ConnectionWizard(dbus.service.Object): @dbus.service.method('org.wicd.daemon') def SetWirelessInterface(self, interface): """ Sets the wireless interface the daemon will use. """ - print "setting wireless interface" , str(interface) + print "setting wireless interface %s" % (str(interface)) self.wifi.wireless_interface = interface - self.wifi.SetWiface(interface) self.wired.wireless_interface = interface - self.wired.SetWiface(interface) config = ConfigParser.ConfigParser() config.read(self.app_conf) config.set("Settings","wireless_interface", interface) @@ -363,7 +359,7 @@ class ConnectionWizard(dbus.service.Object): if fresh: self.Scan() #self.AutoConnectScan() # Also scans for hidden networks - if self.CheckPluggedIn(): + if self.CheckPluggedIn(True): self._wired_autoconnect() else: self._wireless_autoconnect() @@ -691,6 +687,10 @@ class ConnectionWizard(dbus.service.Object): def GetNumberOfNetworks(self): """ Returns number of networks. """ return len(self.LastScan) + + @dbus.service.method('org.wicd.daemon.wireless') + def GetApBssid(self): + return self.wifi.GetBSSID() @dbus.service.method('org.wicd.daemon.wireless') def CreateAdHocNetwork(self, essid, channel, ip, enctype, key, encused, @@ -736,13 +736,19 @@ class ConnectionWizard(dbus.service.Object): print "Couldn't detect a wireless interface." return str(iface) + @dbus.service.method('org.wicd.daemon.wireless') + def DisconnectWireless(self): + """ Disconnects the wireless network. """ + self.SetForcedDisconnect(True) + self.wifi.Disconnect() + @dbus.service.method('org.wicd.daemon.wireless') def IsWirelessUp(self): """ Returns a boolean specifying if wireless is up or down. """ return self.wifi.IsUp() @dbus.service.method('org.wicd.daemon.wireless') - def GetPrintableSignalStrength(self, iwconfig=None): + def GetPrintableSignalStrength(self, iwconfig=None, fast=False): """ Assigns a signal strength appropriate for display This is used separately from the raw signal strength retrieving @@ -752,38 +758,38 @@ class ConnectionWizard(dbus.service.Object): """ if self.GetSignalDisplayType() == 0: - return self.GetCurrentSignalStrength(iwconfig) + return self.GetCurrentSignalStrength(iwconfig, fast) else: - return self.GetCurrentDBMStrength(iwconfig) + return self.GetCurrentDBMStrength(iwconfig, fast) @dbus.service.method('org.wicd.daemon.wireless') - def GetCurrentSignalStrength(self, iwconfig=None): + def GetCurrentSignalStrength(self, iwconfig=None, fast=False): """ Returns the current signal strength. """ try: - strength = int(self.wifi.GetSignalStrength(iwconfig)) + strength = int(self.wifi.GetSignalStrength(iwconfig, fast)) except: strength = 0 return strength @dbus.service.method('org.wicd.daemon.wireless') - def GetCurrentDBMStrength(self, iwconfig=None): + def GetCurrentDBMStrength(self, iwconfig=None, fast=False): """ Returns the current dbm signal strength. """ try: - dbm_strength = int(self.wifi.GetDBMStrength(iwconfig)) + dbm_strength = int(self.wifi.GetDBMStrength(iwconfig, fast)) except: dbm_strength = 0 return dbm_strength @dbus.service.method('org.wicd.daemon.wireless') - def GetCurrentNetwork(self, iwconfig=None): + def GetCurrentNetwork(self, iwconfig=None, fast=False): """ Returns the current network. """ - current_network = str(self.wifi.GetCurrentNetwork(iwconfig)) + current_network = str(self.wifi.GetCurrentNetwork(iwconfig, fast)) return current_network @dbus.service.method('org.wicd.daemon.wireless') - def GetCurrentNetworkID(self, iwconfig=None): + def GetCurrentNetworkID(self, iwconfig=None, fast=False): """ Returns the id of the current network, or -1 if its not found. """ - currentESSID = self.GetCurrentNetwork(iwconfig) + currentESSID = self.GetCurrentNetwork(iwconfig, fast) for x in xrange(0, len(self.LastScan)): if self.LastScan[x]['essid'] == currentESSID: return x @@ -820,18 +826,15 @@ class ConnectionWizard(dbus.service.Object): @dbus.service.method('org.wicd.daemon.wireless') def CheckIfWirelessConnecting(self): """Returns True if wireless interface is connecting, otherwise False.""" - if self.wifi.connecting_thread is not None: - # If connecting_thread exists, then check for it's - # status, if it doesn't, we aren't connecting. - status = self.wifi.connecting_thread.is_connecting - return status + if self.wifi.connecting_thread: + return self.wifi.connecting_thread.is_connecting else: return False @dbus.service.method('org.wicd.daemon.wireless') - def GetWirelessIP(self): + def GetWirelessIP(self, fast=False): """ Returns the IP associated with the wireless interface. """ - ip = self.wifi.GetIP() + ip = self.wifi.GetIP(fast) #if self.debug_mode == 1: #print 'returning wireless ip', ip return ip @@ -849,9 +852,9 @@ class ConnectionWizard(dbus.service.Object): ################################# @dbus.service.method('org.wicd.daemon.wired') - def GetWiredIP(self): + def GetWiredIP(self, fast=False): """ Returns the wired interface's ip address. """ - ip = self.wired.GetIP() + ip = self.wired.GetIP(True) return ip @dbus.service.method('org.wicd.daemon.wired') @@ -917,6 +920,12 @@ class ConnectionWizard(dbus.service.Object): return True else: return False + + @dbus.service.method('org.wicd.daemon.wired') + def DisconnectWired(self): + """ Disconnects the wired network. """ + self.SetForcedDisconnect(True) + self.wired.Disconnect() @dbus.service.method('org.wicd.daemon.wired') def SetAlwaysShowWiredInterface(self, value): @@ -932,9 +941,9 @@ class ConnectionWizard(dbus.service.Object): return bool(self.always_show_wired_interface) @dbus.service.method('org.wicd.daemon.wired') - def CheckPluggedIn(self): + def CheckPluggedIn(self, fast=False): if self.wired.wired_interface and self.wired.wired_interface != "None": - return self.wired.CheckPluggedIn() + return self.wired.CheckPluggedIn(fast) else: return None diff --git a/gui.py b/gui.py index b8af7c4..436e56e 100644 --- a/gui.py +++ b/gui.py @@ -179,8 +179,6 @@ language['setting_broadcast_address'] = _('Setting broadcast address...') language['setting_static_dns'] = _('Setting static DNS servers...') language['setting_static_ip'] = _('Setting static IP addresses...') language['running_dhcp'] = _('Obtaining IP address...') -language['no_dhcp_offers'] = _('Connection Failed: No DHCP offers received. \ - Couldn\'t get an IP Address.') language['dhcp_failed'] = _('Connection Failed: Unable to Get IP Address') language['aborted'] = _('Connection Cancelled') language['bad_pass'] = _('Connection Failed: Bad password') @@ -647,6 +645,10 @@ class NetworkEntry(gtk.HBox): self.connect_hbox.pack_start(self.connect_button, False, False) self.connect_hbox.show() + # Set up the Disconnect button + self.disconnect_button = gtk.Button(stock=gtk.STOCK_DISCONNECT) + self.connect_hbox.pack_start(self.disconnect_button, False, False) + # Set up the VBox that goes in the gtk.Expander self.expander_vbox = gtk.VBox(False, 1) self.expander_vbox.show() @@ -790,6 +792,7 @@ class WiredNetworkEntry(NetworkEntry): self.expander.set_expanded(True) self.profile_help.show() self.check_enable() + self.update_connect_button() self.wireddis = self.connect("destroy", self.destroy_called) def destroy_called(self, *args): @@ -827,7 +830,17 @@ class WiredNetworkEntry(NetworkEntry): self.connect_button.set_sensitive(False) self.advanced_button.set_sensitive(False) self.script_button.set_sensitive(False) - + + def update_connect_button(self, apbssid=None): + """ Update the connection/disconnect button for this entry. """ + state, x = daemon.GetConnectionStatus() + if state == misc.WIRED: + self.disconnect_button.show() + self.connect_button.hide() + else: + self.disconnect_button.hide() + self.connect_button.show() + def add_profile(self, widget): """ Add a profile to the profile list. """ print "adding profile" @@ -871,12 +884,10 @@ class WiredNetworkEntry(NetworkEntry): def toggle_default_profile(self, widget): """ Change the default profile. """ if self.chkbox_default_profile.get_active(): - print 'unsetting previous default profile...' # Make sure there is only one default profile at a time config.UnsetWiredDefault() wired.SetWiredProperty("default", self.chkbox_default_profile.get_active()) - print 'toggle defualt prof' config.SaveWiredNetworkProfile(self.combo_profile_names.get_active_text()) def change_profile(self, widget): @@ -967,6 +978,7 @@ class WirelessNetworkEntry(NetworkEntry): # Show everything self.show_all() self.advanced_dialog = WirelessSettingsDialog(networkID) + self.update_connect_button(wireless.GetApBssid()) self.wifides = self.connect("destroy", self.destroy_called) def destroy_called(self, *args): @@ -1032,6 +1044,17 @@ class WirelessNetworkEntry(NetworkEntry): self.image.set_from_file(wpath.images + signal_img) self.lbl_strength.set_label(disp_strength + ending) + + def update_connect_button(self, apbssid): + """ Update the connection/disconnect button for this entry. """ + state, x = daemon.GetConnectionStatus() + if state == misc.WIRELESS and apbssid == \ + wireless.GetWirelessProperty(self.networkID, "bssid"): + self.disconnect_button.show() + self.connect_button.hide() + else: + self.disconnect_button.hide() + self.connect_button.show() def set_mac_address(self, address): """ Set the MAC address for the WirelessNetworkEntry. """ @@ -1133,7 +1156,7 @@ class appGui: dic = { "refresh_clicked" : self.refresh_networks, "quit_clicked" : self.exit, - "disconnect_clicked" : self.disconnect, + "disconnect_clicked" : self.disconnect_all, "main_exit" : self.exit, "cancel_clicked" : self.cancel_connect, "connect_clicked" : self.connect_hidden, @@ -1157,7 +1180,6 @@ class appGui: self.network_list = self.wTree.get_widget("network_list_vbox") self.status_area = self.wTree.get_widget("connecting_hbox") self.status_bar = self.wTree.get_widget("statusbar") - self.refresh_networks(fresh=False) self.status_area.hide_all() @@ -1168,6 +1190,8 @@ class appGui: self.pulse_active = False self.standalone = standalone self.wpadrivercombo = None + self.fast = True # Use ioctl instead of external program calls + self.refresh_networks(fresh=False) self.window.connect('delete_event', self.exit) @@ -1186,7 +1210,7 @@ class appGui: if width > -1 and height > -1: self.window.resize(int(width), int(height)) - gobject.timeout_add(700, self.update_statusbar) + gobject.timeout_add(400, self.update_statusbar) def create_adhoc_network(self, widget=None): """ Shows a dialog that creates a new adhoc network. """ @@ -1238,9 +1262,10 @@ class appGui: """ Toggles the encryption key entry box for the ad-hoc dialog. """ self.key_entry.set_sensitive(self.chkbox_use_encryption.get_active()) - def disconnect(self, widget=None): + def disconnect_all(self, widget=None): """ Disconnects from any active network. """ daemon.Disconnect() + self.update_connect_buttons() def about_dialog(self, widget, event=None): """ Displays an about dialog. """ @@ -1550,45 +1575,59 @@ class appGui: if not self.is_visible: return True + fast = self.fast wiredConnecting = wired.CheckIfWiredConnecting() wirelessConnecting = wireless.CheckIfWirelessConnecting() + connecting = wiredConnecting or wirelessConnecting - if wirelessConnecting or wiredConnecting: - if not self.pulse_active: - self.pulse_active = True - gobject.timeout_add(100, self.pulse_progress_bar) - + if connecting and not self.pulse_active: + self.pulse_active = True + gobject.timeout_add(100, self.pulse_progress_bar) self.network_list.set_sensitive(False) self.status_area.show_all() if self.statusID: self.status_bar.remove(1, self.statusID) if wirelessConnecting: - iwconfig = wireless.GetIwconfig() - self.set_status(wireless.GetCurrentNetwork(iwconfig) + ': ' + + if not fast: + iwconfig = wireless.GetIwconfig() + else: + iwconfig = '' + self.set_status(wireless.GetCurrentNetwork(iwconfig, fast) + ': ' + language[str(wireless.CheckWirelessConnectingMessage())]) if wiredConnecting: self.set_status(language['wired_network'] + ': ' + language[str(wired.CheckWiredConnectingMessage())]) - else: - self.network_list.set_sensitive(True) + elif self.pulse_active and not connecting: self.pulse_active = False + self.update_connect_buttons() + self.network_list.set_sensitive(True) self.status_area.hide_all() if self.statusID: self.status_bar.remove(1, self.statusID) # Determine connection status. - if self.check_for_wired(wired.GetWiredIP()): + if self.check_for_wired(wired.GetWiredIP(fast)): return True - - if self.check_for_wireless(wireless.GetIwconfig(), - wireless.GetWirelessIP()): + if not fast: + iwconfig = wireless.GetIwconfig() + else: + iwconfig = '' + if self.check_for_wireless(iwconfig, + wireless.GetWirelessIP(fast)): return True self.set_status(language['not_connected']) return True + def update_connect_buttons(self): + """ Updates the connect/disconnect buttons for each network entry. """ + apbssid = wireless.GetApBssid() + for entry in self.network_list: + if hasattr(entry, "update_connect_button"): + entry.update_connect_button(apbssid) + def check_for_wired(self, wired_ip): """ Determine if wired is active, and if yes, set the status. """ - if wired_ip and wired.CheckPluggedIn(): + if wired_ip and wired.CheckPluggedIn(self.fast): self.set_status(language['connected_to_wired'].replace('$A', wired_ip)) return True @@ -1600,15 +1639,15 @@ class appGui: if not wireless_ip: return False - network = wireless.GetCurrentNetwork(iwconfig) + network = wireless.GetCurrentNetwork(iwconfig, self.fast) if not network: return False network = str(network) if daemon.GetSignalDisplayType() == 0: - strength = wireless.GetCurrentSignalStrength(iwconfig) + strength = wireless.GetCurrentSignalStrength(iwconfig, self.fast) else: - strength = wireless.GetCurrentDBMStrength(iwconfig) + strength = wireless.GetCurrentDBMStrength(iwconfig, self.fast) if strength is None: return False @@ -1644,12 +1683,14 @@ class appGui: z.destroy() del z - if wired.CheckPluggedIn() or wired.GetAlwaysShowWiredInterface(): + if wired.CheckPluggedIn(self.fast) or wired.GetAlwaysShowWiredInterface(): printLine = True # In this case we print a separator. wirednet = WiredNetworkEntry() self.network_list.pack_start(wirednet, False, False) wirednet.connect_button.connect("button-press-event", self.connect, "wired", 0, wirednet) + wirednet.disconnect_button.connect("button-press-event", self.disconnect, + "wired", 0, wirednet) wirednet.advanced_button.connect("button-press-event", self.edit_advanced, "wired", 0, wirednet) @@ -1673,11 +1714,13 @@ class appGui: else: printLine = True tempnet = WirelessNetworkEntry(x) - tempnet.show_all() self.network_list.pack_start(tempnet, False, False) tempnet.connect_button.connect("button-press-event", self.connect, "wireless", x, tempnet) + tempnet.disconnect_button.connect("button-press-event", + self.disconnect, "wireless", + x, tempnet) tempnet.advanced_button.connect("button-press-event", self.edit_advanced, "wireless", x, tempnet) @@ -1889,6 +1932,13 @@ class appGui: wired.ConnectWired() self.update_statusbar() + def disconnect(self, widget, event, nettype, networkid, networkentry): + if nettype == "wired": + wired.DisconnectWired() + else: + wireless.DisconnectWireless() + self.update_connect_buttons() + def wait_for_events(self, amt=0): """ Wait for any pending gtk events to finish before moving on. diff --git a/misc.py b/misc.py index 3a5f073..47505a0 100644 --- a/misc.py +++ b/misc.py @@ -277,4 +277,5 @@ def error(parent, message): gtk.BUTTONS_OK) dialog.set_markup(message) dialog.run() - dialog.destroy() \ No newline at end of file + dialog.destroy() + diff --git a/monitor.py b/monitor.py index 4026448..d817adc 100755 --- a/monitor.py +++ b/monitor.py @@ -67,6 +67,9 @@ class ConnectionStatus(): self.reconnect_tries = 0 self.last_reconnect_time = time.time() self.signal_changed = False + # This determines if we use ioctl or external programs + self.fast = True + self.iwconfig = '' def check_for_wired_connection(self, wired_ip): """ Checks for an active wired connection. @@ -76,7 +79,7 @@ class ConnectionStatus(): """ - if wired_ip and wired.CheckPluggedIn(): + if wired_ip and wired.CheckPluggedIn(self.fast): # Only change the interface if it's not already set for wired if not self.still_wired: daemon.SetCurrentInterface(daemon.GetWiredInterface()) @@ -99,6 +102,10 @@ class ConnectionStatus(): if wireless_ip is None: return False + if self.fast: + self.iwconfig = '' + else: + self.iwconfig = wireless.GetIwconfig() # Reset this, just in case. self.tried_reconnect = False @@ -106,9 +113,11 @@ class ConnectionStatus(): # if something goes wrong. try: if daemon.GetSignalDisplayType() == 0: - wifi_signal = int(wireless.GetCurrentSignalStrength(self.iwconfig)) + wifi_signal = int(wireless.GetCurrentSignalStrength(self.iwconfig, + self.fast)) else: - wifi_signal = int(wireless.GetCurrentDBMStrength(self.iwconfig)) + wifi_signal = int(wireless.GetCurrentDBMStrength(self.iwconfig, + self.fast)) except: wifi_signal = 0 @@ -127,7 +136,7 @@ class ConnectionStatus(): # Only update if the signal strength has changed because doing I/O # calls is expensive, and the icon flickers. if (wifi_signal != self.last_strength or - self.network != wireless.GetCurrentNetwork(self.iwconfig)): + self.network != wireless.GetCurrentNetwork(self.iwconfig, self.fast)): self.last_strength = wifi_signal self.signal_changed = True daemon.SetCurrentInterface(daemon.GetWirelessInterface()) @@ -153,15 +162,15 @@ class ConnectionStatus(): # Determine what our current state is. # Check for wired. - wired_ip = wired.GetWiredIP() + wired_ip = wired.GetWiredIP(self.fast) wired_found = self.check_for_wired_connection(wired_ip) if wired_found: self.update_state(misc.WIRED, wired_ip=wired_ip) return True - + # Check for wireless - wifi_ip = wireless.GetWirelessIP() - self.iwconfig = wireless.GetIwconfig() + wifi_ip = wireless.GetWirelessIP(self.fast) + #self.iwconfig = wireless.GetIwconfig() self.signal_changed = False wireless_found = self.check_for_wireless_connection(wifi_ip) if wireless_found: @@ -184,6 +193,8 @@ class ConnectionStatus(): def update_state(self, state, wired_ip=None, wifi_ip=None): """ Set the current connection state. """ # Set our connection state/info. + iwconfig = self.iwconfig + fast = self.fast if state == misc.NOT_CONNECTED: info = [""] elif state == misc.SUSPENDED: @@ -192,12 +203,12 @@ class ConnectionStatus(): if wired.CheckIfWiredConnecting(): info = ["wired"] else: - info = ["wireless", wireless.GetCurrentNetwork(self.iwconfig)] + info = ["wireless", wireless.GetCurrentNetwork(iwconfig, fast)] elif state == misc.WIRELESS: self.reconnect_tries = 0 - info = [wifi_ip, wireless.GetCurrentNetwork(self.iwconfig), - str(wireless.GetPrintableSignalStrength(self.iwconfig)), - str(wireless.GetCurrentNetworkID(self.iwconfig))] + info = [wifi_ip, wireless.GetCurrentNetwork(iwconfig, fast), + str(wireless.GetPrintableSignalStrength(iwconfig, fast)), + str(wireless.GetCurrentNetworkID(iwconfig, fast))] elif state == misc.WIRED: self.reconnect_tries = 0 info = [wired_ip] @@ -240,7 +251,7 @@ class ConnectionStatus(): # If we just lost a wireless connection, try to connect to that # network again. Otherwise just call Autoconnect. - cur_net_id = wireless.GetCurrentNetworkID(self.iwconfig) + cur_net_id = wireless.GetCurrentNetworkID(self.iwconfig, self.fast) if from_wireless and cur_net_id > -1: print 'Trying to reconnect to last used wireless ' + \ 'network' @@ -262,7 +273,7 @@ def err_handle(error): def main(): """ Start the connection monitor and set the updater to run every 2 sec. """ monitor = ConnectionStatus() - gobject.timeout_add(3000, monitor.update_connection_status) + gobject.timeout_add(2000, monitor.update_connection_status) mainloop = gobject.MainLoop() mainloop.run() diff --git a/networking.py b/networking.py index bae3385..de1b638 100644 --- a/networking.py +++ b/networking.py @@ -53,7 +53,6 @@ if __name__ == '__main__': wpath.chdir(__file__) - class Controller(object): """ Parent class for the different interface types. """ def __init__(self): @@ -125,17 +124,7 @@ class Controller(object): debug = property(get_debug, set_debug) flush_tool = property(get_flush_tool, set_flush_tool) dhcp_client = property(get_dhcp_client, set_dhcp_client) - - def SetWiface(self, iface): - """ Sets the wireless interface for the associated wnettools class. """ - if self.wiface: - self.wiface.SetInterface(iface) - def SetLiface(self, iface): - """ Sets the wired interface for the associated wnettools class. """ - if self.liface: - self.liface.SetInterface(iface) - class ConnectThread(threading.Thread): """ A class to perform network connections in a multi-threaded way. @@ -178,6 +167,7 @@ class ConnectThread(threading.Thread): self.after_script = after_script self.disconnect_script = disconnect_script self.should_die = False + self.abort_reason = "" self.global_dns_1 = gdns1 self.global_dns_2 = gdns2 @@ -279,7 +269,7 @@ class ConnectThread(threading.Thread): print "Running DHCP" dhcp_status = iface.StartDHCP() if dhcp_status in ['no_dhcp_offers', 'dhcp_failed']: - self.connect_aborted(dhcp_status) + self.abort_connection(dhcp_status) return def set_dns_addresses(self): @@ -306,12 +296,19 @@ class ConnectThread(threading.Thread): a few seconds to make sure the message is readable """ + if self.abort_reason: + reason = self.abort_reason self.SetStatus(reason) self.is_aborted = True self.abort_msg = reason self.is_connecting = False print 'exiting connection thread' + def abort_connection(self, reason): + """ Schedule a connection abortion for the given reason. """ + self.abort_reason = reason + self.should_die = True + def stop_dhcp_clients(self, iface): """ Stop and running DHCP clients, as well as wpa_supplicant. """ print 'Stopping wpa_supplicant, and any dhcp clients' @@ -399,41 +396,51 @@ class Wireless(Controller): self.connecting_thread.start() return True - def GetSignalStrength(self, iwconfig=None): + def GetSignalStrength(self, iwconfig=None, fast=False): """ Get signal strength of the current network. Returns: The current signal strength. """ - return self.wiface.GetSignalStrength(iwconfig) + return self.wiface.GetSignalStrength(iwconfig, fast) - def GetDBMStrength(self, iwconfig=None): + def GetDBMStrength(self, iwconfig=None, fast=False): """ Get the dBm signal strength of the current network. Returns: The current dBm signal strength. """ - return self.wiface.GetDBMStrength(iwconfig) + return self.wiface.GetDBMStrength(iwconfig, fast) - def GetCurrentNetwork(self, iwconfig=None): + def GetCurrentNetwork(self, iwconfig=None, fast=False): """ Get current network name. Returns: The name of the currently connected network. """ - return self.wiface.GetCurrentNetwork(iwconfig) + return self.wiface.GetCurrentNetwork(iwconfig, fast) - def GetIP(self): + def GetIP(self, fast=False): """ Get the IP of the interface. Returns: The IP address of the interface in dotted notation. """ - return self.wiface.GetIP() + return self.wiface.GetIP(fast) + + def GetBSSID(self, fast=True): + """ Get the BSSID of the current access point. + + Returns: + The MAC Adress of the active access point as a string, or + None the BSSID can't be found. + + """ + return self.wiface.GetBSSID(fast) def GetIwconfig(self): """ Get the out of iwconfig. """ @@ -641,8 +648,8 @@ class WirelessConnectThread(ConnectThread): if self.network.get('enctype'): self.SetStatus('validating_authentication') if not wiface.ValidateAuthentication(time.time()): - self.connect_aborted('bad_pass') - return + self.abort_connection('bad_pass') + self.abort_if_needed() # Set up gateway, IP address, and DNS servers. self.set_broadcast_address(wiface) @@ -657,7 +664,7 @@ class WirelessConnectThread(ConnectThread): self.SetStatus('done') print 'Connecting thread exiting.' if self.debug: - print "IP Address is: " + wiface.GetIP() + print "IP Address is: " + wiface.GetIP(fast=True) self.is_connecting = False def generate_psk_and_authenticate(self, wiface): @@ -709,14 +716,14 @@ class Wired(Controller): """ Load the wnettools controls for the wired/wireless interfaces. """ self.liface = wnettools.WiredInterface(self.wired_interface, self.debug) - def CheckPluggedIn(self): + def CheckPluggedIn(self, fast=False): """ Check whether the wired connection is plugged in. Returns: The status of the physical connection link. """ - return self.liface.GetPluggedIn() + return self.liface.GetPluggedIn(fast) def Connect(self, network, debug=False): """ Spawn a connection thread to connect to the network. @@ -734,14 +741,14 @@ class Wired(Controller): self.connecting_thread.start() return True - def GetIP(self): + def GetIP(self, fast=False): """ Get the IP of the interface. Returns: The IP address of the interface in dotted notation. """ - return self.liface.GetIP() + return self.liface.GetIP(fast) def Disconnect(self): """ Disconnect from the network. """ @@ -858,5 +865,5 @@ class WiredConnectThread(ConnectThread): self.SetStatus('done') print 'Connecting thread exiting.' if self.debug: - print "IP Address is: " + liface.GetIP() + print "IP Address is: " + liface.GetIP(fast=True) self.is_connecting = False diff --git a/wicd.py b/wicd.py index 8b5b934..5d5b7c0 100755 --- a/wicd.py +++ b/wicd.py @@ -170,7 +170,7 @@ class TrayIcon: .replace('$A', self.network) .replace('$B', sig_string) .replace('$C', str(wireless_ip))) - self.set_signal_image(strength, lock) + self.set_signal_image(int(strength), lock) def set_connecting_state(self, info): """ Sets the icon info for a connecting state. """ diff --git a/wnettools.py b/wnettools.py index 74fbdfd..3597726 100644 --- a/wnettools.py +++ b/wnettools.py @@ -33,8 +33,13 @@ class WirelessInterface() -- Control a wireless network interface. import misc import re +import os import wpath import time +import socket +import fcntl +import struct +import array # Compile the regex patterns that will be used to search the output of iwlist # scan for info these are well tested, should work on most cards @@ -60,6 +65,15 @@ wpa2_pattern = re.compile('(WPA2)', re.I | re.M | re.S) auth_pattern = re.compile('.*wpa_state=(.*?)\n', re.I | re.M | re.S) RALINK_DRIVER = 'ralink legacy' +SIOCGIWESSID = 0x8B1B +SIOCGIFADDR = 0x8915 +SIOCGIWSTATS = 0x8B0F +SIOCGIFHWADDR = 0x8927 +SIOCGMIIPHY = 0x8947 +SIOCGETHTOOL = 0x8946 +SIOCGIFFLAGS = 0x8913 +SIOCGIWRANGE = 0x8B0B +SIOCGIWAP = 0x8B15 def SetDNS(dns1=None, dns2=None, dns3=None): """ Set the DNS of the system to the specified DNS servers. @@ -123,7 +137,10 @@ def _fast_get_wifi_interfaces(): device = re.compile('[a-z]{3,4}[0-9]') ifnames = [] - f = open('/proc/net/wireless', 'r') + try: + f = open('/proc/net/wireless', 'r') + except IOError: + return None data = f.readlines() f.close() for line in data: @@ -136,6 +153,20 @@ def _fast_get_wifi_interfaces(): return ifnames[0] else: return None + +def get_iw_ioctl_result(iface, call): + s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + buff = array.array('c', '\0' * 32) + addr, length = buff.buffer_info() + arg = struct.pack('Pi', addr, length) + data = (iface + '\0' * 16)[:16] + arg + try: + result = fcntl.ioctl(s.fileno(), call, data) + except IOError: + return None + except OSError: + return None + return buff.tostring() class Interface(object): """ Control a network interface. """ @@ -157,6 +188,7 @@ class Interface(object): self.IP_FOUND = False self.flush_tool = None self.link_detect = None + self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) self.Check() def SetDebugMode(self, value): @@ -172,7 +204,18 @@ class Interface(object): """ self.iface = str(iface) + def _find_client_path(self, client): + paths = ['/sbin/', '/usr/sbin/', '/bin/', '/usr/bin/', + '/usr/local/sbin/', '/usr/local/bin/'] + for path in paths: + if os.access("%s%s" % (path, client), os.F_OK): + return "%s%s" % (path, client) + if self.verbose: + "WARNING: No path found for %s" % (client) + return None + def _client_found(self, client): + # TODO: Don't use which anymore. Just search path manually. output = misc.Run("which " + client) if output and not ("no " + client) in output: return True @@ -188,46 +231,57 @@ class Interface(object): """ if self.DHCP_CLIENT: - DHCP_CLIENT = self.DHCP_CLIENT + dhcp_client = self.DHCP_CLIENT else: - DHCP_CLIENT = None + dhcp_client = None + dhcp_path = None dhcpclients = ["dhclient", "dhcpcd", "pump"] for client in dhcpclients: - if self._client_found(client): - DHCP_CLIENT = client + dhcp_path = self._find_client_path(client) + if dhcp_path: + dhcp_client = client break - if not DHCP_CLIENT: - print "WARNING: NO DHCP CLIENT DETECTED! Make sure there is one \ - set in your path." + if not dhcp_client: + print "WARNING: No supported DHCP Client could be found!" return - elif DHCP_CLIENT in [misc.DHCLIENT, "dhclient"]: - DHCP_CLIENT = misc.DHCLIENT - DHCP_CMD = "dhclient" - DHCP_RELEASE = "dhclient -r" - elif DHCP_CLIENT in [misc.PUMP, "pump"]: - DHCP_CLIENT = misc.PUMP - DHCP_CMD = "pump -i" - DHCP_RELEASE = "pump -r -i" - elif DHCP_CLIENT in [misc.DHCPCD, "dhcpcd"]: - DHCP_CLIENT = misc.DHCPCD - DHCP_CMD = "dhcpcd" - DHCP_RELEASE = "dhcpcd -r" + elif dhcp_client in [misc.DHCLIENT, "dhclient"]: + dhcp_client = misc.DHCLIENT + dhcp_cmd = dhcp_path + dhcp_release = dhcp_cmd + " -r" + elif dhcp_client in [misc.PUMP, "pump"]: + dhcp_client = misc.PUMP + dhcp_cmd = dhcp_path + " -i" + dhcp_release = dhcp_cmd + " -r -i" + elif dhcp_client in [misc.DHCPCD, "dhcpcd"]: + dhcp_client = misc.DHCPCD + dhcp_cmd = dhcp_path + dhcp_release = dhcp_cmd + " -r" + else: + dhcp_client = None + dhcp_cmd = None + dhcp_release = None - self.DHCP_CMD = DHCP_CMD - self.DHCP_RELEASE = DHCP_RELEASE - self.DHCP_CLIENT = DHCP_CLIENT + self.DHCP_CMD = dhcp_cmd + self.DHCP_RELEASE = dhcp_release + self.DHCP_CLIENT = dhcp_client def CheckWiredTools(self): """ Check for the existence of ethtool and mii-tool. """ - if self._client_found("mii-tool"): + miitool_path = self._find_client_path("mii-tool") + if miitool_path: + self.miitool_cmd = miitool_path self.MIITOOL_FOUND = True else: + self.miitool_cmd = None self.MIITOOL_FOUND = False - if self._client_found("ethtool"): + ethtool_path = self._find_client_path("ethtool") + if ethtool_path: + self.ethtool_cmd = ethtool_path self.ETHTOOL_FOUND = True else: + self.ethtool_cmd = None self.ETHTOOL_FOUND = False def Check(self): @@ -236,9 +290,12 @@ class Interface(object): self.CheckDHCP() self.CheckWiredTools() - if self._client_found("ip"): + ip_path = self._find_client_path("ip") + if ip_path: + self.ip_cmd = ip_path self.IP_FOUND = True else: + self.ip_cmd = None self.IP_FOUND = False def Up(self): @@ -427,25 +484,48 @@ class Interface(object): if self.verbose: print cmd misc.Run(cmd) - def GetIP(self): + def GetIP(self, fast=False): """ Get the IP address of the interface. Returns: The IP address of the interface in dotted quad form. """ + if fast: + return self._fast_get_ip() cmd = 'ifconfig ' + self.iface if self.verbose: print cmd output = misc.Run(cmd) return misc.RunRegex(ip_pattern, output) - def IsUp(self): + def _fast_get_ip(self): + """ Gets the IP Address of the interface using ioctl. + + Using ioctl calls to get the IP Address info is MUCH faster + than calling ifconfig and paring it's output. It's less + portable though, so there may be problems with it on some + systems. + + """ + ifstruct = struct.pack('256s', self.iface) + try: + raw_ip = fcntl.ioctl(self.sock.fileno(), SIOCGIFADDR, ifstruct) + except IOError: + return None + except OSError: + return None + + return socket.inet_ntoa(raw_ip[20:24]) + + def IsUp(self, fast=True): """ Determines if the interface is up. Returns: True if the interface is up, False otherwise. """ + if fast: + return self._fast_is_up() cmd = "ifconfig " + self.iface output = misc.Run(cmd) lines = output.split('\n') @@ -457,6 +537,19 @@ class Interface(object): return True return False + + def _fast_is_up(self): + data = (self.iface + '\0' * 16)[:18] + try: + result = fcntl.ioctl(self.sock.fileno(), SIOCGIFFLAGS, data) + except IOError, e: + if self.verbose: + print "SIOCGIFFLAGS failed: " + str(e) + return False + + flags, = struct.unpack('H', result[16:18]) + return bool(flags & 1) + class WiredInterface(Interface): @@ -471,7 +564,7 @@ class WiredInterface(Interface): """ Interface.__init__(self, iface, verbose) - def GetPluggedIn(self): + def GetPluggedIn(self, fast=False): """ Get the current physical connection state. The method will first attempt to use ethtool do determine @@ -485,21 +578,23 @@ class WiredInterface(Interface): if not self.iface: return False if self.ETHTOOL_FOUND and self.link_detect != misc.MIITOOL: - return self._eth_get_plugged_in() + return self._eth_get_plugged_in(fast) elif self.MIITOOL_FOUND: - return self._mii_get_plugged_in() + return self._mii_get_plugged_in(fast) else: print 'Error: No way of checking for a wired connection. Make \ sure that either mii-tool or ethtool is installed.' return False - def _eth_get_plugged_in(self): + def _eth_get_plugged_in(self, fast): """ Use ethtool to determine the physical connection state. Returns: True if a link is detected, False otherwise. """ + if fast: + self._fast_eth_get_plugged_in() link_tool = 'ethtool' if not self.IsUp(): print 'Wired Interface is down, putting it up' @@ -511,14 +606,33 @@ class WiredInterface(Interface): return True else: return False + + def _fast_eth_get_plugged_in(self): + if not self.IsUp(): + self.Up() + time.sleep(1) + buff = array.array('i', [0x0000000a, 0x00000000]) + addr, length = buff.buffer_info() + arg = struct.pack('Pi', addr, length) + data = (self.iface + '\0' * 16)[:16] + arg + try: + fcntl.ioctl(self.sock.fileno(), SIOCGETHTOOL, data) + except IOError, e: + if self.verbose: + print 'SIOCGETHTOOL failed: ' + str(e) + return False + return bool(buff.tolist()[1]) + - def _mii_get_plugged_in(self): + def _mii_get_plugged_in(self, fast): """ Use mii-tool to determine the physical connection state. Returns: True if a link is detected, False otherwise. """ + if fast: + return self._fast_mii_get_plugged_in() link_tool = 'mii-tool' tool_data = misc.Run(link_tool + ' ' + self.iface, True) if misc.RunRegex(re.compile('(Invalid argument)', re.I | re.M | re.S), @@ -534,6 +648,21 @@ class WiredInterface(Interface): else: return False + def _fast_mii_get_plugged_in(self): + """ Get link status usingthe SIOCGMIIPHY ioctl. """ + if not self.IsUp(): + self.Up() + time.sleep(1) + buff = struct.pack('16shhhh', (self.iface + '\0' * 16)[:16], 0, 1, + 0x0004, 0) + try: + result = fcntl.ioctl(self.sock.fileno(), SIOCGMIIPHY, buff) + except IOError, e: + if self.verbose: + print 'SIOCGMIIPHY failed: ' + str(e) + return False + reg = struct.unpack('16shhhh', result)[-1] + return bool(reg & 0x0004) class WirelessInterface(Interface): """ Control a wireless network interface. """ @@ -974,27 +1103,44 @@ class WirelessInterface(Interface): cmd = 'iwpriv ' + self.iface + ' ' if self.verbose: print cmd misc.Run(cmd) + + def GetBSSID(self, fast=True): + """ Get the MAC address for the interface. """ + if fast: + return self._fast_get_bssid() + else: + return "" + + def _fast_get_bssid(self): + """ Gets the MAC address for the connected AP using ioctl calls. """ + data = (self.iface + '\0' * 32)[:32] + try: + result = fcntl.ioctl(self.sock.fileno(), SIOCGIWAP, data)[16:] + except IOError, e: + if self.verbose: + print "SIOCGIWAP failed: " + str(e) + return "" + raw_addr = struct.unpack("xxBBBBBB", result[:8]) + return "%02X:%02X:%02X:%02X:%02X:%02X" % raw_addr + - def GetSignalStrength(self, iwconfig=None): + def GetSignalStrength(self, iwconfig=None, fast=False): """ Get the signal strength of the current network. Returns: The signal strength. """ + if fast: + return self._get_signal_strength_fast() + if not iwconfig: cmd = 'iwconfig ' + self.iface if self.verbose: print cmd output = misc.Run(cmd) else: output = iwconfig - # implemented the patch provided in - # https://bugs.launchpad.net/wicd/+bug/175104 - # it was for the stable version, so I've improvised here - # should work though - - #strength = misc.RunRegex(strength_pattern,output) [(strength, max_strength)] = strength_pattern.findall(output) if max_strength and strength: @@ -1004,14 +1150,45 @@ class WirelessInterface(Interface): strength = misc.RunRegex(altstrength_pattern, output) return strength - - def GetDBMStrength(self, iwconfig=None): + + def _get_signal_strength_fast(self): + """ Get the link quality using ioctl calls. """ + buff = get_iw_ioctl_result(self.iface, SIOCGIWSTATS) + strength = ord(buff[2]) + max_strength = self._get_max_strength_fast() + if strength and max_strength: + return 100 * int(strength) // int(max_strength) + + return strength + + def _get_max_strength_fast(self): + """ Gets the maximum possible strength from the wireless driver. """ + buff = array.array('c', '\0' * 700) + addr, length = buff.buffer_info() + arg = struct.pack('Pi', addr, length) + iwfreq = (self.iface + '\0' * 16)[:16] + arg + try: + result = fcntl.ioctl(self.sock.fileno(), SIOCGIWRANGE, iwfreq) + except IOError, e: + if self.verbose: + print "SIOCGIWRANGE failed: " + str(e) + return None + fmt = "iiihb6ii4B4Bi32i2i2i2i2i3h8h2b2bhi8i2b3h2i2ihB17x" + 32*"ihbb" + size = struct.calcsize(fmt) + data = buff.tostring() + data = data[0:size] + values = struct.unpack(fmt, data) + return values[12] + + def GetDBMStrength(self, iwconfig=None, fast=False): """ Get the dBm signal strength of the current network. Returns: The dBm signal strength. """ + if fast: + return self._get_dbm_strength_fast() if iwconfig: cmd = 'iwconfig ' + self.iface if self.verbose: print cmd @@ -1020,15 +1197,25 @@ class WirelessInterface(Interface): output = iwconfig dbm_strength = misc.RunRegex(signaldbm_pattern, output) return dbm_strength + + def _get_dbm_strength_fast(self): + buff = misc.get_irwange_ioctl_result(self.iface, SIOCGIWSTATS) + if not buff: + return None + + return str((ord(buff[3]) - 256)) - def GetCurrentNetwork(self, iwconfig=None): + def GetCurrentNetwork(self, iwconfig=None, fast=False): """ Get the essid of the current network. Returns: The current network essid. """ + if fast: + return self._get_essid_fast() + if not iwconfig: cmd = 'iwconfig ' + self.iface if self.verbose: print cmd @@ -1040,3 +1227,12 @@ class WirelessInterface(Interface): if network: network = misc.to_unicode(network) return network + + def _get_essid_fast(self): + """ Get the current essid using ioctl. """ + buff = get_iw_ioctl_result(self.iface, SIOCGIWESSID) + if not buff: + return None + + return buff.strip('\x00') +