Packaging fix: claim ownership of pkgdatadir.
[cnetworkmanager.git] / cnetworkmanager
blob02b0bec0be02b0941cc7fa910971a8367a4fd681
1 #! /usr/bin/python
2 # cnetworkmanager: Command Line Interface for NetworkManager
3 # by: http://en.opensuse.org/User:Mvidner
4 # license: http://www.gnu.org/licenses/gpl-2.0.html or later
6 VERSION = "0.8.3"
7 print "cnetworkmanager %s - Command Line Interface for NetworkManager" % VERSION
9 norpm = False
10 import sys
11 # find other modules in our prefix, if specified
12 if len(sys.argv) > 2 and sys.argv[1] == "--prefix":
13 prefix = sys.argv[2]
14 sys.argv[1:] = sys.argv[3:]
15 sys.path.append(prefix + "/share/cnetworkmanager");
17 import os
18 import string
19 import re
20 import time
21 import uuid
22 import math
23 import hashlib
24 import pbkdf2
25 from binascii import hexlify
26 import ConfigParser # knm config
27 from optparse import OptionParser
28 try:
29 import dbus
30 import dbus.service
31 import _dbus_bindings
32 except:
33 print "Install python-1-dbus.rpm or or python-dbus.rpm or python-dbus.deb"
34 norpm = True
35 import xml.dom.minidom
36 try:
37 import gobject
38 except:
39 # todo - only if loop wanted
40 print "Install python-gobject2.rpm or pygobject2.rpm or python-gobject.deb"
41 norpm = True
42 # python-gnome.rpm has gconf for nm-applet...
43 if norpm:
44 sys.exit(1)
46 from dbus.mainloop.glib import DBusGMainLoop
47 DBusGMainLoop(set_as_default=True)
49 LOOP = False
51 bus = dbus.SystemBus()
53 # FOOC = connection (service) string
54 # FOOI = interface string
55 # fooo = object
56 # fooi = interface
57 # foopi = property interface
58 NMC = 'org.freedesktop.NetworkManager'
59 NMI = NMC
60 PI = 'org.freedesktop.DBus.Properties'
61 SSC = "org.freedesktop.NetworkManagerSystemSettings"
62 USC = "org.freedesktop.NetworkManagerUserSettings"
63 NMIC = "org.freedesktop.NetworkManagerInfo"
65 def introspect(obj):
66 ii = dbus.Interface(obj, 'org.freedesktop.DBus.Introspectable')
67 print ii.Introspect()
69 class cNM:
70 # TODO: pull them from introspection.xml
71 NM_STATE = ["UNKNOWN", "ASLEEP", "CONNECTING", "CONNECTED", "DISCONNECTED",]
73 def __init__(self, opath):
74 self.opath = opath
75 self.nmo = bus.get_object(NMC, self.opath)
76 self.nmi = dbus.Interface(self.nmo, NMI)
77 self.nmpi = dbus.Interface(self.nmo, PI)
79 def Api(self):
80 return "common"
82 def Dump0(self):
83 "Dumps its own info (not owned objects)."
84 pass
86 def Dump(self):
87 self.Dump0()
88 if options.dev:
89 for device in self.Devices():
90 device.Dump()
92 if options.actcon:
93 print "Active Connections"
94 aconns = self.ActiveConnections()
95 for aconn in aconns:
96 aconn.Dump()
98 def ListNets(self):
99 print "Wifi Networks:"
100 for dev in self.Devices():
101 dev.ListNets()
103 def reply_handler(self, opath):
104 print "Connected:", opath
106 def err_handler(self, *args):
107 print "ERR:", args
109 def silent_handler(self, *args):
110 pass
111 print "BOO!:", args
113 def quitter_handler(self, *args):
114 # exit the loop that runs only because of us
115 print "padla"
116 sys.exit(0)
119 def make_nm(opath):
120 "Detects NM version and chooses appropriate class"
122 nmo = bus.get_object(NMC, opath)
123 nmi = dbus.Interface(nmo, NMI)
124 try:
125 dummy = nmi.getDevices()
126 return cNM_06(opath)
127 except dbus.exceptions.DBusException:
128 return cNM_07(opath)
130 class cNM_06(cNM):
131 def Api(self):
132 return "06"
134 def SetWifiEnabled(self, v):
135 # TODO: async call, catch the state signal and exit
136 # weird: synchronous call works, but times out
137 # asynchronous call does not work
138 self.nmi.setWirelessEnabled(v,
139 reply_handler=self.quitter_handler,
140 error_handler=self.quitter_handler)
141 global LOOP
142 LOOP = True
144 def SetOnline(self, v):
145 if v:
146 self.nmi.wake(True,
147 reply_handler=self.quitter_handler,
148 error_handler=self.quitter_handler)
149 else:
150 self.nmi.sleep(True,
151 reply_handler=self.quitter_handler,
152 error_handler=self.quitter_handler)
153 global LOOP
154 LOOP = True
156 def Dump0(self):
157 print "State:", self.NM_STATE[self.nmi.state()]
158 we = self.nmi.getWirelessEnabled()
159 if isinstance(we, tuple):
160 print "Wifi enabled:", bool(we[0])
161 print "Wifi HW enabled:", bool(we[1])
162 else:
163 print "Wifi enabled:", bool(we)
165 try:
166 dup = self.nmi.getDialup()
167 print "Dialup:", dup
168 except dbus.exceptions.DBusException, e:
169 #if e.get_dbus_name() == "org.freedesktop.NetworkManager.NoDialup":
170 # pass
171 #else:
172 print e
174 def Devices(self):
175 opaths = self.nmi.getDevices()
176 return map(cDevice_06, opaths)
178 def ActiveConnections(self):
179 return [] # at most one active connection, FIXME find it
181 def reply_handler(self):
182 print "Connection requested"
184 def err_handler(self, *args):
185 print "ERR:", args
187 def ActivateConnection(self, conn, device, ap):
188 # passing *_handler makes the call asynchronous
189 self.nmi.setActiveDevice(device.opath, ssid_str(conn.Ssid()),
190 reply_handler=self.reply_handler,
191 error_handler=self.err_handler,
194 class cNM_07(cNM):
195 def Api(self):
196 return "07"
198 def SetWifiEnabled(self, v):
199 self.nmpi.Set(NMI, "WirelessEnabled", v)
201 def SetOnline(self, v):
202 self.nmi.Sleep(not v)
204 def Dump0(self):
205 print "State:", self.NM_STATE[self.nmpi.Get(NMI, "State")]
206 print "Wifi enabled:", self.nmpi.Get(NMI, "WirelessEnabled")
207 print "Wifi HW enabled:", self.nmpi.Get(NMI, "WirelessHardwareEnabled")
209 def Devices(self):
210 opaths = self.nmi.GetDevices()
211 return map(cDevice_07, opaths)
213 def ActiveConnections(self):
214 aconns = self.nmpi.Get(NMI, "ActiveConnections")
215 return map(cActiveConnection, aconns)
217 def ActivateConnection(self, conn, device, ap):
218 # passing *_handler makes the call asynchronous
219 self.nmi.ActivateConnection(USC,
220 conn.__dbus_object_path__,
221 device.opath,
222 ap.opath,
223 reply_handler=self.reply_handler,
224 error_handler=self.err_handler,
228 class cActiveConnection:
229 def __init__(self, opath):
230 self.opath = opath
232 def Dump(self):
233 print self.opath
234 co = bus.get_object(NMC, self.opath)
235 copi = dbus.Interface(co, PI)
236 for P in ["ServiceName", "Connection", "SpecificObject",]:
237 print " %s: %s" % (P, copi.Get(NMI, P))
238 devs = copi.Get(NMI, "Devices")
239 print " Devices:"
240 for dev in devs:
241 print " ", dev
243 def bitmask_str(map, value):
244 ret = []
245 for mask in sorted(map.keys()):
246 if value & mask: ret.append(map[mask])
247 return ",".join(ret)
250 class cDevice:
251 def __init__(self, opath):
252 self.opath = opath
253 self.devo = bus.get_object(NMC, self.opath)
254 self.devi = dbus.Interface(self.devo, NMI + ".Device")
255 self.devpi = dbus.Interface(self.devo, PI)
256 self.dt = None
257 self.DeviceType0()
259 DEVICE_TYPE = ["UNKNOWN", "802_3_ETHERNET", "802_11_WIRELESS",
260 "GSM", "CDMA",] #OLPC: 3 is MESH
262 def DeviceType(self):
263 return self.DEVICE_TYPE[self.DeviceType0()]
265 def ip_str(self, i32):
266 ret = []
267 ret.append("%d" % (i32 % 256))
268 i32 /= 256
269 ret.append("%d" % (i32 % 256))
270 i32 /= 256
271 ret.append("%d" % (i32 % 256))
272 i32 /= 256
273 ret.append("%d" % (i32 % 256))
274 i32 /= 256
275 return ".".join(ret)
277 def DumpIp4Config(self, opath):
278 print " Ip4Config:", opath
279 o = bus.get_object(NMC, opath)
280 pi = dbus.Interface(o, PI)
281 try:
282 for P in ["Address", "Netmask", "Broadcast", "Gateway",]: # beta2?
283 print " %s: %s" % (P, self.ip_str(pi.Get(NMI, P)))
284 except:
285 print " Addresses:"
286 addrs = pi.Get(NMI, "Addresses")
287 for addr in addrs:
288 print " %s/%s via %s" % tuple(map(self.ip_str, addr))
289 nss = pi.Get(NMI, "Nameservers")
290 print " Nameservers:", " ".join(map(self.ip_str, nss))
291 doms = pi.Get(NMI, "Domains")
292 print " Domains:", " ".join(doms)
294 NM_DEVICE_CAP = {1: "NM_SUPPORTED", 2: "CARRIER_DETECT", 4: "SCANNING", }
297 def Dump(self):
298 print "Device:", self.opath
301 IW_MODE = ["AUTO", "ADHOC", "INFRA", "MASTER",
302 "REPEAT", "SECOND", "MONITOR",]
304 def APs(self):
305 return []
307 def ListNets(self):
308 for ap in self.APs():
309 ap.ListNets()
311 # mixin
312 class cDeviceEth:
313 pass
315 class cDevice_06(cDevice):
316 def DeviceType0(self):
317 if self.dt is None:
318 self.dt = self.devi.getProperties()[2]
319 if self.dt == 1:
320 self.__class__ = cDeviceEth_06
321 elif self.dt == 2:
322 self.__class__ = cDeviceWifi_06
323 return self.dt
325 NM_ACT_STAGE = [
326 "UNKNOWN", "DEVICE_PREPARE", "DEVICE_CONFIG", "NEED_USER_KEY",
327 "IP_CONFIG_START", "IP_CONFIG_GET", "IP_CONFIG_COMMIT",
328 "ACTIVATED", "FAILED", "CANCELLED", ]
330 def Dump(self):
331 cDevice.Dump(self)
332 print " Driver:", self.devi.getDriver()
333 props = self.devi.getProperties() # osusb ussss sssii biuus as
334 print " Self:", props[0] # o
335 print " Interface:", props[1] # s
336 print " Type:", self.DEVICE_TYPE[props[2]] # u
337 print " UDI:", props[3] # s
338 print " Active:", bool(props[4]) # b
339 print " Activation Stage:", self.NM_ACT_STAGE[props[5]] # u
340 print " IP:", props[6] # s
341 print " Mask:", props[7] # s
342 print " Bcast:", props[8] # s
343 print " HwAddress:", props[9] # s
344 print " GW:", props[10] # s
345 print " NS1:", props[11] # s
346 print " NS2:", props[12] # s
347 self.DumpMore()
349 def DumpMore(self):
350 print " (unknown device type, not dumping more)"
352 class cDeviceEth_06(cDevice_06, cDeviceEth):
353 def DumpMore(self):
354 props = self.devi.getProperties() # osusb ussss sssii biuus as
355 print " Link Active:", bool(props[15]) # b
356 print " Speed:", props[16] # i
357 print " Generic Capabilities:", bitmask_str(self.NM_DEVICE_CAP, props[17]) # u
359 class cDeviceWifi_06(cDevice_06):
360 NM_802_11_CAP = {
361 0x00000001: "PROTO_NONE",
362 0x00000002: "PROTO_WEP",
363 0x00000004: "PROTO_WPA",
364 0x00000008: "PROTO_WPA2",
365 0x00000010: "RESERVED1",
366 0x00000020: "RESERVED2",
367 0x00000040: "KEY_MGMT_PSK",
368 0x00000080: "KEY_MGMT_802_1X",
369 0x00000100: "RESERVED3",
370 0x00000200: "RESERVED4",
371 0x00000400: "RESERVED5",
372 0x00000800: "RESERVED6",
373 0x00001000: "CIPHER_WEP40",
374 0x00002000: "CIPHER_WEP104",
375 0x00004000: "CIPHER_TKIP",
376 0x00008000: "CIPHER_CCMP",
379 def APs(self):
380 self.wdevi = dbus.Interface(self.devo, NMI + ".Device.Wireless")
381 aps = self.devi.getProperties()[20]
382 return map(cAP_06, aps)
384 def DumpMore(self):
385 props = self.devi.getProperties() # osusb ussss sssii biuus as
386 print " Mode:", self.IW_MODE[props[13]] # i
387 print " Strength:", props[14] # i
388 print " Link Active:", bool(props[15]) # b
389 print " Speed:", props[16] # i
390 print " Generic Capabilities:", bitmask_str(self.NM_DEVICE_CAP, props[17]) # u
391 print " Capabilities:", bitmask_str(self.NM_802_11_CAP, props[18]) # u
392 print " Current net:", props[19] # s
393 nets = props[20] # as
394 print " Seen nets:", " ".join(nets)
395 if options.ap:
396 print " Access Points"
397 for ap in self.APs():
398 ap.Dump()
400 class cDevice_07(cDevice):
401 def DeviceType0(self):
402 if self.dt is None:
403 self.dt = self.devpi.Get(NMI, "DeviceType")
404 if self.dt == 1:
405 self.__class__ = cDeviceEth_07
406 elif self.dt == 2:
407 self.__class__ = cDeviceWifi_07
408 return self.dt
410 NM_DEVICE_STATE = [
411 "UNKNOWN", "UNMANAGED", "UNAVAILABLE", "DISCONNECTED", "PREPARE",
412 "CONFIG", "NEED_AUTH", "IP_CONFIG", "ACTIVATED", "FAILED",]
414 def Dump(self):
415 cDevice.Dump(self)
417 # "Ip4Config", only for NM_DEVICE_STATE_ACTIVATED
418 for P in ["Udi", "Interface", "Driver",]:
419 print " %s: %s" % (P, self.devpi.Get(NMI, P))
420 addr = self.devpi.Get(NMI, "Ip4Address")
421 print " Ip4Address:", self.ip_str(addr)
422 caps = self.devpi.Get(NMI, "Capabilities")
423 print " Capabilities:", bitmask_str(self.NM_DEVICE_CAP, caps)
424 state = self.NM_DEVICE_STATE[self.devpi.Get(NMI, "State")]
425 print " Dev State:", state
426 if state == "ACTIVATED":
427 self.DumpIp4Config(self.devpi.Get(NMI, "Ip4Config"))
429 dt = self.DeviceType()
430 print " Dev Type:", dt
431 self.DumpMore()
433 class cDeviceEth_07(cDevice_07, cDeviceEth):
434 def DumpMore(self):
435 for P in ["HwAddress", "Speed", "Carrier"]:
436 print " %s: %s" % (P, self.devpi.Get(NMI, P))
438 class cDeviceWifi_07(cDevice_07):
439 NM_802_11_DEVICE_CAP = {1:"CIPHER_WEP40", 2:"CIPHER_WEP104",
440 4:"CIPHER_TKIP", 8:"CIPHER_CCMP",
441 16:"WPA", 32:"RSN",}
443 def APs(self):
444 self.wdevi = dbus.Interface(self.devo, NMI + ".Device.Wireless")
445 aps = self.wdevi.GetAccessPoints()
446 return map(cAP_07, aps)
448 def DumpMore(self):
449 print " Dev Mode:", self.IW_MODE[self.devpi.Get(NMI, "Mode")]
450 wcaps = self.devpi.Get(NMI, "WirelessCapabilities")
451 print " Wifi Capabilities:", bitmask_str(self.NM_802_11_DEVICE_CAP, wcaps)
452 for P in ["HwAddress", "Bitrate", "ActiveAccessPoint"]:
453 print " %s: %s" % (P, self.devpi.Get(NMI, P))
454 if options.ap:
455 print " Access Points"
456 for ap in self.APs():
457 ap.Dump()
459 """An AP found around us"""
460 class cAP:
461 def __init__(self, opath):
462 self.opath = opath
463 self.apo = bus.get_object(NMC, self.opath)
464 self.appi = dbus.Interface(self.apo, PI)
465 # for _06
466 self.devi = dbus.Interface(self.apo, NMI + ".Devices")
468 NM_802_11_AP_FLAGS = {1: "PRIVACY",}
470 NM_802_11_AP_SEC = {
471 1: "PAIR_WEP40", 2: "PAIR_WEP104", 4: "PAIR_TKIP", 8: "PAIR_CCMP",
472 16: "GROUP_WEP40", 32: "GROUP_WEP104", 64: "GROUP_TKIP",
473 128: "GROUP_CCMP", 256: "KEY_MGMT_PSK", 512: "KEY_MGMT_802_1X",}
475 def ListNets(self, marker = " "):
476 # TODO *mark current
477 mbr = self.Mbr() / 1024 # 07 1000, 06 1024?
478 priv_s = self.PrivS()
479 print "%s%3d: %s (%dMb%s)" % (marker, self.Strength(), self.Ssid(), mbr, priv_s)
481 class cAP_06(cAP):
482 def Mbr(self, props=None):
483 if props is None:
484 props = self.devi.getProperties()
485 return props[5]
488 def PrivS(self):
489 props = self.devi.getProperties()
490 caps_s = bitmask_str(cDeviceWifi_06.NM_802_11_CAP, props[7]) + ","
491 priv_s = ""
492 if caps_s.find("PROTO_WEP,") != -1:
493 priv_s += " WEP"
494 if caps_s.find("PROTO_WPA,") != -1:
495 priv_s += " WPA"
496 if caps_s.find("PROTO_WPA2,") != -1:
497 priv_s += " WPA2"
498 if caps_s.find("KEY_MGMT_802_1X,") != -1:
499 priv_s += " Enterprise"
500 return priv_s
502 def Strength(self, props=None):
503 if props is None:
504 props = self.devi.getProperties()
505 return props[3]
507 def Ssid(self, props=None):
508 if props is None:
509 props = self.devi.getProperties()
510 return props[1]
513 def Dump(self):
514 props = self.devi.getProperties() # ossid iiib
515 print " Self:", props[0]
516 print " Ssid:", self.Ssid(props)
517 print " HwAddress:", props[2]
518 print " Strength:", self.Strength(props)
519 print " Frequency:", props[4]
520 print " MaxBitrate:", self.Mbr(props)
521 print " AP Mode:", cDevice.IW_MODE[props[6]]
522 print " Capabilities:", bitmask_str(cDeviceWifi_06.NM_802_11_CAP, props[7])
523 print " Broadcast:", props[8]
525 def ssid_str(array):
526 s = ""
527 for b in array:
528 s = s + ("%c" % b)
529 return s
531 def opath_validchar(c):
532 # _ is also escaped even though it is valid
533 return \
534 string.ascii_letters.find(c) != -1 or \
535 string.digits.find(c) != -1
537 def opath_escape(s):
538 r = ""
539 for c in s:
540 # TODO find a more elegant way
541 if not opath_validchar(c):
542 # "-" -> "_2d_"
543 c = "_%2x_" % ord(c)
544 r = r + c
545 return r
547 def opath_unescape(s):
548 # "2d" -> "-"
549 unhex = lambda xx: chr(eval("0x"+xx))
550 # all "_2d_" -> "-"
551 return re.sub("_.._", lambda p: unhex(p.group()[1:3]), s)
553 class cAP_07(cAP):
554 def Mbr(self):
555 return self.appi.Get(NMI, "MaxBitrate")
557 def PrivS(self):
558 priv = self.appi.Get(NMI, "Flags") != 0
559 wpa = self.appi.Get(NMI, "WpaFlags") != 0
560 wpa2 = self.appi.Get(NMI, "RsnFlags") != 0
561 priv_s = ""
562 if priv:
563 if not wpa and not wpa2:
564 priv_s = priv_s + " WEP"
565 if wpa:
566 priv_s = priv_s + " WPA"
567 if wpa2:
568 priv_s = priv_s + " WPA2"
569 return priv_s
571 def Strength(self):
572 return int(self.appi.Get(NMI, "Strength"))
574 def Ssid(self):
575 return ssid_str(self.appi.Get(NMI, "Ssid"))
577 def Dump(self):
578 print " AP:", self.opath
579 print " Ssid:", self.Ssid()
580 for P in ["Frequency", "HwAddress", "MaxBitrate",]:
581 print " %s: %s" % (P, self.appi.Get(NMI, P))
582 print " Strength:", self.Strength()
583 print " AP Mode:", cDevice.IW_MODE[self.appi.Get(NMI, "Mode")]
584 print " AP Flags:", bitmask_str(self.NM_802_11_AP_FLAGS,
585 self.appi.Get(NMI, "Flags"))
586 print " AP WPA Flags:", bitmask_str(self.NM_802_11_AP_SEC,
587 self.appi.Get(NMI, "WpaFlags"))
588 print " AP RSN Flags:", bitmask_str(self.NM_802_11_AP_SEC,
589 self.appi.Get(NMI, "RsnFlags"))
591 # this is the client side of the applet; see also UserSettings
592 class cApplet:
593 def __init__(self, svc, opath):
594 self.svc = svc
595 self.opath = opath
596 self.so = bus.get_object(self.svc, self.opath)
597 self.si = dbus.Interface(self.so, 'org.freedesktop.NetworkManagerSettings')
599 def isSystem(self):
600 return self.svc == SSC;
602 def Dump(self):
603 for conn in self.Connections():
604 conn.Dump()
605 if self.isSystem():
606 self.DumpSystem()
608 def DumpSystem(self):
609 sspi = dbus.Interface(self.so, PI)
610 print "Unmanaged Devices"
611 umds = sspi.Get(NMI, "UnmanagedDevices")
612 for umd in umds:
613 print " ", umd
614 # dump_settings_conn(svc, conn) umd?
617 def myConnection(self, opath):
618 return cConnection(self.svc, opath)
620 def Connections(self):
621 opaths = self.si.ListConnections()
622 return map(self.myConnection, opaths)
624 NETWORK_TYPE_ALLOWED = 1
625 class cApplet_06(cApplet):
626 def __init__(self, svc, opath):
627 self.svc = svc
628 self.opath = opath
629 self.io = bus.get_object(self.svc, self.opath)
630 self.ii = dbus.Interface(self.io, 'org.freedesktop.NetworkManagerInfo')
632 def isSystem(self):
633 return False;
635 def myConnection(self, opath):
636 return cConnection_06(self, opath)
638 # TODO also VPN conns
639 def Connections(self):
640 names = self.ii.getNetworks(NETWORK_TYPE_ALLOWED)
641 return map(self.myConnection, names)
643 class cConnection:
644 def __init__(self, svc, opath):
645 self.svc = svc
646 self.opath = opath
647 self.co = bus.get_object(self.svc, self.opath)
648 self.ci = dbus.Interface(self.co, 'org.freedesktop.NetworkManagerSettings.Connection')
650 def Dump(self):
651 print "Conn:", self.opath
652 settings = self.Settings()
653 settings.Dump()
655 si = dbus.Interface(self.co, 'org.freedesktop.NetworkManagerSettings.Connection.Secrets')
656 security = settings.Security()
657 if security != "":
658 print " SECRETS:", security
659 try:
660 # TODO merge them
661 secrets = cSettings(si.GetSecrets(security,[],False))
662 secrets.Dump()
663 except dbus.exceptions.DBusException, e:
664 print e
665 print " FIXME figure out 802-1x secrets"
667 def Settings(self):
668 return cSettings(self.ci.GetSettings())
670 def dump_time(unixtime):
671 return time.asctime(time.localtime(unixtime))
673 class cConnection_06:
674 def __init__(self, applet, id):
675 self.id = id
676 self.applet = applet
678 def Dump(self):
679 print "Conn:", self.id
681 np = self.applet.ii.getNetworkProperties(self.id, NETWORK_TYPE_ALLOWED)
682 ssid = np[0]
683 print " ssid:", ssid
684 print " time:", dump_time(np[1])
685 print " trusted:", bool(np[2])
686 print " bssids:", ", ".join(np[3])
687 enctype = np[4]
688 print " we_cipher:", enctype
689 if enctype != 1:
690 print " secret:", np[5]
691 if enctype == 16:
692 print " wep_auth_algorithm:", np[6]
693 elif enctype == 0:
694 print " wpa_psk_key_mgt:", np[6]
695 print " wpa_psk_wpa_version:", np[7]
697 return # nm-applet will not tell kfn anyway
698 devp = "/org/freedesktop/NetworkManager/Devices/ath0" #FIXME
699 netp = devp + "/Networks/" + opath_escape(self.id)
700 attempt = 1
701 newkey = False
702 kfn = self.applet.ii.getKeyForNetwork(devp, netp, ssid, attempt, newkey)
703 print " kfn:", kfn
706 # 06
707 NM_AUTH_TYPE_WPA_PSK_AUTO = 0x00000000
708 NM_AUTH_TYPE_NONE = 0x00000001
709 NM_AUTH_TYPE_WEP40 = 0x00000002
710 NM_AUTH_TYPE_WPA_PSK_TKIP = 0x00000004
711 NM_AUTH_TYPE_WPA_PSK_CCMP = 0x00000008
712 NM_AUTH_TYPE_WEP104 = 0x00000010
713 NM_AUTH_TYPE_WPA_EAP = 0x00000020
714 NM_AUTH_TYPE_LEAP = 0x00000040
716 IW_AUTH_ALG_OPEN_SYSTEM = 0x00000001
717 IW_AUTH_ALG_SHARED_KEY = 0x00000002
718 IW_AUTH_ALG_LEAP = 0x00000004
720 class cSettings:
721 def __init__(self, conmap):
722 #print "INIT", conmap
723 self.conmap = conmap
725 def Type(self):
726 return self.conmap["connection"]["type"]
728 def ID(self):
729 return self.conmap["connection"]["id"]
731 def Ssid(self):
732 try:
733 return self.conmap["802-11-wireless"]["ssid"]
734 except KeyError:
735 pass
736 # probably 802-3-ethernet
737 return ""
739 def Timestamp(self):
740 try:
741 return self.conmap["connection"]["timestamp"]
742 except KeyError:
743 return 0
745 def Trusted(self):
746 # false by default
747 return False
749 def SeenBssids(self):
750 try:
751 return self.conmap["802-11-wireless"]["seen-bssids"]
752 except KeyError:
753 return []
755 # for 06
756 def WeCipher(self):
757 k = self.Key()
758 if len(k) == 26:
759 return NM_AUTH_TYPE_WEP104
760 elif len(k) == 64:
761 return NM_AUTH_TYPE_WPA_PSK_AUTO
762 elif len(k) == 0:
763 return NM_AUTH_TYPE_NONE
764 print "Defaulting cipher type to none"
765 return NM_AUTH_TYPE_NONE
767 def Key(self):
768 try:
769 return self.conmap["802-11-wireless-security"]["psk"]
770 except KeyError:
771 pass
772 try:
773 return self.conmap["802-11-wireless-security"]["wep-key0"]
774 except KeyError:
775 pass
776 # no key
777 return ""
779 def WepAuthAlgorithm(self):
780 print "FIXME Defaulting WEP auth alg to open"
781 return IW_AUTH_ALG_OPEN_SYSTEM
783 def PskKeyMgt(self):
784 print "FIXME Defaulting PSK key mgmt to 2"
785 return 2
787 def PskWpaVersion(self):
788 print "FIXME Defaulting WPA version to 2"
789 return 2
791 def Security(self):
792 try:
793 return self.conmap[self.Type()]["security"]
794 except KeyError:
795 return ""
797 def isNet(self, net_name):
798 return self.ID() == net_name or self.Ssid() == net_name
800 # FIXME check spec/NM what to censor
801 secrets = dict.fromkeys(["wep-key0", "psk"])
803 def ConMap(self):
804 "For GetSettings: censor secrets."
806 cm = dict()
807 for n1, v1 in self.conmap.iteritems():
808 cm[n1] = dict()
809 for n2, v2 in v1.iteritems():
810 cv2 = v2
811 if self.secrets.has_key(n2):
812 cv2 = ""
813 cm[n1][n2] = cv2
814 return cm
816 def SecMap(self):
817 "For GetSecrets: only secrets."
818 s = self.Security()
819 r = {
820 s: self.conmap[s]
822 print "SECMAP", r
823 return r
825 def Dump(self):
826 for n1, v1 in self.conmap.iteritems():
827 print " ",n1
828 for n2, v2 in v1.iteritems():
829 print " %s: %s" % (n2, v2)
831 def mkconmap_wifi(ssid):
832 return {
833 'connection': {
834 'id': '_cnm_handcrafted_',
835 'uuid': str(uuid.uuid1()), # new in oS 11.1
836 'type': '802-11-wireless',
838 '802-11-wireless': {
839 'ssid': dbus.ByteArray(ssid),
840 'mode': 'infrastructure',
844 def elongate(s, tlen):
845 "repeat string s to target length tlen"
846 if s == "":
847 return ""
848 copies_needed = int(math.ceil(tlen / float(len(s))))
849 return (s * copies_needed)[:tlen]
851 # http://www.mail-archive.com/networkmanager-list@gnome.org/msg07935.html
852 def wep_passphrase_to_hash(p):
853 return hashlib.md5(elongate(p, 64)).hexdigest()
855 def mkconmap_wep_pass(ssid, key):
856 cm = mkconmap_wifi(ssid)
857 cm["802-11-wireless"]["security"] = "802-11-wireless-security"
858 cm["802-11-wireless-security"] = {}
859 cm["802-11-wireless-security"]["key-mgmt"] = "none"
860 cm["802-11-wireless-security"]["wep-tx-keyidx"] = 0
861 cm["802-11-wireless-security"]["wep-key0"] = wep_passphrase_to_hash(key)
862 return cm
864 def mkconmap_wep(ssid, key):
865 cm = mkconmap_wifi(ssid)
866 cm["802-11-wireless-security"]["key-mgmt"] = "none"
867 cm["802-11-wireless-security"]["wep-tx-keyidx"] = 0
868 cm["802-11-wireless-security"]["wep-key0"] = key
869 return cm
871 def mkconmap_psk(ssid, key):
872 cm = mkconmap_wifi(ssid)
873 cm["802-11-wireless"]["security"] = "802-11-wireless-security"
874 cm["802-11-wireless-security"] = {}
875 cm["802-11-wireless-security"]["key-mgmt"] = "wpa-psk"
876 cm["802-11-wireless-security"]["psk"] = key
877 cm["802-11-wireless-security"]["group"] = ["tkip", "ccmp"]
878 cm["802-11-wireless-security"]["pairwise"] = ["tkip", "ccmp"]
879 return cm
882 # server analog of cApplet
883 class UserSettings(dbus.service.Object):
884 # conmaps is a list
885 def __init__(self, opath, conmaps):
886 dbus.service.Object.__init__(self, bus, opath)
887 #print "CONMAPS:", conmaps
888 self.conns = map(self.newCon, conmaps)
890 def addCon(self, conmap):
891 c = self.newCon(conmap)
892 self.conns.append(c)
893 return c
895 counter = 1
896 def newCon(self, conmap):
897 cpath = "/MyConnection/%d" % self.counter
898 self.counter = self.counter + 1
899 c = Connection(cpath, conmap)
900 self.NewConnection(cpath) # announce it
901 return c
903 @dbus.service.method(dbus_interface='org.freedesktop.NetworkManagerSettings',
904 in_signature='', out_signature='ao')
905 def ListConnections(self):
906 return [c.__dbus_object_path__ for c in self.conns]
908 #this is for EMITTING a signal, not receiving it
909 @dbus.service.signal(dbus_interface='org.freedesktop.NetworkManagerSettings',
910 signature='o')
911 def NewConnection(self, opath):
912 pass
913 #print "signalling newconn:", opath
915 def GetByNet(self, net_name):
916 "Returns connection, or None"
917 for c in self.conns:
918 if c.isNet(net_name):
919 return c
920 return None
923 class UserSettings_06(UserSettings):
924 # conmaps is a list
925 def __init__(self, opath, conmaps):
926 dbus.service.Object.__init__(self, bus, opath)
927 #print "CONMAPS:", conmaps
928 self.conns = map(self.newCon, conmaps)
930 counter = 1
931 def newCon(self, conmap):
932 cpath = "/MyConnection/%d" % self.counter
933 self.counter = self.counter + 1
934 c = Connection_06(cpath, conmap)
935 #self.NewConnection(cpath) # announce it
936 return c
938 @dbus.service.method(dbus_interface="org.freedesktop.NetworkManagerInfo",
939 in_signature="i", out_signature='as')
940 def getNetworks(self, i):
941 # FIXME bytearray to str WHERE?
942 #n = [ssid_str(c.Ssid()) for c in self.conns]
943 n = [c.ID() for c in self.conns]
944 print "getNetworks:", n
945 return n
947 @dbus.service.method(dbus_interface="org.freedesktop.NetworkManagerInfo",
948 in_signature="", out_signature='ao') # out??
949 def getVPNConnections(self):
950 return [] # FIXME
952 @dbus.service.method(dbus_interface="org.freedesktop.NetworkManagerInfo",
953 in_signature="si")
954 #out_signature='sibasi') #varies
955 def getNetworkProperties(self, net, type):
956 print "GNP", net
957 # type is 1, NETWORK_TYPE_ALLOWED
958 c = self.GetByNet(net)
959 if c != None:
960 return c.getNetworkProperties()
961 print "Oops, could not getNetworkProperties for " + net
964 @dbus.service.method(dbus_interface="org.freedesktop.NetworkManagerInfo",
965 in_signature="oosib")
966 #out_signature="isi") varies
967 def getKeyForNetwork(self, dev, net, ssid, attempt, newkey):
968 print "GKFN", dev, net, ssid, attempt, bool(newkey)
969 if newkey:
970 m = "Cannot ask for key"
971 print m
972 raise dbus.exceptions.DBusException(m)
974 snet = opath_unescape(net[net.rfind("/")+1 : ]) # only stuff after /
975 c = self.GetByNet(snet)
976 if c != None:
977 return c.getKeyForNetwork()
978 print "Oops, could not getKeyForNetwork " + net
980 @dbus.service.method(dbus_interface="org.freedesktop.NetworkManagerInfo",
981 out_signature='')
982 #in_signature="sbs isi", varies
983 def updateNetworkInfo(self, ssid, automatic, bssid, *security):
984 print "Connected successfully"
985 return
986 print "UNI"
987 print " ssid:", ssid
988 print " automatic:", bool(automatic)
989 print " bssid:", bssid
990 print " security:", security
993 def GetByNet(self, net_name):
994 "Returns connection, or None"
995 for c in self.conns:
996 if c.isNet(net_name):
997 return c
998 return None
1001 # server analog of cConnection
1002 class Connection(dbus.service.Object):
1003 def __init__(self, opath, conmap):
1004 dbus.service.Object.__init__(self, bus, opath)
1005 self.settings = cSettings(conmap)
1007 @dbus.service.method(dbus_interface='org.freedesktop.NetworkManagerSettings.Connection',
1008 sender_keyword='sender',
1009 in_signature='', out_signature='a{sa{sv}}')
1010 def GetSettings(self, sender):
1011 #print "Getting settings:", self. __dbus_object_path__
1012 # return self.settings.ConMap()
1013 # grr, censoring secrets makes NM complain!?
1014 # bnc#479566#c3: Until I figure out how to make it work with
1015 # censored secrets, only pass the settings to the same user.
1016 sender_uid = bus.get_unix_user(sender)
1017 if sender_uid != 0 and sender_uid != os.geteuid():
1018 e = "User %u is not permitted to read the settings" % sender_uid
1019 print e
1020 raise dbus.exceptions.DBusException(e) # could do NM_SETTINGS_ERROR_* instead
1021 return self.settings.conmap
1023 @dbus.service.method(dbus_interface='org.freedesktop.NetworkManagerSettings.Connection.Secrets',
1024 in_signature='sasb', out_signature='a{sa{sv}}')
1025 def GetSecrets(self, tag, hints, ask):
1026 # FIXME respect args
1027 print "Getting secrets:", self.__dbus_object_path__
1028 return self.settings.SecMap()
1030 @dbus.service.method(dbus_interface='org.freedesktop.NetworkManagerSettings.Connection',
1031 in_signature='', out_signature='s')
1032 def ID(self):
1033 return self.settings.ID()
1035 def Ssid(self):
1036 return self.settings.Ssid()
1038 def isNet(self, net_name):
1039 return self.settings.isNet(net_name)
1041 class Connection_06(Connection):
1042 def __init__(self, opath, conmap):
1043 dbus.service.Object.__init__(self, bus, opath)
1044 #print "C6", conmap
1045 self.settings = cSettings(conmap)
1047 # dbus.service.method
1048 def getNetworkProperties(self):
1049 # essid, timestamp, ?, bssids, we_cipher, ?, ...
1050 # we_cipher=16: i wep_auth_algorithm
1051 # we_cipher=0: i wpa_psk_key_mgt, i wpa_psk_wpa_version
1052 ssid = ssid_str(self.settings.Ssid())
1053 time = self.settings.Timestamp() # last sucessfully connected? seen?
1054 trusted = self.settings.Trusted()
1055 bssids = dbus.Array(self.settings.SeenBssids(), signature="s")
1056 r = [ssid, time, trusted, bssids]
1057 security = self.getKeyForNetwork("fake key")
1058 r.extend(security)
1059 return tuple(r)
1061 # dbus.service.method
1062 def getKeyForNetwork(self, fake="no"):
1063 if fake == "no":
1064 key = self.settings.Key()
1065 else:
1066 key = ""
1068 # security
1069 cip = self.settings.WeCipher()
1070 if cip == NM_AUTH_TYPE_NONE:
1071 security = tuple([cip])
1072 elif cip == NM_AUTH_TYPE_WEP40 or cip == NM_AUTH_TYPE_WEP104:
1073 wep_auth_algorithm = self.settings.WepAuthAlgorithm()
1074 security = (cip, key, wep_auth_algorithm)
1075 elif cip == NM_AUTH_TYPE_WPA_PSK_AUTO or cip == NM_AUTH_TYPE_TKIP or \
1076 cip == NM_AUTH_TYPE_CCMP:
1077 wpa_psk_key_mgt = self.settings.PskKeyMgt()
1078 wpa_psk_wpa_version = self.settings.PskWpaVersion()
1079 security = (cip, key, wpa_psk_key_mgt, wpa_psk_wpa_version)
1080 elif cip == NM_AUTH_TYPE_WPA_EAP:
1081 security = tuple([cip]) # TODO more...
1082 elif cip == NM_AUTH_TYPE_LEAP:
1083 security = tuple([cip]) # TODO more...
1084 return security
1087 class ConfigParserKNM:
1088 "Parse ~/.kde/share/config/knetworkmanagerrc"
1090 def __init__(self):
1091 p = ConfigParser.RawConfigParser()
1092 ok = p.read(os.getenv("HOME") + "/.kde/share/config/knetworkmanagerrc")
1094 self.conmaps_d = {}
1095 for s in p.sections():
1096 path = s.split("_")
1097 #print path
1098 if path[0] in ["ConnectionSetting", "ConnectionSecrets"]:
1099 cid = path[1]
1100 self.conmaps_d.setdefault(cid, {})
1101 part = path[2]
1103 values = {}
1104 for (n, v) in p.items(s):
1105 # WTF, Value_ is transfrmed to value_
1106 if n[:6] == "value_":
1107 n = n[6:]
1108 v = self.ParseValue(v)
1109 values[n] = v
1110 if len(values) != 0: # empty 802-1x confuses NM!?
1111 self.conmaps_d[cid].setdefault(part, {})
1112 self.conmaps_d[cid][part].update(**values)
1113 #print "PARSED", cid, part, values
1115 def ConMaps(self):
1116 return self.conmaps_d.values()
1118 def ParseValue(self, v):
1119 v = eval('"%s"' % v) # unescape backslashes
1120 dom = xml.dom.minidom.parseString(v)
1121 return self.ParseNode(dom.documentElement)
1123 def ParseNode(self, n):
1124 t = n.localName
1125 if t != "list":
1126 v = self.NodeText(n)
1128 if t == "string":
1129 return v
1130 elif t == "byte":
1131 return dbus.Byte(int(v))
1132 elif t == "bool":
1133 return v == "true"
1134 elif t == "int32" or t == "uint32":
1135 return int(v)
1136 elif t == "list":
1137 v = []
1138 c = n.firstChild
1139 while c != None:
1140 if c.localName != None: # whitespace
1141 v.append(self.ParseNode(c))
1142 c = c.nextSibling
1143 return v
1145 def NodeText(self, n):
1146 if n.hasChildNodes():
1147 return n.firstChild.wholeText
1148 else:
1149 return ""
1151 def abbr_signal_handler(*args, **kwargs):
1152 ifc = kwargs["interface"]
1153 sig = kwargs["member"]
1154 opath = kwargs["path"]
1155 line = "SIG %s: %s.%s%s" % (abbrev(opath,"/"), abbrev(ifc,"."), sig, args)
1156 print line
1158 class Monitor:
1159 def __init__(self):
1160 self.amap = {}
1161 bus.add_signal_receiver(self.abbr_signal_handler,
1162 path_keyword="path",
1163 interface_keyword="interface",
1164 member_keyword="member")
1166 def abbr_signal_handler(self, *args, **kwargs):
1167 ifc = kwargs["interface"]
1168 sig = kwargs["member"]
1169 opath = kwargs["path"]
1170 line = "SIG %s: %s.%s%s" % (self.abbrev(opath,"/"),
1171 self.abbrev(ifc,"."),
1172 sig, args)
1173 print line
1175 def abbrev(self, s, sep):
1176 words = s.split(sep)
1177 words = map (self.a1, words)
1178 result = sep.join(words)
1179 if not self.amap.has_key(s):
1180 print "ABBR %s is %s" % (result, s)
1181 self.amap[s] = result
1182 else:
1183 if self.amap[s] != result:
1184 print "ABBR COLLISION %s was %s now %s" % (s, self.amap[s], result)
1185 return result
1187 def a1(self, s):
1188 try:
1189 return s[0]
1190 except:
1191 return ""
1193 # main
1195 op = OptionParser(version="%prog " + VERSION)
1196 op.add_option("-d", "--dev",
1197 action="store_true", default=False,
1198 help="list devices")
1199 op.add_option("-c", "--actcon",
1200 action="store_true", default=False,
1201 help="list active connections")
1202 op.add_option("-u", "--usrcon",
1203 action="store_true", default=False,
1204 help="list user connection settings (can CRASH nm-applet)")
1205 op.add_option("-s", "--syscon",
1206 action="store_true", default=False,
1207 help="list system connection settings")
1208 op.add_option("-a", "--ap",
1209 action="store_true", default=False,
1210 help="list found access points")
1211 op.add_option("-n", "--nets",
1212 action="store_true", default=False,
1213 help="list found wireless networks")
1214 # TODO http://docs.python.org/lib/optparse-adding-new-types.html
1215 op.add_option("-w", "--wifi",
1216 choices=["0","1","off","on","no","yes","false","true"],
1217 metavar="BOOL",
1218 help="enable or disable wireless")
1219 op.add_option("-o", "--online",
1220 choices=["0","1","off","on","no","yes","false","true"],
1221 metavar="BOOL",
1222 help="enable or disable network at all")
1224 op.add_option("-C", "--connect",
1225 help="connect to a wireless network NET (using knetworkmanagerrc or the key options below)",
1226 metavar="NET")
1227 op.add_option("--unprotected",
1228 action="store_true", default=False,
1229 help="network does not require a key")
1230 op.add_option("--wep-hex",
1231 metavar="KEY",
1232 help="use this WEP key of 26 hex digits")
1233 op.add_option("--wep-pass",
1234 metavar="KEY",
1235 help="use this WEP passphrase")
1236 op.add_option("--wpa-psk-hex",
1237 metavar="KEY",
1238 help="use this WPA key of 64 hex digits")
1239 op.add_option("--wpa-pass",
1240 metavar="KEY",
1241 help="use this WPA passphrase")
1242 op.add_option("-m", "--monitor",
1243 action="store_true", default=False,
1244 help="loop to show dbus signals")
1247 (options, args) = op.parse_args()
1249 if options.ap:
1250 options.dev = True
1251 if options.monitor:
1252 LOOP = True
1255 nmp = '/org/freedesktop/NetworkManager'
1256 try:
1257 nm = make_nm(nmp)
1258 except dbus.exceptions.DBusException, e:
1259 print e
1260 print "NetworkManager is not running"
1261 sys.exit(1)
1262 if options.dev or options.actcon:
1263 nm.Dump()
1265 true_choices = ["1", "on", "yes", "true"]
1266 if options.wifi != None:
1267 nm.SetWifiEnabled(options.wifi in true_choices)
1268 if options.online != None:
1269 nm.SetOnline(options.online in true_choices)
1271 if options.nets:
1272 nm.ListNets()
1274 if options.syscon:
1275 print "SYSTEM Connections"
1276 if nm.Api() == "06":
1277 print "Cannot do that with NM 0.6"
1278 else:
1279 ss = cApplet(SSC, '/org/freedesktop/NetworkManagerSettings')
1280 ss.Dump()
1282 if options.usrcon:
1283 print "USER Connections"
1284 try:
1285 if nm.Api() == "06":
1286 us = cApplet_06(NMIC, "/org/freedesktop/NetworkManagerInfo")
1287 else:
1288 us = cApplet(USC, '/org/freedesktop/NetworkManagerSettings')
1289 us.Dump()
1290 except dbus.exceptions.DBusException, e:
1291 print e
1292 #if e.get_dbus_name() == "org.freedesktop.DBus.Error.ServiceUnknown":
1293 print "Applet is not running"
1295 nmo = bus.get_object(NMC, nmp)
1296 nmi = dbus.Interface(nmo, NMI)
1298 def service_pid(name):
1299 DBS = 'org.freedesktop.DBus'
1300 DBI = DBS
1301 dbo = bus.get_object(DBS, '/')
1302 dbi = dbus.Interface(dbo, DBI)
1303 owner = dbi.GetNameOwner(name)
1304 pid = dbi.GetConnectionUnixProcessID(owner)
1305 return pid
1307 # TODO UserSettings_06
1308 if options.connect != None:
1309 if nm.Api() == "06":
1310 name = NMIC
1311 else:
1312 name = USC
1313 brn = bus.request_name(name, _dbus_bindings.NAME_FLAG_DO_NOT_QUEUE)
1314 if brn == _dbus_bindings.REQUEST_NAME_REPLY_EXISTS:
1315 print "Could not provide settings service, another applet is running (pid %s)" % service_pid(name)
1316 sys.exit(1)
1317 cfg = ConfigParserKNM()
1318 if nm.Api() == "06":
1319 us = UserSettings_06("/org/freedesktop/NetworkManagerInfo",
1320 cfg.ConMaps())
1321 else:
1322 us = UserSettings("/org/freedesktop/NetworkManagerSettings",
1323 cfg.ConMaps())
1325 def Connect(wanted_net): # any. or take arg. net is config name or ssid name
1326 # ... in general, look for string in all config data. ssid for wifi, whatever for dialup
1327 # TODO also respect autoconnect
1329 # ActivateConn wants setting device ap; can find device from ap? ap is "specific" for wifi devices
1330 #print "Connection wanted to", wanted_net
1331 found_con = found_ap = found_dev = None
1332 for dev in nm.Devices():
1333 for ap in dev.APs():
1334 if wanted_net == ap.Ssid():
1335 found_ap = ap
1336 found_dev = dev
1337 break # FIXME both loops
1338 found_con = us.GetByNet(wanted_net)
1339 if found_ap == None:
1340 print "No AP found with SSID", wanted_net
1341 return False
1342 if found_con == None:
1343 print "No settings for net %s, assuming no key is needed" % wanted_net
1344 c = mkconmap_wifi(wanted_net)
1345 found_con = us.addCon(c)
1346 nm.ActivateConnection(found_con, found_dev, found_ap) # TODO async
1347 # TODO run loop, exit it when we have serviced the required calls
1348 return True
1350 if options.connect != None:
1351 if options.unprotected:
1352 c = mkconmap_wifi(options.connect)
1353 us.addCon(c)
1354 if options.wep_hex != None:
1355 c = mkconmap_wep(options.connect, options.wep_hex)
1356 us.addCon(c)
1357 if options.wep_pass != None:
1358 c = mkconmap_wep_pass(options.connect, options.wep_pass)
1359 us.addCon(c)
1360 if options.wpa_psk_hex != None:
1361 c = mkconmap_psk(options.connect, options.wpa_psk_hex)
1362 us.addCon(c)
1363 if options.wpa_pass != None:
1364 wpa_psk_hex = hexlify(pbkdf2.pbkdf2(options.wpa_pass, options.connect, 4096, 32))
1365 print "pbkdf2", wpa_psk_hex
1366 c = mkconmap_psk(options.connect, wpa_psk_hex)
1367 us.addCon(c)
1368 if Connect(options.connect):
1369 LOOP = True
1371 if options.monitor:
1372 m = Monitor()
1374 def loop():
1375 loop = gobject.MainLoop()
1376 try:
1377 loop.run()
1378 except:
1379 print "Loop exited"
1381 if LOOP:
1382 loop()