From 2099459794c93e488c689e8d8925ff1bf2802518 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Fri, 18 Nov 2011 15:52:42 -0600 Subject: [PATCH] core: add device factory infrastructure and make WiMAX a plugin This allows us to package WiMAX separately so you can choose to install WiMAX or not. For package-based distros you can now install libnm-device-plugin-wimax.so in a separate package that links to/requires the Intel WiMAX service instead of having the main NM package require it and pull it in by default. This plugin infrastructure will be extended to other device types as well. --- src/Makefile.am | 13 +-- src/nm-device-factory.h | 91 ++++++++++++++++++ src/nm-udev-manager.c | 180 ++++++++++++++++++++++++++++++----- src/wimax/Makefile.am | 14 ++- src/wimax/nm-wimax-factory.c | 58 +++++++++++ 5 files changed, 316 insertions(+), 40 deletions(-) create mode 100644 src/nm-device-factory.h create mode 100644 src/wimax/nm-wimax-factory.c diff --git a/src/Makefile.am b/src/Makefile.am index 821d220624..46bf03f1cb 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -36,10 +36,6 @@ INCLUDES = -I${top_srcdir} \ -I${top_srcdir}/libnm-util \ -I${top_srcdir}/callouts -if WITH_WIMAX -INCLUDES += -I$(top_srcdir)/src/wimax -endif - ########################################### # Test libraries ########################################### @@ -146,6 +142,7 @@ NetworkManager_SOURCES = \ nm-dbus-manager.c \ nm-udev-manager.c \ nm-udev-manager.h \ + nm-device-factory.h \ nm-hostname-provider.c \ nm-hostname-provider.h \ nm-ip4-config.c \ @@ -265,14 +262,9 @@ NetworkManager_CPPFLAGS = \ -DLOCALSTATEDIR=\"$(localstatedir)\" \ -DNM_RUN_DIR=\"$(rundir)\" \ -DNMLOCALEDIR=\"$(datadir)/locale\" \ + -DNMPLUGINDIR=\"$(pkglibdir)\" -DARP_DEBUG - -WIMAX_LIBS= -if WITH_WIMAX -WIMAX_LIBS += ./wimax/libwimax.la -endif - NetworkManager_LDADD = \ $(top_builddir)/marshallers/libmarshallers.la \ ./logging/libnm-logging.la \ @@ -287,7 +279,6 @@ NetworkManager_LDADD = \ ./bluez-manager/libbluez-manager.la \ ./firewall-manager/libfirewall-manager.la \ ./settings/libsettings.la \ - $(WIMAX_LIBS) \ ./backends/libnmbackend.la \ $(top_builddir)/libnm-util/libnm-util.la \ $(DBUS_LIBS) \ diff --git a/src/nm-device-factory.h b/src/nm-device-factory.h new file mode 100644 index 0000000000..8a4f446726 --- /dev/null +++ b/src/nm-device-factory.h @@ -0,0 +1,91 @@ +/* -*- 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 - 2011 Red Hat, Inc. + */ + +#ifndef NM_DEVICE_FACTORY_H +#define NM_DEVICE_FACTORY_H + +#include +#include +#include + +#include "NetworkManager.h" + +/* WARNING: this file is private API between NetworkManager and its internal + * device plugins. Its API can change at any time and is not guaranteed to be + * stable. NM and device plugins are distributed together and this API is + * not meant to enable third-party plugins. + */ + +/** + * 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 + * @error: error for failure information + * + * Creates a #NMDevice subclass if the given information represents a device + * the factory is capable of creating. If the information does represent a + * device the factory is capable of creating, but the device could not be + * created, %NULL should be returned and @error should be set. If the + * factory is not capable of creating a device with the given information + * (ie, the factory creates Ethernet devices but the information represents + * a WiFi device) it should return %NULL and leave @error untouched. + * + * Returns: the device object (a subclass of #NMDevice) or %NULL + */ +GObject *nm_device_factory_create_device (GUdevDevice *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, + const char *ifname, + const char *driver, + GError **error); + +/** + * nm_device_factory_get_priority: + * + * Returns the priority of this plugin. Higher numbers mean a higher priority. + * + * Returns: plugin priority + */ +guint32 nm_device_factory_get_priority (void); + +typedef guint32 (*NMDeviceFactoryPriorityFunc) (void); + +/** + * nm_device_factory_get_type: + * + * Returns the type of device this factory can create. Only one factory for + * each type of device is allowed. + * + * Returns: the %NMDeviceType + */ +NMDeviceType nm_device_factory_get_type (void); + +typedef NMDeviceType (*NMDeviceFactoryTypeFunc) (void); + +#endif /* NM_DEVICE_FACTORY_H */ + diff --git a/src/nm-udev-manager.c b/src/nm-udev-manager.c index 7ddd6ce563..3ea447de1b 100644 --- a/src/nm-udev-manager.c +++ b/src/nm-udev-manager.c @@ -28,6 +28,7 @@ #include #include +#include #include #include "nm-udev-manager.h" @@ -38,15 +39,16 @@ #include "nm-device-olpc-mesh.h" #include "nm-device-infiniband.h" #include "nm-device-ethernet.h" +#include "nm-device-factory.h" #include "wifi-utils.h" -#if WITH_WIMAX -#include "nm-device-wimax.h" -#endif #include "nm-system.h" typedef struct { GUdevClient *client; + /* List of NMDeviceFactoryFunc pointers sorted in priority order */ + GSList *factories; + /* Authoritative rfkill state (RFKILL_* enum) */ RfKillState rfkill_states[RFKILL_TYPE_MAX]; GSList *killswitches; @@ -374,15 +376,6 @@ is_olpc_mesh (GUdevDevice *device) return (prop != NULL); } -static gboolean -is_wimax (const char *driver) -{ - /* FIXME: check 'DEVTYPE' instead; but since we only support Intel - * WiMAX devices for now this is appropriate. - */ - return g_strcmp0 (driver, "i2400m_usb") == 0; -} - static gboolean is_infiniband (GUdevDevice *device) { @@ -395,10 +388,13 @@ device_creator (NMUdevManager *manager, GUdevDevice *udev_device, gboolean sleeping) { + NMUdevManagerPrivate *priv = NM_UDEV_MANAGER_GET_PRIVATE (manager); GObject *device = NULL; const char *ifname, *driver, *path, *subsys; GUdevDevice *parent = NULL, *grandparent = NULL; gint ifindex; + GError *error = NULL; + GSList *iter; ifname = g_udev_device_get_name (udev_device); g_assert (ifname); @@ -455,18 +451,35 @@ device_creator (NMUdevManager *manager, goto out; } - if (is_olpc_mesh (udev_device)) /* must be before is_wireless */ - device = (GObject *) nm_device_olpc_mesh_new (path, ifname, driver); - else if (is_wireless (udev_device)) - device = (GObject *) nm_device_wifi_new (path, ifname, driver); - else if (is_wimax (driver)) { -#if WITH_WIMAX - device = (GObject *) nm_device_wimax_new (path, ifname, driver); -#endif - } else if (is_infiniband (udev_device)) - device = (GObject *) nm_device_infiniband_new (path, ifname, driver); - else - device = (GObject *) nm_device_ethernet_new (path, ifname, driver); + /* Try registered device factories */ + for (iter = priv->factories; iter; iter = g_slist_next (iter)) { + NMDeviceFactoryCreateFunc create_func = iter->data; + + g_clear_error (&error); + device = create_func (udev_device, path, ifname, driver, &error); + if (device) { + g_assert_no_error (error); + break; /* success! */ + } + + if (error) { + nm_log_warn (LOGD_HW, "%s: factory failed to create device: (%d) %s", + path, error->code, error->message); + g_clear_error (&error); + goto out; + } + } + + if (device == NULL) { + if (is_olpc_mesh (udev_device)) /* must be before is_wireless */ + device = (GObject *) nm_device_olpc_mesh_new (path, ifname, driver); + else if (is_wireless (udev_device)) + device = (GObject *) nm_device_wifi_new (path, ifname, driver); + else if (is_infiniband (udev_device)) + device = (GObject *) nm_device_infiniband_new (path, ifname, driver); + else + device = (GObject *) nm_device_ethernet_new (path, ifname, driver); + } out: if (grandparent) @@ -555,6 +568,121 @@ nm_udev_manager_query_devices (NMUdevManager *self) g_list_free (devices); } +#define PLUGIN_PREFIX "libnm-device-plugin-" + +typedef struct { + NMDeviceType t; + guint priority; + NMDeviceFactoryCreateFunc create_func; +} PluginInfo; + +static gint +plugin_sort (PluginInfo *a, PluginInfo *b) +{ + /* Higher priority means sort earlier in the list (ie, return -1) */ + if (a->priority > b->priority) + return -1; + else if (a->priority < b->priority) + return 1; + return 0; +} + +static void +load_device_factories (NMUdevManager *self) +{ + NMUdevManagerPrivate *priv = NM_UDEV_MANAGER_GET_PRIVATE (self); + GDir *dir; + GError *error = NULL; + const char *item; + char *path; + GSList *list = NULL, *iter; + + dir = g_dir_open (NMPLUGINDIR, 0, &error); + if (!dir) { + nm_log_warn (LOGD_HW, "Failed to open plugin directory %s: %s", + NMPLUGINDIR, + (error && error->message) ? error->message : "(unknown)"); + g_clear_error (&error); + return; + } + + while ((item = g_dir_read_name (dir))) { + GModule *plugin; + NMDeviceFactoryCreateFunc create_func; + NMDeviceFactoryPriorityFunc priority_func; + NMDeviceFactoryTypeFunc type_func; + PluginInfo *info = NULL; + NMDeviceType plugin_type; + + if (!g_str_has_prefix (item, PLUGIN_PREFIX)) + continue; + + path = g_module_build_path (NMPLUGINDIR, item); + g_assert (path); + plugin = g_module_open (path, G_MODULE_BIND_LOCAL); + g_free (path); + + if (!plugin) { + nm_log_warn (LOGD_HW, "(%s): failed to load plugin: %s", item, g_module_error ()); + continue; + } + + if (!g_module_symbol (plugin, "nm_device_factory_get_type", (gpointer) (&type_func))) { + nm_log_warn (LOGD_HW, "(%s): failed to find device factory: %s", item, g_module_error ()); + g_module_close (plugin); + continue; + } + + /* Make sure we don't double-load plugins */ + plugin_type = type_func (); + for (iter = list; iter; iter = g_slist_next (iter)) { + PluginInfo *candidate = iter->data; + + if (plugin_type == candidate->t) { + info = candidate; + break; + } + } + if (info) { + g_module_close (plugin); + continue; + } + + if (!g_module_symbol (plugin, "nm_device_factory_create_device", (gpointer) (&create_func))) { + nm_log_warn (LOGD_HW, "(%s): failed to find device creator: %s", item, g_module_error ()); + g_module_close (plugin); + continue; + } + + info = g_malloc0 (sizeof (*info)); + info->create_func = create_func; + info->t = plugin_type; + + /* Grab priority; higher number equals higher priority */ + if (g_module_symbol (plugin, "nm_device_factory_get_priority", (gpointer) (&priority_func))) + info->priority = priority_func (); + else { + nm_log_dbg (LOGD_HW, "(%s): failed to find device factory priority func: %s", + item, g_module_error ()); + } + + g_module_make_resident (plugin); + list = g_slist_insert_sorted (list, info, (GCompareFunc) plugin_sort); + + nm_log_info (LOGD_HW, "Loaded device factory: %s", g_module_name (plugin)); + }; + g_dir_close (dir); + + /* Ditch the priority info and copy the factory functions to our private data */ + for (iter = list; iter; iter = g_slist_next (iter)) { + PluginInfo *info = iter->data; + + priv->factories = g_slist_append (priv->factories, info->create_func); + g_free (info); + } + g_slist_free (list); +} + static void handle_uevent (GUdevClient *client, const char *action, @@ -612,6 +740,8 @@ nm_udev_manager_init (NMUdevManager *self) g_list_free (switches); recheck_killswitches (self); + + load_device_factories (self); } static void @@ -631,6 +761,8 @@ dispose (GObject *object) g_slist_foreach (priv->killswitches, (GFunc) killswitch_destroy, NULL); g_slist_free (priv->killswitches); + g_slist_free (priv->factories); + G_OBJECT_CLASS (nm_udev_manager_parent_class)->dispose (object); } diff --git a/src/wimax/Makefile.am b/src/wimax/Makefile.am index 83c8c27bff..7d9f61d87f 100644 --- a/src/wimax/Makefile.am +++ b/src/wimax/Makefile.am @@ -5,9 +5,10 @@ INCLUDES = \ -I${top_srcdir}/libnm-util \ -I${top_builddir}/marshallers -noinst_LTLIBRARIES = libwimax.la +pkglib_LTLIBRARIES = libnm-device-plugin-wimax.la -libwimax_la_SOURCES = \ +libnm_device_plugin_wimax_la_SOURCES = \ + nm-wimax-factory.c \ nm-device-wimax.c \ nm-device-wimax.h \ nm-wimax-nsp.c \ @@ -18,14 +19,17 @@ libwimax_la_SOURCES = \ iwmxsdk.c \ iwmxsdk.h -libwimax_la_CPPFLAGS = \ +libnm_device_plugin_wimax_la_CPPFLAGS = \ $(DBUS_CFLAGS) \ $(IWMX_SDK_CFLAGS) \ - $(LIBNL_CFLAGS) + $(LIBNL_CFLAGS) \ + $(GUDEV_CFLAGS) -libwimax_la_LIBADD = \ +libnm_device_plugin_wimax_la_LDFLAGS = -module -avoid-version +libnm_device_plugin_wimax_la_LIBADD = \ $(DBUS_LIBS) \ $(IWMX_SDK_LIBS) \ + $(GUDEV_LIBS) \ $(top_builddir)/marshallers/libmarshallers.la nm-wimax-nsp-glue.h: $(top_srcdir)/introspection/nm-wimax-nsp.xml diff --git a/src/wimax/nm-wimax-factory.c b/src/wimax/nm-wimax-factory.c new file mode 100644 index 0000000000..95cb4bf743 --- /dev/null +++ b/src/wimax/nm-wimax-factory.c @@ -0,0 +1,58 @@ +/* -*- 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) 2011 Red Hat, Inc. + */ + +#include + +#include "nm-device-factory.h" +#include "nm-device-wimax.h" + +G_MODULE_EXPORT GObject * +nm_device_factory_create_device (GUdevDevice *device, + const char *devpath, + const char *ifname, + const char *driver, + GError **error) +{ + GObject *dev; + + /* FIXME: check 'DEVTYPE' instead; but since we only support Intel + * WiMAX devices for now this is appropriate. + */ + if (g_strcmp0 (driver, "i2400m_usb") != 0) + return NULL; /* unsupported */ + + dev = (GObject *) nm_device_wimax_new (devpath, ifname, driver); + if (dev == NULL) + g_set_error_literal (error, 0, 0, "Failed to create WiMAX device."); + return dev; +} + +G_MODULE_EXPORT guint32 +nm_device_factory_get_priority (void) +{ + return 0; +} + +G_MODULE_EXPORT NMDeviceType +nm_device_factory_get_type (void) +{ + return NM_DEVICE_TYPE_WIMAX; +} +