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);