1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
|
# Copyright (C) 2007, Red Hat, Inc.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
import dbus, dbus.glib, gobject
import logging
_logger = logging.getLogger('s-p-s.psutils')
def bytes_to_string(bytes):
"""The function converts a D-BUS byte array provided by dbus to string format.
bytes -- a D-Bus array of bytes. Handle both DBus byte arrays and strings
"""
try:
# DBus Byte array
ret = ''.join([chr(item) for item in bytes])
except TypeError:
# Python string
ret = ''.join([str(item) for item in bytes])
return ret
NM_SERVICE = 'org.freedesktop.NetworkManager'
NM_IFACE = 'org.freedesktop.NetworkManager'
NM_IFACE_DEVICES = 'org.freedesktop.NetworkManager.Devices'
NM_PATH = '/org/freedesktop/NetworkManager'
_ip4am = None
class IP4AddressMonitor(gobject.GObject):
"""This class, and direct buddy IPv4 address access, will go away quite soon"""
__gsignals__ = {
'address-changed': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
([gobject.TYPE_PYOBJECT]))
}
__gproperties__ = {
'address' : (str, None, None, None, gobject.PARAM_READABLE)
}
def get_instance():
"""Retrieve (or create) the IP4Address monitor singleton instance"""
global _ip4am
if not _ip4am:
_ip4am = IP4AddressMonitor()
return _ip4am
get_instance = staticmethod(get_instance)
def __init__(self):
gobject.GObject.__init__(self)
self._nm_present = False
self._matches = []
self._addr = None
self._nm_obj = None
sys_bus = dbus.SystemBus()
bus_object = sys_bus.get_object('org.freedesktop.DBus', '/org/freedesktop/DBus')
try:
if bus_object.GetNameOwner(NM_SERVICE, dbus_interface='org.freedesktop.DBus'):
self._nm_present = True
except dbus.DBusException:
pass
if self._nm_present:
self._connect_to_nm()
else:
addr = self._get_address_fallback()
self._update_address(addr)
def do_get_property(self, pspec):
if pspec.name == "address":
return self._addr
def _update_address(self, new_addr):
if new_addr == "0.0.0.0":
new_addr = None
if new_addr == self._addr:
return
self._addr = new_addr
_logger.debug("IP4 address now '%s'" % new_addr)
self.emit('address-changed', new_addr)
def _connect_to_nm(self):
"""Connect to NM device state signals to tell when the IPv4 address changes"""
try:
sys_bus = dbus.SystemBus()
proxy = sys_bus.get_object(NM_SERVICE, NM_PATH)
self._nm_obj = dbus.Interface(proxy, NM_IFACE)
except dbus.DBusException, err:
_logger.debug("Error finding NetworkManager: %s" % err)
self._nm_present = False
return
sys_bus = dbus.SystemBus()
match = sys_bus.add_signal_receiver(self._nm_device_active_cb,
signal_name="DeviceNowActive",
dbus_interface=NM_IFACE)
self._matches.append(match)
match = sys_bus.add_signal_receiver(self._nm_device_no_longer_active_cb,
signal_name="DeviceNoLongerActive",
dbus_interface=NM_IFACE,
bus_name=NM_SERVICE)
self._matches.append(match)
match = sys_bus.add_signal_receiver(self._nm_state_change_cb,
signal_name="StateChange",
dbus_interface=NM_IFACE,
bus_name=NM_SERVICE)
self._matches.append(match)
state = self._nm_obj.state()
if state == 3: # NM_STATE_CONNECTED
self._query_devices()
def _device_properties_cb(self, *props):
active = props[4]
if not active:
return
act_stage = props[5]
# HACK: OLPC NM has an extra stage, so activated == 8 on OLPC
# but 7 everywhere else
if act_stage != 8 and act_stage != 7:
# not activated
return
self._update_address(props[6])
def _device_properties_error_cb(self, err):
_logger.debug("Error querying device properties: %s" % err)
def _query_device_properties(self, device):
sys_bus = dbus.SystemBus()
proxy = sys_bus.get_object(NM_SERVICE, device)
dev = dbus.Interface(proxy, NM_IFACE_DEVICES)
dev.getProperties(reply_handler=self._device_properties_cb,
error_handler=self._device_properties_error_cb)
def _get_devices_cb(self, ops):
"""Query each device's properties"""
for op in ops:
self._query_device_properties(op)
def _get_devices_error_cb(self, err):
_logger.debug("Error getting NetworkManager devices: %s" % err)
def _query_devices(self):
"""Query NM for a list of network devices"""
self._nm_obj.getDevices(reply_handler=self._get_devices_cb,
error_handler=self._get_devices_error_cb)
def _nm_device_active_cb(self, device, ssid=None):
self._query_device_properties(device)
def _nm_device_no_longer_active_cb(self, device):
self._update_address(None)
def _nm_state_change_cb(self, new_state):
if new_state == 4: # NM_STATE_DISCONNECTED
self._update_address(None)
def handle_name_owner_changed(self, name, old, new):
"""Clear state when NM goes away"""
if name != NM_SERVICE:
return
if (old and len(old)) and (not new and not len(new)):
# NM went away
self._nm_present = False
for match in self._matches:
match.remove()
self._matches = []
self._update_address(None)
elif (not old and not len(old)) and (new and len(new)):
# NM started up
self._nm_present = True
self._connect_to_nm()
def _get_iface_address(self, iface):
import socket
import fcntl
import struct
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
fd = s.fileno()
SIOCGIFADDR = 0x8915
addr = fcntl.ioctl(fd, SIOCGIFADDR, struct.pack('256s', iface[:15]))[20:24]
s.close()
return socket.inet_ntoa(addr)
def _get_address_fallback(self):
import commands
(s, o) = commands.getstatusoutput("/sbin/route -n")
if s != 0:
return
for line in o.split('\n'):
fields = line.split(" ")
if fields[0] == "0.0.0.0":
iface = fields[len(fields) - 1]
return self._get_iface_address(iface)
return None
|