From 1b62db9b7fb14ec41e21a7f561df6b320f6925d0 Mon Sep 17 00:00:00 2001 From: Dan Winship Date: Tue, 2 Sep 2014 21:41:05 -0400 Subject: libnm: start glib-ifying NMSecretAgent APIs Make register() and unregister() have cancellable sync and async variants. And make NMSecretAgent implement GInitable/GAsyncInitable, and do the initial auto-registration as part of initialization rather than doing it asynchronously and emitting a signal. --- clients/tui/nmt-secret-agent.c | 7 +- clients/tui/nmtui-connect.c | 7 +- libnm/libnm.ver | 4 + libnm/nm-secret-agent.c | 575 +++++++++++++++++++++++++++++++--------- libnm/nm-secret-agent.h | 28 +- libnm/tests/test-secret-agent.c | 101 +++++-- 6 files changed, 547 insertions(+), 175 deletions(-) diff --git a/clients/tui/nmt-secret-agent.c b/clients/tui/nmt-secret-agent.c index cabe5b4a87..7550ce64f6 100644 --- a/clients/tui/nmt-secret-agent.c +++ b/clients/tui/nmt-secret-agent.c @@ -639,8 +639,7 @@ nmt_secret_agent_class_init (NmtSecretAgentClass *klass) NMSecretAgent * nmt_secret_agent_new (void) { - return g_object_new (NMT_TYPE_SECRET_AGENT, - NM_SECRET_AGENT_IDENTIFIER, "nmtui", - NM_SECRET_AGENT_AUTO_REGISTER, FALSE, - NULL); + return g_initable_new (NMT_TYPE_SECRET_AGENT, NULL, NULL, + NM_SECRET_AGENT_IDENTIFIER, "nmtui", + NULL); } diff --git a/clients/tui/nmtui-connect.c b/clients/tui/nmtui-connect.c index ca523ebbaf..dfeb186f90 100644 --- a/clients/tui/nmtui-connect.c +++ b/clients/tui/nmtui-connect.c @@ -142,7 +142,6 @@ activate_connection (NMConnection *connection, nmt_newt_form_set_content (form, label); agent = nmt_secret_agent_new (); - nm_secret_agent_register (agent); g_signal_connect (agent, "request-secrets", G_CALLBACK (secrets_requested), NULL); specific_object_path = specific_object ? nm_object_get_path (specific_object) : NULL; @@ -203,11 +202,7 @@ activate_connection (NMConnection *connection, nmt_newt_form_quit (form); g_object_unref (form); - /* If the activation failed very quickly, then agent won't be registered yet, - * and nm_secret_agent_unregister() would complain if we called it... - */ - if (nm_secret_agent_get_registered (agent)) - nm_secret_agent_unregister (agent); + nm_secret_agent_unregister (agent, NULL, NULL); g_object_unref (agent); } diff --git a/libnm/libnm.ver b/libnm/libnm.ver index 5617fb25f9..3670ec616c 100644 --- a/libnm/libnm.ver +++ b/libnm/libnm.ver @@ -371,8 +371,12 @@ global: nm_secret_agent_get_secrets_flags_get_type; nm_secret_agent_get_type; nm_secret_agent_register; + nm_secret_agent_register_async; + nm_secret_agent_register_finish; nm_secret_agent_save_secrets; nm_secret_agent_unregister; + nm_secret_agent_unregister_async; + nm_secret_agent_unregister_finish; nm_setting_802_1x_add_altsubject_match; nm_setting_802_1x_add_eap_method; nm_setting_802_1x_add_phase2_altsubject_match; diff --git a/libnm/nm-secret-agent.c b/libnm/nm-secret-agent.c index fab584073a..d61aa3d723 100644 --- a/libnm/nm-secret-agent.c +++ b/libnm/nm-secret-agent.c @@ -54,14 +54,18 @@ static void impl_secret_agent_delete_secrets (NMSecretAgent *self, #include "nm-secret-agent-glue.h" -G_DEFINE_ABSTRACT_TYPE (NMSecretAgent, nm_secret_agent, G_TYPE_OBJECT) +static void nm_secret_agent_initable_iface_init (GInitableIface *iface); +static void nm_secret_agent_async_initable_iface_init (GAsyncInitableIface *iface); +G_DEFINE_ABSTRACT_TYPE_WITH_CODE (NMSecretAgent, nm_secret_agent, G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, nm_secret_agent_initable_iface_init); + G_IMPLEMENT_INTERFACE (G_TYPE_ASYNC_INITABLE, nm_secret_agent_async_initable_iface_init); + ) #define NM_SECRET_AGENT_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SECRET_AGENT, NMSecretAgentPrivate)) -static gboolean auto_register_cb (gpointer user_data); - typedef struct { gboolean registered; + gboolean registering; NMSecretAgentCapabilities capabilities; DBusGConnection *bus; @@ -69,7 +73,6 @@ typedef struct { gboolean session_bus; DBusGProxy *dbus_proxy; DBusGProxy *manager_proxy; - DBusGProxyCall *reg_call; /* GetSecretsInfo structs of in-flight GetSecrets requests */ GSList *pending_gets; @@ -79,7 +82,6 @@ typedef struct { char *identifier; gboolean auto_register; gboolean suppress_auto; - gboolean auto_register_id; } NMSecretAgentPrivate; enum { @@ -93,15 +95,6 @@ enum { LAST_PROP }; -enum { - REGISTRATION_RESULT, - - LAST_SIGNAL -}; - -static guint signals[LAST_SIGNAL] = { 0 }; - - /********************************************************************/ GQuark @@ -147,6 +140,7 @@ _internal_unregister (NMSecretAgent *self) if (priv->registered) { dbus_g_connection_unregister_g_object (priv->bus, G_OBJECT (self)); priv->registered = FALSE; + priv->registering = FALSE; g_object_notify (G_OBJECT (self), NM_SECRET_AGENT_REGISTERED); } } @@ -172,6 +166,17 @@ get_secrets_info_finalize (NMSecretAgent *self, GetSecretsInfo *info) g_free (info); } +static inline gboolean +should_auto_register (NMSecretAgent *self) +{ + NMSecretAgentPrivate *priv = NM_SECRET_AGENT_GET_PRIVATE (self); + + return ( priv->auto_register + && !priv->suppress_auto + && !priv->registered + && !priv->registering); +} + static void name_owner_changed (DBusGProxy *proxy, const char *name, @@ -191,7 +196,8 @@ name_owner_changed (DBusGProxy *proxy, if (!old_owner_good && new_owner_good) { /* NM appeared */ - auto_register_cb (self); + if (should_auto_register (self)) + nm_secret_agent_register_async (self, NULL, NULL, NULL); } else if (old_owner_good && !new_owner_good) { /* Cancel any pending secrets requests */ for (iter = priv->pending_gets; iter; iter = g_slist_next (iter)) { @@ -209,7 +215,8 @@ name_owner_changed (DBusGProxy *proxy, } else if (old_owner_good && new_owner_good && strcmp (old_owner, new_owner)) { /* Hmm, NM magically restarted */ _internal_unregister (self); - auto_register_cb (self); + if (should_auto_register (self)) + nm_secret_agent_register_async (self, NULL, NULL, NULL); } } } @@ -539,20 +546,124 @@ impl_secret_agent_delete_secrets (NMSecretAgent *self, /**************************************************************/ +static gboolean +check_nm_running (NMSecretAgent *self, GError **error) +{ + NMSecretAgentPrivate *priv = NM_SECRET_AGENT_GET_PRIVATE (self); + + if (priv->nm_owner || priv->private_bus) + return TRUE; + + g_set_error (error, NM_SECRET_AGENT_ERROR, NM_SECRET_AGENT_ERROR_INTERNAL_ERROR, + "NetworkManager is not running"); + return FALSE; +} + +/**************************************************************/ + +/** + * nm_secret_agent_register: + * @self: a #NMSecretAgent + * @cancellable: a #GCancellable, or %NULL + * @error: return location for a #GError, or %NULL + * + * Registers the #NMSecretAgent with the NetworkManager secret manager, + * indicating to NetworkManager that the agent is able to provide and save + * secrets for connections on behalf of its user. + * + * 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. + **/ +gboolean +nm_secret_agent_register (NMSecretAgent *self, + GCancellable *cancellable, + GError **error) +{ + NMSecretAgentPrivate *priv; + NMSecretAgentClass *class; + + g_return_val_if_fail (NM_IS_SECRET_AGENT (self), FALSE); + + priv = NM_SECRET_AGENT_GET_PRIVATE (self); + + g_return_val_if_fail (priv->registered == FALSE, FALSE); + g_return_val_if_fail (priv->registering == FALSE, FALSE); + g_return_val_if_fail (priv->bus != NULL, FALSE); + g_return_val_if_fail (priv->manager_proxy != NULL, FALSE); + + /* Also make sure the subclass can actually respond to secrets requests */ + class = NM_SECRET_AGENT_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); + + if (!check_nm_running (self, error)) + return FALSE; + + priv->suppress_auto = FALSE; + + /* Export our secret agent interface before registering with the manager */ + dbus_g_connection_register_g_object (priv->bus, + NM_DBUS_PATH_SECRET_AGENT, + G_OBJECT (self)); + + priv->registering = TRUE; + if (dbus_g_proxy_call_with_timeout (priv->manager_proxy, + "RegisterWithCapabilities", + 5000, NULL, + G_TYPE_STRING, priv->identifier, + G_TYPE_UINT, priv->capabilities, + G_TYPE_INVALID, + G_TYPE_INVALID)) + goto success; + + /* Might be an old NetworkManager that doesn't support capabilities; + * fall back to old Register() method instead. + */ + if (dbus_g_proxy_call_with_timeout (priv->manager_proxy, + "Register", + 5000, error, + G_TYPE_STRING, priv->identifier, + G_TYPE_INVALID, + G_TYPE_INVALID)) + goto success; + + /* Failure */ + priv->registering = FALSE; + _internal_unregister (self); + return FALSE; + +success: + priv->registering = FALSE; + priv->registered = TRUE; + g_object_notify (G_OBJECT (self), NM_SECRET_AGENT_REGISTERED); + return TRUE; +} + static void -reg_result (NMSecretAgent *self, GError *error) +reg_result (NMSecretAgent *self, GSimpleAsyncResult *simple, GError *error) { NMSecretAgentPrivate *priv = NM_SECRET_AGENT_GET_PRIVATE (self); + priv->registering = FALSE; + if (error) { + g_simple_async_result_take_error (simple, error); + g_simple_async_result_complete (simple); + /* If registration failed we shouldn't expose ourselves on the bus */ _internal_unregister (self); } else { priv->registered = TRUE; g_object_notify (G_OBJECT (self), NM_SECRET_AGENT_REGISTERED); + + g_simple_async_result_set_op_res_gboolean (simple, TRUE); + g_simple_async_result_complete (simple); } - g_signal_emit (self, signals[REGISTRATION_RESULT], 0, error); + g_object_unref (simple); } static void @@ -560,15 +671,15 @@ reg_request_cb (DBusGProxy *proxy, DBusGProxyCall *call, gpointer user_data) { - NMSecretAgent *self = NM_SECRET_AGENT (user_data); - NMSecretAgentPrivate *priv = NM_SECRET_AGENT_GET_PRIVATE (self); + GSimpleAsyncResult *simple = user_data; + NMSecretAgent *self; GError *error = NULL; - priv->reg_call = NULL; + self = NM_SECRET_AGENT (g_async_result_get_source_object (G_ASYNC_RESULT (simple))); + g_object_unref (self); /* drop extra ref added by get_source_object() */ dbus_g_proxy_end_call (proxy, call, &error, G_TYPE_INVALID); - reg_result (self, error); - g_clear_error (&error); + reg_result (self, simple, error); } static void @@ -576,66 +687,81 @@ reg_with_caps_cb (DBusGProxy *proxy, DBusGProxyCall *call, gpointer user_data) { - NMSecretAgent *self = NM_SECRET_AGENT (user_data); - NMSecretAgentPrivate *priv = NM_SECRET_AGENT_GET_PRIVATE (self); + GSimpleAsyncResult *simple = user_data; + NMSecretAgent *self; + NMSecretAgentPrivate *priv; - priv->reg_call = NULL; + self = NM_SECRET_AGENT (g_async_result_get_source_object (G_ASYNC_RESULT (simple))); + g_object_unref (self); /* drop extra ref added by get_source_object() */ + priv = NM_SECRET_AGENT_GET_PRIVATE (self); if (dbus_g_proxy_end_call (proxy, call, NULL, G_TYPE_INVALID)) { - reg_result (self, NULL); + reg_result (self, simple, NULL); return; } /* Might be an old NetworkManager that doesn't support capabilities; * fall back to old Register() method instead. */ - priv->reg_call = dbus_g_proxy_begin_call_with_timeout (priv->manager_proxy, - "Register", - reg_request_cb, - self, - NULL, - 5000, - G_TYPE_STRING, priv->identifier, - G_TYPE_INVALID); + dbus_g_proxy_begin_call_with_timeout (priv->manager_proxy, + "Register", + reg_request_cb, + self, + NULL, + 5000, + G_TYPE_STRING, priv->identifier, + G_TYPE_INVALID); } /** - * nm_secret_agent_register: + * nm_secret_agent_register_async: * @self: a #NMSecretAgent + * @cancellable: a #GCancellable, or %NULL + * @callback: callback to call when the agent is registered + * @user_data: data for @callback * - * Registers the #NMSecretAgent with the NetworkManager secret manager, - * indicating to NetworkManager that the agent is able to provide and save - * secrets for connections on behalf of its user. Registration is an - * asynchronous operation and its success or failure is indicated via the - * 'registration-result' signal. + * Asynchronously registers the #NMSecretAgent with the NetworkManager secret + * manager, indicating to NetworkManager that the agent is able to provide and + * save secrets for connections on behalf of its user. * - * Returns: a new %TRUE if registration was successfully requested (this does - * not mean registration itself was successful), %FALSE if registration was not - * successfully requested. + * It is a programmer error to attempt to register an agent that is already + * registered, or in the process of registering. **/ -gboolean -nm_secret_agent_register (NMSecretAgent *self) +void +nm_secret_agent_register_async (NMSecretAgent *self, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) { NMSecretAgentPrivate *priv; NMSecretAgentClass *class; + GSimpleAsyncResult *simple; + GError *error = NULL; - g_return_val_if_fail (NM_IS_SECRET_AGENT (self), FALSE); + g_return_if_fail (NM_IS_SECRET_AGENT (self)); priv = NM_SECRET_AGENT_GET_PRIVATE (self); - g_return_val_if_fail (priv->registered == FALSE, FALSE); - g_return_val_if_fail (priv->reg_call == NULL, FALSE); - g_return_val_if_fail (priv->bus != NULL, FALSE); - g_return_val_if_fail (priv->manager_proxy != NULL, FALSE); + g_return_if_fail (priv->registered == FALSE); + g_return_if_fail (priv->registering == FALSE); + g_return_if_fail (priv->bus != NULL); + g_return_if_fail (priv->manager_proxy != NULL); /* Also make sure the subclass can actually respond to secrets requests */ class = NM_SECRET_AGENT_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); + 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 (!priv->nm_owner && !priv->private_bus) - return FALSE; + simple = g_simple_async_result_new (G_OBJECT (self), callback, user_data, + nm_secret_agent_register_async); + + if (!check_nm_running (self, &error)) { + g_simple_async_result_take_error (simple, error); + g_simple_async_result_complete_in_idle (simple); + g_object_unref (simple); + return; + } priv->suppress_auto = FALSE; @@ -644,32 +770,63 @@ nm_secret_agent_register (NMSecretAgent *self) NM_DBUS_PATH_SECRET_AGENT, G_OBJECT (self)); - priv->reg_call = dbus_g_proxy_begin_call_with_timeout (priv->manager_proxy, - "RegisterWithCapabilities", - reg_with_caps_cb, - self, - NULL, - 5000, - G_TYPE_STRING, priv->identifier, - G_TYPE_UINT, priv->capabilities, - G_TYPE_INVALID); - return TRUE; + priv->registering = TRUE; + dbus_g_proxy_begin_call_with_timeout (priv->manager_proxy, + "RegisterWithCapabilities", + reg_with_caps_cb, + simple, + NULL, + 5000, + G_TYPE_STRING, priv->identifier, + G_TYPE_UINT, priv->capabilities, + G_TYPE_INVALID); +} + +/** + * nm_secret_agent_register_finish: + * @self: a #NMSecretAgent + * @result: the result passed to the #GAsyncReadyCallback + * @error: return location for a #GError, or %NULL + * + * Gets the result of a call to nm_secret_agent_register_async(). + * + * Returns: %TRUE if registration was successful, %FALSE on error. + **/ +gboolean +nm_secret_agent_register_finish (NMSecretAgent *self, + GAsyncResult *result, + GError **error) +{ + g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (self), nm_secret_agent_register_async), FALSE); + + if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error)) + return FALSE; + else + return TRUE; } /** * nm_secret_agent_unregister: * @self: a #NMSecretAgent + * @cancellable: a #GCancellable, or %NULL + * @error: return location for a #GError, or %NULL * * Unregisters the #NMSecretAgent with the NetworkManager secret manager, - * indicating to NetworkManager that the agent is will no longer provide or + * indicating to NetworkManager that the agent will no longer provide or * store secrets on behalf of this user. * - * Returns: a new %TRUE if unregistration was successful, %FALSE if it was not. + * It is a programmer error to attempt to unregister an agent that is not + * registered. + * + * Returns: %TRUE if unregistration was successful, %FALSE on error **/ gboolean -nm_secret_agent_unregister (NMSecretAgent *self) +nm_secret_agent_unregister (NMSecretAgent *self, + GCancellable *cancellable, + GError **error) { NMSecretAgentPrivate *priv; + gboolean success; g_return_val_if_fail (NM_IS_SECRET_AGENT (self), FALSE); @@ -679,15 +836,118 @@ nm_secret_agent_unregister (NMSecretAgent *self) g_return_val_if_fail (priv->bus != NULL, FALSE); g_return_val_if_fail (priv->manager_proxy != NULL, FALSE); - if (!priv->nm_owner && !priv->private_bus) + if (!check_nm_running (self, error)) return FALSE; - dbus_g_proxy_call_no_reply (priv->manager_proxy, "Unregister", G_TYPE_INVALID); + priv->suppress_auto = TRUE; + success = dbus_g_proxy_call_with_timeout (priv->manager_proxy, + "Unregister", + 5000, error, + G_TYPE_INVALID, + G_TYPE_INVALID); _internal_unregister (self); + + return success; +} + +static void +unregister_cb (DBusGProxy *proxy, + DBusGProxyCall *call, + gpointer user_data) +{ + GSimpleAsyncResult *simple = user_data; + NMSecretAgent *self; + GError *error = NULL; + + self = NM_SECRET_AGENT (g_async_result_get_source_object (G_ASYNC_RESULT (simple))); + g_object_unref (self); /* drop extra ref added by get_source_object() */ + + _internal_unregister (self); + + if (dbus_g_proxy_end_call (proxy, call, &error, G_TYPE_INVALID)) + g_simple_async_result_set_op_res_gboolean (simple, TRUE); + else + g_simple_async_result_take_error (simple, error); + + g_simple_async_result_complete (simple); + g_object_unref (simple); +} + +/** + * nm_secret_agent_unregister_async: + * @self: a #NMSecretAgent + * @cancellable: a #GCancellable, or %NULL + * @callback: callback to call when the agent is unregistered + * @user_data: data for @callback + * + * Asynchronously unregisters the #NMSecretAgent with the NetworkManager secret + * manager, indicating to NetworkManager that the agent will no longer provide + * or store secrets on behalf of this user. + * + * It is a programmer error to attempt to unregister an agent that is not + * registered. + **/ +void +nm_secret_agent_unregister_async (NMSecretAgent *self, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + NMSecretAgentPrivate *priv; + GSimpleAsyncResult *simple; + GError *error = NULL; + + g_return_val_if_fail (NM_IS_SECRET_AGENT (self), FALSE); + + priv = NM_SECRET_AGENT_GET_PRIVATE (self); + + g_return_val_if_fail (priv->registered == TRUE, FALSE); + g_return_val_if_fail (priv->bus != NULL, FALSE); + g_return_val_if_fail (priv->manager_proxy != NULL, FALSE); + + simple = g_simple_async_result_new (G_OBJECT (self), callback, user_data, + nm_secret_agent_unregister_async); + + if (!check_nm_running (self, &error)) { + g_simple_async_result_take_error (simple, error); + g_simple_async_result_complete_in_idle (simple); + g_object_unref (simple); + return; + } + priv->suppress_auto = TRUE; - return TRUE; + dbus_g_proxy_begin_call_with_timeout (priv->manager_proxy, + "Unregister", + unregister_cb, + simple, + NULL, + 5000, + G_TYPE_INVALID); +} + +/** + * nm_secret_agent_unregister_finish: + * @self: a #NMSecretAgent + * @result: the result passed to the #GAsyncReadyCallback + * @error: return location for a #GError, or %NULL + * + * Gets the result of a call to nm_secret_agent_unregister_async(). + * + * Returns: %TRUE if unregistration was successful, %FALSE on error. + **/ +gboolean +nm_secret_agent_unregister_finish (NMSecretAgent *self, + GAsyncResult *result, + GError **error) +{ + g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (self), nm_secret_agent_unregister_async), FALSE); + + if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error)) + return FALSE; + else + return TRUE; } /** @@ -704,19 +964,6 @@ nm_secret_agent_get_registered (NMSecretAgent *self) return NM_SECRET_AGENT_GET_PRIVATE (self)->registered; } -static gboolean -auto_register_cb (gpointer user_data) -{ - NMSecretAgent *self = NM_SECRET_AGENT (user_data); - NMSecretAgentPrivate *priv = NM_SECRET_AGENT_GET_PRIVATE (self); - - priv->auto_register_id = 0; - if (priv->auto_register && !priv->suppress_auto && - (priv->reg_call == NULL && !priv->registered)) - nm_secret_agent_register (self); - return FALSE; -} - /**************************************************************/ /** @@ -729,7 +976,7 @@ auto_register_cb (gpointer user_data) * @callback: (scope async): a callback, to be invoked when the operation is done * @user_data: (closure): caller-specific data to be passed to @callback * - * Asyncronously retrieve secrets belonging to @connection for the + * Asynchronously retrieves secrets belonging to @connection for the * setting @setting_name. @flags indicate specific behavior that the secret * agent should use when performing the request, for example returning only * existing secrets without user interaction, or requesting entirely new @@ -771,8 +1018,8 @@ nm_secret_agent_get_secrets (NMSecretAgent *self, * @callback: (scope async): a callback, to be invoked when the operation is done * @user_data: (closure): caller-specific data to be passed to @callback * - * Asyncronously ensure that all secrets inside @connection - * are stored to disk. + * Asynchronously ensures that all secrets inside @connection are stored to + * disk. * * Virtual: save_secrets */ @@ -800,7 +1047,7 @@ nm_secret_agent_save_secrets (NMSecretAgent *self, * @callback: (scope async): a callback, to be invoked when the operation is done * @user_data: (closure): caller-specific data to be passed to @callback * - * Asynchronously ask the agent to delete all saved secrets belonging to + * Asynchronously asks the agent to delete all saved secrets belonging to * @connection. * * Virtual: delete_secrets @@ -855,19 +1102,15 @@ nm_secret_agent_init (NMSecretAgent *self) { } -static void -constructed (GObject *object) +static gboolean +init_common (NMSecretAgent *self, GError **error) { - NMSecretAgent *self = NM_SECRET_AGENT (object); NMSecretAgentPrivate *priv = NM_SECRET_AGENT_GET_PRIVATE (self); - GError *error = NULL; - if (!priv->bus) - priv->bus = _nm_dbus_new_connection (&error); if (!priv->bus) { - g_warning ("Couldn't connect to system bus: %s", error->message); - g_error_free (error); - return; + priv->bus = _nm_dbus_new_connection (error); + if (!priv->bus) + return FALSE; } priv->private_bus = _nm_dbus_is_connection_private (priv->bus); @@ -897,14 +1140,82 @@ constructed (GObject *object) NM_DBUS_PATH_AGENT_MANAGER, NM_DBUS_INTERFACE_AGENT_MANAGER); if (!priv->manager_proxy) { - g_warning ("Couldn't create NM agent manager proxy."); + g_set_error_literal (error, NM_SECRET_AGENT_ERROR, NM_SECRET_AGENT_ERROR_INTERNAL_ERROR, + "Couldn't create NM agent manager proxy."); + return FALSE; + } + + return TRUE; +} + +static gboolean +init_sync (GInitable *initable, GCancellable *cancellable, GError **error) +{ + NMSecretAgent *self = NM_SECRET_AGENT (initable); + NMSecretAgentPrivate *priv = NM_SECRET_AGENT_GET_PRIVATE (self); + + if (!init_common (self, error)) + return FALSE; + + if (priv->auto_register) + return nm_secret_agent_register (self, cancellable, error); + else + return TRUE; +} + +static void +init_async_registered (GObject *initable, GAsyncResult *result, gpointer user_data) +{ + NMSecretAgent *self = NM_SECRET_AGENT (initable); + GSimpleAsyncResult *simple = user_data; + GError *error = NULL; + + if (nm_secret_agent_register_finish (self, result, &error)) + g_simple_async_result_set_op_res_gboolean (simple, TRUE); + else + g_simple_async_result_take_error (simple, error); + + g_simple_async_result_complete_in_idle (simple); + g_object_unref (simple); +} + +static void +init_async (GAsyncInitable *initable, int io_priority, + GCancellable *cancellable, GAsyncReadyCallback callback, + gpointer user_data) +{ + NMSecretAgent *self = NM_SECRET_AGENT (initable); + NMSecretAgentPrivate *priv = NM_SECRET_AGENT_GET_PRIVATE (self); + GSimpleAsyncResult *simple; + GError *error = NULL; + + simple = g_simple_async_result_new (G_OBJECT (initable), callback, user_data, init_async); + + if (!init_common (self, &error)) { + g_simple_async_result_take_error (simple, error); + g_simple_async_result_complete_in_idle (simple); + g_object_unref (simple); return; } - if (priv->nm_owner || priv->private_bus) - priv->auto_register_id = g_idle_add (auto_register_cb, self); + if (priv->auto_register) + nm_secret_agent_register_async (self, cancellable, init_async_registered, simple); + else { + g_simple_async_result_set_op_res_gboolean (simple, TRUE); + g_simple_async_result_complete_in_idle (simple); + g_object_unref (simple); + } +} - G_OBJECT_CLASS (nm_secret_agent_parent_class)->constructed (object); +static gboolean +init_finish (GAsyncInitable *initable, GAsyncResult *result, GError **error) +{ + GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (result); + + if (g_simple_async_result_propagate_error (simple, error)) + return FALSE; + else + return TRUE; } static void @@ -986,12 +1297,7 @@ dispose (GObject *object) NMSecretAgentPrivate *priv = NM_SECRET_AGENT_GET_PRIVATE (self); if (priv->registered) - nm_secret_agent_unregister (self); - - if (priv->auto_register_id) { - g_source_remove (priv->auto_register_id); - priv->auto_register_id = 0; - } + nm_secret_agent_unregister_async (self, NULL, NULL, NULL); g_free (priv->identifier); priv->identifier = NULL; @@ -1020,7 +1326,6 @@ nm_secret_agent_class_init (NMSecretAgentClass *class) g_type_class_add_private (class, sizeof (NMSecretAgentPrivate)); /* Virtual methods */ - object_class->constructed = constructed; object_class->dispose = dispose; object_class->get_property = get_property; object_class->set_property = set_property; @@ -1064,13 +1369,23 @@ nm_secret_agent_class_init (NMSecretAgentClass *class) /** * NMSecretAgent:auto-register: * - * If TRUE, the agent will attempt to automatically register itself after - * it is created (via an idle handler) and to re-register itself if - * NetworkManager restarts. If FALSE, the agent does not automatically - * register with NetworkManager, and nm_secret_agent_register() must be - * called. If 'auto-register' is TRUE, calling nm_secret_agent_unregister() - * will suppress auto-registration until nm_secret_agent_register() is - * called, which re-enables auto-registration. + * If %TRUE (the default), the agent will always be registered when + * NetworkManager is running; if NetworkManager exits and restarts, the + * agent will re-register itself automatically. + * + * In particular, if this property is %TRUE at construct time, then the + * agent will register itself with NetworkManager during + * construction/initialization, and initialization will fail with an error + * if the agent is unable to register itself. + * + * If the property is %FALSE, the agent will not automatically register with + * NetworkManager, and nm_secret_agent_register() or + * nm_secret_agent_register_async() must be called to register it. + * + * Calling nm_secret_agent_unregister() will suppress auto-registration + * until nm_secret_agent_register() is called, which re-enables + * auto-registration. This ensures that the agent remains un-registered when + * you expect it to be unregistered. **/ g_object_class_install_property (object_class, PROP_AUTO_REGISTER, @@ -1106,21 +1421,6 @@ nm_secret_agent_class_init (NMSecretAgentClass *class) G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS)); - /** - * NMSecretAgent::registration-result: - * @agent: the agent that received the signal - * @error: the error, if any, that occured while registering - * - * Indicates the result of a registration request; if @error is NULL the - * request was successful. - **/ - signals[REGISTRATION_RESULT] = - g_signal_new (NM_SECRET_AGENT_REGISTRATION_RESULT, - G_OBJECT_CLASS_TYPE (object_class), - G_SIGNAL_RUN_FIRST, - 0, NULL, NULL, NULL, - G_TYPE_NONE, 1, G_TYPE_POINTER); - dbus_g_object_type_install_info (G_TYPE_FROM_CLASS (class), &dbus_glib_nm_secret_agent_object_info); @@ -1128,3 +1428,16 @@ nm_secret_agent_class_init (NMSecretAgentClass *class) NM_DBUS_INTERFACE_SECRET_AGENT, NM_TYPE_SECRET_AGENT_ERROR); } + +static void +nm_secret_agent_initable_iface_init (GInitableIface *iface) +{ + iface->init = init_sync; +} + +static void +nm_secret_agent_async_initable_iface_init (GAsyncInitableIface *iface) +{ + iface->init_async = init_async; + iface->init_finish = init_finish; +} diff --git a/libnm/nm-secret-agent.h b/libnm/nm-secret-agent.h index bb76f576af..5f65da6441 100644 --- a/libnm/nm-secret-agent.h +++ b/libnm/nm-secret-agent.h @@ -25,6 +25,7 @@ #error "Only can be included directly." #endif +#include #include G_BEGIN_DECLS @@ -72,8 +73,6 @@ typedef enum { #define NM_SECRET_AGENT_REGISTERED "registered" #define NM_SECRET_AGENT_CAPABILITIES "capabilities" -#define NM_SECRET_AGENT_REGISTRATION_RESULT "registration-result" - typedef struct { GObject parent; } NMSecretAgent; @@ -171,9 +170,6 @@ typedef void (*NMSecretAgentDeleteSecretsFunc) (NMSecretAgent *agent, typedef struct { GObjectClass parent; - /* Signals */ - void (*registration_result) (NMSecretAgent *agent, GError *error); - /* Virtual methods for subclasses */ /* Called when the subclass should retrieve and return secrets. Subclass @@ -232,9 +228,27 @@ typedef struct { GType nm_secret_agent_get_type (void); -gboolean nm_secret_agent_register (NMSecretAgent *self); +gboolean nm_secret_agent_register (NMSecretAgent *self, + GCancellable *cancellable, + GError **error); +void nm_secret_agent_register_async (NMSecretAgent *self, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); +gboolean nm_secret_agent_register_finish (NMSecretAgent *self, + GAsyncResult *result, + GError **error); -gboolean nm_secret_agent_unregister (NMSecretAgent *self); +gboolean nm_secret_agent_unregister (NMSecretAgent *self, + GCancellable *cancellable, + GError **error); +void nm_secret_agent_unregister_async (NMSecretAgent *self, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); +gboolean nm_secret_agent_unregister_finish (NMSecretAgent *self, + GAsyncResult *result, + GError **error); gboolean nm_secret_agent_get_registered (NMSecretAgent *self); diff --git a/libnm/tests/test-secret-agent.c b/libnm/tests/test-secret-agent.c index 92ecd1a520..3aa8b2177c 100644 --- a/libnm/tests/test-secret-agent.c +++ b/libnm/tests/test-secret-agent.c @@ -171,11 +171,13 @@ test_secret_agent_new (void) bus = dbus_g_bus_get (DBUS_BUS_SESSION, &error); g_assert_no_error (error); - agent = g_object_new (test_secret_agent_get_type (), - NM_SECRET_AGENT_DBUS_CONNECTION, bus, - NM_SECRET_AGENT_IDENTIFIER, "test-secret-agent", - NM_SECRET_AGENT_AUTO_REGISTER, FALSE, - NULL); + agent = g_initable_new (test_secret_agent_get_type (), NULL, &error, + NM_SECRET_AGENT_DBUS_CONNECTION, bus, + NM_SECRET_AGENT_IDENTIFIER, "test-secret-agent", + NM_SECRET_AGENT_AUTO_REGISTER, FALSE, + NULL); + g_assert_no_error (error); + dbus_g_connection_unref (bus); return agent; } @@ -237,11 +239,15 @@ connection_added_cb (NMRemoteSettings *s, } static void -registration_result (NMSecretAgent *agent, GError *error, gpointer user_data) +register_cb (GObject *object, GAsyncResult *result, gpointer user_data) { TestSecretAgentData *sadata = user_data; + GError *error = NULL; + nm_secret_agent_register_finish (sadata->agent, result, &error); g_assert_no_error (error); + g_assert (nm_secret_agent_get_registered (sadata->agent)); + g_main_loop_quit (sadata->loop); } @@ -251,7 +257,7 @@ static void test_setup (TestSecretAgentData *sadata, gconstpointer test_data) { static int counter = 0; - gboolean create_agent = GPOINTER_TO_UINT (test_data); + const char *agent_notes = test_data; NMConnection *connection; NMSettingConnection *s_con; NMSettingWireless *s_wireless; @@ -321,13 +327,18 @@ test_setup (TestSecretAgentData *sadata, gconstpointer test_data) g_main_loop_run (sadata->loop); g_assert (sadata->connection); - if (create_agent) { + if (agent_notes) { sadata->agent = test_secret_agent_new (); - nm_secret_agent_register (sadata->agent); - handler = g_signal_connect (sadata->agent, NM_SECRET_AGENT_REGISTRATION_RESULT, - G_CALLBACK (registration_result), sadata); - g_main_loop_run (sadata->loop); - g_signal_handler_disconnect (sadata->agent, handler); + + if (!strcmp (agent_notes, "sync")) { + nm_secret_agent_register (sadata->agent, NULL, &error); + g_assert_no_error (error); + g_assert (nm_secret_agent_get_registered (sadata->agent)); + } else { + nm_secret_agent_register_async (sadata->agent, NULL, + register_cb, sadata); + g_main_loop_run (sadata->loop); + } } } @@ -338,8 +349,10 @@ test_cleanup (TestSecretAgentData *sadata, gconstpointer test_data) GError *error = NULL; if (sadata->agent) { - if (nm_secret_agent_get_registered (sadata->agent)) - nm_secret_agent_unregister (sadata->agent); + if (nm_secret_agent_get_registered (sadata->agent)) { + nm_secret_agent_unregister (sadata->agent, NULL, &error); + g_assert_no_error (error); + } g_object_unref (sadata->agent); } @@ -549,17 +562,51 @@ test_secret_agent_good (TestSecretAgentData *sadata, gconstpointer test_data) } +static void +async_init_cb (GObject *object, GAsyncResult *result, gpointer user_data) +{ + GMainLoop *loop = user_data; + GError *error = NULL; + GObject *agent; + + agent = g_async_initable_new_finish (G_ASYNC_INITABLE (object), result, &error); + g_assert_error (error, NM_SECRET_AGENT_ERROR, NM_SECRET_AGENT_ERROR_INTERNAL_ERROR); + g_assert (agent == NULL); + g_clear_error (&error); + + g_main_loop_quit (loop); +} + static void test_secret_agent_nm_not_running (void) { + DBusGConnection *bus; NMSecretAgent *agent; - gboolean success; + GMainLoop *loop; + GError *error = NULL; - agent = test_secret_agent_new (); - success = nm_secret_agent_register (agent); - g_assert (!success); + bus = dbus_g_bus_get (DBUS_BUS_SESSION, &error); + g_assert_no_error (error); - g_object_unref (agent); + agent = g_initable_new (test_secret_agent_get_type (), NULL, &error, + NM_SECRET_AGENT_DBUS_CONNECTION, bus, + NM_SECRET_AGENT_IDENTIFIER, "test-secret-agent", + NULL); + g_assert_error (error, NM_SECRET_AGENT_ERROR, NM_SECRET_AGENT_ERROR_INTERNAL_ERROR); + g_assert (agent == NULL); + g_clear_error (&error); + + 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, + NM_SECRET_AGENT_DBUS_CONNECTION, bus, + NM_SECRET_AGENT_IDENTIFIER, "test-secret-agent", + NULL); + g_main_loop_run (loop); + g_main_loop_unref (loop); + + dbus_g_connection_unref (bus); } @@ -577,6 +624,7 @@ test_secret_agent_auto_register (void) NMTestServiceInfo *sinfo; NMSecretAgent *agent; GMainLoop *loop; + GError *error = NULL; sinfo = nm_test_service_init (); loop = g_main_loop_new (NULL, FALSE); @@ -589,9 +637,8 @@ test_secret_agent_auto_register (void) G_CALLBACK (registered_changed), loop); g_assert (!nm_secret_agent_get_registered (agent)); - - /* Wait for initial registration */ - g_main_loop_run (loop); + nm_secret_agent_register (agent, NULL, &error); + g_assert_no_error (error); g_assert (nm_secret_agent_get_registered (agent)); /* Shut down test service */ @@ -626,13 +673,13 @@ main (int argc, char **argv) g_test_init (&argc, &argv, NULL); - g_test_add ("/libnm/secret-agent/none", TestSecretAgentData, GUINT_TO_POINTER (FALSE), + 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, GUINT_TO_POINTER (TRUE), + 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, GUINT_TO_POINTER (TRUE), + 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, GUINT_TO_POINTER (TRUE), + g_test_add ("/libnm/secret-agent/good", TestSecretAgentData, "async", 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); -- cgit v1.2.1