sugar-toolkit-gtk3/services/presence/psutils.py
Simon McVittie 56b95264cf services/presence/psutils, buddy: Track NameOwnerChanged in IP4AddressMonitor
This means Buddy and its subclasses no longer need to care about
NameOwnerChanged at all.

The old code might not have worked anyway, since it was watching for
NameOwnerChanged on the session bus, but invoking NM methods on the system bus.
2007-05-25 12:14:39 +01:00

197 lines
6.8 KiB
Python

# 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 logging
import dbus
import gobject
_logger = logging.getLogger('s-p-s.psutils')
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._nm_has_been_present = False
self._matches = []
self._addr = None
self._nm_obj = None
sys_bus = dbus.SystemBus()
self._watch = sys_bus.watch_name_owner(NM_SERVICE, self._nm_owner_cb)
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 _nm_owner_cb(self, unique_name):
"""Clear state when NM goes away"""
if unique_name == '':
# NM went away, or isn't there at all
self._nm_present = False
for match in self._matches:
match.remove()
self._matches = []
if self._nm_has_been_present:
self._update_address(None)
else:
addr = self._get_address_fallback()
self._update_address(addr)
elif not self._nm_present:
# NM started up
self._nm_present = True
self._nm_has_been_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