summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDan Williams <dcbw@redhat.com>2016-02-26 16:16:09 -0600
committerThomas Haller <thaller@redhat.com>2016-03-02 11:28:27 +0100
commitba7359441b565c5ac6b0524b6aa04b155f0a9123 (patch)
tree7fb85d30d4095ccd5ef87cd3dba9ff1847cd3f87
parentcd6ee5786a4230b0f51952f5f350676b21961271 (diff)
downloadNetworkManager-ba7359441b565c5ac6b0524b6aa04b155f0a9123.tar.gz
libnm-glib/libnm/vpn: fix handling of ConnectInteractive() failure (rh #1298732)
If the plugin supports interactive mode, but the VPN binary (like vpnc or openvpn) doesn't support it, then the plugin should return NM_VPN_PLUGIN_ERROR_INTERACTIVE_NOT_SUPPORTED from its connect_interactive() hook. This lets NetworkManager know to fall back to plain Connect(). Since this notification is done through an error return, the VPN service plugin code sees the failure and moves the plugin state back to STOPPED. NetworkManager sees that state change, and terminates the connection attempt while waiting for a reply to the Connect() method. (VPN service plugins that don't support interactive mode at all don't have this problem because that error is returned before the plugin's state is moved to STARTING.) To fix this, do two things: 1) if the connect_interactive() hook fails and returns the error NM_VPN_PLUGIN_ERROR_INTERACTIVE_NOT_SUPPORTED, postpone the STOPPED state change for a few seconds to allow NM time to fall back to plain Connect(). We still want to move the plugin state back to STOPPED eventually, because otherwise it could stay in STARTING forever. 2) change state to STARTING only if the connect/connect_interactive plugin hooks were successful. Otherwise the plugin would still be in STARTING state, and it's not valid to call Connect()/ConnectInteractive() during the STARTING state. https://mail.gnome.org/archives/networkmanager-list/2016-February/msg00091.html https://bugzilla.redhat.com/show_bug.cgi?id=1298732 (cherry picked from commit abc700c5c71f474730f703c648b0b8dab455d7ba)
-rw-r--r--libnm-glib/nm-vpn-plugin.c24
-rw-r--r--libnm/nm-vpn-plugin-old.c20
2 files changed, 32 insertions, 12 deletions
diff --git a/libnm-glib/nm-vpn-plugin.c b/libnm-glib/nm-vpn-plugin.c
index 4350e6007e..c2faadc69f 100644
--- a/libnm-glib/nm-vpn-plugin.c
+++ b/libnm-glib/nm-vpn-plugin.c
@@ -301,12 +301,15 @@ fail_stop (gpointer data)
}
static void
-schedule_fail_stop (NMVPNPlugin *plugin)
+schedule_fail_stop (NMVPNPlugin *plugin, guint timeout_secs)
{
NMVPNPluginPrivate *priv = NM_VPN_PLUGIN_GET_PRIVATE (plugin);
nm_clear_g_source (&priv->fail_stop_id);
- priv->fail_stop_id = g_idle_add (fail_stop, plugin);
+ if (timeout_secs)
+ priv->fail_stop_id = g_timeout_add_seconds (timeout_secs, fail_stop, plugin);
+ else
+ priv->fail_stop_id = g_idle_add (fail_stop, plugin);
}
static void
@@ -441,6 +444,7 @@ _connect_generic (NMVPNPlugin *plugin,
NMConnection *connection;
gboolean success = FALSE;
GError *local = NULL;
+ guint fail_stop_timeout = 0;
if (priv->state != NM_VPN_SERVICE_STATE_STOPPED &&
priv->state != NM_VPN_SERVICE_STATE_INIT) {
@@ -459,7 +463,6 @@ _connect_generic (NMVPNPlugin *plugin,
return FALSE;
}
-
priv->interactive = FALSE;
if (details && !vpn_class->connect_interactive) {
g_set_error_literal (error, NM_VPN_PLUGIN_ERROR, NM_VPN_PLUGIN_ERROR_INTERACTIVE_NOT_SUPPORTED,
@@ -467,22 +470,29 @@ _connect_generic (NMVPNPlugin *plugin,
return FALSE;
}
- nm_vpn_plugin_set_state (plugin, NM_VPN_SERVICE_STATE_STARTING);
+ nm_clear_g_source (&priv->fail_stop_id);
if (details) {
priv->interactive = TRUE;
- success = vpn_class->connect_interactive (plugin, connection, details, error);
+ success = vpn_class->connect_interactive (plugin, connection, details, &local);
+ if (g_error_matches (local, NM_VPN_PLUGIN_ERROR, NM_VPN_PLUGIN_ERROR_INTERACTIVE_NOT_SUPPORTED)) {
+ /* Give NetworkManager a bit of time to fall back to Connect() */
+ fail_stop_timeout = 5;
+ }
+ g_propagate_error (error, local);
} else
success = vpn_class->connect (plugin, connection, error);
if (success) {
+ nm_vpn_plugin_set_state (plugin, NM_VPN_SERVICE_STATE_STARTING);
+
/* Add a timer to make sure we do not wait indefinitely for the successful connect. */
connect_timer_start (plugin);
} else {
/* Stop the plugin from an idle handler so that the Connect
* method return gets sent before the STOP StateChanged signal.
*/
- schedule_fail_stop (plugin);
+ schedule_fail_stop (plugin, fail_stop_timeout);
}
g_object_unref (connection);
@@ -608,7 +618,7 @@ impl_vpn_plugin_new_secrets (NMVPNPlugin *plugin,
/* Stop the plugin from and idle handler so that the NewSecrets
* method return gets sent before the STOP StateChanged signal.
*/
- schedule_fail_stop (plugin);
+ schedule_fail_stop (plugin, 0);
}
g_object_unref (connection);
diff --git a/libnm/nm-vpn-plugin-old.c b/libnm/nm-vpn-plugin-old.c
index deaa07dd21..f73f755aa5 100644
--- a/libnm/nm-vpn-plugin-old.c
+++ b/libnm/nm-vpn-plugin-old.c
@@ -273,12 +273,15 @@ fail_stop (gpointer data)
}
static void
-schedule_fail_stop (NMVpnPluginOld *plugin)
+schedule_fail_stop (NMVpnPluginOld *plugin, guint timeout_secs)
{
NMVpnPluginOldPrivate *priv = NM_VPN_PLUGIN_OLD_GET_PRIVATE (plugin);
nm_clear_g_source (&priv->fail_stop_id);
- priv->fail_stop_id = g_idle_add (fail_stop, plugin);
+ if (timeout_secs)
+ priv->fail_stop_id = g_timeout_add_seconds (timeout_secs, fail_stop, plugin);
+ else
+ priv->fail_stop_id = g_idle_add (fail_stop, plugin);
}
void
@@ -400,6 +403,7 @@ _connect_generic (NMVpnPluginOld *plugin,
NMConnection *connection;
gboolean success = FALSE;
GError *error = NULL;
+ guint fail_stop_timeout = 0;
if (priv->state != NM_VPN_SERVICE_STATE_STOPPED &&
priv->state != NM_VPN_SERVICE_STATE_INIT) {
@@ -430,15 +434,21 @@ _connect_generic (NMVpnPluginOld *plugin,
return;
}
- nm_vpn_plugin_old_set_state (plugin, NM_VPN_SERVICE_STATE_STARTING);
+ nm_clear_g_source (&priv->fail_stop_id);
if (details) {
priv->interactive = TRUE;
success = vpn_class->connect_interactive (plugin, connection, details, &error);
+ if (g_error_matches (error, NM_VPN_PLUGIN_ERROR, NM_VPN_PLUGIN_ERROR_INTERACTIVE_NOT_SUPPORTED)) {
+ /* Give NetworkManager a bit of time to fall back to Connect() */
+ fail_stop_timeout = 5;
+ }
} else
success = vpn_class->connect (plugin, connection, &error);
if (success) {
+ nm_vpn_plugin_old_set_state (plugin, NM_VPN_SERVICE_STATE_STARTING);
+
g_dbus_method_invocation_return_value (context, NULL);
/* Add a timer to make sure we do not wait indefinitely for the successful connect. */
@@ -449,7 +459,7 @@ _connect_generic (NMVpnPluginOld *plugin,
/* Stop the plugin from an idle handler so that the Connect
* method return gets sent before the STOP StateChanged signal.
*/
- schedule_fail_stop (plugin);
+ schedule_fail_stop (plugin, fail_stop_timeout);
}
g_object_unref (connection);
@@ -579,7 +589,7 @@ impl_vpn_plugin_old_new_secrets (NMVpnPluginOld *plugin,
/* Stop the plugin from and idle handler so that the NewSecrets
* method return gets sent before the STOP StateChanged signal.
*/
- schedule_fail_stop (plugin);
+ schedule_fail_stop (plugin, 0);
}
g_object_unref (connection);