about summary refs log tree commit diff
path: root/src/core/nm-act-request.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/core/nm-act-request.c')
-rw-r--r--src/core/nm-act-request.c542
1 files changed, 542 insertions, 0 deletions
diff --git a/src/core/nm-act-request.c b/src/core/nm-act-request.c
new file mode 100644
index 00000000..defe3e8c
--- /dev/null
+++ b/src/core/nm-act-request.c
@@ -0,0 +1,542 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (C) 2005 - 2012 Red Hat, Inc.
+ * Copyright (C) 2007 - 2008 Novell, Inc.
+ */
+
+#include "src/core/nm-default-daemon.h"
+
+#include "nm-act-request.h"
+
+#include <stdlib.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include "c-list/src/c-list.h"
+
+#include "nm-setting-wireless-security.h"
+#include "nm-setting-8021x.h"
+#include "devices/nm-device.h"
+#include "nm-active-connection.h"
+#include "settings/nm-settings-connection.h"
+#include "nm-libnm-core-intern/nm-auth-subject.h"
+
+typedef struct {
+    CList              call_ids_lst_head;
+    NMUtilsShareRules *share_rules;
+} NMActRequestPrivate;
+
+struct _NMActRequest {
+    NMActiveConnection  parent;
+    NMActRequestPrivate _priv;
+};
+
+typedef struct {
+    NMActiveConnectionClass parent;
+} NMActRequestClass;
+
+enum {
+    PROP_0,
+    PROP_IP4_CONFIG,
+    PROP_DHCP4_CONFIG,
+    PROP_IP6_CONFIG,
+    PROP_DHCP6_CONFIG,
+
+    LAST_PROP
+};
+
+G_DEFINE_TYPE(NMActRequest, nm_act_request, NM_TYPE_ACTIVE_CONNECTION)
+
+#define NM_ACT_REQUEST_GET_PRIVATE(self) _NM_GET_PRIVATE(self, NMActRequest, NM_IS_ACT_REQUEST)
+
+/*****************************************************************************/
+
+NMSettingsConnection *
+nm_act_request_get_settings_connection(NMActRequest *req)
+{
+    g_return_val_if_fail(NM_IS_ACT_REQUEST(req), NULL);
+
+    return nm_active_connection_get_settings_connection(NM_ACTIVE_CONNECTION(req));
+}
+
+NMConnection *
+nm_act_request_get_applied_connection(NMActRequest *req)
+{
+    g_return_val_if_fail(NM_IS_ACT_REQUEST(req), NULL);
+
+    return nm_active_connection_get_applied_connection(NM_ACTIVE_CONNECTION(req));
+}
+
+/*****************************************************************************/
+
+struct _NMActRequestGetSecretsCallId {
+    CList                       call_ids_lst;
+    NMActRequest *              self;
+    NMActRequestSecretsFunc     callback;
+    gpointer                    callback_data;
+    NMSettingsConnectionCallId *call_id;
+    bool                        has_ref;
+};
+
+static void
+_get_secrets_call_id_free(NMActRequestGetSecretsCallId *call_id)
+{
+    nm_assert(call_id);
+    nm_assert(!c_list_is_linked(&call_id->call_ids_lst));
+
+    if (call_id->has_ref)
+        g_object_unref(call_id->self);
+    g_slice_free(NMActRequestGetSecretsCallId, call_id);
+}
+
+static void
+get_secrets_cb(NMSettingsConnection *      connection,
+               NMSettingsConnectionCallId *call_id_s,
+               const char *                agent_username,
+               const char *                setting_name,
+               GError *                    error,
+               gpointer                    user_data)
+{
+    NMActRequestGetSecretsCallId *call_id = user_data;
+    NMActRequestPrivate *         priv;
+
+    g_return_if_fail(call_id && call_id->call_id == call_id_s);
+    g_return_if_fail(NM_IS_ACT_REQUEST(call_id->self));
+
+    if (g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+        return;
+
+    priv = NM_ACT_REQUEST_GET_PRIVATE(call_id->self);
+
+    nm_assert(c_list_contains(&priv->call_ids_lst_head, &call_id->call_ids_lst));
+
+    c_list_unlink(&call_id->call_ids_lst);
+
+    if (call_id->callback)
+        call_id->callback(call_id->self, call_id, connection, error, call_id->callback_data);
+
+    _get_secrets_call_id_free(call_id);
+}
+
+/**
+ * nm_act_request_get_secrets:
+ * @self:
+ * @ref_self: if %TRUE, the pending call take a reference on @self.
+ *   It also allows you to omit the @self argument in nm_act_request_cancel_secrets().
+ * @setting_name:
+ * @flags:
+ * @hint:
+ * @callback:
+ * @callback_data:
+ *
+ * Asynchronously starts the request for secrets. This function cannot
+ * fail.
+ *
+ * The return call-id can be used to cancel the request. You are
+ * only allowed to cancel a still pending operation (once).
+ * The callback will always be invoked once, even for canceling
+ * or disposing of NMActRequest.
+ *
+ * Returns: a call-id.
+ */
+NMActRequestGetSecretsCallId *
+nm_act_request_get_secrets(NMActRequest *               self,
+                           gboolean                     ref_self,
+                           const char *                 setting_name,
+                           NMSecretAgentGetSecretsFlags flags,
+                           const char *const *          hints,
+                           NMActRequestSecretsFunc      callback,
+                           gpointer                     callback_data)
+{
+    NMActRequestPrivate *         priv;
+    NMActRequestGetSecretsCallId *call_id;
+    NMSettingsConnectionCallId *  call_id_s;
+    NMSettingsConnection *        settings_connection;
+    NMConnection *                applied_connection;
+
+    g_return_val_if_fail(NM_IS_ACT_REQUEST(self), NULL);
+
+    priv = NM_ACT_REQUEST_GET_PRIVATE(self);
+
+    settings_connection = nm_act_request_get_settings_connection(self);
+    applied_connection  = nm_act_request_get_applied_connection(self);
+
+    call_id                = g_slice_new0(NMActRequestGetSecretsCallId);
+    call_id->has_ref       = ref_self;
+    call_id->self          = ref_self ? g_object_ref(self) : self;
+    call_id->callback      = callback;
+    call_id->callback_data = callback_data;
+    c_list_link_tail(&priv->call_ids_lst_head, &call_id->call_ids_lst);
+
+    if (nm_active_connection_get_user_requested(NM_ACTIVE_CONNECTION(self)))
+        flags |= NM_SECRET_AGENT_GET_SECRETS_FLAG_USER_REQUESTED;
+
+    call_id_s = nm_settings_connection_get_secrets(
+        settings_connection,
+        applied_connection,
+        nm_active_connection_get_subject(NM_ACTIVE_CONNECTION(self)),
+        setting_name,
+        flags,
+        hints,
+        get_secrets_cb,
+        call_id);
+    call_id->call_id = call_id_s;
+    g_return_val_if_fail(call_id_s, NULL);
+    return call_id;
+}
+
+static void
+_do_cancel_secrets(NMActRequest *self, NMActRequestGetSecretsCallId *call_id, gboolean is_disposing)
+{
+    NMActRequestPrivate *priv = NM_ACT_REQUEST_GET_PRIVATE(self);
+
+    nm_assert(call_id && call_id->self == self);
+    nm_assert(c_list_contains(&priv->call_ids_lst_head, &call_id->call_ids_lst));
+
+    c_list_unlink(&call_id->call_ids_lst);
+
+    nm_settings_connection_cancel_secrets(nm_act_request_get_settings_connection(self),
+                                          call_id->call_id);
+
+    if (call_id->callback) {
+        gs_free_error GError *error = NULL;
+
+        nm_utils_error_set_cancelled(&error, is_disposing, "NMActRequest");
+        call_id->callback(self, call_id, NULL, error, call_id->callback_data);
+    }
+
+    _get_secrets_call_id_free(call_id);
+}
+
+/**
+ * nm_act_request_cancel_secrets:
+ * @self: The #NMActRequest. Note that this argument can be %NULL if, and only if
+ *   the call_id was created with @take_ref.
+ * @call_id:
+ *
+ * You are only allowed to cancel the call once, and only before the callback
+ * is already invoked. Note that cancelling causes the callback to be invoked
+ * synchronously.
+ */
+void
+nm_act_request_cancel_secrets(NMActRequest *self, NMActRequestGetSecretsCallId *call_id)
+{
+    g_return_if_fail(call_id);
+
+    if (self) {
+        g_return_if_fail(NM_IS_ACT_REQUEST(self));
+        g_return_if_fail(self == call_id->self);
+    } else {
+        g_return_if_fail(call_id->has_ref);
+        g_return_if_fail(NM_IS_ACT_REQUEST(call_id->self));
+        self = call_id->self;
+    }
+
+    if (!c_list_is_linked(&call_id->call_ids_lst))
+        g_return_if_reached();
+
+    _do_cancel_secrets(self, call_id, FALSE);
+}
+
+void
+nm_act_request_clear_secrets(NMActRequest *self)
+{
+    g_return_if_fail(NM_IS_ACT_REQUEST(self));
+
+    nm_active_connection_clear_secrets((NMActiveConnection *) self);
+}
+
+/*****************************************************************************/
+
+NMUtilsShareRules *
+nm_act_request_get_shared(NMActRequest *req)
+{
+    g_return_val_if_fail(NM_IS_ACT_REQUEST(req), FALSE);
+
+    return NM_ACT_REQUEST_GET_PRIVATE(req)->share_rules;
+}
+
+void
+nm_act_request_set_shared(NMActRequest *req, NMUtilsShareRules *rules)
+{
+    NMActRequestPrivate *priv = NM_ACT_REQUEST_GET_PRIVATE(req);
+
+    g_return_if_fail(NM_IS_ACT_REQUEST(req));
+
+    if (priv->share_rules == rules)
+        return;
+
+    if (priv->share_rules) {
+        nm_utils_share_rules_apply(priv->share_rules, FALSE);
+        priv->share_rules = NULL;
+    }
+    if (rules) {
+        priv->share_rules = rules;
+        nm_utils_share_rules_apply(priv->share_rules, TRUE);
+    }
+}
+
+/*****************************************************************************/
+
+static void
+device_notify(GObject *object, GParamSpec *pspec, gpointer self)
+{
+    g_object_notify(self, pspec->name);
+}
+
+static void
+device_state_changed(NMActiveConnection *active,
+                     NMDevice *          device,
+                     NMDeviceState       new_state,
+                     NMDeviceState       old_state)
+{
+    NMActiveConnectionState       cur_ac_state    = nm_active_connection_get_state(active);
+    NMActiveConnectionState       ac_state        = NM_ACTIVE_CONNECTION_STATE_UNKNOWN;
+    NMActiveConnectionStateReason ac_state_reason = NM_ACTIVE_CONNECTION_STATE_REASON_UNKNOWN;
+
+    /* Decide which device state changes to handle when this active connection
+     * is not the device's current request.  Two cases here: (a) the AC is
+     * pending and not yet active, and (b) the AC was active but the device is
+     * entering DISCONNECTED state (which clears the device's current AC before
+     * emitting the state change signal).
+     */
+    if (NM_ACTIVE_CONNECTION(nm_device_get_act_request(device)) != active) {
+        /* Some other request is activating; this one must be pending */
+        if (new_state >= NM_DEVICE_STATE_PREPARE)
+            return;
+        else if (new_state == NM_DEVICE_STATE_DISCONNECTED) {
+            /* This request hasn't started activating yet; the device is
+             * disconnecting and cleaning up a previous activation request.
+             */
+            if (cur_ac_state < NM_ACTIVE_CONNECTION_STATE_ACTIVATING)
+                return;
+
+            /* Catch device disconnections after this request has been active */
+        }
+
+        /* All states < DISCONNECTED are fatal and handled */
+    }
+
+    /* Set NMActiveConnection state based on the device's state */
+    switch (new_state) {
+    case NM_DEVICE_STATE_PREPARE:
+    case NM_DEVICE_STATE_CONFIG:
+    case NM_DEVICE_STATE_NEED_AUTH:
+    case NM_DEVICE_STATE_IP_CONFIG:
+    case NM_DEVICE_STATE_IP_CHECK:
+    case NM_DEVICE_STATE_SECONDARIES:
+        ac_state = NM_ACTIVE_CONNECTION_STATE_ACTIVATING;
+        break;
+    case NM_DEVICE_STATE_ACTIVATED:
+        ac_state = NM_ACTIVE_CONNECTION_STATE_ACTIVATED;
+
+        g_signal_connect(device,
+                         "notify::" NM_DEVICE_IP4_CONFIG,
+                         G_CALLBACK(device_notify),
+                         active);
+        g_signal_connect(device,
+                         "notify::" NM_DEVICE_DHCP4_CONFIG,
+                         G_CALLBACK(device_notify),
+                         active);
+        g_signal_connect(device,
+                         "notify::" NM_DEVICE_IP6_CONFIG,
+                         G_CALLBACK(device_notify),
+                         active);
+        g_signal_connect(device,
+                         "notify::" NM_DEVICE_DHCP6_CONFIG,
+                         G_CALLBACK(device_notify),
+                         active);
+        break;
+    case NM_DEVICE_STATE_DEACTIVATING:
+        ac_state = NM_ACTIVE_CONNECTION_STATE_DEACTIVATING;
+        break;
+    case NM_DEVICE_STATE_FAILED:
+    case NM_DEVICE_STATE_DISCONNECTED:
+    case NM_DEVICE_STATE_UNMANAGED:
+    case NM_DEVICE_STATE_UNAVAILABLE:
+        ac_state        = NM_ACTIVE_CONNECTION_STATE_DEACTIVATED;
+        ac_state_reason = NM_ACTIVE_CONNECTION_STATE_REASON_DEVICE_DISCONNECTED;
+
+        g_signal_handlers_disconnect_by_func(device, G_CALLBACK(device_notify), active);
+        break;
+    default:
+        break;
+    }
+
+    if (ac_state == NM_ACTIVE_CONNECTION_STATE_DEACTIVATED
+        || ac_state == NM_ACTIVE_CONNECTION_STATE_UNKNOWN)
+        nm_active_connection_set_default(active, AF_UNSPEC, FALSE);
+
+    nm_active_connection_set_state(active, ac_state, ac_state_reason);
+}
+
+static void
+master_failed(NMActiveConnection *self)
+{
+    NMDevice *    device;
+    NMDeviceState device_state;
+
+    /* If the connection has an active device, fail it */
+    device = nm_active_connection_get_device(self);
+    if (device) {
+        device_state = nm_device_get_state(device);
+        if (nm_device_is_activating(device) || (device_state == NM_DEVICE_STATE_ACTIVATED)) {
+            nm_device_queue_state(device,
+                                  NM_DEVICE_STATE_FAILED,
+                                  NM_DEVICE_STATE_REASON_DEPENDENCY_FAILED);
+            return;
+        }
+    }
+
+    /* If no device, or the device wasn't active, just move to deactivated state */
+    nm_active_connection_set_state(self,
+                                   NM_ACTIVE_CONNECTION_STATE_DEACTIVATED,
+                                   NM_ACTIVE_CONNECTION_STATE_REASON_DEPENDENCY_FAILED);
+}
+
+/*****************************************************************************/
+
+static void
+get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
+{
+    NMActiveConnection *active;
+    NMDevice *          device;
+    char *              name;
+
+    switch (prop_id) {
+    case PROP_IP4_CONFIG:
+        name = NM_DEVICE_IP4_CONFIG;
+        break;
+    case PROP_DHCP4_CONFIG:
+        name = NM_DEVICE_DHCP4_CONFIG;
+        break;
+    case PROP_IP6_CONFIG:
+        name = NM_DEVICE_IP6_CONFIG;
+        break;
+    case PROP_DHCP6_CONFIG:
+        name = NM_DEVICE_DHCP6_CONFIG;
+        break;
+    default:
+        G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
+        return;
+    }
+
+    active = NM_ACTIVE_CONNECTION(object);
+    device = nm_active_connection_get_device(active);
+    if (!device
+        || !NM_IN_SET(nm_active_connection_get_state(active),
+                      NM_ACTIVE_CONNECTION_STATE_ACTIVATED,
+                      NM_ACTIVE_CONNECTION_STATE_DEACTIVATING)) {
+        g_value_set_string(value, NULL);
+        return;
+    }
+
+    g_object_get_property(G_OBJECT(device), name, value);
+}
+
+static void
+nm_act_request_init(NMActRequest *req)
+{
+    NMActRequestPrivate *priv = NM_ACT_REQUEST_GET_PRIVATE(req);
+
+    c_list_init(&priv->call_ids_lst_head);
+}
+
+/**
+ * nm_act_request_new:
+ *
+ * @settings_connection: (allow-none): the connection to activate @device with
+ * @applied_connection: (allow-none): the applied connection
+ * @specific_object: the object path of the specific object (ie, Wi-Fi access point,
+ *    etc) that will be used to activate @connection and @device
+ * @subject: the #NMAuthSubject representing the requestor of the activation
+ * @activation_type: the #NMActivationType
+ * @activation_reason: the reason for activation
+ * @initial_state_flags: the initial state flags.
+ * @device: the device/interface to configure according to @connection
+ *
+ * Creates a new device-based activation request. If an applied connection is
+ * supplied, it shall not be modified by the caller afterwards.
+ *
+ * Returns: the new activation request on success, %NULL on error.
+ */
+NMActRequest *
+nm_act_request_new(NMSettingsConnection * settings_connection,
+                   NMConnection *         applied_connection,
+                   const char *           specific_object,
+                   NMAuthSubject *        subject,
+                   NMActivationType       activation_type,
+                   NMActivationReason     activation_reason,
+                   NMActivationStateFlags initial_state_flags,
+                   NMDevice *             device)
+{
+    g_return_val_if_fail(!settings_connection || NM_IS_SETTINGS_CONNECTION(settings_connection),
+                         NULL);
+    g_return_val_if_fail(NM_IS_DEVICE(device), NULL);
+    g_return_val_if_fail(NM_IS_AUTH_SUBJECT(subject), NULL);
+
+    return g_object_new(NM_TYPE_ACT_REQUEST,
+                        NM_ACTIVE_CONNECTION_INT_APPLIED_CONNECTION,
+                        applied_connection,
+                        NM_ACTIVE_CONNECTION_INT_SETTINGS_CONNECTION,
+                        settings_connection,
+                        NM_ACTIVE_CONNECTION_INT_DEVICE,
+                        device,
+                        NM_ACTIVE_CONNECTION_SPECIFIC_OBJECT,
+                        specific_object,
+                        NM_ACTIVE_CONNECTION_INT_SUBJECT,
+                        subject,
+                        NM_ACTIVE_CONNECTION_INT_ACTIVATION_TYPE,
+                        (int) activation_type,
+                        NM_ACTIVE_CONNECTION_INT_ACTIVATION_REASON,
+                        (int) activation_reason,
+                        NM_ACTIVE_CONNECTION_STATE_FLAGS,
+                        (guint) initial_state_flags,
+                        NULL);
+}
+
+static void
+dispose(GObject *object)
+{
+    NMActRequest *                self = NM_ACT_REQUEST(object);
+    NMActRequestPrivate *         priv = NM_ACT_REQUEST_GET_PRIVATE(self);
+    NMActRequestGetSecretsCallId *call_id, *call_id_safe;
+
+    /* Kill any in-progress secrets requests */
+    c_list_for_each_entry_safe (call_id, call_id_safe, &priv->call_ids_lst_head, call_ids_lst)
+        _do_cancel_secrets(self, call_id, TRUE);
+
+    if (priv->share_rules) {
+        nm_utils_share_rules_apply(priv->share_rules, FALSE);
+        nm_clear_pointer(&priv->share_rules, nm_utils_share_rules_free);
+    }
+
+    G_OBJECT_CLASS(nm_act_request_parent_class)->dispose(object);
+}
+
+static void
+nm_act_request_class_init(NMActRequestClass *req_class)
+{
+    GObjectClass *           object_class = G_OBJECT_CLASS(req_class);
+    NMActiveConnectionClass *active_class = NM_ACTIVE_CONNECTION_CLASS(req_class);
+
+    /* virtual methods */
+    object_class->dispose              = dispose;
+    object_class->get_property         = get_property;
+    active_class->master_failed        = master_failed;
+    active_class->device_state_changed = device_state_changed;
+
+    /* properties */
+    g_object_class_override_property(object_class,
+                                     PROP_IP4_CONFIG,
+                                     NM_ACTIVE_CONNECTION_IP4_CONFIG);
+    g_object_class_override_property(object_class,
+                                     PROP_DHCP4_CONFIG,
+                                     NM_ACTIVE_CONNECTION_DHCP4_CONFIG);
+    g_object_class_override_property(object_class,
+                                     PROP_IP6_CONFIG,
+                                     NM_ACTIVE_CONNECTION_IP6_CONFIG);
+    g_object_class_override_property(object_class,
+                                     PROP_DHCP6_CONFIG,
+                                     NM_ACTIVE_CONNECTION_DHCP6_CONFIG);
+}