core: rework master/slave handling

This commit is contained in:
Dan Williams
2012-11-14 14:05:30 -06:00
parent 1472532f28
commit b4a11a86f7
7 changed files with 374 additions and 492 deletions

View File

@@ -62,8 +62,7 @@ typedef struct {
gulong user_uid;
char *dbus_sender;
NMActiveConnection *dep;
guint dep_state_id;
NMDevice *master;
gboolean shared;
GSList *share_rules;
@@ -75,13 +74,6 @@ enum {
PROP_MASTER = 2000,
};
enum {
DEP_RESULT,
LAST_SIGNAL
};
static guint signals[LAST_SIGNAL] = { 0 };
/*******************************************************************/
typedef struct {
@@ -229,31 +221,10 @@ nm_act_request_get_assumed (NMActRequest *req)
return NM_ACT_REQUEST_GET_PRIVATE (req)->assumed;
}
NMActiveConnection *
nm_act_request_get_dependency (NMActRequest *req)
GObject *
nm_act_request_get_master (NMActRequest *req)
{
return NM_ACT_REQUEST_GET_PRIVATE (req)->dep;
}
static NMActRequestDependencyResult
ac_state_to_dep_result (NMActiveConnection *ac)
{
NMActiveConnectionState state = nm_active_connection_get_state (ac);
if (state == NM_ACTIVE_CONNECTION_STATE_ACTIVATING)
return NM_ACT_REQUEST_DEP_RESULT_WAIT;
else if (state == NM_ACTIVE_CONNECTION_STATE_ACTIVATED)
return NM_ACT_REQUEST_DEP_RESULT_READY;
return NM_ACT_REQUEST_DEP_RESULT_FAILED;
}
NMActRequestDependencyResult
nm_act_request_get_dependency_result (NMActRequest *req)
{
NMActRequestPrivate *priv = NM_ACT_REQUEST_GET_PRIVATE (req);
return priv->dep ? ac_state_to_dep_result (priv->dep) : NM_ACT_REQUEST_DEP_RESULT_READY;
return (GObject *) NM_ACT_REQUEST_GET_PRIVATE (req)->master;
}
/********************************************************************/
@@ -411,39 +382,6 @@ device_state_changed (NMDevice *device,
/********************************************************************/
static void
dep_gone (NMActRequest *self, GObject *ignored)
{
NMActRequestPrivate *priv = NM_ACT_REQUEST_GET_PRIVATE (self);
g_warn_if_fail (G_OBJECT (priv->dep) == ignored);
/* Dependent connection is gone; clean up and fail */
priv->dep = NULL;
priv->dep_state_id = 0;
g_signal_emit (self, signals[DEP_RESULT], 0, NM_ACT_REQUEST_DEP_RESULT_FAILED);
}
static void
dep_state_changed (NMActiveConnection *dep,
GParamSpec *pspec,
NMActRequest *self)
{
NMActRequestPrivate *priv = NM_ACT_REQUEST_GET_PRIVATE (self);
NMActRequestDependencyResult result;
g_warn_if_fail (priv->dep == dep);
result = ac_state_to_dep_result (priv->dep);
if (result == NM_ACT_REQUEST_DEP_RESULT_FAILED) {
g_object_weak_unref (G_OBJECT (priv->dep), (GWeakNotify) dep_gone, self);
g_signal_handler_disconnect (priv->dep, priv->dep_state_id);
priv->dep = NULL;
priv->dep_state_id = 0;
}
g_signal_emit (self, signals[DEP_RESULT], 0, result);
}
/**
* nm_act_request_new:
*
@@ -458,9 +396,9 @@ dep_state_changed (NMActiveConnection *dep,
* @assumed: pass %TRUE if the activation should "assume" (ie, taking over) an
* existing connection made before this instance of NM started
* @device: the device/interface to configure according to @connection
* @dependency: if the activation depends on another device (ie, VLAN slave,
* bond slave, etc) pass the #NMActiveConnection that this activation request
* should wait for before proceeding
* @master: if the activation depends on another device (ie, bond or bridge
* master to which this device will be enslaved) pass the #NMDevice that this
* activation request be enslaved to
*
* Begins activation of @device using the given @connection and other details.
*
@@ -474,7 +412,7 @@ nm_act_request_new (NMConnection *connection,
const char *dbus_sender,
gboolean assumed,
gpointer *device,
NMActiveConnection *dependency)
gpointer *master)
{
GObject *object;
NMActRequestPrivate *priv;
@@ -500,14 +438,11 @@ nm_act_request_new (NMConnection *connection,
priv->user_requested = user_requested;
priv->dbus_sender = g_strdup (dbus_sender);
priv->assumed = assumed;
if (master) {
g_assert (NM_IS_DEVICE (master));
g_assert (NM_DEVICE (master) != NM_DEVICE (device));
if (dependency) {
priv->dep = dependency;
g_object_weak_ref (G_OBJECT (dependency), (GWeakNotify) dep_gone, object);
priv->dep_state_id = g_signal_connect (dependency,
"notify::" NM_ACTIVE_CONNECTION_STATE,
G_CALLBACK (dep_state_changed),
object);
priv->master = g_object_ref (master);
}
if (!nm_active_connection_export (NM_ACTIVE_CONNECTION (object),
@@ -530,16 +465,10 @@ get_property (GObject *object, guint prop_id,
GValue *value, GParamSpec *pspec)
{
NMActRequestPrivate *priv = NM_ACT_REQUEST_GET_PRIVATE (object);
NMDevice *master;
switch (prop_id) {
case PROP_MASTER:
if (priv->dep && NM_IS_ACT_REQUEST (priv->dep)) {
master = NM_DEVICE (nm_act_request_get_device (NM_ACT_REQUEST (priv->dep)));
g_assert (master);
g_value_set_boxed (value, nm_device_get_path (master));
} else
g_value_set_boxed (value, "/");
g_value_set_boxed (value, priv->master ? nm_device_get_path (priv->master) : "/");
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
@@ -580,12 +509,7 @@ dispose (GObject *object)
g_free (priv->dbus_sender);
if (priv->dep) {
g_object_weak_unref (G_OBJECT (priv->dep), (GWeakNotify) dep_gone, object);
g_signal_handler_disconnect (priv->dep, priv->dep_state_id);
priv->dep = NULL;
priv->dep_state_id = 0;
}
g_clear_object (&priv->master);
G_OBJECT_CLASS (nm_act_request_parent_class)->dispose (object);
}
@@ -611,13 +535,5 @@ nm_act_request_class_init (NMActRequestClass *req_class)
object_class->finalize = finalize;
g_object_class_override_property (object_class, PROP_MASTER, NM_ACTIVE_CONNECTION_MASTER);
signals[DEP_RESULT] =
g_signal_new (NM_ACT_REQUEST_DEPENDENCY_RESULT,
G_OBJECT_CLASS_TYPE (object_class),
G_SIGNAL_RUN_FIRST,
0, NULL, NULL,
g_cclosure_marshal_VOID__UINT,
G_TYPE_NONE, 1, G_TYPE_UINT);
}

View File

@@ -34,15 +34,6 @@
#define NM_IS_ACT_REQUEST_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_ACT_REQUEST))
#define NM_ACT_REQUEST_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_ACT_REQUEST, NMActRequestClass))
typedef enum {
NM_ACT_REQUEST_DEP_RESULT_UNKNOWN,
NM_ACT_REQUEST_DEP_RESULT_WAIT,
NM_ACT_REQUEST_DEP_RESULT_READY,
NM_ACT_REQUEST_DEP_RESULT_FAILED,
} NMActRequestDependencyResult;
#define NM_ACT_REQUEST_DEPENDENCY_RESULT "dependency-result"
typedef struct {
GObject parent;
} NMActRequest;
@@ -52,8 +43,6 @@ typedef struct {
/* Signals */
void (*properties_changed) (NMActRequest *req, GHashTable *properties);
void (*dependency_result) (NMActRequest *req, NMActRequestDependencyResult result);
} NMActRequestClass;
GType nm_act_request_get_type (void);
@@ -65,7 +54,7 @@ NMActRequest *nm_act_request_new (NMConnection *connection,
const char *dbus_sender,
gboolean assumed,
gpointer *device, /* An NMDevice */
NMActiveConnection *dependency);
gpointer *master); /* An NMDevice */
NMConnection *nm_act_request_get_connection (NMActRequest *req);
@@ -87,9 +76,7 @@ GObject * nm_act_request_get_device (NMActRequest *req);
gboolean nm_act_request_get_assumed (NMActRequest *req);
NMActiveConnection * nm_act_request_get_dependency (NMActRequest *req);
NMActRequestDependencyResult nm_act_request_get_dependency_result (NMActRequest *req);
GObject * nm_act_request_get_master (NMActRequest *req);
/* Secrets handling */

