wired: defer carrier-off events while connected by a few seconds

Don't immediately tear down an active wired connection when the carrier
flips to off, but wait a few seconds for it to come back before breaking
the user's network.
This commit is contained in:
Dan Williams 2009-09-28 14:12:09 -07:00
parent 4f6eef9e77
commit 963d055abf

View file

@ -110,6 +110,7 @@ typedef struct {
NMNetlinkMonitor * monitor; NMNetlinkMonitor * monitor;
gulong link_connected_id; gulong link_connected_id;
gulong link_disconnected_id; gulong link_disconnected_id;
guint carrier_action_defer_id;
Supplicant supplicant; Supplicant supplicant;
guint supplicant_timeout_id; guint supplicant_timeout_id;
@ -174,7 +175,41 @@ nm_ethernet_error_get_type (void)
} }
static void static void
set_carrier (NMDeviceEthernet *self, const gboolean carrier) carrier_action_defer_clear (NMDeviceEthernet *self)
{
NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (self);
if (priv->carrier_action_defer_id) {
g_source_remove (priv->carrier_action_defer_id);
priv->carrier_action_defer_id = 0;
}
}
static gboolean
carrier_action_defer_cb (gpointer user_data)
{
NMDeviceEthernet *self = NM_DEVICE_ETHERNET (user_data);
NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (self);
NMDeviceState state;
priv->carrier_action_defer_id = 0;
state = nm_device_interface_get_state (NM_DEVICE_INTERFACE (self));
if (state == NM_DEVICE_STATE_UNAVAILABLE) {
if (priv->carrier)
nm_device_state_changed (NM_DEVICE (self), NM_DEVICE_STATE_DISCONNECTED, NM_DEVICE_STATE_REASON_CARRIER);
} else if (state >= NM_DEVICE_STATE_DISCONNECTED) {
if (!priv->carrier)
nm_device_state_changed (NM_DEVICE (self), NM_DEVICE_STATE_UNAVAILABLE, NM_DEVICE_STATE_REASON_CARRIER);
}
return FALSE;
}
static void
set_carrier (NMDeviceEthernet *self,
const gboolean carrier,
const gboolean defer_action)
{ {
NMDeviceEthernetPrivate *priv; NMDeviceEthernetPrivate *priv;
NMDeviceState state; NMDeviceState state;
@ -185,18 +220,23 @@ set_carrier (NMDeviceEthernet *self, const gboolean carrier)
if (priv->carrier == carrier) if (priv->carrier == carrier)
return; return;
/* Clear any previous deferred action */
carrier_action_defer_clear (self);
priv->carrier = carrier; priv->carrier = carrier;
g_object_notify (G_OBJECT (self), NM_DEVICE_ETHERNET_CARRIER); g_object_notify (G_OBJECT (self), NM_DEVICE_ETHERNET_CARRIER);
state = nm_device_interface_get_state (NM_DEVICE_INTERFACE (self)); state = nm_device_interface_get_state (NM_DEVICE_INTERFACE (self));
nm_info ("(%s): carrier now %s (device state %d)", nm_device_get_iface (NM_DEVICE (self)), carrier ? "ON" : "OFF", state); nm_info ("(%s): carrier now %s (device state %d%s)",
if (state == NM_DEVICE_STATE_UNAVAILABLE) { nm_device_get_iface (NM_DEVICE (self)),
if (carrier) carrier ? "ON" : "OFF",
nm_device_state_changed (NM_DEVICE (self), NM_DEVICE_STATE_DISCONNECTED, NM_DEVICE_STATE_REASON_CARRIER); state,
} else if (state >= NM_DEVICE_STATE_DISCONNECTED) { defer_action ? ", deferring action for 4 seconds" : "");
if (!carrier)
nm_device_state_changed (NM_DEVICE (self), NM_DEVICE_STATE_UNAVAILABLE, NM_DEVICE_STATE_REASON_CARRIER); if (defer_action)
} priv->carrier_action_defer_id = g_timeout_add_seconds (4, carrier_action_defer_cb, self);
else
carrier_action_defer_cb (self);
} }
static void static void
@ -216,7 +256,7 @@ carrier_on (NMNetlinkMonitor *monitor,
if (!(caps & NM_DEVICE_CAP_CARRIER_DETECT)) if (!(caps & NM_DEVICE_CAP_CARRIER_DETECT))
return; return;
set_carrier (NM_DEVICE_ETHERNET (device), TRUE); set_carrier (self, TRUE, FALSE);
} }
} }
@ -232,12 +272,23 @@ carrier_off (NMNetlinkMonitor *monitor,
/* Make sure signal is for us */ /* Make sure signal is for us */
if (idx == priv->ifindex) { if (idx == priv->ifindex) {
NMDeviceState state;
gboolean defer = FALSE;
/* Ignore spurious netlink messages */ /* Ignore spurious netlink messages */
caps = nm_device_get_capabilities (device); caps = nm_device_get_capabilities (device);
if (!(caps & NM_DEVICE_CAP_CARRIER_DETECT)) if (!(caps & NM_DEVICE_CAP_CARRIER_DETECT))
return; return;
set_carrier (NM_DEVICE_ETHERNET (device), FALSE); /* Defer carrier-off event actions while connected by a few seconds
* so that tripping over a cable, power-cycling a switch, or breaking
* off the RJ45 locking tab isn't so catastrophic.
*/
state = nm_device_interface_get_state (NM_DEVICE_INTERFACE (self));
if (state > NM_DEVICE_STATE_DISCONNECTED)
defer = TRUE;
set_carrier (self, FALSE, defer);
} }
} }
@ -287,6 +338,10 @@ constructor (GType type,
} else } else
priv->carrier = !!(ifflags & IFF_LOWER_UP); priv->carrier = !!(ifflags & IFF_LOWER_UP);
nm_info ("(%s): carrier is %s",
nm_device_get_iface (NM_DEVICE (self)),
priv->carrier ? "ON" : "OFF");
/* Request link state again just in case an error occurred getting the /* Request link state again just in case an error occurred getting the
* initial link state. * initial link state.
*/ */
@ -1675,6 +1730,8 @@ dispose (GObject *object)
priv->link_disconnected_id = 0; priv->link_disconnected_id = 0;
} }
carrier_action_defer_clear (self);
if (priv->monitor) { if (priv->monitor) {
g_object_unref (priv->monitor); g_object_unref (priv->monitor);
priv->monitor = NULL; priv->monitor = NULL;