about summary refs log tree commit diff
path: root/shared/nm-utils/nm-vpn-plugin-utils.c
diff options
context:
space:
mode:
authorMichael Biebl <biebl@debian.org>2016-08-26 02:18:32 +0200
committerMichael Biebl <biebl@debian.org>2016-08-26 02:18:32 +0200
commit7514efc2f38c9ace4557d4e69d68e7d380389030 (patch)
tree7fb00fda86cfcc2ca377f191633a7cfdbfea7ca3 /shared/nm-utils/nm-vpn-plugin-utils.c
parentd6201f5d8daada3d64a0a3e0038e14eebec683ce (diff)
Imported Upstream version 1.4.0 upstream/1.4.0
Diffstat (limited to 'shared/nm-utils/nm-vpn-plugin-utils.c')
-rw-r--r--shared/nm-utils/nm-vpn-plugin-utils.c130
1 files changed, 130 insertions, 0 deletions
diff --git a/shared/nm-utils/nm-vpn-plugin-utils.c b/shared/nm-utils/nm-vpn-plugin-utils.c
new file mode 100644
index 00000000..772aa39a
--- /dev/null
+++ b/shared/nm-utils/nm-vpn-plugin-utils.c
@@ -0,0 +1,130 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2016 Red Hat, Inc.
+ */
+
+#include "nm-default.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;
+
+	g_return_val_if_fail (module_name && g_path_is_absolute (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);
+
+	/* 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_name, RTLD_LAZY | RTLD_LOCAL);
+		if (!dl_module) {
+			if (!g_file_test (module_name, G_FILE_TEST_EXISTS)) {
+				g_set_error (error,
+				             G_FILE_ERROR,
+				             G_FILE_ERROR_NOENT,
+				             _("missing plugin file \"%s\""), module_name);
+				return NULL;
+			}
+			g_set_error (error,
+			             NM_CONNECTION_ERROR,
+			             NM_CONNECTION_ERROR_FAILED,
+			             _("cannot load editor plugin: %s"), dlerror ());
+			return NULL;
+		}
+
+		factory = dlsym (dl_module, factory_name);
+		if (!factory) {
+			g_set_error (error,
+			             NM_CONNECTION_ERROR,
+			             NM_CONNECTION_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_CONNECTION_ERROR,
+			                     NM_CONNECTION_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;
+}
+