From 963d055abfb4a3235820acc00a3d87543a7dee30 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Mon, 28 Sep 2009 14:12:09 -0700 Subject: [PATCH] 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. --- src/nm-device-ethernet.c | 79 ++++++++++++++++++++++++++++++++++------ 1 file changed, 68 insertions(+), 11 deletions(-) diff --git a/src/nm-device-ethernet.c b/src/nm-device-ethernet.c index f5903e89a6..e0ff6b762e 100644 --- a/src/nm-device-ethernet.c +++ b/src/nm-device-ethernet.c @@ -110,6 +110,7 @@ typedef struct { NMNetlinkMonitor * monitor; gulong link_connected_id; gulong link_disconnected_id; + guint carrier_action_defer_id; Supplicant supplicant; guint supplicant_timeout_id; @@ -174,7 +175,41 @@ nm_ethernet_error_get_type (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; NMDeviceState state; @@ -185,18 +220,23 @@ set_carrier (NMDeviceEthernet *self, const gboolean carrier) if (priv->carrier == carrier) return; + /* Clear any previous deferred action */ + carrier_action_defer_clear (self); + priv->carrier = carrier; g_object_notify (G_OBJECT (self), NM_DEVICE_ETHERNET_CARRIER); 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); - if (state == NM_DEVICE_STATE_UNAVAILABLE) { - if (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 (!carrier) - nm_device_state_changed (NM_DEVICE (self), NM_DEVICE_STATE_UNAVAILABLE, NM_DEVICE_STATE_REASON_CARRIER); - } + nm_info ("(%s): carrier now %s (device state %d%s)", + nm_device_get_iface (NM_DEVICE (self)), + carrier ? "ON" : "OFF", + state, + defer_action ? ", deferring action for 4 seconds" : ""); + + 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 @@ -216,7 +256,7 @@ carrier_on (NMNetlinkMonitor *monitor, if (!(caps & NM_DEVICE_CAP_CARRIER_DETECT)) 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 */ if (idx == priv->ifindex) { + NMDeviceState state; + gboolean defer = FALSE; + /* Ignore spurious netlink messages */ caps = nm_device_get_capabilities (device); if (!(caps & NM_DEVICE_CAP_CARRIER_DETECT)) 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 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 * initial link state. */ @@ -1675,6 +1730,8 @@ dispose (GObject *object) priv->link_disconnected_id = 0; } + carrier_action_defer_clear (self); + if (priv->monitor) { g_object_unref (priv->monitor); priv->monitor = NULL;