Commit feeafb8c authored by Dan Winship's avatar Dan Winship

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
parent 5266e25e
......@@ -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;
......
......@@ -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;
}
/*************************************************************/
/**
......
......@@ -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);
......
......@@ -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;
......
......@@ -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;
}
......
......@@ -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;
}
......
......@@ -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);
......
......@@ -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;
}
/******************************************************************/
......
......@@ -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);
......
......@@ -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;
......
......@@ -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));
......
......@@ -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,
......
......@@ -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
......
......@@ -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);
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment