summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLubomir Rintel <lkundrak@v3.sk>2015-08-19 18:55:53 +0200
committerLubomir Rintel <lkundrak@v3.sk>2015-08-26 16:55:59 +0200
commit359412f61d0f47dd8e51ab4512bdbfae1c87a81e (patch)
tree409996104ac743b4c408f05b3742136e2fc3969a
parent894b74ce5e55d26aa9a809c45283de88c514290b (diff)
downloadNetworkManager-lr/multiple-vpn-1.tar.gz
vpn-manager: support multiple VPN connections of the same typelr/multiple-vpn-1
A separate instance of the support plugin is spawned for each connection with a different bus name. The bus name is passed via --bus-name <name> argument. Plugins that support the feature indicate it with support-multiple-connections=true key in the [VPN Connection] section. The bus name is currently generated by adding a .<connection.uuid> to the VPN service name. It's guarranteed unique, but if it proves to be too long or ugly it can easily be replaced with something more meaningful (such as the same number as is used for connection's DBus name). NMVpnService has been removed and folded into NMVpnConnection. A NMVpnConnection will spawn a service plugin instance whenever it is activated and notices the bus name it needs is not provided. The NMVpnManager no longer needs to keep track of the connections in use apart for compatibility purposes with plugins that don't support the feature.
-rw-r--r--src/Makefile.am2
-rw-r--r--src/vpn-manager/nm-vpn-connection.c177
-rw-r--r--src/vpn-manager/nm-vpn-connection.h5
-rw-r--r--src/vpn-manager/nm-vpn-manager.c128
-rw-r--r--src/vpn-manager/nm-vpn-service.c356
-rw-r--r--src/vpn-manager/nm-vpn-service.h60
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 */