core: create devices first and realize them later

Unrealized devices aren't backed by kernel resources and so won't know
all of their attributes.  That means three things:

1) they must update their attributes when they become realized
2) they must clear those attributes when unrealized
3) they must be looser in checking compatible connections until
they are realized

This requires that the setup() function be split into two parts, start & finish,
because finish must be run after add_device()

Also, we can simplify whether to pay attention to 'recheck-assume', which
is now dependent on priv->is_nm_owned, because the only case where NM should
*not* listen for the 'recheck-assume' signal is when the device is a
software device created by NM itself.  That logic was previously spread
across the callers of add_device() but is now consolidated into
nm-manager.c::device_realized() and nm-device.c::nm_device_create_and_realize().
This commit is contained in:
Dan Williams
2014-09-24 16:58:07 -05:00
committed by Thomas Haller
parent b7eb622c24
commit 4dbaac4ba2
13 changed files with 358 additions and 281 deletions

View File

@@ -97,7 +97,7 @@ check_connection_compatible (NMDevice *device, NMConnection *connection)
return FALSE;
mac_address = nm_setting_bridge_get_mac_address (s_bridge);
if (mac_address) {
if (mac_address && nm_device_is_real (device)) {
const char *hw_addr;
hw_addr = nm_device_get_hw_address (device);

View File

@@ -305,9 +305,9 @@ nm_device_ethernet_init (NMDeviceEthernet *self)
}
static void
setup (NMDevice *device, NMPlatformLink *plink)
setup_start (NMDevice *device, NMPlatformLink *plink)
{
NM_DEVICE_CLASS (nm_device_ethernet_parent_class)->setup (device, plink);
NM_DEVICE_CLASS (nm_device_ethernet_parent_class)->setup_start (device, plink);
g_object_notify (G_OBJECT (device), NM_DEVICE_ETHERNET_PERMANENT_HW_ADDRESS);
}
@@ -1716,7 +1716,7 @@ nm_device_ethernet_class_init (NMDeviceEthernetClass *klass)
object_class->set_property = set_property;
parent_class->get_generic_capabilities = get_generic_capabilities;
parent_class->setup = setup;
parent_class->setup_start = setup_start;
parent_class->check_connection_compatible = check_connection_compatible;
parent_class->complete_connection = complete_connection;
parent_class->new_default_connection = new_default_connection;

View File

@@ -61,13 +61,13 @@ get_type_description (NMDevice *device)
}
static void
setup (NMDevice *device, NMPlatformLink *plink)
setup_start (NMDevice *device, NMPlatformLink *plink)
{
NMDeviceGeneric *self = NM_DEVICE_GENERIC (device);
NMDeviceGenericPrivate *priv = NM_DEVICE_GENERIC_GET_PRIVATE (self);
int ifindex;
NM_DEVICE_CLASS (nm_device_generic_parent_class)->setup (device, plink);
NM_DEVICE_CLASS (nm_device_generic_parent_class)->setup_start (device, plink);
g_clear_pointer (&priv->type_description, g_free);
ifindex = nm_device_get_ip_ifindex (NM_DEVICE (self));
@@ -203,7 +203,7 @@ nm_device_generic_class_init (NMDeviceGenericClass *klass)
object_class->get_property = get_property;
object_class->set_property = set_property;
parent_class->setup = setup;
parent_class->setup_start = setup_start;
parent_class->get_generic_capabilities = get_generic_capabilities;
parent_class->get_type_description = get_type_description;
parent_class->check_connection_compatible = check_connection_compatible;

View File

@@ -149,7 +149,7 @@ check_connection_compatible (NMDevice *device, NMConnection *connection)
if (!s_infiniband)
return FALSE;
if (s_infiniband) {
if (nm_device_is_real (device)) {
const char *mac;
mac = nm_setting_infiniband_get_mac_address (s_infiniband);

View File

@@ -503,6 +503,10 @@ check_connection_compatible (NMDevice *device, NMConnection *connection)
if (!s_ip_tunnel)
return FALSE;
if (nm_setting_ip_tunnel_get_mode (s_ip_tunnel) != priv->mode)
return FALSE;
if (nm_device_is_real (device)) {
/* Check parent interface; could be an interface name or a UUID */
parent = nm_setting_ip_tunnel_get_parent (s_ip_tunnel);
if (parent) {
@@ -510,9 +514,6 @@ check_connection_compatible (NMDevice *device, NMConnection *connection)
return FALSE;
}
if (nm_setting_ip_tunnel_get_mode (s_ip_tunnel) != priv->mode)
return FALSE;
if (!address_equal_pp (priv->addr_family,
nm_setting_ip_tunnel_get_local (s_ip_tunnel),
priv->local))
@@ -539,6 +540,7 @@ check_connection_compatible (NMDevice *device, NMConnection *connection)
if (nm_setting_ip_tunnel_get_flow_label (s_ip_tunnel) != priv->flow_label)
return FALSE;
}
}
return TRUE;
}
@@ -751,9 +753,9 @@ create_and_realize (NMDevice *device,
}
static void
setup (NMDevice *device, NMPlatformLink *plink)
setup_start (NMDevice *device, NMPlatformLink *plink)
{
NM_DEVICE_CLASS (nm_device_ip_tunnel_parent_class)->setup (device, plink);
NM_DEVICE_CLASS (nm_device_ip_tunnel_parent_class)->setup_start (device, plink);
update_properties (device);
}
@@ -847,7 +849,7 @@ nm_device_ip_tunnel_class_init (NMDeviceIPTunnelClass *klass)
device_class->check_connection_compatible = check_connection_compatible;
device_class->create_and_realize = create_and_realize;
device_class->realize = realize;
device_class->setup = setup;
device_class->setup_start = setup_start;
device_class->unrealize = unrealize;
device_class->connection_type = NM_SETTING_IP_TUNNEL_SETTING_NAME;

View File

@@ -226,6 +226,13 @@ realize (NMDevice *device, NMPlatformLink *plink, GError **error)
return TRUE;
}
static void
setup_start (NMDevice *device, NMPlatformLink *plink)
{
NM_DEVICE_CLASS (nm_device_tun_parent_class)->setup_start (device, plink);
reload_tun_properties (device);
}
static gboolean
check_connection_compatible (NMDevice *device, NMConnection *connection)
{
@@ -235,8 +242,6 @@ check_connection_compatible (NMDevice *device, NMConnection *connection)
NMSettingTun *s_tun;
gint64 user, group;
reload_tun_properties (self);
if (!NM_DEVICE_CLASS (nm_device_tun_parent_class)->check_connection_compatible (device, connection))
return FALSE;
@@ -244,6 +249,7 @@ check_connection_compatible (NMDevice *device, NMConnection *connection)
if (!s_tun)
return FALSE;
if (nm_device_is_real (device)) {
mode = tun_mode_from_string (priv->mode);
if (mode != nm_setting_tun_get_mode (s_tun))
return FALSE;
@@ -261,6 +267,7 @@ check_connection_compatible (NMDevice *device, NMConnection *connection)
return FALSE;
if (nm_setting_tun_get_multi_queue (s_tun) != priv->props.multi_queue)
return FALSE;
}
return TRUE;
}
@@ -367,6 +374,7 @@ nm_device_tun_class_init (NMDeviceTunClass *klass)
device_class->check_connection_compatible = check_connection_compatible;
device_class->create_and_realize = create_and_realize;
device_class->realize = realize;
device_class->setup_start = setup_start;
device_class->unrealize = unrealize;
device_class->update_connection = update_connection;

