From 1bb553798c514e5ae1bf32a3cd87e96c4a656dfb Mon Sep 17 00:00:00 2001 From: Lubomir Rintel Date: Wed, 23 Sep 2015 09:08:25 +0200 Subject: [PATCH 1/8] libnm/vpn-service-plugin: add a missing return --- libnm/nm-vpn-service-plugin.c | 1 + 1 file changed, 1 insertion(+) diff --git a/libnm/nm-vpn-service-plugin.c b/libnm/nm-vpn-service-plugin.c index fc4cfdc038..2644732d96 100644 --- a/libnm/nm-vpn-service-plugin.c +++ b/libnm/nm-vpn-service-plugin.c @@ -443,6 +443,7 @@ _connect_generic (NMVpnServicePlugin *plugin, "Invalid connection: (%d) %s", error->code, error->message); g_clear_error (&error); + return; } priv->interactive = FALSE; From 9f15abbda786c3db0431a3a5cb82baf5bea87660 Mon Sep 17 00:00:00 2001 From: Lubomir Rintel Date: Wed, 23 Sep 2015 12:19:09 +0200 Subject: [PATCH 2/8] libnm/vpn-service-plugin: add watch-peer property Make it possible to construct the plugin instance in a way that disconnects the connection if the DBus client that activated it drops off the bus. This makes the plugins conveniently clean up when NetworkManager crashes. We need this, as with multiple VPN support we can loose track of the client bus names when the daemon crashes leaving to nice way to clean up on respawn. However, this behavior is not desired for debugging or hypotetical VPN plugin users other than NetworkManager (say; "gdbus call -m o.fd.NM.VPN.Plugin.Connect"). Let the plugin decide when to use it. --- libnm/nm-vpn-service-plugin.c | 65 +++++++++++++++++++++++++++++++++++ libnm/nm-vpn-service-plugin.h | 1 + 2 files changed, 66 insertions(+) diff --git a/libnm/nm-vpn-service-plugin.c b/libnm/nm-vpn-service-plugin.c index 2644732d96..d9f669c9ee 100644 --- a/libnm/nm-vpn-service-plugin.c +++ b/libnm/nm-vpn-service-plugin.c @@ -53,11 +53,13 @@ typedef struct { GDBusConnection *connection; NMDBusVpnPlugin *dbus_vpn_service_plugin; char *dbus_service_name; + gboolean dbus_watch_peer; /* Temporary stuff */ guint connect_timer; guint quit_timer; guint fail_stop_id; + guint peer_watch_id; gboolean interactive; gboolean got_config; @@ -88,6 +90,7 @@ static guint signals[LAST_SIGNAL] = { 0 }; enum { PROP_0, PROP_DBUS_SERVICE_NAME, + PROP_DBUS_WATCH_PEER, PROP_STATE, LAST_PROP @@ -184,11 +187,13 @@ nm_vpn_service_plugin_failure (NMVpnServicePlugin *plugin, gboolean nm_vpn_service_plugin_disconnect (NMVpnServicePlugin *plugin, GError **err) { + NMVpnServicePluginPrivate *priv; gboolean ret = FALSE; NMVpnServiceState state; g_return_val_if_fail (NM_IS_VPN_SERVICE_PLUGIN (plugin), FALSE); + priv = NM_VPN_SERVICE_PLUGIN_GET_PRIVATE (plugin); state = nm_vpn_service_plugin_get_state (plugin); switch (state) { case NM_VPN_SERVICE_STATE_STOPPING: @@ -207,6 +212,11 @@ nm_vpn_service_plugin_disconnect (NMVpnServicePlugin *plugin, GError **err) break; case NM_VPN_SERVICE_STATE_STARTING: case NM_VPN_SERVICE_STATE_STARTED: + if (priv->peer_watch_id) { + g_dbus_connection_signal_unsubscribe (nm_vpn_service_plugin_get_connection (plugin), + priv->peer_watch_id); + priv->peer_watch_id = 0; + } nm_vpn_service_plugin_set_state (plugin, NM_VPN_SERVICE_STATE_STOPPING); ret = NM_VPN_SERVICE_PLUGIN_GET_CLASS (plugin)->disconnect (plugin, err); nm_vpn_service_plugin_set_state (plugin, NM_VPN_SERVICE_STATE_STOPPED); @@ -413,6 +423,37 @@ connect_timer_start (NMVpnServicePlugin *plugin) priv->connect_timer = g_timeout_add_seconds (60, connect_timer_expired, plugin); } +static void +peer_vanished (GDBusConnection *connection, + const gchar *sender_name, + const gchar *object_path, + const gchar *interface_name, + const gchar *signal_name, + GVariant *parameters, + gpointer user_data) +{ + nm_vpn_service_plugin_disconnect (NM_VPN_SERVICE_PLUGIN (user_data), NULL); +} + +static guint +watch_peer (NMVpnServicePlugin *plugin, + GDBusMethodInvocation *context) +{ + GDBusConnection *connection = g_dbus_method_invocation_get_connection (context); + const gchar *peer = g_dbus_message_get_sender (g_dbus_method_invocation_get_message (context)); + + return g_dbus_connection_signal_subscribe (connection, + "org.freedesktop.DBus", + "org.freedesktop.DBus", + "NameOwnerChanged", + "/org/freedesktop/DBus", + peer, + G_DBUS_SIGNAL_FLAGS_NONE, + peer_vanished, + plugin, + NULL); +} + static void _connect_generic (NMVpnServicePlugin *plugin, GDBusMethodInvocation *context, @@ -457,6 +498,9 @@ _connect_generic (NMVpnServicePlugin *plugin, nm_vpn_service_plugin_set_state (plugin, NM_VPN_SERVICE_STATE_STARTING); + if (priv->dbus_watch_peer) + priv->peer_watch_id = watch_peer (plugin, context); + if (details) { priv->interactive = TRUE; success = vpn_class->connect_interactive (plugin, connection, details, &error); @@ -994,6 +1038,10 @@ set_property (GObject *object, guint prop_id, /* Construct-only */ priv->dbus_service_name = g_value_dup_string (value); break; + case PROP_DBUS_WATCH_PEER: + /* Construct-only */ + priv->dbus_watch_peer = g_value_get_boolean (value); + break; case PROP_STATE: nm_vpn_service_plugin_set_state (NM_VPN_SERVICE_PLUGIN (object), (NMVpnServiceState) g_value_get_enum (value)); @@ -1014,6 +1062,9 @@ get_property (GObject *object, guint prop_id, case PROP_DBUS_SERVICE_NAME: g_value_set_string (value, priv->dbus_service_name); break; + case PROP_DBUS_WATCH_PEER: + g_value_set_boolean (value, priv->dbus_watch_peer); + break; case PROP_STATE: g_value_set_enum (value, nm_vpn_service_plugin_get_state (NM_VPN_SERVICE_PLUGIN (object))); break; @@ -1120,6 +1171,20 @@ nm_vpn_service_plugin_class_init (NMVpnServicePluginClass *plugin_class) G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); + /** + * NMVpnServicePlugin:service-name: + * + * The D-Bus service name of this plugin. + * + * Since: 1.2 + */ + g_object_class_install_property + (object_class, PROP_DBUS_WATCH_PEER, + g_param_spec_boolean (NM_VPN_SERVICE_PLUGIN_DBUS_WATCH_PEER, "", "", + FALSE, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY)); + /** * NMVpnServicePlugin:state: * diff --git a/libnm/nm-vpn-service-plugin.h b/libnm/nm-vpn-service-plugin.h index 81ba40457f..4fb0ed360f 100644 --- a/libnm/nm-vpn-service-plugin.h +++ b/libnm/nm-vpn-service-plugin.h @@ -36,6 +36,7 @@ G_BEGIN_DECLS #define NM_VPN_SERVICE_PLUGIN_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_VPN_SERVICE_PLUGIN, NMVpnServicePluginClass)) #define NM_VPN_SERVICE_PLUGIN_DBUS_SERVICE_NAME "service-name" +#define NM_VPN_SERVICE_PLUGIN_DBUS_WATCH_PEER "watch-peer" #define NM_VPN_SERVICE_PLUGIN_STATE "state" typedef struct { From 78f263a5fd8e3458feeaaddb179913c7459bd25a Mon Sep 17 00:00:00 2001 From: Lubomir Rintel Date: Mon, 5 Oct 2015 18:40:47 +0200 Subject: [PATCH 3/8] libnm/vpn-service-plugin: quit when the peer we watch disconnects We're of no use anymore as another user would start an instance with a different bus name. --- libnm/nm-vpn-service-plugin.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/libnm/nm-vpn-service-plugin.c b/libnm/nm-vpn-service-plugin.c index d9f669c9ee..062b26c777 100644 --- a/libnm/nm-vpn-service-plugin.c +++ b/libnm/nm-vpn-service-plugin.c @@ -212,11 +212,6 @@ nm_vpn_service_plugin_disconnect (NMVpnServicePlugin *plugin, GError **err) break; case NM_VPN_SERVICE_STATE_STARTING: case NM_VPN_SERVICE_STATE_STARTED: - if (priv->peer_watch_id) { - g_dbus_connection_signal_unsubscribe (nm_vpn_service_plugin_get_connection (plugin), - priv->peer_watch_id); - priv->peer_watch_id = 0; - } nm_vpn_service_plugin_set_state (plugin, NM_VPN_SERVICE_STATE_STOPPING); ret = NM_VPN_SERVICE_PLUGIN_GET_CLASS (plugin)->disconnect (plugin, err); nm_vpn_service_plugin_set_state (plugin, NM_VPN_SERVICE_STATE_STOPPED); @@ -1128,7 +1123,15 @@ state_changed (NMVpnServicePlugin *plugin, NMVpnServiceState state) nm_clear_g_source (&priv->fail_stop_id); break; case NM_VPN_SERVICE_STATE_STOPPED: - schedule_quit_timer (plugin); + if (priv->dbus_watch_peer) + nm_vpn_service_plugin_emit_quit (plugin); + else + schedule_quit_timer (plugin); + if (priv->peer_watch_id) { + g_dbus_connection_signal_unsubscribe (nm_vpn_service_plugin_get_connection (plugin), + priv->peer_watch_id); + priv->peer_watch_id = 0; + } break; default: /* Clean up all timers we might have set up. */ From fd61b217064c1b62b505d6a71211bf4340e4b3ac Mon Sep 17 00:00:00 2001 From: Lubomir Rintel Date: Tue, 13 Oct 2015 11:39:44 +0200 Subject: [PATCH 4/8] libnm/vpn-service-plugin: remove nm_vpn_service_plugin_{get,set}_state() The plugins set state only on failures and often forget to do that. Do the correct status transition to STOPPED in nm_vpn_service_plugin_failure() instead. The get_state() is only used to find out whether to fail or orderly disconnect depending on whether we're STARTING or already STARTED. Handle that in nm_vpn_service_plugin_disconnect() in a generic manner instead. --- libnm/libnm.ver | 2 -- libnm/nm-vpn-service-plugin.c | 38 +++++++++++++++-------------------- libnm/nm-vpn-service-plugin.h | 6 ------ 3 files changed, 16 insertions(+), 30 deletions(-) diff --git a/libnm/libnm.ver b/libnm/libnm.ver index a3f9282822..796b666ec3 100644 --- a/libnm/libnm.ver +++ b/libnm/libnm.ver @@ -923,7 +923,6 @@ global: nm_vpn_service_plugin_failure; nm_vpn_service_plugin_get_connection; nm_vpn_service_plugin_get_secret_flags; - nm_vpn_service_plugin_get_state; nm_vpn_service_plugin_get_type; nm_vpn_service_plugin_read_vpn_details; nm_vpn_service_plugin_secrets_required; @@ -931,5 +930,4 @@ global: nm_vpn_service_plugin_set_ip4_config; nm_vpn_service_plugin_set_ip6_config; nm_vpn_service_plugin_set_login_banner; - nm_vpn_service_plugin_set_state; } libnm_1_0_0; diff --git a/libnm/nm-vpn-service-plugin.c b/libnm/nm-vpn-service-plugin.c index 062b26c777..7fa7459967 100644 --- a/libnm/nm-vpn-service-plugin.c +++ b/libnm/nm-vpn-service-plugin.c @@ -133,7 +133,7 @@ nm_vpn_service_plugin_get_connection (NMVpnServicePlugin *plugin) return connection; } -NMVpnServiceState +static NMVpnServiceState nm_vpn_service_plugin_get_state (NMVpnServicePlugin *plugin) { g_return_val_if_fail (NM_IS_VPN_SERVICE_PLUGIN (plugin), NM_VPN_SERVICE_STATE_UNKNOWN); @@ -141,7 +141,7 @@ nm_vpn_service_plugin_get_state (NMVpnServicePlugin *plugin) return NM_VPN_SERVICE_PLUGIN_GET_PRIVATE (plugin)->state; } -void +static void nm_vpn_service_plugin_set_state (NMVpnServicePlugin *plugin, NMVpnServiceState state) { @@ -171,17 +171,24 @@ nm_vpn_service_plugin_set_login_banner (NMVpnServicePlugin *plugin, nmdbus_vpn_plugin_emit_login_banner (priv->dbus_vpn_service_plugin, banner); } +static void +_emit_failure (NMVpnServicePlugin *plugin, + NMVpnPluginFailure reason) +{ + NMVpnServicePluginPrivate *priv = NM_VPN_SERVICE_PLUGIN_GET_PRIVATE (plugin); + + g_signal_emit (plugin, signals[FAILURE], 0, reason); + nmdbus_vpn_plugin_emit_failure (priv->dbus_vpn_service_plugin, reason); +} + void nm_vpn_service_plugin_failure (NMVpnServicePlugin *plugin, NMVpnPluginFailure reason) { - NMVpnServicePluginPrivate *priv; - g_return_if_fail (NM_IS_VPN_SERVICE_PLUGIN (plugin)); - priv = NM_VPN_SERVICE_PLUGIN_GET_PRIVATE (plugin); - g_signal_emit (plugin, signals[FAILURE], 0, reason); - nmdbus_vpn_plugin_emit_failure (priv->dbus_vpn_service_plugin, reason); + _emit_failure (plugin, reason); + nm_vpn_service_plugin_disconnect (plugin, NULL); } gboolean @@ -211,6 +218,8 @@ nm_vpn_service_plugin_disconnect (NMVpnServicePlugin *plugin, GError **err) "Could not process the request because no VPN connection was active."); break; case NM_VPN_SERVICE_STATE_STARTING: + _emit_failure (plugin, NM_VPN_PLUGIN_FAILURE_CONNECT_FAILED); + /* fallthru */ case NM_VPN_SERVICE_STATE_STARTED: nm_vpn_service_plugin_set_state (plugin, NM_VPN_SERVICE_STATE_STOPPING); ret = NM_VPN_SERVICE_PLUGIN_GET_CLASS (plugin)->disconnect (plugin, err); @@ -1188,21 +1197,6 @@ nm_vpn_service_plugin_class_init (NMVpnServicePluginClass *plugin_class) G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); - /** - * NMVpnServicePlugin:state: - * - * The state of the plugin. - * - * Since: 1.2 - */ - g_object_class_install_property - (object_class, PROP_STATE, - g_param_spec_enum (NM_VPN_SERVICE_PLUGIN_STATE, "", "", - NM_TYPE_VPN_SERVICE_STATE, - NM_VPN_SERVICE_STATE_INIT, - G_PARAM_READWRITE | - G_PARAM_STATIC_STRINGS)); - /* signals */ signals[STATE_CHANGED] = g_signal_new ("state-changed", diff --git a/libnm/nm-vpn-service-plugin.h b/libnm/nm-vpn-service-plugin.h index 4fb0ed360f..dd2b52a14f 100644 --- a/libnm/nm-vpn-service-plugin.h +++ b/libnm/nm-vpn-service-plugin.h @@ -37,7 +37,6 @@ G_BEGIN_DECLS #define NM_VPN_SERVICE_PLUGIN_DBUS_SERVICE_NAME "service-name" #define NM_VPN_SERVICE_PLUGIN_DBUS_WATCH_PEER "watch-peer" -#define NM_VPN_SERVICE_PLUGIN_STATE "state" typedef struct { NM_AVAILABLE_IN_1_2 @@ -113,11 +112,6 @@ GType nm_vpn_service_plugin_get_type (void); NM_AVAILABLE_IN_1_2 GDBusConnection *nm_vpn_service_plugin_get_connection (NMVpnServicePlugin *plugin); -NM_AVAILABLE_IN_1_2 -NMVpnServiceState nm_vpn_service_plugin_get_state (NMVpnServicePlugin *plugin); -NM_AVAILABLE_IN_1_2 -void nm_vpn_service_plugin_set_state (NMVpnServicePlugin *plugin, - NMVpnServiceState state); NM_AVAILABLE_IN_1_2 void nm_vpn_service_plugin_secrets_required (NMVpnServicePlugin *plugin, From 6c213e3cb4ed080fa94929e599e79e109625be27 Mon Sep 17 00:00:00 2001 From: Lubomir Rintel Date: Fri, 21 Aug 2015 16:14:27 +0200 Subject: [PATCH 5/8] libnm-core/vpn-plugin-info: add nm_vpn_plugin_info_supports_multiple() --- libnm-core/nm-vpn-plugin-info.c | 20 ++++++++++++++++++++ libnm-core/nm-vpn-plugin-info.h | 2 ++ 2 files changed, 22 insertions(+) diff --git a/libnm-core/nm-vpn-plugin-info.c b/libnm-core/nm-vpn-plugin-info.c index 6dd24a099d..d8ead9a220 100644 --- a/libnm-core/nm-vpn-plugin-info.c +++ b/libnm-core/nm-vpn-plugin-info.c @@ -619,6 +619,26 @@ nm_vpn_plugin_info_get_program (NMVpnPluginInfo *self) _nm_utils_strstrdictkey_static (NM_VPN_PLUGIN_INFO_KF_GROUP_CONNECTION, "program")); } +/** + * nm_vpn_plugin_info_supports_multiple: + * @self: plugin info instance + * + * Returns: %TRUE if the service supports multiple instances with different bus names, otherwise %FALSE + * + * Since: 1.2 + */ +gboolean +nm_vpn_plugin_info_supports_multiple (NMVpnPluginInfo *self) +{ + g_return_val_if_fail (NM_IS_VPN_PLUGIN_INFO (self), FALSE); + + return g_key_file_get_boolean (NM_VPN_PLUGIN_INFO_GET_PRIVATE (self)->keyfile, + NM_VPN_PLUGIN_INFO_KF_GROUP_CONNECTION, + "supports-multiple-connections", + NULL); +} + + /** * nm_vpn_plugin_info_lookup_property: * @self: plugin info instance diff --git a/libnm-core/nm-vpn-plugin-info.h b/libnm-core/nm-vpn-plugin-info.h index def09ab0ef..ead6fb708b 100644 --- a/libnm-core/nm-vpn-plugin-info.h +++ b/libnm-core/nm-vpn-plugin-info.h @@ -81,6 +81,8 @@ const char *nm_vpn_plugin_info_get_plugin (NMVpnPluginInfo *self); NM_AVAILABLE_IN_1_2 const char *nm_vpn_plugin_info_get_program (NMVpnPluginInfo *self); NM_AVAILABLE_IN_1_2 +gboolean nm_vpn_plugin_info_supports_multiple (NMVpnPluginInfo *self); +NM_AVAILABLE_IN_1_2 const char *nm_vpn_plugin_info_lookup_property (NMVpnPluginInfo *self, const char *group, const char *key); NM_AVAILABLE_IN_1_2 From 965363a6d5ef662b2d75cda8aa9d55ac02991176 Mon Sep 17 00:00:00 2001 From: Lubomir Rintel Date: Sat, 22 Aug 2015 16:22:26 +0200 Subject: [PATCH 6/8] vpn-connection: export add nm_vpn_connection_get_service() Will be useful later on. --- src/vpn-manager/nm-vpn-connection.c | 2 +- src/vpn-manager/nm-vpn-connection.h | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/vpn-manager/nm-vpn-connection.c b/src/vpn-manager/nm-vpn-connection.c index c3f4bea458..257ebf41ae 100644 --- a/src/vpn-manager/nm-vpn-connection.c +++ b/src/vpn-manager/nm-vpn-connection.c @@ -727,7 +727,7 @@ nm_vpn_connection_new (NMSettingsConnection *settings_connection, NULL); } -static const char * +const char * nm_vpn_connection_get_service (NMVpnConnection *self) { NMSettingVpn *s_vpn; diff --git a/src/vpn-manager/nm-vpn-connection.h b/src/vpn-manager/nm-vpn-connection.h index 28a4b04d18..3df324c55b 100644 --- a/src/vpn-manager/nm-vpn-connection.h +++ b/src/vpn-manager/nm-vpn-connection.h @@ -78,6 +78,7 @@ NMVpnConnection * nm_vpn_connection_new (NMSettingsConnection *settings_connecti void nm_vpn_connection_activate (NMVpnConnection *self); NMVpnConnectionState nm_vpn_connection_get_vpn_state (NMVpnConnection *self); const char * nm_vpn_connection_get_banner (NMVpnConnection *self); +const gchar * nm_vpn_connection_get_service (NMVpnConnection *self); gboolean nm_vpn_connection_deactivate (NMVpnConnection *self, NMVpnConnectionStateReason reason, From 9bbf5e94c2ff676f685a7740713140026efbcb7c Mon Sep 17 00:00:00 2001 From: Lubomir Rintel Date: Wed, 19 Aug 2015 23:07:13 +0200 Subject: [PATCH 7/8] device: allow multiple vpn IP configurations --- src/devices/nm-device.c | 142 ++++++++++++++++++++++------ src/devices/nm-device.h | 8 +- src/vpn-manager/nm-vpn-connection.c | 57 +++++++---- 3 files changed, 157 insertions(+), 50 deletions(-) diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c index eeffbe95b8..2568a100df 100644 --- a/src/devices/nm-device.c +++ b/src/devices/nm-device.c @@ -272,6 +272,7 @@ typedef struct { NMIP4Config * dev_ip4_config; /* Config from DHCP, PPP, LLv4, etc */ NMIP4Config * ext_ip4_config; /* Stuff added outside NM */ NMIP4Config * wwan_ip4_config; /* WWAN configuration */ + GSList * vpn4_configs; /* VPNs which use this device */ struct { gboolean v4_has; gboolean v4_is_assumed; @@ -289,7 +290,6 @@ typedef struct { gulong dhcp4_state_sigid; NMDhcp4Config * dhcp4_config; guint dhcp4_restart_id; - NMIP4Config * vpn4_config; /* routes added by a VPN which uses this device */ guint arp_round2_id; PingInfo gw_ping; @@ -310,9 +310,9 @@ typedef struct { NMIP6Config * ip6_config; IpState ip6_state; NMIP6Config * con_ip6_config; /* config from the setting */ - NMIP6Config * vpn6_config; /* routes added by a VPN which uses this device */ NMIP6Config * wwan_ip6_config; NMIP6Config * ext_ip6_config; /* Stuff added outside NM */ + GSList * vpn6_configs; /* VPNs which use this device */ gboolean nm_ipv6ll; /* TRUE if NM handles the device's IPv6LL address */ guint32 ip6_mtu; @@ -3577,6 +3577,15 @@ dhcp4_cleanup (NMDevice *self, CleanupType cleanup_type, gboolean release) } } +static void +_ip4_config_merge_default (gpointer value, gpointer user_data) +{ + NMIP4Config *src = (NMIP4Config *) value; + NMIP4Config *dst = (NMIP4Config *) user_data; + + nm_ip4_config_merge (dst, src, NM_IP_CONFIG_MERGE_DEFAULT); +} + static gboolean ip4_config_merge_and_apply (NMDevice *self, NMIP4Config *config, @@ -3622,8 +3631,9 @@ ip4_config_merge_and_apply (NMDevice *self, (ignore_auto_routes ? NM_IP_CONFIG_MERGE_NO_ROUTES : 0) | (ignore_auto_dns ? NM_IP_CONFIG_MERGE_NO_DNS : 0)); } - if (priv->vpn4_config) - nm_ip4_config_merge (composite, priv->vpn4_config, NM_IP_CONFIG_MERGE_DEFAULT); + + g_slist_foreach (priv->vpn4_configs, _ip4_config_merge_default, composite); + if (priv->ext_ip4_config) nm_ip4_config_merge (composite, priv->ext_ip4_config, NM_IP_CONFIG_MERGE_DEFAULT); @@ -4261,6 +4271,15 @@ dhcp6_cleanup (NMDevice *self, CleanupType cleanup_type, gboolean release) } } +static void +_ip6_config_merge_default (gpointer value, gpointer user_data) +{ + NMIP6Config *src = (NMIP6Config *) value; + NMIP6Config *dst = (NMIP6Config *) user_data; + + nm_ip6_config_merge (dst, src, NM_IP_CONFIG_MERGE_DEFAULT); +} + static gboolean ip6_config_merge_and_apply (NMDevice *self, gboolean commit, @@ -4306,8 +4325,9 @@ ip6_config_merge_and_apply (NMDevice *self, (ignore_auto_routes ? NM_IP_CONFIG_MERGE_NO_ROUTES : 0) | (ignore_auto_dns ? NM_IP_CONFIG_MERGE_NO_DNS : 0)); } - if (priv->vpn6_config) - nm_ip6_config_merge (composite, priv->vpn6_config, NM_IP_CONFIG_MERGE_DEFAULT); + + g_slist_foreach (priv->vpn6_configs, _ip6_config_merge_default, composite); + if (priv->ext_ip6_config) nm_ip6_config_merge (composite, priv->ext_ip6_config, NM_IP_CONFIG_MERGE_DEFAULT); @@ -6854,18 +6874,49 @@ nm_device_set_ip4_config (NMDevice *self, return success; } +static gboolean +_replace_vpn_config_in_list (GSList **plist, GObject *old, GObject *new) +{ + GSList *old_link; + + /* Below, assert that we have an @old instance to replace and that + * @new is not yet tracked. But still, behave correctly in any + * case. */ + + if ( old + && (old_link = g_slist_find (*plist, old))) { + if (old != new) { + if (new) + old_link->data = g_object_ref (new); + else + *plist = g_slist_remove_link (*plist, old_link); + g_object_unref (old); + } + return TRUE; + } + + if (new) { + if (!g_slist_find (*plist, new)) + *plist = g_slist_append (*plist, g_object_ref (new)); + else + g_return_val_if_reached (TRUE); + g_return_val_if_fail (!old, TRUE); + return TRUE; + } + + /* return FALSE if both @old and @new are unset. */ + g_return_val_if_fail (!old, FALSE); + return FALSE; +} + void -nm_device_set_vpn4_config (NMDevice *self, NMIP4Config *config) +nm_device_replace_vpn4_config (NMDevice *self, NMIP4Config *old, NMIP4Config *config) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - if (priv->vpn4_config == config) + if (!_replace_vpn_config_in_list (&priv->vpn4_configs, (GObject *) old, (GObject *) config)) return; - g_clear_object (&priv->vpn4_config); - if (config) - priv->vpn4_config = g_object_ref (config); - /* NULL to use existing configs */ if (!ip4_config_merge_and_apply (self, NULL, TRUE, NULL)) _LOGW (LOGD_IP4, "failed to set VPN routes for device"); @@ -6982,17 +7033,13 @@ nm_device_set_ip6_config (NMDevice *self, } void -nm_device_set_vpn6_config (NMDevice *self, NMIP6Config *config) +nm_device_replace_vpn6_config (NMDevice *self, NMIP6Config *old, NMIP6Config *config) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - if (priv->vpn6_config == config) + if (!_replace_vpn_config_in_list (&priv->vpn6_configs, (GObject *) old, (GObject *) config)) return; - g_clear_object (&priv->vpn6_config); - if (config) - priv->vpn6_config = g_object_ref (config); - /* NULL to use existing configs */ if (!ip6_config_merge_and_apply (self, TRUE, NULL)) _LOGW (LOGD_IP6, "failed to set VPN routes for device"); @@ -7589,6 +7636,24 @@ capture_lease_config (NMDevice *self, } } +static void +_ip4_config_intersect (gpointer value, gpointer user_data) +{ + NMIP4Config *dst = (NMIP4Config *) value; + NMIP4Config *src = (NMIP4Config *) user_data; + + nm_ip4_config_intersect (dst, src); +} + +static void +_ip4_config_subtract (gpointer value, gpointer user_data) +{ + NMIP4Config *dst = (NMIP4Config *) value; + NMIP4Config *src = (NMIP4Config *) user_data; + + nm_ip4_config_subtract (dst, src); +} + static void update_ip4_config (NMDevice *self, gboolean initial) { @@ -7626,8 +7691,9 @@ update_ip4_config (NMDevice *self, gboolean initial) nm_ip4_config_intersect (priv->con_ip4_config, priv->ext_ip4_config); if (priv->dev_ip4_config) nm_ip4_config_intersect (priv->dev_ip4_config, priv->ext_ip4_config); - if (priv->vpn4_config) - nm_ip4_config_intersect (priv->vpn4_config, priv->ext_ip4_config); + + g_slist_foreach (priv->vpn4_configs, _ip4_config_intersect, priv->ext_ip4_config); + if (priv->wwan_ip4_config) nm_ip4_config_intersect (priv->wwan_ip4_config, priv->ext_ip4_config); @@ -7638,8 +7704,9 @@ update_ip4_config (NMDevice *self, gboolean initial) nm_ip4_config_subtract (priv->ext_ip4_config, priv->con_ip4_config); if (priv->dev_ip4_config) nm_ip4_config_subtract (priv->ext_ip4_config, priv->dev_ip4_config); - if (priv->vpn4_config) - nm_ip4_config_subtract (priv->ext_ip4_config, priv->vpn4_config); + + g_slist_foreach (priv->vpn4_configs, _ip4_config_subtract, priv->ext_ip4_config); + if (priv->wwan_ip4_config) nm_ip4_config_subtract (priv->ext_ip4_config, priv->wwan_ip4_config); @@ -7647,6 +7714,24 @@ update_ip4_config (NMDevice *self, gboolean initial) } } +static void +_ip6_config_intersect (gpointer value, gpointer user_data) +{ + NMIP6Config *dst = (NMIP6Config *) value; + NMIP6Config *src = (NMIP6Config *) user_data; + + nm_ip6_config_intersect (dst, src); +} + +static void +_ip6_config_subtract (gpointer value, gpointer user_data) +{ + NMIP6Config *dst = (NMIP6Config *) value; + NMIP6Config *src = (NMIP6Config *) user_data; + + nm_ip6_config_subtract (dst, src); +} + static void update_ip6_config (NMDevice *self, gboolean initial) { @@ -7684,8 +7769,7 @@ update_ip6_config (NMDevice *self, gboolean initial) nm_ip6_config_intersect (priv->dhcp6_ip6_config, priv->ext_ip6_config); if (priv->wwan_ip6_config) nm_ip6_config_intersect (priv->wwan_ip6_config, priv->ext_ip6_config); - if (priv->vpn6_config) - nm_ip6_config_intersect (priv->vpn6_config, priv->ext_ip6_config); + g_slist_foreach (priv->vpn6_configs, _ip6_config_intersect, priv->ext_ip6_config); /* Remove parts from ext_ip6_config to only contain the information that * was configured externally -- we already have the same configuration from @@ -7698,8 +7782,7 @@ update_ip6_config (NMDevice *self, gboolean initial) nm_ip6_config_subtract (priv->ext_ip6_config, priv->dhcp6_ip6_config); if (priv->wwan_ip6_config) nm_ip6_config_subtract (priv->ext_ip6_config, priv->wwan_ip6_config); - if (priv->vpn6_config) - nm_ip6_config_subtract (priv->ext_ip6_config, priv->vpn6_config); + g_slist_foreach (priv->vpn6_configs, _ip6_config_subtract, priv->ext_ip6_config); ip6_config_merge_and_apply (self, FALSE, NULL); } @@ -8540,15 +8623,18 @@ _cleanup_generic_post (NMDevice *self, CleanupType cleanup_type) g_clear_object (&priv->dev_ip4_config); g_clear_object (&priv->ext_ip4_config); g_clear_object (&priv->wwan_ip4_config); - g_clear_object (&priv->vpn4_config); g_clear_object (&priv->ip4_config); g_clear_object (&priv->con_ip6_config); g_clear_object (&priv->ac_ip6_config); g_clear_object (&priv->ext_ip6_config); - g_clear_object (&priv->vpn6_config); g_clear_object (&priv->wwan_ip6_config); g_clear_object (&priv->ip6_config); + g_slist_free_full (priv->vpn4_configs, g_object_unref); + priv->vpn4_configs = NULL; + g_slist_free_full (priv->vpn6_configs, g_object_unref); + priv->vpn6_configs = NULL; + clear_act_request (self); /* Clear legacy IPv4 address property */ diff --git a/src/devices/nm-device.h b/src/devices/nm-device.h index 15858194a7..6a5cd46f29 100644 --- a/src/devices/nm-device.h +++ b/src/devices/nm-device.h @@ -345,10 +345,14 @@ NMDhcp4Config * nm_device_get_dhcp4_config (NMDevice *dev); NMDhcp6Config * nm_device_get_dhcp6_config (NMDevice *dev); NMIP4Config * nm_device_get_ip4_config (NMDevice *dev); -void nm_device_set_vpn4_config (NMDevice *dev, NMIP4Config *config); +void nm_device_replace_vpn4_config (NMDevice *dev, + NMIP4Config *old, + NMIP4Config *config); NMIP6Config * nm_device_get_ip6_config (NMDevice *dev); -void nm_device_set_vpn6_config (NMDevice *dev, NMIP6Config *config); +void nm_device_replace_vpn6_config (NMDevice *dev, + NMIP6Config *old, + NMIP6Config *config); void nm_device_capture_initial_config (NMDevice *dev); diff --git a/src/vpn-manager/nm-vpn-connection.c b/src/vpn-manager/nm-vpn-connection.c index 257ebf41ae..d47e15398a 100644 --- a/src/vpn-manager/nm-vpn-connection.c +++ b/src/vpn-manager/nm-vpn-connection.c @@ -105,6 +105,13 @@ typedef struct { guint32 ip4_external_gw; gboolean has_ip6; NMIP6Config *ip6_config; + + /* These config instances are passed on to NMDevice and modified by NMDevice. + * This pointer is only useful for nm_device_replace_vpn4_config() to clear the + * previous configuration. Consider these instances to be owned by NMDevice. */ + NMIP4Config *last_device_ip4_config; + NMIP6Config *last_device_ip6_config; + struct in6_addr *ip6_internal_gw; struct in6_addr *ip6_external_gw; char *ip_iface; @@ -329,6 +336,22 @@ fw_call_cleanup (NMVpnConnection *self) } } +static void +remove_parent_device_config (NMVpnConnection *connection, NMDevice *device) +{ + NMVpnConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (connection); + + if (priv->last_device_ip4_config) { + nm_device_replace_vpn4_config (device, priv->last_device_ip4_config, NULL); + g_clear_object (&priv->last_device_ip4_config); + } + + if (priv->last_device_ip6_config) { + nm_device_replace_vpn6_config (device, priv->last_device_ip6_config, NULL); + g_clear_object (&priv->last_device_ip6_config); + } +} + static void vpn_cleanup (NMVpnConnection *self, NMDevice *parent_dev) { @@ -340,8 +363,7 @@ vpn_cleanup (NMVpnConnection *self, NMDevice *parent_dev) nm_platform_address_flush (NM_PLATFORM_GET, priv->ip_ifindex); } - nm_device_set_vpn4_config (parent_dev, NULL); - nm_device_set_vpn6_config (parent_dev, NULL); + remove_parent_device_config (self, parent_dev); /* Remove zone from firewall */ if (priv->ip_iface) { @@ -1030,22 +1052,19 @@ apply_parent_device_config (NMVpnConnection *self) } } - if (vpn4_parent_config) { - /* Add any explicit route to the VPN gateway through the parent device */ - if (priv->ip4_external_gw) - add_ip4_vpn_gateway_route (vpn4_parent_config, parent_dev, priv->ip4_external_gw); + /* Add any explicit route to the VPN gateway through the parent device */ + if (vpn4_parent_config && priv->ip4_external_gw) + add_ip4_vpn_gateway_route (vpn4_parent_config, parent_dev, priv->ip4_external_gw); + if (vpn6_parent_config && priv->ip6_external_gw) + add_ip6_vpn_gateway_route (vpn6_parent_config, parent_dev, priv->ip6_external_gw); - nm_device_set_vpn4_config (parent_dev, vpn4_parent_config); - g_object_unref (vpn4_parent_config); - } - if (vpn6_parent_config) { - /* Add any explicit route to the VPN gateway through the parent device */ - if (priv->ip6_external_gw) - add_ip6_vpn_gateway_route (vpn6_parent_config, parent_dev, priv->ip6_external_gw); + nm_device_replace_vpn4_config (parent_dev, priv->last_device_ip4_config, vpn4_parent_config); + g_clear_object (&priv->last_device_ip4_config); + priv->last_device_ip4_config = vpn4_parent_config; - nm_device_set_vpn6_config (parent_dev, vpn6_parent_config); - g_object_unref (vpn6_parent_config); - } + nm_device_replace_vpn6_config (parent_dev, priv->last_device_ip6_config, vpn6_parent_config); + g_clear_object (&priv->last_device_ip6_config); + priv->last_device_ip6_config = vpn6_parent_config; } static gboolean @@ -2242,10 +2261,8 @@ device_changed (NMActiveConnection *active, * out that connectivity is down and start its reconnect attempt if it * needs to. */ - if (old_device) { - nm_device_set_vpn4_config (old_device, NULL); - nm_device_set_vpn6_config (old_device, NULL); - } + if (old_device) + remove_parent_device_config (NM_VPN_CONNECTION (active), old_device); if (new_device) apply_parent_device_config (NM_VPN_CONNECTION (active)); From 5b48befaad3281c479673aed7c4b831e6e0da694 Mon Sep 17 00:00:00 2001 From: Lubomir Rintel Date: Wed, 19 Aug 2015 18:55:53 +0200 Subject: [PATCH 8/8] vpn-manager: support multiple VPN connections of the same type 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 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 . 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. --- libnm-core/nm-vpn-plugin-info.c | 3 - src/Makefile.am | 2 - src/nm-manager.c | 3 +- src/vpn-manager/nm-vpn-connection.c | 180 ++++++++++++-- src/vpn-manager/nm-vpn-connection.h | 4 +- src/vpn-manager/nm-vpn-manager.c | 140 +++++------ src/vpn-manager/nm-vpn-manager.h | 4 - src/vpn-manager/nm-vpn-service.c | 356 ---------------------------- src/vpn-manager/nm-vpn-service.h | 60 ----- 9 files changed, 222 insertions(+), 530 deletions(-) delete mode 100644 src/vpn-manager/nm-vpn-service.c delete mode 100644 src/vpn-manager/nm-vpn-service.h diff --git a/libnm-core/nm-vpn-plugin-info.c b/libnm-core/nm-vpn-plugin-info.c index d8ead9a220..28f0f69279 100644 --- a/libnm-core/nm-vpn-plugin-info.c +++ b/libnm-core/nm-vpn-plugin-info.c @@ -449,9 +449,6 @@ nm_vpn_plugin_info_list_add (GSList **list, NMVpnPluginInfo *plugin_info, GError gboolean nm_vpn_plugin_info_list_remove (GSList **list, NMVpnPluginInfo *plugin_info) { - if (!plugin_info) - return FALSE; - g_return_val_if_fail (list, FALSE); g_return_val_if_fail (NM_IS_VPN_PLUGIN_INFO (plugin_info), FALSE); diff --git a/src/Makefile.am b/src/Makefile.am index b845958a6e..2cae35de1c 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -324,8 +324,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/nm-manager.c b/src/nm-manager.c index 1253f99188..67bb1fc015 100644 --- a/src/nm-manager.c +++ b/src/nm-manager.c @@ -3465,7 +3465,6 @@ nm_manager_deactivate_connection (NMManager *manager, NMDeviceStateReason reason, GError **error) { - NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager); NMActiveConnection *active; gboolean success = FALSE; @@ -3481,7 +3480,7 @@ nm_manager_deactivate_connection (NMManager *manager, if (reason == NM_DEVICE_STATE_REASON_CONNECTION_REMOVED) vpn_reason = NM_VPN_CONNECTION_STATE_REASON_CONNECTION_REMOVED; - if (nm_vpn_manager_deactivate_connection (priv->vpn_manager, NM_VPN_CONNECTION (active), vpn_reason)) + if (nm_vpn_connection_deactivate (NM_VPN_CONNECTION (active), vpn_reason, FALSE)) success = TRUE; else g_set_error_literal (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_CONNECTION_NOT_ACTIVE, diff --git a/src/vpn-manager/nm-vpn-connection.c b/src/vpn-manager/nm-vpn-connection.c index d47e15398a..37815644e6 100644 --- a/src/vpn-manager/nm-vpn-connection.c +++ b/src/vpn-manager/nm-vpn-connection.c @@ -43,6 +43,8 @@ #include "nm-route-manager.h" #include "nm-firewall-manager.h" #include "nm-config.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 */ NMFirewallManagerCallId fw_call; @@ -383,6 +389,9 @@ vpn_cleanup (NMVpnConnection *self, 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. */ @@ -1857,6 +1866,111 @@ ip6_config_cb (GDBusProxy *proxy, nm_vpn_connection_ip6_config_get (self, dict); } +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; + _LOGI ("Saw the service appear; activating 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; + _LOGI ("VPN service disappeared"); + 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); + + _LOGW ("Timed out waiting for the service to start"); + 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) { + _LOGI ("Started the VPN service, PID %ld", (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) { @@ -1883,55 +1997,69 @@ 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)) { + _LOGW ("Could not launch the VPN service. error: %s.", + error->message); + + nm_vpn_connection_disconnect (self, NM_VPN_CONNECTION_STATE_REASON_SERVICE_START_FAILED, FALSE); + } } void -nm_vpn_connection_activate (NMVpnConnection *self) +nm_vpn_connection_activate (NMVpnConnection *self, + NMVpnPluginInfo *plugin_info) { NMVpnConnectionPrivate *priv; NMSettingVpn *s_vpn; + const char *service; g_return_if_fail (NM_IS_VPN_CONNECTION (self)); + g_return_if_fail (NM_IS_VPN_PLUGIN_INFO (plugin_info)); priv = NM_VPN_CONNECTION_GET_PRIVATE (self); + g_return_if_fail (!priv->plugin_info); s_vpn = nm_connection_get_setting_vpn (_get_applied_connection (self)); - g_assert (s_vpn); + g_return_if_fail (s_vpn); + + service = nm_setting_vpn_get_service_type (s_vpn); + g_return_if_fail (!g_strcmp0 (service, nm_vpn_plugin_info_get_service (plugin_info))); + + if (nm_vpn_plugin_info_supports_multiple (plugin_info)) { + const char *path; + + path = nm_exported_object_get_path (NM_EXPORTED_OBJECT (self)); + if (path) + path = strrchr (path, '/'); + g_return_if_fail (path); + + priv->bus_name = g_strdup_printf ("%s.Connection_%s", service, &path[1]); + } else + priv->bus_name = g_strdup (service); + 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); 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); } NMVpnConnectionState @@ -2305,6 +2433,7 @@ dispose (GObject *object) g_clear_object (&priv->ip4_config); g_clear_object (&priv->ip6_config); g_clear_object (&priv->proxy); + g_clear_object (&priv->plugin_info); fw_call_cleanup (self); @@ -2429,4 +2558,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 3df324c55b..19b0eb3feb 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,8 @@ NMVpnConnection * nm_vpn_connection_new (NMSettingsConnection *settings_connecti const char *specific_object, NMAuthSubject *subject); -void nm_vpn_connection_activate (NMVpnConnection *self); +void nm_vpn_connection_activate (NMVpnConnection *self, + NMVpnPluginInfo *plugin_info); NMVpnConnectionState nm_vpn_connection_get_vpn_state (NMVpnConnection *self); const char * nm_vpn_connection_get_banner (NMVpnConnection *self); const gchar * nm_vpn_connection_get_service (NMVpnConnection *self); diff --git a/src/vpn-manager/nm-vpn-manager.c b/src/vpn-manager/nm-vpn-manager.c index 343997a250..f39ddf6c11 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,33 @@ 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; + + /* This is only used for services that don't support multiple + * connections, to guard access to them. */ + GHashTable *active_services; } 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; -} +/******************************************************************************/ 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,18 +71,16 @@ nm_vpn_manager_activate_connection (NMVpnManager *manager, NMVpnConnection *vpn, GError **error) { - NMConnection *applied_connection; - NMSettingVpn *s_vpn; - NMVpnService *service; + NMVpnManagerPrivate *priv; NMVpnPluginInfo *plugin_info; const char *service_name; NMDevice *device; g_return_val_if_fail (NM_IS_VPN_MANAGER (manager), 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); + g_return_val_if_fail (!error || !*error, 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,60 +90,57 @@ nm_vpn_manager_activate_connection (NMVpnManager *manager, return FALSE; } - applied_connection = nm_active_connection_get_applied_connection (NM_ACTIVE_CONNECTION (vpn)); - s_vpn = nm_connection_get_setting_vpn (applied_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_contains (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; + } + + nm_vpn_connection_activate (vpn, plugin_info); + + if (!nm_vpn_plugin_info_supports_multiple (plugin_info)) { + /* Block activations of the connections of the same service type. */ + g_hash_table_add (priv->active_services, g_strdup (service_name)); + g_signal_connect (vpn, "notify::" NM_ACTIVE_CONNECTION_STATE, + G_CALLBACK (vpn_state_changed), + g_object_ref (manager)); + } + + return TRUE; } -gboolean -nm_vpn_manager_deactivate_connection (NMVpnManager *self, - NMVpnConnection *connection, - NMVpnConnectionStateReason reason) -{ - return nm_vpn_connection_deactivate (connection, reason, FALSE); -} +/******************************************************************************/ 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; + const char *program; + + program = nm_vpn_plugin_info_get_program (plugin_info); + if (!program || !*program) + return; /* 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 @@ -151,7 +153,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; @@ -161,25 +162,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 @@ -202,7 +194,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: @@ -247,13 +239,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, NULL); } static void @@ -275,16 +269,10 @@ 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); + while (priv->plugins) + nm_vpn_plugin_info_list_remove (&priv->plugins, priv->plugins->data); - 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); - } + 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-manager.h b/src/vpn-manager/nm-vpn-manager.h index 2034e077ad..f557e27acb 100644 --- a/src/vpn-manager/nm-vpn-manager.h +++ b/src/vpn-manager/nm-vpn-manager.h @@ -48,8 +48,4 @@ gboolean nm_vpn_manager_activate_connection (NMVpnManager *manager, NMVpnConnection *vpn, GError **error); -gboolean nm_vpn_manager_deactivate_connection (NMVpnManager *manager, - NMVpnConnection *connection, - NMVpnConnectionStateReason reason); - #endif /* __NETWORKMANAGER_VPN_MANAGER_H__ */ 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 -#include -#include -#include -#include - -#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 */