diff --git a/src/nm-policy.c b/src/nm-policy.c index ca2d340b5..e21df28a9 100644 --- a/src/nm-policy.c +++ b/src/nm-policy.c @@ -1835,6 +1835,28 @@ vpn_connection_state_changed (NMVpnConnection *vpn, } } +static void +vpn_connection_retry_after_failure (NMVpnConnection *vpn, NMPolicy *policy) +{ + NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (policy); + NMActiveConnection *ac = NM_ACTIVE_CONNECTION (vpn); + NMConnection *connection = nm_active_connection_get_connection (ac); + GError *error = NULL; + + /* Attempt to reconnect VPN connections that failed after being connected */ + if (!nm_manager_activate_connection (priv->manager, + connection, + NULL, + NULL, + nm_active_connection_get_subject (ac), + &error)) { + nm_log_warn (LOGD_DEVICE, "VPN '%s' reconnect failed: %s", + nm_connection_get_id (connection), + error->message ? error->message : "unknown"); + g_clear_error (&error); + } +} + static void active_connection_state_changed (NMActiveConnection *active, GParamSpec *pspec, @@ -1859,6 +1881,9 @@ active_connection_added (NMManager *manager, g_signal_connect (active, NM_VPN_CONNECTION_INTERNAL_STATE_CHANGED, G_CALLBACK (vpn_connection_state_changed), policy); + g_signal_connect (active, NM_VPN_CONNECTION_INTERNAL_RETRY_AFTER_FAILURE, + G_CALLBACK (vpn_connection_retry_after_failure), + policy); } g_signal_connect (active, "notify::" NM_ACTIVE_CONNECTION_STATE, @@ -1876,6 +1901,9 @@ active_connection_removed (NMManager *manager, g_signal_handlers_disconnect_by_func (active, vpn_connection_state_changed, policy); + g_signal_handlers_disconnect_by_func (active, + vpn_connection_retry_after_failure, + policy); g_signal_handlers_disconnect_by_func (active, active_connection_state_changed, policy); diff --git a/src/vpn-manager/nm-vpn-connection.c b/src/vpn-manager/nm-vpn-connection.c index a6622f64f..a348ab718 100644 --- a/src/vpn-manager/nm-vpn-connection.c +++ b/src/vpn-manager/nm-vpn-connection.c @@ -111,6 +111,7 @@ typedef struct { enum { VPN_STATE_CHANGED, INTERNAL_STATE_CHANGED, + INTERNAL_RETRY_AFTER_FAILURE, LAST_SIGNAL }; @@ -442,6 +443,13 @@ _service_and_connection_can_persist (NMVpnConnection *self) NM_VPN_CONNECTION_GET_PRIVATE (self)->service_can_persist; } +static gboolean +_connection_only_can_persist (NMVpnConnection *self) +{ + return NM_VPN_CONNECTION_GET_PRIVATE (self)->connection_can_persist && + !NM_VPN_CONNECTION_GET_PRIVATE (self)->service_can_persist; +} + static void device_state_changed (NMActiveConnection *active, NMDevice *device, @@ -736,12 +744,22 @@ plugin_state_changed (DBusGProxy *proxy, nm_connection_clear_secrets (priv->connection); if ((priv->vpn_state >= STATE_WAITING) && (priv->vpn_state <= STATE_ACTIVATED)) { + VpnState old_state = priv->vpn_state; + nm_log_info (LOGD_VPN, "VPN plugin state change reason: %s (%d)", vpn_reason_to_string (priv->failure_reason), priv->failure_reason); _set_vpn_state (connection, STATE_FAILED, priv->failure_reason, FALSE); /* Reset the failure reason */ priv->failure_reason = NM_VPN_CONNECTION_STATE_REASON_UNKNOWN; + + /* If the connection failed, the service cannot persist, but the + * connection can persist, ask listeners to re-activate the connection. + */ + if ( old_state == STATE_ACTIVATED + && priv->vpn_state == STATE_FAILED + && _connection_only_can_persist (connection)) + g_signal_emit (connection, signals[INTERNAL_RETRY_AFTER_FAILURE], 0); } } else if (new_service_state == NM_VPN_SERVICE_STATE_STARTING && old_service_state == NM_VPN_SERVICE_STATE_STARTED) { @@ -2148,6 +2166,13 @@ nm_vpn_connection_class_init (NMVpnConnectionClass *connection_class) 0, NULL, NULL, NULL, G_TYPE_NONE, 3, G_TYPE_UINT, G_TYPE_UINT, G_TYPE_UINT); + signals[INTERNAL_RETRY_AFTER_FAILURE] = + g_signal_new (NM_VPN_CONNECTION_INTERNAL_RETRY_AFTER_FAILURE, + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + 0, NULL, NULL, NULL, + G_TYPE_NONE, 0); + nm_dbus_manager_register_exported_type (nm_dbus_manager_get (), G_TYPE_FROM_CLASS (object_class), &dbus_glib_nm_vpn_connection_object_info); diff --git a/src/vpn-manager/nm-vpn-connection.h b/src/vpn-manager/nm-vpn-connection.h index 156f79f77..74ea38a5d 100644 --- a/src/vpn-manager/nm-vpn-connection.h +++ b/src/vpn-manager/nm-vpn-connection.h @@ -42,7 +42,8 @@ /* Signals */ /* not exported: includes old reason code */ -#define NM_VPN_CONNECTION_INTERNAL_STATE_CHANGED "internal-state-changed" +#define NM_VPN_CONNECTION_INTERNAL_STATE_CHANGED "internal-state-changed" +#define NM_VPN_CONNECTION_INTERNAL_RETRY_AFTER_FAILURE "internal-retry-after-failure" typedef struct { @@ -62,6 +63,8 @@ typedef struct { NMVpnConnectionState new_state, NMVpnConnectionState old_state, NMVpnConnectionStateReason reason); + + void (*internal_failed_retry) (NMVpnConnection *connection); } NMVpnConnectionClass; GType nm_vpn_connection_get_type (void);