about summary refs log tree commit diff
path: root/src/nm-manager.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/nm-manager.c')
-rw-r--r--src/nm-manager.c208
1 files changed, 180 insertions, 28 deletions
diff --git a/src/nm-manager.c b/src/nm-manager.c
index fbc9d23d..3dc60d85 100644
--- a/src/nm-manager.c
+++ b/src/nm-manager.c
@@ -165,6 +165,8 @@ static GSList * remove_one_device (NMManager *manager,
                                    NMDevice *device,
                                    gboolean quitting);
 
+static void rfkill_change_wifi (const char *desc, gboolean enabled);
+
 #define SSD_POKE_INTERVAL 120
 #define ORIGDEV_TAG "originating-device"
 
@@ -244,6 +246,8 @@ typedef struct {
 
 	guint timestamp_update_id;
 
+	GHashTable *nm_bridges;
+
 	gboolean disposed;
 } NMManagerPrivate;
 
@@ -1130,6 +1134,92 @@ get_virtual_iface_placeholder_udi (void)
 	return g_strdup_printf ("/virtual/device/placeholder/%d", id++);
 }
 
+/***************************/
+
+/* FIXME: remove when we handle bridges non-destructively */
+
+#define NM_BRIDGE_FILE  NMRUNDIR "/nm-bridges"
+
+static void
+read_nm_created_bridges (NMManager *self)
+{
+	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
+	char *contents;
+	char **lines, **iter;
+	GTimeVal tv;
+	glong ts;
+
+	if (!g_file_get_contents (NM_BRIDGE_FILE, &contents, NULL, NULL))
+		return;
+
+	g_get_current_time (&tv);
+
+	lines = g_strsplit_set (contents, "\n", 0);
+	g_free (contents);
+
+	for (iter = lines; iter && *iter; iter++) {
+		if (g_str_has_prefix (*iter, "ts=")) {
+			errno = 0;
+			ts = strtol (*iter + 3, NULL, 10);
+			/* allow 30 minutes time difference before we ignore the file */
+			if (errno || ABS (tv.tv_sec - ts) > 1800)
+				goto out;
+		} else if (g_str_has_prefix (*iter, "iface="))
+			g_hash_table_insert (priv->nm_bridges, g_strdup (*iter + 6), GUINT_TO_POINTER (1));
+	}
+
+out:
+	g_strfreev (lines);
+	unlink (NM_BRIDGE_FILE);
+}
+
+static void
+write_nm_created_bridges (NMManager *self)
+{
+	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
+	GString *br_list;
+	GSList *iter;
+	GError *error = NULL;
+	GTimeVal tv;
+	gboolean found = FALSE;
+
+	/* write out nm-created bridges list */
+	br_list = g_string_sized_new (50);
+
+	/* Timestamp is first line */
+	g_get_current_time (&tv);
+	g_string_append_printf (br_list, "ts=%ld\n", tv.tv_sec);
+
+	for (iter = priv->devices; iter; iter = g_slist_next (iter)) {
+		NMDevice *device = iter->data;
+
+		if (nm_device_get_device_type (device) == NM_DEVICE_TYPE_BRIDGE) {
+			g_string_append_printf (br_list, "iface=%s\n", nm_device_get_iface (device));
+			found = TRUE;
+		}
+	}
+
+	if (found) {
+		if (!g_file_set_contents (NM_BRIDGE_FILE, br_list->str, -1, &error)) {
+			nm_log_warn (LOGD_BRIDGE, "Failed to write NetworkManager-created bridge list; "
+			             "on restart bridges may not be recognized. (%s)",
+			             error ? error->message : "unknown");
+			g_clear_error (&error);
+		}
+	}
+	g_string_free (br_list, TRUE);
+}
+
+static gboolean
+bridge_created_by_nm (NMManager *self, const char *iface)
+{
+	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
+
+	return (priv->nm_bridges && g_hash_table_lookup (priv->nm_bridges, iface));
+}
+
+/***************************/
+
 /**
  * system_create_virtual_device:
  * @self: the #NMManager
@@ -1179,12 +1269,21 @@ system_create_virtual_device (NMManager *self, NMConnection *connection)
 		device = nm_device_bond_new (udi, iface);
 		g_free (udi);
 	} else if (nm_connection_is_type (connection, NM_SETTING_BRIDGE_SETTING_NAME)) {
-		if (!nm_system_create_bridge (iface)) {
+		gboolean exists = FALSE;
+
+		if (!nm_system_create_bridge (iface, &exists)) {
 			nm_log_warn (LOGD_DEVICE, "(%s): failed to add bridging interface for '%s'",
 			             iface, nm_connection_get_id (connection));
 			goto out;
 		}
 
+		/* FIXME: remove when we handle bridges non-destructively */
+		if (exists && !bridge_created_by_nm (self, iface)) {
+			nm_log_warn (LOGD_DEVICE, "(%s): cannot use existing bridge for '%s'",
+			             iface, nm_connection_get_id (connection));
+			goto out;
+		}
+
 		udi = get_virtual_iface_placeholder_udi ();
 		device = nm_device_bridge_new (udi, iface);
 		g_free (udi);
