From 2fe8019a7940354ab669cedba0471c876c38f81a Mon Sep 17 00:00:00 2001 From: Dan Winship Date: Wed, 29 May 2013 12:00:50 -0300 Subject: [PATCH] platform: merge remaining NMUdevManager functionality into NMLinuxPlatform Merge the net-subsystem-monitoring functionality of NMUdevManager into NMLinuxPlatform (and kill NMUdevManager). NMLinuxPlatform now only emits link-added signals after udev processes the device, and uses udev attributes to further identify the device. NMManager now identifies devices solely based on the NMLinkType provided by the platform. --- src/Makefile.am | 2 - src/devices/nm-device-factory.h | 8 +- src/devices/wimax/nm-wimax-factory.c | 5 +- src/nm-manager.c | 143 ++++---------- src/nm-udev-manager.c | 279 --------------------------- src/nm-udev-manager.h | 68 ------- src/platform/nm-fake-platform.c | 4 + src/platform/nm-linux-platform.c | 271 ++++++++++++++++++++++++-- src/platform/nm-platform.c | 22 ++- src/platform/nm-platform.h | 6 + src/platform/tests/Makefile.am | 3 +- src/platform/tests/dump.c | 3 + 12 files changed, 330 insertions(+), 484 deletions(-) delete mode 100644 src/nm-udev-manager.c delete mode 100644 src/nm-udev-manager.h diff --git a/src/Makefile.am b/src/Makefile.am index c97c1a2256..69a62c46aa 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -254,8 +254,6 @@ nm_sources = \ nm-system.c \ nm-system.h \ nm-types.h \ - nm-udev-manager.c \ - nm-udev-manager.h \ nm-wifi-ap-utils.c \ nm-wifi-ap-utils.h \ nm-wifi-ap.c \ diff --git a/src/devices/nm-device-factory.h b/src/devices/nm-device-factory.h index 8a4f446726..bf202a6857 100644 --- a/src/devices/nm-device-factory.h +++ b/src/devices/nm-device-factory.h @@ -23,7 +23,6 @@ #include #include -#include #include "NetworkManager.h" @@ -35,7 +34,6 @@ /** * nm_device_factory_create_device: - * @device: GUdev device object representing the device * @devpath: sysfs path of the device * @ifname: interface name of the device * @driver: driver of the device @@ -51,15 +49,13 @@ * * Returns: the device object (a subclass of #NMDevice) or %NULL */ -GObject *nm_device_factory_create_device (GUdevDevice *device, - const char *devpath, +GObject *nm_device_factory_create_device (const char *devpath, const char *ifname, const char *driver, GError **error); /* Should match nm_device_factory() */ -typedef GObject * (*NMDeviceFactoryCreateFunc) (GUdevDevice *device, - const char *devpath, +typedef GObject * (*NMDeviceFactoryCreateFunc) (const char *devpath, const char *ifname, const char *driver, GError **error); diff --git a/src/devices/wimax/nm-wimax-factory.c b/src/devices/wimax/nm-wimax-factory.c index 0326088a66..36fe934525 100644 --- a/src/devices/wimax/nm-wimax-factory.c +++ b/src/devices/wimax/nm-wimax-factory.c @@ -24,13 +24,12 @@ #include "nm-device-wimax.h" G_MODULE_EXPORT GObject * -nm_device_factory_create_device (GUdevDevice *device, - const char *devpath, +nm_device_factory_create_device (const char *devpath, const char *ifname, const char *driver, GError **error) { - /* FIXME: check 'DEVTYPE' instead; but since we only support Intel + /* FIXME: check udev 'DEVTYPE' instead; but since we only support Intel * WiMAX devices for now this is appropriate. */ if (g_strcmp0 (driver, "i2400m_usb") != 0) diff --git a/src/nm-manager.c b/src/nm-manager.c index daf2f80565..1a1315ddfb 100644 --- a/src/nm-manager.c +++ b/src/nm-manager.c @@ -63,7 +63,6 @@ #include "nm-setting-vpn.h" #include "nm-dbus-glib-types.h" #include "nm-platform.h" -#include "nm-udev-manager.h" #include "nm-atm-manager.h" #include "nm-rfkill-manager.h" #include "nm-hostname-provider.h" @@ -75,7 +74,6 @@ #include "NetworkManagerUtils.h" #include "nm-utils.h" #include "nm-device-factory.h" -#include "wifi-utils.h" #include "nm-enum-types.h" #include "nm-sleep-monitor.h" #include "nm-platform.h" @@ -224,7 +222,6 @@ typedef struct { NMDBusManager *dbus_mgr; guint dbus_connection_changed_id; - NMUdevManager *udev_mgr; NMAtmManager *atm_mgr; NMRfkillManager *rfkill_mgr; NMBluezManager *bluez_mgr; @@ -2176,36 +2173,11 @@ load_device_factories (NMManager *self) g_slist_free (list); } -static gboolean -is_wireless (GUdevDevice *device) -{ - const char *tmp; - - /* Check devtype, newer kernels (2.6.32+) have this */ - tmp = g_udev_device_get_property (device, "DEVTYPE"); - if (g_strcmp0 (tmp, "wlan") == 0) - return TRUE; - - /* Otherwise hit up WEXT directly */ - return wifi_utils_is_wifi (g_udev_device_get_name (device), - g_udev_device_get_sysfs_path (device)); -} - -static gboolean -is_olpc_mesh (GUdevDevice *device) -{ - const gchar *prop = g_udev_device_get_property (device, "ID_NM_OLPC_MESH"); - return (prop != NULL); -} - static void -udev_device_added_cb (NMUdevManager *udev_mgr, - GUdevDevice *udev_device, - const char *iface, - const char *sysfs_path, - const char *driver, - int ifindex, - gpointer user_data) +platform_link_added_cb (NMPlatform *platform, + int ifindex, + NMPlatformLink *link, + gpointer user_data) { NMManager *self = NM_MANAGER (user_data); NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); @@ -2213,15 +2185,12 @@ udev_device_added_cb (NMUdevManager *udev_mgr, GSList *iter; GError *error = NULL; - g_return_if_fail (udev_device != NULL); - g_return_if_fail (iface != NULL); - g_return_if_fail (sysfs_path != NULL); g_return_if_fail (ifindex > 0); device = find_device_by_ifindex (self, ifindex); if (device) { /* If it's a virtual device we may need to update its UDI */ - g_object_set (G_OBJECT (device), NM_DEVICE_UDI, sysfs_path, NULL); + g_object_set (G_OBJECT (device), NM_DEVICE_UDI, link->udi, NULL); return; } @@ -2230,7 +2199,7 @@ udev_device_added_cb (NMUdevManager *udev_mgr, NMDeviceFactoryCreateFunc create_func = iter->data; g_clear_error (&error); - device = (NMDevice *) create_func (udev_device, sysfs_path, iface, driver, &error); + device = (NMDevice *) create_func (link->udi, link->name, link->driver, &error); if (device && NM_IS_DEVICE (device)) { g_assert_no_error (error); break; /* success! */ @@ -2238,7 +2207,7 @@ udev_device_added_cb (NMUdevManager *udev_mgr, if (error) { nm_log_warn (LOGD_HW, "%s: factory failed to create device: (%d) %s", - sysfs_path, + link->udi, error ? error->code : -1, error ? error->message : "(unknown)"); g_clear_error (&error); @@ -2247,72 +2216,67 @@ udev_device_added_cb (NMUdevManager *udev_mgr, } if (device == NULL) { - NMLinkType type; int parent_ifindex = -1; NMDevice *parent; - type = nm_platform_link_get_type (ifindex); - - switch (type) { + switch (link->type) { case NM_LINK_TYPE_ETHERNET: - if (driver == NULL) - device = nm_device_generic_new (sysfs_path, iface, driver); - else if (is_olpc_mesh (udev_device)) /* must be before is_wireless */ - device = nm_device_olpc_mesh_new (sysfs_path, iface, driver); - else if (is_wireless (udev_device)) - device = nm_device_wifi_new (sysfs_path, iface, driver); - else - device = nm_device_ethernet_new (sysfs_path, iface, driver); + device = nm_device_ethernet_new (link->udi, link->name, link->driver); break; - case NM_LINK_TYPE_INFINIBAND: - device = nm_device_infiniband_new (sysfs_path, iface, driver); + device = nm_device_infiniband_new (link->udi, link->name, link->driver); + break; + case NM_LINK_TYPE_OLPC_MESH: + device = nm_device_olpc_mesh_new (link->udi, link->name, link->driver); + break; + case NM_LINK_TYPE_WIFI: + device = nm_device_wifi_new (link->udi, link->name, link->driver); break; case NM_LINK_TYPE_BOND: - device = nm_device_bond_new (sysfs_path, iface); + device = nm_device_bond_new (link->udi, link->name); break; case NM_LINK_TYPE_BRIDGE: /* FIXME: always create device when we handle bridges non-destructively */ - if (bridge_created_by_nm (self, iface)) - device = nm_device_bridge_new (sysfs_path, iface); + if (bridge_created_by_nm (self, link->name)) + device = nm_device_bridge_new (link->udi, link->name); else - nm_log_info (LOGD_BRIDGE, "(%s): ignoring bridge not created by NetworkManager", iface); + nm_log_info (LOGD_BRIDGE, "(%s): ignoring bridge not created by NetworkManager", link->name); break; case NM_LINK_TYPE_VLAN: /* Have to find the parent device */ if (nm_platform_vlan_get_info (ifindex, &parent_ifindex, NULL)) { parent = find_device_by_ifindex (self, parent_ifindex); if (parent) - device = nm_device_vlan_new (sysfs_path, iface, parent); + device = nm_device_vlan_new (link->udi, link->name, parent); else { /* If udev signaled the VLAN interface before it signaled * the VLAN's parent at startup we may not know about the * parent device yet. But we'll find it on the second pass * from nm_manager_start(). */ - nm_log_dbg (LOGD_HW, "(%s): VLAN parent interface unknown", iface); + nm_log_dbg (LOGD_HW, "(%s): VLAN parent interface unknown", link->name); } } else - nm_log_err (LOGD_HW, "(%s): failed to get VLAN parent ifindex", iface); + nm_log_err (LOGD_HW, "(%s): failed to get VLAN parent ifindex", link->name); break; case NM_LINK_TYPE_VETH: - device = nm_device_veth_new (sysfs_path, iface, driver); + device = nm_device_veth_new (link->udi, link->name, link->driver); break; case NM_LINK_TYPE_TUN: case NM_LINK_TYPE_TAP: - device = nm_device_tun_new (sysfs_path, iface, driver); + device = nm_device_tun_new (link->udi, link->name, link->driver); break; case NM_LINK_TYPE_MACVLAN: case NM_LINK_TYPE_MACVTAP: - device = nm_device_macvlan_new (sysfs_path, iface, driver); + device = nm_device_macvlan_new (link->udi, link->name, link->driver); break; case NM_LINK_TYPE_GRE: case NM_LINK_TYPE_GRETAP: - device = nm_device_gre_new (sysfs_path, iface, driver); + device = nm_device_gre_new (link->udi, link->name, link->driver); break; default: - device = nm_device_generic_new (sysfs_path, iface, driver); + device = nm_device_generic_new (link->udi, link->name, link->driver); break; } } @@ -2322,42 +2286,16 @@ udev_device_added_cb (NMUdevManager *udev_mgr, } static void -udev_device_removed_cb (NMUdevManager *manager, - GUdevDevice *udev_device, - gpointer user_data) +platform_link_removed_cb (NMPlatform *platform, + int ifindex, + NMPlatformLink *link, + gpointer user_data) { NMManager *self = NM_MANAGER (user_data); NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); NMDevice *device; - guint32 ifindex; - ifindex = g_udev_device_get_property_as_int (udev_device, "IFINDEX"); device = find_device_by_ifindex (self, ifindex); - if (!device) { - GSList *iter; - const char *iface = g_udev_device_get_name (udev_device); - - /* On removal we aren't always be able to read properties like IFINDEX - * anymore, as they may have already been removed from sysfs. So we - * have to fall back on device name (eg, interface name). - * - * Also, some devices (namely PPPoE (pppX), ADSL (nasX, pppX), and - * mobile broadband (pppX, bnepX)) create a kernel netdevice for IP - * communication (called the "IP interface" in NM) as part of the - * connection process and thus the IP interface lifetime does not - * correspond to the NMDevice lifetime. For these devices we must - * ignore removal events for the IP interface name otherwise the - * NMDevice would be removed. Hence the usage here of - * nm_device_get_iface() rather than nm_device_get_ip_iface(). - */ - for (iter = priv->devices; iter; iter = g_slist_next (iter)) { - if (g_strcmp0 (nm_device_get_iface (NM_DEVICE (iter->data)), iface) == 0) { - device = iter->data; - break; - } - } - } - if (device) priv->devices = remove_one_device (self, priv->devices, device, FALSE); } @@ -3786,7 +3724,7 @@ nm_manager_start (NMManager *self) priv->nm_bridges = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); read_nm_created_bridges (self); - nm_udev_manager_query_devices (priv->udev_mgr); + nm_platform_query_devices (); nm_atm_manager_query_devices (priv->atm_mgr); nm_bluez_manager_query_devices (priv->bluez_mgr); @@ -3796,7 +3734,7 @@ nm_manager_start (NMManager *self) * the VLAN would fail. The second query ensures that we'll have a valid * parent for the VLAN during the second pass. */ - nm_udev_manager_query_devices (priv->udev_mgr); + nm_platform_query_devices (); /* * Connections added before the manager is started do not emit @@ -4108,14 +4046,13 @@ nm_manager_new (NMSettings *settings, nm_dbus_manager_register_object (priv->dbus_mgr, NM_DBUS_PATH, singleton); - priv->udev_mgr = nm_udev_manager_new (); - g_signal_connect (priv->udev_mgr, - "device-added", - G_CALLBACK (udev_device_added_cb), + g_signal_connect (nm_platform_get (), + "link-added", + G_CALLBACK (platform_link_added_cb), singleton); - g_signal_connect (priv->udev_mgr, - "device-removed", - G_CALLBACK (udev_device_removed_cb), + g_signal_connect (nm_platform_get (), + "link-removed", + G_CALLBACK (platform_link_removed_cb), singleton); priv->atm_mgr = nm_atm_manager_new (); diff --git a/src/nm-udev-manager.c b/src/nm-udev-manager.c deleted file mode 100644 index bc2d14b06c..0000000000 --- a/src/nm-udev-manager.c +++ /dev/null @@ -1,279 +0,0 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ -/* NetworkManager -- Network link manager - * - * 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 Street, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Copyright (C) 2009 - 2013 Red Hat, Inc. - */ - -#include - -#include - -#include "nm-udev-manager.h" -#include "nm-logging.h" -#include "nm-platform.h" -#include "nm-system.h" - -typedef struct { - GUdevClient *client; - -} NMUdevManagerPrivate; - -#define NM_UDEV_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_UDEV_MANAGER, NMUdevManagerPrivate)) - -G_DEFINE_TYPE (NMUdevManager, nm_udev_manager, G_TYPE_OBJECT) - -enum { - DEVICE_ADDED, - DEVICE_REMOVED, - - LAST_SIGNAL -}; - -static guint signals[LAST_SIGNAL] = { 0 }; - -NMUdevManager * -nm_udev_manager_new (void) -{ - return NM_UDEV_MANAGER (g_object_new (NM_TYPE_UDEV_MANAGER, NULL)); -} - -static gboolean -dev_get_attrs (GUdevDevice *udev_device, - const char **out_ifname, - const char **out_path, - char **out_driver, - int *out_ifindex) -{ - GUdevDevice *parent = NULL, *grandparent = NULL; - const char *ifname, *driver, *path, *subsys; - gint ifindex = -1; - - g_return_val_if_fail (udev_device != NULL, FALSE); - g_return_val_if_fail (out_ifname != NULL, FALSE); - g_return_val_if_fail (out_path != NULL, FALSE); - g_return_val_if_fail (out_driver != NULL, FALSE); - g_return_val_if_fail (out_ifindex != NULL, FALSE); - - ifname = g_udev_device_get_name (udev_device); - if (!ifname) { - nm_log_dbg (LOGD_HW, "failed to get device's interface"); - return FALSE; - } - - path = g_udev_device_get_sysfs_path (udev_device); - if (!path) { - nm_log_warn (LOGD_HW, "couldn't determine device path; ignoring..."); - return FALSE; - } - - if (g_udev_device_get_sysfs_attr (udev_device, "ifindex")) - ifindex = g_udev_device_get_sysfs_attr_as_int (udev_device, "ifindex"); - else { - nm_log_warn (LOGD_HW, "failed to get device's ifindex"); - return FALSE; - } - - driver = g_udev_device_get_driver (udev_device); - if (!driver) { - /* Try the parent */ - parent = g_udev_device_get_parent (udev_device); - if (parent) { - driver = g_udev_device_get_driver (parent); - if (!driver) { - /* try the grandparent if it's an ibmebus device or if the - * subsys is NULL which usually indicates some sort of - * platform device like a 'gadget' net interface. - */ - subsys = g_udev_device_get_subsystem (parent); - if ( (g_strcmp0 (subsys, "ibmebus") == 0) - || (subsys == NULL)) { - grandparent = g_udev_device_get_parent (parent); - if (grandparent) - driver = g_udev_device_get_driver (grandparent); - } - } - } - } - - if (!driver) { - switch (nm_platform_link_get_type (ifindex)) { - case NM_LINK_TYPE_BOND: - driver = "bonding"; - break; - case NM_LINK_TYPE_BRIDGE: - driver = "bridge"; - break; - case NM_LINK_TYPE_VLAN: - driver = "8021q"; - break; - default: - if (g_str_has_prefix (ifname, "easytether")) - driver = "easytether"; - break; - } - } - - *out_ifname = ifname; - *out_path = path; - *out_driver = g_strdup (driver); - *out_ifindex = ifindex; - - if (grandparent) - g_object_unref (grandparent); - if (parent) - g_object_unref (parent); - - return TRUE; -} - -static void -net_add (NMUdevManager *self, GUdevDevice *udev_device) -{ - gint ifindex = -1; - const char *ifname = NULL, *path = NULL, *tmp; - char *driver = NULL; - - g_return_if_fail (udev_device != NULL); - - if (!dev_get_attrs (udev_device, &ifname, &path, &driver, &ifindex)) - return; - - if (ifindex < 0) { - nm_log_warn (LOGD_HW, "%s: device had invalid ifindex %d; ignoring...", path, ifindex); - goto out; - } - - /* Not all ethernet devices are immediately usable; newer mobile broadband - * devices (Ericsson, Option, Sierra) require setup on the tty before the - * ethernet device is usable. 2.6.33 and later kernels set the 'DEVTYPE' - * uevent variable which we can use to ignore the interface as a NMDevice - * subclass. ModemManager will pick it up though and so we'll handle it - * through the mobile broadband stuff. - */ - tmp = g_udev_device_get_property (udev_device, "DEVTYPE"); - if (g_strcmp0 (tmp, "wwan") == 0) { - nm_log_dbg (LOGD_HW, "(%s): ignoring interface with devtype '%s'", ifname, tmp); - goto out; - } - - g_signal_emit (self, signals[DEVICE_ADDED], 0, udev_device, ifname, path, driver, ifindex); - -out: - g_free (driver); -} - -static void -net_remove (NMUdevManager *self, GUdevDevice *device) -{ - g_signal_emit (self, signals[DEVICE_REMOVED], 0, device); -} - -void -nm_udev_manager_query_devices (NMUdevManager *self) -{ - NMUdevManagerPrivate *priv = NM_UDEV_MANAGER_GET_PRIVATE (self); - GUdevEnumerator *enumerator; - GList *devices, *iter; - - g_return_if_fail (NM_IS_UDEV_MANAGER (self)); - - enumerator = g_udev_enumerator_new (priv->client); - g_udev_enumerator_add_match_subsystem (enumerator, "net"); - g_udev_enumerator_add_match_is_initialized (enumerator); - - devices = g_udev_enumerator_execute (enumerator); - for (iter = devices; iter; iter = g_list_next (iter)) { - net_add (self, G_UDEV_DEVICE (iter->data)); - g_object_unref (G_UDEV_DEVICE (iter->data)); - } - g_list_free (devices); - g_object_unref (enumerator); -} - -static void -handle_uevent (GUdevClient *client, - const char *action, - GUdevDevice *device, - gpointer user_data) -{ - NMUdevManager *self = NM_UDEV_MANAGER (user_data); - const char *subsys; - - g_return_if_fail (action != NULL); - - /* A bit paranoid */ - subsys = g_udev_device_get_subsystem (device); - g_return_if_fail (!g_strcmp0 (subsys, "net")); - - nm_log_dbg (LOGD_HW, "UDEV event: action '%s' subsys '%s' device '%s'", - action, subsys, g_udev_device_get_name (device)); - - if (!strcmp (action, "add")) - net_add (self, device); - else if (!strcmp (action, "remove")) - net_remove (self, device); -} - -static void -nm_udev_manager_init (NMUdevManager *self) -{ - NMUdevManagerPrivate *priv = NM_UDEV_MANAGER_GET_PRIVATE (self); - const char *subsys[] = { "net", NULL }; - - priv->client = g_udev_client_new (subsys); - g_signal_connect (priv->client, "uevent", G_CALLBACK (handle_uevent), self); -} - -static void -dispose (GObject *object) -{ - NMUdevManager *self = NM_UDEV_MANAGER (object); - NMUdevManagerPrivate *priv = NM_UDEV_MANAGER_GET_PRIVATE (self); - - g_clear_object (&priv->client); - - G_OBJECT_CLASS (nm_udev_manager_parent_class)->dispose (object); -} - -static void -nm_udev_manager_class_init (NMUdevManagerClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - - g_type_class_add_private (klass, sizeof (NMUdevManagerPrivate)); - - /* virtual methods */ - object_class->dispose = dispose; - - /* Signals */ - signals[DEVICE_ADDED] = - g_signal_new ("device-added", - G_OBJECT_CLASS_TYPE (object_class), - G_SIGNAL_RUN_FIRST, - G_STRUCT_OFFSET (NMUdevManagerClass, device_added), - NULL, NULL, NULL, - G_TYPE_NONE, 5, G_TYPE_POINTER, G_TYPE_POINTER, G_TYPE_POINTER, G_TYPE_POINTER, G_TYPE_INT); - - signals[DEVICE_REMOVED] = - g_signal_new ("device-removed", - G_OBJECT_CLASS_TYPE (object_class), - G_SIGNAL_RUN_FIRST, - G_STRUCT_OFFSET (NMUdevManagerClass, device_removed), - NULL, NULL, NULL, - G_TYPE_NONE, 1, G_TYPE_POINTER); -} - diff --git a/src/nm-udev-manager.h b/src/nm-udev-manager.h deleted file mode 100644 index ab3323d017..0000000000 --- a/src/nm-udev-manager.h +++ /dev/null @@ -1,68 +0,0 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ -/* NetworkManager -- Network link manager - * - * 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 Street, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Copyright (C) 2007 - 2008 Novell, Inc. - * Copyright (C) 2007 - 2012 Red Hat, Inc. - */ - -#ifndef NM_UDEV_MANAGER_H -#define NM_UDEV_MANAGER_H - -#include -#include - -#include - -G_BEGIN_DECLS - -#define NM_TYPE_UDEV_MANAGER (nm_udev_manager_get_type ()) -#define NM_UDEV_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_UDEV_MANAGER, NMUdevManager)) -#define NM_UDEV_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_UDEV_MANAGER, NMUdevManagerClass)) -#define NM_IS_UDEV_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_UDEV_MANAGER)) -#define NM_IS_UDEV_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_UDEV_MANAGER)) -#define NM_UDEV_MANAGER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_UDEV_MANAGER, NMUdevManagerClass)) - -typedef struct { - GObject parent; -} NMUdevManager; - -typedef GObject *(*NMDeviceCreatorFn) (NMUdevManager *manager, - GUdevDevice *device, - gboolean sleeping); - -typedef struct { - GObjectClass parent; - - /* signals */ - void (*device_added) (NMUdevManager *manager, - GUdevDevice *device, - const char *iface, - const char *sysfs_path, - const char *driver, - int ifindex); - - void (*device_removed) (NMUdevManager *manager, GUdevDevice *device); -} NMUdevManagerClass; - -GType nm_udev_manager_get_type (void); - -NMUdevManager *nm_udev_manager_new (void); - -void nm_udev_manager_query_devices (NMUdevManager *manager); - -#endif /* NM_UDEV_MANAGER_H */ - diff --git a/src/platform/nm-fake-platform.c b/src/platform/nm-fake-platform.c index 3a5722b166..9047ee4c1b 100644 --- a/src/platform/nm-fake-platform.c +++ b/src/platform/nm-fake-platform.c @@ -40,6 +40,7 @@ typedef struct { typedef struct { NMPlatformLink link; + char *udi; GBytes *address; int vlan_parent; int vlan_id; @@ -113,6 +114,8 @@ link_init (NMFakePlatformLink *device, int ifindex, int type, const char *name) device->link.ifindex = name ? ifindex : 0; device->link.type = type; device->link.type_name = type_to_type_name (type); + device->link.driver = type_to_type_name (type); + device->link.udi = device->udi = g_strdup_printf ("fake:%d", ifindex); if (name) strcpy (device->link.name, name); switch (device->link.type) { @@ -976,6 +979,7 @@ nm_fake_platform_finalize (GObject *object) NMFakePlatformLink *device = &g_array_index (priv->links, NMFakePlatformLink, i); g_bytes_unref (device->address); + g_free (device->udi); } g_array_unref (priv->links); g_array_unref (priv->ip4_addresses); diff --git a/src/platform/nm-linux-platform.c b/src/platform/nm-linux-platform.c index 4266c6e6c7..fb417346eb 100644 --- a/src/platform/nm-linux-platform.c +++ b/src/platform/nm-linux-platform.c @@ -41,6 +41,7 @@ #include #include #include +#include #include "nm-linux-platform.h" #include "nm-logging.h" @@ -60,6 +61,9 @@ typedef struct { struct nl_cache *route_cache; GIOChannel *event_channel; guint event_id; + + GUdevClient *udev_client; + GHashTable *udev_devices; } NMLinuxPlatformPrivate; #define NM_LINUX_PLATFORM_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_LINUX_PLATFORM, NMLinuxPlatformPrivate)) @@ -393,7 +397,33 @@ type_to_string (NMLinkType type) } G_STMT_END static NMLinkType -link_extract_type (struct rtnl_link *rtnllink, const char **out_name) +link_type_from_udev (NMPlatform *platform, struct rtnl_link *rtnllink, const char **out_name) +{ + NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); + int ifindex = rtnl_link_get_ifindex (rtnllink); + GUdevDevice *udev_device; + const char *prop; + + g_assert_cmpint (rtnl_link_get_arptype (rtnllink), ==, ARPHRD_ETHER); + + udev_device = g_hash_table_lookup (priv->udev_devices, GINT_TO_POINTER (ifindex)); + if (!udev_device) + return_type (NM_LINK_TYPE_UNKNOWN, "unknown"); + + prop = g_udev_device_get_property (udev_device, "ID_NM_OLPC_MESH"); + if (prop) + return_type (NM_LINK_TYPE_OLPC_MESH, "olpc-mesh"); + + prop = g_udev_device_get_property (udev_device, "DEVTYPE"); + if (g_strcmp0 (prop, "wlan") == 0) + return_type (NM_LINK_TYPE_WIFI, "wifi"); + + /* Anything else is assumed to be ethernet */ + return_type (NM_LINK_TYPE_ETHERNET, "ethernet"); +} + +static NMLinkType +link_extract_type (NMPlatform *platform, struct rtnl_link *rtnllink, const char **out_name) { const char *type; @@ -403,23 +433,21 @@ link_extract_type (struct rtnl_link *rtnllink, const char **out_name) type = rtnl_link_get_type (rtnllink); if (!type) { - switch (rtnl_link_get_arptype (rtnllink)) { - case ARPHRD_LOOPBACK: + int arptype = rtnl_link_get_arptype (rtnllink); + + if (arptype == ARPHRD_LOOPBACK) return_type (NM_LINK_TYPE_LOOPBACK, "loopback"); - case ARPHRD_ETHER: - return_type (NM_LINK_TYPE_ETHERNET, "ethernet"); - case 256: + else if (arptype == 256) { /* Some s390 CTC-type devices report 256 for the encapsulation type - * for some reason, but we need to call them Ethernet too. FIXME: use + * for some reason, but we need to call them Ethernet. FIXME: use * something other than interface name to detect CTC here. */ if (g_str_has_prefix (rtnl_link_get_name (rtnllink), "ctc")) return_type (NM_LINK_TYPE_ETHERNET, "ethernet"); - else - break; - } - - return_type (NM_LINK_TYPE_UNKNOWN, "unknown"); + } else if (arptype == ARPHRD_ETHER) + return link_type_from_udev (platform, rtnllink, out_name); + else + return_type (NM_LINK_TYPE_UNKNOWN, "unknown"); } else if (!strcmp (type, "ipoib")) return_type (NM_LINK_TYPE_INFINIBAND, "infiniband"); else if (!strcmp (type, "dummy")) @@ -452,25 +480,80 @@ link_extract_type (struct rtnl_link *rtnllink, const char **out_name) return_type (NM_LINK_TYPE_BOND, "bond"); else if (!strcmp (type, "team")) return_type (NM_LINK_TYPE_TEAM, "team"); - else - return_type (NM_LINK_TYPE_UNKNOWN, "unknown"); + + return_type (NM_LINK_TYPE_UNKNOWN, "unknown"); +} + +static const char * +udev_get_driver (NMPlatform *platform, GUdevDevice *device, int ifindex) +{ + GUdevDevice *parent = NULL, *grandparent = NULL; + const char *driver, *subsys; + + driver = g_udev_device_get_driver (device); + if (driver) + return driver; + + /* Try the parent */ + parent = g_udev_device_get_parent (device); + if (parent) { + driver = g_udev_device_get_driver (parent); + if (!driver) { + /* Try the grandparent if it's an ibmebus device or if the + * subsys is NULL which usually indicates some sort of + * platform device like a 'gadget' net interface. + */ + subsys = g_udev_device_get_subsystem (parent); + if ( (g_strcmp0 (subsys, "ibmebus") == 0) + || (subsys == NULL)) { + grandparent = g_udev_device_get_parent (parent); + if (grandparent) { + driver = g_udev_device_get_driver (grandparent); + } + } + } + } + + /* Intern the string so we don't have to worry about memory + * management in NMPlatformLink. + */ + if (driver) + driver = g_intern_string (driver); + + g_clear_object (&parent); + g_clear_object (&grandparent); + + return driver; } static void -link_init (NMPlatformLink *info, struct rtnl_link *rtnllink) +link_init (NMPlatform *platform, NMPlatformLink *info, struct rtnl_link *rtnllink) { + NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); + GUdevDevice *udev_device; + memset (info, 0, sizeof (*info)); g_assert (rtnllink); info->ifindex = rtnl_link_get_ifindex (rtnllink); strcpy (info->name, rtnl_link_get_name (rtnllink)); - info->type = link_extract_type (rtnllink, &info->type_name); + info->type = link_extract_type (platform, rtnllink, &info->type_name); info->up = !!(rtnl_link_get_flags (rtnllink) & IFF_UP); info->connected = !!(rtnl_link_get_flags (rtnllink) & IFF_LOWER_UP); info->arp = !(rtnl_link_get_flags (rtnllink) & IFF_NOARP); info->master = rtnl_link_get_master (rtnllink); info->mtu = rtnl_link_get_mtu (rtnllink); + + udev_device = g_hash_table_lookup (priv->udev_devices, GINT_TO_POINTER (info->ifindex)); + if (udev_device) { + info->driver = udev_get_driver (platform, udev_device, info->ifindex); + if (!info->driver) + info->driver = rtnl_link_get_type (rtnllink); + if (!info->driver) + info->driver = "unknown"; + info->udi = g_udev_device_get_sysfs_path (udev_device); + } } /* Hack: Empty bridges and bonds have IFF_LOWER_UP flag and therefore they break @@ -502,7 +585,7 @@ hack_empty_master_iff_lower_up (NMPlatform *platform, struct nl_object *object) ifindex = rtnl_link_get_ifindex (rtnllink); - switch (link_extract_type (rtnllink, NULL)) { + switch (link_extract_type (platform, rtnllink, NULL)) { case NM_LINK_TYPE_BRIDGE: case NM_LINK_TYPE_BOND: for (slave = nl_cache_get_first (priv->link_cache); slave; slave = nl_cache_get_next (slave)) { @@ -626,12 +709,19 @@ announce_object (NMPlatform *platform, const struct nl_object *object, ObjectSta ObjectType object_type = object_type_from_nl_object (object); const char *sig = signal_by_type_and_status[object_type][status]; + if (object_type == LINK && status == ADDED) { + /* We have to wait until udev has registered the device; we'll + * emit NM_PLATFORM_LINK_ADDED from udev_device_added(). + */ + return; + } + switch (object_type) { case LINK: { NMPlatformLink device; - link_init (&device, (struct rtnl_link *) object); + link_init (platform, &device, (struct rtnl_link *) object); g_signal_emit_by_name (platform, sig, device.ifindex, &device); } return; @@ -948,7 +1038,7 @@ link_get_all (NMPlatform *platform) struct nl_object *object; for (object = nl_cache_get_first (priv->link_cache); object; object = nl_cache_get_next (object)) { - link_init (&device, (struct rtnl_link *) object); + link_init (platform, &device, (struct rtnl_link *) object); g_array_append_val (links, device); } @@ -1058,7 +1148,7 @@ link_get_type (NMPlatform *platform, int ifindex) { auto_nl_object struct rtnl_link *rtnllink = link_get (platform, ifindex); - return link_extract_type (rtnllink, NULL); + return link_extract_type (platform, rtnllink, NULL); } static const char * @@ -1067,7 +1157,7 @@ link_get_type_name (NMPlatform *platform, int ifindex) auto_nl_object struct rtnl_link *rtnllink = link_get (platform, ifindex); const char *type; - link_extract_type (rtnllink, &type); + link_extract_type (platform, rtnllink, &type); return type; } @@ -2061,6 +2151,113 @@ setup_socket (gboolean event, gpointer user_data) /******************************************************************/ +static void +udev_device_added (NMPlatform *platform, + GUdevDevice *udev_device) +{ + NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); + auto_nl_object struct rtnl_link *rtnllink = NULL; + const char *ifname, *devtype; + NMPlatformLink link; + int ifindex; + + ifname = g_udev_device_get_name (udev_device); + if (!ifname) { + debug ("failed to get device's interface"); + return; + } + + if (g_udev_device_get_sysfs_attr (udev_device, "ifindex")) + ifindex = g_udev_device_get_sysfs_attr_as_int (udev_device, "ifindex"); + else { + warning ("(%s): failed to get device's ifindex", ifname); + return; + } + + if (!g_udev_device_get_sysfs_path (udev_device)) { + debug ("(%s): couldn't determine device path; ignoring...", ifname); + return; + } + + /* Not all ethernet devices are immediately usable; newer mobile broadband + * devices (Ericsson, Option, Sierra) require setup on the tty before the + * ethernet device is usable. 2.6.33 and later kernels set the 'DEVTYPE' + * uevent variable which we can use to ignore the interface as a NMDevice + * subclass. ModemManager will pick it up though and so we'll handle it + * through the mobile broadband stuff. + */ + devtype = g_udev_device_get_property (udev_device, "DEVTYPE"); + if (g_strcmp0 (devtype, "wwan") == 0) { + debug ("(%s): ignoring interface with devtype '%s'", ifname, devtype); + return; + } + + rtnllink = link_get (platform, ifindex); + if (!rtnllink) { + debug ("%s: not found in link cache, ignoring...", ifname); + return; + } + + g_hash_table_insert (priv->udev_devices, GINT_TO_POINTER (ifindex), + g_object_ref (udev_device)); + + link_init (platform, &link, rtnllink); + g_signal_emit_by_name (platform, NM_PLATFORM_LINK_ADDED, ifindex, &link); +} + +static void +udev_device_removed (NMPlatform *platform, + GUdevDevice *udev_device) +{ + NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); + int ifindex; + + if (g_udev_device_get_sysfs_attr (udev_device, "ifindex")) { + ifindex = g_udev_device_get_sysfs_attr_as_int (udev_device, "ifindex"); + g_hash_table_remove (priv->udev_devices, GINT_TO_POINTER (ifindex)); + } else { + GHashTableIter iter; + gpointer key, value; + + /* On removal we aren't always be able to read properties like IFINDEX + * anymore, as they may have already been removed from sysfs. + */ + g_hash_table_iter_init (&iter, priv->udev_devices); + while (g_hash_table_iter_next (&iter, &key, &value)) { + if ((GUdevDevice *)value == udev_device) { + g_hash_table_iter_remove (&iter); + break; + } + } + } +} + +static void +handle_udev_event (GUdevClient *client, + const char *action, + GUdevDevice *udev_device, + gpointer user_data) +{ + NMPlatform *platform = NM_PLATFORM (user_data); + const char *subsys; + + g_return_if_fail (action != NULL); + + /* A bit paranoid */ + subsys = g_udev_device_get_subsystem (udev_device); + g_return_if_fail (!g_strcmp0 (subsys, "net")); + + debug ("UDEV event: action '%s' subsys '%s' device '%s'", + action, subsys, g_udev_device_get_name (udev_device)); + + if (!strcmp (action, "add")) + udev_device_added (platform, udev_device); + if (!strcmp (action, "remove")) + udev_device_removed (platform, udev_device); +} + +/******************************************************************/ + static void nm_linux_platform_init (NMLinuxPlatform *platform) { @@ -2070,6 +2267,9 @@ static gboolean setup (NMPlatform *platform) { NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); + const char *udev_subsys[] = { "net", NULL }; + GUdevEnumerator *enumerator; + GList *devices, *iter; int channel_flags; gboolean status; int nle; @@ -2113,6 +2313,32 @@ setup (NMPlatform *platform) rtnl_route_alloc_cache (priv->nlh, AF_UNSPEC, 0, &priv->route_cache); g_assert (priv->link_cache && priv->address_cache && priv->route_cache); + /* Set up udev monitoring */ + priv->udev_client = g_udev_client_new (udev_subsys); + g_signal_connect (priv->udev_client, "uevent", G_CALLBACK (handle_udev_event), platform); + priv->udev_devices = g_hash_table_new_full (NULL, NULL, NULL, g_object_unref); + + /* And read initial device list */ + enumerator = g_udev_enumerator_new (priv->udev_client); + g_udev_enumerator_add_match_subsystem (enumerator, "net"); + g_udev_enumerator_add_match_is_initialized (enumerator); + + devices = g_udev_enumerator_execute (enumerator); + for (iter = devices; iter; iter = g_list_next (iter)) { + GUdevDevice *udev_device = iter->data; + + if (g_udev_device_get_sysfs_attr (udev_device, "ifindex")) { + int ifindex = g_udev_device_get_sysfs_attr_as_int (udev_device, "ifindex"); + + g_hash_table_insert (priv->udev_devices, GINT_TO_POINTER (ifindex), + g_object_ref (udev_device)); + } + + g_object_unref (G_UDEV_DEVICE (iter->data)); + } + g_list_free (devices); + g_object_unref (enumerator); + return TRUE; } @@ -2130,6 +2356,9 @@ nm_linux_platform_finalize (GObject *object) nl_cache_free (priv->address_cache); nl_cache_free (priv->route_cache); + g_object_unref (priv->udev_client); + g_hash_table_unref (priv->udev_devices); + G_OBJECT_CLASS (nm_linux_platform_parent_class)->finalize (object); } diff --git a/src/platform/nm-platform.c b/src/platform/nm-platform.c index 13c3c674c4..930f3cf238 100644 --- a/src/platform/nm-platform.c +++ b/src/platform/nm-platform.c @@ -241,6 +241,26 @@ nm_platform_sysctl_get (const char *path) /******************************************************************/ +/** + * nm_platform_query_devices: + * + * Emit #NMPlatform:link-added signals for all currently-known links. + * Should only be called at startup. + */ +void +nm_platform_query_devices (void) +{ + GArray *links_array; + NMPlatformLink *links; + int i; + + links_array = nm_platform_link_get_all (); + links = (NMPlatformLink *) links_array->data; + for (i = 0; i < links_array->len; i++) + g_signal_emit (platform, signals[LINK_ADDED], 0, links[i].ifindex, &links[i]); + g_array_unref (links_array); +} + /** * nm_platform_link_get_all: * @@ -248,7 +268,7 @@ nm_platform_sysctl_get (const char *path) * owned by the caller and should be freed with g_array_unref(). */ GArray * -nm_platform_link_get_all () +nm_platform_link_get_all (void) { reset_error (); diff --git a/src/platform/nm-platform.h b/src/platform/nm-platform.h index 26bf629889..b9b9264fa3 100644 --- a/src/platform/nm-platform.h +++ b/src/platform/nm-platform.h @@ -54,6 +54,8 @@ typedef enum { /* Hardware types */ NM_LINK_TYPE_ETHERNET, NM_LINK_TYPE_INFINIBAND, + NM_LINK_TYPE_OLPC_MESH, + NM_LINK_TYPE_WIFI, /* Virtual types */ NM_LINK_TYPE_DUMMY, @@ -80,6 +82,8 @@ typedef struct { char name[IFNAMSIZ]; NMLinkType type; const char *type_name; + const char *udi; + const char *driver; int master; gboolean up; gboolean connected; @@ -297,6 +301,8 @@ void nm_platform_set_error (NMPlatformError error); NMPlatformError nm_platform_get_error (void); const char *nm_platform_get_error_msg (void); +void nm_platform_query_devices (void); + gboolean nm_platform_sysctl_set (const char *path, const char *value); char *nm_platform_sysctl_get (const char *path); diff --git a/src/platform/tests/Makefile.am b/src/platform/tests/Makefile.am index 9dbba3afb2..be0b14fcee 100644 --- a/src/platform/tests/Makefile.am +++ b/src/platform/tests/Makefile.am @@ -5,10 +5,11 @@ AM_CPPFLAGS = \ -I${top_srcdir}/libnm-util \ -I${srcdir}/.. \ $(GLIB_CFLAGS) \ + $(GUDEV_CFLAGS) \ $(LIBNL_CFLAGS) AM_CFLAGS = $(CODE_COVERAGE_CFLAGS) -AM_LDFLAGS = $(GLIB_LIBS) $(LIBNL_LIBS) $(CODE_COVERAGE_LDFLAGS) +AM_LDFLAGS = $(GLIB_LIBS) $(GUDEV_LIBS) $(LIBNL_LIBS) $(CODE_COVERAGE_LDFLAGS) PLATFORM_LDADD = \ $(top_builddir)/src/libNetworkManager.la diff --git a/src/platform/tests/dump.c b/src/platform/tests/dump.c index c0de7a25c4..d7c1b46a45 100644 --- a/src/platform/tests/dump.c +++ b/src/platform/tests/dump.c @@ -38,6 +38,9 @@ dump_interface (NMPlatformLink *link) printf (" master %d", link->master); printf (" mtu %d", link->mtu); printf ("\n"); + if (link->driver) + printf (" driver: %s\n", link->driver); + printf (" UDI: %s\n", link->udi); nm_platform_vlan_get_info (link->ifindex, &vlan_parent, &vlan_id); if (vlan_parent) printf (" vlan parent %d id %d\n", vlan_parent, vlan_id);