View File

@@ -46,7 +46,7 @@ G_DEFINE_TYPE (NMDeviceBond, nm_device_bond, NM_TYPE_DEVICE_WIRED)
#define NM_BOND_ERROR (nm_bond_error_quark ())
typedef struct {
GSList *slaves;
gboolean unused;
} NMDeviceBondPrivate;
enum {
@@ -321,118 +321,43 @@ act_stage1_prepare (NMDevice *dev, NMDeviceStateReason *reason)
return ret;
}
static void
slave_state_changed (NMDevice *slave,
NMDeviceState new_state,
NMDeviceState old_state,
NMDeviceStateReason reason,
gpointer user_data)
{
NMDeviceBond *self = NM_DEVICE_BOND (user_data);
nm_log_dbg (LOGD_BOND, "(%s): slave %s state change %d -> %d",
nm_device_get_iface (NM_DEVICE (self)),
nm_device_get_iface (slave),
old_state,
new_state);
if ( old_state > NM_DEVICE_STATE_DISCONNECTED
&& new_state <= NM_DEVICE_STATE_DISCONNECTED) {
/* Slave is no longer available or managed; can't use it */
nm_device_release_slave (NM_DEVICE (self), slave);
}
}
typedef struct {
NMDevice *slave;
guint state_id;
} SlaveInfo;
static SlaveInfo *
find_slave_info_by_device (NMDeviceBond *self, NMDevice *slave)
{
NMDeviceBondPrivate *priv = NM_DEVICE_BOND_GET_PRIVATE (self);
GSList *iter;
for (iter = priv->slaves; iter; iter = g_slist_next (iter)) {
if (((SlaveInfo *) iter->data)->slave == slave)
return iter->data;
}
return NULL;
}
static void
free_slave_info (SlaveInfo *sinfo)
{
g_return_if_fail (sinfo != NULL);
g_return_if_fail (sinfo->slave != NULL);
g_signal_handler_disconnect (sinfo->slave, sinfo->state_id);
g_object_unref (sinfo->slave);
memset (sinfo, 0, sizeof (*sinfo));
g_free (sinfo);
}
static gboolean
enslave_slave (NMDevice *device, NMDevice *slave, NMConnection *connection)
{
NMDeviceBond *self = NM_DEVICE_BOND (device);
NMDeviceBondPrivate *priv = NM_DEVICE_BOND_GET_PRIVATE (self);
gboolean success, no_firmware = FALSE;
if (find_slave_info_by_device (self, slave))
return TRUE;
const char *iface = nm_device_get_ip_iface (device);
const char *slave_iface = nm_device_get_ip_iface (slave);
nm_device_hw_take_down (slave, TRUE);
success = nm_system_bond_enslave (nm_device_get_ip_ifindex (device),
nm_device_get_ip_iface (device),
iface,
nm_device_get_ip_ifindex (slave),
nm_device_get_ip_iface (slave));
if (success) {
SlaveInfo *sinfo;
sinfo = g_malloc0 (sizeof (*slave));
sinfo->slave = g_object_ref (slave);
sinfo->state_id = g_signal_connect (slave,
"state-changed",
(GCallback) slave_state_changed,
self);
priv->slaves = g_slist_append (priv->slaves, sinfo);
nm_log_dbg (LOGD_BOND, "(%s): enslaved bond slave %s",
nm_device_get_ip_iface (device),
nm_device_get_ip_iface (slave));
g_object_notify (G_OBJECT (device), "slaves");
}
slave_iface);
nm_device_hw_bring_up (slave, TRUE, &no_firmware);
if (success) {
nm_log_info (LOGD_BOND, "(%s): enslaved bond slave %s", iface, slave_iface);
g_object_notify (G_OBJECT (device), "slaves");
}
return success;
}
static gboolean
release_slave (NMDevice *device, NMDevice *slave)
{
NMDeviceBond *self = NM_DEVICE_BOND (device);
NMDeviceBondPrivate *priv = NM_DEVICE_BOND_GET_PRIVATE (self);
gboolean success;
SlaveInfo *sinfo;
sinfo = find_slave_info_by_device (self, slave);
if (!sinfo)
return FALSE;
success = nm_system_bond_release (nm_device_get_ip_ifindex (device),
nm_device_get_ip_iface (device),
nm_device_get_ip_ifindex (slave),
nm_device_get_ip_iface (slave));
nm_log_dbg (LOGD_BOND, "(%s): released bond slave %s (success %d)",
nm_device_get_ip_iface (device),
nm_device_get_ip_iface (slave),
success);
priv->slaves = g_slist_remove (priv->slaves, sinfo);
free_slave_info (sinfo);
nm_log_info (LOGD_BOND, "(%s): released bond slave %s (success %d)",
nm_device_get_ip_iface (device),
nm_device_get_ip_iface (slave),
success);
g_object_notify (G_OBJECT (device), "slaves");
return success;
}
@@ -473,12 +398,9 @@ static void
get_property (GObject *object, guint prop_id,
GValue *value, GParamSpec *pspec)
{
NMDeviceBond *self = NM_DEVICE_BOND (object);
NMDeviceBondPrivate *priv = NM_DEVICE_BOND_GET_PRIVATE (self);
const guint8 *current_addr;
GPtrArray *slaves;
GSList *iter;
SlaveInfo *info;
GSList *list, *iter;
switch (prop_id) {
case PROP_HW_ADDRESS:
@@ -490,10 +412,10 @@ get_property (GObject *object, guint prop_id,
break;
case PROP_SLAVES:
slaves = g_ptr_array_new ();
for (iter = priv->slaves; iter; iter = iter->next) {
info = iter->data;
g_ptr_array_add (slaves, g_strdup (nm_device_get_path (info->slave)));
}
list = nm_device_master_get_slaves (NM_DEVICE (object));
for (iter = list; iter; iter = iter->next)
g_ptr_array_add (slaves, g_strdup (nm_device_get_path (NM_DEVICE (iter->data))));
g_slist_free (list);
g_value_take_boxed (value, slaves);
break;
default:
@@ -513,21 +435,6 @@ set_property (GObject *object, guint prop_id,
}
}
static void
dispose (GObject *object)
{
NMDeviceBond *self = NM_DEVICE_BOND (object);
NMDeviceBondPrivate *priv = NM_DEVICE_BOND_GET_PRIVATE (self);
GSList *iter;
for (iter = priv->slaves; iter; iter = g_slist_next (iter))
release_slave (NM_DEVICE (self), ((SlaveInfo *) iter->data)->slave);
g_slist_free (priv->slaves);
priv->slaves = NULL;
G_OBJECT_CLASS (nm_device_bond_parent_class)->dispose (object);
}
static void
nm_device_bond_class_init (NMDeviceBondClass *klass)
{
@@ -540,7 +447,6 @@ nm_device_bond_class_init (NMDeviceBondClass *klass)
object_class->constructed = constructed;
object_class->get_property = get_property;
object_class->set_property = set_property;
object_class->dispose = dispose;
parent_class->get_generic_capabilities = get_generic_capabilities;
parent_class->update_hw_address = update_hw_address;

View File

@@ -46,7 +46,7 @@ G_DEFINE_TYPE (NMDeviceBridge, nm_device_bridge, NM_TYPE_DEVICE_WIRED)
#define NM_BRIDGE_ERROR (nm_bridge_error_quark ())
typedef struct {
GSList *slaves;
gboolean unused;
} NMDeviceBridgePrivate;
enum {
@@ -384,125 +384,48 @@ act_stage1_prepare (NMDevice *dev, NMDeviceStateReason *reason)
return ret;
}
static void
slave_state_changed (NMDevice *slave,
NMDeviceState new_state,
NMDeviceState old_state,
NMDeviceStateReason reason,
gpointer user_data)
{
NMDeviceBridge *self = NM_DEVICE_BRIDGE (user_data);
nm_log_dbg (LOGD_DEVICE, "(%s): slave %s state change %d -> %d",
nm_device_get_iface (NM_DEVICE (self)),
nm_device_get_iface (slave),
old_state,
new_state);
if ( old_state > NM_DEVICE_STATE_DISCONNECTED
&& new_state <= NM_DEVICE_STATE_DISCONNECTED) {
/* Slave is no longer available or managed; can't use it */
nm_device_release_slave (NM_DEVICE (self), slave);
}
}
typedef struct {
NMDevice *slave;
guint state_id;
} SlaveInfo;
static SlaveInfo *
find_slave_info_by_device (NMDeviceBridge *self, NMDevice *slave)
{
NMDeviceBridgePrivate *priv = NM_DEVICE_BRIDGE_GET_PRIVATE (self);
GSList *iter;
for (iter = priv->slaves; iter; iter = g_slist_next (iter)) {
if (((SlaveInfo *) iter->data)->slave == slave)
return iter->data;
}
return NULL;
}
static void
free_slave_info (SlaveInfo *sinfo)
{
g_return_if_fail (sinfo != NULL);
g_return_if_fail (sinfo->slave != NULL);
g_signal_handler_disconnect (sinfo->slave, sinfo->state_id);
g_object_unref (sinfo->slave);
memset (sinfo, 0, sizeof (*sinfo));
g_free (sinfo);
}
static gboolean
enslave_slave (NMDevice *device, NMDevice *slave, NMConnection *connection)
{
NMDeviceBridge *self = NM_DEVICE_BRIDGE (device);
NMDeviceBridgePrivate *priv = NM_DEVICE_BRIDGE_GET_PRIVATE (self);
gboolean success;
if (find_slave_info_by_device (self, slave))
return TRUE;
NMSettingBridgePort *s_port;
const char *iface = nm_device_get_ip_iface (device);
const char *slave_iface = nm_device_get_ip_iface (slave);
success = nm_system_bridge_attach (nm_device_get_ip_ifindex (device),
nm_device_get_ip_iface (device),
iface,
nm_device_get_ip_ifindex (slave),
nm_device_get_ip_iface (slave));
if (success) {
SlaveInfo *sinfo;
NMSettingBridgePort *s_port;
const char *slave_iface = nm_device_get_ip_iface (slave);
slave_iface);
if (!success)
return FALSE;
sinfo = g_malloc0 (sizeof (*slave));
sinfo->slave = g_object_ref (slave);
sinfo->state_id = g_signal_connect (slave,
"state-changed",
(GCallback) slave_state_changed,
self);
priv->slaves = g_slist_append (priv->slaves, sinfo);
nm_log_dbg (LOGD_DEVICE, "(%s): attached bridge component %s",
nm_device_get_ip_iface (device),
nm_device_get_ip_iface (slave));
g_object_notify (G_OBJECT (device), NM_DEVICE_BRIDGE_SLAVES);
/* Set port properties */
s_port = nm_connection_get_setting_bridge_port (connection);
if (s_port) {
set_sysfs_uint (slave_iface, G_OBJECT (s_port), NM_SETTING_BRIDGE_PORT_PRIORITY, "priority", TRUE, FALSE);
set_sysfs_uint (slave_iface, G_OBJECT (s_port), NM_SETTING_BRIDGE_PORT_PATH_COST, "path_cost", TRUE, FALSE);
set_sysfs_uint (slave_iface, G_OBJECT (s_port), NM_SETTING_BRIDGE_PORT_HAIRPIN_MODE, "hairpin_mode", FALSE, FALSE);
}
/* Set port properties */
s_port = nm_connection_get_setting_bridge_port (connection);
if (s_port) {
set_sysfs_uint (slave_iface, G_OBJECT (s_port), NM_SETTING_BRIDGE_PORT_PRIORITY, "priority", TRUE, FALSE);
set_sysfs_uint (slave_iface, G_OBJECT (s_port), NM_SETTING_BRIDGE_PORT_PATH_COST, "path_cost", TRUE, FALSE);
set_sysfs_uint (slave_iface, G_OBJECT (s_port), NM_SETTING_BRIDGE_PORT_HAIRPIN_MODE, "hairpin_mode", FALSE, FALSE);
}
return success;
nm_log_info (LOGD_DEVICE, "(%s): attached bridge port %s", iface, slave_iface);
g_object_notify (G_OBJECT (device), NM_DEVICE_BRIDGE_SLAVES);
return TRUE;
}
static gboolean
release_slave (NMDevice *device, NMDevice *slave)
{
NMDeviceBridge *self = NM_DEVICE_BRIDGE (device);
NMDeviceBridgePrivate *priv = NM_DEVICE_BRIDGE_GET_PRIVATE (self);
gboolean success;
SlaveInfo *sinfo;
sinfo = find_slave_info_by_device (self, slave);
if (!sinfo)
return FALSE;
success = nm_system_bridge_detach (nm_device_get_ip_ifindex (device),
nm_device_get_ip_iface (device),
nm_device_get_ip_ifindex (slave),
nm_device_get_ip_iface (slave));
nm_log_dbg (LOGD_DEVICE, "(%s): detached bridge component %s (success %d)",
nm_device_get_ip_iface (device),
nm_device_get_ip_iface (slave),
success);
priv->slaves = g_slist_remove (priv->slaves, sinfo);
free_slave_info (sinfo);
nm_log_info (LOGD_DEVICE, "(%s): detached bridge port %s (success %d)",
nm_device_get_ip_iface (device),
nm_device_get_ip_iface (slave),
success);
g_object_notify (G_OBJECT (device), NM_DEVICE_BRIDGE_SLAVES);
return success;
}
@@ -543,12 +466,9 @@ static void
get_property (GObject *object, guint prop_id,
GValue *value, GParamSpec *pspec)
{
NMDeviceBridge *self = NM_DEVICE_BRIDGE (object);
NMDeviceBridgePrivate *priv = NM_DEVICE_BRIDGE_GET_PRIVATE (self);
const guint8 *current_addr;
GPtrArray *slaves;
GSList *iter;
SlaveInfo *info;
GSList *list, *iter;
switch (prop_id) {
case PROP_HW_ADDRESS:
@@ -560,10 +480,10 @@ get_property (GObject *object, guint prop_id,
break;
case PROP_SLAVES:
slaves = g_ptr_array_new ();
for (iter = priv->slaves; iter; iter = iter->next) {
info = iter->data;
g_ptr_array_add (slaves, g_strdup (nm_device_get_path (info->slave)));
}
list = nm_device_master_get_slaves (NM_DEVICE (object));
for (iter = list; iter; iter = iter->next)
g_ptr_array_add (slaves, g_strdup (nm_device_get_path (NM_DEVICE (iter->data))));
g_slist_free (list);
g_value_take_boxed (value, slaves);
break;
default:
@@ -583,21 +503,6 @@ set_property (GObject *object, guint prop_id,
}
}
static void
dispose (GObject *object)
{
NMDeviceBridge *self = NM_DEVICE_BRIDGE (object);
NMDeviceBridgePrivate *priv = NM_DEVICE_BRIDGE_GET_PRIVATE (self);
GSList *iter;
for (iter = priv->slaves; iter; iter = g_slist_next (iter))
release_slave (NM_DEVICE (self), ((SlaveInfo *) iter->data)->slave);
g_slist_free (priv->slaves);
priv->slaves = NULL;
G_OBJECT_CLASS (nm_device_bridge_parent_class)->dispose (object);
}
static void
nm_device_bridge_class_init (NMDeviceBridgeClass *klass)
{
@@ -610,7 +515,6 @@ nm_device_bridge_class_init (NMDeviceBridgeClass *klass)
object_class->constructed = constructed;
object_class->get_property = get_property;
object_class->set_property = set_property;
object_class->dispose = dispose;
parent_class->get_generic_capabilities = get_generic_capabilities;
parent_class->update_hw_address = update_hw_address;

View File

@@ -149,6 +149,12 @@ typedef struct {
guint id;
} QueuedState;
typedef struct {
NMDevice *slave;
gboolean enslaved;
guint watch_id;
} SlaveInfo;
typedef struct {
gboolean disposed;
gboolean initialized;
@@ -181,8 +187,6 @@ typedef struct {
gpointer act_source_func;
guint act_source6_id;
gpointer act_source6_func;
guint act_dep_result_id;
guint act_dep_timeout_id;
gulong secrets_updated_id;
gulong secrets_failed_id;
@@ -241,8 +245,12 @@ typedef struct {
/* allow autoconnect feature */
gboolean autoconnect;
/* master interface for bridge, bond, vlan, etc */
NMDevice * master;
/* master interface for bridge/bond slave */
NMDevice * master;
gboolean enslaved;
/* list of SlaveInfo for bond/bridge master */
GSList * slaves;
NMConnectionProvider *con_provider;
@@ -281,6 +289,8 @@ static void cp_connections_loaded (NMConnectionProvider *cp, NMConnection *conne
static void cp_connection_removed (NMConnectionProvider *cp, NMConnection *connection, gpointer user_data);
static void cp_connection_updated (NMConnectionProvider *cp, NMConnection *connection, gpointer user_data);
static const char *state_to_string (NMDeviceState state);
static void
nm_device_init (NMDevice *self)
{
@@ -762,6 +772,30 @@ nm_device_get_connection_provider (NMDevice *device)
return NM_DEVICE_GET_PRIVATE (device)->con_provider;
}
static SlaveInfo *
find_slave_info (NMDevice *self, NMDevice *slave)
{
NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
SlaveInfo *info;
GSList *iter;
for (iter = priv->slaves; iter; iter = g_slist_next (iter)) {
info = iter->data;
if (info->slave == slave)
return info;
}
return NULL;
}
static void
free_slave_info (SlaveInfo *info)
{
g_signal_handler_disconnect (info->slave, info->watch_id);
g_clear_object (&info->slave);
memset (info, 0, sizeof (*info));
g_free (info);
}
/**
* nm_device_enslave_slave:
* @dev: the master device
@@ -774,22 +808,35 @@ nm_device_get_connection_provider (NMDevice *device)
* Returns: %TRUE on success, %FALSE on failure or if this device cannot enslave
* other devices.
*/
gboolean
static gboolean
nm_device_enslave_slave (NMDevice *dev, NMDevice *slave, NMConnection *connection)
{
SlaveInfo *info;
gboolean success = FALSE;
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 (NM_DEVICE_GET_CLASS (dev)->enslave_slave)
return NM_DEVICE_GET_CLASS (dev)->enslave_slave (dev, slave, connection);
return 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);
if (success) {
info->enslaved = TRUE;
nm_device_slave_notify_enslaved (info->slave, TRUE, FALSE);
}
return success;
}
/**
* nm_device_release_slave:
* nm_device_release_one_slave:
* @dev: the master device
* @slave: the slave device to release
* @failed: %TRUE if the release was unexpected, ie the master failed
*
* If @dev is capable of enslaving other devices (ie it's a bridge, bond, etc)
* then this function releases the previously enslaved @slave.
@@ -797,15 +844,197 @@ nm_device_enslave_slave (NMDevice *dev, NMDevice *slave, NMConnection *connectio
* Returns: %TRUE on success, %FALSE on failure, if this device cannot enslave
* other devices, or if @slave was never enslaved.
*/
gboolean
nm_device_release_slave (NMDevice *dev, NMDevice *slave)
static gboolean
nm_device_release_one_slave (NMDevice *dev, NMDevice *slave, gboolean failed)
{
NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (dev);
SlaveInfo *info;
gboolean success = FALSE;
g_return_val_if_fail (slave != NULL, FALSE);
g_return_val_if_fail (NM_DEVICE_GET_CLASS (dev)->release_slave != NULL, FALSE);
info = find_slave_info (dev, slave);
if (!info)
return FALSE;
if (info->enslaved) {
success = NM_DEVICE_GET_CLASS (dev)->release_slave (dev, slave);
g_warn_if_fail (success);
}
nm_device_slave_notify_enslaved (info->slave, FALSE, failed);
priv->slaves = g_slist_remove (priv->slaves, info);
free_slave_info (info);
return success;
}
static void
slave_state_changed (NMDevice *slave,
NMDeviceState slave_new_state,
NMDeviceState slave_old_state,
NMDeviceStateReason reason,
NMDevice *self)
{
NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
gboolean release = FALSE;
nm_log_dbg (LOGD_DEVICE, "(%s): slave %s state change %d (%s) -> %d (%s)",
nm_device_get_iface (self),
nm_device_get_iface (slave),
slave_old_state,
state_to_string (slave_old_state),
slave_new_state,
state_to_string (slave_new_state));
g_assert (priv->state > NM_DEVICE_STATE_DISCONNECTED);
g_assert (priv->state <= NM_DEVICE_STATE_ACTIVATED);
/* Don't try to enslave slaves until the master is ready */
if (priv->state < NM_DEVICE_STATE_CONFIG)
return;
if (slave_new_state == NM_DEVICE_STATE_IP_CONFIG)
nm_device_enslave_slave (self, slave, nm_device_get_connection (slave));
else if (slave_new_state > NM_DEVICE_STATE_ACTIVATED)
release = TRUE;
else if ( slave_new_state <= NM_DEVICE_STATE_DISCONNECTED
&& slave_old_state > NM_DEVICE_STATE_DISCONNECTED) {
/* Catch failures due to unavailable or unmanaged */
release = TRUE;
}
if (release) {
nm_device_release_one_slave (self, slave, FALSE);
if (priv->slaves == NULL) {
/* FIXME: all slaves gone; do something? */
}
}
}
/**
* nm_device_master_add_slave:
* @dev: the master device
* @slave: the slave device to enslave
*
* If @dev is capable of enslaving other devices (ie it's a bridge, bond, etc)
* then this function adds @slave to the slave list for later enslavement.
*
* Returns: %TRUE on success, %FALSE on failure
*/
gboolean
nm_device_master_add_slave (NMDevice *dev, NMDevice *slave)
{
NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (dev);
SlaveInfo *info;
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 (NM_DEVICE_GET_CLASS (dev)->release_slave)
return NM_DEVICE_GET_CLASS (dev)->release_slave (dev, slave);
return FALSE;
if (!find_slave_info (dev, slave)) {
info = g_malloc0 (sizeof (SlaveInfo));
info->slave = g_object_ref (slave);
info->watch_id = g_signal_connect (slave, "state-changed",
G_CALLBACK (slave_state_changed), dev);
priv->slaves = g_slist_prepend (priv->slaves, info);
}
return TRUE;
}
/**
* nm_device_master_get_slaves:
* @dev: the master device
*
* Returns: any slaves of which @device is the master. Caller owns returned list.
*/
GSList *
nm_device_master_get_slaves (NMDevice *dev)
{
NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (dev);
GSList *slaves = NULL, *iter;
for (iter = priv->slaves; iter; iter = g_slist_next (iter))
slaves = g_slist_prepend (slaves, ((SlaveInfo *) iter->data)->slave);
return slaves;
}
/* release all slaves */
static void
nm_device_master_release_slaves (NMDevice *self, gboolean failed)
{
NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
GSList *iter;
for (iter = priv->slaves; iter; iter = g_slist_next (iter))
nm_device_release_one_slave (self, ((SlaveInfo *) iter->data)->slave, failed);
g_slist_free (priv->slaves);
priv->slaves = NULL;
}
/**
* nm_device_slave_notify_enslaved:
* @dev: the slave device
* @enslaved: %TRUE if the device is now enslaved, %FALSE if released
* @master_failed: if released, indicates whether the release was unexpected,
* ie the master device failed.
*
* Notifies a slave that it has been enslaved or released. If released, provides
* information on whether the release was expected or not, and thus whether the
* slave should fail it's activation or gracefully deactivate.
*/
void
nm_device_slave_notify_enslaved (NMDevice *dev,
gboolean enslaved,
gboolean master_failed)
{
NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (dev);
NMConnection *connection = nm_device_get_connection (dev);
g_assert (priv->master);
if (enslaved) {
g_warn_if_fail (priv->enslaved == FALSE);
g_warn_if_fail (priv->state == NM_DEVICE_STATE_IP_CONFIG);
nm_log_info (LOGD_DEVICE,
"Activation (%s) connection '%s' enslaved, continuing activation",
nm_device_get_iface (dev),
nm_connection_get_id (connection));
/* Now that we're enslaved, proceed with activation. Remember, slaves
* don't have any IP configuration, so they skip directly to SECONDARIES.
*/
priv->enslaved = TRUE;
priv->ip4_state = IP_DONE;
priv->ip6_state = IP_DONE;
nm_device_queue_state (dev, NM_DEVICE_STATE_SECONDARIES, NM_DEVICE_STATE_REASON_NONE);
} else {
NMDeviceState new_state = NM_DEVICE_STATE_DISCONNECTED;
NMDeviceStateReason reason = NM_DEVICE_STATE_REASON_NONE;
if (master_failed) {
new_state = NM_DEVICE_STATE_FAILED;
reason = NM_DEVICE_STATE_REASON_DEPENDENCY_FAILED;
nm_log_warn (LOGD_DEVICE,
"Activation (%s) connection '%s' master failed",
nm_device_get_iface (dev),
nm_connection_get_id (connection));
} else {
nm_log_dbg (LOGD_DEVICE,
"Activation (%s) connection '%s' master deactivated",
nm_device_get_iface (dev),
nm_connection_get_id (connection));
}
nm_device_queue_state (dev, new_state, reason);
}
}
/*
@@ -1097,26 +1326,7 @@ ip6_method_matches (NMConnection *connection, const char *match)
static NMActStageReturn
act_stage1_prepare (NMDevice *self, NMDeviceStateReason *reason)
{
NMActRequest *req;
NMActiveConnection *master_ac;
NMDevice *master;
NMActStageReturn ret = NM_ACT_STAGE_RETURN_SUCCESS;
req = nm_device_get_act_request (self);
g_assert (req);
/* If the interface is going to be a slave, let the master enslave it here */
master_ac = nm_act_request_get_dependency (req);
if (master_ac && NM_IS_ACT_REQUEST (master_ac)) {
/* FIXME: handle VPNs here too */
master = NM_DEVICE (nm_act_request_get_device (NM_ACT_REQUEST (master_ac)));
g_assert (master);
if (!nm_device_enslave_slave (master, self, nm_act_request_get_connection (req)))
ret = NM_ACT_STAGE_RETURN_FAILURE;
}
return ret;
return NM_ACT_STAGE_RETURN_SUCCESS;
}
/*
@@ -1137,11 +1347,6 @@ nm_device_activate_stage1_device_prepare (gpointer user_data)
/* Clear the activation source ID now that this stage has run */
activation_source_clear (self, FALSE, 0);
if (priv->act_dep_timeout_id) {
g_source_remove (priv->act_dep_timeout_id);
priv->act_dep_timeout_id = 0;
}
priv->ip4_state = priv->ip6_state = IP_NONE;
iface = nm_device_get_iface (self);
@@ -1190,6 +1395,17 @@ nm_device_activate_schedule_stage1_device_prepare (NMDevice *self)
static NMActStageReturn
act_stage2_config (NMDevice *dev, NMDeviceStateReason *reason)
{
NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (dev);
GSList *iter;
/* If we have slaves that aren't yet enslaved, do that now */
for (iter = priv->slaves; iter; iter = g_slist_next (iter)) {
SlaveInfo *info = iter->data;
if (nm_device_get_state (info->slave) == NM_DEVICE_STATE_IP_CONFIG)
nm_device_enslave_slave (dev, info->slave, nm_device_get_connection (info->slave));
}
/* Nothing to do */
return NM_ACT_STAGE_RETURN_SUCCESS;
}
@@ -2451,6 +2667,7 @@ nm_device_activate_stage3_ip_config_start (gpointer user_data)
NMIP4Config *ip4_config = NULL;
NMIP6Config *ip6_config = NULL;
int ifindex;
NMDevice *master;
/* Clear the activation source ID now that this stage has run */
activation_source_clear (self, FALSE, 0);
@@ -2466,6 +2683,26 @@ nm_device_activate_stage3_ip_config_start (gpointer user_data)
priv->ip4_state = priv->ip6_state = IP_CONF;
/* If the device is a slave, then we don't do any IP configuration but we
* use the IP config stage to indicate to the master we're ready for
* enslavement. Either the master has already enslaved us, in which case
* our state transition to SECONDARIES is already queued courtesy of
* nm_device_slave_notify_enslaved(), or the master is still activating,
* in which case we postpone activation here until the master enslaves us,
* which calls nm_device_slave_notify_enslaved().
*/
master = (NMDevice *) nm_act_request_get_master (priv->act_request);
if (master) {
if (priv->enslaved == FALSE) {
nm_log_info (LOGD_DEVICE, "Activation (%s) connection '%s' waiting on master '%s'",
nm_device_get_iface (self),
nm_connection_get_id (nm_device_get_connection (self)),
nm_device_get_iface (master));
}
goto out;
}
/* IPv4 */
ret = NM_DEVICE_GET_CLASS (self)->act_stage3_ip4_config_start (self, &ip4_config, &reason);
if (ret == NM_ACT_STAGE_RETURN_SUCCESS) {
g_assert (ip4_config);
@@ -2480,6 +2717,7 @@ nm_device_activate_stage3_ip_config_start (gpointer user_data)
} else
g_assert (ret == NM_ACT_STAGE_RETURN_POSTPONE);
/* IPv6 */
ret = NM_DEVICE_GET_CLASS (self)->act_stage3_ip6_config_start (self, &ip6_config, &reason);
if (ret == NM_ACT_STAGE_RETURN_SUCCESS) {
g_assert (ip6_config);
@@ -2496,16 +2734,6 @@ nm_device_activate_stage3_ip_config_start (gpointer user_data)
out:
nm_log_info (LOGD_DEVICE, "Activation (%s) Stage 3 of 5 (IP Configure Start) complete.", iface);
/* Handle interfaces (bond slaves, etc) that won't have any IP config; they
* need to move to SECONDARIES.
*/
if (priv->ip4_state == IP_DONE && priv->ip6_state == IP_DONE) {
/* FIXME: call layer2 stuff to set MTU? */
nm_device_state_changed (self, NM_DEVICE_STATE_SECONDARIES, NM_DEVICE_STATE_REASON_NONE);
}
return FALSE;
}
@@ -3250,15 +3478,6 @@ nm_device_deactivate (NMDevice *self, NMDeviceStateReason reason)
activation_source_clear (self, TRUE, AF_INET);
activation_source_clear (self, TRUE, AF_INET6);
if (priv->act_dep_result_id) {
g_source_remove (priv->act_dep_result_id);
priv->act_dep_result_id = 0;
}
if (priv->act_dep_timeout_id) {
g_source_remove (priv->act_dep_timeout_id);
priv->act_dep_timeout_id = 0;
}
/* Clear any queued transitions */
nm_device_queued_state_clear (self);
@@ -3284,6 +3503,13 @@ nm_device_deactivate (NMDevice *self, NMDeviceStateReason reason)
if (NM_DEVICE_GET_CLASS (self)->deactivate)
NM_DEVICE_GET_CLASS (self)->deactivate (self);
/* master: release slaves */
g_clear_object (&priv->master);
nm_device_master_release_slaves (self, FALSE);
/* slave: mark no longer enslaved */
priv->enslaved = FALSE;
/* Tear down an existing activation request */
clear_act_request (self);
@@ -3353,61 +3579,6 @@ impl_device_disconnect (NMDevice *device, DBusGMethodInvocation *context)
NULL);
}
static gboolean
act_dep_timeout_cb (gpointer user_data)
{
NMDevice *self = NM_DEVICE (user_data);
NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
NMConnection *connection;
connection = nm_active_connection_get_connection (NM_ACTIVE_CONNECTION (priv->act_request));
nm_log_warn (LOGD_DEVICE,
"Activation (%s) connection '%s' dependency timed out",
nm_device_get_iface (self),
nm_connection_get_id (connection));
nm_device_queue_state (self, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_DEPENDENCY_FAILED);
return FALSE;
}
static void
act_dep_result_cb (NMActRequest *req,
NMActRequestDependencyResult result,
NMDevice *self)
{
NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
NMConnection *connection;
connection = nm_active_connection_get_connection (NM_ACTIVE_CONNECTION (priv->act_request));
switch (result) {
case NM_ACT_REQUEST_DEP_RESULT_FAILED:
g_source_remove (priv->act_dep_result_id);
priv->act_dep_result_id = 0;
nm_log_warn (LOGD_DEVICE,
"Activation (%s) connection '%s' dependency failed",
nm_device_get_iface (self),
nm_connection_get_id (connection));
nm_device_queue_state (self, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_DEPENDENCY_FAILED);
break;
case NM_ACT_REQUEST_DEP_RESULT_READY:
g_warn_if_fail (priv->state == NM_DEVICE_STATE_PREPARE);
if (priv->state == NM_DEVICE_STATE_PREPARE) {
nm_log_info (LOGD_DEVICE,
"Activation (%s) connection '%s' dependency ready, continuing activation",
nm_device_get_iface (self),
nm_connection_get_id (connection));
nm_device_activate_schedule_stage1_device_prepare (self);
}
break;
case NM_ACT_REQUEST_DEP_RESULT_WAIT:
default:
g_assert_not_reached ();
break;
}
}
void
nm_device_activate (NMDevice *self, NMActRequest *req)
{
@@ -3441,8 +3612,7 @@ nm_device_activate (NMDevice *self, NMActRequest *req)
nm_device_state_changed (self, NM_DEVICE_STATE_IP_CONFIG, NM_DEVICE_STATE_REASON_NONE);
nm_device_activate_schedule_stage3_ip_config_start (self);
} else {
NMActiveConnection *dep_ac;
NMConnection *dep_con;
NMDevice *master;
/* HACK: update the state a bit early to avoid a race between the
* scheduled stage1 handler and nm_policy_device_change_check() thinking
@@ -3452,33 +3622,17 @@ nm_device_activate (NMDevice *self, NMActRequest *req)
nm_device_state_changed (self, NM_DEVICE_STATE_PREPARE, NM_DEVICE_STATE_REASON_NONE);
/* Handle any dependencies this connection might have */
switch (nm_act_request_get_dependency_result (priv->act_request)) {
case NM_ACT_REQUEST_DEP_RESULT_FAILED:
nm_device_queue_state (self, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_DEPENDENCY_FAILED);
break;
case NM_ACT_REQUEST_DEP_RESULT_WAIT:
dep_ac = nm_act_request_get_dependency (priv->act_request);
g_assert (dep_ac);
dep_con = nm_active_connection_get_connection (dep_ac);
g_assert (dep_con);
nm_log_info (LOGD_DEVICE, "Activation (%s) connection '%s' waiting on dependency '%s'",
nm_device_get_iface (self),
nm_connection_get_id (connection),
nm_connection_get_id (dep_con));
master = (NMDevice *) nm_act_request_get_master (req);
if (master) {
/* Master should at least already be activating */
g_assert (nm_device_get_state (master) > NM_DEVICE_STATE_DISCONNECTED);
priv->act_dep_result_id = g_signal_connect (priv->act_request,
NM_ACT_REQUEST_DEPENDENCY_RESULT,
G_CALLBACK (act_dep_result_cb),
self);
priv->act_dep_timeout_id = g_timeout_add_seconds (60, act_dep_timeout_cb, self);
break;
default:
g_warn_if_reached ();
/* fall through */
case NM_ACT_REQUEST_DEP_RESULT_READY:
nm_device_activate_schedule_stage1_device_prepare (self);
break;
g_assert (priv->master == NULL);
priv->master = g_object_ref (master);
nm_device_master_add_slave (master, self);
}
nm_device_activate_schedule_stage1_device_prepare (self);
}
}
@@ -3821,6 +3975,8 @@ dispose (GObject *object)
addrconf6_cleanup (self);
dnsmasq_cleanup (self);
g_warn_if_fail (priv->slaves == NULL);
/* Take the device itself down and clear its IPv4 configuration */
if (priv->managed && take_down) {
NMDeviceStateReason ignored = NM_DEVICE_STATE_REASON_NONE;
@@ -4668,6 +4824,9 @@ nm_device_state_changed (NMDevice *device,
nm_device_get_iface (device),
nm_connection_get_id (connection));
/* Notify any slaves of the unexpected failure */
nm_device_master_release_slaves (device, TRUE);
/* If the connection doesn't yet have a timestamp, set it to zero so that
* we can distinguish between connections we've tried to activate and have
* failed (zero timestamp), connections that succeeded (non-zero timestamp),

View File

@@ -217,8 +217,14 @@ NMDHCP6Config * nm_device_get_dhcp6_config (NMDevice *dev);
NMIP4Config * nm_device_get_ip4_config (NMDevice *dev);
NMIP6Config * nm_device_get_ip6_config (NMDevice *dev);
gboolean nm_device_enslave_slave (NMDevice *dev, NMDevice *slave, NMConnection *connection);
gboolean nm_device_release_slave (NMDevice *dev, NMDevice *slave);
/* Master */
gboolean nm_device_master_add_slave (NMDevice *dev, NMDevice *slave);
GSList * nm_device_master_get_slaves (NMDevice *dev);
/* Slave */
void nm_device_slave_notify_enslaved (NMDevice *dev,
gboolean enslaved,
gboolean master_failed);
NMActRequest * nm_device_get_act_request (NMDevice *dev);
NMConnection * nm_device_get_connection (NMDevice *dev);

View File

@@ -2289,6 +2289,7 @@ internal_activate_device (NMManager *manager,
GError **error)
{
NMActRequest *req;
NMDevice *master_device = NULL;
g_return_val_if_fail (NM_IS_MANAGER (manager), NULL);
g_return_val_if_fail (NM_IS_DEVICE (device), NULL);
@@ -2307,6 +2308,9 @@ internal_activate_device (NMManager *manager,
NM_DEVICE_STATE_REASON_NONE);
}
if (master)
master_device = (NMDevice *) nm_act_request_get_device (NM_ACT_REQUEST (master));
req = nm_act_request_new (connection,
specific_object,
user_requested,
@@ -2314,7 +2318,7 @@ internal_activate_device (NMManager *manager,
dbus_sender,
assumed,
(gpointer) device,
master);
(gpointer) master_device);
nm_device_activate (device, req);
g_object_unref (req);