summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThomas Haller <thaller@redhat.com>2019-12-24 13:26:50 +0100
committerThomas Haller <thaller@redhat.com>2020-01-20 12:45:33 +0100
commitda43a1fdc50b3a1ce3a56b4a57ddb3186702da56 (patch)
tree02f2d603811a194f4d88d12eaa83ee2a19acc5b1
parent11555f30bdf3c9b819bd5c39c195138aeadc9c7b (diff)
downloadNetworkManager-th/secret-agent-rework.tar.gz
libnm/secret-agent: rework NMSecretAgentOldth/secret-agent-rework
Note that the name "NMSecretAgentOld" comes from when libnm was forked from libnm-glib. There was a plan to rework the secret agent API and replace it by a better one. That didn't happen (yet), instead our one and only agent implementation is still lacking. Don't add a new API, instead try to improve the existing one, without breaking existing users. Just get over the fact that the name "NMSecretAgentOld" is ugly. Also note how nm-applet uses NMSecretAgentOld. It subtypes a class AppletAgent. The constructor applet_agent_new() is calling the synchronous g_initable_init() initialization with auto-register enabled. As it was, g_initable_init() would call nm_secret_agent_old_register(), and if the "Register" call failed, initialization failed for good. There are even unit tests that test this behavior. This is bad behavior. It means, when you start nm-applet without NetworkManager running, it will fail to create the AppletAgent instance. It would hence be the responsibility of the applet to recover from this situation (e.g. by retrying after timeout or watching the D-Bus name owner). Of course, nm-applet doesn't do that and won't recover from such a failure. NMSecretAgentOld must try hard not to fail and recover automatically. The user of the API is not interested in implementing the registration, unregistration and retry handling. Instead, it should just work best effort and transparently to the user of the API. Differences: - no longer use gdbus-codegen generate bindings. Use GDBusConnection directly instead. These generated proxies complicate the code by introducing an additional, stateful layer. - properly handle GMainContext and synchronous initialization by using an internal GMainContext. With this NMSecretAgentOld can be used in a multi threaded context with separate GMainContext. This does not mean that the object itself became thread safe, but that the GMainContext gives the means to coordinate multi-threaded access. - there are no more blocking calls except g_initiable_init() which iterates an internal GMainContext until initialization completes. - obtaining the Unix user ID with "GetConnectionUnixUser" to authenticate the server is now done asynchronously and only once per name-owner. - NMSecretAgentOld will now register/export the Agent D-Bus object already during initialization and stay registered as long as the instance is alive. This is because usually registering a D-Bus object would not fail, unless the D-Bus path is already taken. Such an error would mean that another agent is registered for the same GDBusConnection, that likely would be a bug in the caller. Hence, such an issue is truly non-recoverable and should be reported early to the user. There is a change in behavior compared to before, where previously the D-Bus object would only be registered while the instance is enabled. This makes a difference if the user intended to keep the NMSecretAgentOld instance around in an unregistered state. Note that nm_secret_agent_old_destroy() was added to really unregister the D-Bus object. A destroyed instance can no longer be registered. - the API no longer fully exposes the current registration state. The user either enables or disables the agent. Then, in the background NMSecretAgentOld will register, and serve requests as they come. It will also always automatically re-register and it can de-facto no longer fail. That is, there might be a failure to register, or the NetworkManager peer might not be authenticated (non-root) or there might be some other error, or NetworkManager might not be running. But such errors are not exposed to the user. The instance is just not able to provide the secrets in those cases, but it may recover if the problem can be resolved. - In particular, it makes no sense that nm_secret_agent_old_register*() fails, returns an error, or waits until registration is complete. This API is now only to enable/disable the agent. It is idempotent and won't fail (there is a catch, see next point). In particular, nm_secret_agent_old_unregister*() cannot fail anymore. - However, with the previous point there is a problem/race. When you create a NMSecretAgentOld instance and immediately afterwards activate a profile, then you want to be sure that the registration is complete first. Otherwise, NetworkManager might fail the activation because no secret agent registered yet. A partial solution for this is that g_initiable_init()/g_async_initable_init_async() will block until registration is complete (or with or without success). That means, if NetworkManager is running, initializing the NMSecretAgentOld will wait until registration is complete (or failed). However, that does not solve the race if NetworkManager was not running when creating the instance. To solve that race, the user may call nm_secret_agent_old_register_async() and wait for the command to finish before starting activating. While async registration no longer fails (in the sense of leaving the agent permanently disconnected), it will try to ensure that we are successfully registered and ready to serve requests. By using this API correctly, a race can be avoided and the user can know that the instance is now ready to serve request.
-rw-r--r--libnm/libnm.ver4
-rw-r--r--libnm/nm-secret-agent-old.c1845
-rw-r--r--libnm/nm-secret-agent-old.h53
-rw-r--r--libnm/tests/test-secret-agent.c187
-rw-r--r--po/POTFILES.in1
5 files changed, 1369 insertions, 721 deletions
diff --git a/libnm/libnm.ver b/libnm/libnm.ver
index 292dbcf826..457b55a798 100644
--- a/libnm/libnm.ver
+++ b/libnm/libnm.ver
@@ -1667,7 +1667,11 @@ global:
nm_device_vrf_get_table;
nm_device_vrf_get_type;
nm_object_get_client;
+ nm_secret_agent_old_destroy;
+ nm_secret_agent_old_enable;
+ nm_secret_agent_old_get_context_busy_watcher;
nm_secret_agent_old_get_dbus_connection;
+ nm_secret_agent_old_get_dbus_name_owner;
nm_secret_agent_old_get_main_context;
nm_setting_vrf_get_table;
nm_setting_vrf_get_type;
diff --git a/libnm/nm-secret-agent-old.c b/libnm/nm-secret-agent-old.c
index 11cca56ff9..eb653327d0 100644
--- a/libnm/nm-secret-agent-old.c
+++ b/libnm/nm-secret-agent-old.c
@@ -12,22 +12,22 @@
#include "nm-dbus-helpers.h"
#include "nm-dbus-interface.h"
#include "nm-enum-types.h"
+#include "nm-glib-aux/nm-c-list.h"
#include "nm-glib-aux/nm-dbus-aux.h"
#include "nm-glib-aux/nm-time-utils.h"
#include "nm-simple-connection.h"
-#include "introspection/org.freedesktop.NetworkManager.SecretAgent.h"
-#include "introspection/org.freedesktop.NetworkManager.AgentManager.h"
-
-#define REGISTER_RETRY_TIMEOUT_MSEC 2000
+#define REGISTER_RETRY_TIMEOUT_MSEC 3000
+#define _CALL_REGISTER_TIMEOUT_MSEC 15000
/*****************************************************************************/
typedef struct {
- char *path;
+ char *connection_path;
char *setting_name;
GDBusMethodInvocation *context;
CList gsi_lst;
+ bool is_cancelling:1;
} GetSecretsInfo;
NM_GOBJECT_PROPERTIES_DEFINE (NMSecretAgentOld,
@@ -41,23 +41,55 @@ NM_GOBJECT_PROPERTIES_DEFINE (NMSecretAgentOld,
typedef struct {
GDBusConnection *dbus_connection;
GMainContext *main_context;
- NMDBusAgentManager *manager_proxy;
- NMDBusSecretAgent *dbus_secret_agent;
+ GMainContext *dbus_context;
+ GObject *context_busy_watcher;
+ GCancellable *name_owner_cancellable;
+ GCancellable *registering_cancellable;
+ GSource *registering_retry_source;
+
+ NMLInitData *init_data;
- /* GetSecretsInfo structs of in-flight GetSecrets requests */
CList gsi_lst_head;
+ CList pending_tasks_register_lst_head;
+
char *identifier;
- NMSecretAgentCapabilities capabilities;
+ NMRefString *name_owner_curr;
+ NMRefString *name_owner_next;
gint64 registering_timeout_msec;
- guint registering_try_count;
- bool registered:1;
+ 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 suppress_auto: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);
@@ -68,7 +100,7 @@ G_DEFINE_ABSTRACT_TYPE_WITH_CODE (NMSecretAgentOld, nm_secret_agent_old, G_TYPE_
G_IMPLEMENT_INTERFACE (G_TYPE_ASYNC_INITABLE, nm_secret_agent_old_async_initable_iface_init);
)
-#define NM_SECRET_AGENT_OLD_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SECRET_AGENT_OLD, NMSecretAgentOldPrivate))
+#define NM_SECRET_AGENT_OLD_GET_PRIVATE(self) (G_TYPE_INSTANCE_GET_PRIVATE ((self), NM_TYPE_SECRET_AGENT_OLD, NMSecretAgentOldPrivate))
/*****************************************************************************/
@@ -80,9 +112,55 @@ G_DEFINE_ABSTRACT_TYPE_WITH_CODE (NMSecretAgentOld, nm_secret_agent_old, G_TYPE_
/*****************************************************************************/
-static void _register_call_cb (GObject *proxy,
- GAsyncResult *result,
- gpointer user_data);
+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);
/*****************************************************************************/
@@ -122,156 +200,201 @@ nm_secret_agent_old_get_main_context (NMSecretAgentOld *self)
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);
-static void
-_internal_unregister (NMSecretAgentOld *self)
+ 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 successfull.
+ *
+ * Since: 1.24
+ */
+const char *
+nm_secret_agent_old_get_dbus_name_owner (NMSecretAgentOld *self)
{
- NMSecretAgentOldPrivate *priv = NM_SECRET_AGENT_OLD_GET_PRIVATE (self);
+ g_return_val_if_fail (NM_IS_SECRET_AGENT_OLD (self), NULL);
- if (priv->registered) {
- g_dbus_interface_skeleton_unexport (G_DBUS_INTERFACE_SKELETON (priv->dbus_secret_agent));
- priv->registered = FALSE;
- priv->registering_timeout_msec = 0;
- _notify (self, PROP_REGISTERED);
- }
+ 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_secrets_info_free (GetSecretsInfo *info)
+get_secret_info_free (GetSecretsInfo *info)
{
nm_assert (info);
+ nm_assert (!info->context);
c_list_unlink_stale (&info->gsi_lst);
-
- g_free (info->path);
+ g_free (info->connection_path);
g_free (info->setting_name);
- g_slice_free (GetSecretsInfo, info);
+ nm_g_slice_free (info);
}
-static gboolean
-should_auto_register (NMSecretAgentOld *self)
+static void
+get_secret_info_complete_and_free (GetSecretsInfo *info,
+ GVariant *secrets,
+ GError *error)
{
- NMSecretAgentOldPrivate *priv = NM_SECRET_AGENT_OLD_GET_PRIVATE (self);
-
- return ( priv->auto_register
- && !priv->suppress_auto
- && !priv->registered
- && priv->registering_timeout_msec == 0);
+ 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
-name_owner_changed (GObject *proxy,
- GParamSpec *pspec,
- gpointer user_data)
+get_secret_info_complete_and_free_error (GetSecretsInfo *info,
+ GQuark error_domain,
+ int error_code,
+ const char *error_message)
{
- NMSecretAgentOld *self = NM_SECRET_AGENT_OLD (user_data);
- NMSecretAgentOldPrivate *priv = NM_SECRET_AGENT_OLD_GET_PRIVATE (self);
- gs_free char *owner = NULL;
- GetSecretsInfo *info;
-
- owner = g_dbus_proxy_get_name_owner (G_DBUS_PROXY (proxy));
+ g_dbus_method_invocation_return_error_literal (g_steal_pointer (&info->context), error_domain, error_code, error_message);
+ get_secret_info_free (info);
+}
- _LOGT ("name owner changed: %s%s%s", NM_PRINT_FMT_QUOTE_STRING (owner));
+/*****************************************************************************/
- if (owner) {
- if (should_auto_register (self))
- nm_secret_agent_old_register_async (self, NULL, NULL, NULL);
- } else {
- while ((info = c_list_first_entry (&priv->gsi_lst_head, GetSecretsInfo, gsi_lst))) {
- c_list_unlink (&info->gsi_lst);
- NM_SECRET_AGENT_OLD_GET_CLASS (self)->cancel_get_secrets (self,
- info->path,
- info->setting_name);
- }
+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;
- _internal_unregister (self);
- }
+ nm_utils_user_data_unpack (user_data, &context_busy_watcher, &callback, &callback_user_data);
+ callback (source, result, callback_user_data);
}
-static gboolean
-verify_sender (NMSecretAgentOld *self,
- GDBusMethodInvocation *context,
- GError **error)
+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);
- gs_free char *owner = NULL;
- const char *sender;
- guint32 sender_uid;
- gs_unref_variant GVariant *ret = NULL;
- gs_free_error GError *local = NULL;
- g_return_val_if_fail (context != NULL, FALSE);
+ 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);
+}
- /* Verify that the sender is the same as NetworkManager's bus name owner. */
+/*****************************************************************************/
- owner = g_dbus_proxy_get_name_owner (G_DBUS_PROXY (priv->manager_proxy));
- if (!owner) {
- g_set_error_literal (error,
- NM_SECRET_AGENT_ERROR,
- NM_SECRET_AGENT_ERROR_PERMISSION_DENIED,
- "NetworkManager bus name owner unknown.");
- return FALSE;
- }
+static GetSecretsInfo *
+find_get_secrets_info (NMSecretAgentOldPrivate *priv,
+ const char *connection_path,
+ const char *setting_name)
+{
+ GetSecretsInfo *info;
- sender = g_dbus_method_invocation_get_sender (context);
- if (!sender) {
- g_set_error_literal (error,
- NM_SECRET_AGENT_ERROR,
- NM_SECRET_AGENT_ERROR_PERMISSION_DENIED,
- "Failed to get request sender.");
- return FALSE;
+ 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;
+}
- if (!nm_streq (sender, owner)) {
- g_set_error_literal (error,
- NM_SECRET_AGENT_ERROR,
- NM_SECRET_AGENT_ERROR_PERMISSION_DENIED,
- "Request sender does not match NetworkManager bus name owner.");
- return FALSE;
- }
+static void
+_cancel_get_secret_request (NMSecretAgentOld *self,
+ GetSecretsInfo *info,
+ const char *message)
+{
+ c_list_unlink (&info->gsi_lst);
+ info->is_cancelling = TRUE;
- /* If we're connected to the session bus, then this must be a test program,
- * so skip the UID check.
- */
- if (priv->session_bus)
- return TRUE;
-
- /* Check the UID of the sender */
- ret = g_dbus_connection_call_sync (priv->dbus_connection,
- DBUS_SERVICE_DBUS,
- DBUS_PATH_DBUS,
- DBUS_INTERFACE_DBUS,
- "GetConnectionUnixUser",
- g_variant_new ("(s)", sender),
- G_VARIANT_TYPE ("(u)"),
- G_DBUS_CALL_FLAGS_NONE, -1,
- NULL, &local);
- if (!ret) {
- gs_free char *remote_error = NULL;
-
- remote_error = g_dbus_error_get_remote_error (local);
- g_dbus_error_strip_remote_error (local);
- g_set_error (error,
- NM_SECRET_AGENT_ERROR,
- NM_SECRET_AGENT_ERROR_PERMISSION_DENIED,
- "Failed to request unix user: (%s) %s.",
- remote_error ?: "",
- local->message);
- return FALSE;
- }
- g_variant_get (ret, "(u)", &sender_uid);
+ _LOGT ("cancel get-secrets request \"%s\", \"%s\": %s", info->connection_path, info->setting_name, message);
- /* We only accept requests from NM, which always runs as root */
- if (sender_uid != 0) {
- g_set_error_literal (error,
- NM_SECRET_AGENT_ERROR,
- NM_SECRET_AGENT_ERROR_PERMISSION_DENIED,
- "Request sender is not root.");
- return FALSE;
- }
+ NM_SECRET_AGENT_OLD_GET_CLASS (self)->cancel_get_secrets (self,
+ info->connection_path,
+ info->setting_name);
- return TRUE;
+ get_secret_info_complete_and_free_error (info,
+ NM_SECRET_AGENT_ERROR,
+ NM_SECRET_AGENT_ERROR_AGENT_CANCELED,
+ message);
}
static gboolean
@@ -285,15 +408,7 @@ verify_request (NMSecretAgentOld *self,
gs_unref_object NMConnection *connection = NULL;
gs_free_error GError *local = NULL;
- if (!verify_sender (self, context, error))
- return FALSE;
-
- /* No connection? If the sender verified, then we allow the request */
- if (connection_dict == NULL)
- return TRUE;
-
- /* If we have a connection dictionary, we require a path too */
- if (connection_path == NULL) {
+ if (!nm_dbus_path_not_empty (connection_path)) {
g_set_error_literal (error,
NM_SECRET_AGENT_ERROR,
NM_SECRET_AGENT_ERROR_INVALID_CONNECTION,
@@ -301,7 +416,6 @@ verify_request (NMSecretAgentOld *self,
return FALSE;
}
- /* Make sure the given connection is valid */
connection = _nm_simple_connection_new_from_dbus (connection_dict, NM_SETTING_PARSE_FLAGS_BEST_EFFORT, &local);
if (!connection) {
g_set_error (error,
@@ -325,102 +439,99 @@ get_secrets_cb (NMSecretAgentOld *self,
{
GetSecretsInfo *info = user_data;
- if (error)
- g_dbus_method_invocation_return_gerror (info->context, error);
- else {
- g_variant_take_ref (secrets);
- g_dbus_method_invocation_return_value (info->context,
- g_variant_new ("(@a{sa{sv}})", secrets));
+ if (info->is_cancelling) {
+ if (secrets)
+ nm_g_variant_unref_floating (secrets);
+ return;
}
- get_secrets_info_free (info);
+ _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_secret_agent_old_get_secrets (NMSecretAgentOld *self,
- GDBusMethodInvocation *context,
- GVariant *connection_dict,
- const char *connection_path,
- const char *setting_name,
- const char * const *hints,
- guint flags,
- gpointer user_data)
+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;
-
- /* Make sure the request comes from NetworkManager and is valid */
- if (!verify_request (self, context, connection_dict, connection_path, &connection, &error)) {
+ 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) {
- .path = g_strdup (connection_path),
- .setting_name = g_strdup (setting_name),
- .context = context,
+ .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,
- connection_path,
- setting_name,
- (const char **) hints,
- flags,
+ info->connection_path,
+ info->setting_name,
+ arg_hints,
+ arg_flags,
get_secrets_cb,
info);
}
-static GetSecretsInfo *
-find_get_secrets_info (NMSecretAgentOldPrivate *priv,
- const char *path,
- const char *setting_name)
-{
- GetSecretsInfo *info;
-
- c_list_for_each_entry (info, &priv->gsi_lst_head, gsi_lst) {
- if ( nm_streq0 (path, info->path)
- && nm_streq0 (setting_name, info->setting_name))
- return info;
- }
- return NULL;
-}
-
static void
-impl_secret_agent_old_cancel_get_secrets (NMSecretAgentOld *self,
- GDBusMethodInvocation *context,
- const char *connection_path,
- const char *setting_name,
- gpointer user_data)
+impl_cancel_get_secrets (NMSecretAgentOld *self,
+ GVariant *parameters,
+ GDBusMethodInvocation *context)
{
NMSecretAgentOldPrivate *priv = NM_SECRET_AGENT_OLD_GET_PRIVATE (self);
- GError *error = NULL;
GetSecretsInfo *info;
+ const char *arg_connection_path;
+ const char *arg_setting_name;
- /* Make sure the request comes from NetworkManager and is valid */
- if (!verify_request (self, context, NULL, NULL, NULL, &error)) {
- g_dbus_method_invocation_take_error (context, error);
- return;
- }
+ g_variant_get (parameters,
+ "(&o&s)",
+ &arg_connection_path,
+ &arg_setting_name);
- info = find_get_secrets_info (priv, connection_path, setting_name);
+ info = find_get_secrets_info (priv, arg_connection_path, arg_setting_name);
if (!info) {
- g_dbus_method_invocation_return_error (context,
- NM_SECRET_AGENT_ERROR,
- NM_SECRET_AGENT_ERROR_FAILED,
- "No secrets request in progress for this connection.");
+ 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;
}
- c_list_unlink (&info->gsi_lst);
-
- NM_SECRET_AGENT_OLD_GET_CLASS (self)->cancel_get_secrets (self,
- info->path,
- info->setting_name);
+ _cancel_get_secret_request (self,
+ info,
+ "Request cancelled by NetworkManager");
g_dbus_method_invocation_return_value (context, NULL);
}
@@ -440,24 +551,30 @@ save_secrets_cb (NMSecretAgentOld *self,
}
static void
-impl_secret_agent_old_save_secrets (NMSecretAgentOld *self,
- GDBusMethodInvocation *context,
- GVariant *connection_dict,
- const char *connection_path,
- gpointer user_data)
+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;
- /* Make sure the request comes from NetworkManager and is valid */
- if (!verify_request (self, context, connection_dict, connection_path, &connection, &error)) {
+ 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,
- connection_path,
+ arg_connection_path,
save_secrets_cb,
context);
}
@@ -477,64 +594,124 @@ delete_secrets_cb (NMSecretAgentOld *self,
}
static void
-impl_secret_agent_old_delete_secrets (NMSecretAgentOld *self,
- GDBusMethodInvocation *context,
- GVariant *connection_dict,
- const char *connection_path,
- gpointer user_data)
+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;
- /* Make sure the request comes from NetworkManager and is valid */
- if (!verify_request (self, context, connection_dict, connection_path, &connection, &error)) {
+ 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,
- connection_path,
+ arg_connection_path,
delete_secrets_cb,
context);
}
/*****************************************************************************/
-static gboolean
-check_nm_running (NMSecretAgentOld *self, GError **error)
+/**
+ * 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 = NM_SECRET_AGENT_OLD_GET_PRIVATE (self);
- gs_free char *owner = NULL;
+ NMSecretAgentOldPrivate *priv;
+
+ g_return_if_fail (NM_IS_SECRET_AGENT_OLD (self));
+
+ priv = NM_SECRET_AGENT_OLD_GET_PRIVATE (self);
- owner = g_dbus_proxy_get_name_owner (G_DBUS_PROXY (priv->manager_proxy));
- if (owner)
- return TRUE;
+ enable = (!!enable);
- g_set_error (error, NM_SECRET_AGENT_ERROR, NM_SECRET_AGENT_ERROR_FAILED,
- "NetworkManager is not running");
- return FALSE;
+ 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);
-static gboolean
-_register_should_retry (NMSecretAgentOldPrivate *priv,
- guint *out_timeout_msec)
+ 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)
{
- guint timeout_msec;
+ g_return_if_fail (NM_IS_SECRET_AGENT_OLD (self));
- if (priv->registering_try_count++ == 0)
- timeout_msec = 0;
- else if (nm_utils_get_monotonic_timestamp_msec () < priv->registering_timeout_msec)
- timeout_msec = 1ULL * (1ULL << NM_MIN (7, priv->registering_try_count));
- else
- return FALSE;
+ _LOGT ("destroying");
- *out_timeout_msec = timeout_msec;
- return TRUE;
+ _secret_agent_old_destroy (self);
}
+/*****************************************************************************/
+
/**
* nm_secret_agent_old_register:
* @self: a #NMSecretAgentOld
@@ -545,10 +722,17 @@ _register_should_retry (NMSecretAgentOldPrivate *priv,
* indicating to NetworkManager that the agent is able to provide and save
* secrets for connections on behalf of its user.
*
- * It is a programmer error to attempt to register an agent that is already
- * registered, or in the process of registering.
- *
* 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,
@@ -556,206 +740,59 @@ nm_secret_agent_old_register (NMSecretAgentOld *self,
GError **error)
{
NMSecretAgentOldPrivate *priv;
- NMSecretAgentOldClass *class;
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->registered == FALSE, FALSE);
- g_return_val_if_fail (priv->registering_timeout_msec == 0, FALSE);
- g_return_val_if_fail (priv->dbus_connection != NULL, FALSE);
- g_return_val_if_fail (priv->manager_proxy != NULL, FALSE);
+ g_return_val_if_fail (priv->is_initialized && !priv->is_destroyed, FALSE);
- /* Also make sure the subclass can actually respond to secrets requests */
- class = NM_SECRET_AGENT_OLD_GET_CLASS (self);
- g_return_val_if_fail (class->get_secrets != NULL, FALSE);
- g_return_val_if_fail (class->save_secrets != NULL, FALSE);
- g_return_val_if_fail (class->delete_secrets != NULL, FALSE);
+ priv->is_enabled = TRUE;
+ _register_state_change (self);
- if (!check_nm_running (self, error))
+ if (g_cancellable_set_error_if_cancelled (cancellable, error))
return FALSE;
- priv->suppress_auto = FALSE;
-
- /* Export our secret agent interface before registering with the manager */
- if (!g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (priv->dbus_secret_agent),
- priv->dbus_connection,
- NM_DBUS_PATH_SECRET_AGENT,
- error))
- return FALSE;
-
- priv->registering_timeout_msec = nm_utils_get_monotonic_timestamp_msec () + REGISTER_RETRY_TIMEOUT_MSEC;
- priv->registering_try_count = 0;
-
- while (TRUE) {
- gs_free_error GError *local = NULL;
-
- nmdbus_agent_manager_call_register_with_capabilities_sync (priv->manager_proxy,
- priv->identifier,
- priv->capabilities,
- cancellable,
- &local);
- if (nm_dbus_error_is (local, NM_DBUS_ERROR_NAME_UNKNOWN_METHOD)) {
- guint timeout_msec;
-
- if (_register_should_retry (priv, &timeout_msec)) {
- if (timeout_msec > 0)
- g_usleep (timeout_msec * 1000LU);
- continue;
- }
- }
-
- priv->registering_timeout_msec = 0;
-
- if (local) {
- g_dbus_error_strip_remote_error (local);
- g_propagate_error (error, g_steal_pointer (&local));
- _internal_unregister (self);
- return FALSE;
- }
-
- priv->registered = TRUE;
- _notify (self, PROP_REGISTERED);
- return TRUE;
- }
-}
-
-/*****************************************************************************/
-
-typedef struct {
- GCancellable *cancellable;
- GSource *timeout_source;
- gulong cancellable_signal_id;
-} RegisterData;
-
-static void
-_register_data_free (RegisterData *register_data)
-{
- nm_clear_g_cancellable_disconnect (register_data->cancellable, &register_data->cancellable_signal_id);
- nm_clear_g_source_inst (&register_data->timeout_source);
- g_clear_object (&register_data->cancellable);
- nm_g_slice_free (register_data);
-}
-
-static gboolean
-_register_retry_cb (gpointer user_data)
-{
- gs_unref_object GTask *task = user_data;
- NMSecretAgentOld *self = g_task_get_source_object (task);
- NMSecretAgentOldPrivate *priv = NM_SECRET_AGENT_OLD_GET_PRIVATE (self);
- GCancellable *cancellable;
-
- _LOGT ("register: retry registration...");
-
- g_task_set_task_data (task, NULL, NULL);
-
- cancellable = g_task_get_cancellable (task);
-
- nmdbus_agent_manager_call_register_with_capabilities (priv->manager_proxy,
- priv->identifier,
- priv->capabilities,
- cancellable,
- _register_call_cb,
- g_steal_pointer (&task));
- return G_SOURCE_REMOVE;
-}
-
-static void
-_register_cancelled_cb (GCancellable *cancellable,
- gpointer user_data)
-{
- gs_unref_object GTask *task = user_data;
- NMSecretAgentOld *self = g_task_get_source_object (task);
- RegisterData *register_data = g_task_get_task_data (task);
- GError *error = NULL;
-
- nm_clear_g_signal_handler (register_data->cancellable, &register_data->cancellable_signal_id);
- g_task_set_task_data (task, NULL, NULL);
-
- _LOGT ("register: registration cancelled. Stop waiting...");
-
- nm_utils_error_set_cancelled (&error, FALSE, NULL);
- g_task_return_error (task, error);
+ /* 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_call_cb (GObject *proxy,
- GAsyncResult *result,
- gpointer user_data)
+_register_cancelled_cb (GObject *object, gpointer user_data)
{
- gs_unref_object GTask *task = user_data;
- NMSecretAgentOld *self = g_task_get_source_object (task);
+ 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;
- nmdbus_agent_manager_call_register_with_capabilities_finish (NMDBUS_AGENT_MANAGER (proxy), result, &error);
-
- if (nm_utils_error_is_cancelled (error, FALSE)) {
- /* FIXME: we should unregister right away. For now, don't do that, likely the
- * application is anyway about to exit. */
- } else if (nm_dbus_error_is (error, NM_DBUS_ERROR_NAME_UNKNOWN_METHOD)) {
- gboolean already_cancelled = FALSE;
- RegisterData *register_data;
- guint timeout_msec;
-
- if (!_register_should_retry (priv, &timeout_msec))
- goto done;
-
- _LOGT ("register: registration failed with error \"%s\". Retry in %u msec...", error->message, timeout_msec);
- nm_assert (G_IS_TASK (task));
- nm_assert (!g_task_get_task_data (task));
-
- register_data = g_slice_new (RegisterData);
-
- *register_data = (RegisterData) {
- .cancellable = nm_g_object_ref (g_task_get_cancellable (task)),
- };
-
- g_task_set_task_data (task,
- register_data,
- (GDestroyNotify) _register_data_free);
-
- if (register_data->cancellable) {
- register_data->cancellable_signal_id = g_cancellable_connect (register_data->cancellable,
- G_CALLBACK (_register_cancelled_cb),
- task,
- NULL);
- if (register_data->cancellable_signal_id == 0)
- already_cancelled = TRUE;
- }
+ elem = nm_c_list_elem_find_first (&priv->pending_tasks_register_lst_head, x, x == task0);
- if (!already_cancelled) {
- register_data->timeout_source = nm_g_source_attach (nm_g_timeout_source_new (timeout_msec,
- g_task_get_priority (task),
- _register_retry_cb,
- task,
- NULL),
- g_task_get_context (task));
- }
-
- /* The reference of the task is owned by the _register_cancelled_cb and _register_retry_cb actions.
- * Whichever completes first, will consume it. */
- g_steal_pointer (&task);
- return;
- }
+ g_return_if_fail (elem);
-done:
- priv->registering_timeout_msec = 0;
+ task = nm_c_list_elem_free_steal (elem);
- if (error) {
- _LOGT ("register: registration failed with error \"%s\"", error->message);
- g_dbus_error_strip_remote_error (error);
- _internal_unregister (self);
- g_task_return_error (task, g_steal_pointer (&error));
- return;
+ 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);
}
- _LOGT ("register: registration succeeded");
- priv->registered = TRUE;
- _notify (self, PROP_REGISTERED);
-
- g_task_return_boolean (task, TRUE);
+ nm_utils_error_set_cancelled (&error, FALSE, NULL);
+ g_task_return_error (task, error);
}
/**
@@ -769,8 +806,16 @@ done:
* manager, indicating to NetworkManager that the agent is able to provide and
* save secrets for connections on behalf of its user.
*
- * It is a programmer error to attempt to register an agent that is already
- * registered, or in the process of registering.
+ * 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,
@@ -779,54 +824,39 @@ nm_secret_agent_old_register_async (NMSecretAgentOld *self,
gpointer user_data)
{
NMSecretAgentOldPrivate *priv;
- NMSecretAgentOldClass *class;
- gs_unref_object GTask *task = NULL;
- gs_free_error GError *error = NULL;
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->registered == FALSE);
- g_return_if_fail (priv->registering_timeout_msec == 0);
- g_return_if_fail (priv->dbus_connection != NULL);
- g_return_if_fail (priv->manager_proxy != NULL);
+ g_return_if_fail (priv->is_initialized && !priv->is_destroyed);
- /* Also make sure the subclass can actually respond to secrets requests */
- class = NM_SECRET_AGENT_OLD_GET_CLASS (self);
- g_return_if_fail (class->get_secrets != NULL);
- g_return_if_fail (class->save_secrets != NULL);
- g_return_if_fail (class->delete_secrets != NULL);
+ if (callback) {
+ GTask *task;
- task = nm_g_task_new (self, cancellable, nm_secret_agent_old_register_async, callback, user_data);
+ task = nm_g_task_new (self, cancellable, nm_secret_agent_old_register_async, callback, user_data);
- if (!check_nm_running (self, &error)) {
- _LOGT ("register: failed because NetworkManager is not running");
- g_task_return_error (task, g_steal_pointer (&error));
- return;
- }
+ c_list_link_tail (&priv->pending_tasks_register_lst_head,
+ &nm_c_list_elem_new_stale (task)->lst);
- /* Export our secret agent interface before registering with the manager */
- if (!g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (priv->dbus_secret_agent),
- priv->dbus_connection,
- NM_DBUS_PATH_SECRET_AGENT,
- &error)) {
- _LOGT ("register: failed to export D-Bus service: %s", error->message);
- g_task_return_error (task, g_steal_pointer (&error));
- return;
- }
+ if (cancellable) {
+ gulong cancelled_id;
- priv->suppress_auto = FALSE;
- priv->registering_timeout_msec = nm_utils_get_monotonic_timestamp_msec () + REGISTER_RETRY_TIMEOUT_MSEC;
- priv->registering_try_count = 0;
+ cancelled_id = g_cancellable_connect (cancellable,
+ G_CALLBACK (_register_cancelled_cb),
+ task,
+ NULL);
+ if (cancelled_id != 0) {
+ g_task_set_task_data (task,
+ g_memdup (&cancelled_id, sizeof (cancelled_id)),
+ g_free);
+ }
+ }
+ }
- _LOGT ("register: starting asynchronous registration...");
- nmdbus_agent_manager_call_register_with_capabilities (priv->manager_proxy,
- priv->identifier,
- priv->capabilities,
- cancellable,
- _register_call_cb,
- g_steal_pointer (&task));
+ priv->is_enabled = TRUE;
+ _register_state_change (self);
}
/**
@@ -838,6 +868,10 @@ nm_secret_agent_old_register_async (NMSecretAgentOld *self,
* 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,
@@ -861,6 +895,12 @@ nm_secret_agent_old_register_finish (NMSecretAgentOld *self,
* 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,
@@ -868,43 +908,19 @@ nm_secret_agent_old_unregister (NMSecretAgentOld *self,
GError **error)
{
NMSecretAgentOldPrivate *priv;
- gboolean success;
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->dbus_connection != NULL, FALSE);
- g_return_val_if_fail (priv->manager_proxy != NULL, FALSE);
-
- priv->suppress_auto = TRUE;
-
- success = nmdbus_agent_manager_call_unregister_sync (priv->manager_proxy, cancellable, error);
- if (error && *error)
- g_dbus_error_strip_remote_error (*error);
- _internal_unregister (self);
-
- return success;
-}
-
-static void
-unregister_cb (GObject *proxy, GAsyncResult *result, gpointer user_data)
-{
- gs_unref_object GTask *task = user_data;
- NMSecretAgentOld *self = g_task_get_source_object (task);
- gs_free_error GError *error = NULL;
-
- _internal_unregister (self);
+ g_return_val_if_fail (priv->is_initialized && !priv->is_destroyed, FALSE);
- if (!nmdbus_agent_manager_call_unregister_finish (NMDBUS_AGENT_MANAGER (proxy),
- result,
- &error)) {
- g_dbus_error_strip_remote_error (error);
- g_task_return_error (task, g_steal_pointer (&error));
- return;
- }
+ priv->is_enabled = FALSE;
+ _register_state_change (self);
- g_task_return_boolean (task, TRUE);
+ return !g_cancellable_set_error_if_cancelled (cancellable, error);
}
/**
@@ -917,6 +933,12 @@ unregister_cb (GObject *proxy, GAsyncResult *result, gpointer user_data)
* 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,
@@ -925,29 +947,23 @@ nm_secret_agent_old_unregister_async (NMSecretAgentOld *self,
gpointer user_data)
{
NMSecretAgentOldPrivate *priv;
- gs_unref_object GTask *task = NULL;
- gs_free_error GError *error = NULL;
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->dbus_connection != NULL);
- g_return_if_fail (priv->manager_proxy != NULL);
+ g_return_if_fail (priv->is_initialized && !priv->is_destroyed);
- task = nm_g_task_new (self, cancellable, nm_secret_agent_old_unregister_async, callback, user_data);
+ if (callback) {
+ gs_unref_object GTask *task = NULL;
- if (!check_nm_running (self, &error)) {
- g_task_return_error (task, g_steal_pointer (&error));
- return;
+ task = nm_g_task_new (self, cancellable, nm_secret_agent_old_unregister_async, callback, user_data);
+ g_task_return_boolean (task, TRUE);
}
- priv->suppress_auto = TRUE;
-
- nmdbus_agent_manager_call_unregister (priv->manager_proxy,
- cancellable,
- unregister_cb,
- g_steal_pointer (&task));
+ priv->is_enabled = FALSE;
+ _register_state_change (self);
}
/**
@@ -959,6 +975,12 @@ nm_secret_agent_old_unregister_async (NMSecretAgentOld *self,
* 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,
@@ -971,20 +993,6 @@ nm_secret_agent_old_unregister_finish (NMSecretAgentOld *self,
return g_task_propagate_boolean (G_TASK (result), error);
}
-/**
- * nm_secret_agent_old_get_registered:
- * @self: a #NMSecretAgentOld
- *
- * 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)->registered;
-}
-
/*****************************************************************************/
/**
@@ -1114,118 +1122,675 @@ validate_identifier (const char *identifier)
/*****************************************************************************/
+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
-init_common (NMSecretAgentOld *self)
+_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;
- priv->session_bus = _nm_dbus_bus_type () == G_BUS_TYPE_SESSION;
+ ret = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source), result, &error);
- g_signal_connect (priv->manager_proxy, "notify::g-name-owner",
- G_CALLBACK (name_owner_changed), self);
+ if (nm_utils_error_is_cancelled (error, FALSE))
+ 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
-init_async_registered (GObject *object, GAsyncResult *result, gpointer user_data)
+_register_dbus_call (NMSecretAgentOld *self)
{
- gs_unref_object GTask *task = user_data;
- NMSecretAgentOld *self = g_task_get_source_object (task);
- GError *error = NULL;
+ NMSecretAgentOldPrivate *priv = NM_SECRET_AGENT_OLD_GET_PRIVATE (self);
- nm_secret_agent_old_register_finish (self, result, &error);
+ _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);
+}
- if (error)
- g_task_return_error (task, error);
+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, FALSE))
+ 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, FALSE))
+ _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
- g_task_return_boolean (task, TRUE);
+ nm_assert_not_reached ();
}
+/*****************************************************************************/
+
static void
-init_async_got_proxy (GObject *object, GAsyncResult *result, gpointer user_data)
+_register_state_complete (NMSecretAgentOld *self)
{
- gs_unref_object GTask *task = user_data;
- NMSecretAgentOld *self = g_task_get_source_object (task);
NMSecretAgentOldPrivate *priv = NM_SECRET_AGENT_OLD_GET_PRIVATE (self);
- GError *error = NULL;
+ 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;
+ }
- priv->manager_proxy = nmdbus_agent_manager_proxy_new_finish (result, &error);
- if (!priv->manager_proxy) {
- g_task_return_error (task, error);
+ if (priv->name_owner_cancellable) {
+ /* we still wait for the name owner. Nothing to do for now. */
return;
}
- init_common (self);
+ if (!priv->name_owner_curr) {
+ /* we don't have a name owner. We are done and wait. */
+ _register_state_complete (self);
+ return;
+ }
- if (!priv->auto_register) {
- g_task_return_boolean (task, TRUE);
+ if (priv->registering_cancellable) {
+ /* we are already registering... wait longer. */
return;
}
- nm_secret_agent_old_register_async (self,
- g_task_get_cancellable (task),
- init_async_registered,
- task);
- g_steal_pointer (&task);
+ 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
-init_async_start (NMSecretAgentOld *self, GTask *task_take)
+_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;
+ }
- nmdbus_agent_manager_proxy_new (priv->dbus_connection,
- G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES
- | G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START,
- NM_DBUS_SERVICE,
- NM_DBUS_PATH_AGENT_MANAGER,
- g_task_get_cancellable (task_take),
- init_async_got_proxy,
- task_take);
- g_steal_pointer (&task_take);
+ 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_async_got_bus (GObject *initable, GAsyncResult *result, gpointer user_data)
+_init_complete (NMSecretAgentOld *self,
+ GError *error_take)
{
- gs_unref_object GTask *task = user_data;
- NMSecretAgentOld *self = g_task_get_source_object (task);
NMSecretAgentOldPrivate *priv = NM_SECRET_AGENT_OLD_GET_PRIVATE (self);
- GError *error = NULL;
+ 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) {
- g_task_return_error (task, error);
+ _init_complete (self, g_steal_pointer (&error));
return;
}
- init_async_start (self, g_steal_pointer (&task));
+ _LOGT ("init: got GDBusConnection");
_notify (self, PROP_DBUS_CONNECTION);
+
+ _init_register_object (self);
}
static void
-init_async (GAsyncInitable *initable, int io_priority,
- GCancellable *cancellable, GAsyncReadyCallback callback,
- gpointer user_data)
+_init_start (NMSecretAgentOld *self)
{
- NMSecretAgentOld *self = NM_SECRET_AGENT_OLD (initable);
NMSecretAgentOldPrivate *priv = NM_SECRET_AGENT_OLD_GET_PRIVATE (self);
- GTask *task;
+
+ 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...");
- task = g_task_new (self, cancellable, callback, user_data);
+ 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);
- if (!priv->dbus_connection) {
- g_bus_get (_nm_dbus_bus_type (),
- cancellable,
- init_async_got_bus,
- task);
- return;
+ 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;
}
- init_async_start (self, task);
+ return TRUE;
}
/*****************************************************************************/
@@ -1249,7 +1814,7 @@ get_property (GObject *object,
g_value_set_boolean (value, priv->auto_register);
break;
case PROP_REGISTERED:
- g_value_set_boolean (value, priv->registered);
+ g_value_set_boolean (value, priv->is_registered);
break;
case PROP_CAPABILITIES:
g_value_set_flags (value, priv->capabilities);
@@ -1266,8 +1831,9 @@ set_property (GObject *object,
const GValue *value,
GParamSpec *pspec)
{
- NMSecretAgentOldPrivate *priv = NM_SECRET_AGENT_OLD_GET_PRIVATE (object);
- const char *identifier;
+ 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:
@@ -1275,18 +1841,24 @@ set_property (GObject *object,
priv->dbus_connection = g_value_dup_object (value);
break;
case PROP_IDENTIFIER:
- identifier = g_value_get_string (value);
-
- g_return_if_fail (validate_identifier (identifier));
-
- g_free (priv->identifier);
- priv->identifier = g_strdup (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:
- priv->capabilities = g_value_get_flags (value);
+ /* 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);
@@ -1304,83 +1876,46 @@ nm_secret_agent_old_init (NMSecretAgentOld *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->dbus_secret_agent = nmdbus_secret_agent_skeleton_new ();
- _nm_dbus_bind_properties (self, priv->dbus_secret_agent);
- _nm_dbus_bind_methods (self, priv->dbus_secret_agent,
- "GetSecrets", impl_secret_agent_old_get_secrets,
- "CancelGetSecrets", impl_secret_agent_old_cancel_get_secrets,
- "DeleteSecrets", impl_secret_agent_old_delete_secrets,
- "SaveSecrets", impl_secret_agent_old_save_secrets,
- NULL);
+ priv->context_busy_watcher = g_object_new (G_TYPE_OBJECT, NULL);
}
-static gboolean
-init_sync (GInitable *initable, GCancellable *cancellable, GError **error)
+static void
+dispose (GObject *object)
{
- NMSecretAgentOld *self = NM_SECRET_AGENT_OLD (initable);
- NMSecretAgentOldPrivate *priv = NM_SECRET_AGENT_OLD_GET_PRIVATE (self);
-
- _LOGT ("init-sync");
-
- if (!priv->dbus_connection) {
- priv->dbus_connection = g_bus_get_sync (_nm_dbus_bus_type (), cancellable, error);
- if (!priv->dbus_connection)
- return FALSE;
- _notify (self, PROP_DBUS_CONNECTION);
- }
+ NMSecretAgentOld *self = NM_SECRET_AGENT_OLD (object);
- priv->manager_proxy = nmdbus_agent_manager_proxy_new_sync (priv->dbus_connection,
- G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES
- | G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START,
- NM_DBUS_SERVICE,
- NM_DBUS_PATH_AGENT_MANAGER,
- cancellable,
- error);
- if (!priv->manager_proxy)
- return FALSE;
+ _LOGT ("disposing");
- init_common (self);
+ _secret_agent_old_destroy (self);
- if (priv->auto_register)
- return nm_secret_agent_old_register (self, cancellable, error);
- else
- return TRUE;
+ G_OBJECT_CLASS (nm_secret_agent_old_parent_class)->dispose (object);
}
static void
-dispose (GObject *object)
+finalize (GObject *object)
{
NMSecretAgentOld *self = NM_SECRET_AGENT_OLD (object);
NMSecretAgentOldPrivate *priv = NM_SECRET_AGENT_OLD_GET_PRIVATE (self);
- GetSecretsInfo *info;
- _LOGT ("disposing");
+ _LOGT ("finalizing");
- if (priv->registered) {
- priv->registered = FALSE;
- nm_secret_agent_old_unregister_async (self, NULL, NULL, NULL);
+ if (priv->dbus_context) {
+ nml_cleanup_context_busy_watcher_on_idle (g_steal_pointer (&priv->context_busy_watcher),
+ priv->dbus_context);
}
- nm_clear_g_free (&priv->identifier);
+ 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);
- while ((info = c_list_first_entry (&priv->gsi_lst_head, GetSecretsInfo, gsi_lst)))
- get_secrets_info_free (info);
+ g_clear_object (&priv->context_busy_watcher);
- if (priv->dbus_secret_agent) {
- g_signal_handlers_disconnect_matched (priv->dbus_secret_agent, G_SIGNAL_MATCH_DATA,
- 0, 0, NULL, NULL, self);
- g_clear_object (&priv->dbus_secret_agent);
- }
+ g_free (priv->identifier);
- g_clear_object (&priv->manager_proxy);
-
- G_OBJECT_CLASS (nm_secret_agent_old_parent_class)->dispose (object);
-
- g_clear_object (&priv->dbus_connection);
- nm_clear_pointer (&priv->main_context, g_main_context_unref);
+ G_OBJECT_CLASS (nm_secret_agent_old_parent_class)->finalize (object);
}
static void
@@ -1390,9 +1925,10 @@ nm_secret_agent_old_class_init (NMSecretAgentOldClass *class)
g_type_class_add_private (class, sizeof (NMSecretAgentOldPrivate));
- object_class->dispose = dispose;
object_class->get_property = get_property;
object_class->set_property = set_property;
+ object_class->dispose = dispose;
+ object_class->finalize = finalize;
/**
* NMSecretAgentOld:dbus-connection:
@@ -1437,17 +1973,17 @@ nm_secret_agent_old_class_init (NMSecretAgentOldClass *class)
*
* In particular, if this property is %TRUE at construct time, then the
* agent will register itself with NetworkManager during
- * construction/initialization, and initialization will fail with an error
- * if the agent is unable to register itself.
+ * construction/initialization and initialization will only complete
+ * after registration is completed (either successfully or unsucessfully).
+ * 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_register() or
+ * 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_unregister() will suppress auto-registration
- * until nm_secret_agent_old_register() is called, which re-enables
- * auto-registration. This ensures that the agent remains un-registered when
- * you expect it to be unregistered.
+ * 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, "", "",
@@ -1471,6 +2007,9 @@ nm_secret_agent_old_class_init (NMSecretAgentOldClass *class)
* 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, "", "",
@@ -1492,6 +2031,6 @@ nm_secret_agent_old_initable_iface_init (GInitableIface *iface)
static void
nm_secret_agent_old_async_initable_iface_init (GAsyncInitableIface *iface)
{
- iface->init_async = init_async;
- /* Use default implementation for init_finish */
+ iface->init_async = init_async;
+ iface->init_finish = init_finish;
}
diff --git a/libnm/nm-secret-agent-old.h b/libnm/nm-secret-agent-old.h
index 2f71fc1987..07deee2ce4 100644
--- a/libnm/nm-secret-agent-old.h
+++ b/libnm/nm-secret-agent-old.h
@@ -127,9 +127,12 @@ typedef struct {
/* Called when the subclass should retrieve and return secrets. Subclass
* must copy or reference any arguments it may require after returning from
* this method, as the arguments will freed (except for 'self', 'callback',
- * and 'user_data' of course). If the request is canceled, the callback
- * should still be called, but with the
- * NM_SECRET_AGENT_OLD_ERROR_AGENT_CANCELED error.
+ * and 'user_data' of course).
+ *
+ * Before version 1.24, if the request is canceled, the callback
+ * should still be called, but with the NM_SECRET_AGENT_ERROR_AGENT_CANCELED
+ * error. Since 1.24, invoking the callback has no effect during cancellation
+ * and may be omitted.
*/
void (*get_secrets) (NMSecretAgentOld *self,
NMConnection *connection,
@@ -141,10 +144,12 @@ typedef struct {
gpointer user_data);
/* Called when the subclass should cancel an outstanding request to
- * get secrets for a given connection. Canceling the request MUST
- * call the callback that was passed along with the initial get_secrets
- * call, sending the NM_SECRET_AGENT_OLD_ERROR/
- * NM_SECRET_AGENT_OLD_ERROR_AGENT_CANCELED error to that callback.
+ * get secrets for a given connection.
+ *
+ * Before version 1.24, canceling the request MUST call the callback that was
+ * passed along with the initial get_secrets call, sending the NM_SECRET_AGENT_ERROR/
+ * NM_SECRET_AGENT_ERROR_AGENT_CANCELED error to that callback. Since 1.24,
+ * the get_secrets callback will be ignored during cancellation and may be omitted.
*/
void (*cancel_get_secrets) (NMSecretAgentOld *self,
const char *connection_path,
@@ -186,9 +191,20 @@ GDBusConnection *nm_secret_agent_old_get_dbus_connection (NMSecretAgentOld *self
NM_AVAILABLE_IN_1_24
GMainContext *nm_secret_agent_old_get_main_context (NMSecretAgentOld *self);
-gboolean nm_secret_agent_old_register (NMSecretAgentOld *self,
- GCancellable *cancellable,
- GError **error);
+NM_AVAILABLE_IN_1_24
+GObject *nm_secret_agent_old_get_context_busy_watcher (NMSecretAgentOld *self);
+
+NM_AVAILABLE_IN_1_24
+const char *nm_secret_agent_old_get_dbus_name_owner (NMSecretAgentOld *self);
+
+gboolean nm_secret_agent_old_get_registered (NMSecretAgentOld *self);
+
+/*****************************************************************************/
+
+NM_AVAILABLE_IN_1_24
+void nm_secret_agent_old_enable (NMSecretAgentOld *self,
+ gboolean enable);
+
void nm_secret_agent_old_register_async (NMSecretAgentOld *self,
GCancellable *cancellable,
GAsyncReadyCallback callback,
@@ -197,18 +213,33 @@ gboolean nm_secret_agent_old_register_finish (NMSecretAgentOld *self,
GAsyncResult *result,
GError **error);
+NM_AVAILABLE_IN_1_24
+void nm_secret_agent_old_destroy (NMSecretAgentOld *self);
+
+/*****************************************************************************/
+
+NM_DEPRECATED_IN_1_24_FOR (nm_secret_agent_old_enable)
+gboolean nm_secret_agent_old_register (NMSecretAgentOld *self,
+ GCancellable *cancellable,
+ GError **error);
+
+NM_DEPRECATED_IN_1_24_FOR (nm_secret_agent_old_enable)
gboolean nm_secret_agent_old_unregister (NMSecretAgentOld *self,
GCancellable *cancellable,
GError **error);
+
+NM_DEPRECATED_IN_1_24_FOR (nm_secret_agent_old_enable)
void nm_secret_agent_old_unregister_async (NMSecretAgentOld *self,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
+
+NM_DEPRECATED_IN_1_24_FOR (nm_secret_agent_old_enable)
gboolean nm_secret_agent_old_unregister_finish (NMSecretAgentOld *self,
GAsyncResult *result,
GError **error);
-gboolean nm_secret_agent_old_get_registered (NMSecretAgentOld *self);
+/*****************************************************************************/
void nm_secret_agent_old_get_secrets (NMSecretAgentOld *self,
NMConnection *connection,
diff --git a/libnm/tests/test-secret-agent.c b/libnm/tests/test-secret-agent.c
index 26df6f3c6e..9bceeb588d 100644
--- a/libnm/tests/test-secret-agent.c
+++ b/libnm/tests/test-secret-agent.c
@@ -122,12 +122,12 @@ test_secret_agent_init (TestSecretAgent *agent)
}
static NMSecretAgentOld *
-test_secret_agent_new (void)
+test_secret_agent_new (gboolean auto_register)
{
return nmtstc_context_object_new (test_secret_agent_get_type (),
TRUE,
NM_SECRET_AGENT_OLD_IDENTIFIER, "test-secret-agent",
- NM_SECRET_AGENT_OLD_AUTO_REGISTER, FALSE,
+ NM_SECRET_AGENT_OLD_AUTO_REGISTER, auto_register,
NULL);
}
@@ -211,7 +211,7 @@ test_setup (TestSecretAgentData *sadata, gconstpointer test_data)
{
static int static_counter = 0;
const int counter = static_counter++;
- const char *agent_notes = test_data;
+ const char *create_agent = test_data;
NMConnection *connection;
NMSettingConnection *s_con;
NMSettingWireless *s_wireless;
@@ -273,18 +273,25 @@ test_setup (TestSecretAgentData *sadata, gconstpointer test_data)
g_main_loop_run (sadata->loop);
g_assert (sadata->connection);
- if (agent_notes) {
- sadata->agent = test_secret_agent_new ();
+ if (nm_streq (create_agent, "1")) {
+ gboolean auto_register = nmtst_get_rand_bool ();
- if (!strcmp (agent_notes, "sync")) {
+ sadata->agent = test_secret_agent_new (auto_register);
+
+ if (auto_register) {
+ g_assert (nm_secret_agent_old_get_registered (sadata->agent));
nm_secret_agent_old_register (sadata->agent, NULL, &error);
g_assert_no_error (error);
- g_assert (nm_secret_agent_old_get_registered (sadata->agent));
} else {
- nm_secret_agent_old_register_async (sadata->agent, NULL,
- register_cb, sadata);
+ g_assert (!nm_secret_agent_old_get_registered (sadata->agent));
+ nm_secret_agent_old_register_async (sadata->agent,
+ NULL,
+ register_cb,
+ sadata);
g_main_loop_run (sadata->loop);
}
+
+ g_assert (nm_secret_agent_old_get_registered (sadata->agent));
}
}
@@ -306,6 +313,9 @@ test_cleanup (TestSecretAgentData *sadata, gconstpointer test_data)
nm_client_get_context_busy_watcher (sadata->client));
if (sadata->agent) {
+ nmtst_context_busy_watcher_add (&watcher_data,
+ nm_secret_agent_old_get_context_busy_watcher (sadata->agent));
+
if (nm_secret_agent_old_get_registered (sadata->agent)) {
nm_secret_agent_old_unregister (sadata->agent, NULL, &error);
g_assert_no_error (error);
@@ -542,17 +552,18 @@ test_secret_agent_good (TestSecretAgentData *sadata, gconstpointer test_data)
g_assert_cmpint (sadata->secrets_requested, ==, 1);
}
+/*****************************************************************************/
+
static void
async_init_cb (GObject *object, GAsyncResult *result, gpointer user_data)
{
GMainLoop *loop = user_data;
- GError *error = NULL;
- GObject *agent;
+ gs_free_error GError *error = NULL;
+ gs_unref_object GObject *agent = NULL;
agent = g_async_initable_new_finish (G_ASYNC_INITABLE (object), result, &error);
- g_assert_error (error, NM_SECRET_AGENT_ERROR, NM_SECRET_AGENT_ERROR_FAILED);
- g_assert (agent == NULL);
- g_clear_error (&error);
+ nmtst_assert_success (NM_IS_SECRET_AGENT_OLD (agent), error);
+ g_assert (!nm_secret_agent_old_get_registered (NM_SECRET_AGENT_OLD (agent)));
g_main_loop_quit (loop);
}
@@ -560,89 +571,151 @@ async_init_cb (GObject *object, GAsyncResult *result, gpointer user_data)
static void
test_secret_agent_nm_not_running (void)
{
- NMSecretAgentOld *agent;
- GMainLoop *loop;
+ gs_unref_object NMSecretAgentOld *agent = NULL;
+ nm_auto_unref_gmainloop GMainLoop *loop = NULL;
GError *error = NULL;
- agent = g_initable_new (test_secret_agent_get_type (), NULL, &error,
+ agent = g_initable_new (test_secret_agent_get_type (),
+ NULL,
+ &error,
NM_SECRET_AGENT_OLD_IDENTIFIER, "test-secret-agent",
NULL);
- g_assert_error (error, NM_SECRET_AGENT_ERROR, NM_SECRET_AGENT_ERROR_FAILED);
- g_assert (agent == NULL);
- g_clear_error (&error);
+ nmtst_assert_success (NM_IS_SECRET_AGENT_OLD (agent), error);
+ g_assert (!nm_secret_agent_old_get_registered (agent));
+ g_clear_object (&agent);
loop = g_main_loop_new (NULL, FALSE);
g_async_initable_new_async (test_secret_agent_get_type (),
G_PRIORITY_DEFAULT,
- NULL, async_init_cb, loop,
+ NULL,
+ async_init_cb,
+ loop,
NM_SECRET_AGENT_OLD_IDENTIFIER, "test-secret-agent",
NULL);
g_main_loop_run (loop);
- g_main_loop_unref (loop);
}
+/*****************************************************************************/
+
+typedef struct {
+ int step;
+ int invoke_count;
+} AutoRegisterData;
+
static void
registered_changed (GObject *object, GParamSpec *pspec, gpointer user_data)
{
- GMainLoop *loop = user_data;
-
- g_main_loop_quit (loop);
+ NMSecretAgentOld *agent = NM_SECRET_AGENT_OLD (object);
+ AutoRegisterData *data = user_data;
+
+ g_assert (data);
+ g_assert (NM_IS_SECRET_AGENT_OLD (agent));
+
+ data->invoke_count++;
+ g_assert_cmpint (data->invoke_count, ==, data->step);
+
+ switch (data->step) {
+ case 1:
+ case 3:
+ g_assert (nm_secret_agent_old_get_registered (agent));
+ break;
+ case 2:
+ case 4:
+ g_assert (!nm_secret_agent_old_get_registered (agent));
+ break;
+ default:
+ g_assert_not_reached ();
+ }
}
static void
test_secret_agent_auto_register (void)
{
NMTstcServiceInfo *sinfo;
- NMSecretAgentOld *agent;
- GMainLoop *loop;
+ gs_unref_object NMSecretAgentOld *agent = NULL;
GError *error = NULL;
+ AutoRegisterData auto_register_data = {
+ .step = 0,
+ .invoke_count = 0,
+ };
+ gulong signal_id;
+ NMTstContextBusyWatcherData watcher_data = { };
sinfo = nmtstc_service_init ();
if (!nmtstc_service_available (sinfo))
return;
- loop = g_main_loop_new (NULL, FALSE);
+ agent = test_secret_agent_new (FALSE);
+ g_assert (!nm_secret_agent_old_get_registered (agent));
- agent = test_secret_agent_new ();
- g_object_set (agent,
- NM_SECRET_AGENT_OLD_AUTO_REGISTER, TRUE,
- NULL);
- g_signal_connect (agent, "notify::" NM_SECRET_AGENT_OLD_REGISTERED,
- G_CALLBACK (registered_changed), loop);
+ signal_id = g_signal_connect (agent, "notify::" NM_SECRET_AGENT_OLD_REGISTERED,
+ G_CALLBACK (registered_changed), &auto_register_data);
+ if (nmtst_get_rand_bool ()) {
+ g_object_set (agent,
+ NM_SECRET_AGENT_OLD_AUTO_REGISTER, TRUE,
+ NULL);
+ } else
+ nm_secret_agent_old_enable (agent, TRUE);
g_assert (!nm_secret_agent_old_get_registered (agent));
+
nm_secret_agent_old_register (agent, NULL, &error);
g_assert_no_error (error);
- g_assert (nm_secret_agent_old_get_registered (agent));
+ g_assert (!nm_secret_agent_old_get_registered (agent));
+
+ auto_register_data.step = 1;
+ nmtst_main_context_iterate_until_assert (NULL,
+ 1000,
+ nm_secret_agent_old_get_registered (agent));
+
+ auto_register_data.step = 2;
+ nm_secret_agent_old_enable (agent, FALSE);
+ g_assert (!nm_secret_agent_old_get_registered (agent));
+
+ nmtst_main_context_iterate_until (NULL,
+ nmtst_get_rand_uint32 () % 200,
+ FALSE);
- /* The GLib ObjectManager doesn't like when we drop the service
- * in between it sees the service disappear and the call to
- * GetManagedObjects. Give it a chance to do its business.
- * Arguably a bug. */
- g_main_context_iteration (NULL, FALSE);
+ g_assert (!nm_secret_agent_old_get_registered (agent));
- /* Shut down test service */
nmtstc_service_cleanup (sinfo);
- g_main_loop_run (loop);
+
+ g_assert (!nm_secret_agent_old_get_registered (agent));
+
+ nm_secret_agent_old_enable (agent, TRUE);
+
+ g_assert (!nm_secret_agent_old_get_registered (agent));
+
+ nmtst_main_context_iterate_until (NULL,
+ nmtst_get_rand_uint32 () % 200,
+ FALSE);
+
g_assert (!nm_secret_agent_old_get_registered (agent));
- /* Restart test service */
sinfo = nmtstc_service_init ();
g_assert (nmtstc_service_available (sinfo));
- g_main_loop_run (loop);
- g_assert (nm_secret_agent_old_get_registered (agent));
-
- /* Let ObjectManager initialize (see above). */
- g_main_context_iteration (NULL, FALSE);
+ auto_register_data.step = 3;
+ nmtst_main_context_iterate_until_assert (NULL,
+ 1000,
+ nm_secret_agent_old_get_registered (agent));
- /* Shut down test service again */
nmtstc_service_cleanup (sinfo);
- g_main_loop_run (loop);
- g_assert (!nm_secret_agent_old_get_registered (agent));
- g_object_unref (agent);
- g_main_loop_unref (loop);
+ auto_register_data.step = 4;
+ nmtst_main_context_iterate_until_assert (NULL,
+ 1000,
+ !nm_secret_agent_old_get_registered (agent));
+
+ nm_clear_g_signal_handler (agent, &signal_id);
+
+ nmtst_context_busy_watcher_add (&watcher_data, nm_secret_agent_old_get_context_busy_watcher (agent));
+
+ g_clear_object (&agent);
+
+ nmtst_context_busy_watcher_wait (&watcher_data);
+
+ nmtst_main_context_assert_no_dispatch (NULL, nmtst_get_rand_uint32 () % 500);
}
/*****************************************************************************/
@@ -656,10 +729,10 @@ main (int argc, char **argv)
nmtst_init (&argc, &argv, TRUE);
- g_test_add ("/libnm/secret-agent/none", TestSecretAgentData, NULL, test_setup, test_secret_agent_none, test_cleanup);
- g_test_add ("/libnm/secret-agent/no-secrets", TestSecretAgentData, "sync", test_setup, test_secret_agent_no_secrets, test_cleanup);
- g_test_add ("/libnm/secret-agent/cancel", TestSecretAgentData, "async", test_setup, test_secret_agent_cancel, test_cleanup);
- g_test_add ("/libnm/secret-agent/good", TestSecretAgentData, "async", test_setup, test_secret_agent_good, test_cleanup);
+ g_test_add ("/libnm/secret-agent/none", TestSecretAgentData, "0", test_setup, test_secret_agent_none, test_cleanup);
+ g_test_add ("/libnm/secret-agent/no-secrets", TestSecretAgentData, "1", test_setup, test_secret_agent_no_secrets, test_cleanup);
+ g_test_add ("/libnm/secret-agent/cancel", TestSecretAgentData, "1", test_setup, test_secret_agent_cancel, test_cleanup);
+ g_test_add ("/libnm/secret-agent/good", TestSecretAgentData, "1", test_setup, test_secret_agent_good, test_cleanup);
g_test_add_func ("/libnm/secret-agent/nm-not-running", test_secret_agent_nm_not_running);
g_test_add_func ("/libnm/secret-agent/auto-register", test_secret_agent_auto_register);
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 9379fae271..852901d8ab 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -134,6 +134,7 @@ libnm/nm-device-wpan.c
libnm/nm-device.c
libnm/nm-object.c
libnm/nm-remote-connection.c
+libnm/nm-secret-agent-old.c
libnm/nm-vpn-plugin-old.c
libnm/nm-vpn-service-plugin.c
data/org.freedesktop.NetworkManager.policy.in.in