From feeafb8cf1d91fa31a99923859cb28ddc9e172e5 Mon Sep 17 00:00:00 2001 From: Dan Winship Date: Mon, 12 Nov 2012 13:29:06 -0500 Subject: [PATCH] core: Update device activation for :carrier-detect Add a "need_carrier" argument to nm_device_is_available(), to allow distinguishing between "device is not available", "device is fully available", and "device is available except for not having carrier". Adjust various parts of NMDevice and NMManager to allow for the possibility of activating a connection with :carrier-detect = "no" on a device with no carrier, and to avoid auto-disconnecting devices with :carrier-detect = "on-activate". https://bugzilla.gnome.org/show_bug.cgi?id=688284 --- libnm-util/libnm-util.ver | 1 + libnm-util/nm-connection.c | 36 +++++++++++++++++++ libnm-util/nm-connection.h | 6 ++-- src/nm-device-adsl.c | 5 ++- src/nm-device-bond.c | 4 +-- src/nm-device-bridge.c | 4 +-- src/nm-device-bt.c | 6 ++-- src/nm-device-vlan.c | 4 +-- src/nm-device-wifi.c | 4 +-- src/nm-device-wired.c | 5 ++- src/nm-device.c | 72 +++++++++++++++++++++++++++++++++---- src/nm-device.h | 6 ++-- src/nm-manager.c | 26 ++++++++++---- src/wimax/nm-device-wimax.c | 10 +++--- 14 files changed, 150 insertions(+), 39 deletions(-) diff --git a/libnm-util/libnm-util.ver b/libnm-util/libnm-util.ver index cd144a150e..a03d836c68 100644 --- a/libnm-util/libnm-util.ver +++ b/libnm-util/libnm-util.ver @@ -11,6 +11,7 @@ global: nm_connection_error_get_type; nm_connection_error_quark; nm_connection_for_each_setting_value; + nm_connection_get_carrier_detect; nm_connection_get_id; nm_connection_get_path; nm_connection_get_setting; diff --git a/libnm-util/nm-connection.c b/libnm-util/nm-connection.c index 47a9ec090e..8715aed3fc 100644 --- a/libnm-util/nm-connection.c +++ b/libnm-util/nm-connection.c @@ -1249,6 +1249,42 @@ nm_connection_get_id (NMConnection *connection) return nm_setting_connection_get_id (s_con); } + +/** + * nm_connection_get_carrier_detect: + * @connection: the #NMConnection + * + * A shortcut to return the "carrier-detect" property from the + * connection's device-specific #NMSetting. + * + * Returns: the connection's "carrier-detect" property, or + * %NULL if the connection is for a device that does not + * support carrier detection. + **/ +const char * +nm_connection_get_carrier_detect (NMConnection *connection) +{ + NMSettingConnection *s_con; + NMSetting *s_type; + const char *type, *ret; + char *carrier_detect; + + s_con = nm_connection_get_setting_connection (connection); + g_return_val_if_fail (s_con != NULL, NULL); + + type = nm_setting_connection_get_connection_type (s_con); + s_type = nm_connection_get_setting_by_name (connection, type); + g_return_val_if_fail (s_type != NULL, NULL); + + if (!g_object_class_find_property (G_OBJECT_GET_CLASS (s_type), "carrier-detect")) + return NULL; + + g_object_get (G_OBJECT (s_type), "carrier-detect", &carrier_detect, NULL); + ret = g_intern_string (carrier_detect); + g_free (carrier_detect); + return ret; +} + /*************************************************************/ /** diff --git a/libnm-util/nm-connection.h b/libnm-util/nm-connection.h index 4d060cc98f..3ebffebcd5 100644 --- a/libnm-util/nm-connection.h +++ b/libnm-util/nm-connection.h @@ -183,9 +183,11 @@ GType nm_connection_lookup_setting_type (const char *name); GType nm_connection_lookup_setting_type_by_quark (GQuark error_quark); /* Helpers */ -const char * nm_connection_get_uuid (NMConnection *connection); +const char * nm_connection_get_uuid (NMConnection *connection); -const char * nm_connection_get_id (NMConnection *connection); +const char * nm_connection_get_id (NMConnection *connection); + +const char * nm_connection_get_carrier_detect (NMConnection *connection); NMSetting8021x * nm_connection_get_setting_802_1x (NMConnection *connection); NMSettingBluetooth * nm_connection_get_setting_bluetooth (NMConnection *connection); diff --git a/src/nm-device-adsl.c b/src/nm-device-adsl.c index dd03699389..1de31a018b 100644 --- a/src/nm-device-adsl.c +++ b/src/nm-device-adsl.c @@ -125,12 +125,11 @@ can_interrupt_activation (NMDevice *dev) } static gboolean -is_available (NMDevice *dev) +is_available (NMDevice *dev, gboolean need_carrier) { NMDeviceAdsl *self = NM_DEVICE_ADSL (dev); - /* Can't do anything if there isn't a carrier */ - if (!NM_DEVICE_ADSL_GET_PRIVATE (self)->carrier) + if (need_carrier && !NM_DEVICE_ADSL_GET_PRIVATE (self)->carrier) return FALSE; return TRUE; diff --git a/src/nm-device-bond.c b/src/nm-device-bond.c index 21d78575a9..45522b7917 100644 --- a/src/nm-device-bond.c +++ b/src/nm-device-bond.c @@ -109,10 +109,10 @@ get_generic_capabilities (NMDevice *dev) } static gboolean -is_available (NMDevice *dev) +is_available (NMDevice *dev, gboolean need_carrier) { if (NM_DEVICE_GET_CLASS (dev)->hw_is_up) - return NM_DEVICE_GET_CLASS (dev)->hw_is_up (dev); + return NM_DEVICE_GET_CLASS (dev)->hw_is_up (dev) || !need_carrier; return FALSE; } diff --git a/src/nm-device-bridge.c b/src/nm-device-bridge.c index 1ec8305a37..1da45826ae 100644 --- a/src/nm-device-bridge.c +++ b/src/nm-device-bridge.c @@ -109,10 +109,10 @@ get_generic_capabilities (NMDevice *dev) } static gboolean -is_available (NMDevice *dev) +is_available (NMDevice *dev, gboolean need_carrier) { if (NM_DEVICE_GET_CLASS (dev)->hw_is_up) - return NM_DEVICE_GET_CLASS (dev)->hw_is_up (dev); + return NM_DEVICE_GET_CLASS (dev)->hw_is_up (dev) || !need_carrier; return FALSE; } diff --git a/src/nm-device-bt.c b/src/nm-device-bt.c index 366e2683f0..8ecb9b119f 100644 --- a/src/nm-device-bt.c +++ b/src/nm-device-bt.c @@ -1067,7 +1067,7 @@ deactivate (NMDevice *device) /*****************************************************************************/ static gboolean -is_available (NMDevice *dev) +is_available (NMDevice *dev, gboolean need_carrier) { NMDeviceBt *self = NM_DEVICE_BT (dev); NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE (self); @@ -1096,7 +1096,7 @@ handle_availability_change (NMDeviceBt *self, return; } - available = nm_device_is_available (device); + available = nm_device_is_available (device, TRUE); if (available == old_available) return; @@ -1127,7 +1127,7 @@ set_mm_running (NMDeviceBt *self, gboolean running) nm_device_get_iface (NM_DEVICE (self)), running ? "available" : "unavailable"); - old_available = nm_device_is_available (NM_DEVICE (self)); + old_available = nm_device_is_available (NM_DEVICE (self), TRUE); priv->mm_running = running; handle_availability_change (self, old_available, NM_DEVICE_STATE_REASON_MODEM_MANAGER_UNAVAILABLE); diff --git a/src/nm-device-vlan.c b/src/nm-device-vlan.c index c224fcfd0f..7eb3d8d4ed 100644 --- a/src/nm-device-vlan.c +++ b/src/nm-device-vlan.c @@ -187,9 +187,9 @@ can_interrupt_activation (NMDevice *dev) } static gboolean -is_available (NMDevice *dev) +is_available (NMDevice *dev, gboolean need_carrier) { - return NM_DEVICE_VLAN_GET_PRIVATE (dev)->carrier ? TRUE : FALSE; + return NM_DEVICE_VLAN_GET_PRIVATE (dev)->carrier || !need_carrier; } /******************************************************************/ diff --git a/src/nm-device-wifi.c b/src/nm-device-wifi.c index 2a19a46bff..12b9b13c4b 100644 --- a/src/nm-device-wifi.c +++ b/src/nm-device-wifi.c @@ -1354,7 +1354,7 @@ complete_connection (NMDevice *device, } static gboolean -is_available (NMDevice *dev) +is_available (NMDevice *dev, gboolean need_carrier) { NMDeviceWifi *self = NM_DEVICE_WIFI (dev); NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); @@ -2396,7 +2396,7 @@ supplicant_iface_state_cb (NMSupplicantInterface *iface, /* If the interface can now be activated because the supplicant is now * available, transition to DISCONNECTED. */ - if ((devstate == NM_DEVICE_STATE_UNAVAILABLE) && nm_device_is_available (device)) { + if ((devstate == NM_DEVICE_STATE_UNAVAILABLE) && nm_device_is_available (device, TRUE)) { nm_device_state_changed (device, NM_DEVICE_STATE_DISCONNECTED, NM_DEVICE_STATE_REASON_SUPPLICANT_AVAILABLE); diff --git a/src/nm-device-wired.c b/src/nm-device-wired.c index fef10b63a9..20c16a65f8 100644 --- a/src/nm-device-wired.c +++ b/src/nm-device-wired.c @@ -289,12 +289,11 @@ can_interrupt_activation (NMDevice *dev) } static gboolean -is_available (NMDevice *dev) +is_available (NMDevice *dev, gboolean need_carrier) { NMDeviceWired *self = NM_DEVICE_WIRED (dev); - /* Can't do anything if there isn't a carrier */ - if (!NM_DEVICE_WIRED_GET_PRIVATE (self)->carrier) + if (need_carrier && !NM_DEVICE_WIRED_GET_PRIVATE (self)->carrier) return FALSE; return TRUE; diff --git a/src/nm-device.c b/src/nm-device.c index bd7d70b50a..d45857c655 100644 --- a/src/nm-device.c +++ b/src/nm-device.c @@ -1225,8 +1225,19 @@ carrier_changed (GObject *object, GParamSpec *param, gpointer user_data) return; } - if (priv->act_request && !carrier) - defer_action = TRUE; + if (priv->act_request) { + NMConnection *connection; + const char *carrier_detect; + + connection = nm_act_request_get_connection (priv->act_request); + carrier_detect = nm_connection_get_carrier_detect (connection); + + if ( g_strcmp0 (carrier_detect, "no") == 0 + || (!carrier && g_strcmp0 (carrier_detect, "on-activate") == 0)) + return; + else if (!carrier && (!carrier_detect || strcmp (carrier_detect, "yes") == 0)) + defer_action = TRUE; + } nm_log_info (LOGD_HW | LOGD_DEVICE, "(%s): carrier now %s (device state %d%s)", nm_device_get_iface (self), @@ -1273,7 +1284,7 @@ nm_device_get_connection (NMDevice *self) } gboolean -nm_device_is_available (NMDevice *self) +nm_device_is_available (NMDevice *self, gboolean need_carrier) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); @@ -1281,10 +1292,33 @@ nm_device_is_available (NMDevice *self) return FALSE; if (NM_DEVICE_GET_CLASS (self)->is_available) - return NM_DEVICE_GET_CLASS (self)->is_available (self); + return NM_DEVICE_GET_CLASS (self)->is_available (self, need_carrier); return TRUE; } +static gboolean +nm_device_has_available_connection (NMDevice *self) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + const GSList *connections, *iter; + + if (nm_device_is_available (self, TRUE)) + return TRUE; + if (!nm_device_is_available (self, FALSE)) + return FALSE; + + /* We're only available if there's an ignore-carrier connection */ + connections = nm_connection_provider_get_connections (priv->con_provider); + for (iter = connections; iter; iter = iter->next) { + const char *carrier_detect; + + carrier_detect = nm_connection_get_carrier_detect (NM_CONNECTION (iter->data)); + if (g_strcmp0 (carrier_detect, "no") == 0) + return TRUE; + } + return FALSE; +} + gboolean nm_device_get_enabled (NMDevice *self) { @@ -1355,6 +1389,7 @@ nm_device_get_best_auto_connection (NMDevice *dev, guint32 caps; GSList *iter, *available_conns; NMConnection *best_connection; + gboolean need_ignore_carrier = FALSE; g_return_val_if_fail (NM_IS_DEVICE (dev), NULL); g_return_val_if_fail (specific_object != NULL, NULL); @@ -1365,6 +1400,12 @@ nm_device_get_best_auto_connection (NMDevice *dev, if (!(caps & NM_DEVICE_CAP_NM_SUPPORTED)) return NULL; + if (!nm_device_is_available (dev, TRUE)) { + if (!nm_device_is_available (dev, FALSE)) + return NULL; + need_ignore_carrier = TRUE; + } + if (!NM_DEVICE_GET_CLASS (dev)->get_best_auto_connection) return NULL; @@ -1372,11 +1413,20 @@ nm_device_get_best_auto_connection (NMDevice *dev, for (iter = connections; iter; iter = iter->next) { NMConnection *connection = NM_CONNECTION (iter->data); NMSettingConnection *s_con; + const char *carrier_detect; s_con = nm_connection_get_setting_connection (connection); g_assert (s_con); - if (nm_setting_connection_get_autoconnect (s_con)) - available_conns = g_slist_prepend (available_conns, connection); + if (!nm_setting_connection_get_autoconnect (s_con)) + continue; + + if (need_ignore_carrier) { + carrier_detect = nm_connection_get_carrier_detect (connection); + if (g_strcmp0 (carrier_detect, "no") != 0) + continue; + } + + available_conns = g_slist_prepend (available_conns, connection); } if (!available_conns) @@ -5235,7 +5285,7 @@ nm_device_state_changed (NMDevice *device, * we can't change states again from the state handler for a variety of * reasons. */ - if (nm_device_is_available (device)) { + if (nm_device_has_available_connection (device)) { nm_log_dbg (LOGD_DEVICE, "(%s): device is available, will transition to DISCONNECTED", nm_device_get_iface (device)); nm_device_queue_state (device, NM_DEVICE_STATE_DISCONNECTED, NM_DEVICE_STATE_REASON_NONE); @@ -5244,6 +5294,14 @@ nm_device_state_changed (NMDevice *device, nm_device_get_iface (device)); } break; + case NM_DEVICE_STATE_DISCONNECTED: + /* If a previous connection was up despite not having carrier, then we're + * actually UNAVAILABLE now. + */ + if ( old_state > NM_DEVICE_STATE_DISCONNECTED + && !nm_device_is_available (device, TRUE)) + nm_device_queue_state (device, NM_DEVICE_STATE_UNAVAILABLE, NM_DEVICE_STATE_REASON_CARRIER); + break; case NM_DEVICE_STATE_ACTIVATED: nm_log_info (LOGD_DEVICE, "Activation (%s) successful, device activated.", nm_device_get_iface (device)); diff --git a/src/nm-device.h b/src/nm-device.h index 87e428f596..678dcc5c1a 100644 --- a/src/nm-device.h +++ b/src/nm-device.h @@ -118,7 +118,8 @@ typedef struct { guint32 (* get_type_capabilities) (NMDevice *self); guint32 (* get_generic_capabilities) (NMDevice *self); - gboolean (* is_available) (NMDevice *self); + gboolean (* is_available) (NMDevice *self, + gboolean need_carrier); gboolean (* get_enabled) (NMDevice *self); @@ -238,7 +239,8 @@ void nm_device_slave_notify_enslaved (NMDevice *dev, NMActRequest * nm_device_get_act_request (NMDevice *dev); NMConnection * nm_device_get_connection (NMDevice *dev); -gboolean nm_device_is_available (NMDevice *dev); +gboolean nm_device_is_available (NMDevice *dev, + gboolean need_carrier); NMConnection * nm_device_get_best_auto_connection (NMDevice *dev, GSList *connections, diff --git a/src/nm-manager.c b/src/nm-manager.c index 40c5692e4c..0248465ae6 100644 --- a/src/nm-manager.c +++ b/src/nm-manager.c @@ -1900,13 +1900,22 @@ add_device (NMManager *self, NMDevice *device) /* Check if we should assume the device's active connection by matching its * config with an existing system connection. */ - if (nm_device_can_assume_connections (device)) { + if ( nm_device_is_available (device, FALSE) + && nm_device_can_assume_connections (device)) { GSList *connections = NULL; connections = nm_settings_get_connections (priv->settings); existing = nm_device_connection_match_config (device, (const GSList *) connections); g_slist_free (connections); + if (existing && !nm_device_is_available (device, TRUE)) { + const char *carrier_detect; + + carrier_detect = nm_connection_get_carrier_detect (existing); + if (g_strcmp0 (carrier_detect, "no") != 0) + existing = NULL; + } + if (existing) nm_log_dbg (LOGD_DEVICE, "(%s): found existing device connection '%s'", nm_device_get_iface (device), @@ -1933,7 +1942,7 @@ add_device (NMManager *self, NMDevice *device) system_create_virtual_devices (self); /* If the device has a connection it can assume, do that now */ - if (existing && managed && nm_device_is_available (device)) { + if (existing && managed) { NMActiveConnection *ac; GError *error = NULL; @@ -2877,7 +2886,7 @@ nm_manager_activate_connection (NMManager *manager, * in the UNAVAILABLE state here. Since we want to use it right * away, we transition it immediately to DISCONNECTED. */ - if ( nm_device_is_available (device) + if ( nm_device_is_available (device, FALSE) && (nm_device_get_state (device) == NM_DEVICE_STATE_UNAVAILABLE)) { nm_device_state_changed (device, NM_DEVICE_STATE_DISCONNECTED, @@ -2888,9 +2897,14 @@ nm_manager_activate_connection (NMManager *manager, state = nm_device_get_state (device); if (state < NM_DEVICE_STATE_DISCONNECTED) { - g_set_error_literal (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_UNMANAGED_DEVICE, - "Device not managed by NetworkManager or unavailable"); - return NULL; + const char *carrier_detect = nm_connection_get_carrier_detect (connection); + + if ( state != NM_DEVICE_STATE_UNAVAILABLE + || g_strcmp0 (carrier_detect, "no") != 0) { + g_set_error_literal (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_UNMANAGED_DEVICE, + "Device not managed by NetworkManager or unavailable"); + return NULL; + } } /* If this is an autoconnect request, but the device isn't allowing autoconnect diff --git a/src/wimax/nm-device-wimax.c b/src/wimax/nm-device-wimax.c index 3e2e2833b4..27ac25ff43 100644 --- a/src/wimax/nm-device-wimax.c +++ b/src/wimax/nm-device-wimax.c @@ -271,7 +271,7 @@ update_availability (NMDeviceWimax *self, gboolean old_available) NMDeviceState state; gboolean new_available, changed = FALSE; - new_available = nm_device_is_available (device); + new_available = nm_device_is_available (device, TRUE); if (new_available == old_available) return FALSE; @@ -313,7 +313,7 @@ set_enabled (NMDevice *device, gboolean enabled) if (priv->enabled == enabled) return; - old_available = nm_device_is_available (NM_DEVICE (device)); + old_available = nm_device_is_available (NM_DEVICE (device), TRUE); priv->enabled = enabled; nm_log_dbg (LOGD_WIMAX, "(%s): radio now %s", @@ -629,7 +629,7 @@ get_generic_capabilities (NMDevice *dev) } static gboolean -is_available (NMDevice *device) +is_available (NMDevice *device, gboolean need_carrier) { NMDeviceWimaxPrivate *priv = NM_DEVICE_WIMAX_GET_PRIVATE (device); const char *iface = nm_device_get_iface (device); @@ -857,7 +857,7 @@ wmx_state_change_cb (struct wmxsdk *wmxsdk, return; state = nm_device_get_state (NM_DEVICE (self)); - old_available = nm_device_is_available (NM_DEVICE (self)); + old_available = nm_device_is_available (NM_DEVICE (self), TRUE); priv->status = new_status; if (priv->current_nsp) @@ -1302,7 +1302,7 @@ static gboolean sdk_action_defer_cb (gpointer user_data) { NMDeviceWimax *self = NM_DEVICE_WIMAX (user_data); - gboolean old_available = nm_device_is_available (NM_DEVICE (self)); + gboolean old_available = nm_device_is_available (NM_DEVICE (self), TRUE); NM_DEVICE_WIMAX_GET_PRIVATE (self)->sdk_action_defer_id = 0; update_availability (self, old_available);