From bbe664ca53f0c9ec431f059747bcf8596a2d03b0 Mon Sep 17 00:00:00 2001
From: Akkana Peck
Date: Sun, 25 Mar 2012 19:32:39 -0700
Subject: [PATCH] Support both old and new ifconfig output formats; better
reliability for open wi-fi 802.11b connections; monitor success of starting
dhcp client; don't run ifup twice on debian; rename 'add' option to 'multi';
better method documentation.
---
netscheme | 57 +++++++++++++++++++++++++++++------------
netutils.html | 67 +++++++++++++++++++++---------------------------
netutils.py | 70 +++++++++++++++++++++++++++++++++------------------
3 files changed, 115 insertions(+), 79 deletions(-)
diff --git a/netscheme b/netscheme
index a9c1879..d68fe2c 100755
--- a/netscheme
+++ b/netscheme
@@ -1,6 +1,7 @@
#! /usr/bin/env python
#
-# Copyright 2010 by Akkana Peck akkana@shallowsky.com
+# Network scheme-setting tool, version 1.4.
+# Copyright 2010-2012 by Akkana Peck akkana@shallowsky.com
# ... share and enjoy under the GPLv2 or (at your option) later.
#
@@ -93,7 +94,7 @@ class NetScheme :
return s
def dump(self, filename=None) :
- print "dump(", filename, ")"
+ print 'dump(', filename, ')'
if filename == None :
filename = homedirfile(".netscheme")
print "Appending new scheme to", filename
@@ -111,6 +112,7 @@ class NetScheme :
up_ifaces = netutils.get_interfaces(True)
for i in up_ifaces :
print "Taking", i, "down with ifdown"
+ i.ifconfig_down()
subprocess.call(["ifdown", i.name])
if add :
@@ -143,7 +145,6 @@ auto %s""" % (iface.name)
print "Encryption:", self.encryption
if self.encryption == "wpa" or self.encryption == "wpa2" :
- print "It's WPA"
if self.essid :
print >>fp, "wpa-ssid", self.essid
if self.key :
@@ -186,8 +187,8 @@ auto %s""" % (iface.name)
if self.essid :
iwargs.append("essid")
iwargs.append(self.essid)
- #iwargs.append("mode")
- #iwargs.append("managed")
+ iwargs.append("mode")
+ iwargs.append("managed")
if accesspoint and accesspoint.address :
iwargs.append("ap")
iwargs.append(accesspoint.address)
@@ -197,11 +198,16 @@ auto %s""" % (iface.name)
else :
iwargs.append("key")
iwargs.append("off")
- iwargs.append("enc")
- iwargs.append("off")
+ #iwargs.append("enc")
+ #iwargs.append("off")
+ iwargs.append("channel")
+ iwargs.append("auto")
print "Calling", iwargs
subprocess.call(iwargs)
+ print "Called iwconfig: now it says"
+ os.system("iwconfig")
+
elif self.encryption == "wpa" :
tempfp = tempfile.NamedTemporaryFile(prefix="wpasup",
delete=False)
@@ -215,19 +221,22 @@ key_mgmt=WPA-PSK
psk="%s"
}""" % (self.essid, self.key)
tempfp.close()
- print "Starting wpa_supplicant"
subprocess.Popen(["wpa_supplicant", "wpa_supplicant",
"-Dwext", "-i", iface.name, "-c", tempname])
# Is 1 second long enough for wpa_supplicant to open the file?
# 2 might be safer. What a drag.
time.sleep(2)
- print "removing tmp file"
os.unlink(tempname)
if self.dhcp :
print "Getting dhcp"
- subprocess.call(["dhcpcd", "-G", "-C", "resolv.conf", iface.name])
- #subprocess.call(["dhclient", "-v", iface.name])
+ try :
+ subprocess.check_call(["dhcpcd", "-G", "-C", "resolv.conf",
+ iface.name])
+ #subprocess.call(["dhclient", "-v", iface.name])
+ except subprocess.CalledProcessError, e :
+ print "DHCP failed, error", e.returncode
+ iface.ifconfig_down()
else :
if not self.ip :
@@ -256,6 +265,7 @@ psk="%s"
# find the first wireless interface
for i in all :
print "Trying interface", i.name
+ if i.wireless: print i.name, "is wireless"
# The clause from hell. What this does is:
# - If the scheme has an encryption setting (including "open")
# then it's a wireless scheme and needs a wireless interface.
@@ -307,8 +317,17 @@ psk="%s"
# Mark the interface up (and call ifup too, if applicable)
subprocess.call(["ifconfig", iface.name, "up"])
+ # This somehow ends up being the second time it's called.
+ # So don't do that!
if is_deb :
- subprocess.call(["ifup", iface.name])
+ # At one point I thought it was a good idea to call ifup
+ # explicitly. But at least with some cards, ifup associates
+ # with the accesspoint and calls DHCP -- all of which is
+ # about to be done again from service networking restart.
+ # And calling it the second time can fail (e.g. for Broadcom
+ # BCM4313 where the driver needs to be reloaded before
+ # each association).
+ #subprocess.call(["ifup", iface.name])
subprocess.call(["service", "networking", "restart"])
# Try to make the routing tables sane:
@@ -323,7 +342,6 @@ psk="%s"
if r.dest == 'default' or r.dest == '0.0.0.0' :
defaults.append(r)
print "Default route:", r
- print "============"
if len(defaults) > 1 :
wired_route = None
# Find the first wired route
@@ -415,6 +433,10 @@ def find_and_set_scheme(newscheme, add=False) :
scheme.set_scheme(add)
return
+ # XXX TO DO: use python soundex module if available
+ # to see if there's a similarly named scheme --
+ # for instance, "Coffee Bean" instead of "coffeebean".
+
print "No scheme named", newscheme
aplist = netutils.get_accesspoints()
@@ -435,6 +457,8 @@ def find_and_set_scheme(newscheme, add=False) :
if ans == 'y' or ans == 'Y' :
scheme.dump()
+ return
+
#
# A couple of default schemes:
#
@@ -472,9 +496,9 @@ if __name__ == "__main__" :
default=False,
help="Reset the connection without changing scheme")
parser.add_option("-m", "--multi",
- action="store_true", dest="add",
+ action="store_true", dest="multi",
default=False,
- help="Add a new interface without bringing down current one")
+ help="Multiple interfaces: add a new interface without bringing down current one")
parser.add_option("-s", "--save",
action="store_true", dest="save_new_scheme",
default=False,
@@ -511,6 +535,7 @@ if __name__ == "__main__" :
newscheme.interface = config.get(scheme, 'interface')
Schemes.append(newscheme)
+ # Schemes are read, options are read, time to do stuff.
if (options.list_schemes) :
list_schemes()
elif (options.list_accesspoints) :
@@ -521,6 +546,6 @@ if __name__ == "__main__" :
#print parser.usage
print_current_scheme()
else :
- find_and_set_scheme(args[0], options.add)
+ find_and_set_scheme(args[0], options.multi)
except KeyboardInterrupt :
print "Interrupt"
diff --git a/netutils.html b/netutils.html
index 14d670d..20163c1 100644
--- a/netutils.html
+++ b/netutils.html
@@ -1,33 +1,20 @@
-Python: module netutils-1.3
+Python: module netutils
+>index
/home/akkana/bin/netutils.py
netutils: a set of networking utilities for Python.
Copyright 2010 by Akkana Peck <akkana@shallowsky.com>
... share and enjoy under the GPLv2 or (at your option) later.
-Provides the following:
-
-Classes:
- NetInterface: name, ip, broadcast, netmask, essid, encryption, wireless
- AccessPoint : address, essid, encryption, quality, interface
- Route : display or change network routing tables.
-
-Functions outside of classes:
- get_interfaces(only_up=False): returns a list of all NetInterface.
- If only_up, will only return the ones currently marked UP.
- get_wireless_interfaces(): returns a list of wireless NetInterface.
- get_accesspoints(): returns a list of visible AccessPoints.
- ifdown_all(): take all interfaces down, killing any wpa_supplicant or dhcp
- kill_by_name(namelist): Kill a any running processes that include any
- of the names in namelist. Return a list of actual process names killed.
+Provides classes and functions to allow finding a wireless interface,
+querying for accesspoints, or establishing a connection.
@@ -47,16 +34,16 @@
| |
-- AccessPoint
-
- Connection
+
- AccessPoint
+
- Connection
-
-- DebianConnection
-
- ManualConnection
+
- DebianConnection
+
- ManualConnection
-- NetInterface
-
- Route
+
- NetInterface
+
- Route
@@ -65,7 +52,7 @@
class AccessPoint
|
-One Cell or AccessPoint from iwlist output |
+One Cell or wireless access point from iwlist output |
|
Methods defined here:
- __init__(self)
@@ -78,7 +65,10 @@
|
class Connection |
- | |
+ |
+Make a connection. Subclass this with details of how the
+connection will actually be made. |
+ |
Methods defined here:
- __init__(self, iface=None)
@@ -86,9 +76,12 @@
-class DebianConnection(Connection) |
+class DebianConnection(Connection)
- | |
+ |
+Make a connection on a Debian system, using /etc/network/interfaces
+and service network restart (along with other helpers). |
+ |
Methods defined here:
- __init__(self, iface=None)
@@ -101,9 +94,14 @@
-class ManualConnection(Connection) |
+class ManualConnection(Connection)
- | |
+ |
+Make a connection by calling explicit programs like ifconfig,
+iwconfig, wpasupplicant, etc. In theory should work on any
+Linux machine, but some distros (especially Debian derivatives)
+may not work well and may need their own connection type. |
+ |
Methods defined here:
- __init__(self, iface=None)
@@ -179,12 +177,5 @@
- ifdown_all()
- Take all current interfaces down.
- kill_by_name(namelist)
- Kills all running processes that start with any of the
strings in the given name list.
- |
-
-
-
-Data |
-
- | |
-__warningregistry__ = {("Parent module 'netutils-1' not found while handling absolute import", <type 'exceptions.RuntimeWarning'>, 28): True} |
+ |
\ No newline at end of file
diff --git a/netutils.py b/netutils.py
index ed4f66c..e5a6354 100755
--- a/netutils.py
+++ b/netutils.py
@@ -5,24 +5,11 @@
# ... share and enjoy under the GPLv2 or (at your option) later.
"""netutils: a set of networking utilities for Python.
-Copyright 2010, 2011 by Akkana Peck
+Copyright 2010 by Akkana Peck
... share and enjoy under the GPLv2 or (at your option) later.
-Provides the following:
-
-Classes:
- NetInterface: name, ip, broadcast, netmask, essid, encryption, wireless
- AccessPoint : address, essid, encryption, quality, interface
- Route : display or change network routing tables.
-
-Functions outside of classes:
- get_interfaces(only_up=False): returns a list of all NetInterface.
- If only_up, will only return the ones currently marked UP.
- get_wireless_interfaces(): returns a list of wireless NetInterface.
- get_accesspoints(): returns a list of visible AccessPoints.
- ifdown_all(): take all interfaces down, killing any wpa_supplicant or dhcp
- kill_by_name(namelist): Kill a any running processes that include any
- of the names in namelist. Return a list of actual process names killed.
+Provides classes and functions to allow finding a wireless interface,
+querying for accesspoints, or establishing a connection.
"""
import os, subprocess, re, shutil
@@ -73,6 +60,9 @@ def ifconfig_down(self) :
class Connection :
+ """Make a connection. Subclass this with details of how the
+ connection will actually be made.
+ """
def __init__(self, iface=None) :
if iface :
self.iface = iface
@@ -82,6 +72,11 @@ def __init__(self, iface=None) :
self.essid = "unknown"
class ManualConnection(Connection) :
+ """Make a connection by calling explicit programs like ifconfig,
+ iwconfig, wpasupplicant, etc. In theory should work on any
+ Linux machine, but some distros (especially Debian derivatives)
+ may not work well and may need their own connection type.
+ """
def __init__(self, iface=None):
Connection.__init__(self, iface)
@@ -128,6 +123,9 @@ def reset(self):
self.essid = None
class DebianConnection(Connection) :
+ """Make a connection on a Debian system, using /etc/network/interfaces
+ and service network restart (along with other helpers).
+ """
def __init__(self, iface=None) :
Connection.__init__(self, iface)
self.interfaces = "/etc/network/interfaces"
@@ -196,7 +194,7 @@ def reset(self ):
self.essid = "unknown"
class AccessPoint :
- """ One Cell or AccessPoint from iwlist output"""
+ """ One Cell or wireless access point from iwlist output"""
def __init__(self) :
self.clear()
@@ -332,17 +330,28 @@ def get_interfaces(only_up=False, name=None) :
if line[0] != ' ' :
# It's a new interface. Should have a line like:
# eth0 Link encap:Ethernet HWaddr 00:01:4A:98:F1:51
+ # or else a line line:
+ # flags=4098
+ # with no LOOPBACK flag.
# We only want the encap:Ethernet lines, not others like
# loopback, vpn, ipv6 etc.
- if words[2] == 'encap:Ethernet' :
+ if words[2] == 'encap:Ethernet' or \
+ words[1].startswith('flags') and not 'LOOPBACK' in words[1]:
+ if words[0].endswith(':') :
+ words[0] = words[0][0:-1]
cur_iface = NetInterface(words[0])
ifaces.append(cur_iface)
+
+ if words[2].startswith('flags') : # new format
+ cur_iface.up = ('UP' in words[2])
+
else :
cur_iface = None
else :
if not cur_iface :
continue
if words[0] == 'inet' :
+ # Old format:
# inet addr:192.168.1.6 Bcast:192.168.1.255 Mask:255.255.255.0
match = re.search('addr:(\d+\.\d+\.\d+\.\d+)', line)
if match :
@@ -353,29 +362,36 @@ def get_interfaces(only_up=False, name=None) :
match = re.search('Mask:(\d+\.\d+\.\d+\.\d+)', line)
if match :
cur_iface.netmask = match.group(1)
+ # New format:
+ # inet 127.0.0.1 netmask 255.0.0.0
+ match = re.search('inet (\d+\.\d+\.\d+\.\d+)', line)
+ if match :
+ cur_iface.ip = match.group(1)
+ match = re.search('netmask (\d+\.\d+\.\d+\.\d+)', line)
+ if match :
+ cur_iface.netmask = match.group(1)
+ match = re.search('ether (..:..:..:..:..:..)', line)
+ if match :
+ cur_iface.mac = match.group(1)
elif words[0] == 'UP' :
cur_iface.up = True
# Now we have the list of all interfaces. Find out which are wireless:
proc = subprocess.Popen('iwconfig', shell=False,
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
- # TEMPORARY, FOR TESTING ON DESKTOPS:
- #proc = subprocess.Popen('cat /home/akkana/iwconfig.out', shell=True,
- # stdout=subprocess.PIPE, stderr=subprocess.PIPE)
stdout_str = proc.communicate()[0]
stdout_list = stdout_str.split('\n')
cur_iface = None
for line in stdout_list :
- # print "line", line
if len(line) == 0 :
continue
if line[0] != ' ' :
words = line.split()
- # print "New interface", words[0]
+ #print "Wireless interface", words[0]
for iface in ifaces :
- # print "Checking", words[0], "against", iface.name
+ #print "Checking", words[0], "against", iface.name
if iface.name == words[0] :
- # print "It's in the list"
+ #print "It's in the list"
cur_iface = iface
cur_iface.wireless = True
match = re.search('ESSID:"(.*)"', line)
@@ -590,6 +606,10 @@ def kill_by_name(namelist) :
# main
if __name__ == "__main__" :
+ print "All interfaces:"
for iface in get_interfaces() :
print iface
+ print "Wireless interfaces:"
+ for iface in get_wireless_interfaces() :
+ print iface
| |