diff --git a/src/devices/nm-device-bond.c b/src/devices/nm-device-bond.c index 2708dad89e..6f8caeb40b 100644 --- a/src/devices/nm-device-bond.c +++ b/src/devices/nm-device-bond.c @@ -421,42 +421,56 @@ enslave_slave (NMDevice *device, success = nm_platform_link_enslave (nm_device_get_ip_ifindex (device), nm_device_get_ip_ifindex (slave)); nm_device_bring_up (slave, TRUE, &no_firmware); - } - if (success) { + if (!success) + return FALSE; + nm_log_info (LOGD_BOND, "(%s): enslaved bond slave %s", iface, slave_iface); - g_object_notify (G_OBJECT (device), "slaves"); - } + } else + nm_log_info (LOGD_BOND, "(%s): bond slave %s was enslaved", iface, slave_iface); - return success; + g_object_notify (G_OBJECT (device), NM_DEVICE_BOND_SLAVES); + return TRUE; } static gboolean -release_slave (NMDevice *device, NMDevice *slave) +release_slave (NMDevice *device, + NMDevice *slave, + gboolean configure) { - gboolean success, no_firmware = FALSE; + gboolean success = TRUE, no_firmware = FALSE; - success = nm_platform_link_release (nm_device_get_ip_ifindex (device), - nm_device_get_ip_ifindex (slave)); + if (configure) { + success = nm_platform_link_release (nm_device_get_ip_ifindex (device), + nm_device_get_ip_ifindex (slave)); - if (success) { - nm_log_info (LOGD_BOND, "(%s): released bond slave %s", - nm_device_get_ip_iface (device), - nm_device_get_ip_iface (slave)); + if (success) { + nm_log_info (LOGD_BOND, "(%s): released bond slave %s", + nm_device_get_ip_iface (device), + nm_device_get_ip_iface (slave)); + } else { + nm_log_warn (LOGD_BOND, "(%s): failed to release bond slave %s", + nm_device_get_ip_iface (device), + nm_device_get_ip_iface (slave)); + } } else { - nm_log_warn (LOGD_BOND, "(%s): failed to release bond slave %s", + nm_log_info (LOGD_BOND, "(%s): bond slave %s was released", nm_device_get_ip_iface (device), nm_device_get_ip_iface (slave)); } - g_object_notify (G_OBJECT (device), "slaves"); - /* Kernel bonding code "closes" the slave when releasing it, (which clears - * IFF_UP), so we must bring it back up here to ensure carrier changes and - * other state is noticed by the now-released slave. - */ - if (!nm_device_bring_up (slave, TRUE, &no_firmware)) { - nm_log_warn (LOGD_BOND, "(%s): released bond slave could not be brought up.", - nm_device_get_iface (slave)); + if (success) + g_object_notify (G_OBJECT (device), NM_DEVICE_BOND_SLAVES); + + if (configure) { + /* Kernel bonding code "closes" the slave when releasing it, (which clears + * IFF_UP), so we must bring it back up here to ensure carrier changes and + * other state is noticed by the now-released slave. + */ + if (!nm_device_bring_up (slave, TRUE, &no_firmware)) { + nm_log_warn (LOGD_BOND, "(%s): released bond slave could not be brought up.", + nm_device_get_iface (slave)); + } } return success; diff --git a/src/devices/nm-device-bridge.c b/src/devices/nm-device-bridge.c index ad2dc7c8bb..5c99c0e998 100644 --- a/src/devices/nm-device-bridge.c +++ b/src/devices/nm-device-bridge.c @@ -391,30 +391,43 @@ enslave_slave (NMDevice *device, return FALSE; commit_slave_options (slave, nm_connection_get_setting_bridge_port (connection)); + + nm_log_info (LOGD_BRIDGE, "(%s): attached bridge port %s", + nm_device_get_ip_iface (device), + nm_device_get_ip_iface (slave)); + } else { + nm_log_info (LOGD_BRIDGE, "(%s): bridge port %s was attached", + nm_device_get_ip_iface (device), + nm_device_get_ip_iface (slave)); } - nm_log_info (LOGD_BRIDGE, "(%s): attached bridge port %s", - nm_device_get_ip_iface (device), - nm_device_get_ip_iface (slave)); g_object_notify (G_OBJECT (device), NM_DEVICE_BRIDGE_SLAVES); return TRUE; } static gboolean -release_slave (NMDevice *device, NMDevice *slave) +release_slave (NMDevice *device, + NMDevice *slave, + gboolean configure) { - gboolean success; + gboolean success = TRUE; - success = nm_platform_link_release (nm_device_get_ip_ifindex (device), - nm_device_get_ip_ifindex (slave)); + if (configure) { + success = nm_platform_link_release (nm_device_get_ip_ifindex (device), + nm_device_get_ip_ifindex (slave)); - if (success) { - nm_log_info (LOGD_BRIDGE, "(%s): detached bridge port %s", - nm_device_get_ip_iface (device), - nm_device_get_ip_iface (slave)); + if (success) { + nm_log_info (LOGD_BRIDGE, "(%s): detached bridge port %s", + nm_device_get_ip_iface (device), + nm_device_get_ip_iface (slave)); + } else { + nm_log_warn (LOGD_BRIDGE, "(%s): failed to detach bridge port %s", + nm_device_get_ip_iface (device), + nm_device_get_ip_iface (slave)); + } } else { - nm_log_warn (LOGD_BRIDGE, "(%s): failed to detach bridge port %s", + nm_log_info (LOGD_BRIDGE, "(%s): bridge port %s was detached", nm_device_get_ip_iface (device), nm_device_get_ip_iface (slave)); } diff --git a/src/devices/nm-device-team.c b/src/devices/nm-device-team.c index e672f6e1a3..63c2673fd9 100644 --- a/src/devices/nm-device-team.c +++ b/src/devices/nm-device-team.c @@ -691,42 +691,57 @@ enslave_slave (NMDevice *device, success = nm_platform_link_enslave (nm_device_get_ip_ifindex (device), nm_device_get_ip_ifindex (slave)); nm_device_bring_up (slave, TRUE, &no_firmware); - } - if (success) { + if (!success) + return FALSE; + nm_log_info (LOGD_TEAM, "(%s): enslaved team port %s", iface, slave_iface); - g_object_notify (G_OBJECT (device), "slaves"); - } + } else + nm_log_info (LOGD_TEAM, "(%s): team port %s was enslaved", iface, slave_iface); - return success; + g_object_notify (G_OBJECT (device), NM_DEVICE_TEAM_SLAVES); + + return TRUE; } static gboolean -release_slave (NMDevice *device, NMDevice *slave) +release_slave (NMDevice *device, + NMDevice *slave, + gboolean configure) { - gboolean success, no_firmware = FALSE; + gboolean success = TRUE, no_firmware = FALSE; - success = nm_platform_link_release (nm_device_get_ip_ifindex (device), - nm_device_get_ip_ifindex (slave)); + if (configure) { + success = nm_platform_link_release (nm_device_get_ip_ifindex (device), + nm_device_get_ip_ifindex (slave)); - if (success) { - nm_log_info (LOGD_TEAM, "(%s): released team port %s", - nm_device_get_ip_iface (device), - nm_device_get_ip_iface (slave)); + if (success) { + nm_log_info (LOGD_TEAM, "(%s): released team port %s", + nm_device_get_ip_iface (device), + nm_device_get_ip_iface (slave)); + } else { + nm_log_warn (LOGD_TEAM, "(%s): failed to release team port %s", + nm_device_get_ip_iface (device), + nm_device_get_ip_iface (slave)); + } } else { - nm_log_warn (LOGD_TEAM, "(%s): failed to release team port %s", + nm_log_info (LOGD_TEAM, "(%s): team port %s was released", nm_device_get_ip_iface (device), nm_device_get_ip_iface (slave)); } - g_object_notify (G_OBJECT (device), "slaves"); - /* Kernel team code "closes" the port when releasing it, (which clears - * IFF_UP), so we must bring it back up here to ensure carrier changes and - * other state is noticed by the now-released port. - */ - if (!nm_device_bring_up (slave, TRUE, &no_firmware)) { - nm_log_warn (LOGD_TEAM, "(%s): released team port could not be brought up.", - nm_device_get_iface (slave)); + if (success) + g_object_notify (G_OBJECT (device), NM_DEVICE_TEAM_SLAVES); + + if (configure) { + /* Kernel team code "closes" the port when releasing it, (which clears + * IFF_UP), so we must bring it back up here to ensure carrier changes and + * other state is noticed by the now-released port. + */ + if (!nm_device_bring_up (slave, TRUE, &no_firmware)) { + nm_log_warn (LOGD_TEAM, "(%s): released team port could not be brought up.", + nm_device_get_iface (slave)); + } } return success; diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c index 10ebb7a92d..2b576add4a 100644 --- a/src/devices/nm-device.c +++ b/src/devices/nm-device.c @@ -896,7 +896,7 @@ free_slave_info (SlaveInfo *info) * nm_device_enslave_slave: * @dev: the master device * @slave: the slave device to enslave - * @connection: the slave device's connection + * @connection: (allow-none): the slave device's connection * * If @dev is capable of enslaving other devices (ie it's a bridge, bond, team, * etc) then this function enslaves @slave. @@ -909,20 +909,27 @@ nm_device_enslave_slave (NMDevice *dev, NMDevice *slave, NMConnection *connectio { SlaveInfo *info; gboolean success = FALSE; + gboolean configure; g_return_val_if_fail (dev != NULL, FALSE); g_return_val_if_fail (slave != NULL, FALSE); - g_return_val_if_fail (nm_device_get_state (slave) >= NM_DEVICE_STATE_DISCONNECTED, FALSE); g_return_val_if_fail (NM_DEVICE_GET_CLASS (dev)->enslave_slave != NULL, FALSE); info = find_slave_info (dev, slave); if (!info) return FALSE; - g_warn_if_fail (info->enslaved == FALSE); - success = NM_DEVICE_GET_CLASS (dev)->enslave_slave (dev, slave, connection, info->configure); + if (info->enslaved) + success = TRUE; + else { + configure = (info->configure && connection != NULL); + if (configure) + g_return_val_if_fail (nm_device_get_state (slave) >= NM_DEVICE_STATE_DISCONNECTED, FALSE); + + success = NM_DEVICE_GET_CLASS (dev)->enslave_slave (dev, slave, connection, configure); + info->enslaved = success; + } - info->enslaved = success; nm_device_slave_notify_enslave (info->slave, success); /* Ensure the device's hardware address is up-to-date; it often changes @@ -949,15 +956,17 @@ nm_device_enslave_slave (NMDevice *dev, NMDevice *slave, NMConnection *connectio * nm_device_release_one_slave: * @dev: the master device * @slave: the slave device to release + * @configure: whether @dev needs to actually release @slave * * If @dev is capable of enslaving other devices (ie it's a bridge, bond, team, - * etc) then this function releases the previously enslaved @slave. + * etc) then this function releases the previously enslaved @slave and/or + * updates the state of @dev and @slave to reflect its release. * * Returns: %TRUE on success, %FALSE on failure, if this device cannot enslave * other devices, or if @slave was never enslaved. */ static gboolean -nm_device_release_one_slave (NMDevice *dev, NMDevice *slave) +nm_device_release_one_slave (NMDevice *dev, NMDevice *slave, gboolean configure) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (dev); SlaveInfo *info; @@ -973,13 +982,15 @@ nm_device_release_one_slave (NMDevice *dev, NMDevice *slave) priv->slaves = g_slist_remove (priv->slaves, info); if (info->enslaved) { - success = NM_DEVICE_GET_CLASS (dev)->release_slave (dev, slave); + success = NM_DEVICE_GET_CLASS (dev)->release_slave (dev, slave, configure); /* The release_slave() implementation logs success/failure (in the * correct device-specific log domain), so we don't have to do anything. */ } - if (priv->state == NM_DEVICE_STATE_FAILED) + if (!configure) + reason = NM_DEVICE_STATE_REASON_NONE; + else if (priv->state == NM_DEVICE_STATE_FAILED) reason = NM_DEVICE_STATE_REASON_DEPENDENCY_FAILED; else reason = priv->state_reason; @@ -1208,6 +1219,29 @@ device_link_changed (NMDevice *device, NMPlatformLink *info) nm_device_emit_recheck_auto_activate (device); } + /* Update slave status for external changes */ + if (info->master && !priv->enslaved) { + NMDevice *master; + + master = nm_manager_get_device_by_ifindex (nm_manager_get (), info->master); + if (master && NM_DEVICE_GET_CLASS (master)->enslave_slave) { + g_clear_object (&priv->master); + priv->master = g_object_ref (master); + nm_device_master_add_slave (master, device, FALSE); + nm_device_enslave_slave (master, device, NULL); + } else if (master) { + nm_log_info (LOGD_DEVICE, "(%s): enslaved to non-master-type device %s; ignoring", + nm_device_get_iface (device), + nm_device_get_iface (master)); + } else { + nm_log_warn (LOGD_DEVICE, "(%s): enslaved to unknown device %d %s", + nm_device_get_iface (device), + info->master, + nm_platform_link_get_name (info->master)); + } + } else if (priv->enslaved && !info->master) + nm_device_release_one_slave (priv->master, device, FALSE); + if (klass->link_changed) klass->link_changed (device, info); @@ -1330,8 +1364,6 @@ slave_state_changed (NMDevice *slave, slave_new_state, state_to_string (slave_new_state)); - g_assert (priv->state > NM_DEVICE_STATE_DISCONNECTED); - /* Don't try to enslave slaves until the master is ready */ if (priv->state < NM_DEVICE_STATE_CONFIG) return; @@ -1347,7 +1379,7 @@ slave_state_changed (NMDevice *slave, } if (release) { - nm_device_release_one_slave (self, slave); + nm_device_release_one_slave (self, slave, TRUE); /* Bridge/bond/team interfaces are left up until manually deactivated */ if (priv->slaves == NULL && priv->state == NM_DEVICE_STATE_ACTIVATED) { nm_log_dbg (LOGD_DEVICE, "(%s): last slave removed; remaining activated", @@ -1376,9 +1408,11 @@ nm_device_master_add_slave (NMDevice *dev, NMDevice *slave, gboolean configure) g_return_val_if_fail (dev != NULL, FALSE); g_return_val_if_fail (slave != NULL, FALSE); - g_return_val_if_fail (nm_device_get_state (slave) >= NM_DEVICE_STATE_DISCONNECTED, FALSE); g_return_val_if_fail (NM_DEVICE_GET_CLASS (dev)->enslave_slave != NULL, FALSE); + if (configure) + g_return_val_if_fail (nm_device_get_state (slave) >= NM_DEVICE_STATE_DISCONNECTED, FALSE); + if (!find_slave_info (dev, slave)) { info = g_malloc0 (sizeof (SlaveInfo)); info->slave = g_object_ref (slave); @@ -1493,10 +1527,14 @@ nm_device_master_release_slaves (NMDevice *self) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + /* Don't release the slaves if this connection doesn't belong to NM. */ + if (nm_device_uses_generated_connection (self)) + return; + while (priv->slaves) { SlaveInfo *info = priv->slaves->data; - nm_device_release_one_slave (self, info->slave); + nm_device_release_one_slave (self, info->slave, TRUE); } } @@ -1535,31 +1573,42 @@ nm_device_slave_notify_enslave (NMDevice *dev, gboolean success) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (dev); NMConnection *connection = nm_device_get_connection (dev); + gboolean activating = (priv->state == NM_DEVICE_STATE_IP_CONFIG); g_assert (priv->master); - g_warn_if_fail (priv->enslaved == FALSE); - g_warn_if_fail (priv->state == NM_DEVICE_STATE_IP_CONFIG); - if (success) { - nm_log_info (LOGD_DEVICE, - "Activation (%s) connection '%s' enslaved, continuing activation", - nm_device_get_iface (dev), - nm_connection_get_id (connection)); + if (!priv->enslaved) { + if (success) { + if (activating) { + nm_log_info (LOGD_DEVICE, + "Activation (%s) connection '%s' enslaved, continuing activation", + nm_device_get_iface (dev), + nm_connection_get_id (connection)); + } else { + nm_log_info (LOGD_DEVICE, + "(%s): enslaved to %s", + nm_device_get_iface (dev), + nm_device_get_iface (priv->master)); + } - priv->enslaved = TRUE; - g_object_notify (G_OBJECT (dev), NM_DEVICE_MASTER); - } else { - nm_log_warn (LOGD_DEVICE, - "Activation (%s) connection '%s' could not be enslaved", - nm_device_get_iface (dev), - nm_connection_get_id (connection)); + priv->enslaved = TRUE; + g_object_notify (G_OBJECT (dev), NM_DEVICE_MASTER); + } else if (activating) { + nm_log_warn (LOGD_DEVICE, + "Activation (%s) connection '%s' could not be enslaved", + nm_device_get_iface (dev), + nm_connection_get_id (connection)); + } } - priv->ip4_state = IP_DONE; - priv->ip6_state = IP_DONE; - nm_device_queue_state (dev, - success ? NM_DEVICE_STATE_SECONDARIES : NM_DEVICE_STATE_FAILED, - NM_DEVICE_STATE_REASON_NONE); + if (activating) { + priv->ip4_state = IP_DONE; + priv->ip6_state = IP_DONE; + nm_device_queue_state (dev, + success ? NM_DEVICE_STATE_SECONDARIES : NM_DEVICE_STATE_FAILED, + NM_DEVICE_STATE_REASON_NONE); + } else + nm_device_queue_recheck_assume (dev); } /** @@ -1577,7 +1626,8 @@ nm_device_slave_notify_release (NMDevice *dev, NMDeviceStateReason reason) NMDeviceState new_state; const char *master_status; - if ( priv->state > NM_DEVICE_STATE_DISCONNECTED + if ( reason != NM_DEVICE_STATE_REASON_NONE + && priv->state > NM_DEVICE_STATE_DISCONNECTED && priv->state <= NM_DEVICE_STATE_ACTIVATED) { if (reason == NM_DEVICE_STATE_REASON_DEPENDENCY_FAILED) { new_state = NM_DEVICE_STATE_FAILED; @@ -1597,6 +1647,11 @@ nm_device_slave_notify_release (NMDevice *dev, NMDeviceStateReason reason) master_status); nm_device_queue_state (dev, new_state, reason); + } else { + nm_log_info (LOGD_DEVICE, + "(%s): released from master %s", + nm_device_get_iface (dev), + nm_device_get_iface (priv->master)); } if (priv->enslaved) { diff --git a/src/devices/nm-device.h b/src/devices/nm-device.h index 503837281c..1e2f39a31e 100644 --- a/src/devices/nm-device.h +++ b/src/devices/nm-device.h @@ -198,7 +198,8 @@ typedef struct { gboolean configure); gboolean (* release_slave) (NMDevice *self, - NMDevice *slave); + NMDevice *slave, + gboolean configure); gboolean (* have_any_ready_slaves) (NMDevice *self, const GSList *slaves);