From e108f1cfafb2d3de1849b82b04e44c4f1251395e Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Tue, 7 Jun 2016 12:58:21 +0200 Subject: shared: add nm_strcmp_p() utils macro --- shared/nm-macros-internal.h | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/shared/nm-macros-internal.h b/shared/nm-macros-internal.h index 04e6a1ab6c..4622af9fd8 100644 --- a/shared/nm-macros-internal.h +++ b/shared/nm-macros-internal.h @@ -484,6 +484,36 @@ nm_strstrip (char *str) return str ? g_strstrip (str) : NULL; } +/* g_ptr_array_sort()'s compare function takes pointers to the + * value. Thus, you cannot use strcmp directly. You can use + * nm_strcmp_p(). + * + * Like strcmp(), this function is not forgiving to accept %NULL. */ +static inline int +nm_strcmp_p (gconstpointer a, gconstpointer b) +{ + const char *s1 = *((const char **) a); + const char *s2 = *((const char **) b); + + return strcmp (s1, s2); +} + +/* like nm_strcmp_p(), suitable for g_ptr_array_sort_with_data(). + * g_ptr_array_sort() just casts nm_strcmp_p() to a function of different + * signature. I guess, in glib there are knowledgeable people that ensure + * that this additional argument doesn't cause problems due to different ABI + * for every architecture that glib supports. + * For NetworkManager, we'd rather avoid such stunts. + **/ +static inline int +nm_strcmp_p_with_data (gconstpointer a, gconstpointer b, gpointer user_data) +{ + const char *s1 = *((const char **) a); + const char *s2 = *((const char **) b); + + return strcmp (s1, s2); +} + /*****************************************************************************/ static inline guint -- cgit v1.2.1 From c629378d2e91cbb49ad5c64925ef987e1ddd6593 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Tue, 7 Jun 2016 13:10:38 +0200 Subject: clients: fix potential crash due to wrong sort function g_qsort_with_data() passes the pointers to the compared items to the compare function, that is not the "const char *" pointers itself. Fixes: 41976e30690d36cc3998c5025ac70c8cbaa8f897 --- clients/common/nm-vpn-helpers.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/clients/common/nm-vpn-helpers.c b/clients/common/nm-vpn-helpers.c index 43ac4b2e61..eb0f3af131 100644 --- a/clients/common/nm-vpn-helpers.c +++ b/clients/common/nm-vpn-helpers.c @@ -95,12 +95,6 @@ nm_vpn_get_plugins (void) return plugins; } -static int -_strcmp_data (gconstpointer a, gconstpointer b, gpointer unused) -{ - return strcmp (a, b); -} - const char ** nm_vpn_get_plugin_names (gboolean only_available_plugins) { @@ -132,7 +126,7 @@ nm_vpn_get_plugin_names (gboolean only_available_plugins) list[i++] = known_names[j]; } - g_qsort_with_data (list, i, sizeof (gpointer), _strcmp_data, NULL); + g_qsort_with_data (list, i, sizeof (gpointer), nm_strcmp_p_with_data, NULL); /* remove duplicates */ for (k = 0, j = 1; j < i; j++) { -- cgit v1.2.1 From d37945b06426e782c09483b69bb03c7bbdd37e07 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Tue, 7 Jun 2016 13:05:26 +0200 Subject: all: use nm_strcmp_p() for sorting strings --- libnm-core/nm-keyfile-writer.c | 8 +------- src/nm-config.c | 11 +---------- 2 files changed, 2 insertions(+), 17 deletions(-) diff --git a/libnm-core/nm-keyfile-writer.c b/libnm-core/nm-keyfile-writer.c index f62e97c3e6..e0e6ba4bf8 100644 --- a/libnm-core/nm-keyfile-writer.c +++ b/libnm-core/nm-keyfile-writer.c @@ -233,12 +233,6 @@ route_writer (KeyfileWriterInfo *info, write_ip_values (info->keyfile, setting_name, array, NULL, TRUE); } -static int -sort_hash_keys (gconstpointer a, gconstpointer b, gpointer user_data) -{ - return g_strcmp0 (*((const char **) a), *((const char **) b)); -} - static void write_hash_of_string (GKeyFile *file, NMSetting *setting, @@ -262,7 +256,7 @@ write_hash_of_string (GKeyFile *file, if (!keys) return; - g_qsort_with_data (keys, l, sizeof (const char *), sort_hash_keys, NULL); + g_qsort_with_data (keys, l, sizeof (const char *), nm_strcmp_p_with_data, NULL); for (i = 0; keys[i]; i++) { const char *property, *data; diff --git a/src/nm-config.c b/src/nm-config.c index 67c0497810..4d0ebc1f8f 100644 --- a/src/nm-config.c +++ b/src/nm-config.c @@ -880,15 +880,6 @@ read_base_config (GKeyFile *keyfile, return TRUE; } -static int -sort_asciibetically (gconstpointer a, gconstpointer b) -{ - const char *s1 = *(const char **)a; - const char *s2 = *(const char **)b; - - return strcmp (s1, s2); -} - static GPtrArray * _get_config_dir_files (const char *config_dir) { @@ -917,7 +908,7 @@ _get_config_dir_files (const char *config_dir) } g_object_unref (dir); - g_ptr_array_sort (confs, sort_asciibetically); + g_ptr_array_sort (confs, nm_strcmp_p); return confs; } -- cgit v1.2.1 From 10445bedb83fafbeed76ef25c45774f1c0a1ea94 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Fri, 3 Jun 2016 16:43:26 +0200 Subject: libnm/vpn: add nm_vpn_plugin_info_get_aliases --- libnm-core/nm-vpn-plugin-info.c | 30 ++++++++++++++++++++++++++++-- libnm-core/nm-vpn-plugin-info.h | 2 ++ libnm/libnm.ver | 1 + 3 files changed, 31 insertions(+), 2 deletions(-) diff --git a/libnm-core/nm-vpn-plugin-info.c b/libnm-core/nm-vpn-plugin-info.c index 11393b54f1..d5e3dc0ba9 100644 --- a/libnm-core/nm-vpn-plugin-info.c +++ b/libnm-core/nm-vpn-plugin-info.c @@ -600,8 +600,6 @@ nm_vpn_plugin_info_list_find_by_service (GSList *list, const char *service) for (iter = list; iter; iter = iter->next) { char **aliases = (NM_VPN_PLUGIN_INFO_GET_PRIVATE (iter->data))->aliases; - if (!aliases) - continue; if (_nm_utils_strv_find_first (aliases, -1, service) >= 0) return iter->data; } @@ -767,6 +765,32 @@ nm_vpn_plugin_info_supports_multiple (NMVpnPluginInfo *self) } +/** + * nm_vpn_plugin_info_get_aliases: + * @self: plugin info instance + * + * Returns: (array zero-terminated=1) (element-type utf8) (transfer none): + * the aliases from the name-file. + * + * Since: 1.4 + */ +const char *const* +nm_vpn_plugin_info_get_aliases (NMVpnPluginInfo *self) +{ + NMVpnPluginInfoPrivate *priv; + + g_return_val_if_fail (NM_IS_VPN_PLUGIN_INFO (self), NULL); + + priv = NM_VPN_PLUGIN_INFO_GET_PRIVATE (self); + if (priv->aliases) + return (const char *const*) priv->aliases; + + /* For convenience, we always want to return non-NULL, even for empty + * aliases. Hack around that, by making a NULL terminated array using + * the NULL of priv->aliases. */ + return (const char *const*) &priv->aliases; +} + /** * nm_vpn_plugin_info_lookup_property: * @self: plugin info instance @@ -1003,6 +1027,8 @@ init_sync (GInitable *initable, GCancellable *cancellable, GError **error) } priv->aliases = g_key_file_get_string_list (priv->keyfile, NM_VPN_PLUGIN_INFO_KF_GROUP_CONNECTION, "aliases", NULL, NULL); + if (priv->aliases && !priv->aliases[0]) + g_clear_pointer (&priv->aliases, g_free); priv->keys = g_hash_table_new_full (_nm_utils_strstrdictkey_hash, _nm_utils_strstrdictkey_equal, diff --git a/libnm-core/nm-vpn-plugin-info.h b/libnm-core/nm-vpn-plugin-info.h index b9ed83f1dc..f1bba28197 100644 --- a/libnm-core/nm-vpn-plugin-info.h +++ b/libnm-core/nm-vpn-plugin-info.h @@ -93,6 +93,8 @@ NM_AVAILABLE_IN_1_4 gboolean nm_vpn_plugin_info_supports_hints (NMVpnPluginInfo *self); NM_AVAILABLE_IN_1_2 gboolean nm_vpn_plugin_info_supports_multiple (NMVpnPluginInfo *self); +NM_AVAILABLE_IN_1_4 +const char *const*nm_vpn_plugin_info_get_aliases (NMVpnPluginInfo *self); NM_AVAILABLE_IN_1_2 const char *nm_vpn_plugin_info_lookup_property (NMVpnPluginInfo *self, const char *group, const char *key); diff --git a/libnm/libnm.ver b/libnm/libnm.ver index 4bdef30186..4064522e09 100644 --- a/libnm/libnm.ver +++ b/libnm/libnm.ver @@ -1069,6 +1069,7 @@ global: nm_setting_ip6_config_get_token; nm_setting_ip_config_get_dns_priority; nm_vpn_editor_plugin_load; + nm_vpn_plugin_info_get_aliases; nm_vpn_plugin_info_get_auth_dialog; nm_vpn_plugin_info_get_service; nm_vpn_plugin_info_new_search_file; -- cgit v1.2.1 From 971a8181f68596d04ef9f81d2c3ad32d73f56de8 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Mon, 6 Jun 2016 10:03:19 +0200 Subject: nmcli: fix VPN export to find plugin by service-type --- clients/cli/connections.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clients/cli/connections.c b/clients/cli/connections.c index 0c61667d92..5b0261c4d3 100644 --- a/clients/cli/connections.c +++ b/clients/cli/connections.c @@ -10790,7 +10790,7 @@ do_connection_export (NmCli *nmc, int argc, char **argv) type = nm_setting_vpn_get_service_type (nm_connection_get_setting_vpn (connection)); /* Export VPN configuration */ - plugin = nm_vpn_lookup_plugin (type, NULL, &error); + plugin = nm_vpn_lookup_plugin (NULL, type, &error); if (!plugin) { g_string_printf (nmc->return_text, _("Error: failed to load VPN plugin: %s."), error->message); -- cgit v1.2.1 From a2b34f7f126491215ef9de9c78289c0287ade05b Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Mon, 6 Jun 2016 10:21:18 +0200 Subject: clients/trivial: rename nm_vpn_get_plugins() to nm_vpn_get_plugin_infos() --- clients/common/nm-vpn-helpers.c | 8 ++++---- clients/common/nm-vpn-helpers.h | 2 +- clients/tui/nm-editor-utils.c | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/clients/common/nm-vpn-helpers.c b/clients/common/nm-vpn-helpers.c index eb0f3af131..4eaa92d9f8 100644 --- a/clients/common/nm-vpn-helpers.c +++ b/clients/common/nm-vpn-helpers.c @@ -45,7 +45,7 @@ nm_vpn_lookup_plugin (const char *name, const char *service, GError **error) g_return_val_if_fail (error == NULL || *error == NULL, NULL); if (G_UNLIKELY (!plugins_loaded)) - nm_vpn_get_plugins (); + nm_vpn_get_plugin_infos (); if (service) plugin_info = nm_vpn_plugin_info_list_find_by_service (plugins, service); @@ -86,7 +86,7 @@ nm_vpn_lookup_plugin (const char *name, const char *service, GError **error) } GSList * -nm_vpn_get_plugins (void) +nm_vpn_get_plugin_infos (void) { if (G_LIKELY (plugins_loaded)) return plugins; @@ -115,7 +115,7 @@ nm_vpn_get_plugin_names (gboolean only_available_plugins) }; guint i, j, k; - p = nm_vpn_get_plugins (); + p = nm_vpn_get_plugin_infos (); list = g_new0 (const char *, g_slist_length (p) + G_N_ELEMENTS (known_names) + 1); i = 0; @@ -146,7 +146,7 @@ nm_vpn_get_service_for_name (const char *name) g_return_val_if_fail (name, NULL); - plugin_info = nm_vpn_plugin_info_list_find_by_name (nm_vpn_get_plugins (), name); + plugin_info = nm_vpn_plugin_info_list_find_by_name (nm_vpn_get_plugin_infos (), name); if (plugin_info) { /* this only means we have a .name file (NMVpnPluginInfo). Possibly the * NMVpnEditorPlugin is not loadable. */ diff --git a/clients/common/nm-vpn-helpers.h b/clients/common/nm-vpn-helpers.h index 79fc94e957..3d192f7fc8 100644 --- a/clients/common/nm-vpn-helpers.h +++ b/clients/common/nm-vpn-helpers.h @@ -28,7 +28,7 @@ struct { const char *ui_name; } typedef VpnPasswordName; -GSList *nm_vpn_get_plugins (void); +GSList *nm_vpn_get_plugin_infos (void); const char **nm_vpn_get_plugin_names (gboolean only_available_plugins); diff --git a/clients/tui/nm-editor-utils.c b/clients/tui/nm-editor-utils.c index 25a64c2eed..9e6263e633 100644 --- a/clients/tui/nm-editor-utils.c +++ b/clients/tui/nm-editor-utils.c @@ -223,7 +223,7 @@ nm_editor_utils_get_connection_type_list (void) #if 0 /* Add "VPN" only if there are plugins */ - vpn_plugins_hash = nm_vpn_get_plugins (); + vpn_plugins_hash = nm_vpn_get_plugin_infos (); have_vpn_plugins = vpn_plugins_hash && g_hash_table_size (vpn_plugins_hash); if (have_vpn_plugins) { GHashTableIter iter; -- cgit v1.2.1 From 57783347bd23d88d7313a36523ddd5c9ba13375d Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Tue, 7 Jun 2016 11:10:23 +0200 Subject: libnm/vpn: refactor nm_vpn_plugin_info_list_find_by_service() and prefer aliases first Refactor code to add function _list_find_by_service(), which will be used in the next commit. A notable change is that we now search also through the aliases together with the service-name. That makes a difference, if one plugin privdes an "alias" which another plugin provides as "service". Due to that change, we would also find the aliased plugin first. In practice it shouldn't matter, because different plugins are not supposed to provide identical services. --- libnm-core/nm-vpn-plugin-info.c | 34 ++++++++++++++++------------------ 1 file changed, 16 insertions(+), 18 deletions(-) diff --git a/libnm-core/nm-vpn-plugin-info.c b/libnm-core/nm-vpn-plugin-info.c index d5e3dc0ba9..2f03431b07 100644 --- a/libnm-core/nm-vpn-plugin-info.c +++ b/libnm-core/nm-vpn-plugin-info.c @@ -573,10 +573,24 @@ nm_vpn_plugin_info_list_find_by_filename (GSList *list, const char *filename) return NULL; } +static NMVpnPluginInfo * +_list_find_by_service (GSList *list, const char *service) +{ + for (; list; list = list->next) { + NMVpnPluginInfoPrivate *priv = NM_VPN_PLUGIN_INFO_GET_PRIVATE (list->data); + + if ( nm_streq (priv->service, service) + || _nm_utils_strv_find_first (priv->aliases, -1, service) >= 0) + return list->data; + } + return NULL; +} + /** * nm_vpn_plugin_info_list_find_by_service: * @list: (element-type NMVpnPluginInfo): list of plugins - * @service: service to search + * @service: service to search. This can be the main service-type + * or one of the provided aliases. * * Returns: (transfer none): the first plugin with a matching @service (or %NULL). * @@ -585,25 +599,9 @@ nm_vpn_plugin_info_list_find_by_filename (GSList *list, const char *filename) NMVpnPluginInfo * nm_vpn_plugin_info_list_find_by_service (GSList *list, const char *service) { - GSList *iter; - if (!service) g_return_val_if_reached (NULL); - - /* First, consider the primary service name. */ - for (iter = list; iter; iter = iter->next) { - if (strcmp (nm_vpn_plugin_info_get_service (iter->data), service) == 0) - return iter->data; - } - - /* Then look into the aliases. */ - for (iter = list; iter; iter = iter->next) { - char **aliases = (NM_VPN_PLUGIN_INFO_GET_PRIVATE (iter->data))->aliases; - - if (_nm_utils_strv_find_first (aliases, -1, service) >= 0) - return iter->data; - } - return NULL; + return _list_find_by_service (list, service); } /*********************************************************************/ -- cgit v1.2.1 From 46665898bbe2c0998529cb75056d674654ba41a9 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Tue, 7 Jun 2016 11:11:30 +0200 Subject: libnm/vpn: add nm_vpn_plugin_info_list_find_service_name() function --- libnm-core/nm-vpn-plugin-info.c | 173 +++++++++++++++++++++++++++++++++++++++- libnm-core/nm-vpn-plugin-info.h | 6 ++ libnm/libnm.ver | 2 + 3 files changed, 180 insertions(+), 1 deletion(-) diff --git a/libnm-core/nm-vpn-plugin-info.c b/libnm-core/nm-vpn-plugin-info.c index 2f03431b07..94911e377b 100644 --- a/libnm-core/nm-vpn-plugin-info.c +++ b/libnm-core/nm-vpn-plugin-info.c @@ -491,7 +491,7 @@ nm_vpn_plugin_info_list_add (GSList **list, NMVpnPluginInfo *plugin_info, GError } /* the plugin must have unique values for certain properties. E.g. two different - * plugins cannot share the same service name. */ + * plugins cannot share the same service type. */ if (!_check_no_conflict (plugin_info, iter->data, error)) return FALSE; } @@ -604,6 +604,177 @@ nm_vpn_plugin_info_list_find_by_service (GSList *list, const char *service) return _list_find_by_service (list, service); } +/* known_names are well known short names for the service-type. They all implicitly + * have a prefix "org.freedesktop.NetworkManager." + known_name. */ +static const char *known_names[] = { + "openvpn", + "vpnc", + "pptp", + "openconnect", + "openswan", + "libreswan", + "strongswan", + "ssh", + "l2tp", + "iodine", + "fortisslvpn", +}; + +/** + * nm_vpn_plugin_info_list_find_service_type: + * @list: a possibly empty #GSList of #NMVpnPluginInfo instances + * @name: a name to lookup the service-type. + * + * A VPN plugin provides one or several service-types, like org.freedesktop.NetworkManager.libreswan + * Certain plugins provide more then one service type, via aliases (org.freedesktop.NetworkManager.openswan). + * This function looks up a service-type (or an alias) based on a name. + * + * Preferably, the name can be a full service-type/alias of an installed + * plugin. Otherwise, it can be the name of a VPN plugin (in which case, the + * primary, non-aliased service-type is returned). Otherwise, it can be + * one of several well known short-names (which is a hard-coded list of + * types in libnm). On success, this returns a full qualified service-type + * (or an alias). It doesn't say, that such an plugin is actually available, + * but it could be retrieved via nm_vpn_plugin_info_list_find_by_service(). + * + * Returns: (transfer-full): the resolved service-type or %NULL on failure. + * + * Since: 1.4 + */ +char * +nm_vpn_plugin_info_list_find_service_type (GSList *list, const char *name) +{ + GSList *iter; + char *n; + + if (!name) + g_return_val_if_reached (NULL); + if (!*name) + return NULL; + + /* First, try to interpret @name as a full service-type (or alias). */ + if (_list_find_by_service (list, name)) + return g_strdup (name); + + /* try to interpret @name as plugin name, in which case we return + * the main service-type (not an alias). */ + for (iter = list; iter; iter = iter->next) { + NMVpnPluginInfoPrivate *priv = NM_VPN_PLUGIN_INFO_GET_PRIVATE (iter->data); + + if (nm_streq (priv->name, name)) + return g_strdup (priv->service); + } + + /* check the hard-coded list of short-names. They all have have the same + * well-known prefix org.freedesktop.NetworkManager and the name. */ + if (_nm_utils_strv_find_first ((char **) known_names, G_N_ELEMENTS (known_names), name) >= 0) + return g_strdup_printf ("%s.%s", NM_DBUS_INTERFACE, name); + + /* try, if there exists a plugin with @name under org.freedesktop.NetworkManager. + * Allow this to be a valid abbreviation. */ + n = g_strdup_printf ("%s.%s", NM_DBUS_INTERFACE, name); + if (_list_find_by_service (list, n)) + return n; + g_free (n); + + /* currently, VPN plugins have no way to define a short-name for their + * alias name, unless the alias name is prefixed by org.freedesktop.NetworkManager. */ + + return NULL; +} + +static const char * +_service_type_get_default_abbreviation (const char *service_type) +{ + if (!g_str_has_prefix (service_type, NM_DBUS_INTERFACE)) + return NULL; + service_type += NM_STRLEN (NM_DBUS_INTERFACE); + if (service_type[0] != '.') + return NULL; + service_type++; + if (!service_type[0]) + return NULL; + return service_type; +} + +/** + * nm_vpn_plugin_info_list_get_service_types: + * @list: a possibly empty #GSList of #NMVpnPluginInfo + * @only_existing: only include results that are actually in @list. + * Otherwise, the result is extended with a hard-code list or + * well-known plugins + * @with_abbreviations: if %FALSE, only full service types are returned. + * Otherwise, this also includes abbreviated names that can be used + * with nm_vpn_plugin_info_list_find_service_type(). + * + * Returns: (transfer-full): a %NULL terminated strv list of strings. + * The list itself and the values must be freed with g_strfreev(). + * + * Since: 1.4 + */ +char ** +nm_vpn_plugin_info_list_get_service_types (GSList *list, + gboolean only_existing, + gboolean with_abbreviations) +{ + GSList *iter; + GPtrArray *l; + guint i, j; + const char *n; + + l = g_ptr_array_sized_new (20); + + for (iter = list; iter; iter = iter->next) { + NMVpnPluginInfoPrivate *priv = NM_VPN_PLUGIN_INFO_GET_PRIVATE (iter->data); + + g_ptr_array_add (l, g_strdup (priv->service)); + if (priv->aliases) { + for (i = 0; priv->aliases[i]; i++) + g_ptr_array_add (l, g_strdup (priv->aliases[i])); + } + + if (with_abbreviations) { + g_ptr_array_add (l, g_strdup (priv->name)); + n = _service_type_get_default_abbreviation (priv->service); + if (n) + g_ptr_array_add (l, g_strdup (n)); + for (i = 0; priv->aliases[i]; i++) { + n = _service_type_get_default_abbreviation (priv->aliases[i]); + if (n) + g_ptr_array_add (l, g_strdup (n)); + } + } + } + + if (!only_existing) { + for (i = 0; i < G_N_ELEMENTS (known_names); i++) { + g_ptr_array_add (l, g_strdup_printf ("%s.%s", NM_DBUS_INTERFACE, known_names[i])); + if (with_abbreviations) + g_ptr_array_add (l, g_strdup (known_names[i])); + } + } + + if (l->len <= 0) { + g_ptr_array_free (l, TRUE); + return g_new0 (char *, 1); + } + + /* sort the result and remove duplicates. */ + g_ptr_array_sort (l, nm_strcmp_p); + for (i = 1, j = 1; i < l->len; i++) { + if (nm_streq (l->pdata[j-1], l->pdata[i])) + g_free (l->pdata[i]); + else + l->pdata[j++] = l->pdata[i]; + } + + if (j == l->len) + g_ptr_array_add (l, NULL); + else + l->pdata[j] = NULL; + return (char **) g_ptr_array_free (l, FALSE); +} + /*********************************************************************/ /** diff --git a/libnm-core/nm-vpn-plugin-info.h b/libnm-core/nm-vpn-plugin-info.h index f1bba28197..1df8598b80 100644 --- a/libnm-core/nm-vpn-plugin-info.h +++ b/libnm-core/nm-vpn-plugin-info.h @@ -114,6 +114,12 @@ NMVpnPluginInfo *nm_vpn_plugin_info_list_find_by_filename (GSList *list, const c NM_AVAILABLE_IN_1_2 NMVpnPluginInfo *nm_vpn_plugin_info_list_find_by_service (GSList *list, const char *service); +NM_AVAILABLE_IN_1_4 +char *nm_vpn_plugin_info_list_find_service_type (GSList *list, const char *name); +NM_AVAILABLE_IN_1_4 +char **nm_vpn_plugin_info_list_get_service_types (GSList *list, + gboolean only_existing, + gboolean with_abbreviations); NM_AVAILABLE_IN_1_2 NMVpnEditorPlugin *nm_vpn_plugin_info_get_editor_plugin (NMVpnPluginInfo *self); diff --git a/libnm/libnm.ver b/libnm/libnm.ver index 4064522e09..9b3a741d97 100644 --- a/libnm/libnm.ver +++ b/libnm/libnm.ver @@ -1072,6 +1072,8 @@ global: nm_vpn_plugin_info_get_aliases; nm_vpn_plugin_info_get_auth_dialog; nm_vpn_plugin_info_get_service; + nm_vpn_plugin_info_list_get_service_types; + nm_vpn_plugin_info_list_find_service_type; nm_vpn_plugin_info_new_search_file; nm_vpn_plugin_info_supports_hints; } libnm_1_2_0; -- cgit v1.2.1 From 4cb57964d934696aa5956dcf82bfb7ab01196c54 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Tue, 7 Jun 2016 11:06:14 +0200 Subject: cli: fix handling of VPN names in nmcli by using libnm functions At various places, nmcli requires to specify a VPN type by name, for example $ nmcli connection add type vpn ifname '*' vpn-type $VPN_TYPE This $VPN_TYPE used to be a hard-coded list of known VPN plugin names. But actually, it should be a VPN service-type. A service-type used to be the D-Bus name of the VPN plugin. Now, with multiple VPN support that is no longer the case, but it still has the form of a D-Bus bus name. Alternativley, it could be an alias, which is just a way for plugins to support multiple service-types. Fix that, to support fully qualified service-types in the form of D-Bus bus names. Also, support lookup by name, in which case the present plugin-info instances are searched. Finally, support a list of hard-code short-names. All the logic how to translate a short-name to a fully qualified service-type is now inside libnm, so that various user agree on those names and don't have to hard-code them each. --- clients/cli/connections.c | 38 ++++++++--------- clients/common/nm-vpn-helpers.c | 90 +++++------------------------------------ clients/common/nm-vpn-helpers.h | 7 +--- 3 files changed, 29 insertions(+), 106 deletions(-) diff --git a/clients/cli/connections.c b/clients/cli/connections.c index 5b0261c4d3..506bfe0fd0 100644 --- a/clients/cli/connections.c +++ b/clients/cli/connections.c @@ -5886,16 +5886,13 @@ cleanup_bridge_slave: /* Build up the settings required for 'vpn' */ gboolean success = FALSE; const char *vpn_type = NULL; - char *vpn_type_ask = NULL; + gs_free char *vpn_type_ask = NULL; const char *user_c = NULL; char *user = NULL; - const char *st; gs_free char *service_type_free = NULL; - const char *service_type = NULL; nmc_arg_t exp_args[] = { {"vpn-type", TRUE, &vpn_type, !ask}, {"user", TRUE, &user_c, FALSE}, {NULL} }; - gs_free const char **plugin_names = NULL; if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error)) return FALSE; @@ -5910,15 +5907,11 @@ cleanup_bridge_slave: if (vpn_type_ask) vpn_type = g_strstrip (vpn_type_ask); - plugin_names = nm_vpn_get_plugin_names (FALSE); - if (!(st = nmc_string_is_valid (vpn_type, plugin_names, NULL))) { + service_type_free = nm_vpn_plugin_info_list_find_service_type (nm_vpn_get_plugin_infos (), vpn_type); + if (!service_type_free) g_print (_("Warning: 'vpn-type': %s not known.\n"), vpn_type); - st = vpn_type; - } - - service_type = nm_vpn_get_service_for_name (st); - if (!service_type) - service_type = service_type_free = nm_vpn_get_service_for_name_default (st); + else + vpn_type = service_type_free; /* Also ask for all optional arguments if '--ask' is specified. */ user = g_strdup (user_c); @@ -5929,12 +5922,11 @@ cleanup_bridge_slave: s_vpn = (NMSettingVpn *) nm_setting_vpn_new (); nm_connection_add_setting (connection, NM_SETTING (s_vpn)); - g_object_set (s_vpn, NM_SETTING_VPN_SERVICE_TYPE, service_type, NULL); + g_object_set (s_vpn, NM_SETTING_VPN_SERVICE_TYPE, vpn_type, NULL); g_object_set (s_vpn, NM_SETTING_VPN_USER_NAME, user, NULL); success = TRUE; cleanup_vpn: - g_free (vpn_type_ask); g_free (user); if (!success) return FALSE; @@ -6702,10 +6694,10 @@ update_connection (gboolean persistent, static char * gen_func_vpn_types (const char *text, int state) { - gs_free const char **plugin_names = NULL; + gs_strfreev char **plugin_names = NULL; - plugin_names = nm_vpn_get_plugin_names (FALSE); - return nmc_rl_gen_func_basic (text, state, plugin_names); + plugin_names = nm_vpn_plugin_info_list_get_service_types (nm_vpn_get_plugin_infos (), FALSE, TRUE); + return nmc_rl_gen_func_basic (text, state, (const char **) plugin_names); } static char * @@ -10625,6 +10617,7 @@ do_connection_import (NmCli *nmc, gboolean temporary, int argc, char **argv) AddConnectionInfo *info; NMConnection *connection = NULL; NMVpnEditorPlugin *plugin; + gs_free char *service_type = NULL; if (argc == 0) { if (nmc->ask) { @@ -10682,8 +10675,15 @@ do_connection_import (NmCli *nmc, gboolean temporary, int argc, char **argv) goto finish; } + service_type = nm_vpn_plugin_info_list_find_service_type (nm_vpn_get_plugin_infos (), type); + if (!service_type) { + g_string_printf (nmc->return_text, _("Error: failed to find VPN plugin for %s."), type); + nmc->return_value = NMC_RESULT_ERROR_UNKNOWN; + goto finish; + } + /* Import VPN configuration */ - plugin = nm_vpn_lookup_plugin (type, NULL, &error); + plugin = nm_vpn_get_editor_plugin (service_type, &error); if (!plugin) { g_string_printf (nmc->return_text, _("Error: failed to load VPN plugin: %s."), error->message); @@ -10790,7 +10790,7 @@ do_connection_export (NmCli *nmc, int argc, char **argv) type = nm_setting_vpn_get_service_type (nm_connection_get_setting_vpn (connection)); /* Export VPN configuration */ - plugin = nm_vpn_lookup_plugin (NULL, type, &error); + plugin = nm_vpn_get_editor_plugin (type, &error); if (!plugin) { g_string_printf (nmc->return_text, _("Error: failed to load VPN plugin: %s."), error->message); diff --git a/clients/common/nm-vpn-helpers.c b/clients/common/nm-vpn-helpers.c index 4eaa92d9f8..f2e6f4a662 100644 --- a/clients/common/nm-vpn-helpers.c +++ b/clients/common/nm-vpn-helpers.c @@ -19,8 +19,6 @@ /** * SECTION:nm-vpn-helpers * @short_description: VPN-related utilities - * - * Some functions should probably eventually move into libnm. */ #include "nm-default.h" @@ -31,30 +29,23 @@ #include "nm-utils.h" -static gboolean plugins_loaded; -static GSList *plugins = NULL; +/*****************************************************************************/ NMVpnEditorPlugin * -nm_vpn_lookup_plugin (const char *name, const char *service, GError **error) +nm_vpn_get_editor_plugin (const char *service_type, GError **error) { NMVpnEditorPlugin *plugin = NULL; NMVpnPluginInfo *plugin_info; gs_free_error GError *local = NULL; - g_return_val_if_fail (!service ^ !name, NULL); + g_return_val_if_fail (service_type, NULL); g_return_val_if_fail (error == NULL || *error == NULL, NULL); - if (G_UNLIKELY (!plugins_loaded)) - nm_vpn_get_plugin_infos (); - - if (service) - plugin_info = nm_vpn_plugin_info_list_find_by_service (plugins, service); - else - plugin_info = nm_vpn_plugin_info_list_find_by_name (plugins, name); + plugin_info = nm_vpn_plugin_info_list_find_by_service (nm_vpn_get_plugin_infos (), service_type); if (!plugin_info) { g_set_error (error, NM_VPN_PLUGIN_ERROR, NM_VPN_PLUGIN_ERROR_FAILED, - _("unknown VPN plugin \"%s\""), service ?: name); + _("unknown VPN plugin \"%s\""), service_type); return NULL; } plugin = nm_vpn_plugin_info_get_editor_plugin (plugin_info); @@ -88,6 +79,9 @@ nm_vpn_lookup_plugin (const char *name, const char *service, GError **error) GSList * nm_vpn_get_plugin_infos (void) { + static bool plugins_loaded; + static GSList *plugins = NULL; + if (G_LIKELY (plugins_loaded)) return plugins; plugins_loaded = TRUE; @@ -95,72 +89,6 @@ nm_vpn_get_plugin_infos (void) return plugins; } -const char ** -nm_vpn_get_plugin_names (gboolean only_available_plugins) -{ - GSList *p; - const char **list; - const char *known_names[] = { - "openvpn", - "vpnc", - "pptp", - "openconnect", - "openswan", - "libreswan", - "strongswan", - "ssh", - "l2tp", - "iodine", - "fortisslvpn", - }; - guint i, j, k; - - p = nm_vpn_get_plugin_infos (); - list = g_new0 (const char *, g_slist_length (p) + G_N_ELEMENTS (known_names) + 1); - - i = 0; - for (i = 0; p; p = p->next) - list[i++] = nm_vpn_plugin_info_get_name (p->data); - if (!only_available_plugins) { - for (j = 0; j < G_N_ELEMENTS (known_names); j++) - list[i++] = known_names[j]; - } - - g_qsort_with_data (list, i, sizeof (gpointer), nm_strcmp_p_with_data, NULL); - - /* remove duplicates */ - for (k = 0, j = 1; j < i; j++) { - if (nm_streq (list[k], list[j])) - continue; - list[k++] = list[j]; - } - list[k++] = NULL; - - return list; -} - -const char * -nm_vpn_get_service_for_name (const char *name) -{ - NMVpnPluginInfo *plugin_info; - - g_return_val_if_fail (name, NULL); - - plugin_info = nm_vpn_plugin_info_list_find_by_name (nm_vpn_get_plugin_infos (), name); - if (plugin_info) { - /* this only means we have a .name file (NMVpnPluginInfo). Possibly the - * NMVpnEditorPlugin is not loadable. */ - return nm_vpn_plugin_info_get_service (plugin_info); - } - return NULL; -} - -char * -nm_vpn_get_service_for_name_default (const char *name) -{ - return g_strdup_printf ("%s.%s", NM_DBUS_INTERFACE, name); -} - gboolean nm_vpn_supports_ipv6 (NMConnection *connection) { @@ -176,7 +104,7 @@ nm_vpn_supports_ipv6 (NMConnection *connection) if (!service_type) return FALSE; - plugin = nm_vpn_lookup_plugin (NULL, service_type, NULL); + plugin = nm_vpn_get_editor_plugin (service_type, NULL); if (!plugin) return FALSE; diff --git a/clients/common/nm-vpn-helpers.h b/clients/common/nm-vpn-helpers.h index 3d192f7fc8..e69e5b4e64 100644 --- a/clients/common/nm-vpn-helpers.h +++ b/clients/common/nm-vpn-helpers.h @@ -30,12 +30,7 @@ struct { GSList *nm_vpn_get_plugin_infos (void); -const char **nm_vpn_get_plugin_names (gboolean only_available_plugins); - -const char *nm_vpn_get_service_for_name (const char *name); -char * nm_vpn_get_service_for_name_default (const char *name); - -NMVpnEditorPlugin *nm_vpn_lookup_plugin (const char *name, const char *service, GError **error); +NMVpnEditorPlugin *nm_vpn_get_editor_plugin (const char *service_type, GError **error); gboolean nm_vpn_supports_ipv6 (NMConnection *connection); -- cgit v1.2.1 From 1c42375efb56bccb2ed55153cfd82826f910cac1 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Thu, 9 Jun 2016 00:50:40 +0200 Subject: libnm/vpn: pass NMVpnPluginInfo to the NMVpnEditorPlugin instance The NMVpnPluginInfo is essentially the .name file, that is, a configuration file about the plugin itself. Via NMVpnPluginInfo instance, the NMVpnEditorPlugin can be created. Usually, one would create a NMVpnPluginInfo (that is, reading the .name file) and then create a NMVpnEditorPlugin instance from there. In this case, usually the editor-plugin is owned by the plugin-info instance (although the API allows for creating the editor-plugin independently). Now, pass the NMVpnPluginInfo to the editor-plugin too. This is useful, because then the editor-plugin can look at the .name file. The .name file is not user configuration. Instead it is configuration about the plugin itself. Although the .name file is part of the plugin build artefacts, it is useful to allow the plugin to access the .name file. The reason is, that this can allow the user to easily change a configuration knob of the plugin without requiring to patch or the plugin. --- libnm-core/nm-vpn-editor-plugin.c | 106 ++++++++++++++++++++++++++++++++++++++ libnm-core/nm-vpn-editor-plugin.h | 12 +++++ libnm-core/nm-vpn-plugin-info.c | 2 + libnm-core/nm-vpn-plugin-info.h | 4 +- libnm/libnm.ver | 2 + 5 files changed, 125 insertions(+), 1 deletion(-) diff --git a/libnm-core/nm-vpn-editor-plugin.c b/libnm-core/nm-vpn-editor-plugin.c index 09df246957..1a4944b066 100644 --- a/libnm-core/nm-vpn-editor-plugin.c +++ b/libnm-core/nm-vpn-editor-plugin.c @@ -74,6 +74,112 @@ nm_vpn_editor_plugin_default_init (NMVpnEditorPluginInterface *iface) /*********************************************************************/ +typedef struct { + NMVpnPluginInfo *plugin_info; +} NMVpnEditorPluginPrivate; + +static void +_private_destroy (gpointer data) +{ + NMVpnEditorPluginPrivate *priv = data; + + if (priv->plugin_info) + g_object_remove_weak_pointer ((GObject *) priv->plugin_info, (gpointer *) &priv->plugin_info); + + g_slice_free (NMVpnEditorPluginPrivate, priv); +} + +static NMVpnEditorPluginPrivate * +_private_get (NMVpnEditorPlugin *plugin, gboolean create) +{ + static GQuark quark = 0; + NMVpnEditorPluginPrivate *priv; + + nm_assert (NM_IS_VPN_EDITOR_PLUGIN (plugin)); + + if (G_UNLIKELY (quark == 0)) + quark = g_quark_from_string ("nm-vpn-editor-plugin-private"); + + priv = g_object_get_qdata ((GObject *) plugin, quark); + if (G_LIKELY (priv)) + return priv; + if (!create) + return NULL; + priv = g_slice_new0 (NMVpnEditorPluginPrivate); + g_object_set_qdata_full ((GObject *) plugin, quark, priv, _private_destroy); + return priv; +} + +#define NM_VPN_EDITOR_PLUGIN_GET_PRIVATE(plugin) _private_get (plugin, TRUE) +#define NM_VPN_EDITOR_PLUGIN_TRY_GET_PRIVATE(plugin) _private_get (plugin, FALSE) + +/*********************************************************************/ + +/** + * nm_vpn_editor_plugin_get_plugin_info: + * @plugin: the #NMVpnEditorPlugin instance + * + * Returns: (transfer-none): if set, return the #NMVpnPluginInfo instance. + * + * Since: 1.4 + */ +NMVpnPluginInfo * +nm_vpn_editor_plugin_get_plugin_info (NMVpnEditorPlugin *plugin) +{ + NMVpnEditorPluginPrivate *priv; + + g_return_val_if_fail (NM_IS_VPN_EDITOR_PLUGIN (plugin), NULL); + + priv = NM_VPN_EDITOR_PLUGIN_TRY_GET_PRIVATE (plugin); + return priv ? priv->plugin_info : NULL; +} + +/** + * nm_vpn_editor_plugin_set_plugin_info: + * @plugin: the #NMVpnEditorPlugin instance + * @plugin_info: (allow-none): a #NMVpnPluginInfo instance or %NULL + * + * Returns: (transfer-none): set or clear the plugin-info instance. + * This takes a weak reference on @plugin_info, to avoid circular + * reference as the plugin-info might also reference the editor-plugin. + * + * Since: 1.4 + */ +void +nm_vpn_editor_plugin_set_plugin_info (NMVpnEditorPlugin *plugin, NMVpnPluginInfo *plugin_info) +{ + NMVpnEditorPluginInterface *interface; + NMVpnEditorPluginPrivate *priv; + + g_return_if_fail (NM_IS_VPN_EDITOR_PLUGIN (plugin)); + + if (!plugin_info) { + priv = NM_VPN_EDITOR_PLUGIN_TRY_GET_PRIVATE (plugin); + if (!priv) + return; + } else { + g_return_if_fail (NM_IS_VPN_PLUGIN_INFO (plugin_info)); + priv = NM_VPN_EDITOR_PLUGIN_GET_PRIVATE (plugin); + } + + if (priv->plugin_info == plugin_info) + return; + if (priv->plugin_info) + g_object_remove_weak_pointer ((GObject *) priv->plugin_info, (gpointer *) &priv->plugin_info); + priv->plugin_info = plugin_info; + if (priv->plugin_info) + g_object_add_weak_pointer ((GObject *) priv->plugin_info, (gpointer *) &priv->plugin_info); + + if (plugin_info) { + interface = NM_VPN_EDITOR_PLUGIN_GET_INTERFACE (plugin); + if (interface->notify_plugin_info_set) + interface->notify_plugin_info_set (plugin, plugin_info); + } + +} + +/*********************************************************************/ + static NMVpnEditorPlugin * _nm_vpn_editor_plugin_load (const char *plugin_name, gboolean do_file_checks, diff --git a/libnm-core/nm-vpn-editor-plugin.h b/libnm-core/nm-vpn-editor-plugin.h index 82c609b969..dc18f04b74 100644 --- a/libnm-core/nm-vpn-editor-plugin.h +++ b/libnm-core/nm-vpn-editor-plugin.h @@ -34,6 +34,8 @@ G_BEGIN_DECLS +struct _NMVpnPluginInfo; + typedef struct _NMVpnEditorPlugin NMVpnEditorPlugin; typedef struct _NMVpnEditor NMVpnEditor; @@ -119,6 +121,9 @@ typedef struct { GError **error); char * (*get_suggested_filename) (NMVpnEditorPlugin *plugin, NMConnection *connection); + + void (*notify_plugin_info_set) (NMVpnEditorPlugin *plugin, + struct _NMVpnPluginInfo *plugin_info); } NMVpnEditorPluginInterface; GType nm_vpn_editor_plugin_get_type (void); @@ -152,6 +157,13 @@ NMVpnEditorPlugin *nm_vpn_editor_plugin_load (const char *plugin_name, const char *check_service, GError **error); +NM_AVAILABLE_IN_1_4 +struct _NMVpnPluginInfo *nm_vpn_editor_plugin_get_plugin_info (NMVpnEditorPlugin *plugin); +NM_AVAILABLE_IN_1_4 +void nm_vpn_editor_plugin_set_plugin_info (NMVpnEditorPlugin *plugin, struct _NMVpnPluginInfo *plugin_info); + +#include "nm-vpn-plugin-info.h" + G_END_DECLS #endif /* __NM_VPN_EDITOR_PLUGIN_H__ */ diff --git a/libnm-core/nm-vpn-plugin-info.c b/libnm-core/nm-vpn-plugin-info.c index 94911e377b..7a3198c3d9 100644 --- a/libnm-core/nm-vpn-plugin-info.c +++ b/libnm-core/nm-vpn-plugin-info.c @@ -1089,6 +1089,8 @@ nm_vpn_plugin_info_load_editor_plugin (NMVpnPluginInfo *self, GError **error) NULL, NULL, error); + if (priv->editor_plugin) + nm_vpn_editor_plugin_set_plugin_info (priv->editor_plugin, self); return priv->editor_plugin; } diff --git a/libnm-core/nm-vpn-plugin-info.h b/libnm-core/nm-vpn-plugin-info.h index 1df8598b80..908ee4827c 100644 --- a/libnm-core/nm-vpn-plugin-info.h +++ b/libnm-core/nm-vpn-plugin-info.h @@ -44,10 +44,12 @@ G_BEGIN_DECLS #define NM_VPN_PLUGIN_INFO_KF_GROUP_LIBNM "libnm" #define NM_VPN_PLUGIN_INFO_KF_GROUP_GNOME "GNOME" +struct _NMVpnPluginInfo; + /** * NMVpnPluginInfo: */ -typedef struct { +typedef struct _NMVpnPluginInfo { NM_AVAILABLE_IN_1_2 GObject parent; } NMVpnPluginInfo; diff --git a/libnm/libnm.ver b/libnm/libnm.ver index 9b3a741d97..2dc2103517 100644 --- a/libnm/libnm.ver +++ b/libnm/libnm.ver @@ -1068,7 +1068,9 @@ global: nm_device_team_get_config; nm_setting_ip6_config_get_token; nm_setting_ip_config_get_dns_priority; + nm_vpn_editor_plugin_get_plugin_info; nm_vpn_editor_plugin_load; + nm_vpn_editor_plugin_set_plugin_info; nm_vpn_plugin_info_get_aliases; nm_vpn_plugin_info_get_auth_dialog; nm_vpn_plugin_info_get_service; -- cgit v1.2.1 From cf34211c90d2d45cb325f5558304f64297033bc6 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Mon, 13 Jun 2016 11:47:58 +0200 Subject: libnm/vpn: add nm_vpn_editor_plugin_load_vt() Let VPN plugins return a virtual function table to extend the API while bypassing libnm. This allows to add and use new functionality to VPN plugins without updating libnm. The actual definitions are in a header-only file "nm-vpn-editor-plugin-call.h", which can be copied to the caller/plugin. --- libnm-core/nm-vpn-editor-plugin.c | 47 +++++++++ libnm-core/nm-vpn-editor-plugin.h | 12 +++ libnm/libnm.ver | 1 + libnm/nm-vpn-service-plugin.c | 8 ++ po/POTFILES.skip | 1 + shared/Makefile.am | 3 +- shared/nm-vpn-editor-plugin-call.h | 189 +++++++++++++++++++++++++++++++++++++ 7 files changed, 260 insertions(+), 1 deletion(-) create mode 100644 shared/nm-vpn-editor-plugin-call.h diff --git a/libnm-core/nm-vpn-editor-plugin.c b/libnm-core/nm-vpn-editor-plugin.c index 1a4944b066..bc72ca3911 100644 --- a/libnm-core/nm-vpn-editor-plugin.c +++ b/libnm-core/nm-vpn-editor-plugin.c @@ -180,6 +180,53 @@ nm_vpn_editor_plugin_set_plugin_info (NMVpnEditorPlugin *plugin, NMVpnPluginInfo /*********************************************************************/ +/** + * nm_vpn_editor_plugin_get_vt: + * @plugin: the #NMVpnEditorPlugin + * @vt: (out): buffer to be filled with the VT table of the plugin + * @vt_size: the size of the buffer. Can be 0 to only query the + * size of plugin's VT. + * + * Returns an opaque VT function table for the plugin to extend + * functionality. The actual meaning of NMVpnEditorPluginVT is not + * defined in public API of libnm, instead it must be agreed by + * both the plugin and the caller. See the header-only file + * 'nm-vpn-editor-plugin-call.h' which defines the meaning. + * + * Returns: the actual size of the @plugin's virtual function table. + * + * Since: 1.4 + **/ +gsize +nm_vpn_editor_plugin_get_vt (NMVpnEditorPlugin *plugin, + NMVpnEditorPluginVT *vt, + gsize vt_size) +{ + const NMVpnEditorPluginVT *p_vt = NULL; + gsize p_vt_size = 0; + NMVpnEditorPluginInterface *interface; + + g_return_val_if_fail (NM_IS_VPN_EDITOR_PLUGIN (plugin), 0); + + if (vt_size) { + g_return_val_if_fail (vt, 0); + memset (vt, 0, vt_size); + } + + interface = NM_VPN_EDITOR_PLUGIN_GET_INTERFACE (plugin); + if (interface->get_vt) { + p_vt = interface->get_vt (plugin, &p_vt_size); + if (!p_vt) + p_vt_size = 0; + g_return_val_if_fail (p_vt_size, 0); + memcpy (vt, p_vt, MIN (vt_size, p_vt_size)); + } + + return p_vt_size; +} + +/*********************************************************************/ + static NMVpnEditorPlugin * _nm_vpn_editor_plugin_load (const char *plugin_name, gboolean do_file_checks, diff --git a/libnm-core/nm-vpn-editor-plugin.h b/libnm-core/nm-vpn-editor-plugin.h index dc18f04b74..fb51f7e621 100644 --- a/libnm-core/nm-vpn-editor-plugin.h +++ b/libnm-core/nm-vpn-editor-plugin.h @@ -82,6 +82,8 @@ typedef enum /*< flags >*/ { /* D-Bus service name of the plugin's VPN service */ #define NM_VPN_EDITOR_PLUGIN_SERVICE "service" +typedef struct _NMVpnEditorPluginVT NMVpnEditorPluginVT; + /** * NMVpnEditorPluginInterface: * @g_iface: the parent interface @@ -99,6 +101,8 @@ typedef enum /*< flags >*/ { * @get_suggested_filename: For a given connection, return a suggested file * name. Returned value will be %NULL or a suggested file name to be freed by * the caller. + * @get_vt: return a virtual function table to implement further functions in + * the plugin, without requiring to update libnm. Used by nm_vpn_editor_plugin_get_vt(). * * Interface for VPN editor plugins. */ @@ -124,6 +128,9 @@ typedef struct { void (*notify_plugin_info_set) (NMVpnEditorPlugin *plugin, struct _NMVpnPluginInfo *plugin_info); + + const NMVpnEditorPluginVT *(*get_vt) (NMVpnEditorPlugin *plugin, + gsize *out_vt_size); } NMVpnEditorPluginInterface; GType nm_vpn_editor_plugin_get_type (void); @@ -134,6 +141,11 @@ NMVpnEditor *nm_vpn_editor_plugin_get_editor (NMVpnEditorPlugin *plugin, NMVpnEditorPluginCapability nm_vpn_editor_plugin_get_capabilities (NMVpnEditorPlugin *plugin); +NM_AVAILABLE_IN_1_4 +gsize nm_vpn_editor_plugin_get_vt (NMVpnEditorPlugin *plugin, + NMVpnEditorPluginVT *vt, + gsize vt_size); + NMConnection *nm_vpn_editor_plugin_import (NMVpnEditorPlugin *plugin, const char *path, GError **error); diff --git a/libnm/libnm.ver b/libnm/libnm.ver index 2dc2103517..41f650f008 100644 --- a/libnm/libnm.ver +++ b/libnm/libnm.ver @@ -1069,6 +1069,7 @@ global: nm_setting_ip6_config_get_token; nm_setting_ip_config_get_dns_priority; nm_vpn_editor_plugin_get_plugin_info; + nm_vpn_editor_plugin_get_vt; nm_vpn_editor_plugin_load; nm_vpn_editor_plugin_set_plugin_info; nm_vpn_plugin_info_get_aliases; diff --git a/libnm/nm-vpn-service-plugin.c b/libnm/nm-vpn-service-plugin.c index bf4369e4ad..df99eb28d9 100644 --- a/libnm/nm-vpn-service-plugin.c +++ b/libnm/nm-vpn-service-plugin.c @@ -1306,3 +1306,11 @@ nm_vpn_service_plugin_initable_iface_init (GInitableIface *iface) { iface->init = init_sync; } + +/*****************************************************************************/ + +/* this header is intended to be copied to users of nm_vpn_editor_plugin_call(), + * to simplify invocation of generic functions. Include it here, to complile + * the code. */ +#include "nm-vpn-editor-plugin-call.h" + diff --git a/po/POTFILES.skip b/po/POTFILES.skip index 70e222c6a9..842f22a568 100644 --- a/po/POTFILES.skip +++ b/po/POTFILES.skip @@ -6,5 +6,6 @@ vpn-daemons/openvpn vpn-daemons/pptp vpn-daemons/vpnc contrib/fedora/rpm/ +shared/nm-vpn-editor-plugin-call.h # https://bugs.launchpad.net/intltool/+bug/1117944 sub/policy/org.freedesktop.NetworkManager.policy.in diff --git a/shared/Makefile.am b/shared/Makefile.am index c6ede00b6a..01818d0cd1 100644 --- a/shared/Makefile.am +++ b/shared/Makefile.am @@ -10,6 +10,7 @@ EXTRA_DIST = \ nm-test-libnm-utils.h \ nm-test-utils.h \ nm-test-utils-impl.c \ - nm-version-macros.h.in + nm-version-macros.h.in \ + nm-vpn-editor-plugin-call.h CLEANFILES=nm-version.h diff --git a/shared/nm-vpn-editor-plugin-call.h b/shared/nm-vpn-editor-plugin-call.h new file mode 100644 index 0000000000..c6b7ee76ab --- /dev/null +++ b/shared/nm-vpn-editor-plugin-call.h @@ -0,0 +1,189 @@ +/* -*- 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) 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 + +/* we make use of other internal header files, you need those too. */ +#include "gsystem-local-alloc.h" +#include "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__ */ -- cgit v1.2.1