From 570c7e6052d419ec86587a8c1ebda66c0db8c2e1 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Mon, 30 Apr 2007 23:44:39 -0400 Subject: [PATCH] Add buddy ip4-address property (temporary) --- services/presence/buddy.py | 230 +++++++++++++++++++++++++++-- services/presence/server_plugin.py | 7 +- sugar/presence/buddy.py | 8 +- 3 files changed, 229 insertions(+), 16 deletions(-) diff --git a/services/presence/buddy.py b/services/presence/buddy.py index ca3aff22..fcc655b3 100644 --- a/services/presence/buddy.py +++ b/services/presence/buddy.py @@ -47,6 +47,9 @@ _PROP_COLOR = "color" _PROP_OWNER = "owner" _PROP_VALID = "valid" +# Will go away soon +_PROP_IP4_ADDRESS = "ip4-address" + class Buddy(DBusGObject): """Person on the network (tracks properties and shared activites) @@ -86,7 +89,8 @@ class Buddy(DBusGObject): _PROP_COLOR : (str, None, None, None, gobject.PARAM_READWRITE), _PROP_CURACT : (str, None, None, None, gobject.PARAM_READWRITE), _PROP_VALID : (bool, None, None, False, gobject.PARAM_READABLE), - _PROP_OWNER : (bool, None, None, False, gobject.PARAM_READABLE) + _PROP_OWNER : (bool, None, None, False, gobject.PARAM_READABLE), + _PROP_IP4_ADDRESS : (str, None, None, None, gobject.PARAM_READWRITE) } def __init__(self, bus_name, object_id, **kwargs): @@ -120,11 +124,12 @@ class Buddy(DBusGObject): self._current_activity = None self._nick = None self._color = None + self._ip4_address = None if not kwargs.get(_PROP_KEY): raise ValueError("key required") - _ALLOWED_INIT_PROPS = [_PROP_NICK, _PROP_KEY, _PROP_ICON, _PROP_CURACT, _PROP_COLOR] + _ALLOWED_INIT_PROPS = [_PROP_NICK, _PROP_KEY, _PROP_ICON, _PROP_CURACT, _PROP_COLOR, _PROP_IP4_ADDRESS] for (key, value) in kwargs.items(): if key not in _ALLOWED_INIT_PROPS: logging.debug("Invalid init property '%s'; ignoring..." % key) @@ -155,6 +160,8 @@ class Buddy(DBusGObject): return self._valid elif pspec.name == _PROP_OWNER: return self._owner + elif pspec.name == _PROP_IP4_ADDRESS: + return self._ip4_address def do_set_property(self, pspec, value): """Set given property @@ -177,7 +184,11 @@ class Buddy(DBusGObject): elif pspec.name == _PROP_CURACT: self._current_activity = value elif pspec.name == _PROP_KEY: + if self._key: + raise RuntimeError("Key already set.") self._key = value + elif pspec.name == _PROP_IP4_ADDRESS: + self._ip4_address = value self._update_validity() @@ -259,6 +270,12 @@ class Buddy(DBusGObject): props[_PROP_OWNER] = self.props.owner props[_PROP_KEY] = self.props.key props[_PROP_COLOR] = self.props.color + + if self.props.ip4_address: + props[_PROP_IP4_ADDRESS] = self.props.ip4_address + else: + props[_PROP_IP4_ADDRESS] = "" + if self.props.current_activity: props[_PROP_CURACT] = self.props.current_activity else: @@ -346,6 +363,12 @@ class Buddy(DBusGObject): self._current_activity = curact changed_props[_PROP_CURACT] = curact changed = True + if _PROP_IP4_ADDRESS in properties.keys(): + ip4addr = properties[_PROP_IP4_ADDRESS] + if ip4addr != self._ip4_address: + self._ip4_address = ip4addr + changed_props[_PROP_IP4_ADDRESS] = ip4addr + changed = True if not changed or not len(changed_props.keys()): return @@ -378,6 +401,176 @@ class Buddy(DBusGObject): except AttributeError: self._valid = False + +NM_SERVICE = 'org.freedesktop.NetworkManager' +NM_IFACE = 'org.freedesktop.NetworkManager' +NM_IFACE_DEVICES = 'org.freedesktop.NetworkManager.Devices' +NM_PATH = '/org/freedesktop/NetworkManager' + +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 __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 + logging.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: + logging.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, + named_service=NM_SERVICE) + self._matches.append(match) + + match = sys_bus.add_signal_receiver(self._nm_state_change_cb, + signal_name="StateChange", + dbus_interface=NM_IFACE, + named_service=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): + logging.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): + logging.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 + class GenericOwner(Buddy): """Common functionality for Local User-like objects @@ -421,6 +614,23 @@ class GenericOwner(Buddy): Buddy.__init__(self, bus_name, object_id, **kwargs) self._owner = True + self._bus = dbus.SessionBus() + self._bus.add_signal_receiver(self._name_owner_changed_cb, + signal_name="NameOwnerChanged", + dbus_interface="org.freedesktop.DBus") + + self._ip4_addr_monitor = IP4AddressMonitor() + self._ip4_addr_monitor.connect("address-changed", self._ip4_address_changed_cb) + + def _ip4_address_changed_cb(self, monitor, address): + """Handle IPv4 address change, set property to generate event""" + props = {_PROP_IP4_ADDRESS: address} + self.set_properties(props) + + def _name_owner_changed_cb(self, name, old, new): + """Handle D-Bus services we care about appearing and disappearing.""" + self._ip4_addr_monitor.handle_name_owner_changed(name, old, new) + def get_registered(self): """Retrieve whether owner has registered with presence server""" return self._registered @@ -479,11 +689,6 @@ class ShellOwner(GenericOwner): color=color, icon=icon, server=server, key_hash=key_hash, registered=registered) - self._bus = dbus.SessionBus() - self._bus.add_signal_receiver(self._name_owner_changed_handler, - signal_name="NameOwnerChanged", - dbus_interface="org.freedesktop.DBus") - # Connect to the shell to get notifications on Owner object # property changes try: @@ -496,13 +701,10 @@ class ShellOwner(GenericOwner): if value: profile.set_server_registered() - def _name_owner_changed_handler(self, name, old, new): - """Handle DBUS notification of a new / renamed service - - Watches for the _SHELL_SERVICE, i.e. the Sugar Shell, - and registers with it if we have not yet registered - with it (using _connect_to_shell). - """ + def _name_owner_changed_cb(self, name, old, new): + # chain up to superclass + GenericOwner._name_owner_changed_cb(self, name, old, new) + if name != self._SHELL_SERVICE: return if (old and len(old)) and (not new and not len(new)): diff --git a/services/presence/server_plugin.py b/services/presence/server_plugin.py index ddbee414..cfdaf47a 100644 --- a/services/presence/server_plugin.py +++ b/services/presence/server_plugin.py @@ -164,7 +164,7 @@ class ServerPlugin(gobject.GObject): if properties.has_key("nick"): self._set_self_alias() - if properties.has_key("color"): + if properties.has_key("color") or properties.has_key("ip4-address"): self._set_self_olpc_properties() def _owner_icon_changed_cb(self, owner, icon): @@ -447,6 +447,11 @@ class ServerPlugin(gobject.GObject): props = {} props['color'] = self._owner.props.color props['key'] = dbus.ByteArray(self._owner.props.key) + addr = self._owner.props.ip4_address + if not addr: + props['ip4-address'] = "" + else: + props['ip4-address'] = addr self._conn[CONN_INTERFACE_BUDDY_INFO].SetProperties(props, reply_handler=self._ignore_success_cb, error_handler=lambda *args: self._log_error_cb("setting properties", *args)) diff --git a/sugar/presence/buddy.py b/sugar/presence/buddy.py index f21c8839..ead94821 100644 --- a/sugar/presence/buddy.py +++ b/sugar/presence/buddy.py @@ -65,7 +65,8 @@ class Buddy(gobject.GObject): 'nick' : (str, None, None, None, gobject.PARAM_READABLE), 'color' : (str, None, None, None, gobject.PARAM_READABLE), 'current-activity' : (object, None, None, gobject.PARAM_READABLE), - 'owner' : (bool, None, None, False, gobject.PARAM_READABLE) + 'owner' : (bool, None, None, False, gobject.PARAM_READABLE), + 'ip4-address' : (str, None, None, None, gobject.PARAM_READABLE) } _PRESENCE_SERVICE = "org.laptop.Sugar.Presence" @@ -134,6 +135,11 @@ class Buddy(gobject.GObject): if not self._icon: self._icon = _bytes_to_string(self._buddy.GetIcon()) return self._icon + elif pspec.name == "ip4-address": + # IPv4 address will go away quite soon + if not self._properties.has_key("ip4-address"): + return None + return self._properties["ip4-address"] def object_path(self): """Retrieve our dbus object path"""