diff --git a/libnm-core/nm-vpn-plugin-info.c b/libnm-core/nm-vpn-plugin-info.c index 6dd24a099d..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); @@ -619,6 +616,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 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 fc4cfdc038..7fa7459967 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 @@ -130,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); @@ -138,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) { @@ -168,27 +171,36 @@ 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 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: @@ -206,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); @@ -413,6 +427,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, @@ -443,6 +488,7 @@ _connect_generic (NMVpnServicePlugin *plugin, "Invalid connection: (%d) %s", error->code, error->message); g_clear_error (&error); + return; } priv->interactive = FALSE; @@ -456,6 +502,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); @@ -993,6 +1042,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)); @@ -1013,6 +1066,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; @@ -1076,7 +1132,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. */ @@ -1120,19 +1184,18 @@ nm_vpn_service_plugin_class_init (NMVpnServicePluginClass *plugin_class) G_PARAM_STATIC_STRINGS)); /** - * NMVpnServicePlugin:state: + * NMVpnServicePlugin:service-name: * - * The state of the plugin. + * The D-Bus service name of this 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)); + (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)); /* signals */ signals[STATE_CHANGED] = diff --git a/libnm/nm-vpn-service-plugin.h b/libnm/nm-vpn-service-plugin.h index 81ba40457f..dd2b52a14f 100644 --- a/libnm/nm-vpn-service-plugin.h +++ b/libnm/nm-vpn-service-plugin.h @@ -36,7 +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_STATE "state" +#define NM_VPN_SERVICE_PLUGIN_DBUS_WATCH_PEER "watch-peer" typedef struct { NM_AVAILABLE_IN_1_2 @@ -112,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, 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/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/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 c3f4bea458..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; @@ -105,6 +111,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 +342,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 +369,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) { @@ -361,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. */ @@ -727,7 +758,7 @@ nm_vpn_connection_new (NMSettingsConnection *settings_connection, NULL); } -static const char * +const char * nm_vpn_connection_get_service (NMVpnConnection *self) { NMSettingVpn *s_vpn; @@ -1030,22 +1061,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 @@ -1838,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) { @@ -1864,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 @@ -2242,10 +2389,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)); @@ -2288,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); @@ -2412,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 28a4b04d18..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,9 +76,11 @@ 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); gboolean nm_vpn_connection_deactivate (NMVpnConnection *self, NMVpnConnectionStateReason reason, 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 */