NetworkManager/src/libnm-client-impl/nm-secret-agent-old.c
Fernando Fernandez Mancera dd9aca4bd9 libnm: fix warnings due to invalid "closure" annotation
The "closure" annotation needs to be set on the callback parameter
instead of on the data for the callback function.

This patch fixes the following warning:

"""
../src/libnm-core-impl/nm-utils.c:3632: Warning: NM: invalid "closure" annotation: only valid on callback parameters
../src/libnm-client-impl/nm-client.c:4778: Warning: NM: invalid "closure" annotation: only valid on callback parameters
../src/libnm-client-impl/nm-client.c:5776: Warning: NM: invalid "closure" annotation: only valid on callback parameters
../src/libnm-client-impl/nm-client.c:5849: Warning: NM: invalid "closure" annotation: only valid on callback parameters
../src/libnm-client-impl/nm-client.c:5976: Warning: NM: invalid "closure" annotation: only valid on callback parameters
../src/libnm-client-impl/nm-client.c:6091: Warning: NM: invalid "closure" annotation: only valid on callback parameters
../src/libnm-client-impl/nm-client.c:6448: Warning: NM: invalid "closure" annotation: only valid on callback parameters
../src/libnm-client-impl/nm-client.c:6521: Warning: NM: invalid "closure" annotation: only valid on callback parameters
../src/libnm-client-impl/nm-client.c:6581: Warning: NM: invalid "closure" annotation: only valid on callback parameters
../src/libnm-client-impl/nm-client.c:6663: Warning: NM: invalid "closure" annotation: only valid on callback parameters
../src/libnm-client-impl/nm-client.c:6728: Warning: NM: invalid "closure" annotation: only valid on callback parameters
../src/libnm-client-impl/nm-secret-agent-old.c:974: Warning: NM: invalid "closure" annotation: only valid on callback parameters
../src/libnm-client-impl/nm-secret-agent-old.c:1014: Warning: NM: invalid "closure" annotation: only valid on callback parameters
../src/libnm-client-impl/nm-secret-agent-old.c:1041: Warning: NM: invalid "closure" annotation: only valid on callback parameters
../src/libnm-client-impl/nm-secret-agent-old.c:974: Warning: NM: invalid "closure" annotation: only valid on callback parameters
../src/libnm-client-impl/nm-secret-agent-old.c:1014: Warning: NM: invalid "closure" annotation: only valid on callback parameters
../src/libnm-client-impl/nm-secret-agent-old.c:1041: Warning: NM: invalid "closure" annotation: only valid on callback parameters
"""
2024-11-27 12:57:00 +01:00

1995 lines
71 KiB
C