View File

@@ -148,12 +148,12 @@ nm_device_vlan_set_parent (NMDeviceVlan *self, NMDevice *parent)
}
static void
setup (NMDevice *device, NMPlatformLink *plink)
setup_start (NMDevice *device, NMPlatformLink *plink)
{
NMDeviceVlan *self = NM_DEVICE_VLAN (device);
NMDeviceVlanPrivate *priv = NM_DEVICE_VLAN_GET_PRIVATE (self);
NM_DEVICE_CLASS (nm_device_vlan_parent_class)->setup (device, plink);
NM_DEVICE_CLASS (nm_device_vlan_parent_class)->setup_start (device, plink);
_LOGI (LOGD_HW | LOGD_VLAN, "VLAN ID %d with parent %s",
priv->vlan_id, nm_device_get_iface (priv->parent));
@@ -311,6 +311,9 @@ notify_new_device_added (NMDevice *device, NMDevice *new_device)
if (priv->parent)
return;
if (!nm_device_is_real (device))
return;
plnk = nm_platform_link_get_lnk_vlan (NM_PLATFORM_GET, nm_device_get_ifindex (device), &plink);
if (!plnk) {
_LOGW (LOGD_VLAN, "failed to get VLAN interface info while checking added component.");
@@ -397,6 +400,8 @@ check_connection_compatible (NMDevice *device, NMConnection *connection)
if (!s_vlan)
return FALSE;
/* Before the device is realized some properties will not be set */
if (nm_device_is_real (device)) {
if (nm_setting_vlan_get_id (s_vlan) != priv->vlan_id)
return FALSE;
@@ -410,6 +415,7 @@ check_connection_compatible (NMDevice *device, NMConnection *connection)
if (!match_hwaddr (device, connection, TRUE))
return FALSE;
}
}
/* Ensure the interface name matches. If not specified we assume a match
* since both the parent interface and the VLAN ID matched by the time we
@@ -672,7 +678,7 @@ nm_device_vlan_class_init (NMDeviceVlanClass *klass)
parent_class->create_and_realize = create_and_realize;
parent_class->realize = realize;
parent_class->setup = setup;
parent_class->setup_start = setup_start;
parent_class->unrealize = unrealize;
parent_class->get_generic_capabilities = get_generic_capabilities;
parent_class->bring_up = bring_up;

View File

@@ -133,11 +133,11 @@ link_changed (NMDevice *device, NMPlatformLink *info)
}
static void
setup (NMDevice *device, NMPlatformLink *plink)
setup_start (NMDevice *device, NMPlatformLink *plink)
{
g_assert (plink->type == NM_LINK_TYPE_VXLAN);
NM_DEVICE_CLASS (nm_device_vxlan_parent_class)->setup (device, plink);
NM_DEVICE_CLASS (nm_device_vxlan_parent_class)->setup_start (device, plink);
update_properties (device);
}
@@ -247,7 +247,7 @@ nm_device_vxlan_class_init (NMDeviceVxlanClass *klass)
object_class->get_property = get_property;
device_class->link_changed = link_changed;
device_class->setup = setup;
device_class->setup_start = setup_start;
device_class->unrealize = unrealize;
/* properties */

View File

@@ -556,7 +556,7 @@ nm_device_get_iface (NMDevice *self)
int
nm_device_get_ifindex (NMDevice *self)
{
g_return_val_if_fail (self != NULL, 0);
g_return_val_if_fail (NM_IS_DEVICE (self), 0);
return NM_DEVICE_GET_PRIVATE (self)->ifindex;
}
@@ -1152,8 +1152,7 @@ nm_device_release_one_slave (NMDevice *self, NMDevice *slave, gboolean configure
static gboolean
can_unmanaged_external_down (NMDevice *self)
{
return nm_device_is_software (self)
&& !nm_device_get_is_nm_owned (self);
return nm_device_is_software (self) && !NM_DEVICE_GET_PRIVATE (self)->is_nm_owned;
}
/**
@@ -1385,12 +1384,19 @@ nm_device_set_carrier (NMDevice *self, gboolean carrier)
}
static void
device_set_master (NMDevice *self, int ifindex)
device_recheck_slave_status (NMDevice *self, NMPlatformLink *plink)
{
NMDevice *master;
NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
master = nm_manager_get_device_by_ifindex (nm_manager_get (), ifindex);
g_return_if_fail (plink != NULL);
if (priv->enslaved && plink->master != nm_device_get_ifindex (priv->master))
nm_device_release_one_slave (priv->master, self, FALSE, NM_DEVICE_STATE_REASON_CONNECTION_ASSUMED);
if (plink->master && !priv->enslaved) {
NMDevice *master;
master = nm_manager_get_device_by_ifindex (nm_manager_get (), plink->master);
if (master && NM_DEVICE_GET_CLASS (master)->enslave_slave) {
g_clear_object (&priv->master);
priv->master = g_object_ref (master);
@@ -1400,8 +1406,9 @@ device_set_master (NMDevice *self, int ifindex)
nm_device_get_iface (master));
} else {
_LOGW (LOGD_DEVICE, "enslaved to unknown device %d %s",
ifindex,
nm_platform_link_get_name (NM_PLATFORM_GET, ifindex));
plink->master,
nm_platform_link_get_name (NM_PLATFORM_GET, plink->master));
}
}
}
@@ -1449,6 +1456,12 @@ device_link_changed (NMDevice *self)
g_object_notify (G_OBJECT (self), NM_DEVICE_MTU);
}
if (info.driver && g_strcmp0 (priv->driver, info.driver) != 0) {
g_free (priv->driver);
priv->driver = g_strdup (info.driver);
g_object_notify (G_OBJECT (self), NM_DEVICE_DRIVER);
}
if (info.name[0] && strcmp (priv->iface, info.name) != 0) {
_LOGI (LOGD_DEVICE, "interface index %d renamed iface from '%s' to '%s'",
priv->ifindex, priv->iface, info.name);
@@ -1471,15 +1484,6 @@ device_link_changed (NMDevice *self)
nm_device_emit_recheck_auto_activate (self);
}
/* Update slave status for external changes */
if (priv->enslaved && info.master != nm_device_get_ifindex (priv->master))
nm_device_release_one_slave (priv->master, self, FALSE, NM_DEVICE_STATE_REASON_CONNECTION_ASSUMED);
if (info.master && !priv->enslaved) {
device_set_master (self, info.master);
if (priv->master)
nm_device_enslave_slave (priv->master, self, NULL);
}
if (priv->rdisc && nm_platform_link_get_ipv6_token (NM_PLATFORM_GET, priv->ifindex, &token_iid)) {
_LOGD (LOGD_DEVICE, "IPv6 tokenized identifier present on device %s", priv->iface);
if (nm_rdisc_set_iid (priv->rdisc, token_iid))
@@ -1562,6 +1566,7 @@ device_link_changed (NMDevice *self)
if (emit_link_initialized)
g_signal_emit (self, signals[LINK_INITIALIZED], 0);
device_recheck_slave_status (self, &info);
return G_SOURCE_REMOVE;
}
@@ -1635,7 +1640,8 @@ link_changed (NMDevice *self, NMPlatformLink *info)
* @plink: an existing platform link or %NULL
* @error: location to store error, or %NULL
*
* Initializes and sets up the device using existing backing resources.
* Initializes and sets up the device using existing backing resources. Before
* the device is ready for use nm_device_setup_finish() must be called.
*
* Returns: %TRUE on success, %FALSE on error
*/
@@ -1648,7 +1654,7 @@ nm_device_realize (NMDevice *self, NMPlatformLink *plink, GError **error)
return FALSE;
}
NM_DEVICE_GET_CLASS (self)->setup (self, plink);
NM_DEVICE_GET_CLASS (self)->setup_start (self, plink);
return TRUE;
}
@@ -1671,15 +1677,20 @@ nm_device_create_and_realize (NMDevice *self,
NMDevice *parent,
GError **error)
{
NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
NMPlatformLink plink = { .type = NM_LINK_TYPE_UNKNOWN };
/* Must be set before device is realized */
priv->is_nm_owned = !nm_platform_link_get_by_ifname (NM_PLATFORM_GET, priv->iface);
/* Create any resources the device needs */
if (NM_DEVICE_GET_CLASS (self)->create_and_realize) {
if (!NM_DEVICE_GET_CLASS (self)->create_and_realize (self, connection, parent, &plink, error))
return FALSE;
}
NM_DEVICE_GET_CLASS (self)->setup (self, (plink.type != NM_LINK_TYPE_UNKNOWN) ? &plink : NULL);
NM_DEVICE_GET_CLASS (self)->setup_start (self, (plink.type != NM_LINK_TYPE_UNKNOWN) ? &plink : NULL);
nm_device_setup_finish (self, (plink.type != NM_LINK_TYPE_UNKNOWN) ? &plink : NULL);
g_return_val_if_fail (nm_device_check_connection_compatible (self, connection), TRUE);
return TRUE;
@@ -1742,7 +1753,7 @@ check_carrier (NMDevice *self)
}
static void
setup (NMDevice *self, NMPlatformLink *plink)
setup_start (NMDevice *self, NMPlatformLink *plink)
{
NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
static guint32 id = 0;
@@ -1751,6 +1762,7 @@ setup (NMDevice *self, NMPlatformLink *plink)
g_return_if_fail (priv->ip_ifindex <= 0);
g_return_if_fail (priv->ip_iface == NULL);
/* Balanced by a thaw in nm_device_setup_finish() */
g_object_freeze_notify (G_OBJECT (self));
if (plink) {
@@ -1759,7 +1771,7 @@ setup (NMDevice *self, NMPlatformLink *plink)
}
if (priv->ifindex > 0) {
_LOGD (LOGD_DEVICE, "setup(): %s, kernel ifindex %d", G_OBJECT_TYPE_NAME (self), priv->ifindex);
_LOGD (LOGD_DEVICE, "setup_start(): %s, kernel ifindex %d", G_OBJECT_TYPE_NAME (self), priv->ifindex);
priv->physical_port_id = nm_platform_link_get_physical_port_id (NM_PLATFORM_GET, priv->ifindex);
g_object_notify (G_OBJECT (self), NM_DEVICE_PHYSICAL_PORT_ID);
@@ -1844,17 +1856,29 @@ setup (NMDevice *self, NMPlatformLink *plink)
g_object_notify (G_OBJECT (self), NM_DEVICE_CAPABILITIES);
/* Enslave ourselves */
if (priv->ifindex > 0) {
int master = nm_platform_link_get_master (NM_PLATFORM_GET, priv->ifindex);
if (master > 0)
device_set_master (self, master);
priv->real = TRUE;
}
priv->real = TRUE;
static void
setup_finish (NMDevice *self, NMPlatformLink *plink)
{
if (plink) {
update_device_from_platform_link (self, plink);
device_recheck_slave_status (self, plink);
}
}
void
nm_device_setup_finish (NMDevice *self, NMPlatformLink *plink)
{
NM_DEVICE_GET_CLASS (self)->setup_finish (self, plink);
NM_DEVICE_GET_PRIVATE (self)->real = TRUE;
g_object_notify (G_OBJECT (self), NM_DEVICE_REAL);
nm_device_recheck_available_connections (self);
/* Balanced by a freeze in setup_start() */
g_object_thaw_notify (G_OBJECT (self));
}
@@ -2810,7 +2834,8 @@ nm_device_check_connection_compatible (NMDevice *self, NMConnection *connection)
static gboolean
nm_device_can_assume_connections (NMDevice *self)
{
return !!NM_DEVICE_GET_CLASS (self)->update_connection;
return !!NM_DEVICE_GET_CLASS (self)->update_connection
&& !NM_DEVICE_GET_PRIVATE (self)->is_nm_owned;
}
/**
@@ -6547,14 +6572,6 @@ nm_device_get_is_nm_owned (NMDevice *self)
return NM_DEVICE_GET_PRIVATE (self)->is_nm_owned;
}
void
nm_device_set_nm_owned (NMDevice *self)
{
g_return_if_fail (NM_IS_DEVICE (self));
NM_DEVICE_GET_PRIVATE (self)->is_nm_owned = TRUE;
}
/*
* delete_on_deactivate_link_delete
*
@@ -8461,6 +8478,7 @@ _nm_device_check_connection_available (NMDevice *self,
&& nm_device_get_unmanaged (self, NM_UNMANAGED_ALL & ~NM_UNMANAGED_DEFAULT))
return FALSE;
if ( state < NM_DEVICE_STATE_DISCONNECTED
&& !nm_device_is_software (self)
&& ( ( !NM_FLAGS_HAS (flags, _NM_DEVICE_CHECK_CON_AVAILABLE_FOR_USER_REQUEST_WAITING_CARRIER)
&& !nm_device_is_available (self, NM_DEVICE_CHECK_DEV_AVAILABLE_NONE))
|| ( NM_FLAGS_HAS (flags, _NM_DEVICE_CHECK_CON_AVAILABLE_FOR_USER_REQUEST_WAITING_CARRIER)
@@ -10324,7 +10342,8 @@ nm_device_class_init (NMDeviceClass *klass)
klass->check_connection_compatible = check_connection_compatible;
klass->check_connection_available = check_connection_available;
klass->can_unmanaged_external_down = can_unmanaged_external_down;
klass->setup = setup;
klass->setup_start = setup_start;
klass->setup_finish = setup_finish;
klass->unrealize = unrealize;
klass->is_up = is_up;
klass->bring_up = bring_up;

View File

@@ -174,14 +174,28 @@ typedef struct {
GError **error);
/**
* setup():
* setup_start():
* @self: the #NMDevice
* @plink: the #NMPlatformLink if backed by a kernel netdevice
*
* Update the device from backing resource properties (like hardware
* addresses, carrier states, driver/firmware info, etc).
* addresses, carrier states, driver/firmware info, etc). This function
* should only change properties for this device, and should not perform
* any tasks that affect other interfaces (like master/slave or parent/child
* stuff).
*/
void (*setup) (NMDevice *self, NMPlatformLink *plink);
void (*setup_start) (NMDevice *self, NMPlatformLink *plink);
/**
* setup_finish():
* @self: the #NMDevice
* @plink: the #NMPlatformLink if backed by a kernel netdevice
*
* Update the device's master/slave or parent/child relationships from
* backing resource properties. After this function finishes, the device
* is ready for network connectivity.
*/
void (*setup_finish) (NMDevice *self, NMPlatformLink *plink);
/**
* unrealize():
@@ -466,7 +480,6 @@ void nm_device_set_unmanaged_initial (NMDevice *device,
gboolean unmanaged);
gboolean nm_device_get_is_nm_owned (NMDevice *device);
void nm_device_set_nm_owned (NMDevice *device);
gboolean nm_device_has_capability (NMDevice *self, NMDeviceCapabilities caps);
@@ -477,6 +490,8 @@ gboolean nm_device_create_and_realize (NMDevice *self,
NMConnection *connection,
NMDevice *parent,
GError **error);
void nm_device_setup_finish (NMDevice *self,
NMPlatformLink *plink);
gboolean nm_device_unrealize (NMDevice *device,
gboolean remove_resources,
GError **error);

View File

@@ -424,9 +424,9 @@ periodic_update_cb (gpointer user_data)
}
static void
setup (NMDevice *device, NMPlatformLink *plink)
setup_start (NMDevice *device, NMPlatformLink *plink)
{
NM_DEVICE_CLASS (nm_device_wifi_parent_class)->setup (device, plink);
NM_DEVICE_CLASS (nm_device_wifi_parent_class)->setup_start (device, plink);
g_object_notify (G_OBJECT (device), NM_DEVICE_WIFI_PERMANENT_HW_ADDRESS);
}
@@ -3040,7 +3040,7 @@ nm_device_wifi_class_init (NMDeviceWifiClass *klass)
object_class->dispose = dispose;
object_class->finalize = finalize;
parent_class->setup = setup;
parent_class->setup_start = setup_start;
parent_class->bring_up = bring_up;
parent_class->can_auto_connect = can_auto_connect;
parent_class->is_available = is_available;

View File

@@ -598,17 +598,17 @@ master_state_cb (NMActiveConnection *master,
gpointer user_data)
{
NMActiveConnection *self = NM_ACTIVE_CONNECTION (user_data);
NMActiveConnectionPrivate *priv = NM_ACTIVE_CONNECTION_GET_PRIVATE (self);
NMActiveConnectionState master_state = nm_active_connection_get_state (master);
NMDevice *master_device = nm_active_connection_get_device (master);
check_master_ready (self);
_LOGD ("master ActiveConnection [%p] state now '%s' (%d)",
master, state_to_string (master_state), master_state);
if ( master_state >= NM_ACTIVE_CONNECTION_STATE_DEACTIVATING
&& !priv->master_ready) {
/* Master failed without ever creating its device */
if ( master_state == NM_ACTIVE_CONNECTION_STATE_DEACTIVATING
&& (!master_device || !nm_device_is_real (master_device))) {
/* Master failed without ever creating or realizing its device */
if (NM_ACTIVE_CONNECTION_GET_CLASS (self)->master_failed)
NM_ACTIVE_CONNECTION_GET_CLASS (self)->master_failed (self);
}

View File

@@ -58,7 +58,7 @@
#include "nmdbus-manager.h"
#include "nmdbus-device.h"
static void add_device (NMManager *self, NMDevice *device, gboolean try_assume);
static void add_device (NMManager *self, NMDevice *device);
static NMActiveConnection *_new_active_connection (NMManager *self,
NMConnection *connection,
@@ -487,8 +487,11 @@ find_device_by_ip_iface (NMManager *self, const gchar *iface)
g_return_val_if_fail (iface != NULL, NULL);
for (iter = NM_MANAGER_GET_PRIVATE (self)->devices; iter; iter = g_slist_next (iter)) {
if (g_strcmp0 (nm_device_get_ip_iface (NM_DEVICE (iter->data)), iface) == 0)
return NM_DEVICE (iter->data);
NMDevice *candidate = iter->data;
if ( nm_device_is_real (candidate)
&& g_strcmp0 (nm_device_get_ip_iface (candidate), iface) == 0)
return candidate;
}
return NULL;
}
@@ -840,6 +843,8 @@ find_parent_device_for_connection (NMManager *self, NMConnection *connection)
NMDevice *parent, *first_compatible = NULL;
GSList *iter;
g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL);
factory = nm_device_factory_manager_find_factory_for_connection (connection);
if (!factory)
return NULL;
@@ -849,7 +854,7 @@ find_parent_device_for_connection (NMManager *self, NMConnection *connection)
return NULL;
/* Try as an interface name */
parent = find_device_by_ip_iface (self, parent_name);
parent = find_device_by_iface (self, parent_name);
if (parent)
return parent;
@@ -964,10 +969,9 @@ system_create_virtual_device (NMManager *self, NMConnection *connection, GError
{
NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
NMDeviceFactory *factory;
GSList *iter;
char *iface = NULL;
GSList *connections, *iter;
gs_free char *iface = NULL;
NMDevice *device = NULL, *parent = NULL;
gboolean nm_owned = FALSE;
g_return_val_if_fail (NM_IS_MANAGER (self), NULL);
g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL);
@@ -989,7 +993,7 @@ system_create_virtual_device (NMManager *self, NMConnection *connection, GError
NM_MANAGER_ERROR,
NM_MANAGER_ERROR_FAILED,
"interface name '%s' already created", iface);
goto out;
return NULL;
}
}
@@ -1003,79 +1007,61 @@ system_create_virtual_device (NMManager *self, NMConnection *connection, GError
NM_MANAGER_ERROR_FAILED,
"NetworkManager plugin for '%s' unavailable",
nm_connection_get_connection_type (connection));
goto out;
return NULL;
}
nm_owned = !nm_platform_link_get_by_ifname (NM_PLATFORM_GET, iface);
device = nm_device_factory_create_device (factory, iface, NULL, connection, NULL, error);
if (device) {
if (!nm_device_create_and_realize (device, connection, parent, error)) {
g_clear_object (&device);
goto out;
}
if (!device)
return NULL;
if (nm_owned)
nm_device_set_nm_owned (device);
/* If it was created by NM there's no connection to assume, but if it
* previously existed there might be one.
*/
add_device (self, device, !nm_owned);
add_device (self, device);
/* Add device takes a reference that NMManager still owns, so it's
* safe to unref here and still return @device.
*/
g_object_unref (device);
/* Create backing resources if the device has any autoconnect connections */
connections = nm_settings_get_connections (priv->settings);
for (iter = connections; iter; iter = g_slist_next (iter)) {
NMConnection *candidate = iter->data;
NMSettingConnection *s_con;
if (!nm_device_check_connection_compatible (device, candidate))
continue;
s_con = nm_connection_get_setting_connection (candidate);
g_assert (s_con);
if (!nm_setting_connection_get_autoconnect (s_con))
continue;
/* Create any backing resources the device needs */
if (!nm_device_create_and_realize (device, connection, parent, error)) {
remove_device (self, device, FALSE, TRUE);
device = NULL;
}
break;
}
out:
g_free (iface);
return device;
}
static void
system_create_virtual_devices (NMManager *self)
{
NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
GSList *iter, *connections;
nm_log_dbg (LOGD_CORE, "creating virtual devices...");
connections = nm_settings_get_connections (priv->settings);
for (iter = connections; iter; iter = g_slist_next (iter)) {
NMConnection *connection = iter->data;
/* We only create a virtual interface if the connection can autoconnect */
if ( nm_connection_is_virtual (connection)
&& nm_settings_connection_can_autoconnect (NM_SETTINGS_CONNECTION (connection)))
system_create_virtual_device (self, connection, NULL);
}
g_slist_free (connections);
}
static void
connection_added (NMSettings *settings,
NMSettingsConnection *settings_connection,
NMConnection *connection,
NMManager *manager)
{
NMConnection *connection = NM_CONNECTION (settings_connection);
if (nm_connection_is_virtual (connection)) {
NMSettingConnection *s_con = nm_connection_get_setting_connection (connection);
g_assert (s_con);
if (nm_setting_connection_get_autoconnect (s_con))
if (nm_connection_is_virtual (connection))
system_create_virtual_device (manager, connection, NULL);
}
}
static void
connection_changed (NMSettings *settings,
NMSettingsConnection *connection,
NMConnection *connection,
NMManager *manager)
{
/* FIXME: Some virtual devices may need to be updated in the future. */
if (nm_connection_is_virtual (connection))
system_create_virtual_device (manager, connection, NULL);
}
static void
@@ -1590,17 +1576,25 @@ assume_connection (NMManager *self, NMDevice *device, NMSettingsConnection *conn
return TRUE;
}
static gboolean
can_start_device (NMManager *self, NMDevice *device)
{
return nm_device_is_real (device)
&& !manager_sleeping (self)
&& !nm_device_get_unmanaged (device, NM_UNMANAGED_ALL & ~NM_UNMANAGED_DEFAULT);
}
static gboolean
recheck_assume_connection (NMDevice *device, gpointer user_data)
{
NMManager *self = NM_MANAGER (user_data);
NMSettingsConnection *connection;
gboolean was_unmanaged = FALSE, success, generated;
gboolean was_unmanaged = FALSE, success, generated = FALSE;
NMDeviceState state;
if (manager_sleeping (self))
return FALSE;
if (nm_device_get_unmanaged (device, NM_UNMANAGED_ALL & ~NM_UNMANAGED_DEFAULT))
g_return_val_if_fail (!nm_device_get_is_nm_owned (device), FALSE);
if (!can_start_device (self, device))
return FALSE;
state = nm_device_get_state (device);
@@ -1671,26 +1665,51 @@ device_ip_iface_changed (NMDevice *device,
}
}
static void
device_realized (NMDevice *device,
GParamSpec *pspec,
NMManager *self)
{
int ifindex;
gboolean assumed = FALSE;
/* Loopback device never gets managed */
ifindex = nm_device_get_ifindex (device);
if (ifindex > 0 && nm_platform_link_get_type (NM_PLATFORM_GET, ifindex) == NM_LINK_TYPE_LOOPBACK)
return;
if (!can_start_device (self, device))
return;
if (!nm_device_get_is_nm_owned (device)) {
assumed = recheck_assume_connection (device, self);
g_signal_connect (device, NM_DEVICE_RECHECK_ASSUME,
G_CALLBACK (recheck_assume_connection), self);
}
if (!assumed && nm_device_get_managed (device)) {
nm_device_state_changed (device,
NM_DEVICE_STATE_UNAVAILABLE,
NM_DEVICE_STATE_REASON_NOW_MANAGED);
}
}
/**
* add_device:
* @self: the #NMManager
* @device: the #NMDevice to add
* @try_assume: %TRUE if existing connection (if any) should be assumed
*
* If successful, this function will increase the references count of @device.
* Callers should decrease the reference count.
*/
static void
add_device (NMManager *self, NMDevice *device, gboolean try_assume)
add_device (NMManager *self, NMDevice *device)
{
NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
const char *iface, *driver, *type_desc;
const char *iface, *type_desc;
const GSList *unmanaged_specs;
gboolean user_unmanaged, sleeping;
gboolean enabled = FALSE;
RfKillType rtype;
GSList *iter, *remove = NULL;
gboolean connection_assumed = FALSE;
int ifindex;
const char *dbus_path;
@@ -1738,6 +1757,9 @@ add_device (NMManager *self, NMDevice *device, gboolean try_assume)
g_signal_connect (device, "notify::" NM_DEVICE_IP_IFACE,
G_CALLBACK (device_ip_iface_changed),
self);
g_signal_connect (device, "notify::" NM_DEVICE_REAL,
G_CALLBACK (device_realized),
self);
if (priv->startup) {
g_signal_connect (device, "notify::" NM_DEVICE_HAS_PENDING_ACTION,
@@ -1752,52 +1774,29 @@ add_device (NMManager *self, NMDevice *device, gboolean try_assume)
rtype = nm_device_get_rfkill_type (device);
if (rtype != RFKILL_TYPE_UNKNOWN) {
nm_manager_rfkill_update (self, rtype);
enabled = radio_enabled_for_type (self, rtype, TRUE);
nm_device_set_enabled (device, enabled);
nm_device_set_enabled (device, radio_enabled_for_type (self, rtype, TRUE));
}
iface = nm_device_get_iface (device);
g_assert (iface);
type_desc = nm_device_get_type_desc (device);
g_assert (type_desc);
driver = nm_device_get_driver (device);
if (!driver)
driver = "unknown";
nm_log_info (LOGD_HW, "(%s): new %s device (carrier: %s, driver: '%s', ifindex: %d)",
iface, type_desc,
nm_device_has_capability (device, NM_DEVICE_CAP_CARRIER_DETECT)
? (nm_device_has_carrier (device) ? "ON" : "OFF")
: "UNKNOWN",
driver, nm_device_get_ifindex (device));
nm_log_info (LOGD_HW, "(%s): new %s device", iface, type_desc);
unmanaged_specs = nm_settings_get_unmanaged_specs (priv->settings);
user_unmanaged = nm_device_spec_match_list (device, unmanaged_specs);
nm_device_set_unmanaged_initial (device, NM_UNMANAGED_USER, user_unmanaged);
sleeping = manager_sleeping (self);
nm_device_set_unmanaged_initial (device, NM_UNMANAGED_INTERNAL, sleeping);
nm_device_set_unmanaged_initial (device,
NM_UNMANAGED_USER,
nm_device_spec_match_list (device, unmanaged_specs));
nm_device_set_unmanaged_initial (device,
NM_UNMANAGED_INTERNAL,
manager_sleeping (self));
dbus_path = nm_exported_object_export (NM_EXPORTED_OBJECT (device));
nm_log_dbg (LOGD_DEVICE, "(%s): exported as %s", nm_device_get_iface (device), dbus_path);
nm_device_finish_init (device);
if (try_assume) {
connection_assumed = recheck_assume_connection (device, self);
g_signal_connect (device, NM_DEVICE_RECHECK_ASSUME,
G_CALLBACK (recheck_assume_connection), self);
}
if (!connection_assumed && nm_device_get_managed (device)) {
nm_device_state_changed (device,
NM_DEVICE_STATE_UNAVAILABLE,
NM_DEVICE_STATE_REASON_NOW_MANAGED);
}
/* Try to generate a default connection. If this fails because the link is
* not initialized, we will retry again in device_link_initialized_cb().
*/
nm_settings_device_added (priv->settings, device);
g_signal_emit (self, signals[DEVICE_ADDED], 0, device);
g_object_notify (G_OBJECT (self), NM_MANAGER_DEVICES);
@@ -1808,11 +1807,6 @@ add_device (NMManager *self, NMDevice *device, gboolean try_assume)
if (d != device)
nm_device_notify_new_device_added (d, device);
}
/* New devices might be master interfaces for virtual interfaces; so we may
* need to create new virtual interfaces now.
*/
system_create_virtual_devices (self);
}
/*******************************************************************/
@@ -1824,9 +1818,10 @@ factory_device_added_cb (NMDeviceFactory *factory,
{
GError *error = NULL;
if (nm_device_realize (device, NULL, &error))
add_device (NM_MANAGER (user_data), device, TRUE);
else {
if (nm_device_realize (device, NULL, &error)) {
add_device (NM_MANAGER (user_data), device);
nm_device_setup_finish (device, NULL);
} else {
nm_log_warn (LOGD_DEVICE, "(%s): failed to realize device: %s",
nm_device_get_iface (device), error->message);
g_error_free (error);
@@ -1881,6 +1876,26 @@ platform_link_added (NMManager *self,
if (nm_manager_get_device_by_ifindex (self, ifindex))
return;
device = find_device_by_iface (self, plink->name);
if (device) {
if (!nm_device_is_real (device)) {
if (nm_device_realize (device, plink, &error))
nm_device_setup_finish (device, plink);
else {
nm_log_warn (LOGD_DEVICE, "(%s): %s", plink->name, error->message);
g_clear_error (&error);
remove_device (self, device, FALSE, FALSE);
}
return;
} else if (!nm_device_realize (device, plink, &error)) {
nm_log_warn (LOGD_HW, "%s: factory failed to create device: %s",
plink->name, error->message);
g_clear_error (&error);
return;
}
return;
}
/* Try registered device factories */
factory = nm_device_factory_manager_find_factory_for_link_type (plink->type);
if (factory) {
@@ -1917,10 +1932,11 @@ platform_link_added (NMManager *self,
if (device) {
if (nm_plugin_missing)
nm_device_set_nm_plugin_missing (device, TRUE);
if (nm_device_realize (device, plink, &error))
add_device (self, device, plink->type != NM_LINK_TYPE_LOOPBACK);
else {
nm_log_warn (LOGD_HW, "%s: failed to realize device: %s",
if (nm_device_realize (device, plink, &error)) {
add_device (self, device);
nm_device_setup_finish (device, plink);
} else {
nm_log_warn (LOGD_DEVICE, "%s: failed to realize device: %s",
plink->name, error->message);
g_clear_error (&error);
}
@@ -1954,18 +1970,22 @@ _platform_link_cb_idle (PlatformLinkCbData *data)
device = nm_manager_get_device_by_ifindex (self, data->ifindex);
if (device) {
if (nm_device_is_software (device)) {
/* Software devices stick around until their connection is removed */
if (!nm_device_unrealize (device, FALSE, &error)) {
nm_log_warn (LOGD_DEVICE, "(%s): failed to unrealize: %s",
nm_device_get_iface (device),
error->message);
g_clear_error (&error);
remove_device (self, device, FALSE, TRUE);
}
}
} else {
/* Hardware devices always get removed when their kernel link is gone */
remove_device (self, device, FALSE, TRUE);
}
}
g_object_remove_weak_pointer (G_OBJECT (self), (gpointer *) &data->self);
}
}
g_slice_free (PlatformLinkCbData, data);
return G_SOURCE_REMOVE;
}
@@ -2079,7 +2099,8 @@ impl_manager_get_devices (NMManager *self,
const char *path;
path = nm_exported_object_get_path (NM_EXPORTED_OBJECT (iter->data));
if (path)
if ( path
&& nm_device_is_real (iter->data))
paths[i++] = path;
}
paths[i++] = NULL;
@@ -2181,7 +2202,7 @@ find_master (NMManager *self,
const char *master;
NMDevice *master_device = NULL;
NMSettingsConnection *master_connection = NULL;
GSList *iter, *connections = NULL;
GSList *iter;
s_con = nm_connection_get_setting_connection (connection);
g_assert (s_con);
@@ -2191,7 +2212,7 @@ find_master (NMManager *self,
return TRUE; /* success, but no master */
/* Try as an interface name first */
master_device = find_device_by_ip_iface (self, master);
master_device = find_device_by_iface (self, master);
if (master_device) {
if (master_device == device) {
g_set_error_literal (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_DEPENDENCY_FAILED,
@@ -2223,23 +2244,6 @@ find_master (NMManager *self,
break;
}
}
} else {
/* Might be a virtual interface that hasn't been created yet, so
* look through the interface names of connections that require
* virtual interfaces and see if one of their virtual interface
* names matches the master.
*/
connections = nm_manager_get_activatable_connections (self);
for (iter = connections; iter && !master_connection; iter = g_slist_next (iter)) {
NMSettingsConnection *candidate = iter->data;
char *vname;
vname = get_virtual_iface_name (self, NM_CONNECTION (candidate), NULL, NULL);
if (g_strcmp0 (master, vname) == 0 && is_compatible_with_slave (NM_CONNECTION (candidate), connection))
master_connection = candidate;
g_free (vname);
}
g_slist_free (connections);
}
}
@@ -2333,7 +2337,7 @@ ensure_master_active_connection (NMManager *self,
/* If the device is disconnected, find a compatible connection and
* activate it on the device.
*/
if (master_state == NM_DEVICE_STATE_DISCONNECTED) {
if (master_state == NM_DEVICE_STATE_DISCONNECTED || !nm_device_is_real (master_device)) {
GSList *connections;
g_assert (master_connection == NULL);
@@ -2394,9 +2398,11 @@ ensure_master_active_connection (NMManager *self,
continue;
found_device = TRUE;
if (!nm_device_is_software (candidate)) {
master_state = nm_device_get_state (candidate);
if (master_state != NM_DEVICE_STATE_DISCONNECTED)
if (nm_device_is_real (candidate) && master_state != NM_DEVICE_STATE_DISCONNECTED)
continue;
}
master_ac = nm_manager_activate_connection (self,
master_connection,
@@ -2683,6 +2689,17 @@ _internal_activate_device (NMManager *self, NMActiveConnection *active, GError *
return FALSE;
}
/* Create any backing resources the device needs */
if (!nm_device_is_real (device)) {
NMDevice *parent;
parent = find_parent_device_for_connection (self, (NMConnection *) connection);
if (!nm_device_create_and_realize (device, (NMConnection *) connection, parent, error)) {
g_prefix_error (error, "%s failed to create resources: ", nm_device_get_iface (device));
return FALSE;
}
}
/* Try to find the master connection/device if the connection has a dependency */
if (!find_master (self, applied, device,
&master_connection, &master_device, &master_ac,
@@ -3086,7 +3103,7 @@ validate_activation_request (NMManager *self,
if (!iface)
goto error;
device = find_device_by_ip_iface (self, iface);
device = find_device_by_iface (self, iface);
g_free (iface);
}
}
@@ -4209,6 +4226,7 @@ gboolean
nm_manager_start (NMManager *self, GError **error)
{
NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
GSList *iter, *connections;
guint i;
if (!nm_settings_start (priv->settings, error))
@@ -4256,11 +4274,14 @@ nm_manager_start (NMManager *self, GError **error)
/* Load VPN plugins */
priv->vpn_manager = g_object_ref (nm_vpn_manager_get ());
/*
* Connections added before the manager is started do not emit
/* Connections added before the manager is started do not emit
* connection-added signals thus devices have to be created manually.
*/
system_create_virtual_devices (self);
nm_log_dbg (LOGD_CORE, "creating virtual devices...");
connections = nm_settings_get_connections (priv->settings);
for (iter = connections; iter; iter = iter->next)
connection_added (priv->settings, NM_CONNECTION (iter->data), self);
g_slist_free (connections);
priv->devices_inited = TRUE;
@@ -5067,6 +5088,12 @@ nm_manager_init (NMManager *manager)
priv->metered = NM_METERED_UNKNOWN;
}
static gboolean
device_is_real (GObject *device, gpointer user_data)
{
return nm_device_is_real (NM_DEVICE (device));
}
static void
get_property (GObject *object, guint prop_id,
GValue *value, GParamSpec *pspec)
@@ -5139,7 +5166,7 @@ get_property (GObject *object, guint prop_id,
g_value_set_boolean (value, priv->sleeping);
break;
case PROP_DEVICES:
nm_utils_g_value_set_object_path_array (value, priv->devices, NULL, NULL);
nm_utils_g_value_set_object_path_array (value, priv->devices, device_is_real, NULL);
break;
case PROP_METERED:
g_value_set_uint (value, priv->metered);