@@ -2175,9 +2274,14 @@ udev_device_added_cb (NMUdevManager *udev_mgr,
 			device = nm_device_infiniband_new (sysfs_path, iface, driver);
 		else if (is_bond (ifindex))
 			device = nm_device_bond_new (sysfs_path, iface);
-		else if (is_bridge (ifindex))
-			device = nm_device_bridge_new (sysfs_path, iface);
-		else if (is_vlan (ifindex)) {
+		else if (is_bridge (ifindex)) {
+
+			/* FIXME: always create device when we handle bridges non-destructively */
+			if (bridge_created_by_nm (self, iface))
+				device = nm_device_bridge_new (sysfs_path, iface);
+			else
+				nm_log_info (LOGD_BRIDGE, "(%s): ignoring bridge not created by NetworkManager", iface);
+		} else if (is_vlan (ifindex)) {
 			int parent_ifindex = -1;
 			NMDevice *parent;
 
@@ -2215,23 +2319,32 @@ udev_device_removed_cb (NMUdevManager *manager,
 	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
 	NMDevice *device;
 	guint32 ifindex;
-	const char *iface = g_udev_device_get_name (udev_device);
-
-	/* Ignore PPP interfaces as they are the IP interface of a device,
-	 * but they come and go when the device gets activated or deactivated.
-	 * We don't want their transient nature to affect their master device.
-	 */
-	if (strncmp (iface, "ppp", 3) == 0)
-		return;
 
 	ifindex = g_udev_device_get_property_as_int (udev_device, "IFINDEX");
 	device = find_device_by_ifindex (self, ifindex);
 	if (!device) {
-		/* On removal we won't always be able to read properties anymore, as
-		 * they may have already been removed from sysfs.  Instead, we just
-		 * have to fall back to the device's interface name.
+		GSList *iter;
+		const char *iface = g_udev_device_get_name (udev_device);
+
+		/* On removal we aren't always be able to read properties like IFINDEX
+		 * anymore, as they may have already been removed from sysfs.  So we
+		 * have to fall back on device name (eg, interface name).
+		 *
+		 * Also, some devices (namely PPPoE (pppX), ADSL (nasX, pppX), and
+		 * mobile broadband (pppX, bnepX)) create a kernel netdevice for IP
+		 * communication (called the "IP interface" in NM) as part of the
+		 * connection process and thus the IP interface lifetime does not
+		 * correspond to the NMDevice lifetime.  For these devices we must
+		 * ignore removal events for the IP interface name otherwise the
+		 * NMDevice would be removed. Hence the usage here of
+		 * nm_device_get_iface() rather than nm_device_get_ip_iface().
 		 */
-		device = find_device_by_ip_iface (self, iface);
+		for (iter = priv->devices; iter; iter = g_slist_next (iter)) {
+			if (g_strcmp0 (nm_device_get_iface (NM_DEVICE (iter->data)), iface) == 0) {
+				device = iter->data;
+				break;
+			}
+		}
 	}
 
 	if (device)
@@ -2529,6 +2642,8 @@ ensure_master_active_connection (NMManager *self,
 					                                            nm_device_get_path (master_device),
 					                                            dbus_sender,
 					                                            error);
+					if (!master_ac)
+						g_prefix_error (error, "%s", "Master device activation failed: ");
 					g_slist_free (connections);
 					return master_ac;
 				}
@@ -2569,24 +2684,30 @@ ensure_master_active_connection (NMManager *self,
 			if (master_state != NM_DEVICE_STATE_DISCONNECTED)
 				continue;
 
-			return nm_manager_activate_connection (self,
-			                                       master_connection,
-			                                       NULL,
-			                                       nm_device_get_path (candidate),
-			                                       dbus_sender,
-			                                       error);
+			master_ac = nm_manager_activate_connection (self,
+			                                            master_connection,
+			                                            NULL,
+			                                            nm_device_get_path (candidate),
+			                                            dbus_sender,
+			                                            error);
+			if (!master_ac)
+				g_prefix_error (error, "%s", "Master device activation failed: ");
+			return master_ac;
 		}
 
 		/* Device described by master_connection may be a virtual one that's
 		 * not created yet.
 		 */
 		if (!found_device && connection_needs_virtual_device (master_connection)) {
-			return nm_manager_activate_connection (self,
-			                                       master_connection,
-			                                       NULL,
-			                                       NULL,
-			                                       dbus_sender,
-			                                       error);
+			master_ac = nm_manager_activate_connection (self,
+			                                            master_connection,
+			                                            NULL,
+			                                            NULL,
+			                                            dbus_sender,
+			                                            error);
+			if (!master_ac)
+				g_prefix_error (error, "%s", "Master device activation failed: ");
+			return master_ac;
 		}
 
 		g_set_error (error,
@@ -2774,6 +2895,16 @@ nm_manager_activate_connection (NMManager *manager,
 		return NULL;
 	}
 
+	/* If this is an autoconnect request, but the device isn't allowing autoconnect
+	 * right now, we reject it.
+	 */
+	if (!dbus_sender && !nm_device_autoconnect_allowed (device)) {
+		g_set_error (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_AUTOCONNECT_NOT_ALLOWED,
+		             "%s does not allow automatic connections at this time",
+		             nm_device_get_iface (device));
+		return NULL;
+	}
+
 	/* Try to find the master connection/device if the connection has a dependency */
 	if (!find_master (manager, connection, device, &master_connection, &master_device)) {
 		g_set_error_literal (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_UNKNOWN_DEVICE,
@@ -3621,6 +3752,13 @@ nm_manager_start (NMManager *self)
 	system_unmanaged_devices_changed_cb (priv->settings, NULL, self);
 	system_hostname_changed_cb (priv->settings, NULL, self);
 
+	/* FIXME: remove when we handle bridges non-destructively */
+	/* Read a list of bridges NM managed when it last quit, and only
+	 * manage those bridges to avoid conflicts with external tools.
+	 */
+	priv->nm_bridges = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
+	read_nm_created_bridges (self);
+
 	nm_udev_manager_query_devices (priv->udev_mgr);
 	nm_bluez_manager_query_devices (priv->bluez_mgr);
 
@@ -3637,6 +3775,10 @@ nm_manager_start (NMManager *self)
 	 * connection-added signals thus devices have to be created manually.
 	 */
 	system_create_virtual_devices (self);
+
+	/* FIXME: remove when we handle bridges non-destructively */
+	g_hash_table_unref (priv->nm_bridges);
+	priv->nm_bridges = NULL;
 }
 
 static gboolean
@@ -4016,6 +4158,12 @@ nm_manager_new (NMSettings *settings,
 	                  G_CALLBACK (bluez_manager_bdaddr_removed_cb),
 	                  singleton);
 
+	/* Force kernel WiFi rfkill state to follow NM saved wifi state in case
+	 * the BIOS doesn't save rfkill state, and to be consistent with user
+	 * changes to the WirelessEnabled property which toggles kernel rfkill.
+	 */
+	rfkill_change_wifi (priv->radio_states[RFKILL_TYPE_WLAN].desc, initial_wifi_enabled);
+
 	return singleton;
 }
 
@@ -4046,6 +4194,10 @@ dispose (GObject *object)
 
 	nm_auth_changed_func_unregister (authority_changed_cb, manager);
 
+	/* FIXME: remove when we handle bridges non-destructively */
+	write_nm_created_bridges (manager);
+
+	/* Remove all devices */
 	while (g_slist_length (priv->devices)) {
 		priv->devices = remove_one_device (manager,
 		                                   priv->devices,