/* SPDX-License-Identifier: LGPL-2.1-or-later */
/*
* Copyright (C) 2010 - 2011 Red Hat, Inc.
*/
#include "libnm-client-impl/nm-default-libnm.h"
#include "nm-secret-agent-old.h"
#include "c-list/src/c-list.h"
#include "libnm-core-intern/nm-core-internal.h"
#include "nm-dbus-helpers.h"
#include "nm-dbus-interface.h"
#include "nm-enum-types.h"
#include "libnm-glib-aux/nm-c-list.h"
#include "libnm-glib-aux/nm-dbus-aux.h"
#include "libnm-glib-aux/nm-time-utils.h"
#include "nm-simple-connection.h"
#define REGISTER_RETRY_TIMEOUT_MSEC 3000
#define _CALL_REGISTER_TIMEOUT_MSEC 15000
/*****************************************************************************/
typedef struct {
char *connection_path;
char *setting_name;
GDBusMethodInvocation *context;
CList gsi_lst;
bool is_cancelling : 1;
} GetSecretsInfo;
NM_GOBJECT_PROPERTIES_DEFINE(NMSecretAgentOld,
PROP_IDENTIFIER,
PROP_AUTO_REGISTER,
PROP_REGISTERED,
PROP_CAPABILITIES,
PROP_DBUS_CONNECTION, );
typedef struct {
GDBusConnection *dbus_connection;
GMainContext *main_context;
GMainContext *dbus_context;
GObject *context_busy_watcher;
GCancellable *name_owner_cancellable;
GCancellable *registering_cancellable;
GSource *registering_retry_source;
NMLInitData *init_data;
CList gsi_lst_head;
CList pending_tasks_register_lst_head;
char *identifier;
NMRefString *name_owner_curr;
NMRefString *name_owner_next;
gint64 registering_timeout_msec;
guint name_owner_changed_id;
guint exported_id;
guint capabilities;
guint8 registering_try_count;
guint8 register_state_change_reenter : 2;
bool session_bus : 1;
bool auto_register : 1;
bool is_registered : 1;
bool is_enabled : 1;
bool registration_force_unregister : 1;
/* This is true, if we either are in the process of RegisterWithCapabilities() or
* are already successfully registered.
*
* This is only TRUE, if the name owner was authenticated to run as root user.
*
* It also means, we should follow up with an Unregister() call during shutdown. */
bool registered_against_server : 1;
bool is_initialized : 1;
bool is_destroyed : 1;
} NMSecretAgentOldPrivate;
static void nm_secret_agent_old_initable_iface_init(GInitableIface *iface);
static void nm_secret_agent_old_async_initable_iface_init(GAsyncInitableIface *iface);
G_DEFINE_ABSTRACT_TYPE_WITH_CODE(
NMSecretAgentOld,
nm_secret_agent_old,
G_TYPE_OBJECT,
G_IMPLEMENT_INTERFACE(G_TYPE_INITABLE, nm_secret_agent_old_initable_iface_init);
G_IMPLEMENT_INTERFACE(G_TYPE_ASYNC_INITABLE, nm_secret_agent_old_async_initable_iface_init);)
#define NM_SECRET_AGENT_OLD_GET_PRIVATE(self) \
(G_TYPE_INSTANCE_GET_PRIVATE((self), NM_TYPE_SECRET_AGENT_OLD, NMSecretAgentOldPrivate))
/*****************************************************************************/
#define _NMLOG(level, ...) \
NML_DBUS_LOG((level), \
"secret-agent[" NM_HASH_OBFUSCATE_PTR_FMT \
"]: " _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \
NM_HASH_OBFUSCATE_PTR(self) _NM_UTILS_MACRO_REST(__VA_ARGS__))
/*****************************************************************************/
static const GDBusInterfaceInfo interface_info = NM_DEFINE_GDBUS_INTERFACE_INFO_INIT(
NM_DBUS_INTERFACE_SECRET_AGENT,
.methods = NM_DEFINE_GDBUS_METHOD_INFOS(
NM_DEFINE_GDBUS_METHOD_INFO("GetSecrets",
.in_args = NM_DEFINE_GDBUS_ARG_INFOS(
NM_DEFINE_GDBUS_ARG_INFO("connection", "a{sa{sv}}"),
NM_DEFINE_GDBUS_ARG_INFO("connection_path", "o"),
NM_DEFINE_GDBUS_ARG_INFO("setting_name", "s"),
NM_DEFINE_GDBUS_ARG_INFO("hints", "as"),
NM_DEFINE_GDBUS_ARG_INFO("flags", "u"), ),
.out_args = NM_DEFINE_GDBUS_ARG_INFOS(
NM_DEFINE_GDBUS_ARG_INFO("secrets", "a{sa{sv}}"), ), ),
NM_DEFINE_GDBUS_METHOD_INFO("CancelGetSecrets",
.in_args = NM_DEFINE_GDBUS_ARG_INFOS(
NM_DEFINE_GDBUS_ARG_INFO("connection_path", "o"),
NM_DEFINE_GDBUS_ARG_INFO("setting_name", "s"), ), ),
NM_DEFINE_GDBUS_METHOD_INFO("SaveSecrets",
.in_args = NM_DEFINE_GDBUS_ARG_INFOS(
NM_DEFINE_GDBUS_ARG_INFO("connection", "a{sa{sv}}"),
NM_DEFINE_GDBUS_ARG_INFO("connection_path", "o"), ), ),
NM_DEFINE_GDBUS_METHOD_INFO(
"DeleteSecrets",
.in_args = NM_DEFINE_GDBUS_ARG_INFOS(
NM_DEFINE_GDBUS_ARG_INFO("connection", "a{sa{sv}}"),
NM_DEFINE_GDBUS_ARG_INFO("connection_path", "o"), ), ), ), );
/*****************************************************************************/
static void _register_state_change(NMSecretAgentOld *self);
static void _register_dbus_call(NMSecretAgentOld *self);
static void _init_complete(NMSecretAgentOld *self, GError *error_take);
static void _register_state_complete(NMSecretAgentOld *self);
/*****************************************************************************/
/**
* nm_secret_agent_old_get_dbus_connection:
* @self: the #NMSecretAgentOld instance
*
* Returns: (transfer none): the #GDBusConnection used by the secret agent.
* You may either set this as construct property %NM_SECRET_AGENT_OLD_DBUS_CONNECTION,
* or it will automatically set during initialization.
*
* Since: 1.24
*/
GDBusConnection *
nm_secret_agent_old_get_dbus_connection(NMSecretAgentOld *self)
{
g_return_val_if_fail(NM_IS_SECRET_AGENT_OLD(self), NULL);
return NM_SECRET_AGENT_OLD_GET_PRIVATE(self)->dbus_connection;
}
/**
* nm_secret_agent_old_get_main_context:
* @self: the #NMSecretAgentOld instance
*
* Returns: (transfer none): the #GMainContext instance associate with the
* instance. This is the g_main_context_get_thread_default() at the time
* when creating the instance.
*
* Since: 1.24
*/
GMainContext *
nm_secret_agent_old_get_main_context(NMSecretAgentOld *self)
{
g_return_val_if_fail(NM_IS_SECRET_AGENT_OLD(self), NULL);
return NM_SECRET_AGENT_OLD_GET_PRIVATE(self)->main_context;
}
/**
* nm_secret_agent_old_get_context_busy_watcher:
* @self: the #NMSecretAgentOld instance
*
* Returns a #GObject that stays alive as long as there are pending
* requests in the #GDBusConnection. Such requests keep the #GMainContext
* alive, and thus you may want to keep iterating the context as long
* until a weak reference indicates that this object is gone. This is
* useful because even when you destroy the instance right away (and all
* the internally pending requests get cancelled), any pending g_dbus_connection_call()
* requests will still invoke the result on the #GMainContext. Hence, this
* allows you to know how long you must iterate the context to know
* that all remains are cleaned up.
*
* Returns: (transfer none): a #GObject that you may register a weak pointer
* to know that the #GMainContext is still kept busy by @self.
*
* Since: 1.24
*/
GObject *
nm_secret_agent_old_get_context_busy_watcher(NMSecretAgentOld *self)
{
g_return_val_if_fail(NM_IS_SECRET_AGENT_OLD(self), NULL);
return NM_SECRET_AGENT_OLD_GET_PRIVATE(self)->context_busy_watcher;
}
/**
* nm_secret_agent_old_get_dbus_name_owner:
* @self: the #NMSecretAgentOld instance
*
* Returns: the current D-Bus name owner. While this property
* is set while registering, it really only makes sense when
* the nm_secret_agent_old_get_registered() indicates that
* registration is successful.
*
* Since: 1.24
*/
const char *
nm_secret_agent_old_get_dbus_name_owner(NMSecretAgentOld *self)
{
g_return_val_if_fail(NM_IS_SECRET_AGENT_OLD(self), NULL);
return nm_ref_string_get_str(NM_SECRET_AGENT_OLD_GET_PRIVATE(self)->name_owner_curr);
}
/**
* nm_secret_agent_old_get_registered:
* @self: a #NMSecretAgentOld
*
* Note that the secret agent transparently registers and re-registers
* as the D-Bus name owner appears. Hence, this property is not really
* useful. Also, to be graceful against races during registration, the
* instance will already accept requests while being in the process of
* registering.
* If you need to avoid races and want to wait until @self is registered,
* call nm_secret_agent_old_register_async(). If that function completes
* with success, you know the instance is registered.
*
* Returns: a %TRUE if the agent is registered, %FALSE if it is not.
**/
gboolean
nm_secret_agent_old_get_registered(NMSecretAgentOld *self)
{
g_return_val_if_fail(NM_IS_SECRET_AGENT_OLD(self), FALSE);
return NM_SECRET_AGENT_OLD_GET_PRIVATE(self)->is_registered;
}
/*****************************************************************************/
static void
get_secret_info_free(GetSecretsInfo *info)
{
nm_assert(info);
nm_assert(!info->context);
c_list_unlink_stale(&info->gsi_lst);
g_free(info->connection_path);
g_free(info->setting_name);
nm_g_slice_free(info);
}
static void
get_secret_info_complete_and_free(GetSecretsInfo *info, GVariant *secrets, GError *error)
{
if (error) {
if (secrets)
nm_g_variant_unref_floating(secrets);
g_dbus_method_invocation_return_gerror(g_steal_pointer(&info->context), error);
} else {
g_dbus_method_invocation_return_value(g_steal_pointer(&info->context),
g_variant_new("(@a{sa{sv}})", secrets));
}
get_secret_info_free(info);
}
static void
get_secret_info_complete_and_free_error(GetSecretsInfo *info,
GQuark error_domain,
int error_code,
const char *error_message)
{
g_dbus_method_invocation_return_error_literal(g_steal_pointer(&info->context),
error_domain,
error_code,
error_message);
get_secret_info_free(info);
}
/*****************************************************************************/
static void
_dbus_connection_call_cb(GObject *source, GAsyncResult *result, gpointer user_data)
{
gs_unref_object GObject *context_busy_watcher = NULL;
GAsyncReadyCallback callback;
gpointer callback_user_data;
nm_utils_user_data_unpack(user_data, &context_busy_watcher, &callback, &callback_user_data);
callback(source, result, callback_user_data);
}
static void
_dbus_connection_call(NMSecretAgentOld *self,
const char *bus_name,
const char *object_path,
const char *interface_name,
const char *method_name,
GVariant *parameters,
const GVariantType *reply_type,
GDBusCallFlags flags,
int timeout_msec,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
NMSecretAgentOldPrivate *priv = NM_SECRET_AGENT_OLD_GET_PRIVATE(self);
nm_assert(nm_g_main_context_is_thread_default(priv->dbus_context));
g_dbus_connection_call(
priv->dbus_connection,
bus_name,
object_path,
interface_name,
method_name,
parameters,
reply_type,
flags,
timeout_msec,
cancellable,
callback ? _dbus_connection_call_cb : NULL,
callback
? nm_utils_user_data_pack(g_object_ref(priv->context_busy_watcher), callback, user_data)
: NULL);
}
/*****************************************************************************/
static GetSecretsInfo *
find_get_secrets_info(NMSecretAgentOldPrivate *priv,
const char *connection_path,
const char *setting_name)
{
GetSecretsInfo *info;
c_list_for_each_entry (info, &priv->gsi_lst_head, gsi_lst) {
if (nm_streq(connection_path, info->connection_path)
&& nm_streq(setting_name, info->setting_name))
return info;
}
return NULL;
}
static void
_cancel_get_secret_request(NMSecretAgentOld *self, GetSecretsInfo *info, const char *message)
{
c_list_unlink(&info->gsi_lst);
info->is_cancelling = TRUE;
_LOGT("cancel get-secrets request \"%s\", \"%s\": %s",
info->connection_path,
info->setting_name,
message);
NM_SECRET_AGENT_OLD_GET_CLASS(self)->cancel_get_secrets(self,
info->connection_path,
info->setting_name);
get_secret_info_complete_and_free_error(info,
NM_SECRET_AGENT_ERROR,
NM_SECRET_AGENT_ERROR_AGENT_CANCELED,
message);
}
static gboolean
verify_request(NMSecretAgentOld *self,
GDBusMethodInvocation *context,
GVariant *connection_dict,
const char *connection_path,
NMConnection **out_connection,
GError **error)
{
gs_unref_object NMConnection *connection = NULL;
gs_free_error GError *local = NULL;
if (!nm_dbus_path_not_empty(connection_path)) {
g_set_error_literal(error,
NM_SECRET_AGENT_ERROR,
NM_SECRET_AGENT_ERROR_INVALID_CONNECTION,
"Invalid connection: no connection path given.");
return FALSE;
}
connection = _nm_simple_connection_new_from_dbus(connection_dict,
NM_SETTING_PARSE_FLAGS_BEST_EFFORT,
&local);
if (!connection) {
g_set_error(error,
NM_SECRET_AGENT_ERROR,
NM_SECRET_AGENT_ERROR_INVALID_CONNECTION,
"Invalid connection: %s",
local->message);
return FALSE;
}
nm_connection_set_path(connection, connection_path);
NM_SET_OUT(out_connection, g_steal_pointer(&connection));
return TRUE;
}
static void
get_secrets_cb(NMSecretAgentOld *self,
NMConnection *connection,
GVariant *secrets,
GError *error,
gpointer user_data)
{
GetSecretsInfo *info = user_data;
if (info->is_cancelling) {
if (secrets)
nm_g_variant_unref_floating(secrets);
return;
}
_LOGT("request: get-secrets request \"%s\", \"%s\" complete with %s%s%s",
info->connection_path,
info->setting_name,
NM_PRINT_FMT_QUOTED(error, "error: ", error->message, "", "success"));
get_secret_info_complete_and_free(info, secrets, error);
}
static void
impl_get_secrets(NMSecretAgentOld *self, GVariant *parameters, GDBusMethodInvocation *context)
{
NMSecretAgentOldPrivate *priv = NM_SECRET_AGENT_OLD_GET_PRIVATE(self);
GError *error = NULL;
gs_unref_object NMConnection *connection = NULL;
GetSecretsInfo *info;
gs_unref_variant GVariant *arg_connection = NULL;
const char *arg_connection_path;
const char *arg_setting_name;
gs_free const char **arg_hints = NULL;
guint32 arg_flags;
g_variant_get(parameters,
"(@a{sa{sv}}&o&s^a&su)",
&arg_connection,
&arg_connection_path,
&arg_setting_name,
&arg_hints,
&arg_flags);
if (!verify_request(self, context, arg_connection, arg_connection_path, &connection, &error)) {
g_dbus_method_invocation_take_error(context, error);
return;
}
_LOGT("request: get-secrets(\"%s\", \"%s\")", arg_connection_path, arg_setting_name);
info = find_get_secrets_info(priv, arg_connection_path, arg_setting_name);
if (info)
_cancel_get_secret_request(self, info, "Request aborted due to new request");
info = g_slice_new(GetSecretsInfo);
*info = (GetSecretsInfo) {
.context = context,
.connection_path = g_strdup(arg_connection_path),
.setting_name = g_strdup(arg_setting_name),
};
c_list_link_tail(&priv->gsi_lst_head, &info->gsi_lst);
NM_SECRET_AGENT_OLD_GET_CLASS(self)->get_secrets(self,
connection,
info->connection_path,
info->setting_name,
arg_hints,
arg_flags,
get_secrets_cb,
info);
}
static void
impl_cancel_get_secrets(NMSecretAgentOld *self,
GVariant *parameters,
GDBusMethodInvocation *context)
{
NMSecretAgentOldPrivate *priv = NM_SECRET_AGENT_OLD_GET_PRIVATE(self);
GetSecretsInfo *info;
const char *arg_connection_path;
const char *arg_setting_name;
g_variant_get(parameters, "(&o&s)", &arg_connection_path, &arg_setting_name);
info = find_get_secrets_info(priv, arg_connection_path, arg_setting_name);
if (!info) {
g_dbus_method_invocation_return_error_literal(
context,
NM_SECRET_AGENT_ERROR,
NM_SECRET_AGENT_ERROR_FAILED,
"No secrets request in progress for this connection.");
return;
}
_cancel_get_secret_request(self, info, "Request cancelled by NetworkManager");
g_dbus_method_invocation_return_value(context, NULL);
}
static void
save_secrets_cb(NMSecretAgentOld *self, NMConnection *connection, GError *error, gpointer user_data)
{
GDBusMethodInvocation *context = user_data;
if (error)
g_dbus_method_invocation_return_gerror(context, error);
else
g_dbus_method_invocation_return_value(context, NULL);
}
static void
impl_save_secrets(NMSecretAgentOld *self, GVariant *parameters, GDBusMethodInvocation *context)
{
gs_unref_object NMConnection *connection = NULL;
gs_unref_variant GVariant *arg_connection = NULL;
const char *arg_connection_path;
GError *error = NULL;
g_variant_get(parameters, "(@a{sa{sv}}&o)", &arg_connection, &arg_connection_path);
if (!verify_request(self, context, arg_connection, arg_connection_path, &connection, &error)) {
g_dbus_method_invocation_take_error(context, error);
return;
}
_LOGT("request: save-secrets(\"%s\")", arg_connection_path);
NM_SECRET_AGENT_OLD_GET_CLASS(self)->save_secrets(self,
connection,
arg_connection_path,
save_secrets_cb,
context);
}
static void
delete_secrets_cb(NMSecretAgentOld *self,
NMConnection *connection,
GError *error,
gpointer user_data)
{
GDBusMethodInvocation *context = user_data;
if (error)
g_dbus_method_invocation_return_gerror(context, error);
else
g_dbus_method_invocation_return_value(context, NULL);
}
static void
impl_delete_secrets(NMSecretAgentOld *self, GVariant *parameters, GDBusMethodInvocation *context)
{
gs_unref_object NMConnection *connection = NULL;
gs_unref_variant GVariant *arg_connection = NULL;
const char *arg_connection_path;
GError *error = NULL;
g_variant_get(parameters, "(@a{sa{sv}}&o)", &arg_connection, &arg_connection_path);
if (!verify_request(self, context, arg_connection, arg_connection_path, &connection, &error)) {
g_dbus_method_invocation_take_error(context, error);
return;
}
_LOGT("request: delete-secrets(\"%s\")", arg_connection_path);
NM_SECRET_AGENT_OLD_GET_CLASS(self)->delete_secrets(self,
connection,
arg_connection_path,
delete_secrets_cb,
context);
}
/*****************************************************************************/
/**
* nm_secret_agent_old_enable:
* @self: the #NMSecretAgentOld instance
* @enable: whether to enable or disable the listener.
*
* This has the same effect as setting %NM_SECRET_AGENT_OLD_AUTO_REGISTER
* property.
*
* Unlike most other functions, you may already call this function before
* initialization completes.
*
* Since: 1.24
*/
void
nm_secret_agent_old_enable(NMSecretAgentOld *self, gboolean enable)
{
NMSecretAgentOldPrivate *priv;
g_return_if_fail(NM_IS_SECRET_AGENT_OLD(self));
priv = NM_SECRET_AGENT_OLD_GET_PRIVATE(self);
enable = (!!enable);
if (priv->auto_register != enable) {
priv->auto_register = enable;
priv->is_enabled = enable;
_notify(self, PROP_AUTO_REGISTER);
}
_register_state_change(self);
}
static void
_secret_agent_old_destroy(NMSecretAgentOld *self)
{
NMSecretAgentOldPrivate *priv = NM_SECRET_AGENT_OLD_GET_PRIVATE(self);
priv->is_destroyed = TRUE;
if (priv->exported_id != 0) {
g_dbus_connection_unregister_object(priv->dbus_connection,
nm_steal_int(&priv->exported_id));
}
_register_state_change(self);
nm_assert(!priv->name_owner_changed_id);
nm_assert(!priv->name_owner_curr);
nm_assert(!priv->name_owner_next);
nm_assert(!priv->name_owner_cancellable);
nm_assert(!priv->registering_retry_source);
nm_assert(!priv->registering_cancellable);
nm_assert(!priv->init_data);
nm_assert(c_list_is_empty(&priv->gsi_lst_head));
nm_assert(c_list_is_empty(&priv->pending_tasks_register_lst_head));
}
/**
* nm_secret_agent_old_destroy:
* @self: the #NMSecretAgentOld instance.
*
* Since 1.24, the instance will already register a D-Bus object on the
* D-Bus connection during initialization. That object will stay registered
* until @self gets unrefed (destroyed) or this function is called. This
* function performs the necessary cleanup to tear down the instance. Afterwards,
* the function can not longer be used. This is optional, but necessary to
* ensure unregistering the D-Bus object at a define point, when other users
* might still have a reference on @self.
*
* You may call this function any time and repeatedly. However, after destroying
* the instance, it is a bug to still use the instance for other purposes. The
* instance becomes defunct and cannot re-register.
*
* Since: 1.24
*/
void
nm_secret_agent_old_destroy(NMSecretAgentOld *self)
{
g_return_if_fail(NM_IS_SECRET_AGENT_OLD(self));
_LOGT("destroying");
_secret_agent_old_destroy(self);
}
/*****************************************************************************/
/**
* nm_secret_agent_old_register:
* @self: a #NMSecretAgentOld
* @cancellable: a #GCancellable, or %NULL
* @error: return location for a #GError, or %NULL
*
* Registers the #NMSecretAgentOld with the NetworkManager secret manager,
* indicating to NetworkManager that the agent is able to provide and save
* secrets for connections on behalf of its user.
*
* Returns: %TRUE if registration was successful, %FALSE on error.
*
* Since 1.24, this can no longer fail unless the @cancellable gets
* cancelled. Contrary to nm_secret_agent_old_register_async(), this also
* does not wait for the registration to succeed. You cannot synchronously
* (without iterating the caller's GMainContext) wait for registration.
*
* Since 1.24, registration is idempotent. It has the same effect as setting
* %NM_SECRET_AGENT_OLD_AUTO_REGISTER to %TRUE or nm_secret_agent_old_enable().
*
* Deprecated: 1.24: Use nm_secret_agent_old_enable() or nm_secret_agent_old_register_async().
**/
gboolean
nm_secret_agent_old_register(NMSecretAgentOld *self, GCancellable *cancellable, GError **error)
{
NMSecretAgentOldPrivate *priv;
g_return_val_if_fail(NM_IS_SECRET_AGENT_OLD(self), FALSE);
g_return_val_if_fail(!error || !*error, FALSE);
priv = NM_SECRET_AGENT_OLD_GET_PRIVATE(self);
g_return_val_if_fail(priv->is_initialized && !priv->is_destroyed, FALSE);
priv->is_enabled = TRUE;
_register_state_change(self);
if (g_cancellable_set_error_if_cancelled(cancellable, error))
return FALSE;
/* This is a synchronous function, meaning: we are not allowed to iterate
* the caller's GMainContext. This is a catch 22, because we don't want
* to perform synchronous calls that bypasses the ordering of our otherwise
* asynchronous mode of operation. Hence, we always signal success.
* That's why this function is deprecated.
*
* So despite claiming success, we might still be in the process of registering
* or NetworkManager might not be available.
*
* This is a change in behavior with respect to libnm before 1.24.
*/
return TRUE;
}
static void
_register_cancelled_cb(GObject *object, gpointer user_data)
{
GTask *task0 = user_data;
gs_unref_object GTask *task = NULL;
NMSecretAgentOld *self = g_task_get_source_object(task0);
NMSecretAgentOldPrivate *priv = NM_SECRET_AGENT_OLD_GET_PRIVATE(self);
gulong *p_cancelled_id;
NMCListElem *elem;
gs_free_error GError *error = NULL;
elem = nm_c_list_elem_find_first(&priv->pending_tasks_register_lst_head, x, x == task0);
g_return_if_fail(elem);
task = nm_c_list_elem_free_steal(elem);
p_cancelled_id = g_task_get_task_data(task);
if (p_cancelled_id) {
g_signal_handler_disconnect(g_task_get_cancellable(task), *p_cancelled_id);
g_task_set_task_data(task, NULL, NULL);
}
nm_utils_error_set_cancelled(&error, FALSE, NULL);
g_task_return_error(task, error);
}
/**
* nm_secret_agent_old_register_async:
* @self: a #NMSecretAgentOld
* @cancellable: a #GCancellable, or %NULL
* @callback: callback to call when the agent is registered
* @user_data: data for @callback
*
* Asynchronously registers the #NMSecretAgentOld with the NetworkManager secret
* manager, indicating to NetworkManager that the agent is able to provide and
* save secrets for connections on behalf of its user.
*
* Since 1.24, registration cannot fail and is idempotent. It has
* the same effect as setting %NM_SECRET_AGENT_OLD_AUTO_REGISTER to %TRUE
* or nm_secret_agent_old_enable().
*
* Since 1.24, the asynchronous result indicates whether the instance is successfully
* registered. In any case, this call enables the agent and it will automatically
* try to register and handle secret requests. A failure of this function only indicates
* that currently the instance might not be ready (but since it will automatically
* try to recover, it might be ready in a moment afterwards). Use this function if
* you want to check and ensure that the agent is registered.
**/
void
nm_secret_agent_old_register_async(NMSecretAgentOld *self,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
NMSecretAgentOldPrivate *priv;
g_return_if_fail(NM_IS_SECRET_AGENT_OLD(self));
g_return_if_fail(!cancellable || G_IS_CANCELLABLE(cancellable));
priv = NM_SECRET_AGENT_OLD_GET_PRIVATE(self);
g_return_if_fail(priv->is_initialized && !priv->is_destroyed);
if (callback) {
GTask *task;
task = nm_g_task_new(self,
cancellable,
nm_secret_agent_old_register_async,
callback,
user_data);
c_list_link_tail(&priv->pending_tasks_register_lst_head,
&nm_c_list_elem_new_stale(task)->lst);
if (cancellable) {
gulong cancelled_id;
cancelled_id =
g_cancellable_connect(cancellable, G_CALLBACK(_register_cancelled_cb), task, NULL);
if (cancelled_id != 0) {
g_task_set_task_data(task, nm_memdup(&cancelled_id, sizeof(cancelled_id)), g_free);
}
}
}
priv->is_enabled = TRUE;
_register_state_change(self);
}
/**
* nm_secret_agent_old_register_finish:
* @self: a #NMSecretAgentOld
* @result: the result passed to the #GAsyncReadyCallback
* @error: return location for a #GError, or %NULL
*
* Gets the result of a call to nm_secret_agent_old_register_async().
*
* Returns: %TRUE if registration was successful, %FALSE on error.
*
* Since 1.24, registration cannot fail and is idempotent. It has
* the same effect as setting %NM_SECRET_AGENT_OLD_AUTO_REGISTER to %TRUE
* or nm_secret_agent_old_enable().
**/
gboolean
nm_secret_agent_old_register_finish(NMSecretAgentOld *self, GAsyncResult *result, GError **error)
{
g_return_val_if_fail(NM_IS_SECRET_AGENT_OLD(self), FALSE);
g_return_val_if_fail(nm_g_task_is_valid(result, self, nm_secret_agent_old_register_async),
FALSE);
return g_task_propagate_boolean(G_TASK(result), error);
}
/**
* nm_secret_agent_old_unregister:
* @self: a #NMSecretAgentOld
* @cancellable: a #GCancellable, or %NULL
* @error: return location for a #GError, or %NULL
*
* Unregisters the #NMSecretAgentOld with the NetworkManager secret manager,
* indicating to NetworkManager that the agent will no longer provide or
* store secrets on behalf of this user.
*
* Returns: %TRUE if unregistration was successful, %FALSE on error
*
* Since 1.24, registration cannot fail and is idempotent. It has
* the same effect as setting %NM_SECRET_AGENT_OLD_AUTO_REGISTER to %FALSE
* or nm_secret_agent_old_enable().
*
* Deprecated: 1.24: Use nm_secret_agent_old_enable().
**/
gboolean
nm_secret_agent_old_unregister(NMSecretAgentOld *self, GCancellable *cancellable, GError **error)
{
NMSecretAgentOldPrivate *priv;
g_return_val_if_fail(NM_IS_SECRET_AGENT_OLD(self), FALSE);
g_return_val_if_fail(!cancellable || G_IS_CANCELLABLE(cancellable), FALSE);
g_return_val_if_fail(!error || !*error, FALSE);
priv = NM_SECRET_AGENT_OLD_GET_PRIVATE(self);
g_return_val_if_fail(priv->is_initialized && !priv->is_destroyed, FALSE);
priv->is_enabled = FALSE;
_register_state_change(self);
return !g_cancellable_set_error_if_cancelled(cancellable, error);
}
/**
* nm_secret_agent_old_unregister_async:
* @self: a #NMSecretAgentOld
* @cancellable: a #GCancellable, or %NULL
* @callback: callback to call when the agent is unregistered
* @user_data: data for @callback
*
* Asynchronously unregisters the #NMSecretAgentOld with the NetworkManager secret
* manager, indicating to NetworkManager that the agent will no longer provide
* or store secrets on behalf of this user.
*
* Since 1.24, registration cannot fail and is idempotent. It has
* the same effect as setting %NM_SECRET_AGENT_OLD_AUTO_REGISTER to %FALSE
* or nm_secret_agent_old_enable().
*
* Deprecated: 1.24: Use nm_secret_agent_old_enable().
**/
void
nm_secret_agent_old_unregister_async(NMSecretAgentOld *self,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
NMSecretAgentOldPrivate *priv;
g_return_if_fail(NM_IS_SECRET_AGENT_OLD(self));
g_return_if_fail(!cancellable || G_IS_CANCELLABLE(cancellable));
priv = NM_SECRET_AGENT_OLD_GET_PRIVATE(self);
g_return_if_fail(priv->is_initialized && !priv->is_destroyed);
if (callback) {
gs_unref_object GTask *task = NULL;
task = nm_g_task_new(self,
cancellable,
nm_secret_agent_old_unregister_async,
callback,
user_data);
g_task_return_boolean(task, TRUE);
}
priv->is_enabled = FALSE;
_register_state_change(self);
}
/**
* nm_secret_agent_old_unregister_finish:
* @self: a #NMSecretAgentOld
* @result: the result passed to the #GAsyncReadyCallback
* @error: return location for a #GError, or %NULL
*
* Gets the result of a call to nm_secret_agent_old_unregister_async().
*
* Returns: %TRUE if unregistration was successful, %FALSE on error.
*
* Since 1.24, registration cannot fail and is idempotent. It has
* the same effect as setting %NM_SECRET_AGENT_OLD_AUTO_REGISTER to %FALSE
* or nm_secret_agent_old_enable().
*
* Deprecated: 1.24: Use nm_secret_agent_old_enable().
**/
gboolean
nm_secret_agent_old_unregister_finish(NMSecretAgentOld *self, GAsyncResult *result, GError **error)
{
g_return_val_if_fail(NM_IS_SECRET_AGENT_OLD(self), FALSE);
g_return_val_if_fail(nm_g_task_is_valid(result, self, nm_secret_agent_old_unregister_async),
FALSE);
return g_task_propagate_boolean(G_TASK(result), error);
}
/*****************************************************************************/
/**
* nm_secret_agent_old_get_secrets: (virtual get_secrets)
* @self: a #NMSecretAgentOld
* @connection: the #NMConnection for which we're asked secrets
* @setting_name: the name of the secret setting
* @hints: (array zero-terminated=1): hints to the agent
* @flags: flags that modify the behavior of the request
* @callback: (scope async) (closure user_data): a callback, to be invoked when the operation is done
* @user_data: caller-specific data to be passed to @callback
*
* Asynchronously retrieves secrets belonging to @connection for the
* setting @setting_name. @flags indicate specific behavior that the secret
* agent should use when performing the request, for example returning only
* existing secrets without user interaction, or requesting entirely new
* secrets from the user.
*/
void
nm_secret_agent_old_get_secrets(NMSecretAgentOld *self,
NMConnection *connection,
const char *setting_name,
const char **hints,
NMSecretAgentGetSecretsFlags flags,
NMSecretAgentOldGetSecretsFunc callback,
gpointer user_data)
{
g_return_if_fail(NM_IS_SECRET_AGENT_OLD(self));
g_return_if_fail(NM_IS_CONNECTION(connection));
g_return_if_fail(nm_connection_get_path(connection));
g_return_if_fail(setting_name && setting_name[0]);
g_return_if_fail(!(flags & NM_SECRET_AGENT_GET_SECRETS_FLAG_ONLY_SYSTEM));
g_return_if_fail(!(flags & NM_SECRET_AGENT_GET_SECRETS_FLAG_NO_ERRORS));
g_return_if_fail(callback != NULL);
NM_SECRET_AGENT_OLD_GET_CLASS(self)->get_secrets(self,
connection,
nm_connection_get_path(connection),
setting_name,
hints,
flags,
callback,
user_data);
}
/**
* nm_secret_agent_old_save_secrets: (virtual save_secrets)
* @self: a #NMSecretAgentOld
* @connection: a #NMConnection
* @callback: (scope async) (closure user_data): a callback, to be invoked when the operation is done
* @user_data: caller-specific data to be passed to @callback
*
* Asynchronously ensures that all secrets inside @connection are stored to
* disk.
*/
void
nm_secret_agent_old_save_secrets(NMSecretAgentOld *self,
NMConnection *connection,
NMSecretAgentOldSaveSecretsFunc callback,
gpointer user_data)
{
g_return_if_fail(NM_IS_SECRET_AGENT_OLD(self));
g_return_if_fail(NM_IS_CONNECTION(connection));
g_return_if_fail(nm_connection_get_path(connection));
NM_SECRET_AGENT_OLD_GET_CLASS(self)->save_secrets(self,
connection,
nm_connection_get_path(connection),
callback,
user_data);
}
/**
* nm_secret_agent_old_delete_secrets: (virtual delete_secrets)
* @self: a #NMSecretAgentOld
* @connection: a #NMConnection
* @callback: (scope async) (closure user_data): a callback, to be invoked when the operation is done
* @user_data: caller-specific data to be passed to @callback
*
* Asynchronously asks the agent to delete all saved secrets belonging to
* @connection.
*/
void
nm_secret_agent_old_delete_secrets(NMSecretAgentOld *self,
NMConnection *connection,
NMSecretAgentOldDeleteSecretsFunc callback,
gpointer user_data)
{
g_return_if_fail(NM_IS_SECRET_AGENT_OLD(self));
g_return_if_fail(NM_IS_CONNECTION(connection));
g_return_if_fail(nm_connection_get_path(connection));
NM_SECRET_AGENT_OLD_GET_CLASS(self)->delete_secrets(self,
connection,
nm_connection_get_path(connection),
callback,
user_data);
}
/*****************************************************************************/
static gboolean
validate_identifier(const char *identifier)
{
const char *p = identifier;
size_t id_len;
/* Length between 3 and 255 characters inclusive */
id_len = strlen(identifier);
if (id_len < 3 || id_len > 255)
return FALSE;
if ((identifier[0] == '.') || (identifier[id_len - 1] == '.'))
return FALSE;
/* FIXME: do complete validation here */
while (p && *p) {
if (!g_ascii_isalnum(*p) && (*p != '_') && (*p != '-') && (*p != '.'))
return FALSE;
if ((*p == '.') && (*(p + 1) == '.'))
return FALSE;
p++;
}
return TRUE;
}
/*****************************************************************************/
static gboolean
_register_retry_cb(gpointer user_data)
{
NMSecretAgentOld *self = user_data;
NMSecretAgentOldPrivate *priv = NM_SECRET_AGENT_OLD_GET_PRIVATE(self);
nm_auto_pop_gmaincontext GMainContext *dbus_context = NULL;
dbus_context = nm_g_main_context_push_thread_default_if_necessary(priv->dbus_context);
nm_clear_g_source_inst(&priv->registering_retry_source);
_register_dbus_call(self);
return G_SOURCE_CONTINUE;
}
static void
_register_call_cb(GObject *source, GAsyncResult *result, gpointer user_data)
{
NMSecretAgentOld *self = user_data;
NMSecretAgentOldPrivate *priv = NM_SECRET_AGENT_OLD_GET_PRIVATE(self);
gs_unref_variant GVariant *ret = NULL;
gs_free_error GError *error = NULL;
ret = g_dbus_connection_call_finish(G_DBUS_CONNECTION(source), result, &error);
if (nm_utils_error_is_cancelled(error))
return;
nm_assert(!priv->registering_retry_source);
nm_assert(!priv->is_registered);
nm_assert(priv->registering_cancellable);
if (nm_dbus_error_is(error, NM_DBUS_ERROR_NAME_UNKNOWN_METHOD)
&& nm_utils_get_monotonic_timestamp_msec() < priv->registering_timeout_msec) {
guint timeout_msec;
timeout_msec = (2u << NM_MIN(6u, ++priv->registering_try_count));
_LOGT("register: registration failed with error \"%s\". Retry in %u msec...",
error->message,
timeout_msec);
priv->registering_retry_source =
nm_g_source_attach(nm_g_timeout_source_new(timeout_msec,
G_PRIORITY_DEFAULT,
_register_retry_cb,
self,
NULL),
priv->dbus_context);
return;
}
g_clear_object(&priv->registering_cancellable);
if (error) {
/* registration apparently failed. However we still keep priv->registered_against_server TRUE, because
*
* - eventually we want to still make an Unregister() call. Even if it probably has no effect,
* better be sure.
*
* - we actually accept secret request (from the right name owner). We register so that
* NetworkManager knows that we are here. We don't require the registration to succeed
* for our purpose. If NetworkManager makes requests for us, despite the registration
* failing, that is fine. */
_LOGT("register: registration failed with error \"%s\"", error->message);
goto out;
}
_LOGT("register: registration succeeded");
priv->is_registered = TRUE;
_notify(self, PROP_REGISTERED);
out:
_register_state_complete(self);
}
static void
_register_dbus_call(NMSecretAgentOld *self)
{
NMSecretAgentOldPrivate *priv = NM_SECRET_AGENT_OLD_GET_PRIVATE(self);
_dbus_connection_call(self,
nm_ref_string_get_str(priv->name_owner_curr),
NM_DBUS_PATH_AGENT_MANAGER,
NM_DBUS_INTERFACE_AGENT_MANAGER,
"RegisterWithCapabilities",
g_variant_new("(su)", priv->identifier, (guint32) priv->capabilities),
G_VARIANT_TYPE("()"),
G_DBUS_CALL_FLAGS_NONE,
_CALL_REGISTER_TIMEOUT_MSEC,
priv->registering_cancellable,
_register_call_cb,
self);
}
static void
_get_connection_unix_user_cb(GObject *source, GAsyncResult *result, gpointer user_data)
{
NMSecretAgentOld *self;
NMSecretAgentOldPrivate *priv;
gs_unref_variant GVariant *ret = NULL;
gs_free_error GError *error = NULL;
guint32 sender_uid = 0;
ret = g_dbus_connection_call_finish(G_DBUS_CONNECTION(source), result, &error);
if (nm_utils_error_is_cancelled(error))
return;
self = user_data;
priv = NM_SECRET_AGENT_OLD_GET_PRIVATE(self);
nm_assert(priv->registering_cancellable);
nm_assert(!priv->registered_against_server);
if (ret)
g_variant_get(ret, "(u)", &sender_uid);
if (ret && sender_uid == 0)
_LOGT("register: peer %s is owned by root. Validated to accept requests.",
priv->name_owner_curr->str);
else if (ret && priv->session_bus) {
_LOGT(
"register: peer %s is owned by user %d for session bus. Validated to accept requests.",
priv->name_owner_curr->str,
sender_uid);
} else {
/* the peer is not validated. We don't actually register. */
if (ret)
_LOGT("register: peer %s is owned by user %u. Not validated as NetworkManager service.",
priv->name_owner_curr->str,
sender_uid);
else
_LOGT("register: failed to get user id for peer %s: %s. Not validated as "
"NetworkManager service.",
priv->name_owner_curr->str,
error->message);
/* we actually don't do anything and keep the agent unregistered.
*
* We keep priv->registering_cancellable set to not retry this again, until we loose the
* name owner. But the state of the agent is lingering and won't accept any requests. */
return;
}
priv->registering_timeout_msec =
nm_utils_get_monotonic_timestamp_msec() + REGISTER_RETRY_TIMEOUT_MSEC;
priv->registering_try_count = 0;
priv->registered_against_server = TRUE;
_register_dbus_call(self);
}
/*****************************************************************************/
static void
_name_owner_changed(NMSecretAgentOld *self, const char *name_owner, gboolean is_event)
{
NMSecretAgentOldPrivate *priv = NM_SECRET_AGENT_OLD_GET_PRIVATE(self);
if (is_event) {
if (priv->name_owner_cancellable) {
/* we are still fetching the name-owner. Ignore this event. */
return;
}
} else
g_clear_object(&priv->name_owner_cancellable);
nm_ref_string_unref(priv->name_owner_next);
priv->name_owner_next = nm_ref_string_new(nm_str_not_empty(name_owner));
_LOGT("name-owner changed: %s%s%s -> %s%s%s",
NM_PRINT_FMT_QUOTED(priv->name_owner_curr,
"\"",
priv->name_owner_curr->str,
"\"",
"(null)"),
NM_PRINT_FMT_QUOTED(priv->name_owner_next,
"\"",
priv->name_owner_next->str,
"\"",
"(null)"));
_register_state_change(self);
}
static void
_name_owner_changed_cb(GDBusConnection *connection,
const char *sender_name,
const char *object_path,
const char *interface_name,
const char *signal_name,
GVariant *parameters,
gpointer user_data)
{
NMSecretAgentOld *self = user_data;
const char *new_owner;
if (!g_variant_is_of_type(parameters, G_VARIANT_TYPE("(sss)")))
return;
g_variant_get(parameters, "(&s&s&s)", NULL, NULL, &new_owner);
_name_owner_changed(self, new_owner, TRUE);
}
static void
_name_owner_get_cb(const char *name_owner, GError *error, gpointer user_data)
{
if (name_owner || !nm_utils_error_is_cancelled(error))
_name_owner_changed(user_data, name_owner, FALSE);
}
/*****************************************************************************/
static void
_method_call(GDBusConnection *connection,
const char *sender,
const char *object_path,
const char *interface_name,
const char *method_name,
GVariant *parameters,
GDBusMethodInvocation *context,
gpointer user_data)
{
NMSecretAgentOld *self = user_data;
NMSecretAgentOldPrivate *priv = NM_SECRET_AGENT_OLD_GET_PRIVATE(self);
nm_assert(nm_streq0(object_path, NM_DBUS_PATH_SECRET_AGENT));
nm_assert(nm_streq0(interface_name, NM_DBUS_INTERFACE_SECRET_AGENT));
nm_assert(sender);
nm_assert(nm_streq0(sender, g_dbus_method_invocation_get_sender(context)));
if (!priv->name_owner_curr || !priv->registered_against_server) {
/* priv->registered_against_server means that we started to register, but not necessarily
* that the registration fully succeeded. However, we already authenticated the request
* and so we accept it, even if the registration is not yet complete. */
g_dbus_method_invocation_return_error_literal(context,
NM_SECRET_AGENT_ERROR,
NM_SECRET_AGENT_ERROR_PERMISSION_DENIED,
"Request by non authenticated peer rejected");
return;
}
if (nm_streq(method_name, "GetSecrets"))
impl_get_secrets(self, parameters, context);
else if (nm_streq(method_name, "CancelGetSecrets"))
impl_cancel_get_secrets(self, parameters, context);
else if (nm_streq(method_name, "SaveSecrets"))
impl_save_secrets(self, parameters, context);
else if (nm_streq(method_name, "DeleteSecrets"))
impl_delete_secrets(self, parameters, context);
else
nm_assert_not_reached();
}
/*****************************************************************************/
static void
_register_state_complete(NMSecretAgentOld *self)
{
NMSecretAgentOldPrivate *priv = NM_SECRET_AGENT_OLD_GET_PRIVATE(self);
NMCListElem *elem;
gboolean any_tasks_to_complete = FALSE;
if (!c_list_is_empty(&priv->pending_tasks_register_lst_head)) {
/* add a dummy sentinel. We want to complete all the task we started
* so far, but as we invoke user callbacks, the user might register
* new tasks. Those we don't complete in this run. */
g_object_ref(self);
any_tasks_to_complete = TRUE;
c_list_link_tail(&priv->pending_tasks_register_lst_head,
&nm_c_list_elem_new_stale(&any_tasks_to_complete)->lst);
}
_init_complete(self, NULL);
if (any_tasks_to_complete) {
while (
(elem = c_list_first_entry(&priv->pending_tasks_register_lst_head, NMCListElem, lst))) {
gpointer data = nm_c_list_elem_free_steal(elem);
gs_unref_object GTask *task = NULL;
if (data == &any_tasks_to_complete) {
any_tasks_to_complete = FALSE;
break;
}
task = data;
if (!priv->is_registered) {
g_task_return_error(task,
g_error_new_literal(NM_SECRET_AGENT_ERROR,
NM_SECRET_AGENT_ERROR_FAILED,
_("registration failed")));
continue;
}
g_task_return_boolean(task, TRUE);
}
nm_assert(!any_tasks_to_complete);
g_object_unref(self);
}
}
static void
_register_state_change_do(NMSecretAgentOld *self)
{
NMSecretAgentOldPrivate *priv = NM_SECRET_AGENT_OLD_GET_PRIVATE(self);
if (priv->is_destroyed)
priv->is_enabled = FALSE;
if (!priv->is_enabled || priv->registration_force_unregister
|| priv->name_owner_curr != priv->name_owner_next) {
GetSecretsInfo *info;
while ((info = c_list_first_entry(&priv->gsi_lst_head, GetSecretsInfo, gsi_lst))) {
_cancel_get_secret_request(self, info, "The secret agent is going away");
_register_state_change(self);
return;
}
priv->registration_force_unregister = FALSE;
nm_clear_g_cancellable(&priv->registering_cancellable);
nm_clear_g_source_inst(&priv->registering_retry_source);
if (priv->registered_against_server) {
priv->registered_against_server = FALSE;
if (priv->name_owner_curr) {
_LOGT("register: unregister from %s", priv->name_owner_curr->str);
_dbus_connection_call(self,
priv->name_owner_curr->str,
NM_DBUS_PATH_AGENT_MANAGER,
NM_DBUS_INTERFACE_AGENT_MANAGER,
"Unregister",
g_variant_new("()"),
G_VARIANT_TYPE("()"),
G_DBUS_CALL_FLAGS_NONE,
_CALL_REGISTER_TIMEOUT_MSEC,
NULL,
NULL,
NULL);
}
}
if (!priv->is_enabled) {
nm_clear_g_cancellable(&priv->name_owner_cancellable);
nm_clear_g_dbus_connection_signal(priv->dbus_connection, &priv->name_owner_changed_id);
nm_clear_pointer(&priv->name_owner_curr, nm_ref_string_unref);
nm_clear_pointer(&priv->name_owner_next, nm_ref_string_unref);
}
if (priv->is_registered) {
priv->is_registered = FALSE;
if (!priv->is_destroyed) {
_LOGT("register: now unregistered");
_notify(self, PROP_REGISTERED);
_register_state_change(self);
return;
}
}
if (!priv->is_enabled) {
_register_state_complete(self);
return;
}
if (priv->name_owner_curr != priv->name_owner_next) {
nm_ref_string_unref(priv->name_owner_curr);
priv->name_owner_curr = nm_ref_string_ref(priv->name_owner_next);
}
}
if (priv->name_owner_changed_id == 0) {
nm_assert(!priv->name_owner_cancellable);
nm_assert(!priv->name_owner_curr);
nm_assert(!priv->name_owner_next);
priv->name_owner_cancellable = g_cancellable_new();
priv->name_owner_changed_id =
nm_dbus_connection_signal_subscribe_name_owner_changed(priv->dbus_connection,
NM_DBUS_SERVICE,
_name_owner_changed_cb,
self,
NULL);
nm_dbus_connection_call_get_name_owner(priv->dbus_connection,
NM_DBUS_SERVICE,
-1,
priv->name_owner_cancellable,
_name_owner_get_cb,
self);
return;
}
if (priv->name_owner_cancellable) {
/* we still wait for the name owner. Nothing to do for now. */
return;
}
if (!priv->name_owner_curr) {
/* we don't have a name owner. We are done and wait. */
_register_state_complete(self);
return;
}
if (priv->registering_cancellable) {
/* we are already registering... wait longer. */
return;
}
nm_assert(!priv->registering_retry_source);
if (!priv->is_registered) {
/* start registering... */
priv->registering_cancellable = g_cancellable_new();
_dbus_connection_call(self,
DBUS_SERVICE_DBUS,
DBUS_PATH_DBUS,
DBUS_INTERFACE_DBUS,
"GetConnectionUnixUser",
g_variant_new("(s)", priv->name_owner_curr->str),
G_VARIANT_TYPE("(u)"),
G_DBUS_CALL_FLAGS_NONE,
_CALL_REGISTER_TIMEOUT_MSEC,
priv->registering_cancellable,
_get_connection_unix_user_cb,
self);
return;
}
/* we are fully registered and done. */
_register_state_complete(self);
}
static void
_register_state_change(NMSecretAgentOld *self)
{
_nm_unused gs_unref_object NMSecretAgentOld *self_keep_alive = g_object_ref(self);
NMSecretAgentOldPrivate *priv = NM_SECRET_AGENT_OLD_GET_PRIVATE(self);
nm_auto_pop_gmaincontext GMainContext *dbus_context = NULL;
if (priv->register_state_change_reenter == 0) {
/* We are not yet initialized. Do nothing. */
return;
}
if (priv->register_state_change_reenter != 1) {
/* Recursive calls are prevented. Do nothing for now, but repeat
* the state change afterwards. */
priv->register_state_change_reenter = 3;
return;
}
dbus_context = nm_g_main_context_push_thread_default_if_necessary(priv->dbus_context);
again:
priv->register_state_change_reenter = 2;
_register_state_change_do(self);
if (priv->register_state_change_reenter != 2)
goto again;
priv->register_state_change_reenter = 1;
}
/*****************************************************************************/
static void
_init_complete(NMSecretAgentOld *self, GError *error_take)
{
NMSecretAgentOldPrivate *priv = NM_SECRET_AGENT_OLD_GET_PRIVATE(self);
gs_free_error GError *error = g_steal_pointer(&error_take);
GError *error_cancelled = NULL;
if (!priv->init_data)
return;
if (g_cancellable_set_error_if_cancelled(priv->init_data->cancellable, &error_cancelled)) {
g_clear_error(&error);
g_propagate_error(&error, error_cancelled);
}
priv->is_initialized = (!error);
_LOGT("%s init complete with %s%s%s",
priv->init_data->is_sync ? "sync" : "async",
NM_PRINT_FMT_QUOTED(error_take, "error: ", error_take->message, "", "success"));
nml_init_data_return(g_steal_pointer(&priv->init_data), g_steal_pointer(&error));
}
static void
_init_register_object(NMSecretAgentOld *self)
{
NMSecretAgentOldPrivate *priv = NM_SECRET_AGENT_OLD_GET_PRIVATE(self);
gs_free_error GError *error = NULL;
GDBusInterfaceVTable interface_vtable = {
.method_call = _method_call,
};
if (g_cancellable_set_error_if_cancelled(priv->init_data->cancellable, &error)) {
_init_complete(self, g_steal_pointer(&error));
return;
}
priv->exported_id = g_dbus_connection_register_object(priv->dbus_connection,
NM_DBUS_PATH_SECRET_AGENT,
(GDBusInterfaceInfo *) &interface_info,
&interface_vtable,
self,
NULL,
&error);
if (priv->exported_id == 0) {
_init_complete(self, g_steal_pointer(&error));
return;
}
priv->register_state_change_reenter = 1;
_register_state_change(self);
}
static void
_init_got_bus(GObject *initable, GAsyncResult *result, gpointer user_data)
{
NMSecretAgentOld *self = user_data;
NMSecretAgentOldPrivate *priv = NM_SECRET_AGENT_OLD_GET_PRIVATE(self);
gs_free_error GError *error = NULL;
priv->dbus_connection = g_bus_get_finish(result, &error);
if (!priv->dbus_connection) {
_init_complete(self, g_steal_pointer(&error));
return;
}
_LOGT("init: got GDBusConnection");
_notify(self, PROP_DBUS_CONNECTION);
_init_register_object(self);
}
static void
_init_start(NMSecretAgentOld *self)
{
NMSecretAgentOldPrivate *priv = NM_SECRET_AGENT_OLD_GET_PRIVATE(self);
if (!priv->dbus_connection) {
GBusType bus_type;
bus_type = _nm_dbus_bus_type();
priv->session_bus = (bus_type == G_BUS_TYPE_SESSION);
g_bus_get(bus_type, priv->init_data->cancellable, _init_got_bus, self);
return;
}
_init_register_object(self);
}
static void
init_async(GAsyncInitable *initable,
int io_priority,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
NMSecretAgentOld *self;
NMSecretAgentOldClass *klass;
NMSecretAgentOldPrivate *priv;
nm_auto_pop_gmaincontext GMainContext *dbus_context = NULL;
gs_unref_object GTask *task = NULL;
g_return_if_fail(NM_IS_SECRET_AGENT_OLD(initable));
self = NM_SECRET_AGENT_OLD(initable);
priv = NM_SECRET_AGENT_OLD_GET_PRIVATE(self);
g_return_if_fail(!priv->dbus_context);
g_return_if_fail(!priv->is_destroyed);
klass = NM_SECRET_AGENT_OLD_GET_CLASS(self);
g_return_if_fail(klass->get_secrets);
g_return_if_fail(klass->cancel_get_secrets);
g_return_if_fail(klass->save_secrets);
g_return_if_fail(klass->delete_secrets);
_LOGT("init-async starting...");
priv->dbus_context = g_main_context_ref(priv->main_context);
dbus_context = nm_g_main_context_push_thread_default_if_necessary(priv->dbus_context);
task = nm_g_task_new(self, cancellable, init_async, callback, user_data);
g_task_set_priority(task, io_priority);
priv->init_data = nml_init_data_new_async(cancellable, g_steal_pointer(&task));
_init_start(self);
}
static gboolean
init_finish(GAsyncInitable *initable, GAsyncResult *result, GError **error)
{
g_return_val_if_fail(NM_IS_SECRET_AGENT_OLD(initable), FALSE);
g_return_val_if_fail(nm_g_task_is_valid(result, initable, init_async), FALSE);
return g_task_propagate_boolean(G_TASK(result), error);
}
/*****************************************************************************/
static gboolean
init_sync(GInitable *initable, GCancellable *cancellable, GError **error)
{
gs_unref_object NMSecretAgentOld *self = NULL;
NMSecretAgentOldPrivate *priv;
NMSecretAgentOldClass *klass;
GMainLoop *main_loop;
GError *local_error = NULL;
g_return_val_if_fail(NM_IS_SECRET_AGENT_OLD(initable), FALSE);
self = g_object_ref(NM_SECRET_AGENT_OLD(initable));
priv = NM_SECRET_AGENT_OLD_GET_PRIVATE(self);
g_return_val_if_fail(!priv->dbus_context, FALSE);
g_return_val_if_fail(!priv->is_destroyed, FALSE);
klass = NM_SECRET_AGENT_OLD_GET_CLASS(self);
g_return_val_if_fail(klass->get_secrets, FALSE);
g_return_val_if_fail(klass->cancel_get_secrets, FALSE);
g_return_val_if_fail(klass->save_secrets, FALSE);
g_return_val_if_fail(klass->delete_secrets, FALSE);
_LOGT("init-sync");
/* See NMClient's sync-init method for explanation about why we create
* an internal GMainContext priv->dbus_context. */
priv->dbus_context = g_main_context_new();
g_main_context_push_thread_default(priv->dbus_context);
main_loop = g_main_loop_new(priv->dbus_context, FALSE);
priv->init_data = nml_init_data_new_sync(cancellable, main_loop, &local_error);
_init_start(self);
g_main_loop_run(main_loop);
g_main_loop_unref(main_loop);
g_main_context_pop_thread_default(priv->dbus_context);
nm_context_busy_watcher_integrate_source(priv->main_context,
priv->dbus_context,
priv->context_busy_watcher);
if (local_error) {
g_propagate_error(error, local_error);
return FALSE;
}
return TRUE;
}
/*****************************************************************************/
static void
get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
{
NMSecretAgentOldPrivate *priv = NM_SECRET_AGENT_OLD_GET_PRIVATE(object);
switch (prop_id) {
case PROP_DBUS_CONNECTION:
g_value_set_object(value, priv->dbus_connection);
break;
case PROP_IDENTIFIER:
g_value_set_string(value, priv->identifier);
break;
case PROP_AUTO_REGISTER:
g_value_set_boolean(value, priv->auto_register);
break;
case PROP_REGISTERED:
g_value_set_boolean(value, priv->is_registered);
break;
case PROP_CAPABILITIES:
g_value_set_flags(value, priv->capabilities);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
break;
}
}
static void
set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
{
NMSecretAgentOld *self = NM_SECRET_AGENT_OLD(object);
NMSecretAgentOldPrivate *priv = NM_SECRET_AGENT_OLD_GET_PRIVATE(self);
guint u;
switch (prop_id) {
case PROP_DBUS_CONNECTION:
/* construct-only */
priv->dbus_connection = g_value_dup_object(value);
break;
case PROP_IDENTIFIER:
/* construct-only */
priv->identifier = g_value_dup_string(value);
g_return_if_fail(validate_identifier(priv->identifier));
break;
case PROP_AUTO_REGISTER:
/* construct */
priv->auto_register = g_value_get_boolean(value);
priv->is_enabled = priv->auto_register;
_register_state_change(self);
break;
case PROP_CAPABILITIES:
/* construct */
u = g_value_get_flags(value);
if (u != priv->capabilities) {
priv->capabilities = u;
priv->registration_force_unregister = TRUE;
_register_state_change(self);
}
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
break;
}
}
/*****************************************************************************/
static void
nm_secret_agent_old_init(NMSecretAgentOld *self)
{
NMSecretAgentOldPrivate *priv = NM_SECRET_AGENT_OLD_GET_PRIVATE(self);
_LOGT("create new instance");
c_list_init(&priv->gsi_lst_head);
c_list_init(&priv->pending_tasks_register_lst_head);
priv->main_context = g_main_context_ref_thread_default();
priv->context_busy_watcher = g_object_new(G_TYPE_OBJECT, NULL);
}
static void
dispose(GObject *object)
{
NMSecretAgentOld *self = NM_SECRET_AGENT_OLD(object);
_LOGT("disposing");
_secret_agent_old_destroy(self);
G_OBJECT_CLASS(nm_secret_agent_old_parent_class)->dispose(object);
}
static void
finalize(GObject *object)
{
NMSecretAgentOld *self = NM_SECRET_AGENT_OLD(object);
NMSecretAgentOldPrivate *priv = NM_SECRET_AGENT_OLD_GET_PRIVATE(self);
_LOGT("finalizing");
if (priv->dbus_context) {
nml_cleanup_context_busy_watcher_on_idle(g_steal_pointer(&priv->context_busy_watcher),
priv->dbus_context);
}
g_clear_object(&priv->dbus_connection);
nm_clear_pointer(&priv->dbus_context, g_main_context_unref);
nm_clear_pointer(&priv->main_context, g_main_context_unref);
g_clear_object(&priv->context_busy_watcher);
g_free(priv->identifier);
G_OBJECT_CLASS(nm_secret_agent_old_parent_class)->finalize(object);
}
static void
nm_secret_agent_old_class_init(NMSecretAgentOldClass *class)
{
GObjectClass *object_class = G_OBJECT_CLASS(class);
g_type_class_add_private(class, sizeof(NMSecretAgentOldPrivate));
object_class->get_property = get_property;
object_class->set_property = set_property;
object_class->dispose = dispose;
object_class->finalize = finalize;
/**
* NMSecretAgentOld:dbus-connection:
*
* The #GDBusConnection used by the instance. You may either set this
* as construct-only property, or otherwise #NMSecretAgentOld will choose
* a connection via g_bus_get() during initialization.
*
* Since: 1.24
**/
obj_properties[PROP_DBUS_CONNECTION] =
g_param_spec_object(NM_SECRET_AGENT_OLD_DBUS_CONNECTION,
"",
"",
G_TYPE_DBUS_CONNECTION,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
/**
* NMSecretAgentOld:identifier:
*
* Identifies this agent; only one agent in each user session may use the
* same identifier. Identifier formatting follows the same rules as
* D-Bus bus names with the exception that the ':' character is not
* allowed. The valid set of characters is "[A-Z][a-z][0-9]_-." and the
* identifier is limited in length to 255 characters with a minimum
* of 3 characters. An example valid identifier is 'org.gnome.nm-applet'
* (without quotes).
**/
obj_properties[PROP_IDENTIFIER] =
g_param_spec_string(NM_SECRET_AGENT_OLD_IDENTIFIER,
"",
"",
NULL,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
/**
* NMSecretAgentOld:auto-register:
*
* If %TRUE (the default), the agent will always be registered when
* NetworkManager is running; if NetworkManager exits and restarts, the
* agent will re-register itself automatically.
*
* In particular, if this property is %TRUE at construct time, then the
* agent will register itself with NetworkManager during
* construction/initialization and initialization will only complete
* after registration is completed (either successfully or unsuccessfully).
* Since 1.24, a failure to register will no longer cause initialization
* of #NMSecretAgentOld to fail.
*
* If the property is %FALSE, the agent will not automatically register with
* NetworkManager, and nm_secret_agent_old_enable() or
* nm_secret_agent_old_register_async() must be called to register it.
*
* Calling nm_secret_agent_old_enable() has the same effect as setting this
* property.
**/
obj_properties[PROP_AUTO_REGISTER] =
g_param_spec_boolean(NM_SECRET_AGENT_OLD_AUTO_REGISTER,
"",
"",
TRUE,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS);
/**
* NMSecretAgentOld:registered:
*
* %TRUE if the agent is registered with NetworkManager, %FALSE if not.
**/
obj_properties[PROP_REGISTERED] =
g_param_spec_boolean(NM_SECRET_AGENT_OLD_REGISTERED,
"",
"",
FALSE,
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
/**
* NMSecretAgentOld:capabilities:
*
* A bitfield of %NMSecretAgentCapabilities.
*
* Changing this property is possible at any time. In case the secret
* agent is currently registered, this will cause a re-registration.
**/
obj_properties[PROP_CAPABILITIES] =
g_param_spec_flags(NM_SECRET_AGENT_OLD_CAPABILITIES,
"",
"",
NM_TYPE_SECRET_AGENT_CAPABILITIES,
NM_SECRET_AGENT_CAPABILITY_NONE,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS);
g_object_class_install_properties(object_class, _PROPERTY_ENUMS_LAST, obj_properties);
}
static void
nm_secret_agent_old_initable_iface_init(GInitableIface *iface)
{
iface->init = init_sync;
}
static void
nm_secret_agent_old_async_initable_iface_init(GAsyncInitableIface *iface)
{
iface->init_async = init_async;
iface->init_finish = init_finish;
}