diff options
Diffstat (limited to 'src/vpn-manager/nm-vpn-connection.c')
-rw-r--r-- | src/vpn-manager/nm-vpn-connection.c | 177 |
1 files changed, 152 insertions, 25 deletions
diff --git a/src/vpn-manager/nm-vpn-connection.c b/src/vpn-manager/nm-vpn-connection.c index 4016a493d3..47717fb679 100644 --- a/src/vpn-manager/nm-vpn-connection.c +++ b/src/vpn-manager/nm-vpn-connection.c @@ -42,6 +42,8 @@ #include "nm-default-route-manager.h" #include "nm-route-manager.h" #include "nm-firewall-manager.h" +#include "nm-vpn-plugin-info.h" +#include "nm-vpn-manager.h" #include "nmdbus-vpn-connection.h" @@ -89,6 +91,10 @@ typedef struct { NMVpnConnectionStateReason failure_reason; NMVpnServiceState service_state; + guint start_timeout; + gboolean service_running; + NMVpnPluginInfo *plugin_info; + char *bus_name; /* Firewall */ NMFirewallPendingCall fw_call; @@ -276,6 +282,9 @@ vpn_cleanup (NMVpnConnection *connection, NMDevice *parent_dev) priv->ip_iface = NULL; priv->ip_ifindex = 0; + g_free (priv->bus_name); + priv->bus_name = NULL; + /* Clear out connection secrets to ensure that the settings service * gets asked for them next time the connection is activated. */ @@ -1750,6 +1759,116 @@ ip6_config_cb (GDBusProxy *proxy, } static void +_name_owner_changed (GObject *object, + GParamSpec *pspec, + gpointer user_data) +{ + NMVpnConnection *self = NM_VPN_CONNECTION (user_data); + NMVpnConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (self); + char *owner; + + owner = g_dbus_proxy_get_name_owner (G_DBUS_PROXY (object)); + + if (owner && !priv->service_running) { + /* service appeared */ + priv->service_running = TRUE; + nm_log_info (LOGD_VPN, "VPN connection '%s' saw the service appear; activating connection", + nm_connection_get_id (priv->connection)); + + /* No need to wait for the timeout any longer */ + nm_clear_g_source (&priv->start_timeout); + + /* Expect success because the VPN service has already appeared */ + _nm_dbus_signal_connect (priv->proxy, "Failure", G_VARIANT_TYPE ("(u)"), + G_CALLBACK (failure_cb), self); + _nm_dbus_signal_connect (priv->proxy, "StateChanged", G_VARIANT_TYPE ("(u)"), + G_CALLBACK (state_changed_cb), self); + _nm_dbus_signal_connect (priv->proxy, "SecretsRequired", G_VARIANT_TYPE ("(sas)"), + G_CALLBACK (secrets_required_cb), self); + _nm_dbus_signal_connect (priv->proxy, "Config", G_VARIANT_TYPE ("(a{sv})"), + G_CALLBACK (config_cb), self); + _nm_dbus_signal_connect (priv->proxy, "Ip4Config", G_VARIANT_TYPE ("(a{sv})"), + G_CALLBACK (ip4_config_cb), self); + _nm_dbus_signal_connect (priv->proxy, "Ip6Config", G_VARIANT_TYPE ("(a{sv})"), + G_CALLBACK (ip6_config_cb), self); + + _set_vpn_state (self, STATE_NEED_AUTH, NM_VPN_CONNECTION_STATE_REASON_NONE, FALSE); + + /* Kick off the secrets requests; first we get existing system secrets + * and ask the plugin if these are sufficient, next we get all existing + * secrets from system and from user agents and ask the plugin again, + * and last we ask the user for new secrets if required. + */ + get_secrets (self, SECRETS_REQ_SYSTEM, NULL); + } else if (!owner && priv->service_running) { + /* service went away */ + priv->service_running = FALSE; + nm_log_info (LOGD_VPN, "VPN connection '%s' service disappeared", + nm_vpn_connection_get_connection_id (self)); + nm_vpn_connection_disconnect (self, NM_VPN_CONNECTION_STATE_REASON_SERVICE_STOPPED, FALSE); + } + + g_free (owner); +} + + +static gboolean +_daemon_exec_timeout (gpointer data) +{ + NMVpnConnection *self = NM_VPN_CONNECTION (data); + NMVpnConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (self); + + nm_log_warn (LOGD_VPN, "VPN connection '%s' timed out waiting for the service to start", + nm_vpn_connection_get_connection_id (self)); + priv->start_timeout = 0; + nm_vpn_connection_disconnect (self, NM_VPN_CONNECTION_STATE_REASON_SERVICE_START_TIMEOUT, FALSE); + + g_object_unref (self); + + return G_SOURCE_REMOVE; +} + +static gboolean +nm_vpn_service_daemon_exec (NMVpnConnection *self, GError **error) +{ + NMVpnConnectionPrivate *priv; + GPid pid; + char *vpn_argv[4]; + gboolean success = FALSE; + GError *spawn_error = NULL; + int i = 0; + + g_return_val_if_fail (NM_IS_VPN_CONNECTION (self), FALSE); + priv = NM_VPN_CONNECTION_GET_PRIVATE (self); + + vpn_argv[i++] = (char *) nm_vpn_plugin_info_get_program (priv->plugin_info); + if (nm_vpn_plugin_info_supports_multiple (priv->plugin_info)) { + vpn_argv[i++] = "--bus-name"; + vpn_argv[i++] = priv->bus_name; + } + vpn_argv[i] = NULL; + g_assert (vpn_argv[0]); + + success = g_spawn_async (NULL, vpn_argv, NULL, 0, nm_utils_setpgid, NULL, &pid, &spawn_error); + + if (success) { + nm_log_info (LOGD_VPN, "VPN connection '%s' started the service, PID %ld", + nm_vpn_connection_get_connection_id (self), + (long int) pid); + priv->start_timeout = g_timeout_add_seconds (5, _daemon_exec_timeout, g_object_ref (self)); + } else { + g_set_error (error, + NM_MANAGER_ERROR, NM_MANAGER_ERROR_FAILED, + "%s", spawn_error ? spawn_error->message : "unknown g_spawn_async() error"); + + if (spawn_error) + g_error_free (spawn_error); + } + + return success; +} + +static void on_proxy_acquired (GObject *object, GAsyncResult *result, gpointer user_data) { NMVpnConnection *self; @@ -1777,36 +1896,32 @@ on_proxy_acquired (GObject *object, GAsyncResult *result, gpointer user_data) } priv->proxy = proxy; - _nm_dbus_signal_connect (priv->proxy, "Failure", G_VARIANT_TYPE ("(u)"), - G_CALLBACK (failure_cb), self); - _nm_dbus_signal_connect (priv->proxy, "StateChanged", G_VARIANT_TYPE ("(u)"), - G_CALLBACK (state_changed_cb), self); - _nm_dbus_signal_connect (priv->proxy, "SecretsRequired", G_VARIANT_TYPE ("(sas)"), - G_CALLBACK (secrets_required_cb), self); - _nm_dbus_signal_connect (priv->proxy, "Config", G_VARIANT_TYPE ("(a{sv})"), - G_CALLBACK (config_cb), self); - _nm_dbus_signal_connect (priv->proxy, "Ip4Config", G_VARIANT_TYPE ("(a{sv})"), - G_CALLBACK (ip4_config_cb), self); - _nm_dbus_signal_connect (priv->proxy, "Ip6Config", G_VARIANT_TYPE ("(a{sv})"), - G_CALLBACK (ip6_config_cb), self); - _set_vpn_state (self, STATE_NEED_AUTH, NM_VPN_CONNECTION_STATE_REASON_NONE, FALSE); + g_signal_connect (priv->proxy, "notify::g-name-owner", + G_CALLBACK (_name_owner_changed), self); + _name_owner_changed (G_OBJECT (priv->proxy), NULL, self); - /* Kick off the secrets requests; first we get existing system secrets - * and ask the plugin if these are sufficient, next we get all existing - * secrets from system and from user agents and ask the plugin again, - * and last we ask the user for new secrets if required. - */ - get_secrets (self, SECRETS_REQ_SYSTEM, NULL); + if (priv->service_running) + return; + + if (!nm_vpn_service_daemon_exec (self, &error)) { + nm_log_warn (LOGD_VPN, "VPN connection '%s': could not launch the VPN service. error: %s.", + nm_vpn_connection_get_connection_id (self), + error->message); + + nm_vpn_connection_disconnect (self, NM_VPN_CONNECTION_STATE_REASON_SERVICE_START_FAILED, FALSE); + } } -void -nm_vpn_connection_activate (NMVpnConnection *self) +gboolean +nm_vpn_connection_activate (NMVpnConnection *self, + NMVpnPluginInfo *plugin_info, + GError **error) { NMVpnConnectionPrivate *priv; NMSettingVpn *s_vpn; - g_return_if_fail (NM_IS_VPN_CONNECTION (self)); + g_return_val_if_fail (NM_IS_VPN_CONNECTION (self), FALSE); priv = NM_VPN_CONNECTION_GET_PRIVATE (self); @@ -1814,18 +1929,30 @@ nm_vpn_connection_activate (NMVpnConnection *self) g_assert (s_vpn); priv->connection_can_persist = nm_setting_vpn_get_persistent (s_vpn); - _set_vpn_state (self, STATE_PREPARE, NM_VPN_CONNECTION_STATE_REASON_NONE, FALSE); + priv->plugin_info = g_object_ref (plugin_info); + if (nm_vpn_plugin_info_supports_multiple (priv->plugin_info)) { + priv->bus_name = g_strdup_printf ("%s.%s", + nm_vpn_connection_get_service (self), + nm_connection_get_uuid (priv->connection)); + } else { + priv->bus_name = g_strdup (nm_vpn_connection_get_service (self)); + } priv->cancellable = g_cancellable_new (); + g_dbus_proxy_new_for_bus (G_BUS_TYPE_SYSTEM, G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES, NULL, - nm_vpn_connection_get_service (self), + priv->bus_name, NM_VPN_DBUS_PLUGIN_PATH, NM_VPN_DBUS_PLUGIN_INTERFACE, priv->cancellable, (GAsyncReadyCallback) on_proxy_acquired, self); + + _set_vpn_state (self, STATE_PREPARE, NM_VPN_CONNECTION_STATE_REASON_NONE, FALSE); + + return TRUE; } NMConnection * @@ -2250,6 +2377,7 @@ dispose (GObject *object) g_clear_object (&priv->connection); g_clear_object (&priv->default_route_manager); g_clear_object (&priv->route_manager); + g_clear_object (&priv->plugin_info); fw_call_cleanup (NM_VPN_CONNECTION (object)); @@ -2372,4 +2500,3 @@ nm_vpn_connection_class_init (NMVpnConnectionClass *connection_class) NMDBUS_TYPE_VPN_CONNECTION_SKELETON, NULL); } - |