|
Packit Service |
dff8e4 |
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
|
Packit Service |
dff8e4 |
/*
|
|
Packit Service |
dff8e4 |
* Copyright (C) 2016, 2018 Red Hat, Inc.
|
|
Packit Service |
dff8e4 |
*/
|
|
Packit Service |
dff8e4 |
|
|
Packit Service |
dff8e4 |
#include "libnm-client-aux-extern/nm-default-client.h"
|
|
Packit Service |
dff8e4 |
|
|
Packit Service |
dff8e4 |
#include "nm-vpn-plugin-utils.h"
|
|
Packit Service |
dff8e4 |
|
|
Packit Service |
dff8e4 |
#include <dlfcn.h>
|
|
Packit Service |
dff8e4 |
|
|
Packit Service |
dff8e4 |
/*****************************************************************************/
|
|
Packit Service |
dff8e4 |
|
|
Packit Service |
dff8e4 |
NMVpnEditor *
|
|
Packit Service |
dff8e4 |
nm_vpn_plugin_utils_load_editor(const char * module_name,
|
|
Packit Service |
dff8e4 |
const char * factory_name,
|
|
Packit Service |
dff8e4 |
NMVpnPluginUtilsEditorFactory editor_factory,
|
|
Packit Service |
dff8e4 |
NMVpnEditorPlugin * editor_plugin,
|
|
Packit Service |
dff8e4 |
NMConnection * connection,
|
|
Packit Service |
dff8e4 |
gpointer user_data,
|
|
Packit Service |
dff8e4 |
GError ** error)
|
|
Packit Service |
dff8e4 |
|
|
Packit Service |
dff8e4 |
{
|
|
Packit Service |
dff8e4 |
static struct {
|
|
Packit Service |
dff8e4 |
gpointer factory;
|
|
Packit Service |
dff8e4 |
void * dl_module;
|
|
Packit Service |
dff8e4 |
char * module_name;
|
|
Packit Service |
dff8e4 |
char * factory_name;
|
|
Packit Service |
dff8e4 |
} cached = {0};
|
|
Packit Service |
dff8e4 |
NMVpnEditor * editor;
|
|
Packit Service |
dff8e4 |
gs_free char *module_path = NULL;
|
|
Packit Service |
dff8e4 |
gs_free char *dirname = NULL;
|
|
Packit Service |
dff8e4 |
Dl_info plugin_info;
|
|
Packit Service |
dff8e4 |
|
|
Packit Service |
dff8e4 |
g_return_val_if_fail(module_name, NULL);
|
|
Packit Service |
dff8e4 |
g_return_val_if_fail(factory_name && factory_name[0], NULL);
|
|
Packit Service |
dff8e4 |
g_return_val_if_fail(editor_factory, NULL);
|
|
Packit Service |
dff8e4 |
g_return_val_if_fail(NM_IS_VPN_EDITOR_PLUGIN(editor_plugin), NULL);
|
|
Packit Service |
dff8e4 |
g_return_val_if_fail(NM_IS_CONNECTION(connection), NULL);
|
|
Packit Service |
dff8e4 |
g_return_val_if_fail(!error || !*error, NULL);
|
|
Packit Service |
dff8e4 |
|
|
Packit Service |
dff8e4 |
if (!g_path_is_absolute(module_name)) {
|
|
Packit Service |
dff8e4 |
/*
|
|
Packit Service |
dff8e4 |
* Load an editor from the same directory this plugin is in.
|
|
Packit Service |
dff8e4 |
* Ideally, we'd get our .so name from the NMVpnEditorPlugin if it
|
|
Packit Service |
dff8e4 |
* would just have a property with it...
|
|
Packit Service |
dff8e4 |
*/
|
|
Packit Service |
dff8e4 |
if (!dladdr(nm_vpn_plugin_utils_load_editor, &plugin_info)) {
|
|
Packit Service |
dff8e4 |
/* Really a "can not happen" scenario. */
|
|
Packit Service |
dff8e4 |
g_set_error(error,
|
|
Packit Service |
dff8e4 |
NM_VPN_PLUGIN_ERROR,
|
|
Packit Service |
dff8e4 |
NM_VPN_PLUGIN_ERROR_FAILED,
|
|
Packit Service |
dff8e4 |
_("unable to get editor plugin name: %s"),
|
|
Packit Service |
dff8e4 |
dlerror());
|
|
Packit Service |
dff8e4 |
}
|
|
Packit Service |
dff8e4 |
|
|
Packit Service |
dff8e4 |
dirname = g_path_get_dirname(plugin_info.dli_fname);
|
|
Packit Service |
dff8e4 |
module_path = g_build_filename(dirname, module_name, NULL);
|
|
Packit Service |
dff8e4 |
} else {
|
|
Packit Service |
dff8e4 |
module_path = g_strdup(module_name);
|
|
Packit Service |
dff8e4 |
}
|
|
Packit Service |
dff8e4 |
|
|
Packit Service |
dff8e4 |
/* we really expect this function to be called with unchanging @module_name
|
|
Packit Service |
dff8e4 |
* and @factory_name. And we only want to load the module once, hence it would
|
|
Packit Service |
dff8e4 |
* be more complicated to accept changing @module_name/@factory_name arguments.
|
|
Packit Service |
dff8e4 |
*
|
|
Packit Service |
dff8e4 |
* The reason for only loading once is that due to glib types, we cannot create a
|
|
Packit Service |
dff8e4 |
* certain type-name more then once, so loading the same module or another version
|
|
Packit Service |
dff8e4 |
* of the same module will fail horribly as both try to create a GType with the same
|
|
Packit Service |
dff8e4 |
* name.
|
|
Packit Service |
dff8e4 |
*
|
|
Packit Service |
dff8e4 |
* Only support loading once, any future calls will reuse the handle. To simplify
|
|
Packit Service |
dff8e4 |
* that, we enforce that the @factory_name and @module_name is the same. */
|
|
Packit Service |
dff8e4 |
if (cached.factory) {
|
|
Packit Service |
dff8e4 |
g_return_val_if_fail(cached.dl_module, NULL);
|
|
Packit Service |
dff8e4 |
g_return_val_if_fail(cached.factory_name && nm_streq0(cached.factory_name, factory_name),
|
|
Packit Service |
dff8e4 |
NULL);
|
|
Packit Service |
dff8e4 |
g_return_val_if_fail(cached.module_name && nm_streq0(cached.module_name, module_name),
|
|
Packit Service |
dff8e4 |
NULL);
|
|
Packit Service |
dff8e4 |
} else {
|
|
Packit Service |
dff8e4 |
gpointer factory;
|
|
Packit Service |
dff8e4 |
void * dl_module;
|
|
Packit Service |
dff8e4 |
|
|
Packit Service |
dff8e4 |
dl_module = dlopen(module_path, RTLD_LAZY | RTLD_LOCAL);
|
|
Packit Service |
dff8e4 |
if (!dl_module) {
|
|
Packit Service |
dff8e4 |
if (!g_file_test(module_path, G_FILE_TEST_EXISTS)) {
|
|
Packit Service |
dff8e4 |
g_set_error(error,
|
|
Packit Service |
dff8e4 |
G_FILE_ERROR,
|
|
Packit Service |
dff8e4 |
G_FILE_ERROR_NOENT,
|
|
Packit Service |
dff8e4 |
_("missing plugin file \"%s\""),
|
|
Packit Service |
dff8e4 |
module_path);
|
|
Packit Service |
dff8e4 |
return NULL;
|
|
Packit Service |
dff8e4 |
}
|
|
Packit Service |
dff8e4 |
g_set_error(error,
|
|
Packit Service |
dff8e4 |
NM_VPN_PLUGIN_ERROR,
|
|
Packit Service |
dff8e4 |
NM_VPN_PLUGIN_ERROR_FAILED,
|
|
Packit Service |
dff8e4 |
_("cannot load editor plugin: %s"),
|
|
Packit Service |
dff8e4 |
dlerror());
|
|
Packit Service |
dff8e4 |
return NULL;
|
|
Packit Service |
dff8e4 |
}
|
|
Packit Service |
dff8e4 |
|
|
Packit Service |
dff8e4 |
factory = dlsym(dl_module, factory_name);
|
|
Packit Service |
dff8e4 |
if (!factory) {
|
|
Packit Service |
dff8e4 |
g_set_error(error,
|
|
Packit Service |
dff8e4 |
NM_VPN_PLUGIN_ERROR,
|
|
Packit Service |
dff8e4 |
NM_VPN_PLUGIN_ERROR_FAILED,
|
|
Packit Service |
dff8e4 |
_("cannot load factory %s from plugin: %s"),
|
|
Packit Service |
dff8e4 |
factory_name,
|
|
Packit Service |
dff8e4 |
dlerror());
|
|
Packit Service |
dff8e4 |
dlclose(dl_module);
|
|
Packit Service |
dff8e4 |
return NULL;
|
|
Packit Service |
dff8e4 |
}
|
|
Packit Service |
dff8e4 |
|
|
Packit Service |
dff8e4 |
/* we cannot ever unload the module because it creates glib types, which
|
|
Packit Service |
dff8e4 |
* cannot be unregistered.
|
|
Packit Service |
dff8e4 |
*
|
|
Packit Service |
dff8e4 |
* Thus we just leak the dl_module handle indefinitely. */
|
|
Packit Service |
dff8e4 |
cached.factory = factory;
|
|
Packit Service |
dff8e4 |
cached.dl_module = dl_module;
|
|
Packit Service |
dff8e4 |
cached.module_name = g_strdup(module_name);
|
|
Packit Service |
dff8e4 |
cached.factory_name = g_strdup(factory_name);
|
|
Packit Service |
dff8e4 |
}
|
|
Packit Service |
dff8e4 |
|
|
Packit Service |
dff8e4 |
editor = editor_factory(cached.factory, editor_plugin, connection, user_data, error);
|
|
Packit Service |
dff8e4 |
if (!editor) {
|
|
Packit Service |
dff8e4 |
if (error && !*error) {
|
|
Packit Service |
dff8e4 |
g_set_error_literal(error,
|
|
Packit Service |
dff8e4 |
NM_VPN_PLUGIN_ERROR,
|
|
Packit Service |
dff8e4 |
NM_VPN_PLUGIN_ERROR_FAILED,
|
|
Packit Service |
dff8e4 |
_("unknown error creating editor instance"));
|
|
Packit Service |
dff8e4 |
g_return_val_if_reached(NULL);
|
|
Packit Service |
dff8e4 |
}
|
|
Packit Service |
dff8e4 |
return NULL;
|
|
Packit Service |
dff8e4 |
}
|
|
Packit Service |
dff8e4 |
|
|
Packit Service |
dff8e4 |
g_return_val_if_fail(NM_IS_VPN_EDITOR(editor), NULL);
|
|
Packit Service |
dff8e4 |
return editor;
|
|
Packit Service |
dff8e4 |
}
|