Blob Blame History Raw
commit e86ef05d84749c5a15d7bcf30f78056ca205489c
Author: Dan Williams <dcbw@redhat.com>
Date:   Wed Sep 1 17:08:10 2010 -0500

    wifi: ensure Enabled state is preserved regardless of rfkill (bgo #624479)
    
    Previously the "Enable Wireless" state was somewhat tied to rfkill state,
    in that when NM started up, rfkill state would take precedence over what
    was listed in the state file, and if you rmmodded your wifi driver and
    then modprobed it again after disabling wifi from the menu, wifi would
    magically become re-enabled becuase rfkill state changed.
    
    Fix that by creating a third wifi/wwan enable state that tracks the
    actual user preference instead of just the rfkill state so that when
    the user disables wifi it stays disabled, regardless of what happens
    with rfkill.

diff --git a/src/nm-manager.c b/src/nm-manager.c
index 1e9c3c6..abe30bf 100644
--- a/src/nm-manager.c
+++ b/src/nm-manager.c
@@ -187,7 +187,8 @@ struct PendingActivation {
 };
 
 typedef struct {
-	gboolean enabled;
+	gboolean user_enabled;
+	gboolean sw_enabled;
 	gboolean hw_enabled;
 	RfKillType rtype;
 	const char *desc;
@@ -1584,50 +1585,40 @@ write_value_to_state_file (const char *filename,
 	return ret;
 }
 
+static gboolean
+radio_enabled_for_rstate (RadioState *rstate)
+{
+	return rstate->user_enabled && rstate->sw_enabled && rstate->hw_enabled;
+}
+
+static gboolean
+radio_enabled_for_type (NMManager *self, RfKillType rtype)
+{
+	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
+
+	return radio_enabled_for_rstate (&priv->radio_states[rtype]);
+}
+
 static void
-manager_set_radio_enabled (NMManager *manager,
-                           RadioState *rstate,
-                           gboolean enabled)
+manager_update_radio_enabled (NMManager *self, RadioState *rstate)
 {
-	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager);
+	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
 	GSList *iter;
-	GError *error = NULL;
 
 	/* Do nothing for radio types not yet implemented */
 	if (!rstate->prop)
 		return;
 
-	if (rstate->enabled == enabled)
-		return;
-
-	/* Can't set wireless enabled if it's disabled in hardware */
-	if (!rstate->hw_enabled && enabled)
-		return;
-
-	rstate->enabled = enabled;
-
-	g_object_notify (G_OBJECT (manager), rstate->prop);
-
-	/* Update enabled key in state file */
-	if (priv->state_file) {
-		if (!write_value_to_state_file (priv->state_file,
-		                                "main", rstate->key,
-		                                G_TYPE_BOOLEAN, (gpointer) &enabled,
-		                                &error)) {
-			nm_log_warn (LOGD_CORE, "writing to state file %s failed: (%d) %s.",
-			             priv->state_file,
-			             error ? error->code : -1,
-			             (error && error->message) ? error->message : "unknown");
-		}
-	}
+	g_object_notify (G_OBJECT (self), rstate->prop);
 
 	/* Don't touch devices if asleep/networking disabled */
-	if (manager_sleeping (manager))
+	if (manager_sleeping (self))
 		return;
 
 	/* enable/disable wireless devices as required */
 	for (iter = priv->devices; iter; iter = iter->next) {
 		RfKillType devtype = RFKILL_TYPE_UNKNOWN;
+		gboolean enabled = radio_enabled_for_rstate (rstate);
 
 		g_object_get (G_OBJECT (iter->data), NM_DEVICE_INTERFACE_RFKILL_TYPE, &devtype, NULL);
 		if (devtype == rstate->rtype) {
@@ -1750,6 +1741,21 @@ nm_manager_get_modem_enabled_state (NMManager *self)
 }
 
 static void
+update_rstate_from_rfkill (RadioState *rstate, RfKillState rfkill)
+{
+	if (rfkill == RFKILL_UNBLOCKED) {
+		rstate->sw_enabled = TRUE;
+		rstate->hw_enabled = TRUE;
+	} else if (rfkill == RFKILL_SOFT_BLOCKED) {
+		rstate->sw_enabled = FALSE;
+		rstate->hw_enabled = TRUE;
+	} else if (rfkill == RFKILL_HARD_BLOCKED) {
+		rstate->sw_enabled = FALSE;
+		rstate->hw_enabled = FALSE;
+	}
+}
+
+static void
 manager_rfkill_update_one_type (NMManager *self,
                                 RadioState *rstate,
                                 RfKillType rtype)
@@ -1758,7 +1764,12 @@ manager_rfkill_update_one_type (NMManager *self,
 	RfKillState udev_state = RFKILL_UNBLOCKED;
 	RfKillState other_state = RFKILL_UNBLOCKED;
 	RfKillState composite;
-	gboolean new_e = TRUE, new_he = TRUE;
+	gboolean old_enabled, new_enabled, old_rfkilled, new_rfkilled;
+	gboolean old_hwe;
+
+	old_enabled = radio_enabled_for_rstate (rstate);
+	old_rfkilled = rstate->hw_enabled && rstate->sw_enabled;
+	old_hwe = rstate->hw_enabled;
 
 	udev_state = nm_udev_manager_get_rfkill_state (priv->udev_mgr, rtype);
 
@@ -1773,38 +1784,31 @@ manager_rfkill_update_one_type (NMManager *self,
 	else
 		composite = RFKILL_UNBLOCKED;
 
-	switch (composite) {
-	case RFKILL_UNBLOCKED:
-		new_e = TRUE;
-		new_he = TRUE;
-		break;
-	case RFKILL_SOFT_BLOCKED:
-		new_e = FALSE;
-		new_he = TRUE;
-		break;
-	case RFKILL_HARD_BLOCKED:
-		new_e = FALSE;
-		new_he = FALSE;
-		break;
-	default:
-		break;
-	}
+	update_rstate_from_rfkill (rstate, composite);
 
 	if (rstate->desc) {
-		nm_log_dbg (LOGD_RFKILL, "%s hw-enabled %d enabled %d",
-		            rstate->desc, new_he, new_e);
+		nm_log_dbg (LOGD_RFKILL, "%s hw-enabled %d sw-enabled %d",
+		            rstate->desc, rstate->hw_enabled, rstate->sw_enabled);
 	}
 
-	if (new_he != rstate->hw_enabled) {
+	/* Log new killswitch state */
+	new_rfkilled = rstate->hw_enabled && rstate->sw_enabled;
+	if (old_rfkilled != new_rfkilled) {
 		nm_log_info (LOGD_RFKILL, "%s now %s by radio killswitch",
 		             rstate->desc,
-		             (new_e && new_he) ? "enabled" : "disabled");
+		             new_rfkilled ? "enabled" : "disabled");
+	}
 
-		rstate->hw_enabled = new_he;
+	/* Send out property changed signal for HW enabled */
+	if (rstate->hw_enabled != old_hwe) {
 		if (rstate->hw_prop)
 			g_object_notify (G_OBJECT (self), rstate->hw_prop);
 	}
-	manager_set_radio_enabled (self, rstate, new_e);
+
+	/* And finally update the actual device radio state itself */
+	new_enabled = radio_enabled_for_rstate (rstate);
+	if (new_enabled != old_enabled)
+		manager_update_radio_enabled (self, rstate);
 }
 
 static void
@@ -2014,7 +2018,7 @@ add_device (NMManager *self, NMDevice *device)
 	NMConnection *existing = NULL;
 	GHashTableIter iter;
 	gpointer value;
-	gboolean managed = FALSE;
+	gboolean managed = FALSE, enabled = FALSE;
 
 	iface = nm_device_get_ip_iface (device);
 	g_assert (iface);
@@ -2053,14 +2057,15 @@ add_device (NMManager *self, NMDevice *device)
 		 * then set this device's rfkill state based on the global state.
 		 */
 		nm_manager_rfkill_update (self, RFKILL_TYPE_WLAN);
-		nm_device_interface_set_enabled (NM_DEVICE_INTERFACE (device),
-		                                 priv->radio_states[RFKILL_TYPE_WLAN].enabled);
+		enabled = radio_enabled_for_type (self, RFKILL_TYPE_WLAN);
+		nm_device_interface_set_enabled (NM_DEVICE_INTERFACE (device), enabled);
 	} else if (NM_IS_DEVICE_MODEM (device)) {
 		g_signal_connect (device, NM_DEVICE_MODEM_ENABLE_CHANGED,
 		                  G_CALLBACK (manager_modem_enabled_changed),
 		                  self);
 
 		nm_manager_rfkill_update (self, RFKILL_TYPE_WWAN);
+		enabled = radio_enabled_for_type (self, RFKILL_TYPE_WWAN);
 		/* Until we start respecting WWAN rfkill switches the modem itself
 		 * is the source of the enabled/disabled state, so the manager shouldn't
 		 * touch it here.
@@ -3305,13 +3310,13 @@ do_sleep_wake (NMManager *self)
 			 */
 			for (i = 0; i < RFKILL_TYPE_MAX; i++) {
 				RadioState *rstate = &priv->radio_states[i];
-				gboolean enabled = (rstate->hw_enabled && rstate->enabled);
+				gboolean enabled = radio_enabled_for_rstate (rstate);
 				RfKillType devtype = RFKILL_TYPE_UNKNOWN;
 
 				if (rstate->desc) {
-					nm_log_dbg (LOGD_RFKILL, "%s %s devices (hw_enabled %d, enabled %d)",
+					nm_log_dbg (LOGD_RFKILL, "%s %s devices (hw_enabled %d, sw_enabled %d, user_enabled %d)",
 					            enabled ? "enabling" : "disabling",
-					            rstate->desc, rstate->hw_enabled, rstate->enabled);
+					            rstate->desc, rstate->hw_enabled, rstate->sw_enabled, rstate->user_enabled);
 				}
 
 				g_object_get (G_OBJECT (device), NM_DEVICE_INTERFACE_RFKILL_TYPE, &devtype, NULL);
@@ -3879,34 +3884,21 @@ nm_manager_start (NMManager *self)
 	/* Set initial radio enabled/disabled state */
 	for (i = 0; i < RFKILL_TYPE_MAX; i++) {
 		RadioState *rstate = &priv->radio_states[i];
-		gboolean enabled = TRUE, hw_enabled = TRUE;
+		RfKillState udev_state;
 
 		if (!rstate->desc)
 			continue;
 
-		switch (nm_udev_manager_get_rfkill_state (priv->udev_mgr, i)) {
-		case RFKILL_UNBLOCKED:
-			enabled = TRUE;
-			hw_enabled = TRUE;
-			break;
-		case RFKILL_SOFT_BLOCKED:
-			enabled = FALSE;
-			hw_enabled = TRUE;
-			break;
-		case RFKILL_HARD_BLOCKED:
-			enabled = FALSE;
-			hw_enabled = FALSE;
-			break;
-		default:
-			break;
-		}
+		udev_state = nm_udev_manager_get_rfkill_state (priv->udev_mgr, i);
+		update_rstate_from_rfkill (rstate, udev_state);
 
-		rstate->hw_enabled = hw_enabled;
-		nm_log_info (LOGD_RFKILL, "%s %s by radio killswitch; %s by state file",
-		             rstate->desc,
-		             (rstate->hw_enabled && enabled) ? "enabled" : "disabled",
-		             (rstate->enabled) ? "enabled" : "disabled");
-		manager_set_radio_enabled (self, rstate, rstate->enabled && enabled);
+		if (rstate->desc) {
+			nm_log_info (LOGD_RFKILL, "%s %s by radio killswitch; %s by state file",
+				         rstate->desc,
+				         (rstate->hw_enabled && rstate->sw_enabled) ? "enabled" : "disabled",
+				         rstate->user_enabled ? "enabled" : "disabled");
+		}
+		manager_update_radio_enabled (self, rstate);
 	}
 
 	/* Log overall networking status - enabled/disabled */
@@ -4188,8 +4180,8 @@ nm_manager_get (const char *config_file,
 
 	priv->net_enabled = initial_net_enabled;
 
-	priv->radio_states[RFKILL_TYPE_WLAN].enabled = initial_wifi_enabled;
-	priv->radio_states[RFKILL_TYPE_WWAN].enabled = initial_wwan_enabled;
+	priv->radio_states[RFKILL_TYPE_WLAN].user_enabled = initial_wifi_enabled;
+	priv->radio_states[RFKILL_TYPE_WWAN].user_enabled = initial_wwan_enabled;
 
 	g_signal_connect (priv->sys_settings, "notify::" NM_SYSCONFIG_SETTINGS_UNMANAGED_SPECS,
 	                  G_CALLBACK (system_unmanaged_devices_changed_cb), singleton);
@@ -4335,6 +4327,42 @@ dispose (GObject *object)
 }
 
 static void
+manager_radio_user_toggled (NMManager *self,
+                            RadioState *rstate,
+                            gboolean enabled)
+{
+	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
+	GError *error = NULL;
+	gboolean old_enabled, new_enabled;
+
+	if (rstate->desc) {
+		nm_log_dbg (LOGD_RFKILL, "(%s): setting radio %s by user",
+		            rstate->desc,
+		            enabled ? "enabled" : "disabled");
+	}
+
+	/* Update enabled key in state file */
+	if (priv->state_file) {
+		if (!write_value_to_state_file (priv->state_file,
+		                                "main", rstate->key,
+		                                G_TYPE_BOOLEAN, (gpointer) &enabled,
+		                                &error)) {
+			nm_log_warn (LOGD_CORE, "writing to state file %s failed: (%d) %s.",
+			             priv->state_file,
+			             error ? error->code : -1,
+			             (error && error->message) ? error->message : "unknown");
+			g_clear_error (&error);
+		}
+	}
+
+	old_enabled = radio_enabled_for_rstate (rstate);
+	rstate->user_enabled = enabled;
+	new_enabled = radio_enabled_for_rstate (rstate);
+	if (new_enabled != old_enabled)
+		manager_update_radio_enabled (self, rstate);
+}
+
+static void
 set_property (GObject *object, guint prop_id,
 			  const GValue *value, GParamSpec *pspec)
 {
@@ -4347,14 +4375,14 @@ set_property (GObject *object, guint prop_id,
 		priv->net_enabled = g_value_get_boolean (value);
 		break;
 	case PROP_WIRELESS_ENABLED:
-		manager_set_radio_enabled (NM_MANAGER (object),
-		                           &priv->radio_states[RFKILL_TYPE_WLAN],
-		                           g_value_get_boolean (value));
+		manager_radio_user_toggled (NM_MANAGER (object),
+		                            &priv->radio_states[RFKILL_TYPE_WLAN],
+		                            g_value_get_boolean (value));
 		break;
 	case PROP_WWAN_ENABLED:
-		manager_set_radio_enabled (NM_MANAGER (object),
-		                           &priv->radio_states[RFKILL_TYPE_WWAN],
-		                           g_value_get_boolean (value));
+		manager_radio_user_toggled (NM_MANAGER (object),
+		                            &priv->radio_states[RFKILL_TYPE_WWAN],
+		                            g_value_get_boolean (value));
 		break;
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
@@ -4378,13 +4406,13 @@ get_property (GObject *object, guint prop_id,
 		g_value_set_boolean (value, priv->net_enabled);
 		break;
 	case PROP_WIRELESS_ENABLED:
-		g_value_set_boolean (value, priv->radio_states[RFKILL_TYPE_WLAN].enabled);
+		g_value_set_boolean (value, radio_enabled_for_type (self, RFKILL_TYPE_WLAN));
 		break;
 	case PROP_WIRELESS_HARDWARE_ENABLED:
 		g_value_set_boolean (value, priv->radio_states[RFKILL_TYPE_WLAN].hw_enabled);
 		break;
 	case PROP_WWAN_ENABLED:
-		g_value_set_boolean (value, priv->radio_states[RFKILL_TYPE_WWAN].enabled);
+		g_value_set_boolean (value, radio_enabled_for_type (self, RFKILL_TYPE_WWAN));
 		break;
 	case PROP_WWAN_HARDWARE_ENABLED:
 		g_value_set_boolean (value, priv->radio_states[RFKILL_TYPE_WWAN].hw_enabled);
@@ -4416,7 +4444,7 @@ nm_manager_init (NMManager *manager)
 	/* Initialize rfkill structures and states */
 	memset (priv->radio_states, 0, sizeof (priv->radio_states));
 
-	priv->radio_states[RFKILL_TYPE_WLAN].enabled = TRUE;
+	priv->radio_states[RFKILL_TYPE_WLAN].user_enabled = TRUE;
 	priv->radio_states[RFKILL_TYPE_WLAN].key = "WirelessEnabled";
 	priv->radio_states[RFKILL_TYPE_WLAN].prop = NM_MANAGER_WIRELESS_ENABLED;
 	priv->radio_states[RFKILL_TYPE_WLAN].hw_prop = NM_MANAGER_WIRELESS_HARDWARE_ENABLED;
@@ -4424,7 +4452,7 @@ nm_manager_init (NMManager *manager)
 	priv->radio_states[RFKILL_TYPE_WLAN].other_enabled_func = nm_manager_get_ipw_rfkill_state;
 	priv->radio_states[RFKILL_TYPE_WLAN].rtype = RFKILL_TYPE_WLAN;
 
-	priv->radio_states[RFKILL_TYPE_WWAN].enabled = TRUE;
+	priv->radio_states[RFKILL_TYPE_WWAN].user_enabled = TRUE;
 	priv->radio_states[RFKILL_TYPE_WWAN].key = "WWANEnabled";
 	priv->radio_states[RFKILL_TYPE_WWAN].prop = NM_MANAGER_WWAN_ENABLED;
 	priv->radio_states[RFKILL_TYPE_WWAN].hw_prop = NM_MANAGER_WWAN_HARDWARE_ENABLED;
@@ -4432,7 +4460,7 @@ nm_manager_init (NMManager *manager)
 	priv->radio_states[RFKILL_TYPE_WWAN].other_enabled_func = nm_manager_get_modem_enabled_state;
 	priv->radio_states[RFKILL_TYPE_WWAN].rtype = RFKILL_TYPE_WWAN;
 
-	priv->radio_states[RFKILL_TYPE_WIMAX].enabled = TRUE;
+	priv->radio_states[RFKILL_TYPE_WIMAX].user_enabled = TRUE;
 	priv->radio_states[RFKILL_TYPE_WIMAX].key = "WiMAXEnabled";
 	priv->radio_states[RFKILL_TYPE_WIMAX].prop = NULL;
 	priv->radio_states[RFKILL_TYPE_WIMAX].hw_prop = NULL;