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; +} +