diff options
Diffstat (limited to 'src/contrib')
| -rw-r--r-- | src/contrib/meson.build | 5 | ||||
| -rw-r--r-- | src/contrib/nm-compat.c | 76 | ||||
| -rw-r--r-- | src/contrib/nm-compat.h | 55 | ||||
| -rw-r--r-- | src/contrib/nm-vpn-editor-plugin-call.h | 167 | ||||
| -rw-r--r-- | src/contrib/nm-vpn-plugin-utils.c | 138 | ||||
| -rw-r--r-- | src/contrib/nm-vpn-plugin-utils.h | 25 | ||||
| -rw-r--r-- | src/contrib/tests/meson.build | 11 |
7 files changed, 477 insertions, 0 deletions
diff --git a/src/contrib/meson.build b/src/contrib/meson.build new file mode 100644 index 00000000..2745087a --- /dev/null +++ b/src/contrib/meson.build @@ -0,0 +1,5 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later + +nm_vpn_plugin_utils_source = files('nm-vpn-plugin-utils.c') + +src_contrib_nm_compat_source = files('nm-compat.c') diff --git a/src/contrib/nm-compat.c b/src/contrib/nm-compat.c new file mode 100644 index 00000000..f15c29c9 --- /dev/null +++ b/src/contrib/nm-compat.c @@ -0,0 +1,76 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2017 Red Hat, Inc. + */ + +#include "libnm-client-impl/nm-default-libnm.h" + +#include "nm-compat.h" + +/*****************************************************************************/ + +static void +_get_keys_cb(const char *key, const char *val, gpointer user_data) +{ + GPtrArray *a = user_data; + + g_ptr_array_add(a, g_strdup(key)); +} + +static const char ** +_get_keys(NMSettingVpn *setting, gboolean is_secrets, guint *out_length) +{ + guint len; + const char **keys = NULL; + GPtrArray * a; + + nm_assert(NM_IS_SETTING_VPN(setting)); + + if (is_secrets) + len = nm_setting_vpn_get_num_secrets(setting); + else + len = nm_setting_vpn_get_num_data_items(setting); + + a = g_ptr_array_sized_new(len + 1); + + if (is_secrets) + nm_setting_vpn_foreach_secret(setting, _get_keys_cb, a); + else + nm_setting_vpn_foreach_data_item(setting, _get_keys_cb, a); + + len = a->len; + if (len) { + g_ptr_array_sort(a, nm_strcmp_p); + g_ptr_array_add(a, NULL); + keys = g_malloc(a->len * sizeof(gpointer)); + memcpy(keys, a->pdata, a->len * sizeof(gpointer)); + + /* we need to cache the keys *somewhere*. */ + g_object_set_qdata_full(G_OBJECT(setting), + is_secrets + ? NM_CACHED_QUARK("libnm._nm_setting_vpn_get_secret_keys") + : NM_CACHED_QUARK("libnm._nm_setting_vpn_get_data_keys"), + g_ptr_array_free(a, FALSE), + (GDestroyNotify) g_strfreev); + } else + g_ptr_array_free(a, TRUE); + + NM_SET_OUT(out_length, len); + return keys; +} + +const char ** +_nm_setting_vpn_get_data_keys(NMSettingVpn *setting, guint *out_length) +{ + g_return_val_if_fail(NM_IS_SETTING_VPN(setting), NULL); + + return _get_keys(setting, FALSE, out_length); +} + +const char ** +_nm_setting_vpn_get_secret_keys(NMSettingVpn *setting, guint *out_length) +{ + g_return_val_if_fail(NM_IS_SETTING_VPN(setting), NULL); + + return _get_keys(setting, TRUE, out_length); +} diff --git a/src/contrib/nm-compat.h b/src/contrib/nm-compat.h new file mode 100644 index 00000000..33eaec12 --- /dev/null +++ b/src/contrib/nm-compat.h @@ -0,0 +1,55 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2017 Red Hat, Inc. + */ + +#ifndef __NM_COMPAT_H__ +#define __NM_COMPAT_H__ + +#include "nm-setting-vpn.h" + +/*****************************************************************************/ + +const char **_nm_setting_vpn_get_data_keys(NMSettingVpn *setting, guint *out_length); + +const char **_nm_setting_vpn_get_secret_keys(NMSettingVpn *setting, guint *out_length); + +#if NM_CHECK_VERSION(1, 11, 0) + #define nm_setting_vpn_get_data_keys(setting, out_length) \ + ({ \ + G_GNUC_BEGIN_IGNORE_DEPRECATIONS \ + nm_setting_vpn_get_data_keys(setting, out_length); \ + G_GNUC_END_IGNORE_DEPRECATIONS \ + }) + #define nm_setting_vpn_get_secret_keys(setting, out_length) \ + ({ \ + G_GNUC_BEGIN_IGNORE_DEPRECATIONS \ + nm_setting_vpn_get_secret_keys(setting, out_length); \ + G_GNUC_END_IGNORE_DEPRECATIONS \ + }) +#else + #define nm_setting_vpn_get_data_keys(setting, out_length) \ + _nm_setting_vpn_get_data_keys(setting, out_length) + #define nm_setting_vpn_get_secret_keys(setting, out_length) \ + _nm_setting_vpn_get_secret_keys(setting, out_length) +#endif + +/*****************************************************************************/ + +/* possibly missing defines from newer libnm API. */ + +#ifndef NM_VPN_PLUGIN_CONFIG_PROXY_PAC + #define NM_VPN_PLUGIN_CONFIG_PROXY_PAC "pac" +#endif + +#ifndef NM_VPN_PLUGIN_IP4_CONFIG_PRESERVE_ROUTES + #define NM_VPN_PLUGIN_IP4_CONFIG_PRESERVE_ROUTES "preserve-routes" +#endif + +#ifndef NM_VPN_PLUGIN_IP6_CONFIG_PRESERVE_ROUTES + #define NM_VPN_PLUGIN_IP6_CONFIG_PRESERVE_ROUTES "preserve-routes" +#endif + +/*****************************************************************************/ + +#endif /* __NM_COMPAT_H__ */ diff --git a/src/contrib/nm-vpn-editor-plugin-call.h b/src/contrib/nm-vpn-editor-plugin-call.h new file mode 100644 index 00000000..be0a6772 --- /dev/null +++ b/src/contrib/nm-vpn-editor-plugin-call.h @@ -0,0 +1,167 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2016 Red Hat, Inc. + */ + +#ifndef __NM_VPN_EDITOR_PLUGIN_CALL_H__ +#define __NM_VPN_EDITOR_PLUGIN_CALL_H__ + +/* This header is an internal, header-only file that can be copied to + * other projects to call well-known service functions on VPN plugins. + * + * This uses the NMVpnEditorPluginVT and allows a user (nm-applet) + * to directly communicate with a VPN plugin using API that is newer + * then the current libnm version. That is, it allows to call to a VPN + * plugin bypassing libnm. */ + +#include <NetworkManager.h> + +/* we make use of other internal header files, you need those too. */ +#include "libnm-glib-aux/nm-macros-internal.h" + +/*****************************************************************************/ + +/** + * NMVpnEditorPluginServiceFlags: + * @NM_VPN_EDITOR_PLUGIN_SERVICE_FLAGS_NONE: no flags + * @NM_VPN_EDITOR_PLUGIN_SERVICE_FLAGS_CAN_ADD: whether the plugin can + * add a new connection for the given service-type. + **/ +typedef enum { /*< skip >*/ + NM_VPN_EDITOR_PLUGIN_SERVICE_FLAGS_NONE = 0x00, + NM_VPN_EDITOR_PLUGIN_SERVICE_FLAGS_CAN_ADD = 0x01, +} NMVpnEditorPluginServiceFlags; + +struct _NMVpnEditorPluginVT { + gboolean (*fcn_get_service_info)(NMVpnEditorPlugin * plugin, + const char * service_type, + char ** out_short_name, + char ** out_pretty_name, + char ** out_description, + NMVpnEditorPluginServiceFlags *out_flags); + char **(*fcn_get_service_add_details)(NMVpnEditorPlugin *plugin, const char *service_name); + gboolean (*fcn_get_service_add_detail)(NMVpnEditorPlugin *plugin, + const char * service_type, + const char * add_detail, + char ** out_pretty_name, + char ** out_description, + char ** out_add_detail_key, + char ** out_add_detail_val, + guint * out_flags); +}; + +/***************************************************************************** + * Call + * + * The following wrap the calling of generic functions for a VPN plugin. + * They are used by callers (for example nm-connection-editor). + *****************************************************************************/ + +static inline gboolean +nm_vpn_editor_plugin_get_service_info(NMVpnEditorPlugin * plugin, + const char * service_type, + char ** out_short_name, + char ** out_pretty_name, + char ** out_description, + NMVpnEditorPluginServiceFlags *out_flags) +{ + NMVpnEditorPluginVT vt; + gs_free char * short_name_local = NULL; + gs_free char * pretty_name_local = NULL; + gs_free char * description_local = NULL; + guint flags_local = 0; + + g_return_val_if_fail(NM_IS_VPN_EDITOR_PLUGIN(plugin), FALSE); + g_return_val_if_fail(service_type, FALSE); + + nm_vpn_editor_plugin_get_vt(plugin, &vt, sizeof(vt)); + if (!vt.fcn_get_service_info + || !vt.fcn_get_service_info(plugin, + service_type, + out_short_name ? &short_name_local : NULL, + out_pretty_name ? &pretty_name_local : NULL, + out_description ? &description_local : NULL, + out_flags ? &flags_local : NULL)) + return FALSE; + NM_SET_OUT(out_short_name, g_steal_pointer(&short_name_local)); + NM_SET_OUT(out_pretty_name, g_steal_pointer(&pretty_name_local)); + NM_SET_OUT(out_description, g_steal_pointer(&description_local)); + NM_SET_OUT(out_flags, flags_local); + return TRUE; +} + +static inline char ** +nm_vpn_editor_plugin_get_service_add_details(NMVpnEditorPlugin *plugin, const char *service_name) +{ + NMVpnEditorPluginVT vt; + char ** details = NULL; + + g_return_val_if_fail(NM_IS_VPN_EDITOR_PLUGIN(plugin), NULL); + g_return_val_if_fail(service_name, NULL); + + nm_vpn_editor_plugin_get_vt(plugin, &vt, sizeof(vt)); + if (vt.fcn_get_service_add_details) + details = vt.fcn_get_service_add_details(plugin, service_name); + if (!details) + return g_new0(char *, 1); + return details; +} + +static inline gboolean +nm_vpn_editor_plugin_get_service_add_detail(NMVpnEditorPlugin *plugin, + const char * service_type, + const char * add_detail, + char ** out_pretty_name, + char ** out_description, + char ** out_add_detail_key, + char ** out_add_detail_val, + guint * out_flags) +{ + NMVpnEditorPluginVT vt; + gs_free char * pretty_name_local = NULL; + gs_free char * description_local = NULL; + gs_free char * add_detail_key_local = NULL; + gs_free char * add_detail_val_local = NULL; + guint flags_local = 0; + + g_return_val_if_fail(NM_IS_VPN_EDITOR_PLUGIN(plugin), FALSE); + g_return_val_if_fail(service_type, FALSE); + g_return_val_if_fail(add_detail, FALSE); + + nm_vpn_editor_plugin_get_vt(plugin, &vt, sizeof(vt)); + if (!vt.fcn_get_service_add_detail + || !vt.fcn_get_service_add_detail(plugin, + service_type, + add_detail, + out_pretty_name ? &pretty_name_local : NULL, + out_description ? &description_local : NULL, + out_add_detail_key ? &add_detail_key_local : NULL, + out_add_detail_val ? &add_detail_val_local : NULL, + out_flags ? &flags_local : NULL)) + return FALSE; + NM_SET_OUT(out_pretty_name, g_steal_pointer(&pretty_name_local)); + NM_SET_OUT(out_description, g_steal_pointer(&description_local)); + NM_SET_OUT(out_add_detail_key, g_steal_pointer(&add_detail_key_local)); + NM_SET_OUT(out_add_detail_val, g_steal_pointer(&add_detail_val_local)); + NM_SET_OUT(out_flags, flags_local); + return TRUE; +} + +/***************************************************************************** + * Implementation + * + * The following glue code can be used to implement calls in a VPN plugin. + *****************************************************************************/ + +#define NM_VPN_EDITOR_PLUGIN_VT_DEFINE(vt_name, get_vt, ...) \ + static const NMVpnEditorPluginVT vt_name = {__VA_ARGS__}; \ + static const NMVpnEditorPluginVT *get_vt(NMVpnEditorPlugin *plugin, gsize *out_vt_size) \ + { \ + nm_assert(NM_IS_VPN_EDITOR_PLUGIN(plugin)); \ + nm_assert(out_vt_size); \ + \ + *out_vt_size = sizeof(vt_name); \ + return &vt_name; \ + } + +#endif /* __NM_VPN_EDITOR_PLUGIN_CALL_H__ */ diff --git a/src/contrib/nm-vpn-plugin-utils.c b/src/contrib/nm-vpn-plugin-utils.c new file mode 100644 index 00000000..b10695b7 --- /dev/null +++ b/src/contrib/nm-vpn-plugin-utils.c @@ -0,0 +1,138 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2016, 2018 Red Hat, Inc. + */ + +#include "libnm-client-aux-extern/nm-default-client.h" + +#include "nm-vpn-plugin-utils.h" + +#include <dlfcn.h> + +/*****************************************************************************/ + +NMVpnEditor * +nm_vpn_plugin_utils_load_editor(const char * module_name, + const char * factory_name, + NMVpnPluginUtilsEditorFactory editor_factory, + NMVpnEditorPlugin * editor_plugin, + NMConnection * connection, + gpointer user_data, + GError ** error) + +{ + static struct { + gpointer factory; + void * dl_module; + char * module_name; + char * factory_name; + } cached = {0}; + NMVpnEditor * editor; + gs_free char *module_path = NULL; + gs_free char *dirname = NULL; + Dl_info plugin_info; + + g_return_val_if_fail(module_name, NULL); + g_return_val_if_fail(factory_name && factory_name[0], NULL); + g_return_val_if_fail(editor_factory, NULL); + g_return_val_if_fail(NM_IS_VPN_EDITOR_PLUGIN(editor_plugin), NULL); + g_return_val_if_fail(NM_IS_CONNECTION(connection), NULL); + g_return_val_if_fail(!error || !*error, NULL); + + if (!g_path_is_absolute(module_name)) { + /* + * Load an editor from the same directory this plugin is in. + * Ideally, we'd get our .so name from the NMVpnEditorPlugin if it + * would just have a property with it... + */ + if (!dladdr(nm_vpn_plugin_utils_load_editor, &plugin_info)) { + /* Really a "can not happen" scenario. */ + g_set_error(error, + NM_VPN_PLUGIN_ERROR, + NM_VPN_PLUGIN_ERROR_FAILED, + _("unable to get editor plugin name: %s"), + dlerror()); + } + + dirname = g_path_get_dirname(plugin_info.dli_fname); + module_path = g_build_filename(dirname, module_name, NULL); + } else { + module_path = g_strdup(module_name); + } + + /* we really expect this function to be called with unchanging @module_name + * and @factory_name. And we only want to load the module once, hence it would + * be more complicated to accept changing @module_name/@factory_name arguments. + * + * The reason for only loading once is that due to glib types, we cannot create a + * certain type-name more then once, so loading the same module or another version + * of the same module will fail horribly as both try to create a GType with the same + * name. + * + * Only support loading once, any future calls will reuse the handle. To simplify + * that, we enforce that the @factory_name and @module_name is the same. */ + if (cached.factory) { + g_return_val_if_fail(cached.dl_module, NULL); + g_return_val_if_fail(cached.factory_name && nm_streq0(cached.factory_name, factory_name), + NULL); + g_return_val_if_fail(cached.module_name && nm_streq0(cached.module_name, module_name), + NULL); + } else { + gpointer factory; + void * dl_module; + + dl_module = dlopen(module_path, RTLD_LAZY | RTLD_LOCAL); + if (!dl_module) { + if (!g_file_test(module_path, G_FILE_TEST_EXISTS)) { + g_set_error(error, + G_FILE_ERROR, + G_FILE_ERROR_NOENT, + _("missing plugin file \"%s\""), + module_path); + return NULL; + } + g_set_error(error, + NM_VPN_PLUGIN_ERROR, + NM_VPN_PLUGIN_ERROR_FAILED, + _("cannot load editor plugin: %s"), + dlerror()); + return NULL; + } + + factory = dlsym(dl_module, factory_name); + if (!factory) { + g_set_error(error, + NM_VPN_PLUGIN_ERROR, + NM_VPN_PLUGIN_ERROR_FAILED, + _("cannot load factory %s from plugin: %s"), + factory_name, + dlerror()); + dlclose(dl_module); + return NULL; + } + + /* we cannot ever unload the module because it creates glib types, which + * cannot be unregistered. + * + * Thus we just leak the dl_module handle indefinitely. */ + cached.factory = factory; + cached.dl_module = dl_module; + cached.module_name = g_strdup(module_name); + cached.factory_name = g_strdup(factory_name); + } + + editor = editor_factory(cached.factory, editor_plugin, connection, user_data, error); + if (!editor) { + if (error && !*error) { + g_set_error_literal(error, + NM_VPN_PLUGIN_ERROR, + NM_VPN_PLUGIN_ERROR_FAILED, + _("unknown error creating editor instance")); + g_return_val_if_reached(NULL); + } + return NULL; + } + + g_return_val_if_fail(NM_IS_VPN_EDITOR(editor), NULL); + return editor; +} diff --git a/src/contrib/nm-vpn-plugin-utils.h b/src/contrib/nm-vpn-plugin-utils.h new file mode 100644 index 00000000..288143ef --- /dev/null +++ b/src/contrib/nm-vpn-plugin-utils.h @@ -0,0 +1,25 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2016 Red Hat, Inc. + */ + +#ifndef __NM_VPN_PLUGIN_UTILS_H__ +#define __NM_VPN_PLUGIN_UTILS_H__ + +#include <NetworkManager.h> + +typedef NMVpnEditor *(NMVpnPluginUtilsEditorFactory) (gpointer factory, + NMVpnEditorPlugin *editor_plugin, + NMConnection * connection, + gpointer user_data, + GError ** error); + +NMVpnEditor *nm_vpn_plugin_utils_load_editor(const char * module_name, + const char * factory_name, + NMVpnPluginUtilsEditorFactory editor_factory, + NMVpnEditorPlugin * editor_plugin, + NMConnection * connection, + gpointer user_data, + GError ** error); + +#endif /* __NM_VPN_PLUGIN_UTILS_H__ */ diff --git a/src/contrib/tests/meson.build b/src/contrib/tests/meson.build new file mode 100644 index 00000000..b9b22ca8 --- /dev/null +++ b/src/contrib/tests/meson.build @@ -0,0 +1,11 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later + +# just test, that we can build "nm-vpn-plugin-utils.c" +static_library( + 'nm-vpn-plugin-utils-test', + sources: nm_vpn_plugin_utils_source, + dependencies: [ + libnm_dep, + glib_dep, + ], +) |