summaryrefslogtreecommitdiff
path: root/src/core/devices/nm-device-factory.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/core/devices/nm-device-factory.c')
-rw-r--r--src/core/devices/nm-device-factory.c413
1 files changed, 413 insertions, 0 deletions
diff --git a/src/core/devices/nm-device-factory.c b/src/core/devices/nm-device-factory.c
new file mode 100644
index 0000000000..7034202823
--- /dev/null
+++ b/src/core/devices/nm-device-factory.c
@@ -0,0 +1,413 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (C) 2014 - 2018 Red Hat, Inc.
+ */
+
+#include "nm-default.h"
+
+#include "nm-device-factory.h"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <gmodule.h>
+
+#include "platform/nm-platform.h"
+#include "nm-utils.h"
+#include "nm-core-internal.h"
+#include "nm-setting-bluetooth.h"
+
+#define PLUGIN_PREFIX "libnm-device-plugin-"
+
+/*****************************************************************************/
+
+enum { DEVICE_ADDED, LAST_SIGNAL };
+
+static guint signals[LAST_SIGNAL] = {0};
+
+G_DEFINE_ABSTRACT_TYPE(NMDeviceFactory, nm_device_factory, G_TYPE_OBJECT)
+
+/*****************************************************************************/
+
+static void
+nm_device_factory_get_supported_types(NMDeviceFactory * factory,
+ const NMLinkType ** out_link_types,
+ const char *const **out_setting_types)
+{
+ g_return_if_fail(NM_IS_DEVICE_FACTORY(factory));
+
+ NM_DEVICE_FACTORY_GET_CLASS(factory)->get_supported_types(factory,
+ out_link_types,
+ out_setting_types);
+}
+
+void
+nm_device_factory_start(NMDeviceFactory *factory)
+{
+ g_return_if_fail(factory != NULL);
+
+ if (NM_DEVICE_FACTORY_GET_CLASS(factory)->start)
+ NM_DEVICE_FACTORY_GET_CLASS(factory)->start(factory);
+}
+
+NMDevice *
+nm_device_factory_create_device(NMDeviceFactory * factory,
+ const char * iface,
+ const NMPlatformLink *plink,
+ NMConnection * connection,
+ gboolean * out_ignore,
+ GError ** error)
+{
+ NMDeviceFactoryClass *klass;
+ NMDevice * device;
+ gboolean ignore = FALSE;
+
+ g_return_val_if_fail(factory, NULL);
+ g_return_val_if_fail(iface && *iface, NULL);
+ if (plink) {
+ g_return_val_if_fail(!connection, NULL);
+ g_return_val_if_fail(strcmp(iface, plink->name) == 0, NULL);
+ nm_assert(factory == nm_device_factory_manager_find_factory_for_link_type(plink->type));
+ } else if (connection)
+ nm_assert(factory == nm_device_factory_manager_find_factory_for_connection(connection));
+ else
+ g_return_val_if_reached(NULL);
+
+ klass = NM_DEVICE_FACTORY_GET_CLASS(factory);
+ if (!klass->create_device) {
+ g_set_error(error,
+ NM_MANAGER_ERROR,
+ NM_MANAGER_ERROR_FAILED,
+ "Device factory %s cannot manage new devices",
+ G_OBJECT_TYPE_NAME(factory));
+ NM_SET_OUT(out_ignore, FALSE);
+ return NULL;
+ }
+
+ device = klass->create_device(factory, iface, plink, connection, &ignore);
+ NM_SET_OUT(out_ignore, ignore);
+ if (!device) {
+ if (ignore) {
+ g_set_error(error,
+ NM_MANAGER_ERROR,
+ NM_MANAGER_ERROR_FAILED,
+ "Device factory %s ignores device %s",
+ G_OBJECT_TYPE_NAME(factory),
+ iface);
+ } else {
+ g_set_error(error,
+ NM_MANAGER_ERROR,
+ NM_MANAGER_ERROR_FAILED,
+ "Device factory %s failed to create device %s",
+ G_OBJECT_TYPE_NAME(factory),
+ iface);
+ }
+ }
+ return device;
+}
+
+const char *
+nm_device_factory_get_connection_parent(NMDeviceFactory *factory, NMConnection *connection)
+{
+ g_return_val_if_fail(factory != NULL, NULL);
+ g_return_val_if_fail(connection != NULL, NULL);
+
+ if (!nm_connection_is_virtual(connection))
+ return NULL;
+
+ if (NM_DEVICE_FACTORY_GET_CLASS(factory)->get_connection_parent)
+ return NM_DEVICE_FACTORY_GET_CLASS(factory)->get_connection_parent(factory, connection);
+ return NULL;
+}
+
+char *
+nm_device_factory_get_connection_iface(NMDeviceFactory *factory,
+ NMConnection * connection,
+ const char * parent_iface,
+ GError ** error)
+{
+ NMDeviceFactoryClass *klass;
+ char * ifname;
+
+ g_return_val_if_fail(factory != NULL, NULL);
+ g_return_val_if_fail(connection != NULL, NULL);
+ g_return_val_if_fail(!error || !*error, NULL);
+
+ klass = NM_DEVICE_FACTORY_GET_CLASS(factory);
+
+ ifname = g_strdup(nm_connection_get_interface_name(connection));
+ if (!ifname && klass->get_connection_iface)
+ ifname = klass->get_connection_iface(factory, connection, parent_iface);
+
+ if (!ifname) {
+ g_set_error(error,
+ NM_MANAGER_ERROR,
+ NM_MANAGER_ERROR_FAILED,
+ "failed to determine interface name: error determine name for %s",
+ nm_connection_get_connection_type(connection));
+ return NULL;
+ }
+
+ return ifname;
+}
+
+/*****************************************************************************/
+
+static void
+nm_device_factory_init(NMDeviceFactory *self)
+{}
+
+static void
+nm_device_factory_class_init(NMDeviceFactoryClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS(klass);
+
+ signals[DEVICE_ADDED] = g_signal_new(NM_DEVICE_FACTORY_DEVICE_ADDED,
+ G_OBJECT_CLASS_TYPE(object_class),
+ G_SIGNAL_RUN_FIRST,
+ 0,
+ NULL,
+ NULL,
+ NULL,
+ G_TYPE_NONE,
+ 1,
+ NM_TYPE_DEVICE);
+}
+
+/*****************************************************************************/
+
+static GHashTable *factories_by_link = NULL;
+static GHashTable *factories_by_setting = NULL;
+
+static void __attribute__((destructor)) _cleanup(void)
+{
+ nm_clear_pointer(&factories_by_link, g_hash_table_unref);
+ nm_clear_pointer(&factories_by_setting, g_hash_table_unref);
+}
+
+NMDeviceFactory *
+nm_device_factory_manager_find_factory_for_link_type(NMLinkType link_type)
+{
+ g_return_val_if_fail(factories_by_link, NULL);
+
+ return g_hash_table_lookup(factories_by_link, GUINT_TO_POINTER(link_type));
+}
+
+NMDeviceFactory *
+nm_device_factory_manager_find_factory_for_connection(NMConnection *connection)
+{
+ NMDeviceFactoryClass *klass;
+ NMDeviceFactory * factory;
+ const char * type;
+ GSList * list;
+
+ g_return_val_if_fail(factories_by_setting, NULL);
+
+ type = nm_connection_get_connection_type(connection);
+ list = g_hash_table_lookup(factories_by_setting, type);
+
+ for (; list; list = g_slist_next(list)) {
+ factory = list->data;
+ klass = NM_DEVICE_FACTORY_GET_CLASS(factory);
+ if (!klass->match_connection || klass->match_connection(factory, connection))
+ return factory;
+ }
+
+ return NULL;
+}
+
+void
+nm_device_factory_manager_for_each_factory(NMDeviceFactoryManagerFactoryFunc callback,
+ gpointer user_data)
+{
+ GHashTableIter iter;
+ NMDeviceFactory *factory;
+ GSList * list_iter, *list = NULL;
+
+ if (factories_by_link) {
+ g_hash_table_iter_init(&iter, factories_by_link);
+ while (g_hash_table_iter_next(&iter, NULL, (gpointer) &factory)) {
+ if (!g_slist_find(list, factory))
+ list = g_slist_prepend(list, factory);
+ }
+ }
+
+ if (factories_by_setting) {
+ g_hash_table_iter_init(&iter, factories_by_setting);
+ while (g_hash_table_iter_next(&iter, NULL, (gpointer) &list_iter)) {
+ for (; list_iter; list_iter = g_slist_next(list_iter)) {
+ if (!g_slist_find(list, list_iter->data))
+ list = g_slist_prepend(list, list_iter->data);
+ }
+ }
+ }
+
+ for (list_iter = list; list_iter; list_iter = list_iter->next)
+ callback(list_iter->data, user_data);
+
+ g_slist_free(list);
+}
+
+static gboolean
+_add_factory(NMDeviceFactory * factory,
+ const char * path,
+ NMDeviceFactoryManagerFactoryFunc callback,
+ gpointer user_data)
+{
+ const NMLinkType * link_types = NULL;
+ const char *const *setting_types = NULL;
+ GSList * list, *list2;
+ int i;
+
+ g_return_val_if_fail(factories_by_link, FALSE);
+ g_return_val_if_fail(factories_by_setting, FALSE);
+
+ nm_device_factory_get_supported_types(factory, &link_types, &setting_types);
+
+ g_return_val_if_fail((link_types && link_types[0] > NM_LINK_TYPE_UNKNOWN)
+ || (setting_types && setting_types[0]),
+ FALSE);
+
+ for (i = 0; link_types && link_types[i] > NM_LINK_TYPE_UNKNOWN; i++)
+ g_hash_table_insert(factories_by_link,
+ GUINT_TO_POINTER(link_types[i]),
+ g_object_ref(factory));
+ for (i = 0; setting_types && setting_types[i]; i++) {
+ list = g_hash_table_lookup(factories_by_setting, (char *) setting_types[i]);
+ if (list) {
+ list2 = g_slist_append(list, g_object_ref(factory));
+ nm_assert(list == list2);
+ } else {
+ list = g_slist_append(list, g_object_ref(factory));
+ g_hash_table_insert(factories_by_setting, (char *) setting_types[i], list);
+ }
+ }
+
+ callback(factory, user_data);
+
+ nm_log(path ? LOGL_INFO : LOGL_DEBUG,
+ LOGD_PLATFORM,
+ NULL,
+ NULL,
+ "Loaded device plugin: %s (%s)",
+ G_OBJECT_TYPE_NAME(factory),
+ path ?: "internal");
+ return TRUE;
+}
+
+static void
+_load_internal_factory(GType factory_gtype,
+ NMDeviceFactoryManagerFactoryFunc callback,
+ gpointer user_data)
+{
+ gs_unref_object NMDeviceFactory *factory = NULL;
+
+ factory = g_object_new(factory_gtype, NULL);
+ _add_factory(factory, NULL, callback, user_data);
+}
+
+static void
+factories_list_unref(GSList *list)
+{
+ g_slist_free_full(list, g_object_unref);
+}
+
+static void
+load_factories_from_dir(const char * dirname,
+ NMDeviceFactoryManagerFactoryFunc callback,
+ gpointer user_data)
+{
+ NMDeviceFactory *factory;
+ GError * error = NULL;
+ char ** path, **paths;
+
+ paths = nm_utils_read_plugin_paths(dirname, PLUGIN_PREFIX);
+ if (!paths)
+ return;
+
+ for (path = paths; *path; path++) {
+ GModule * plugin;
+ NMDeviceFactoryCreateFunc create_func;
+ const char * item;
+
+ item = strrchr(*path, '/');
+ g_assert(item);
+
+ plugin = g_module_open(*path, G_MODULE_BIND_LOCAL);
+
+ if (!plugin) {
+ nm_log_warn(LOGD_PLATFORM, "(%s): failed to load plugin: %s", item, g_module_error());
+ continue;
+ }
+
+ if (!g_module_symbol(plugin, "nm_device_factory_create", (gpointer) &create_func)) {
+ nm_log_warn(LOGD_PLATFORM,
+ "(%s): failed to find device factory creator: %s",
+ item,
+ g_module_error());
+ g_module_close(plugin);
+ continue;
+ }
+
+ /* after loading glib types from the plugin, we cannot unload the library anymore.
+ * Make it resident. */
+ g_module_make_resident(plugin);
+
+ factory = create_func(&error);
+ if (!factory) {
+ nm_log_warn(LOGD_PLATFORM,
+ "(%s): failed to initialize device factory: %s",
+ item,
+ NM_G_ERROR_MSG(error));
+ g_clear_error(&error);
+ continue;
+ }
+ g_clear_error(&error);
+
+ _add_factory(factory, g_module_name(plugin), callback, user_data);
+
+ g_object_unref(factory);
+ }
+ g_strfreev(paths);
+}
+
+void
+nm_device_factory_manager_load_factories(NMDeviceFactoryManagerFactoryFunc callback,
+ gpointer user_data)
+{
+ g_return_if_fail(factories_by_link == NULL);
+ g_return_if_fail(factories_by_setting == NULL);
+
+ factories_by_link = g_hash_table_new_full(nm_direct_hash, NULL, NULL, g_object_unref);
+ factories_by_setting = g_hash_table_new_full(nm_str_hash,
+ g_str_equal,
+ NULL,
+ (GDestroyNotify) factories_list_unref);
+
+#define _ADD_INTERNAL(get_type_fcn) \
+ G_STMT_START \
+ { \
+ GType get_type_fcn(void); \
+ _load_internal_factory(get_type_fcn(), callback, user_data); \
+ } \
+ G_STMT_END
+
+ _ADD_INTERNAL(nm_6lowpan_device_factory_get_type);
+ _ADD_INTERNAL(nm_bond_device_factory_get_type);
+ _ADD_INTERNAL(nm_bridge_device_factory_get_type);
+ _ADD_INTERNAL(nm_dummy_device_factory_get_type);
+ _ADD_INTERNAL(nm_ethernet_device_factory_get_type);
+ _ADD_INTERNAL(nm_infiniband_device_factory_get_type);
+ _ADD_INTERNAL(nm_ip_tunnel_device_factory_get_type);
+ _ADD_INTERNAL(nm_macsec_device_factory_get_type);
+ _ADD_INTERNAL(nm_macvlan_device_factory_get_type);
+ _ADD_INTERNAL(nm_ppp_device_factory_get_type);
+ _ADD_INTERNAL(nm_tun_device_factory_get_type);
+ _ADD_INTERNAL(nm_veth_device_factory_get_type);
+ _ADD_INTERNAL(nm_vlan_device_factory_get_type);
+ _ADD_INTERNAL(nm_vrf_device_factory_get_type);
+ _ADD_INTERNAL(nm_vxlan_device_factory_get_type);
+ _ADD_INTERNAL(nm_wireguard_device_factory_get_type);
+ _ADD_INTERNAL(nm_wpan_device_factory_get_type);
+
+ load_factories_from_dir(NMPLUGINDIR, callback, user_data);
+}