about summary refs log tree commit diff
path: root/src/vpn-manager/nm-vpn-manager.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/vpn-manager/nm-vpn-manager.c')
-rw-r--r--src/vpn-manager/nm-vpn-manager.c342
1 files changed, 253 insertions, 89 deletions
diff --git a/src/vpn-manager/nm-vpn-manager.c b/src/vpn-manager/nm-vpn-manager.c
index 221a8b54..4b58be0d 100644
--- a/src/vpn-manager/nm-vpn-manager.c
+++ b/src/vpn-manager/nm-vpn-manager.c
@@ -15,11 +15,12 @@
  * with this program; if not, write to the Free Software Foundation, Inc.,
  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  *
- * Copyright (C) 2005 - 2008 Red Hat, Inc.
+ * Copyright (C) 2005 - 2010 Red Hat, Inc.
  * Copyright (C) 2006 - 2008 Novell, Inc.
  */
 
 #include <string.h>
+#include <gio/gio.h>
 
 #include "nm-vpn-manager.h"
 #include "nm-vpn-service.h"
@@ -28,13 +29,18 @@
 #include "nm-dbus-manager.h"
 #include "NetworkManagerVPN.h"
 #include "nm-marshal.h"
+#include "nm-logging.h"
 
 G_DEFINE_TYPE (NMVPNManager, nm_vpn_manager, G_TYPE_OBJECT)
 
 #define NM_VPN_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_VPN_MANAGER, NMVPNManagerPrivate))
 
 typedef struct {
-	GSList *services;
+	gboolean disposed;
+
+	GHashTable *services;
+	GFileMonitor *monitor;
+	guint monitor_id;
 } NMVPNManagerPrivate;
 
 enum {
@@ -81,53 +87,42 @@ nm_vpn_manager_error_get_type (void)
 }
 
 
-
 static NMVPNService *
-nm_vpn_manager_get_service (NMVPNManager *manager, const char *service_name)
+get_service_by_namefile (NMVPNManager *self, const char *namefile)
 {
-	GSList *iter;
+	NMVPNManagerPrivate *priv = NM_VPN_MANAGER_GET_PRIVATE (self);
+	GHashTableIter iter;
+	gpointer data;
 
-	for (iter = NM_VPN_MANAGER_GET_PRIVATE (manager)->services; iter; iter = iter->next) {
-		NMVPNService *service = NM_VPN_SERVICE (iter->data);
+	g_return_val_if_fail (namefile, NULL);
+	g_return_val_if_fail (g_path_is_absolute (namefile), NULL);
 
-		if (!strcmp (service_name, nm_vpn_service_get_name (service)))
-			return g_object_ref (service);
-	}
+	g_hash_table_iter_init (&iter, priv->services);
+	while (g_hash_table_iter_next (&iter, NULL, &data)) {
+		NMVPNService *candidate = NM_VPN_SERVICE (data);
+		const char *service_namefile;
 
+		service_namefile = nm_vpn_service_get_name_file (candidate);
+		if (!strcmp (namefile, service_namefile))
+			return candidate;
+	}
 	return NULL;
 }
 
-static void
-remove_service (gpointer data, GObject *service)
-{
-	NMVPNManagerPrivate *priv = NM_VPN_MANAGER_GET_PRIVATE (data);
-
-	priv->services = g_slist_remove (priv->services, service);
-}
-
-static void
-nm_vpn_manager_add_service (NMVPNManager *manager, NMVPNService *service)
-{
-	NMVPNManagerPrivate *priv = NM_VPN_MANAGER_GET_PRIVATE (manager);
-
-	priv->services = g_slist_prepend (priv->services, service);
-	g_object_weak_ref (G_OBJECT (service), remove_service, manager);
-}
-
 static NMVPNConnection *
