diff --git a/src/dhcp-manager/nm-dhcp-dhclient.c b/src/dhcp-manager/nm-dhcp-dhclient.c index f35465976..ac7d9f58a 100644 --- a/src/dhcp-manager/nm-dhcp-dhclient.c +++ b/src/dhcp-manager/nm-dhcp-dhclient.c @@ -18,6 +18,10 @@ * Copyright (C) 2005 - 2008 Red Hat, Inc. */ +#define _XOPEN_SOURCE +#include +#undef _XOPEN_SOURCE + #include #include #include @@ -74,6 +78,206 @@ get_leasefile_for_iface (const char * iface, const char *uuid) NM_DHCP_MANAGER_LEASE_FILE_EXT); } +static void +add_lease_option (GHashTable *hash, char *line) +{ + char *spc; + + spc = strchr (line, ' '); + if (!spc) { + g_warning ("%s: line '%s' did not contain a space", __func__, line); + return; + } + + /* If it's an 'option' line, split at second space */ + if (g_str_has_prefix (line, "option ")) { + spc = strchr (spc + 1, ' '); + if (!spc) { + g_warning ("%s: option line '%s' did not contain a second space", + __func__, line); + return; + } + } + + /* Split the line at the space */ + *spc = '\0'; + spc++; + + /* Kill the ';' at the end of the line, if any */ + if (*(spc + strlen (spc) - 1) == ';') + *(spc + strlen (spc) - 1) = '\0'; + + /* Treat 'interface' specially */ + if (g_str_has_prefix (line, "interface")) { + if (*(spc) == '"') + spc++; /* Jump past the " */ + if (*(spc + strlen (spc) - 1) == '"') + *(spc + strlen (spc) - 1) = '\0'; /* Kill trailing " */ + } + + g_hash_table_insert (hash, g_strdup (line), g_strdup (spc)); +} + +GSList * +nm_dhcp_client_get_lease_ip4_config (const char *iface, const char *uuid) +{ + GSList *parsed = NULL, *iter, *leases = NULL; + char *contents = NULL; + char *leasefile; + char **line, **split = NULL; + GHashTable *hash = NULL; + + leasefile = get_leasefile_for_iface (iface, uuid); + if (!leasefile) + return NULL; + + if (!g_file_test (leasefile, G_FILE_TEST_EXISTS)) + goto out; + + if (!g_file_get_contents (leasefile, &contents, NULL, NULL)) + goto out; + + split = g_strsplit_set (contents, "\n\r", -1); + g_free (contents); + if (!split) + goto out; + + for (line = split; line && *line; line++) { + *line = g_strstrip (*line); + + if (!strcmp (*line, "}")) { + /* Lease ends */ + parsed = g_slist_append (parsed, hash); + hash = NULL; + } else if (!strcmp (*line, "lease {")) { + /* Beginning of a new lease */ + if (hash) { + g_warning ("%s: lease file %s malformed; new lease started " + "without ending previous lease", + __func__, leasefile); + g_hash_table_destroy (hash); + } + + hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); + } else if (strlen (*line)) + add_lease_option (hash, *line); + } + g_strfreev (split); + + /* Check if the last lease in the file was properly ended */ + if (hash) { + g_warning ("%s: lease file %s malformed; new lease started " + "without ending previous lease", + __func__, leasefile); + g_hash_table_destroy (hash); + hash = NULL; + } + + for (iter = parsed; iter; iter = g_slist_next (iter)) { + NMIP4Config *ip4; + NMIP4Address *addr; + const char *data; + struct in_addr tmp; + guint32 prefix; + struct tm expire; + + hash = iter->data; + + /* Make sure this lease is for the interface we want */ + data = g_hash_table_lookup (hash, "interface"); + if (!data || strcmp (data, iface)) + continue; + + data = g_hash_table_lookup (hash, "expire"); + if (data) { + time_t now_tt; + struct tm *now; + + /* Read lease expiration (in UTC) */ + if (!strptime (data, "%w %Y/%m/%d %H:%M:%S", &expire)) { + g_warning ("%s: couldn't parse expire time '%s'", + __func__, data); + continue; + } + + now_tt = time (NULL); + now = gmtime(&now_tt); + + /* Ignore this lease if it's already expired */ + if (expire.tm_year < now->tm_year) + continue; + else if (expire.tm_year == now->tm_year) { + if (expire.tm_mon < now->tm_mon) + continue; + else if (expire.tm_mon == now->tm_mon) { + if (expire.tm_mday < now->tm_mday) + continue; + else if (expire.tm_mday == now->tm_mday) { + if (expire.tm_hour < now->tm_hour) + continue; + else if (expire.tm_hour == now->tm_hour) { + if (expire.tm_min < now->tm_min) + continue; + else if (expire.tm_min == now->tm_min) { + if (expire.tm_sec <= now->tm_sec) + continue; + } + } + } + } + } + /* If we get this far, the lease hasn't expired */ + } + + data = g_hash_table_lookup (hash, "fixed-address"); + if (!data) + continue; + + ip4 = nm_ip4_config_new (); + addr = nm_ip4_address_new (); + + /* IP4 address */ + if (!inet_pton (AF_INET, data, &tmp)) { + g_warning ("%s: couldn't parse IP4 address '%s'", __func__, data); + goto error; + } + nm_ip4_address_set_address (addr, tmp.s_addr); + + /* Netmask */ + data = g_hash_table_lookup (hash, "option subnet-mask"); + if (!data) + data = "255.255.255.0"; /* FIXME: assume class C? */ + if (!inet_pton (AF_INET, data, &tmp)) { + g_warning ("%s: couldn't parse IP4 subnet mask '%s'", __func__, data); + goto error; + } + prefix = nm_utils_ip4_netmask_to_prefix (tmp.s_addr); + nm_ip4_address_set_prefix (addr, prefix); + + /* Gateway */ + data = g_hash_table_lookup (hash, "option routers"); + if (data) { + if (!inet_pton (AF_INET, data, &tmp)) { + g_warning ("%s: couldn't parse IP4 gateway '%s'", __func__, data); + goto error; + } + nm_ip4_address_set_gateway (addr, tmp.s_addr); + } + + nm_ip4_config_take_address (ip4, addr); + leases = g_slist_append (leases, ip4); + continue; + + error: + nm_ip4_address_unref (addr); + g_object_unref (ip4); + } + +out: + g_slist_foreach (parsed, (GFunc) g_hash_table_destroy, NULL); + g_free (leasefile); + return leases; +} #define DHCP_CLIENT_ID_TAG "send dhcp-client-identifier" diff --git a/src/dhcp-manager/nm-dhcp-dhcpcd.c b/src/dhcp-manager/nm-dhcp-dhcpcd.c index a6ce8d21e..a8d929a52 100644 --- a/src/dhcp-manager/nm-dhcp-dhcpcd.c +++ b/src/dhcp-manager/nm-dhcp-dhcpcd.c @@ -49,6 +49,11 @@ get_pidfile_for_iface (const char * iface) NM_DHCP_MANAGER_PID_FILE_EXT); } +GSList * +nm_dhcp_client_get_lease_ip4_config (const char *iface, const char *uuid) +{ + return NULL; +} static void dhcpcd_child_setup (gpointer user_data G_GNUC_UNUSED) diff --git a/src/dhcp-manager/nm-dhcp-manager.c b/src/dhcp-manager/nm-dhcp-manager.c index bdd2695b2..f099ea289 100644 --- a/src/dhcp-manager/nm-dhcp-manager.c +++ b/src/dhcp-manager/nm-dhcp-manager.c @@ -1121,3 +1121,16 @@ nm_dhcp_manager_set_hostname_provider (NMDHCPManager *manager, g_object_weak_ref (G_OBJECT (provider), hostname_provider_destroyed, manager); } } + +GSList * +nm_dhcp_manager_get_lease_ip4_config (NMDHCPManager *self, + const char *iface, + const char *uuid) +{ + g_return_val_if_fail (NM_IS_DHCP_MANAGER (self), NULL); + g_return_val_if_fail (iface != NULL, NULL); + g_return_val_if_fail (uuid != NULL, NULL); + + return nm_dhcp_client_get_lease_ip4_config (iface, uuid); +} + diff --git a/src/dhcp-manager/nm-dhcp-manager.h b/src/dhcp-manager/nm-dhcp-manager.h index 6880a2336..124008493 100644 --- a/src/dhcp-manager/nm-dhcp-manager.h +++ b/src/dhcp-manager/nm-dhcp-manager.h @@ -107,6 +107,10 @@ gboolean nm_dhcp_manager_foreach_dhcp4_option (NMDHCPManager *self, GHFunc func, gpointer user_data); +GSList * nm_dhcp_manager_get_lease_ip4_config (NMDHCPManager *self, + const char *iface, + const char *uuid); + /* The following are implemented by the DHCP client backends */ GPid nm_dhcp_client_start (NMDHCPDevice *device, const char *uuid, @@ -118,6 +122,9 @@ gboolean nm_dhcp_client_process_classless_routes (GHashTable *options, NMIP4Config *ip4_config, guint32 *gwaddr); +GSList * nm_dhcp_client_get_lease_ip4_config (const char *iface, + const char *uuid); + /* Test functions */ NMIP4Config *nm_dhcp_manager_options_to_ip4_config (const char *iface, GHashTable *options); diff --git a/src/nm-device-ethernet.c b/src/nm-device-ethernet.c index 759a183ea..2c68f790d 100644 --- a/src/nm-device-ethernet.c +++ b/src/nm-device-ethernet.c @@ -53,6 +53,7 @@ #include "ppp-manager/nm-ppp-manager.h" #include "nm-utils.h" #include "nm-properties-changed-signal.h" +#include "nm-dhcp-manager.h" #include "nm-device-ethernet-glue.h" @@ -280,8 +281,8 @@ constructor (GType type, guint32 caps; object = G_OBJECT_CLASS (nm_device_ethernet_parent_class)->constructor (type, - n_construct_params, - construct_params); + n_construct_params, + construct_params); if (!object) return NULL; @@ -1472,6 +1473,196 @@ spec_match_list (NMDevice *device, const GSList *specs) return matched; } +static gboolean +wired_match_config (NMDevice *self, NMConnection *connection) +{ + NMSettingWired *s_wired; + struct ether_addr ether; + const GByteArray *s_ether; + + s_wired = (NMSettingWired *) nm_connection_get_setting (connection, NM_TYPE_SETTING_WIRED); + if (!s_wired) + return FALSE; + + /* MAC address check */ + s_ether = nm_setting_wired_get_mac_address (s_wired); + if (s_ether) { + nm_device_ethernet_get_address (NM_DEVICE_ETHERNET (self), ðer); + + if (memcmp (s_ether->data, ether.ether_addr_octet, ETH_ALEN)) + return FALSE; + } + + return TRUE; +} + +typedef struct { + int ifindex; + NMIP4Address *addr; + gboolean found; +} AddrData; + +static void +check_one_address (struct nl_object *object, void *user_data) +{ + AddrData *data = user_data; + struct rtnl_addr *addr = (struct rtnl_addr *) object; + struct nl_addr *local; + struct in_addr tmp; + + if (rtnl_addr_get_ifindex (addr) != data->ifindex) + return; + if (rtnl_addr_get_family (addr) != AF_INET) + return; + + if (nm_ip4_address_get_prefix (data->addr) != rtnl_addr_get_prefixlen (addr)) + return; + + local = rtnl_addr_get_local (addr); + if (nl_addr_get_family (local) != AF_INET) + return; + if (nl_addr_get_len (local) != sizeof (struct in_addr)) + return; + if (!nl_addr_get_binary_addr (local)) + return; + + memcpy (&tmp, nl_addr_get_binary_addr (local), nl_addr_get_len (local)); + if (tmp.s_addr != nm_ip4_address_get_address (data->addr)) + return; + + /* Yay, found it */ + data->found = TRUE; +} + +static gboolean +ip4_match_config (NMDevice *self, NMConnection *connection) +{ + NMSettingIP4Config *s_ip4; + NMSettingConnection *s_con; + struct nl_handle *nlh = NULL; + struct nl_cache *addr_cache = NULL; + int i, num; + GSList *leases, *iter; + NMDHCPManager *dhcp_mgr; + const char *method; + int ifindex; + AddrData check_data; + + ifindex = nm_device_ethernet_get_ifindex (NM_DEVICE_ETHERNET (self)); + + s_con = (NMSettingConnection *) nm_connection_get_setting (connection, NM_TYPE_SETTING_CONNECTION); + g_assert (s_con); + g_assert (nm_setting_connection_get_uuid (s_con)); + + s_ip4 = (NMSettingIP4Config *) nm_connection_get_setting (connection, NM_TYPE_SETTING_IP4_CONFIG); + if (!s_ip4) + return FALSE; + + /* Read all the device's IP addresses */ + nlh = nm_netlink_get_default_handle (); + if (!nlh) + return FALSE; + + addr_cache = rtnl_addr_alloc_cache (nlh); + if (!addr_cache) + return FALSE; + nl_cache_mngt_provide (addr_cache); + + /* Get any saved leases that apply to this connection */ + dhcp_mgr = nm_dhcp_manager_get (); + leases = nm_dhcp_manager_get_lease_ip4_config (dhcp_mgr, + nm_device_get_iface (self), + nm_setting_connection_get_uuid (s_con)); + g_object_unref (dhcp_mgr); + + method = nm_setting_ip4_config_get_method (s_ip4); + if (!strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_AUTO)) { + gboolean found = FALSE; + + /* Find at least one lease's address on the device */ + for (iter = leases; iter; iter = g_slist_next (iter)) { + NMIP4Config *addr = iter->data; + + memset (&check_data, 0, sizeof (check_data)); + check_data.ifindex = ifindex; + check_data.found = FALSE; + check_data.addr = nm_ip4_config_get_address (addr, 0); + + nl_cache_foreach (addr_cache, check_one_address, &check_data); + if (check_data.found) { + found = TRUE; /* Yay, device has same address as a lease */ + break; + } + } + g_slist_foreach (leases, (GFunc) g_object_unref, NULL); + g_slist_free (leases); + return found; + } else { + /* Maybe the connection used to be DHCP and there are stale leases; ignore them */ + g_slist_foreach (leases, (GFunc) g_object_unref, NULL); + g_slist_free (leases); + } + + /* 'shared' and 'link-local' aren't supported methods because 'shared' + * requires too much iptables and dnsmasq state to be reclaimed, and + * avahi-autoipd isn't smart enough to allow the link-local address to be + * determined at any point other than when it was first assigned. + */ + if (strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_MANUAL)) + return FALSE; + + /* Everything below for static addressing */ + + /* Find all IP4 addresses of this connection in the device's address list */ + num = nm_setting_ip4_config_get_num_addresses (s_ip4); + for (i = 0; i < num; i++) { + memset (&check_data, 0, sizeof (check_data)); + check_data.ifindex = ifindex; + check_data.found = FALSE; + check_data.addr = nm_setting_ip4_config_get_address (s_ip4, i); + + nl_cache_foreach (addr_cache, check_one_address, &check_data); + if (!check_data.found) + return FALSE; + } + + /* Success; all the connection's static IP addresses are assigned to the device */ + return TRUE; +} + +static NMConnection * +connection_match_config (NMDevice *self, const GSList *connections) +{ + GSList *iter; + NMSettingConnection *s_con; + + for (iter = (GSList *) connections; iter; iter = g_slist_next (iter)) { + NMConnection *candidate = NM_CONNECTION (iter->data); + + s_con = (NMSettingConnection *) nm_connection_get_setting (candidate, NM_TYPE_SETTING_CONNECTION); + g_assert (s_con); + if (strcmp (nm_setting_connection_get_connection_type (s_con), NM_SETTING_WIRED_SETTING_NAME)) + continue; + + /* Can't assume 802.1x or PPPoE connections; they have too much state + * that's impossible to get on-the-fly from PPPoE or the supplicant. + */ + if ( nm_connection_get_setting (candidate, NM_TYPE_SETTING_802_1X) + || nm_connection_get_setting (candidate, NM_TYPE_SETTING_PPPOE)) + continue; + + if (!wired_match_config (self, candidate)) + continue; + + if (!ip4_match_config (self, candidate)) + continue; + + return candidate; + } + + return NULL; +} + static void dispose (GObject *object) { @@ -1587,6 +1778,7 @@ nm_device_ethernet_class_init (NMDeviceEthernetClass *klass) parent_class->act_stage4_get_ip4_config = real_act_stage4_get_ip4_config; parent_class->deactivate_quickly = real_deactivate_quickly; parent_class->spec_match_list = spec_match_list; + parent_class->connection_match_config = connection_match_config; /* properties */ g_object_class_install_property diff --git a/src/nm-device-interface.c b/src/nm-device-interface.c index 5647adb42..7f287a869 100644 --- a/src/nm-device-interface.c +++ b/src/nm-device-interface.c @@ -282,8 +282,29 @@ gboolean nm_device_interface_spec_match_list (NMDeviceInterface *device, const GSList *specs) { + g_return_val_if_fail (NM_IS_DEVICE_INTERFACE (device), FALSE); + if (NM_DEVICE_INTERFACE_GET_INTERFACE (device)->spec_match_list) return NM_DEVICE_INTERFACE_GET_INTERFACE (device)->spec_match_list (device, specs); return FALSE; } +NMConnection * +nm_device_interface_connection_match_config (NMDeviceInterface *device, + const GSList *connections) +{ + g_return_val_if_fail (NM_IS_DEVICE_INTERFACE (device), NULL); + + if (NM_DEVICE_INTERFACE_GET_INTERFACE (device)->connection_match_config) + return NM_DEVICE_INTERFACE_GET_INTERFACE (device)->connection_match_config (device, connections); + return NULL; +} + +gboolean +nm_device_interface_can_assume_connection (NMDeviceInterface *device) +{ + g_return_val_if_fail (NM_IS_DEVICE_INTERFACE (device), FALSE); + + return !!NM_DEVICE_INTERFACE_GET_INTERFACE (device)->connection_match_config; +} + diff --git a/src/nm-device-interface.h b/src/nm-device-interface.h index 760cccacc..97a95da47 100644 --- a/src/nm-device-interface.h +++ b/src/nm-device-interface.h @@ -88,6 +88,8 @@ struct _NMDeviceInterface { gboolean (*spec_match_list) (NMDeviceInterface *device, const GSList *specs); + NMConnection * (*connection_match_config) (NMDeviceInterface *device, const GSList *specs); + /* Signals */ void (*state_changed) (NMDeviceInterface *device, NMDeviceState new_state, @@ -115,4 +117,9 @@ NMDeviceState nm_device_interface_get_state (NMDeviceInterface *device); gboolean nm_device_interface_spec_match_list (NMDeviceInterface *device, const GSList *specs); +NMConnection * nm_device_interface_connection_match_config (NMDeviceInterface *device, + const GSList *connections); + +gboolean nm_device_interface_can_assume_connection (NMDeviceInterface *device); + #endif /* NM_DEVICE_INTERFACE_H */ diff --git a/src/nm-device.c b/src/nm-device.c index 6f7423b38..56634ef8f 100644 --- a/src/nm-device.c +++ b/src/nm-device.c @@ -121,7 +121,8 @@ static gboolean nm_device_activate (NMDeviceInterface *device, NMActRequest *req, GError **error); static void nm_device_deactivate (NMDeviceInterface *device, NMDeviceStateReason reason); -static gboolean nm_device_spec_match_list (NMDeviceInterface *device, const GSList *specs); +static gboolean spec_match_list (NMDeviceInterface *device, const GSList *specs); +static NMConnection *connection_match_config (NMDeviceInterface *device, const GSList *connections); static void nm_device_activate_schedule_stage5_ip_config_commit (NMDevice *self); @@ -139,7 +140,8 @@ device_interface_init (NMDeviceInterface *device_interface_class) device_interface_class->check_connection_compatible = check_connection_compatible; device_interface_class->activate = nm_device_activate; device_interface_class->deactivate = nm_device_deactivate; - device_interface_class->spec_match_list = nm_device_spec_match_list; + device_interface_class->spec_match_list = spec_match_list; + device_interface_class->connection_match_config = connection_match_config; } @@ -2202,25 +2204,45 @@ dispose (GObject *object) { NMDevice *self = NM_DEVICE (object); NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + gboolean take_down = TRUE; if (priv->disposed || !priv->initialized) goto out; priv->disposed = TRUE; + /* Don't down can-assume-connection capable devices that are activated with + * a connection that can be assumed. + */ + if (nm_device_interface_can_assume_connection (NM_DEVICE_INTERFACE (self)) + && (nm_device_get_state (self) == NM_DEVICE_STATE_ACTIVATED)) { + NMConnection *connection; + NMSettingIP4Config *s_ip4; + const char *method = NULL; + + /* Only system connections can be left up */ + connection = nm_act_request_get_connection (priv->act_request); + if ( connection + && (nm_connection_get_scope (connection) == NM_CONNECTION_SCOPE_SYSTEM)) { + + /* Only static or DHCP connections can be left up */ + s_ip4 = (NMSettingIP4Config *) nm_connection_get_setting (connection, NM_TYPE_SETTING_IP4_CONFIG); + g_assert (s_ip4); + + method = nm_setting_ip4_config_get_method (s_ip4); + if ( !method + || !strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_AUTO) + || !strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_MANUAL)) + take_down = FALSE; + } + } + if (priv->failed_to_disconnected_id) { g_source_remove (priv->failed_to_disconnected_id); priv->failed_to_disconnected_id = 0; } - /* - * In dispose, you are supposed to free all types referenced from this - * object which might themselves hold a reference to self. Generally, - * the most simple solution is to unref all members on which you own a - * reference. - */ - - if (priv->managed) { + if (priv->managed && take_down) { NMDeviceStateReason ignored = NM_DEVICE_STATE_REASON_NONE; nm_device_take_down (self, FALSE, NM_DEVICE_STATE_REASON_REMOVED); @@ -2231,7 +2253,8 @@ dispose (GObject *object) activation_source_clear (self, TRUE); - nm_device_set_use_dhcp (self, FALSE); + if (!take_down) + nm_device_set_use_dhcp (self, FALSE); if (priv->dnsmasq_manager) { if (priv->dnsmasq_state_id) { @@ -2577,7 +2600,7 @@ nm_device_set_managed (NMDevice *device, } static gboolean -nm_device_spec_match_list (NMDeviceInterface *device, const GSList *specs) +spec_match_list (NMDeviceInterface *device, const GSList *specs) { NMDevice *self; @@ -2590,6 +2613,16 @@ nm_device_spec_match_list (NMDeviceInterface *device, const GSList *specs) return FALSE; } +static NMConnection * +connection_match_config (NMDeviceInterface *device, const GSList *connections) +{ + g_return_val_if_fail (device != NULL, FALSE); + + if (NM_DEVICE_GET_CLASS (device)->connection_match_config) + return NM_DEVICE_GET_CLASS (device)->connection_match_config (NM_DEVICE (device), connections); + return NULL; +} + void nm_device_set_dhcp_timeout (NMDevice *device, guint32 timeout) { diff --git a/src/nm-device.h b/src/nm-device.h index 31c58fb54..f3d63235e 100644 --- a/src/nm-device.h +++ b/src/nm-device.h @@ -106,6 +106,8 @@ typedef struct { gboolean (* can_interrupt_activation) (NMDevice *self); gboolean (* spec_match_list) (NMDevice *self, const GSList *specs); + + NMConnection * (* connection_match_config) (NMDevice *self, const GSList *connections); } NMDeviceClass; diff --git a/src/nm-manager.c b/src/nm-manager.c index 731589c40..87338a10d 100644 --- a/src/nm-manager.c +++ b/src/nm-manager.c @@ -348,12 +348,24 @@ manager_device_state_changed (NMDevice *device, /* Removes a device from a device list; returns the start of the new device list */ static GSList * -remove_one_device (NMManager *manager, GSList *list, NMDevice *device) +remove_one_device (NMManager *manager, + GSList *list, + NMDevice *device, + gboolean quitting) { NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager); - if (nm_device_get_managed (device)) - nm_device_set_managed (device, FALSE, NM_DEVICE_STATE_REASON_REMOVED); + if (nm_device_get_managed (device)) { + gboolean unmanage = !quitting; + + /* Don't unmanage active assume-connection-capable devices at shutdown */ + if ( nm_device_interface_can_assume_connection (NM_DEVICE_INTERFACE (device)) + && nm_device_get_state (device) == NM_DEVICE_STATE_ACTIVATED) + unmanage = FALSE; + + if (unmanage) + nm_device_set_managed (device, FALSE, NM_DEVICE_STATE_REASON_REMOVED); + } g_signal_handlers_disconnect_by_func (device, manager_device_state_changed, manager); @@ -372,7 +384,7 @@ modem_removed (NMModemManager *modem_manager, NMManager *self = NM_MANAGER (user_data); NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); - priv->devices = remove_one_device (self, priv->devices, modem); + priv->devices = remove_one_device (self, priv->devices, modem, FALSE); } static void @@ -1153,6 +1165,10 @@ add_device (NMManager *self, NMDevice *device) char *path; static guint32 devcount = 0; const GSList *unmanaged_specs; + GSList *connections = NULL; + NMConnection *existing; + GHashTableIter iter; + gpointer value; priv->devices = g_slist_append (priv->devices, device); @@ -1189,6 +1205,16 @@ add_device (NMManager *self, NMDevice *device) nm_info ("(%s): exported as %s", iface, path); g_free (path); + /* Check if we should assume the device's active connection by matching its + * config with an existing system connection. + */ + g_hash_table_iter_init (&iter, priv->system_connections); + while (g_hash_table_iter_next (&iter, NULL, &value)) + connections = g_slist_append (connections, value); + existing = nm_device_interface_connection_match_config (NM_DEVICE_INTERFACE (device), + (const GSList *) connections); + g_slist_free (connections); + /* Start the device if it's supposed to be managed */ unmanaged_specs = nm_sysconfig_settings_get_unmanaged_specs (priv->sys_settings); if (!priv->sleeping && !nm_device_interface_spec_match_list (NM_DEVICE_INTERFACE (device), unmanaged_specs)) @@ -1302,7 +1328,7 @@ bluez_manager_resync_devices (NMManager *self) priv->devices = keep; while (g_slist_length (gone)) - gone = remove_one_device (self, gone, NM_DEVICE (gone->data)); + gone = remove_one_device (self, gone, NM_DEVICE (gone->data), FALSE); } else { g_slist_free (keep); g_slist_free (gone); @@ -1372,7 +1398,7 @@ bluez_manager_bdaddr_removed_cb (NMBluezManager *bluez_mgr, NMDevice *device = NM_DEVICE (iter->data); if (!strcmp (nm_device_get_udi (device), object_path)) { - priv->devices = remove_one_device (self, priv->devices, device); + priv->devices = remove_one_device (self, priv->devices, device, FALSE); break; } } @@ -1433,8 +1459,7 @@ udev_device_removed_cb (NMUdevManager *manager, ifindex = g_udev_device_get_property_as_int (udev_device, "IFINDEX"); device = find_device_by_ifindex (self, ifindex); if (device) - priv->devices = remove_one_device (self, priv->devices, device); - + priv->devices = remove_one_device (self, priv->devices, device, FALSE); } static void @@ -2522,7 +2547,7 @@ dispose (GObject *object) while (g_slist_length (priv->devices)) { NMDevice *device = NM_DEVICE (priv->devices->data); - priv->devices = remove_one_device (manager, priv->devices, device); + priv->devices = remove_one_device (manager, priv->devices, device, TRUE); } user_destroy_connections (manager);