core: base infrastructure for connection assumption

Figure out what connections can be assumed when a device is added,
and on shutdown don't blow away connections that can be assumed on
NM restart.
This commit is contained in:
Dan Williams
2009-08-03 17:15:03 -04:00
parent 8f0652a9f0
commit f2eb3dea65
10 changed files with 532 additions and 23 deletions

View File

@@ -18,6 +18,10 @@
* Copyright (C) 2005 - 2008 Red Hat, Inc. * Copyright (C) 2005 - 2008 Red Hat, Inc.
*/ */
#define _XOPEN_SOURCE
#include <time.h>
#undef _XOPEN_SOURCE
#include <glib.h> #include <glib.h>
#include <glib/gi18n.h> #include <glib/gi18n.h>
#include <dbus/dbus.h> #include <dbus/dbus.h>
@@ -74,6 +78,206 @@ get_leasefile_for_iface (const char * iface, const char *uuid)
NM_DHCP_MANAGER_LEASE_FILE_EXT); 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" #define DHCP_CLIENT_ID_TAG "send dhcp-client-identifier"

View File

@@ -49,6 +49,11 @@ get_pidfile_for_iface (const char * iface)
NM_DHCP_MANAGER_PID_FILE_EXT); NM_DHCP_MANAGER_PID_FILE_EXT);
} }
GSList *
nm_dhcp_client_get_lease_ip4_config (const char *iface, const char *uuid)
{
return NULL;
}
static void static void
dhcpcd_child_setup (gpointer user_data G_GNUC_UNUSED) dhcpcd_child_setup (gpointer user_data G_GNUC_UNUSED)

View File

@@ -1121,3 +1121,16 @@ nm_dhcp_manager_set_hostname_provider (NMDHCPManager *manager,
g_object_weak_ref (G_OBJECT (provider), hostname_provider_destroyed, 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);
}

View File

@@ -107,6 +107,10 @@ gboolean nm_dhcp_manager_foreach_dhcp4_option (NMDHCPManager *self,
GHFunc func, GHFunc func,
gpointer user_data); 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 */ /* The following are implemented by the DHCP client backends */
GPid nm_dhcp_client_start (NMDHCPDevice *device, GPid nm_dhcp_client_start (NMDHCPDevice *device,
const char *uuid, const char *uuid,
@@ -118,6 +122,9 @@ gboolean nm_dhcp_client_process_classless_routes (GHashTable *options,
NMIP4Config *ip4_config, NMIP4Config *ip4_config,
guint32 *gwaddr); guint32 *gwaddr);
GSList * nm_dhcp_client_get_lease_ip4_config (const char *iface,
const char *uuid);
/* Test functions */ /* Test functions */
NMIP4Config *nm_dhcp_manager_options_to_ip4_config (const char *iface, NMIP4Config *nm_dhcp_manager_options_to_ip4_config (const char *iface,
GHashTable *options); GHashTable *options);

View File

@@ -53,6 +53,7 @@
#include "ppp-manager/nm-ppp-manager.h" #include "ppp-manager/nm-ppp-manager.h"
#include "nm-utils.h" #include "nm-utils.h"
#include "nm-properties-changed-signal.h" #include "nm-properties-changed-signal.h"
#include "nm-dhcp-manager.h"
#include "nm-device-ethernet-glue.h" #include "nm-device-ethernet-glue.h"
@@ -280,8 +281,8 @@ constructor (GType type,
guint32 caps; guint32 caps;
object = G_OBJECT_CLASS (nm_device_ethernet_parent_class)->constructor (type, object = G_OBJECT_CLASS (nm_device_ethernet_parent_class)->constructor (type,
n_construct_params, n_construct_params,
construct_params); construct_params);
if (!object) if (!object)
return NULL; return NULL;
@@ -1472,6 +1473,196 @@ spec_match_list (NMDevice *device, const GSList *specs)
return matched; 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), &ether);
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 static void
dispose (GObject *object) 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->act_stage4_get_ip4_config = real_act_stage4_get_ip4_config;
parent_class->deactivate_quickly = real_deactivate_quickly; parent_class->deactivate_quickly = real_deactivate_quickly;
parent_class->spec_match_list = spec_match_list; parent_class->spec_match_list = spec_match_list;
parent_class->connection_match_config = connection_match_config;
/* properties */ /* properties */
g_object_class_install_property g_object_class_install_property

View File