-find_active_vpn_connection_by_connection (NMVPNManager *manager, NMConnection *connection)
+find_active_vpn_connection_by_connection (NMVPNManager *self, NMConnection *connection)
 {
-	NMVPNManagerPrivate *priv;
-	GSList *iter;
+	NMVPNManagerPrivate *priv = NM_VPN_MANAGER_GET_PRIVATE (self);
+	GHashTableIter iter;
+	gpointer data;
+	GSList *connections, *elt;
 
-	g_return_val_if_fail (NM_IS_VPN_MANAGER (manager), NULL);
+	g_return_val_if_fail (connection, NULL);
 	g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL);
 
-	priv = NM_VPN_MANAGER_GET_PRIVATE (manager);
-	for (iter = priv->services; iter; iter = g_slist_next (iter)) {
-		GSList *connections, *elt;
-
-		connections = nm_vpn_service_get_active_connections (NM_VPN_SERVICE (iter->data));
+	g_hash_table_iter_init (&iter, priv->services);
+	while (g_hash_table_iter_next (&iter, NULL, &data)) {
+		connections = nm_vpn_service_get_active_connections (NM_VPN_SERVICE (data));
 		for (elt = connections; elt; elt = g_slist_next (elt)) {
 			NMVPNConnection *vpn = NM_VPN_CONNECTION (elt->data);
 
@@ -169,7 +164,7 @@ nm_vpn_manager_activate_connection (NMVPNManager *manager,
 	NMSettingVPN *vpn_setting;
 	NMVPNService *service;
 	NMVPNConnection *vpn = NULL;
-	const char *service_type;
+	const char *service_name;
 
 	g_return_val_if_fail (NM_IS_VPN_MANAGER (manager), NULL);
 	g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL);
@@ -199,78 +194,78 @@ nm_vpn_manager_activate_connection (NMVPNManager *manager,
 		vpn = NULL;
 	}
 
-	service_type = nm_setting_vpn_get_service_type (vpn_setting);
-	service = nm_vpn_manager_get_service (manager, service_type);
+	service_name = nm_setting_vpn_get_service_type (vpn_setting);
+	g_assert (service_name);
+	service = g_hash_table_lookup (NM_VPN_MANAGER_GET_PRIVATE (manager)->services, service_name);
 	if (!service) {
-		service = nm_vpn_service_new (service_type);
-		if (service)
-			nm_vpn_manager_add_service (manager, service);
-	}
-
-	if (service) {
-		vpn = nm_vpn_service_activate (service, connection, act_request, device, error);
-		if (vpn) {
-			g_signal_connect (vpn, "vpn-state-changed",
-			                  G_CALLBACK (connection_vpn_state_changed),
-			                  manager);
-		}
-	} else {
 		g_set_error (error,
 		             NM_VPN_MANAGER_ERROR, NM_VPN_MANAGER_ERROR_SERVICE_INVALID,
-		             "%s", "The VPN service was invalid.");
+		             "The VPN service '%s' was not installed.",
+		             service_name);
+		return NULL;
+	}
+
+	vpn = nm_vpn_service_activate (service, connection, act_request, device, error);
+	if (vpn) {
+		g_signal_connect (vpn, "vpn-state-changed",
+		                  G_CALLBACK (connection_vpn_state_changed),
+		                  manager);
 	}
 
 	return vpn;
 }
 
 gboolean
-nm_vpn_manager_deactivate_connection (NMVPNManager *manager,
+nm_vpn_manager_deactivate_connection (NMVPNManager *self,
                                       const char *path,
                                       NMVPNConnectionStateReason reason)
 {
 	NMVPNManagerPrivate *priv;
-	GSList *iter;
-	gboolean found = FALSE;
+	GHashTableIter iter;
+	gpointer data;
+	GSList *active, *elt;
 
-	g_return_val_if_fail (NM_IS_VPN_MANAGER (manager), FALSE);
+	g_return_val_if_fail (self, FALSE);
+	g_return_val_if_fail (NM_IS_VPN_MANAGER (self), FALSE);
 	g_return_val_if_fail (path != NULL, FALSE);
 
-	priv = NM_VPN_MANAGER_GET_PRIVATE (manager);
-	for (iter = priv->services; iter; iter = g_slist_next (iter)) {
-		GSList *connections, *elt;
-
-		connections = nm_vpn_service_get_active_connections (NM_VPN_SERVICE (iter->data));
-		for (elt = connections; elt; elt = g_slist_next (elt)) {
+	priv = NM_VPN_MANAGER_GET_PRIVATE (self);
+	g_hash_table_iter_init (&iter, priv->services);
+	while (g_hash_table_iter_next (&iter, NULL, &data)) {
+		active = nm_vpn_service_get_active_connections (NM_VPN_SERVICE (data));
+		for (elt = active; elt; elt = g_slist_next (elt)) {
 			NMVPNConnection *vpn = NM_VPN_CONNECTION (elt->data);
 			const char *vpn_path;
 
 			vpn_path = nm_vpn_connection_get_active_connection_path (vpn);
 			if (!strcmp (path, vpn_path)) {
 				nm_vpn_connection_disconnect (vpn, reason);
-				found = TRUE;
+				return TRUE;
 			}
 		}
 	}
 
-	return found ? TRUE : FALSE;
+	return FALSE;
 }
 
 void
-nm_vpn_manager_add_active_connections (NMVPNManager *manager,
+nm_vpn_manager_add_active_connections (NMVPNManager *self,
                                        NMConnection *filter,
                                        GPtrArray *array)
 {
 	NMVPNManagerPrivate *priv;
-	GSList *iter;
+	GHashTableIter iter;
+	gpointer data;
+	GSList *active, *elt;
 
-	g_return_if_fail (NM_IS_VPN_MANAGER (manager));
+	g_return_if_fail (self);
+	g_return_if_fail (NM_IS_VPN_MANAGER (self));
 	g_return_if_fail (array != NULL);
 
-	priv = NM_VPN_MANAGER_GET_PRIVATE (manager);
-	for (iter = priv->services; iter; iter = g_slist_next (iter)) {
-		GSList *active, *elt;
-
-		active = nm_vpn_service_get_active_connections (NM_VPN_SERVICE (iter->data));
+	priv = NM_VPN_MANAGER_GET_PRIVATE (self);
+	g_hash_table_iter_init (&iter, priv->services);
+	while (g_hash_table_iter_next (&iter, NULL, &data)) {
+		active = nm_vpn_service_get_active_connections (NM_VPN_SERVICE (data));
 		for (elt = active; elt; elt = g_slist_next (elt)) {
 			NMVPNConnection *vpn = NM_VPN_CONNECTION (elt->data);
 			const char *path;
@@ -284,26 +279,155 @@ nm_vpn_manager_add_active_connections (NMVPNManager *manager,
 }
 
 GSList *
-nm_vpn_manager_get_active_connections (NMVPNManager *manager)
+nm_vpn_manager_get_active_connections (NMVPNManager *self)
 {
 	NMVPNManagerPrivate *priv;
-	GSList *iter;
-	GSList *list = NULL;
+	GHashTableIter iter;
+	gpointer data;
+	GSList *list = NULL, *active, *elt;
+
+	g_return_val_if_fail (self, NULL);
+	g_return_val_if_fail (NM_IS_VPN_MANAGER (self), NULL);
+
+	priv = NM_VPN_MANAGER_GET_PRIVATE (self);
+	g_hash_table_iter_init (&iter, priv->services);
+	while (g_hash_table_iter_next (&iter, NULL, &data)) {
+		active = nm_vpn_service_get_active_connections (NM_VPN_SERVICE (data));
+		for (elt = active; elt; elt = g_slist_next (elt))
+			list = g_slist_append (list, g_object_ref (G_OBJECT (elt->data)));
+	}
+	return list;
+}
+
+NMConnection *
+nm_vpn_manager_get_connection_for_active (NMVPNManager *manager,
+                                          const char *active_path)
+{
+	NMVPNManagerPrivate *priv;
+	GHashTableIter iter;
+	gpointer data;
+	GSList *active, *elt;
 
 	g_return_val_if_fail (NM_IS_VPN_MANAGER (manager), NULL);
 
 	priv = NM_VPN_MANAGER_GET_PRIVATE (manager);
-	for (iter = priv->services; iter; iter = g_slist_next (iter)) {
-		GSList *active, *elt;
+	g_hash_table_iter_init (&iter, priv->services);
+	while (g_hash_table_iter_next (&iter, NULL, &data)) {
+		active = nm_vpn_service_get_active_connections (NM_VPN_SERVICE (data));
+		for (elt = active; elt; elt = g_slist_next (elt)) {
+			NMVPNConnection *vpn = NM_VPN_CONNECTION (elt->data);
+			const char *ac_path;
 
-		active = nm_vpn_service_get_active_connections (NM_VPN_SERVICE (iter->data));
-		for (elt = active; elt; elt = g_slist_next (elt))
-			list = g_slist_append (list, g_object_ref (NM_VPN_CONNECTION (elt->data)));
+			ac_path = nm_vpn_connection_get_active_connection_path (vpn);
+			if (ac_path && !strcmp (ac_path, active_path))
+				return nm_vpn_connection_get_connection (vpn);
+		}
 	}
 
-	return list;
+	return NULL;
 }
 
+static char *
+service_name_from_file (const char *path)
+{
+	GKeyFile *kf = NULL;
+	char *service_name = NULL;
+
+	g_return_val_if_fail (g_path_is_absolute (path), NULL);
+
+	if (!g_str_has_suffix (path, ".name"))
+		return NULL;
+
+	kf = g_key_file_new ();
+	if (g_key_file_load_from_file (kf, path, G_KEY_FILE_NONE, NULL))
+		service_name = g_key_file_get_string (kf, VPN_CONNECTION_GROUP, "service", NULL);
+
+	g_key_file_free (kf);
+	return service_name;
+}
+
+static void
+try_add_service (NMVPNManager *self, const char *namefile)
+{
+	NMVPNManagerPrivate *priv = NM_VPN_MANAGER_GET_PRIVATE (self);
+	NMVPNService *service = NULL;
+	GError *error = NULL;
+	const char *service_name;
+	char *tmp;
+
+	g_return_if_fail (g_path_is_absolute (namefile));
+
+	/* Make sure we don't add dupes */
+	tmp = service_name_from_file (namefile);
+	if (tmp)
+		service = g_hash_table_lookup (priv->services, tmp);
+	g_free (tmp);
+	if (service)
+		return;
+
+	/* New service, add it */
+	service = nm_vpn_service_new (namefile, &error);
+	if (!service) {
+		nm_log_warn (LOGD_VPN, "failed to load VPN service file %s: (%d) %s",
+		             error ? error->code : -1,
+		             error && error->message ? error->message : "(unknown)");
+		g_clear_error (&error);
+		return;
+	}
+
+	service_name = nm_vpn_service_get_dbus_service (service);
+	g_hash_table_insert (priv->services, (char *) service_name, service);
+	nm_log_info (LOGD_VPN, "VPN: loaded %s", service_name, service);
+}
+
+static void
+vpn_dir_changed (GFileMonitor *monitor,
+                 GFile *file,
+                 GFile *other_file,
+                 GFileMonitorEvent event_type,
+                 gpointer user_data)
+{
+	NMVPNManager *self = NM_VPN_MANAGER (user_data);
+	NMVPNManagerPrivate *priv = NM_VPN_MANAGER_GET_PRIVATE (self);
+	NMVPNService *service;
+	char *path;
+
+	path = g_file_get_path (file);
+	if (!g_str_has_suffix (path, ".name")) {
+		g_free (path);
+		return;
+	}
+
+	switch (event_type) {
+	case G_FILE_MONITOR_EVENT_DELETED:
+		nm_log_dbg (LOGD_VPN, "service file %s deleted", path);
+
+		service = get_service_by_namefile (self, path);
+		if (service) {
+			const char *service_name = nm_vpn_service_get_dbus_service (service);
+
+			/* Stop active VPN connections and destroy the service */
+			nm_vpn_service_connections_stop (service, TRUE,
+			                                 NM_VPN_CONNECTION_STATE_REASON_SERVICE_STOPPED);
+			nm_log_info (LOGD_VPN, "VPN: unloaded %s", service_name, service);
+			g_hash_table_remove (priv->services, service_name);
+		}
+		break;
+	case G_FILE_MONITOR_EVENT_CREATED:
+	case G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT:
+		nm_log_dbg (LOGD_VPN, "service file %s created or modified", path);
+		try_add_service (self, path);
+		break;
+	default:
+		nm_log_dbg (LOGD_VPN, "service file %s change event %d", path, event_type);
+		break;
+	}
+
+	g_free (path);
+}
+
+/******************************************************************************/
+
 NMVPNManager *
 nm_vpn_manager_get (void)
 {
@@ -318,21 +442,61 @@ nm_vpn_manager_get (void)
 	return singleton;
 }
 
-/******************************************************************************/
-
 static void
-nm_vpn_manager_init (NMVPNManager *manager)
+nm_vpn_manager_init (NMVPNManager *self)
 {
+	NMVPNManagerPrivate *priv = NM_VPN_MANAGER_GET_PRIVATE (self);
+	GFile *file;
+	GDir *dir;
+	const char *fn;
+	char *path;
+
+	priv->services = g_hash_table_new_full (g_str_hash, g_str_equal,
+	                                        NULL, g_object_unref);
+
+	/* Watch the VPN directory for changes */
+	file = g_file_new_for_path (VPN_NAME_FILES_DIR "/");
+	priv->monitor = g_file_monitor_directory (file, G_FILE_MONITOR_NONE, NULL, NULL);
+	g_object_unref (file);
+	if (priv->monitor) {
+		priv->monitor_id = g_signal_connect (priv->monitor, "changed",
+		                                     G_CALLBACK (vpn_dir_changed), self);
+	}
+
+	/* Load VPN service files */
+	dir = g_dir_open (VPN_NAME_FILES_DIR, 0, NULL);
+	if (dir) {
+		while ((fn = g_dir_read_name (dir))) {
+			/* only parse filenames that end with .name */
+			if (g_str_has_suffix (fn, ".name")) {
+				path = g_build_filename (VPN_NAME_FILES_DIR, fn, NULL);
+				try_add_service (self, path);
+				g_free (path);
+			}
+		}
+		g_dir_close (dir);
+	}
 }
 
 static void
-finalize (GObject *object)
+dispose (GObject *object)
 {
 	NMVPNManagerPrivate *priv = NM_VPN_MANAGER_GET_PRIVATE (object);
 
-	g_slist_foreach (priv->services, (GFunc) g_object_unref, NULL);
+	if (!priv->disposed) {
+		priv->disposed = TRUE;
+
+		if (priv->monitor) {
+			if (priv->monitor_id)
+				g_signal_handler_disconnect (priv->monitor, priv->monitor_id);
+			g_file_monitor_cancel (priv->monitor);
+			g_object_unref (priv->monitor);
+		}
+
+		g_hash_table_destroy (priv->services);
+	}
 
-	G_OBJECT_CLASS (nm_vpn_manager_parent_class)->finalize (object);
+	G_OBJECT_CLASS (nm_vpn_manager_parent_class)->dispose (object);
 }
 
 static void
@@ -343,7 +507,7 @@ nm_vpn_manager_class_init (NMVPNManagerClass *manager_class)
 	g_type_class_add_private (manager_class, sizeof (NMVPNManagerPrivate));
 
 	/* virtual methods */
-	object_class->finalize = finalize;
+	object_class->dispose = dispose;
 
 	/* signals */
 	signals[CONNECTION_ACTIVATED] =