diff options
Diffstat (limited to 'src/core/devices/nm-device-factory.c')
-rw-r--r-- | src/core/devices/nm-device-factory.c | 413 |
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); +} |