summaryrefslogtreecommitdiff
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 0000000000..248b57b861
--- /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 "nm-default.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);
+}