diff --git a/src/nm-manager.c b/src/nm-manager.c index e1234ab0d5..fad14fcf91 100644 --- a/src/nm-manager.c +++ b/src/nm-manager.c @@ -244,6 +244,8 @@ typedef struct { guint timestamp_update_id; + GHashTable *nm_bridges; + gboolean disposed; } NMManagerPrivate; @@ -1130,6 +1132,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 +1267,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 +2272,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; @@ -3639,6 +3741,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); @@ -3655,6 +3764,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 @@ -4064,6 +4177,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, diff --git a/src/nm-system.c b/src/nm-system.c index 2f95076612..031daf8aaa 100644 --- a/src/nm-system.c +++ b/src/nm-system.c @@ -2363,14 +2363,16 @@ _bridge_create_compat (const char *iface) /** * nm_system_create_bridge: * @iface: Name bridging device to create + * @out_exists: on return, %TRUE if the bridge already exists * * Creates a new bridging device in the kernel. If a bridging device with - * the specified name already exists, it is being reused. + * the specified name already exists, it is reused and no error is returned, + * but @out_exists is set to %TRUE. * * Returns: %TRUE on success, %FALSE on error. */ gboolean -nm_system_create_bridge (const char *iface) +nm_system_create_bridge (const char *iface, gboolean *out_exists) { int err; @@ -2381,6 +2383,8 @@ nm_system_create_bridge (const char *iface) iface, strerror (-err)); return FALSE; } + if (out_exists && err == -EEXIST) + *out_exists = TRUE; return TRUE; } diff --git a/src/nm-system.h b/src/nm-system.h index ca07d0f25b..632786d3d2 100644 --- a/src/nm-system.h +++ b/src/nm-system.h @@ -128,7 +128,7 @@ gboolean nm_system_add_vlan_iface (NMConnection *connection, int parent_ifindex); gboolean nm_system_del_vlan_iface (const char *iface); -gboolean nm_system_create_bridge (const char *iface); +gboolean nm_system_create_bridge (const char *iface, gboolean *out_exists); gboolean nm_system_del_bridge (const char *iface); gboolean nm_system_bridge_attach (int master_ifindex,