Commit db94e37e authored by Dan Williams's avatar Dan Williams

core: wait for dependencies before continuing with device activation

Before jumping to activation stage 1, make sure dependencies are activated.
parent 0b8ff52f
......@@ -486,6 +486,9 @@ typedef enum {
/* Infiniband device does not support connected mode */
NM_DEVICE_STATE_REASON_INFINIBAND_MODE = 49,
/* A dependency of the connection failed */
NM_DEVICE_STATE_REASON_DEPENDENCY_FAILED = 50,
/* Unused */
NM_DEVICE_STATE_REASON_LAST = 0xFFFF
} NMDeviceStateReason;
......
......@@ -521,6 +521,11 @@
Infiniband device does not support connected mode.
</tp:docstring>
</tp:enumvalue>
<tp:enumvalue suffix="DEPENDENCY_FAILED" value="50">
<tp:docstring>
A dependency of the connection failed.
</tp:docstring>
</tp:enumvalue>
</tp:enum>
<tp:struct name="NM_DEVICE_STATE_REASON_STRUCT">
......
......@@ -208,6 +208,12 @@ nm_act_request_get_assumed (NMActRequest *req)
return NM_ACT_REQUEST_GET_PRIVATE (req)->assumed;
}
NMActiveConnection *
nm_act_request_get_dependency (NMActRequest *req)
{
return NM_ACT_REQUEST_GET_PRIVATE (req)->dep;
}
static NMActRequestDependencyResult
ac_state_to_dep_result (NMActiveConnection *ac)
{
......
......@@ -82,6 +82,8 @@ 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);
/* Secrets handling */
......
......@@ -164,6 +164,8 @@ 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;
......@@ -997,6 +999,11 @@ 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);
......@@ -3102,6 +3109,15 @@ 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 */
queued_state_clear (self);
......@@ -3175,6 +3191,61 @@ impl_device_disconnect (NMDevice *device, DBusGMethodInvocation *context)
g_signal_emit (device, signals[DISCONNECT_REQUEST], 0, context);
}
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;
}
}
gboolean
nm_device_activate (NMDevice *self, NMActRequest *req, GError **error)
{
......@@ -3213,13 +3284,44 @@ nm_device_activate (NMDevice *self, NMActRequest *req, GError **error)
priv->act_request = g_object_ref (req);
if (!nm_act_request_get_assumed (req)) {
NMActiveConnection *dep_ac;
NMConnection *dep_con;
/* HACK: update the state a bit early to avoid a race between the
* scheduled stage1 handler and nm_policy_device_change_check() thinking
* that the activation request isn't deferred because the deferred bit
* gets cleared a bit too early, when the connection becomes valid.
*/
nm_device_state_changed (self, NM_DEVICE_STATE_PREPARE, NM_DEVICE_STATE_REASON_NONE);
nm_device_activate_schedule_stage1_device_prepare (self);
/* 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));
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;
}
} else {
/* If it's an assumed connection, let the device subclass short-circuit
* the normal connection process and just copy its IP configs from the
......@@ -4184,6 +4286,8 @@ reason_to_string (NMDeviceStateReason reason)
return "gsm-sim-wrong";
case NM_DEVICE_STATE_REASON_INFINIBAND_MODE:
return "infiniband-mode";
case NM_DEVICE_STATE_REASON_DEPENDENCY_FAILED:
return "dependency-failed";
default:
break;
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment