diff options
-rw-r--r-- | src/Makefile.am | 2 | ||||
-rw-r--r-- | src/vpn-manager/nm-vpn-connection.c | 177 | ||||
-rw-r--r-- | src/vpn-manager/nm-vpn-connection.h | 5 | ||||
-rw-r--r-- | src/vpn-manager/nm-vpn-manager.c | 128 | ||||
-rw-r--r-- | src/vpn-manager/nm-vpn-service.c | 356 | ||||
-rw-r--r-- | src/vpn-manager/nm-vpn-service.h | 60 |
6 files changed, 215 insertions, 513 deletions
diff --git a/src/Makefile.am b/src/Makefile.am index 0a60fb9745..734f4d62b3 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -297,8 +297,6 @@ libNetworkManager_la_SOURCES = \ vpn-manager/nm-vpn-connection.h \ vpn-manager/nm-vpn-manager.c \ vpn-manager/nm-vpn-manager.h \ - vpn-manager/nm-vpn-service.c \ - vpn-manager/nm-vpn-service.h \ \ nm-activation-request.c \ nm-activation-request.h \ diff --git a/src/vpn-manager/nm-vpn-connection.c b/src/vpn-manager/nm-vpn-connection.c index 4016a493d3..47717fb679 100644 --- a/src/vpn-manager/nm-vpn-connection.c +++ b/src/vpn-manager/nm-vpn-connection.c @@ -42,6 +42,8 @@ #include "nm-default-route-manager.h" #include "nm-route-manager.h" #include "nm-firewall-manager.h" +#include "nm-vpn-plugin-info.h" +#include "nm-vpn-manager.h" #include "nmdbus-vpn-connection.h" @@ -89,6 +91,10 @@ typedef struct { NMVpnConnectionStateReason failure_reason; NMVpnServiceState service_state; + guint start_timeout; + gboolean service_running; + NMVpnPluginInfo *plugin_info; + char *bus_name; /* Firewall */ NMFirewallPendingCall fw_call; @@ -276,6 +282,9 @@ vpn_cleanup (NMVpnConnection *connection, NMDevice *parent_dev) priv->ip_iface = NULL; priv->ip_ifindex = 0; + g_free (priv->bus_name); + priv->bus_name = NULL; + /* Clear out connection secrets to ensure that the settings service * gets asked for them next time the connection is activated. */ @@ -1750,6 +1759,116 @@ ip6_config_cb (GDBusProxy *proxy, } static void +_name_owner_changed (GObject *object, + GParamSpec *pspec, + gpointer user_data) +{ + NMVpnConnection *self = NM_VPN_CONNECTION (user_data); + NMVpnConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (self); + char *owner; + + owner = g_dbus_proxy_get_name_owner (G_DBUS_PROXY (object)); + + if (owner && !priv->service_running) { + /* service appeared */ + priv->service_running = TRUE; + nm_log_info (LOGD_VPN, "VPN connection '%s' saw the service appear; activating connection", + nm_connection_get_id (priv->connection)); + + /* No need to wait for the timeout any longer */ + nm_clear_g_source (&priv->start_timeout); + + /* Expect success because the VPN service has already appeared */ + _nm_dbus_signal_connect (priv->proxy, "Failure", G_VARIANT_TYPE ("(u)"), + G_CALLBACK (failure_cb), self); + _nm_dbus_signal_connect (priv->proxy, "StateChanged", G_VARIANT_TYPE ("(u)"), + G_CALLBACK (state_changed_cb), self); + _nm_dbus_signal_connect (priv->proxy, "SecretsRequired", G_VARIANT_TYPE ("(sas)"), + G_CALLBACK (secrets_required_cb), self); + _nm_dbus_signal_connect (priv->proxy, "Config", G_VARIANT_TYPE ("(a{sv})"), + G_CALLBACK (config_cb), self); + _nm_dbus_signal_connect (priv->proxy, "Ip4Config", G_VARIANT_TYPE ("(a{sv})"), + G_CALLBACK (ip4_config_cb), self); + _nm_dbus_signal_connect (priv->proxy, "Ip6Config", G_VARIANT_TYPE ("(a{sv})"), + G_CALLBACK (ip6_config_cb), self); + + _set_vpn_state (self, STATE_NEED_AUTH, NM_VPN_CONNECTION_STATE_REASON_NONE, FALSE); + + /* Kick off the secrets requests; first we get existing system secrets + * and ask the plugin if these are sufficient, next we get all existing + * secrets from system and from user agents and ask the plugin again, + * and last we ask the user for new secrets if required. + */ + get_secrets (self, SECRETS_REQ_SYSTEM, NULL); + } else if (!owner && priv->service_running) { + /* service went away */ + priv->service_running = FALSE; + nm_log_info (LOGD_VPN, "VPN connection '%s' service disappeared", + nm_vpn_connection_get_connection_id (self)); + nm_vpn_connection_disconnect (self, NM_VPN_CONNECTION_STATE_REASON_SERVICE_STOPPED, FALSE); + } + + g_free (owner); +} + + +static gboolean +_daemon_exec_timeout (gpointer data) +{ + NMVpnConnection *self = NM_VPN_CONNECTION (data); + NMVpnConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (self); + + nm_log_warn (LOGD_VPN, "VPN connection '%s' timed out waiting for the service to start", + nm_vpn_connection_get_connection_id (self)); + priv->start_timeout = 0; + nm_vpn_connection_disconnect (self, NM_VPN_CONNECTION_STATE_REASON_SERVICE_START_TIMEOUT, FALSE); + + g_object_unref (self); + + return G_SOURCE_REMOVE; +} + +static gboolean +nm_vpn_service_daemon_exec (NMVpnConnection *self, GError **error) +{ + NMVpnConnectionPrivate *priv; + GPid pid; + char *vpn_argv[4]; + gboolean success = FALSE; + GError *spawn_error = NULL; + int i = 0; + + g_return_val_if_fail (NM_IS_VPN_CONNECTION (self), FALSE); + priv = NM_VPN_CONNECTION_GET_PRIVATE (self); + + vpn_argv[i++] = (char *) nm_vpn_plugin_info_get_program (priv->plugin_info); + if (nm_vpn_plugin_info_supports_multiple (priv->plugin_info)) { + vpn_argv[i++] = "--bus-name"; + vpn_argv[i++] = priv->bus_name; + } + vpn_argv[i] = NULL; + g_assert (vpn_argv[0]); + + success = g_spawn_async (NULL, vpn_argv, NULL, 0, nm_utils_setpgid, NULL, &pid, &spawn_error); + + if (success) { + nm_log_info (LOGD_VPN, "VPN connection '%s' started the service, PID %ld", + nm_vpn_connection_get_connection_id (self), + (long int) pid); + priv->start_timeout = g_timeout_add_seconds (5, _daemon_exec_timeout, g_object_ref (self)); + } else { + g_set_error (error, + NM_MANAGER_ERROR, NM_MANAGER_ERROR_FAILED, + "%s", spawn_error ? spawn_error->message : "unknown g_spawn_async() error"); + + if (spawn_error) + g_error_free (spawn_error); + } + + return success; +} + +static void on_proxy_acquired (GObject *object, GAsyncResult *result, gpointer user_data) { NMVpnConnection *self; @@ -1777,36 +1896,32 @@ on_proxy_acquired (GObject *object, GAsyncResult *result, gpointer user_data) } priv->proxy = proxy; - _nm_dbus_signal_connect (priv->proxy, "Failure", G_VARIANT_TYPE ("(u)"), - G_CALLBACK (failure_cb), self); - _nm_dbus_signal_connect (priv->proxy, "StateChanged", G_VARIANT_TYPE ("(u)"), - G_CALLBACK (state_changed_cb), self); - _nm_dbus_signal_connect (priv->proxy, "SecretsRequired", G_VARIANT_TYPE ("(sas)"), - G_CALLBACK (secrets_required_cb), self); - _nm_dbus_signal_connect (priv->proxy, "Config", G_VARIANT_TYPE ("(a{sv})"), - G_CALLBACK (config_cb), self); - _nm_dbus_signal_connect (priv->proxy, "Ip4Config", G_VARIANT_TYPE ("(a{sv})"), - G_CALLBACK (ip4_config_cb), self); - _nm_dbus_signal_connect (priv->proxy, "Ip6Config", G_VARIANT_TYPE ("(a{sv})"), - G_CALLBACK (ip6_config_cb), self); - _set_vpn_state (self, STATE_NEED_AUTH, NM_VPN_CONNECTION_STATE_REASON_NONE, FALSE); + g_signal_connect (priv->proxy, "notify::g-name-owner", + G_CALLBACK (_name_owner_changed), self); + _name_owner_changed (G_OBJECT (priv->proxy), NULL, self); - /* Kick off the secrets requests; first we get existing system secrets - * and ask the plugin if these are sufficient, next we get all existing - * secrets from system and from user agents and ask the plugin again, - * and last we ask the user for new secrets if required. - */ - get_secrets (self, SECRETS_REQ_SYSTEM, NULL); + if (priv->service_running) + return; + + if (!nm_vpn_service_daemon_exec (self, &error)) { + nm_log_warn (LOGD_VPN, "VPN connection '%s': could not launch the VPN service. error: %s.", + nm_vpn_connection_get_connection_id (self), + error->message); + + nm_vpn_connection_disconnect (self, NM_VPN_CONNECTION_STATE_REASON_SERVICE_START_FAILED, FALSE); + } } -void -nm_vpn_connection_activate (NMVpnConnection *self) +gboolean +nm_vpn_connection_activate (NMVpnConnection *self, + NMVpnPluginInfo *plugin_info, + GError **error) { NMVpnConnectionPrivate *priv; NMSettingVpn *s_vpn; - g_return_if_fail (NM_IS_VPN_CONNECTION (self)); + g_return_val_if_fail (NM_IS_VPN_CONNECTION (self), FALSE); priv = NM_VPN_CONNECTION_GET_PRIVATE (self); @@ -1814,18 +1929,30 @@ nm_vpn_connection_activate (NMVpnConnection *self) g_assert (s_vpn); priv->connection_can_persist = nm_setting_vpn_get_persistent (s_vpn); - _set_vpn_state (self, STATE_PREPARE, NM_VPN_CONNECTION_STATE_REASON_NONE, FALSE); + priv->plugin_info = g_object_ref (plugin_info); + if (nm_vpn_plugin_info_supports_multiple (priv->plugin_info)) { + priv->bus_name = g_strdup_printf ("%s.%s", + nm_vpn_connection_get_service (self), + nm_connection_get_uuid (priv->connection)); + } else { + priv->bus_name = g_strdup (nm_vpn_connection_get_service (self)); + } priv->cancellable = g_cancellable_new (); + g_dbus_proxy_new_for_bus (G_BUS_TYPE_SYSTEM, G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES, NULL, - nm_vpn_connection_get_service (self), + priv->bus_name, NM_VPN_DBUS_PLUGIN_PATH, NM_VPN_DBUS_PLUGIN_INTERFACE, priv->cancellable, (GAsyncReadyCallback) on_proxy_acquired, self); + + _set_vpn_state (self, STATE_PREPARE, NM_VPN_CONNECTION_STATE_REASON_NONE, FALSE); + + return TRUE; } NMConnection * @@ -2250,6 +2377,7 @@ dispose (GObject *object) g_clear_object (&priv->connection); g_clear_object (&priv->default_route_manager); g_clear_object (&priv->route_manager); + g_clear_object (&priv->plugin_info); fw_call_cleanup (NM_VPN_CONNECTION (object)); @@ -2372,4 +2500,3 @@ nm_vpn_connection_class_init (NMVpnConnectionClass *connection_class) NMDBUS_TYPE_VPN_CONNECTION_SKELETON, NULL); } - diff --git a/src/vpn-manager/nm-vpn-connection.h b/src/vpn-manager/nm-vpn-connection.h index a41c2630f4..46e62c9727 100644 --- a/src/vpn-manager/nm-vpn-connection.h +++ b/src/vpn-manager/nm-vpn-connection.h @@ -26,6 +26,7 @@ #include "nm-device.h" #include "nm-auth-subject.h" #include "nm-active-connection.h" +#include "nm-vpn-plugin-info.h" #define NM_TYPE_VPN_CONNECTION (nm_vpn_connection_get_type ()) #define NM_VPN_CONNECTION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_VPN_CONNECTION, NMVpnConnection)) @@ -75,7 +76,9 @@ NMVpnConnection * nm_vpn_connection_new (NMConnection *connection, const char *specific_object, NMAuthSubject *subject); -void nm_vpn_connection_activate (NMVpnConnection *connection); +gboolean nm_vpn_connection_activate (NMVpnConnection *connection, + NMVpnPluginInfo *plugin_info, + GError **error); NMConnection * nm_vpn_connection_get_connection (NMVpnConnection *connection); const char* nm_vpn_connection_get_connection_id (NMVpnConnection *connection); NMVpnConnectionState nm_vpn_connection_get_vpn_state (NMVpnConnection *connection); diff --git a/src/vpn-manager/nm-vpn-manager.c b/src/vpn-manager/nm-vpn-manager.c index d7947b4621..9b1db587f4 100644 --- a/src/vpn-manager/nm-vpn-manager.c +++ b/src/vpn-manager/nm-vpn-manager.c @@ -25,7 +25,7 @@ #include "nm-default.h" #include "nm-vpn-manager.h" -#include "nm-vpn-service.h" +#include "nm-vpn-plugin-info.h" #include "nm-vpn-connection.h" #include "nm-setting-vpn.h" #include "nm-vpn-dbus-interface.h" @@ -37,26 +37,31 @@ 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; + GSList *plugins; GFileMonitor *monitor_etc; GFileMonitor *monitor_lib; guint monitor_id_etc; guint monitor_id_lib; -} NMVpnManagerPrivate; -static NMVpnService * -_plugin_info_get_service (NMVpnPluginInfo *plugin_info) -{ - if (plugin_info) - return NM_VPN_SERVICE (g_object_get_data (G_OBJECT (plugin_info), "service-instance")); - return NULL; -} + /* This is only used for services that don't support multiple + * connections, to guard access to them. */ + GHashTable *active_services; +} NMVpnManagerPrivate; static void -_plugin_info_set_service (NMVpnPluginInfo *plugin_info, NMVpnService *service) +vpn_state_changed (NMVpnConnection *vpn, + GParamSpec *pspec, + NMVpnManager *manager) { - g_object_set_data_full (G_OBJECT (plugin_info), "service-instance", service, - (GDestroyNotify) g_object_unref); + NMVpnManagerPrivate *priv = NM_VPN_MANAGER_GET_PRIVATE (manager); + NMActiveConnectionState state = nm_active_connection_get_state (NM_ACTIVE_CONNECTION (vpn)); + const char *service_name = nm_vpn_connection_get_service (vpn); + + if (state == NM_ACTIVE_CONNECTION_STATE_DEACTIVATED) { + g_hash_table_remove (priv->active_services, service_name); + g_signal_handlers_disconnect_by_func (vpn, vpn_state_changed, manager); + g_object_unref (manager); + } } gboolean @@ -64,9 +69,7 @@ nm_vpn_manager_activate_connection (NMVpnManager *manager, NMVpnConnection *vpn, GError **error) { - NMConnection *connection; - NMSettingVpn *s_vpn; - NMVpnService *service; + NMVpnManagerPrivate *priv; NMVpnPluginInfo *plugin_info; const char *service_name; NMDevice *device; @@ -76,6 +79,7 @@ nm_vpn_manager_activate_connection (NMVpnManager *manager, g_return_val_if_fail (error != NULL, FALSE); g_return_val_if_fail (*error == NULL, FALSE); + priv = NM_VPN_MANAGER_GET_PRIVATE (manager); device = nm_active_connection_get_device (NM_ACTIVE_CONNECTION (vpn)); g_assert (device); if ( nm_device_get_state (device) != NM_DEVICE_STATE_ACTIVATED @@ -85,22 +89,36 @@ nm_vpn_manager_activate_connection (NMVpnManager *manager, return FALSE; } - connection = nm_active_connection_get_connection (NM_ACTIVE_CONNECTION (vpn)); - g_assert (connection); - s_vpn = nm_connection_get_setting_vpn (connection); - g_assert (s_vpn); + service_name = nm_vpn_connection_get_service (vpn); - service_name = nm_setting_vpn_get_service_type (s_vpn); - plugin_info = nm_vpn_plugin_info_list_find_by_service (NM_VPN_MANAGER_GET_PRIVATE (manager)->services, service_name); - service = _plugin_info_get_service (plugin_info); - if (!service) { + plugin_info = nm_vpn_plugin_info_list_find_by_service (priv->plugins, service_name); + if (!plugin_info) { g_set_error (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_CONNECTION_NOT_AVAILABLE, "The VPN service '%s' was not installed.", service_name); return FALSE; } - return nm_vpn_service_activate (service, vpn, error); + if ( !nm_vpn_plugin_info_supports_multiple (plugin_info) + && g_hash_table_lookup (priv->active_services, service_name)) { + g_set_error (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_CONNECTION_NOT_AVAILABLE, + "The '%s' plugin only supports a single active connection.", + nm_vpn_plugin_info_get_name (plugin_info)); + return FALSE; + } + + if (!nm_vpn_connection_activate (vpn, plugin_info, error)) + return FALSE; + + if (!nm_vpn_plugin_info_supports_multiple (plugin_info)) { + /* Block activations of the connections of the same service type. */ + g_hash_table_insert (priv->active_services, g_strdup (service_name), g_object_ref (vpn)); + g_signal_connect (vpn, "notify::" NM_ACTIVE_CONNECTION_STATE, + G_CALLBACK (vpn_state_changed), + g_object_ref (manager)); + } + + return TRUE; } gboolean @@ -112,34 +130,18 @@ nm_vpn_manager_deactivate_connection (NMVpnManager *self, } static void -try_add_service (NMVpnManager *self, NMVpnPluginInfo *plugin_info) +try_add_plugin (NMVpnManager *self, NMVpnPluginInfo *plugin_info) { NMVpnManagerPrivate *priv = NM_VPN_MANAGER_GET_PRIVATE (self); - NMVpnService *service = NULL; - GError *error = NULL; /* Make sure we don't add dupes. * We don't really allow reload of the same file. What we do allow is however to * delete a file and re-add it. */ - if (nm_vpn_plugin_info_list_find_by_filename (priv->services, + if (nm_vpn_plugin_info_list_find_by_filename (priv->plugins, nm_vpn_plugin_info_get_filename (plugin_info))) return; - if (!nm_vpn_plugin_info_list_add (&priv->services, plugin_info, NULL)) + if (!nm_vpn_plugin_info_list_add (&priv->plugins, plugin_info, NULL)) return; - - /* New service */ - service = nm_vpn_service_new (plugin_info, &error); - if (service) { - _plugin_info_set_service (plugin_info, service); - nm_log_info (LOGD_VPN, "VPN: loaded %s - %s", - nm_vpn_plugin_info_get_name (plugin_info), - nm_vpn_service_get_dbus_service (service)); - } else { - nm_log_warn (LOGD_VPN, "failed to load VPN service file %s: %s", - nm_vpn_plugin_info_get_filename (plugin_info), - error && error->message ? error->message : "(unknown)"); - g_clear_error (&error); - } } static void @@ -152,7 +154,6 @@ vpn_dir_changed (GFileMonitor *monitor, NMVpnManager *self = NM_VPN_MANAGER (user_data); NMVpnManagerPrivate *priv = NM_VPN_MANAGER_GET_PRIVATE (self); NMVpnPluginInfo *plugin_info; - NMVpnService *service; gs_free char *path = NULL; GError *error = NULL; @@ -162,25 +163,16 @@ vpn_dir_changed (GFileMonitor *monitor, switch (event_type) { case G_FILE_MONITOR_EVENT_DELETED: - plugin_info = nm_vpn_plugin_info_list_find_by_filename (priv->services, path); + plugin_info = nm_vpn_plugin_info_list_find_by_filename (priv->plugins, path); if (!plugin_info) break; nm_log_dbg (LOGD_VPN, "vpn: service file %s deleted", path); - service = _plugin_info_get_service (plugin_info); - if (service) { - /* Stop active VPN connections and destroy the service */ - nm_vpn_service_stop_connections (service, FALSE, - NM_VPN_CONNECTION_STATE_REASON_SERVICE_STOPPED); - nm_log_info (LOGD_VPN, "VPN: unloaded %s", nm_vpn_service_get_dbus_service (service)); - - _plugin_info_set_service (plugin_info, NULL); - } - nm_vpn_plugin_info_list_remove (&priv->services, plugin_info); + nm_vpn_plugin_info_list_remove (&priv->plugins, plugin_info); break; case G_FILE_MONITOR_EVENT_CREATED: case G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT: - plugin_info = nm_vpn_plugin_info_list_find_by_filename (priv->services, path); + plugin_info = nm_vpn_plugin_info_list_find_by_filename (priv->plugins, path); if (plugin_info) { /* we don't support reloading an existing plugin. You can only remove the file * and re-add it. By reloading we want to support the use case of installing @@ -203,7 +195,7 @@ vpn_dir_changed (GFileMonitor *monitor, } nm_log_dbg (LOGD_VPN, "vpn: service file %s created or modified", path); - try_add_service (self, plugin_info); + try_add_plugin (self, plugin_info); g_object_unref (plugin_info); break; default: @@ -248,13 +240,15 @@ nm_vpn_manager_init (NMVpnManager *self) * In case of no-conflict, the order doesn't matter. */ infos = _nm_vpn_plugin_info_list_load_dir (conf_dir_lib, TRUE, 0, NULL, NULL); for (info = infos; info; info = info->next) - try_add_service (self, info->data); + try_add_plugin (self, info->data); g_slist_free_full (infos, g_object_unref); infos = _nm_vpn_plugin_info_list_load_dir (conf_dir_etc, TRUE, 0, NULL, NULL); for (info = infos; info; info = info->next) - try_add_service (self, info->data); + try_add_plugin (self, info->data); g_slist_free_full (infos, g_object_unref); + + priv->active_services = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref); } static void @@ -276,17 +270,13 @@ dispose (GObject *object) g_clear_object (&priv->monitor_lib); } - while (priv->services) { - NMVpnPluginInfo *plugin_info = priv->services->data; - NMVpnService *service = _plugin_info_get_service (plugin_info); - - if (service) { - nm_vpn_service_stop_connections (service, TRUE, NM_VPN_CONNECTION_STATE_REASON_SERVICE_STOPPED); - _plugin_info_set_service (plugin_info, NULL); - } - nm_vpn_plugin_info_list_remove (&priv->services, plugin_info); + while (priv->plugins) { + NMVpnPluginInfo *plugin_info = priv->plugins->data; + nm_vpn_plugin_info_list_remove (&priv->plugins, plugin_info); } + g_hash_table_unref (priv->active_services); + G_OBJECT_CLASS (nm_vpn_manager_parent_class)->dispose (object); } diff --git a/src/vpn-manager/nm-vpn-service.c b/src/vpn-manager/nm-vpn-service.c deleted file mode 100644 index c96242c940..0000000000 --- a/src/vpn-manager/nm-vpn-service.c +++ /dev/null @@ -1,356 +0,0 @@ -/* -*- 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) 2005 - 2014 Red Hat, Inc. - * Copyright (C) 2005 - 2008 Novell, Inc. - */ - -#include "config.h" - -#include <string.h> -#include <sys/types.h> -#include <sys/wait.h> -#include <signal.h> -#include <unistd.h> - -#include "nm-default.h" -#include "nm-vpn-service.h" -#include "nm-vpn-manager.h" - -G_DEFINE_TYPE (NMVpnService, nm_vpn_service, G_TYPE_OBJECT) - -typedef struct { - NMVpnPluginInfo *plugin_info; - - NMVpnConnection *active; - GSList *pending; - - guint start_timeout; - GDBusProxy *proxy; - gboolean service_running; -} NMVpnServicePrivate; - -#define NM_VPN_SERVICE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_VPN_SERVICE, NMVpnServicePrivate)) - -static gboolean start_pending_vpn (NMVpnService *self, GError **error); - -static void _name_owner_changed (GObject *object, GParamSpec *pspec, gpointer user_data); - -NMVpnService * -nm_vpn_service_new (NMVpnPluginInfo *plugin_info, GError **error) -{ - NMVpnService *self; - NMVpnServicePrivate *priv; - - g_return_val_if_fail (NM_IS_VPN_PLUGIN_INFO (plugin_info), NULL); - g_return_val_if_fail (nm_vpn_plugin_info_get_filename (plugin_info), NULL); - - if (!nm_vpn_plugin_info_get_program (plugin_info)) { - g_set_error (error, - NM_VPN_PLUGIN_ERROR, - NM_VPN_PLUGIN_ERROR_FAILED, - "missing \"program\" entry"); - return NULL; - } - - self = (NMVpnService *) g_object_new (NM_TYPE_VPN_SERVICE, NULL); - priv = NM_VPN_SERVICE_GET_PRIVATE (self); - priv->plugin_info = g_object_ref (plugin_info); - - priv->proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM, - G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES | - G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS | - G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START, - NULL, - nm_vpn_plugin_info_get_service (plugin_info), - NM_VPN_DBUS_PLUGIN_PATH, - NM_VPN_DBUS_PLUGIN_INTERFACE, - NULL, error); - if (!priv->proxy) { - g_object_unref (self); - return NULL; - } - - g_signal_connect (priv->proxy, "notify::g-name-owner", - G_CALLBACK (_name_owner_changed), self); - _name_owner_changed (G_OBJECT (priv->proxy), NULL, self); - - return self; -} - -const char * -nm_vpn_service_get_dbus_service (NMVpnService *service) -{ - g_return_val_if_fail (NM_IS_VPN_SERVICE (service), NULL); - - return nm_vpn_plugin_info_get_service (NM_VPN_SERVICE_GET_PRIVATE (service)->plugin_info); -} - -static void -connection_vpn_state_changed (NMVpnConnection *connection, - NMVpnConnectionState new_state, - NMVpnConnectionState old_state, - NMVpnConnectionStateReason reason, - gpointer user_data) -{ - NMVpnService *self = NM_VPN_SERVICE (user_data); - NMVpnServicePrivate *priv = NM_VPN_SERVICE_GET_PRIVATE (self); - - if (new_state == NM_VPN_CONNECTION_STATE_FAILED || - new_state == NM_VPN_CONNECTION_STATE_DISCONNECTED) { - g_signal_handlers_disconnect_by_func (connection, G_CALLBACK (connection_vpn_state_changed), self); - if (connection == priv->active) { - priv->active = NULL; - start_pending_vpn (self, NULL); - } else - priv->pending = g_slist_remove (priv->pending, connection); - g_object_unref (connection); - } -} - -void -nm_vpn_service_stop_connections (NMVpnService *service, - gboolean quitting, - NMVpnConnectionStateReason reason) -{ - NMVpnServicePrivate *priv = NM_VPN_SERVICE_GET_PRIVATE (service); - GSList *iter; - - /* Just add priv->active to the beginning of priv->pending, - * since we're going to clear priv->pending immediately anyway. - */ - if (priv->active) { - priv->pending = g_slist_prepend (priv->pending, priv->active); - priv->active = NULL; - } - - for (iter = priv->pending; iter; iter = iter->next) { - NMVpnConnection *vpn = NM_VPN_CONNECTION (iter->data); - - g_signal_handlers_disconnect_by_func (vpn, G_CALLBACK (connection_vpn_state_changed), service); - if (quitting) { - /* Deactivate to allow pre-down before disconnecting */ - nm_vpn_connection_deactivate (vpn, reason, quitting); - } - nm_vpn_connection_disconnect (vpn, reason, quitting); - g_object_unref (vpn); - } - g_clear_pointer (&priv->pending, g_slist_free); -} - -static gboolean -_daemon_exec_timeout (gpointer data) -{ - NMVpnService *self = NM_VPN_SERVICE (data); - NMVpnServicePrivate *priv = NM_VPN_SERVICE_GET_PRIVATE (self); - - nm_log_warn (LOGD_VPN, "VPN service '%s' start timed out", nm_vpn_plugin_info_get_name (priv->plugin_info)); - priv->start_timeout = 0; - nm_vpn_service_stop_connections (self, FALSE, NM_VPN_CONNECTION_STATE_REASON_SERVICE_START_TIMEOUT); - return G_SOURCE_REMOVE; -} - -static gboolean -nm_vpn_service_daemon_exec (NMVpnService *service, GError **error) -{ - NMVpnServicePrivate *priv = NM_VPN_SERVICE_GET_PRIVATE (service); - GPid pid; - char *vpn_argv[2]; - gboolean success = FALSE; - GError *spawn_error = NULL; - - g_return_val_if_fail (NM_IS_VPN_SERVICE (service), FALSE); - - vpn_argv[0] = (char *) nm_vpn_plugin_info_get_program (priv->plugin_info); - vpn_argv[1] = NULL; - - g_assert (vpn_argv[0]); - - success = g_spawn_async (NULL, vpn_argv, NULL, 0, nm_utils_setpgid, NULL, &pid, &spawn_error); - if (success) { - nm_log_info (LOGD_VPN, "VPN service '%s' started (%s), PID %ld", - nm_vpn_plugin_info_get_name (priv->plugin_info), - nm_vpn_service_get_dbus_service (service), - (long int) pid); - priv->start_timeout = g_timeout_add_seconds (5, _daemon_exec_timeout, service); - } else { - nm_log_warn (LOGD_VPN, "VPN service '%s': could not launch the VPN service. error: (%d) %s.", - nm_vpn_plugin_info_get_name (priv->plugin_info), - spawn_error ? spawn_error->code : -1, - spawn_error && spawn_error->message ? spawn_error->message : "(unknown)"); - - g_set_error (error, - NM_MANAGER_ERROR, NM_MANAGER_ERROR_FAILED, - "%s", spawn_error ? spawn_error->message : "unknown g_spawn_async() error"); - - nm_vpn_service_stop_connections (service, FALSE, NM_VPN_CONNECTION_STATE_REASON_SERVICE_START_FAILED); - if (spawn_error) - g_error_free (spawn_error); - } - - return success; -} - -static gboolean -start_active_vpn (NMVpnService *self, GError **error) -{ - NMVpnServicePrivate *priv = NM_VPN_SERVICE_GET_PRIVATE (self); - - if (!priv->active) - return TRUE; - - if (priv->service_running) { - /* Just activate the VPN */ - nm_vpn_connection_activate (priv->active); - return TRUE; - } else if (priv->start_timeout == 0) { - /* VPN service not running, start it */ - nm_log_info (LOGD_VPN, "Starting VPN service '%s'...", nm_vpn_plugin_info_get_name (priv->plugin_info)); - return nm_vpn_service_daemon_exec (self, error); - } - - /* Already started VPN service, waiting for it to appear on D-Bus */ - return TRUE; -} - -static gboolean -start_pending_vpn (NMVpnService *self, GError **error) -{ - NMVpnServicePrivate *priv = NM_VPN_SERVICE_GET_PRIVATE (self); - - g_assert (priv->active == NULL); - - if (!priv->pending) - return TRUE; - - /* Make next VPN active */ - priv->active = g_slist_nth_data (priv->pending, 0); - priv->pending = g_slist_remove (priv->pending, priv->active); - - return start_active_vpn (self, error); -} - -gboolean -nm_vpn_service_activate (NMVpnService *service, - NMVpnConnection *vpn, - GError **error) -{ - NMVpnServicePrivate *priv; - - g_return_val_if_fail (NM_IS_VPN_SERVICE (service), FALSE); - g_return_val_if_fail (NM_IS_VPN_CONNECTION (vpn), FALSE); - g_return_val_if_fail (error != NULL, FALSE); - g_return_val_if_fail (*error == NULL, FALSE); - - priv = NM_VPN_SERVICE_GET_PRIVATE (service); - - g_signal_connect (vpn, NM_VPN_CONNECTION_INTERNAL_STATE_CHANGED, - G_CALLBACK (connection_vpn_state_changed), - service); - - /* Queue up the new VPN connection */ - priv->pending = g_slist_append (priv->pending, g_object_ref (vpn)); - - /* Tell the active VPN to deactivate and wait for it to quit before we - * start the next VPN. The just-queued VPN will then be started from - * connection_vpn_state_changed(). - */ - if (priv->active) { - nm_vpn_connection_deactivate (priv->active, NM_VPN_CONNECTION_STATE_REASON_USER_DISCONNECTED, FALSE); - return TRUE; - } - - /* Otherwise start the next VPN */ - return start_pending_vpn (service, error); -} - -static void -_name_owner_changed (GObject *object, - GParamSpec *pspec, - gpointer user_data) -{ - NMVpnService *service = NM_VPN_SERVICE (user_data); - NMVpnServicePrivate *priv = NM_VPN_SERVICE_GET_PRIVATE (service); - gboolean success; - char *owner; - - owner = g_dbus_proxy_get_name_owner (G_DBUS_PROXY (object)); - - /* Service changed, no need to wait for the timeout any longer */ - if (priv->start_timeout) { - g_source_remove (priv->start_timeout); - priv->start_timeout = 0; - } - - if (owner && !priv->service_running) { - /* service appeared */ - priv->service_running = TRUE; - nm_log_info (LOGD_VPN, "VPN service '%s' appeared; activating connections", nm_vpn_plugin_info_get_name (priv->plugin_info)); - /* Expect success because the VPN service has already appeared */ - success = start_active_vpn (service, NULL); - g_warn_if_fail (success); - } else if (!owner && priv->service_running) { - /* service went away */ - priv->service_running = FALSE; - nm_log_info (LOGD_VPN, "VPN service '%s' disappeared", nm_vpn_plugin_info_get_name (priv->plugin_info)); - nm_vpn_service_stop_connections (service, FALSE, NM_VPN_CONNECTION_STATE_REASON_SERVICE_STOPPED); - } - - g_free (owner); -} - -/******************************************************************************/ - -static void -nm_vpn_service_init (NMVpnService *self) -{ -} - -static void -dispose (GObject *object) -{ - NMVpnService *self = NM_VPN_SERVICE (object); - NMVpnServicePrivate *priv = NM_VPN_SERVICE_GET_PRIVATE (self); - - nm_clear_g_source (&priv->start_timeout); - - g_clear_object (&priv->plugin_info); - - /* VPNService owner is required to stop connections before releasing */ - g_assert (priv->active == NULL); - g_assert (priv->pending == NULL); - - if (priv->proxy) { - g_signal_handlers_disconnect_by_func (priv->proxy, - G_CALLBACK (_name_owner_changed), - self); - g_clear_object (&priv->proxy); - } - - G_OBJECT_CLASS (nm_vpn_service_parent_class)->dispose (object); -} - -static void -nm_vpn_service_class_init (NMVpnServiceClass *service_class) -{ - GObjectClass *object_class = G_OBJECT_CLASS (service_class); - - g_type_class_add_private (service_class, sizeof (NMVpnServicePrivate)); - - /* virtual methods */ - object_class->dispose = dispose; -} diff --git a/src/vpn-manager/nm-vpn-service.h b/src/vpn-manager/nm-vpn-service.h deleted file mode 100644 index c43d7cd428..0000000000 --- a/src/vpn-manager/nm-vpn-service.h +++ /dev/null @@ -1,60 +0,0 @@ -/* -*- 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) 2005 - 2011 Red Hat, Inc. - * Copyright (C) 2005 - 2008 Novell, Inc. - */ - -#ifndef __NETWORKMANAGER_VPN_SERVICE_H__ -#define __NETWORKMANAGER_VPN_SERVICE_H__ - -#include "nm-default.h" -#include "nm-device.h" -#include "nm-vpn-connection.h" -#include "nm-vpn-plugin-info.h" - -#define NM_TYPE_VPN_SERVICE (nm_vpn_service_get_type ()) -#define NM_VPN_SERVICE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_VPN_SERVICE, NMVpnService)) -#define NM_VPN_SERVICE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_VPN_SERVICE, NMVpnServiceClass)) -#define NM_IS_VPN_SERVICE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_VPN_SERVICE)) -#define NM_IS_VPN_SERVICE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_VPN_SERVICE)) -#define NM_VPN_SERVICE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_VPN_SERVICE, NMVpnServiceClass)) - -typedef struct { - GObject parent; -} NMVpnService; - -typedef struct { - GObjectClass parent; -} NMVpnServiceClass; - -GType nm_vpn_service_get_type (void); - -NMVpnService * nm_vpn_service_new (NMVpnPluginInfo *plugin_info, GError **error); - -/* Returns the VPN service's D-Bus service name */ -const char *nm_vpn_service_get_dbus_service (NMVpnService *service); - -gboolean nm_vpn_service_activate (NMVpnService *service, - NMVpnConnection *vpn, - GError **error); - -void nm_vpn_service_stop_connections (NMVpnService *service, - gboolean quitting, - NMVpnConnectionStateReason reason); - -#endif /* NM_VPN_VPN_SERVICE_H */ |