@@ -282,8 +282,29 @@ gboolean
nm_device_interface_spec_match_list (NMDeviceInterface *device, nm_device_interface_spec_match_list (NMDeviceInterface *device,
const GSList *specs) const GSList *specs)
{ {
g_return_val_if_fail (NM_IS_DEVICE_INTERFACE (device), FALSE);
if (NM_DEVICE_INTERFACE_GET_INTERFACE (device)->spec_match_list) if (NM_DEVICE_INTERFACE_GET_INTERFACE (device)->spec_match_list)
return NM_DEVICE_INTERFACE_GET_INTERFACE (device)->spec_match_list (device, specs); return NM_DEVICE_INTERFACE_GET_INTERFACE (device)->spec_match_list (device, specs);
return FALSE; 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;
}

View File

@@ -88,6 +88,8 @@ struct _NMDeviceInterface {
gboolean (*spec_match_list) (NMDeviceInterface *device, const GSList *specs); gboolean (*spec_match_list) (NMDeviceInterface *device, const GSList *specs);
NMConnection * (*connection_match_config) (NMDeviceInterface *device, const GSList *specs);
/* Signals */ /* Signals */
void (*state_changed) (NMDeviceInterface *device, void (*state_changed) (NMDeviceInterface *device,
NMDeviceState new_state, NMDeviceState new_state,
@@ -115,4 +117,9 @@ NMDeviceState nm_device_interface_get_state (NMDeviceInterface *device);
gboolean nm_device_interface_spec_match_list (NMDeviceInterface *device, gboolean nm_device_interface_spec_match_list (NMDeviceInterface *device,
const GSList *specs); 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 */ #endif /* NM_DEVICE_INTERFACE_H */

View File

@@ -121,7 +121,8 @@ static gboolean nm_device_activate (NMDeviceInterface *device,
NMActRequest *req, NMActRequest *req,
GError **error); GError **error);
static void nm_device_deactivate (NMDeviceInterface *device, NMDeviceStateReason reason); 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); 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->check_connection_compatible = check_connection_compatible;
device_interface_class->activate = nm_device_activate; device_interface_class->activate = nm_device_activate;
device_interface_class->deactivate = nm_device_deactivate; 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); NMDevice *self = NM_DEVICE (object);
NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
gboolean take_down = TRUE;
if (priv->disposed || !priv->initialized) if (priv->disposed || !priv->initialized)
goto out; goto out;
priv->disposed = TRUE; 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) { if (priv->failed_to_disconnected_id) {
g_source_remove (priv->failed_to_disconnected_id); g_source_remove (priv->failed_to_disconnected_id);
priv->failed_to_disconnected_id = 0; priv->failed_to_disconnected_id = 0;
} }
/* if (priv->managed && take_down) {
* 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) {
NMDeviceStateReason ignored = NM_DEVICE_STATE_REASON_NONE; NMDeviceStateReason ignored = NM_DEVICE_STATE_REASON_NONE;
nm_device_take_down (self, FALSE, NM_DEVICE_STATE_REASON_REMOVED); nm_device_take_down (self, FALSE, NM_DEVICE_STATE_REASON_REMOVED);
@@ -2231,7 +2253,8 @@ dispose (GObject *object)
activation_source_clear (self, TRUE); 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_manager) {
if (priv->dnsmasq_state_id) { if (priv->dnsmasq_state_id) {
@@ -2577,7 +2600,7 @@ nm_device_set_managed (NMDevice *device,
} }
static gboolean static gboolean
nm_device_spec_match_list (NMDeviceInterface *device, const GSList *specs) spec_match_list (NMDeviceInterface *device, const GSList *specs)
{ {
NMDevice *self; NMDevice *self;
@@ -2590,6 +2613,16 @@ nm_device_spec_match_list (NMDeviceInterface *device, const GSList *specs)
return FALSE; 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 void
nm_device_set_dhcp_timeout (NMDevice *device, guint32 timeout) nm_device_set_dhcp_timeout (NMDevice *device, guint32 timeout)
{ {

View File

@@ -106,6 +106,8 @@ typedef struct {
gboolean (* can_interrupt_activation) (NMDevice *self); gboolean (* can_interrupt_activation) (NMDevice *self);
gboolean (* spec_match_list) (NMDevice *self, const GSList *specs); gboolean (* spec_match_list) (NMDevice *self, const GSList *specs);
NMConnection * (* connection_match_config) (NMDevice *self, const GSList *connections);
} NMDeviceClass; } NMDeviceClass;

View File

@@ -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 */ /* Removes a device from a device list; returns the start of the new device list */
static GSList * 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); NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager);
if (nm_device_get_managed (device)) if (nm_device_get_managed (device)) {
nm_device_set_managed (device, FALSE, NM_DEVICE_STATE_REASON_REMOVED); 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); 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); NMManager *self = NM_MANAGER (user_data);
NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); 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 static void
@@ -1153,6 +1165,10 @@ add_device (NMManager *self, NMDevice *device)
char *path; char *path;
static guint32 devcount = 0; static guint32 devcount = 0;
const GSList *unmanaged_specs; const GSList *unmanaged_specs;
GSList *connections = NULL;
NMConnection *existing;
GHashTableIter iter;
gpointer value;
priv->devices = g_slist_append (priv->devices, device); 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); nm_info ("(%s): exported as %s", iface, path);
g_free (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 */ /* Start the device if it's supposed to be managed */
unmanaged_specs = nm_sysconfig_settings_get_unmanaged_specs (priv->sys_settings); 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)) 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; priv->devices = keep;
while (g_slist_length (gone)) 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 { } else {
g_slist_free (keep); g_slist_free (keep);
g_slist_free (gone); g_slist_free (gone);
@@ -1372,7 +1398,7 @@ bluez_manager_bdaddr_removed_cb (NMBluezManager *bluez_mgr,
NMDevice *device = NM_DEVICE (iter->data); NMDevice *device = NM_DEVICE (iter->data);
if (!strcmp (nm_device_get_udi (device), object_path)) { 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; break;
} }
} }
@@ -1433,8 +1459,7 @@ udev_device_removed_cb (NMUdevManager *manager,
ifindex = g_udev_device_get_property_as_int (udev_device, "IFINDEX"); ifindex = g_udev_device_get_property_as_int (udev_device, "IFINDEX");
device = find_device_by_ifindex (self, ifindex); device = find_device_by_ifindex (self, ifindex);
if (device) if (device)
priv->devices = remove_one_device (self, priv->devices, device); priv->devices = remove_one_device (self, priv->devices, device, FALSE);
} }
static void static void
@@ -2522,7 +2547,7 @@ dispose (GObject *object)
while (g_slist_length (priv->devices)) { while (g_slist_length (priv->devices)) {
NMDevice *device = NM_DEVICE (priv->devices->data); 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); user_destroy_connections (manager);