diff options
author | Thomas Haller <thaller@redhat.com> | 2020-01-28 11:07:31 +0100 |
---|---|---|
committer | Thomas Haller <thaller@redhat.com> | 2020-01-28 11:07:59 +0100 |
commit | 8590d2ecfb9aec9d78ddfa0f6cbc8adce3d3669b (patch) | |
tree | cecde6883b799c8d2d4dff57723908f132c961e2 | |
parent | 51ed70228f42fe5e172718cc92c8222ad41fa825 (diff) | |
parent | c1ec82909988add353acad4917a8545a9d3d7488 (diff) | |
download | NetworkManager-8590d2ecfb9aec9d78ddfa0f6cbc8adce3d3669b.tar.gz |
libnm/secret-agent: merge branch 'th/secret-agent-rework'
https://gitlab.freedesktop.org/NetworkManager/NetworkManager/merge_requests/383
-rw-r--r-- | libnm-core/tests/test-general.c | 2 | ||||
-rw-r--r-- | libnm/libnm.ver | 6 | ||||
-rw-r--r-- | libnm/nm-client.c | 247 | ||||
-rw-r--r-- | libnm/nm-device.c | 2 | ||||
-rw-r--r-- | libnm/nm-libnm-utils.h | 58 | ||||
-rw-r--r-- | libnm/nm-secret-agent-old.c | 1928 | ||||
-rw-r--r-- | libnm/nm-secret-agent-old.h | 60 | ||||
-rw-r--r-- | libnm/tests/test-remote-settings-client.c | 12 | ||||
-rw-r--r-- | libnm/tests/test-secret-agent.c | 311 | ||||
-rw-r--r-- | po/POTFILES.in | 1 | ||||
-rw-r--r-- | shared/nm-glib-aux/nm-shared-utils.h | 22 | ||||
-rw-r--r-- | shared/nm-test-libnm-utils.h | 16 | ||||
-rw-r--r-- | shared/nm-test-utils-impl.c | 274 | ||||
-rw-r--r-- | shared/nm-utils/nm-test-utils.h | 142 |
14 files changed, 2036 insertions, 1045 deletions
diff --git a/libnm-core/tests/test-general.c b/libnm-core/tests/test-general.c index 1bdaf5bd62..9ea3079e83 100644 --- a/libnm-core/tests/test-general.c +++ b/libnm-core/tests/test-general.c @@ -8270,7 +8270,7 @@ test_integrate_maincontext (gconstpointer test_data) g_source_set_callback (idle_source_1, _test_integrate_maincontext_cb_idle1, &count, NULL); g_source_attach (idle_source_1, c2); - nmtst_main_context_iterate_until (c1, 2000, count == 5); + nmtst_main_context_iterate_until_assert (c1, 2000, count == 5); } if (TEST_IDX == 2) { diff --git a/libnm/libnm.ver b/libnm/libnm.ver index 3ff469b0c5..457b55a798 100644 --- a/libnm/libnm.ver +++ b/libnm/libnm.ver @@ -1667,6 +1667,12 @@ 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; nm_setting_vrf_new; diff --git a/libnm/nm-client.c b/libnm/nm-client.c index 66a36adee3..b530d7d671 100644 --- a/libnm/nm-client.c +++ b/libnm/nm-client.c @@ -60,7 +60,7 @@ /*****************************************************************************/ -static NM_CACHED_QUARK_FCN ("nm-client-context-busy-watcher", nm_client_context_busy_watcher_quark) +NM_CACHED_QUARK_FCN ("nm-context-busy-watcher", nm_context_busy_watcher_quark) static void _context_busy_watcher_attach_integration_source_cb (gpointer data, @@ -69,10 +69,21 @@ _context_busy_watcher_attach_integration_source_cb (gpointer data, nm_g_source_destroy_and_unref (data); } -static void -_context_busy_watcher_attach_integration_source (GObject *context_busy_watcher, - GSource *source_take) +void +nm_context_busy_watcher_integrate_source (GMainContext *outer_context, + GMainContext *inner_context, + GObject *context_busy_watcher) { + GSource *source; + + nm_assert (outer_context); + nm_assert (inner_context); + nm_assert (outer_context != inner_context); + nm_assert (G_IS_OBJECT (context_busy_watcher)); + + source = nm_utils_g_main_context_create_integrate_source (inner_context); + g_source_attach (source, outer_context); + /* The problem is... * * NMClient is associated with a GMainContext, just like its underlying GDBusConnection @@ -114,7 +125,7 @@ _context_busy_watcher_attach_integration_source (GObject *context_busy_watcher, g_object_weak_ref (context_busy_watcher, _context_busy_watcher_attach_integration_source_cb, - source_take); + source); } /*****************************************************************************/ @@ -175,22 +186,6 @@ typedef struct { /*****************************************************************************/ -typedef struct { - GCancellable *cancellable; - GSource *cancel_on_idle_source; - gulong cancelled_id; - union { - struct { - GTask *task; - } async; - struct { - GMainLoop *main_loop; - GError **error_location; - } sync; - } data; - bool is_sync:1; -} InitData; - NM_GOBJECT_PROPERTIES_DEFINE (NMClient, PROP_DBUS_CONNECTION, PROP_DBUS_NAME_OWNER, @@ -263,7 +258,7 @@ typedef struct { GMainContext *dbus_context; GObject *context_busy_watcher; GDBusConnection *dbus_connection; - InitData *init_data; + NMLInitData *init_data; GHashTable *dbus_objects; CList obj_changed_lst_head; GCancellable *name_owner_get_cancellable; @@ -409,15 +404,15 @@ NM_CACHED_QUARK_FCN ("nm-client-error-quark", nm_client_error_quark) /*****************************************************************************/ -static InitData * -_init_data_new_sync (GCancellable *cancellable, - GMainLoop *main_loop, - GError **error_location) +NMLInitData * +nml_init_data_new_sync (GCancellable *cancellable, + GMainLoop *main_loop, + GError **error_location) { - InitData *init_data; + NMLInitData *init_data; - init_data = g_slice_new (InitData); - *init_data = (InitData) { + init_data = g_slice_new (NMLInitData); + *init_data = (NMLInitData) { .cancellable = nm_g_object_ref (cancellable), .is_sync = TRUE, .data.sync = { @@ -428,14 +423,14 @@ _init_data_new_sync (GCancellable *cancellable, return init_data; } -static InitData * -_init_data_new_async (GCancellable *cancellable, - GTask *task_take) +NMLInitData * +nml_init_data_new_async (GCancellable *cancellable, + GTask *task_take) { - InitData *init_data; + NMLInitData *init_data; - init_data = g_slice_new (InitData); - *init_data = (InitData) { + init_data = g_slice_new (NMLInitData); + *init_data = (NMLInitData) { .cancellable = nm_g_object_ref (cancellable), .is_sync = FALSE, .data.async = { @@ -445,6 +440,30 @@ _init_data_new_async (GCancellable *cancellable, return init_data; } +void +nml_init_data_return (NMLInitData *init_data, + GError *error_take) +{ + nm_assert (init_data); + + nm_clear_pointer (&init_data->cancel_on_idle_source, nm_g_source_destroy_and_unref); + nm_clear_g_signal_handler (init_data->cancellable, &init_data->cancelled_id); + + if (init_data->is_sync) { + if (error_take) + g_propagate_error (init_data->data.sync.error_location, error_take); + g_main_loop_quit (init_data->data.sync.main_loop); + } else { + if (error_take) + g_task_return_error (init_data->data.async.task, error_take); + else + g_task_return_boolean (init_data->data.async.task, TRUE); + g_object_unref (init_data->data.async.task); + } + nm_g_object_unref (init_data->cancellable); + nm_g_slice_free (init_data); +} + /*****************************************************************************/ GError * @@ -1008,7 +1027,7 @@ nm_client_get_context_busy_watcher (NMClient *self) g_return_val_if_fail (NM_IS_CLIENT (self), NULL); w = NM_CLIENT_GET_PRIVATE (self)->context_busy_watcher; - return g_object_get_qdata (w, nm_client_context_busy_watcher_quark ()) + return g_object_get_qdata (w, nm_context_busy_watcher_quark ()) ?: w; } @@ -6816,7 +6835,7 @@ name_owner_changed (NMClient *self, old_context_busy_watcher = g_steal_pointer (&priv->context_busy_watcher); priv->context_busy_watcher = g_object_ref (g_object_get_qdata (old_context_busy_watcher, - nm_client_context_busy_watcher_quark ())); + nm_context_busy_watcher_quark ())); g_main_context_ref (priv->main_context); g_main_context_unref (priv->dbus_context); @@ -6952,35 +6971,80 @@ name_owner_get_call (NMClient *self) /*****************************************************************************/ +static inline gboolean +_nml_cleanup_context_busy_watcher_on_idle_cb (gpointer user_data) +{ + nm_auto_unref_gmaincontext GMainContext *context = NULL; + gs_unref_object GObject *context_busy_watcher = NULL; + + nm_utils_user_data_unpack (user_data, &context, &context_busy_watcher); + + nm_assert (context); + nm_assert (G_IS_OBJECT (context_busy_watcher)); + return G_SOURCE_REMOVE; +} + +void +nml_cleanup_context_busy_watcher_on_idle (GObject *context_busy_watcher_take, + GMainContext *context) +{ + gs_unref_object GObject *context_busy_watcher = g_steal_pointer (&context_busy_watcher_take); + GSource *cleanup_source; + + nm_assert (G_IS_OBJECT (context_busy_watcher)); + nm_assert (context); + + /* Technically, we cancelled all pending actions (and these actions + * (GTask) keep the context_busy_watcher object alive). Also, we passed + * no destroy notify to g_dbus_connection_signal_subscribe(). + * That means, there should be no other unaccounted GSource'es left. + * + * However, we really need to be sure that the context_busy_watcher's + * lifetime matches the time that the context is busy. That is especially + * important with synchronous initialization, where the context-busy-watcher + * keeps the inner GMainContext integrated in the caller's. + * We must not g_source_destroy() that integration too early. + * + * So to be really sure all this is given, always schedule one last + * cleanup idle action with low priority. This should be the last + * thing related to this instance that keeps the context busy. + * + * Note that we could also *not* take a reference on @context + * and unref @context_busy_watcher via the GDestroyNotify. That would + * allow for the context to be wrapped up early, and when the last user + * gives up the reference to the context, the destroy notify could complete + * without even invoke the idle handler. However, that destroy notify may + * not be called in the right thread. So, we want to be sure that we unref + * the context-busy-watcher in the right context. Hence, we always take an + * additional reference and always cleanup in the idle handler. This means: + * the user *MUST* always keep iterating the context after NMClient got destroyed. + * But that is not a severe limitation, because the user anyway must be prepared + * to do that. That is because in many cases it is necessary anyway (and the user + * wouldn't know a priory when not). This way, it is just always necessary. */ + + cleanup_source = nm_g_idle_source_new (G_PRIORITY_LOW + 10, + _nml_cleanup_context_busy_watcher_on_idle_cb, + nm_utils_user_data_pack (g_main_context_ref (context), + g_steal_pointer (&context_busy_watcher)), + NULL); + g_source_attach (cleanup_source, context); + g_source_unref (cleanup_source); +} + +/*****************************************************************************/ + static void _init_start_complete (NMClient *self, GError *error_take) { NMClientPrivate *priv = NM_CLIENT_GET_PRIVATE (self); - InitData *init_data; - - init_data = g_steal_pointer (&priv->init_data); NML_NMCLIENT_LOG_D (self, "%s init complete with %s%s%s", - init_data->is_sync ? "sync" : "async", + priv->init_data->is_sync ? "sync" : "async", NM_PRINT_FMT_QUOTED (error_take, "error: ", error_take->message, "", "success")); - nm_clear_pointer (&init_data->cancel_on_idle_source, nm_g_source_destroy_and_unref); - nm_clear_g_signal_handler (init_data->cancellable, &init_data->cancelled_id); - - if (init_data->is_sync) { - if (error_take) - g_propagate_error (init_data->data.sync.error_location, error_take); - g_main_loop_quit (init_data->data.sync.main_loop); - } else { - if (error_take) - g_task_return_error (init_data->data.async.task, error_take); - else - g_task_return_boolean (init_data->data.async.task, TRUE); - g_object_unref (init_data->data.async.task); - } - nm_g_object_unref (init_data->cancellable); - nm_g_slice_free (init_data); + nml_init_data_return (g_steal_pointer (&priv->init_data), + error_take); } static void @@ -7379,12 +7443,12 @@ init_sync (GInitable *initable, GCancellable *cancellable, GError **error) * to resync and drop the inner context. That means, requests made against the inner * context have a different lifetime. Hence, we create a separate tracking * object. This "wraps" the outer context-busy-watcher and references it, so - * that the work together. Grep for nm_client_context_busy_watcher_quark() to + * that the work together. Grep for nm_context_busy_watcher_quark() to * see how this works. */ parent_context_busy_watcher = g_steal_pointer (&priv->context_busy_watcher); priv->context_busy_watcher = g_object_new (G_TYPE_OBJECT, NULL); g_object_set_qdata_full (priv->context_busy_watcher, - nm_client_context_busy_watcher_quark (), + nm_context_busy_watcher_quark (), parent_context_busy_watcher, g_object_unref); @@ -7392,7 +7456,7 @@ init_sync (GInitable *initable, GCancellable *cancellable, GError **error) main_loop = g_main_loop_new (dbus_context, FALSE); - priv->init_data = _init_data_new_sync (cancellable, main_loop, &local_error); + priv->init_data = nml_init_data_new_sync (cancellable, main_loop, &local_error); _init_start (self); @@ -7403,12 +7467,9 @@ init_sync (GInitable *initable, GCancellable *cancellable, GError **error) g_main_context_pop_thread_default (dbus_context); if (priv->main_context != priv->dbus_context) { - GSource *source; - - source = nm_utils_g_main_context_create_integrate_source (priv->dbus_context); - g_source_attach (source, priv->main_context); - _context_busy_watcher_attach_integration_source (priv->context_busy_watcher, - g_steal_pointer (&source)); + nm_context_busy_watcher_integrate_source (priv->main_context, + priv->dbus_context, + priv->context_busy_watcher); } g_main_context_unref (dbus_context); @@ -7448,7 +7509,7 @@ init_async (GAsyncInitable *initable, task = nm_g_task_new (self, cancellable, init_async, callback, user_data); g_task_set_priority (task, io_priority); - priv->init_data = _init_data_new_async (cancellable, g_steal_pointer (&task)); + priv->init_data = nml_init_data_new_async (cancellable, g_steal_pointer (&task)); _init_start (self); } @@ -7569,18 +7630,6 @@ constructed (GObject *object) NML_NMCLIENT_LOG_D (self, "new NMClient instance"); } -static inline gboolean -_dispose_cleanup_context_busy_watcher_cb (gpointer user_data) -{ - nm_auto_unref_gmaincontext GMainContext *context = NULL; - gs_unref_object GObject *context_busy_watcher = NULL; - - nm_utils_user_data_unpack (user_data, &context, &context_busy_watcher); - - nm_assert (G_IS_OBJECT (context_busy_watcher)); - return G_SOURCE_REMOVE; -} - static void dispose (GObject *object) { @@ -7631,42 +7680,8 @@ dispose (GObject *object) if ( priv->context_busy_watcher && priv->dbus_context) { - GSource *cleanup_source; - - /* Technically, we cancelled all pending actions (and these actions - * (GTask) keep the context_busy_watcher object alive). Also, we passed - * no destroy notify to g_dbus_connection_signal_subscribe(). - * That means, there should be no other unaccounted GSource'es left. - * - * However, we really need to be sure that the context_busy_watcher's - * lifetime matches the time that the context is busy. That is especially - * important with synchronous initialization, where the context-busy-watcher - * keeps the inner GMainContext integrated in the caller's. - * We must not g_source_destroy() that integration too early. - * - * So to be really sure all this is given, always schedule one last - * cleanup idle action with low priority. This should be the last - * thing related to this instance that keeps the context busy. - * - * Note that we could also *not* take a reference on priv->dbus_context - * and unref priv->context_busy_watcher via the GDestroyNotify. That would - * allow for the context to be wrapped up early, and when the last user - * gives up the reference to the context, the destroy notify could complete - * without even invoke the idle handler. However, that destroy notify may - * not be called in the right thread. So, we want to be sure that we unref - * the context-busy-watcher in the right context. Hence, we always take an - * additional reference and always cleanup in the idle handler. This means: - * the user *MUST* always keep iterating the context after NMClient got destroyed. - * But that is not a severe limitation, because the user anyway must be prepared - * to do that. That is because in many cases it is necessary anyway (and the user - * wouldn't know a priory when not). This way, it is just always necessary. */ - cleanup_source = nm_g_idle_source_new (G_PRIORITY_LOW + 10, - _dispose_cleanup_context_busy_watcher_cb, - nm_utils_user_data_pack (g_main_context_ref (priv->dbus_context), - g_steal_pointer (&priv->context_busy_watcher)), - NULL); - g_source_attach (cleanup_source, priv->dbus_context); - g_source_unref (cleanup_source); + nml_cleanup_context_busy_watcher_on_idle (g_steal_pointer (&priv->context_busy_watcher), + priv->dbus_context); } nm_clear_pointer (&priv->dbus_context, g_main_context_unref); @@ -7682,6 +7697,8 @@ dispose (GObject *object) priv->nm.capabilities_len = 0; nm_clear_g_free (&priv->nm.capabilities_arr); + + NML_NMCLIENT_LOG_D (self, "disposed"); } const NMLDBusMetaIface _nml_dbus_meta_iface_nm_agentmanager = NML_DBUS_META_IFACE_INIT ( diff --git a/libnm/nm-device.c b/libnm/nm-device.c index 77b7da6b09..f2701cb7e5 100644 --- a/libnm/nm-device.c +++ b/libnm/nm-device.c @@ -158,7 +158,7 @@ _notify_event_state_changed (NMClient *client, gs_unref_object NMDevice *self = notify_event->user_data; NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - NML_NMCLIENT_LOG_T (_nm_object_get_client (self), + NML_NMCLIENT_LOG_T (client, "[%s] emit Device's StateChanged signal %u -> %u, reason: %u", _nm_object_get_path (self), (guint) priv->old_state, diff --git a/libnm/nm-libnm-utils.h b/libnm/nm-libnm-utils.h index ec9f23ba8e..f2affc5f62 100644 --- a/libnm/nm-libnm-utils.h +++ b/libnm/nm-libnm-utils.h @@ -31,6 +31,8 @@ gboolean nm_utils_g_param_spec_is_default (const GParamSpec *pspec); /*****************************************************************************/ typedef enum { + _NML_DBUS_LOG_LEVEL_NONE = 0x00, + _NML_DBUS_LOG_LEVEL_INITIALIZED = 0x01, _NML_DBUS_LOG_LEVEL_TRACE = 0x02, @@ -83,6 +85,7 @@ nml_dbus_log_enabled (NMLDBusLogLevel level) int l; nm_assert (NM_IN_SET (level, NML_DBUS_LOG_LEVEL_ANY, + _NML_DBUS_LOG_LEVEL_NONE, NML_DBUS_LOG_LEVEL_TRACE, NML_DBUS_LOG_LEVEL_DEBUG, NML_DBUS_LOG_LEVEL_WARN, @@ -104,7 +107,8 @@ void _nml_dbus_log (NMLDBusLogLevel level, #define NML_DBUS_LOG(level, ...) \ G_STMT_START { \ - G_STATIC_ASSERT ( (level) == NML_DBUS_LOG_LEVEL_TRACE \ + G_STATIC_ASSERT ( (level) == _NML_DBUS_LOG_LEVEL_NONE \ + || (level) == NML_DBUS_LOG_LEVEL_TRACE \ || (level) == NML_DBUS_LOG_LEVEL_DEBUG \ || (level) == NML_DBUS_LOG_LEVEL_WARN \ || (level) == NML_DBUS_LOG_LEVEL_ERROR); \ @@ -119,8 +123,19 @@ void _nml_dbus_log (NMLDBusLogLevel level, #define NML_DBUS_LOG_W(...) NML_DBUS_LOG (NML_DBUS_LOG_LEVEL_WARN, __VA_ARGS__) #define NML_DBUS_LOG_E(...) NML_DBUS_LOG (NML_DBUS_LOG_LEVEL_ERROR, __VA_ARGS__) +/* _NML_NMCLIENT_LOG_LEVEL_COERCE is only for printf debugging. You can disable client logging by + * mapping the requested log level to a different one (or disable it altogether). + * That's useful for example if you are interested in *other* trace logging messages from + * libnm and don't want to get flooded by NMClient's trace messages. */ +#define _NML_NMCLIENT_LOG_LEVEL_COERCE(level) \ + /* for example, change condition below to suppress <trace> messages from NMClient. */ \ + (( TRUE \ + || ((level) != NML_DBUS_LOG_LEVEL_TRACE)) \ + ? (level) \ + : _NML_DBUS_LOG_LEVEL_NONE) + #define NML_NMCLIENT_LOG(level, self, ...) \ - NML_DBUS_LOG ((level), \ + NML_DBUS_LOG (_NML_NMCLIENT_LOG_LEVEL_COERCE (level), \ "nmclient["NM_HASH_OBFUSCATE_PTR_FMT"]: " _NM_UTILS_MACRO_FIRST (__VA_ARGS__), \ NM_HASH_OBFUSCATE_PTR (self) \ _NM_UTILS_MACRO_REST (__VA_ARGS__)) @@ -160,6 +175,45 @@ _nml_coerce_property_strv_not_null (char **strv) /*****************************************************************************/ +GQuark nm_context_busy_watcher_quark (void); + +void nm_context_busy_watcher_integrate_source (GMainContext *outer_context, + GMainContext *inner_context, + GObject *context_busy_watcher); + +/*****************************************************************************/ + +typedef struct { + GCancellable *cancellable; + GSource *cancel_on_idle_source; + gulong cancelled_id; + union { + struct { + GTask *task; + } async; + struct { + GMainLoop *main_loop; + GError **error_location; + } sync; + } data; + bool is_sync:1; +} NMLInitData; + +NMLInitData *nml_init_data_new_sync (GCancellable *cancellable, + GMainLoop *main_loop, + GError **error_location); + +NMLInitData *nml_init_data_new_async (GCancellable *cancellable, + GTask *task_take); + +void nml_init_data_return (NMLInitData *init_data, + GError *error_take); + +void nml_cleanup_context_busy_watcher_on_idle (GObject *context_busy_watcher_take, + GMainContext *context); + +/*****************************************************************************/ + typedef struct _NMLDBusObject NMLDBusObject; typedef struct _NMLDBusObjWatcher NMLDBusObjWatcher; typedef struct _NMLDBusMetaIface NMLDBusMetaIface; diff --git a/libnm/nm-secret-agent-old.c b/libnm/nm-secret-agent-old.c index 03aa87140f..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, @@ -35,27 +35,61 @@ NM_GOBJECT_PROPERTIES_DEFINE (NMSecretAgentOld, PROP_AUTO_REGISTER, PROP_REGISTERED, PROP_CAPABILITIES, + PROP_DBUS_CONNECTION, ); typedef struct { - GDBusConnection *bus; - NMDBusAgentManager *manager_proxy; - NMDBusSecretAgent *dbus_secret_agent; + GDBusConnection *dbus_connection; + GMainContext *main_context; + GMainContext *dbus_context; + GObject *context_busy_watcher; + GCancellable *name_owner_cancellable; + GCancellable *registering_cancellable; + GSource *registering_retry_source; + + NMLInitData *init_data; - /* 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); @@ -66,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)) /*****************************************************************************/ @@ -78,160 +112,289 @@ 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 -_internal_unregister (NMSecretAgentOld *self) +static void _register_state_change (NMSecretAgentOld *self); + +static void _register_dbus_call (NMSecretAgentOld *self); + +static void _init_complete (NMSecretAgentOld *self, GError *error_take); + +static void _register_state_complete (NMSecretAgentOld *self); + +/*****************************************************************************/ + +/** + * nm_secret_agent_old_get_dbus_connection: + * @self: the #NMSecretAgentOld instance + * + * Returns: (transfer none): the #GDBusConnection used by the secret agent. + * You may either set this as construct property %NM_SECRET_AGENT_OLD_DBUS_CONNECTION, + * or it will automatically set during initialization. + * + * Since: 1.24 + */ +GDBusConnection * +nm_secret_agent_old_get_dbus_connection (NMSecretAgentOld *self) { - 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_SECRET_AGENT_OLD_GET_PRIVATE (self)->dbus_connection; } +/** + * nm_secret_agent_old_get_main_context: + * @self: the #NMSecretAgentOld instance + * + * Returns: (transfer none): the #GMainContext instance associate with the + * instance. This is the g_main_context_get_thread_default() at the time + * when creating the instance. + * + * Since: 1.24 + */ +GMainContext * +nm_secret_agent_old_get_main_context (NMSecretAgentOld *self) +{ + g_return_val_if_fail (NM_IS_SECRET_AGENT_OLD (self), NULL); + + return NM_SECRET_AGENT_OLD_GET_PRIVATE (self)->main_context; +} + +/** + * nm_secret_agent_old_get_context_busy_watcher: + * @self: the #NMSecretAgentOld instance + * + * Returns a #GObject that stays alive as long as there are pending + * requests in the #GDBusConnection. Such requests keep the #GMainContext + * alive, and thus you may want to keep iterating the context as long + * until a weak reference indicates that this object is gone. This is + * useful because even when you destroy the instance right away (and all + * the internally pending requests get cancelled), any pending g_dbus_connection_call() + * requests will still invoke the result on the #GMainContext. Hence, this + * allows you to know how long you must iterate the context to know + * that all remains are cleaned up. + * + * Returns: (transfer none): a #GObject that you may register a weak pointer + * to know that the #GMainContext is still kept busy by @self. + * + * Since: 1.24 + */ +GObject * +nm_secret_agent_old_get_context_busy_watcher (NMSecretAgentOld *self) +{ + g_return_val_if_fail (NM_IS_SECRET_AGENT_OLD (self), NULL); + + return NM_SECRET_AGENT_OLD_GET_PRIVATE (self)->context_busy_watcher; +} + +/** + * nm_secret_agent_old_get_dbus_name_owner: + * @self: the #NMSecretAgentOld instance + * + * Returns: the current D-Bus name owner. While this property + * is set while registering, it really only makes sense when + * the nm_secret_agent_old_get_registered() indicates that + * registration is successfull. + * + * Since: 1.24 + */ +const char * +nm_secret_agent_old_get_dbus_name_owner (NMSecretAgentOld *self) +{ + g_return_val_if_fail (NM_IS_SECRET_AGENT_OLD (self), NULL); + + return nm_ref_string_get_str (NM_SECRET_AGENT_OLD_GET_PRIVATE (self)->name_owner_curr); +} + +/** + * nm_secret_agent_old_get_registered: + * @self: a #NMSecretAgentOld + * + * Note that the secret agent transparently registers and re-registers + * as the D-Bus name owner appears. Hence, this property is not really + * useful. Also, to be graceful against races during registration, the + * instance will already accept requests while being in the process of + * registering. + * If you need to avoid races and want to wait until @self is registered, + * call nm_secret_agent_old_register_async(). If that function completes + * with success, you know the instance is registered. + * + * Returns: a %TRUE if the agent is registered, %FALSE if it is not. + **/ +gboolean +nm_secret_agent_old_get_registered (NMSecretAgentOld *self) +{ + g_return_val_if_fail (NM_IS_SECRET_AGENT_OLD (self), FALSE); + + return NM_SECRET_AGENT_OLD_GET_PRIVATE (self)->is_registered; +} + +/*****************************************************************************/ + static void -get_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->bus, - 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 @@ -245,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, @@ -261,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, @@ -285,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); } @@ -400,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); } @@ -437,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)); - owner = g_dbus_proxy_get_name_owner (G_DBUS_PROXY (priv->manager_proxy)); - if (owner) - return TRUE; + priv = NM_SECRET_AGENT_OLD_GET_PRIVATE (self); + + 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 @@ -505,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, @@ -516,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->bus != 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->bus, - 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, ®ister_data->cancellable_signal_id); - nm_clear_g_source_inst (®ister_data->timeout_source); - g_clear_object (®ister_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, ®ister_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); + elem = nm_c_list_elem_find_first (&priv->pending_tasks_register_lst_head, x, x == task0); - 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; - } - - 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); } /** @@ -729,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, @@ -739,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->bus != 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->bus, - 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); } /** @@ -798,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, @@ -821,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, @@ -828,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->bus != NULL, FALSE); - g_return_val_if_fail (priv->manager_proxy != NULL, FALSE); + g_return_val_if_fail (priv->is_initialized && !priv->is_destroyed, FALSE); - priv->suppress_auto = TRUE; + priv->is_enabled = FALSE; + _register_state_change (self); - 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); - - 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; - } - - g_task_return_boolean (task, TRUE); + return !g_cancellable_set_error_if_cancelled (cancellable, error); } /** @@ -877,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, @@ -885,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->bus != 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); } /** @@ -919,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, @@ -931,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; -} - /*****************************************************************************/ /** @@ -1074,83 +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; + + ret = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source), result, &error); - priv->session_bus = _nm_dbus_bus_type () == G_BUS_TYPE_SESSION; + 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_signal_connect (priv->manager_proxy, "notify::g-name-owner", - G_CALLBACK (name_owner_changed), self); + 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; - priv->manager_proxy = nmdbus_agent_manager_proxy_new_finish (result, &error); - if (!priv->manager_proxy) { - g_task_return_error (task, error); + 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; } - init_common (self); + if (priv->name_owner_cancellable) { + /* we still wait for the name owner. Nothing to do for now. */ + return; + } - if (!priv->auto_register) { - g_task_return_boolean (task, TRUE); + if (!priv->name_owner_curr) { + /* we don't have a name owner. We are done and wait. */ + _register_state_complete (self); return; } - nm_secret_agent_old_register_async (self, - g_task_get_cancellable (task), - init_async_registered, - task); - g_steal_pointer (&task); + if (priv->registering_cancellable) { + /* we are already registering... wait longer. */ + return; + } + + nm_assert (!priv->registering_retry_source); + + if (!priv->is_registered) { + /* start registering... */ + priv->registering_cancellable = g_cancellable_new (); + _dbus_connection_call (self, + DBUS_SERVICE_DBUS, + DBUS_PATH_DBUS, + DBUS_INTERFACE_DBUS, + "GetConnectionUnixUser", + g_variant_new ("(s)", priv->name_owner_curr->str), + G_VARIANT_TYPE ("(u)"), + G_DBUS_CALL_FLAGS_NONE, + _CALL_REGISTER_TIMEOUT_MSEC, + priv->registering_cancellable, + _get_connection_unix_user_cb, + self); + return; + } + + /* we are fully registered and done. */ + _register_state_complete (self); } static void -init_async_got_bus (GObject *initable, GAsyncResult *result, gpointer user_data) +_register_state_change (NMSecretAgentOld *self) { - gs_unref_object GTask *task = user_data; - NMSecretAgentOld *self = g_task_get_source_object (task); + _nm_unused gs_unref_object NMSecretAgentOld *self_keep_alive = g_object_ref (self); NMSecretAgentOldPrivate *priv = NM_SECRET_AGENT_OLD_GET_PRIVATE (self); - GError *error = NULL; + nm_auto_pop_gmaincontext GMainContext *dbus_context = NULL; - priv->bus = g_bus_get_finish (result, &error); - if (!priv->bus) { - g_task_return_error (task, error); + if (priv->register_state_change_reenter == 0) { + /* We are not yet initialized. Do nothing. */ return; } - nmdbus_agent_manager_proxy_new (priv->bus, - 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), - init_async_got_proxy, - task); - g_steal_pointer (&task); + if (priv->register_state_change_reenter != 1) { + /* Recursive calls are prevented. Do nothing for now, but repeat + * the state change afterwards. */ + priv->register_state_change_reenter = 3; + return; + } + + dbus_context = nm_g_main_context_push_thread_default_if_necessary (priv->dbus_context); + +again: + priv->register_state_change_reenter = 2; + + _register_state_change_do (self); + + if (priv->register_state_change_reenter != 2) + goto again; + + priv->register_state_change_reenter = 1; +} + +/*****************************************************************************/ + +static void +_init_complete (NMSecretAgentOld *self, + GError *error_take) +{ + NMSecretAgentOldPrivate *priv = NM_SECRET_AGENT_OLD_GET_PRIVATE (self); + gs_free_error GError *error = g_steal_pointer (&error_take); + GError *error_cancelled = NULL; + + if (!priv->init_data) + return; + + if (g_cancellable_set_error_if_cancelled (priv->init_data->cancellable, &error_cancelled)) { + g_clear_error (&error); + g_propagate_error (&error, error_cancelled); + } + + priv->is_initialized = (!error); + + _LOGT ("%s init complete with %s%s%s", + priv->init_data->is_sync ? "sync" : "async", + NM_PRINT_FMT_QUOTED (error_take, "error: ", error_take->message, "", "success")); + + nml_init_data_return (g_steal_pointer (&priv->init_data), + g_steal_pointer (&error)); +} + +static void +_init_register_object (NMSecretAgentOld *self) +{ + NMSecretAgentOldPrivate *priv = NM_SECRET_AGENT_OLD_GET_PRIVATE (self); + gs_free_error GError *error = NULL; + GDBusInterfaceVTable interface_vtable = { + .method_call = _method_call, + }; + + if (g_cancellable_set_error_if_cancelled (priv->init_data->cancellable, &error)) { + _init_complete (self, g_steal_pointer (&error)); + return; + } + + priv->exported_id = g_dbus_connection_register_object (priv->dbus_connection, + NM_DBUS_PATH_SECRET_AGENT, + (GDBusInterfaceInfo*) &interface_info, + &interface_vtable, + self, + NULL, + &error); + if (priv->exported_id == 0) { + _init_complete (self, g_steal_pointer (&error)); + return; + } + + priv->register_state_change_reenter = 1; + + _register_state_change (self); +} + +static void +_init_got_bus (GObject *initable, GAsyncResult *result, gpointer user_data) +{ + NMSecretAgentOld *self = user_data; + NMSecretAgentOldPrivate *priv = NM_SECRET_AGENT_OLD_GET_PRIVATE (self); + gs_free_error GError *error = NULL; + + priv->dbus_connection = g_bus_get_finish (result, &error); + if (!priv->dbus_connection) { + _init_complete (self, g_steal_pointer (&error)); + return; + } + + _LOGT ("init: got GDBusConnection"); + + _notify (self, PROP_DBUS_CONNECTION); + + _init_register_object (self); +} + +static void +_init_start (NMSecretAgentOld *self) +{ + NMSecretAgentOldPrivate *priv = NM_SECRET_AGENT_OLD_GET_PRIVATE (self); + + if (!priv->dbus_connection) { + GBusType bus_type; + + bus_type = _nm_dbus_bus_type (); + + priv->session_bus = (bus_type == G_BUS_TYPE_SESSION); + + g_bus_get (bus_type, + priv->init_data->cancellable, + _init_got_bus, + self); + return; + } + + _init_register_object (self); +} + +static void +init_async (GAsyncInitable *initable, + int io_priority, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + NMSecretAgentOld *self; + NMSecretAgentOldClass *klass; + NMSecretAgentOldPrivate *priv; + nm_auto_pop_gmaincontext GMainContext *dbus_context = NULL; + gs_unref_object GTask *task = NULL; + + g_return_if_fail (NM_IS_SECRET_AGENT_OLD (initable)); + + self = NM_SECRET_AGENT_OLD (initable); + priv = NM_SECRET_AGENT_OLD_GET_PRIVATE (self); + + g_return_if_fail (!priv->dbus_context); + g_return_if_fail (!priv->is_destroyed); + + klass = NM_SECRET_AGENT_OLD_GET_CLASS (self); + g_return_if_fail (klass->get_secrets); + g_return_if_fail (klass->cancel_get_secrets); + g_return_if_fail (klass->save_secrets); + g_return_if_fail (klass->delete_secrets); + + _LOGT ("init-async starting..."); + + priv->dbus_context = g_main_context_ref (priv->main_context); + + dbus_context = nm_g_main_context_push_thread_default_if_necessary (priv->dbus_context); + + task = nm_g_task_new (self, cancellable, init_async, callback, user_data); + g_task_set_priority (task, io_priority); + + priv->init_data = nml_init_data_new_async (cancellable, g_steal_pointer (&task)); + + _init_start (self); +} + +static gboolean +init_finish (GAsyncInitable *initable, GAsyncResult *result, GError **error) +{ + g_return_val_if_fail (NM_IS_SECRET_AGENT_OLD (initable), FALSE); + g_return_val_if_fail (nm_g_task_is_valid (result, initable, init_async), FALSE); + + return g_task_propagate_boolean (G_TASK (result), error); +} + +/*****************************************************************************/ + +static gboolean +init_sync (GInitable *initable, + GCancellable *cancellable, + GError **error) +{ + gs_unref_object NMSecretAgentOld *self = NULL; + NMSecretAgentOldPrivate *priv; + NMSecretAgentOldClass *klass; + GMainLoop *main_loop; + GError *local_error = NULL; + + g_return_val_if_fail (NM_IS_SECRET_AGENT_OLD (initable), FALSE); + + self = g_object_ref (NM_SECRET_AGENT_OLD (initable)); + priv = NM_SECRET_AGENT_OLD_GET_PRIVATE (self); + + g_return_val_if_fail (!priv->dbus_context, FALSE); + g_return_val_if_fail (!priv->is_destroyed, FALSE); + + klass = NM_SECRET_AGENT_OLD_GET_CLASS (self); + g_return_val_if_fail (klass->get_secrets, FALSE); + g_return_val_if_fail (klass->cancel_get_secrets, FALSE); + g_return_val_if_fail (klass->save_secrets, FALSE); + g_return_val_if_fail (klass->delete_secrets, FALSE); + + _LOGT ("init-sync"); + + /* See NMClient's sync-init method for explanation about why we create + * an internal GMainContext priv->dbus_context. */ + + priv->dbus_context = g_main_context_new (); + + g_main_context_push_thread_default (priv->dbus_context); + + main_loop = g_main_loop_new (priv->dbus_context, FALSE); + + priv->init_data = nml_init_data_new_sync (cancellable, main_loop, &local_error); + + _init_start (self); + + g_main_loop_run (main_loop); + + g_main_loop_unref (main_loop); + + g_main_context_pop_thread_default (priv->dbus_context); + + nm_context_busy_watcher_integrate_source (priv->main_context, + priv->dbus_context, + priv->context_busy_watcher); + + if (local_error) { + g_propagate_error (error, local_error); + return FALSE; + } + + return TRUE; } /*****************************************************************************/ @@ -1164,6 +1804,9 @@ get_property (GObject *object, NMSecretAgentOldPrivate *priv = NM_SECRET_AGENT_OLD_GET_PRIVATE (object); switch (prop_id) { + case PROP_DBUS_CONNECTION: + g_value_set_object (value, priv->dbus_connection); + break; case PROP_IDENTIFIER: g_value_set_string (value, priv->identifier); break; @@ -1171,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); @@ -1188,23 +1831,34 @@ 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: + /* construct-only */ + 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); @@ -1222,94 +1876,46 @@ nm_secret_agent_old_init (NMSecretAgentOld *self) _LOGT ("create new instance"); c_list_init (&priv->gsi_lst_head); - 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); -} - -static gboolean -init_sync (GInitable *initable, GCancellable *cancellable, GError **error) -{ - NMSecretAgentOld *self = NM_SECRET_AGENT_OLD (initable); - NMSecretAgentOldPrivate *priv = NM_SECRET_AGENT_OLD_GET_PRIVATE (self); - - _LOGT ("init-sync"); - - priv->bus = g_bus_get_sync (_nm_dbus_bus_type (), cancellable, error); - if (!priv->bus) - return FALSE; - - priv->manager_proxy = nmdbus_agent_manager_proxy_new_sync (priv->bus, - 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; + c_list_init (&priv->pending_tasks_register_lst_head); - init_common (self); - - if (priv->auto_register) - return nm_secret_agent_old_register (self, cancellable, error); - else - return TRUE; + priv->main_context = g_main_context_ref_thread_default (); + priv->context_busy_watcher = g_object_new (G_TYPE_OBJECT, NULL); } static void -init_async (GAsyncInitable *initable, int io_priority, - GCancellable *cancellable, GAsyncReadyCallback callback, - gpointer user_data) +dispose (GObject *object) { - NMSecretAgentOld *self = NM_SECRET_AGENT_OLD (initable); - GTask *task; + NMSecretAgentOld *self = NM_SECRET_AGENT_OLD (object); - _LOGT ("init-async starting..."); + _LOGT ("disposing"); - task = g_task_new (self, cancellable, callback, user_data); - g_task_set_priority (task, io_priority); + _secret_agent_old_destroy (self); - g_bus_get (_nm_dbus_bus_type (), - cancellable, - init_async_got_bus, - task); + 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_clear_object (&priv->bus); - - G_OBJECT_CLASS (nm_secret_agent_old_parent_class)->dispose (object); + G_OBJECT_CLASS (nm_secret_agent_old_parent_class)->finalize (object); } static void @@ -1319,9 +1925,26 @@ 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: + * + * The #GDBusConnection used by the instance. You may either set this + * as construct-only property, or otherwise #NMSecretAgentOld will choose + * a connection via g_bus_get() during initialization. + * + * Since: 1.24 + **/ + obj_properties[PROP_DBUS_CONNECTION] = + g_param_spec_object (NM_SECRET_AGENT_OLD_DBUS_CONNECTION, "", "", + G_TYPE_DBUS_CONNECTION, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS); /** * NMSecretAgentOld:identifier: @@ -1350,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, "", "", @@ -1384,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, "", "", @@ -1405,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 af0dabe729..07deee2ce4 100644 --- a/libnm/nm-secret-agent-old.h +++ b/libnm/nm-secret-agent-old.h @@ -21,6 +21,7 @@ G_BEGIN_DECLS #define NM_SECRET_AGENT_OLD_AUTO_REGISTER "auto-register" #define NM_SECRET_AGENT_OLD_REGISTERED "registered" #define NM_SECRET_AGENT_OLD_CAPABILITIES "capabilities" +#define NM_SECRET_AGENT_OLD_DBUS_CONNECTION "dbus-connection" /** * NMSecretAgentOld: @@ -126,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, @@ -140,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, @@ -179,9 +185,26 @@ typedef struct { GType nm_secret_agent_old_get_type (void); -gboolean nm_secret_agent_old_register (NMSecretAgentOld *self, - GCancellable *cancellable, - GError **error); +NM_AVAILABLE_IN_1_24 +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); + +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, @@ -190,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-remote-settings-client.c b/libnm/tests/test-remote-settings-client.c index 429eecb235..f8771a988e 100644 --- a/libnm/tests/test-remote-settings-client.c +++ b/libnm/tests/test-remote-settings-client.c @@ -62,7 +62,7 @@ test_add_connection (void) add_cb, &done); - nmtst_main_context_iterate_until (NULL, 5000, done); + nmtst_main_context_iterate_until_assert (NULL, 5000, done); g_assert (gl.remote != NULL); @@ -151,7 +151,7 @@ test_make_invisible (void) set_visible_cb, NULL); /* Wait for the connection to be removed */ - nmtst_main_context_iterate_until (NULL, 5000, visible_changed && connection_removed); + nmtst_main_context_iterate_until_assert (NULL, 5000, visible_changed && connection_removed); g_signal_handlers_disconnect_by_func (gl.remote, G_CALLBACK (visible_changed_cb), &visible_changed); g_signal_handlers_disconnect_by_func (gl.client, G_CALLBACK (connection_removed_cb), &connection_removed); @@ -225,7 +225,7 @@ test_make_visible (void) set_visible_cb, NULL); /* Wait for the settings service to announce the connection again */ - nmtst_main_context_iterate_until (NULL, 5000, new); + nmtst_main_context_iterate_until_assert (NULL, 5000, new); /* Ensure the new connection is the same as the one we made visible again */ g_assert (new == gl.remote); @@ -313,7 +313,7 @@ test_remove_connection (void) NULL, deleted_cb, NULL); - nmtst_main_context_iterate_until (NULL, 5000, done && !gl.remote); + nmtst_main_context_iterate_until_assert (NULL, 5000, done && !gl.remote); /* Ensure NMClient no longer has the connection */ conns = nm_client_get_connections (gl.client); @@ -378,7 +378,7 @@ test_add_remove_connection (void) add_remove_cb, &done); - nmtst_main_context_iterate_until (NULL, 5000, done); + nmtst_main_context_iterate_until_assert (NULL, 5000, done); } /*****************************************************************************/ @@ -417,7 +417,7 @@ test_add_bad_connection (void) &done); g_clear_object (&connection); - nmtst_main_context_iterate_until (NULL, 5000, done); + nmtst_main_context_iterate_until_assert (NULL, 5000, done); g_assert (gl.remote == NULL); } diff --git a/libnm/tests/test-secret-agent.c b/libnm/tests/test-secret-agent.c index fef6071df9..9bceeb588d 100644 --- a/libnm/tests/test-secret-agent.c +++ b/libnm/tests/test-secret-agent.c @@ -16,22 +16,17 @@ enum { SECRET_REQUESTED, - - LAST_SIGNAL + LAST_SIGNAL, }; static guint signals[LAST_SIGNAL] = { 0 }; -typedef NMSecretAgentOld TestSecretAgent; +typedef NMSecretAgentOld TestSecretAgent; typedef NMSecretAgentOldClass TestSecretAgentClass; GType test_secret_agent_get_type (void); -G_DEFINE_TYPE (TestSecretAgent, test_secret_agent, NM_TYPE_SECRET_AGENT_OLD) -static void -test_secret_agent_init (TestSecretAgent *agent) -{ -} +G_DEFINE_TYPE (TestSecretAgent, test_secret_agent, NM_TYPE_SECRET_AGENT_OLD) static void test_secret_agent_get_secrets (NMSecretAgentOld *agent, @@ -122,42 +117,41 @@ test_secret_agent_delete_secrets (NMSecretAgentOld *agent, } static void +test_secret_agent_init (TestSecretAgent *agent) +{ +} + +static NMSecretAgentOld * +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, auto_register, + NULL); +} + +static void test_secret_agent_class_init (TestSecretAgentClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); NMSecretAgentOldClass *agent_class = NM_SECRET_AGENT_OLD_CLASS (klass); - agent_class->get_secrets = test_secret_agent_get_secrets; + agent_class->get_secrets = test_secret_agent_get_secrets; agent_class->cancel_get_secrets = test_secret_agent_cancel_get_secrets; - agent_class->save_secrets = test_secret_agent_save_secrets; - agent_class->delete_secrets = test_secret_agent_delete_secrets; + agent_class->save_secrets = test_secret_agent_save_secrets; + agent_class->delete_secrets = test_secret_agent_delete_secrets; signals[SECRET_REQUESTED] = - g_signal_new ("secret-requested", - G_OBJECT_CLASS_TYPE (object_class), - G_SIGNAL_RUN_LAST, - 0, NULL, NULL, NULL, - G_TYPE_STRING, 4, - NM_TYPE_CONNECTION, - G_TYPE_STRING, - G_TYPE_STRING, - G_TYPE_STRING); - -} - -static NMSecretAgentOld * -test_secret_agent_new (void) -{ - NMSecretAgentOld *agent; - GError *error = NULL; - - agent = g_initable_new (test_secret_agent_get_type (), NULL, &error, - NM_SECRET_AGENT_OLD_IDENTIFIER, "test-secret-agent", - NM_SECRET_AGENT_OLD_AUTO_REGISTER, FALSE, - NULL); - g_assert_no_error (error); - - return agent; + g_signal_new ("secret-requested", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + 0, NULL, NULL, NULL, + G_TYPE_STRING, 4, + NM_TYPE_CONNECTION, + G_TYPE_STRING, + G_TYPE_STRING, + G_TYPE_STRING); } /*****************************************************************************/ @@ -171,7 +165,7 @@ typedef struct { NMConnection *connection; GMainLoop *loop; - guint timeout_id; + GSource *timeout_source; char *ifname; char *con_id; @@ -179,12 +173,6 @@ typedef struct { int secrets_requested; } TestSecretAgentData; -static gboolean -timeout_assert (gpointer user_data) -{ - g_assert_not_reached (); -} - static void connection_added_cb (GObject *s, GAsyncResult *result, @@ -221,33 +209,40 @@ register_cb (GObject *object, GAsyncResult *result, gpointer user_data) static void test_setup (TestSecretAgentData *sadata, gconstpointer test_data) { - static int counter = 0; - const char *agent_notes = test_data; + static int static_counter = 0; + const int counter = static_counter++; + const char *create_agent = test_data; NMConnection *connection; NMSettingConnection *s_con; NMSettingWireless *s_wireless; GBytes *ssid; NMSetting *s_wsec; - GError *error = NULL; + gs_free_error GError *error = NULL; sadata->sinfo = nmtstc_service_init (); if (!sadata->sinfo) return; - g_assert (g_main_context_get_thread_default () == NULL); + g_assert (nm_g_main_context_is_thread_default (NULL)); sadata->client = nmtstc_client_new (TRUE); + g_assert (nm_g_main_context_is_thread_default (NULL)); + g_assert (nm_g_main_context_is_thread_default (nm_client_get_main_context (sadata->client))); + sadata->loop = g_main_loop_new (NULL, FALSE); - sadata->timeout_id = g_timeout_add_seconds (5, timeout_assert, NULL); + + sadata->timeout_source = g_timeout_source_new_seconds (5); + g_source_set_callback (sadata->timeout_source, nmtst_g_source_assert_not_called, NULL, NULL); + g_source_attach (sadata->timeout_source, NULL); sadata->ifname = g_strdup_printf ("wlan%d", counter); sadata->con_id = g_strdup_printf ("%s-%d", TEST_CON_ID_PREFIX, counter); - counter++; - /* Create the device */ - sadata->device = nmtstc_service_add_device (sadata->sinfo, sadata->client, - "AddWifiDevice", sadata->ifname); + sadata->device = nmtstc_service_add_device (sadata->sinfo, + sadata->client, + "AddWifiDevice", + sadata->ifname); /* Create the connection */ connection = nmtst_create_minimal_connection (sadata->con_id, NULL, NM_SETTING_WIRELESS_SETTING_NAME, &s_con); @@ -278,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 (); + + sadata->agent = test_secret_agent_new (auto_register); - if (!strcmp (agent_notes, "sync")) { + 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)); } } @@ -298,11 +300,22 @@ test_cleanup (TestSecretAgentData *sadata, gconstpointer test_data) { GVariant *ret; GError *error = NULL; + NMTstContextBusyWatcherData watcher_data = { }; + + g_assert (nm_g_main_context_is_thread_default (NULL)); if (!sadata->sinfo) return; + g_assert (nm_g_main_context_is_thread_default (nm_client_get_main_context (sadata->client))); + + nmtst_context_busy_watcher_add (&watcher_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); @@ -325,11 +338,21 @@ test_cleanup (TestSecretAgentData *sadata, gconstpointer test_data) nmtstc_service_cleanup (sadata->sinfo); - g_source_remove (sadata->timeout_id); + nm_clear_g_source_inst (&sadata->timeout_source); + g_main_loop_unref (sadata->loop); g_free (sadata->ifname); g_free (sadata->con_id); + + *sadata = (TestSecretAgentData) { }; + + nmtst_context_busy_watcher_wait (&watcher_data); + + while (g_main_context_iteration (NULL, FALSE)) { + } + + nmtst_main_context_assert_no_dispatch (NULL, nmtst_get_rand_uint32 () % 500); } /*****************************************************************************/ @@ -529,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); } @@ -547,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)); - /* 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); + 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); + + 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); } /*****************************************************************************/ @@ -639,25 +725,16 @@ NMTST_DEFINE (); int main (int argc, char **argv) { - int ret; - g_setenv ("LIBNM_USE_SESSION_BUS", "1", TRUE); 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); - ret = g_test_run (); - - return ret; + return g_test_run (); } - 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 diff --git a/shared/nm-glib-aux/nm-shared-utils.h b/shared/nm-glib-aux/nm-shared-utils.h index ad461d9d00..e7934cf163 100644 --- a/shared/nm-glib-aux/nm-shared-utils.h +++ b/shared/nm-glib-aux/nm-shared-utils.h @@ -1026,6 +1026,25 @@ nm_g_main_context_push_thread_default (GMainContext *context) return context; } +static inline gboolean +nm_g_main_context_is_thread_default (GMainContext *context) +{ + GMainContext *cur_context; + + cur_context = g_main_context_get_thread_default (); + if (cur_context == context) + return TRUE; + + if (G_UNLIKELY (!cur_context)) + cur_context = g_main_context_default (); + else if (G_UNLIKELY (!context)) + context = g_main_context_default (); + else + return FALSE; + + return (cur_context == context); +} + static inline GMainContext * nm_g_main_context_push_thread_default_if_necessary (GMainContext *context) { @@ -1198,8 +1217,9 @@ _nm_utils_strv_equal (char **strv1, char **strv2) /*****************************************************************************/ #define NM_UTILS_NSEC_PER_SEC ((gint64) 1000000000) -#define NM_UTILS_NSEC_PER_MSEC ((gint64) 1000000) +#define NM_UTILS_USEC_PER_SEC ((gint64) 1000000) #define NM_UTILS_MSEC_PER_SEC ((gint64) 1000) +#define NM_UTILS_NSEC_PER_MSEC ((gint64) 1000000) static inline gint64 NM_UTILS_NSEC_TO_MSEC_CEIL (gint64 nsec) diff --git a/shared/nm-test-libnm-utils.h b/shared/nm-test-libnm-utils.h index 63e3b4c72c..06dbc72d71 100644 --- a/shared/nm-test-libnm-utils.h +++ b/shared/nm-test-libnm-utils.h @@ -67,4 +67,18 @@ void nmtstc_service_update_connection_variant (NMTstcServiceInfo *sinfo, GVariant *connection, gboolean verify_connection); -NMClient *nmtstc_client_new (gboolean allow_iterate_main_context); +gpointer nmtstc_context_object_new_valist (GType gtype, + gboolean allow_iterate_main_context, + const char *first_property_name, + va_list var_args); + +gpointer nmtstc_context_object_new (GType gtype, + gboolean allow_iterate_main_context, + const char *first_property_name, + ...); + +static inline NMClient * +nmtstc_client_new (gboolean allow_iterate_main_context) +{ + return nmtstc_context_object_new (NM_TYPE_CLIENT, allow_iterate_main_context, NULL); +} diff --git a/shared/nm-test-utils-impl.c b/shared/nm-test-utils-impl.c index 43176f4c7e..0ef6591f32 100644 --- a/shared/nm-test-utils-impl.c +++ b/shared/nm-test-utils-impl.c @@ -216,7 +216,7 @@ again_wait: typedef struct { GMainLoop *loop; const char *ifname; - char *path; + const char *path; NMDevice *device; } AddDeviceInfo; @@ -227,21 +227,17 @@ device_added_cb (NMClient *client, { AddDeviceInfo *info = user_data; - g_assert (device); + g_assert (info); + g_assert (!info->device); + + g_assert (NM_IS_DEVICE (device)); g_assert_cmpstr (nm_object_get_path (NM_OBJECT (device)), ==, info->path); g_assert_cmpstr (nm_device_get_iface (device), ==, info->ifname); - info->device = device; + info->device = g_object_ref (device); g_main_loop_quit (info->loop); } -static gboolean -timeout (gpointer user_data) -{ - g_assert_not_reached (); - return G_SOURCE_REMOVE; -} - static GVariant * call_add_wired_device (GDBusProxy *proxy, const char *ifname, const char *hwaddr, const char **subchannels, GError **error) @@ -275,39 +271,53 @@ call_add_device (GDBusProxy *proxy, const char *method, const char *ifname, GErr } static NMDevice * -add_device_common (NMTstcServiceInfo *sinfo, NMClient *client, - const char *method, const char *ifname, - const char *hwaddr, const char **subchannels) +add_device_common (NMTstcServiceInfo *sinfo, + NMClient *client, + const char *method, + const char *ifname, + const char *hwaddr, + const char **subchannels) { + nm_auto_unref_gmainloop GMainLoop *loop = NULL; + gs_unref_variant GVariant *ret = NULL; + gs_free_error GError *error = NULL; AddDeviceInfo info; - GError *error = NULL; - GVariant *ret; - guint timeout_id; - if (g_strcmp0 (method, "AddWiredDevice") == 0) + g_assert (sinfo); + g_assert (NM_IS_CLIENT (client)); + + if (nm_streq0 (method, "AddWiredDevice")) ret = call_add_wired_device (sinfo->proxy, ifname, hwaddr, subchannels, &error); else ret = call_add_device (sinfo->proxy, method, ifname, &error); - g_assert_no_error (error); - g_assert (ret); + nmtst_assert_success (ret, error); g_assert_cmpstr (g_variant_get_type_string (ret), ==, "(o)"); - g_variant_get (ret, "(o)", &info.path); - g_variant_unref (ret); - - /* Wait for libnm to find the device */ - info.ifname = ifname; - info.loop = g_main_loop_new (NULL, FALSE); - g_signal_connect (client, "device-added", - G_CALLBACK (device_added_cb), &info); - timeout_id = g_timeout_add_seconds (5, timeout, NULL); - g_main_loop_run (info.loop); - - g_source_remove (timeout_id); + + /* Wait for NMClient to find the device */ + + loop = g_main_loop_new (nm_client_get_main_context (client), FALSE); + + info = (AddDeviceInfo) { + .ifname = ifname, + .loop = loop, + }; + g_variant_get (ret, "(&o)", &info.path); + + g_signal_connect (client, + NM_CLIENT_DEVICE_ADDED, + G_CALLBACK (device_added_cb), + &info); + + if (!nmtst_main_loop_run (loop, 5000)) + g_assert_not_reached (); + g_signal_handlers_disconnect_by_func (client, device_added_cb, &info); - g_free (info.path); - g_main_loop_unref (info.loop); + g_assert (NM_IS_DEVICE (info.device)); + + g_assert (info.device == nm_client_get_device_by_path (client, nm_object_get_path (NM_OBJECT (info.device)))); + g_object_unref (info.device); return info.device; } @@ -410,35 +420,47 @@ nmtstc_service_update_connection_variant (NMTstcServiceInfo *sinfo, /*****************************************************************************/ typedef struct { + GType gtype; GMainLoop *loop; - NMClient *client; -} NMTstcClientNewData; + GObject *obj; + bool call_nm_client_new_async:1; +} NMTstcObjNewData; static void -_nmtstc_client_new_cb (GObject *source_object, - GAsyncResult *res, - gpointer user_data) +_context_object_new_do_cb (GObject *source_object, + GAsyncResult *res, + gpointer user_data) { - NMTstcClientNewData *d = user_data; + NMTstcObjNewData *d = user_data; gs_free_error GError *error = NULL; - g_assert (!d->client); + g_assert (!d->obj); - d->client = nm_client_new_finish (res, - nmtst_get_rand_bool () ? &error : NULL); + if (d->call_nm_client_new_async) { + d->obj = G_OBJECT (nm_client_new_finish (res, + nmtst_get_rand_bool () ? &error : NULL)); + } else { + d->obj = g_async_initable_new_finish (G_ASYNC_INITABLE (source_object), + res, + nmtst_get_rand_bool () ? &error : NULL); + } - nmtst_assert_success (NM_IS_CLIENT (d->client), error); + nmtst_assert_success (G_IS_OBJECT (d->obj), error); + g_assert (G_OBJECT_TYPE (d->obj) == d->gtype); g_main_loop_quit (d->loop); } -static NMClient * -_nmtstc_client_new (gboolean sync) +static GObject * +_context_object_new_do (GType gtype, + gboolean sync, + const gchar *first_property_name, + va_list var_args) { gs_free_error GError *error = NULL; - NMClient *client; + GObject *obj; - /* Create a NMClient instance synchronously, and arbitrarily use either + /* Create a GObject instance synchronously, and arbitrarily use either * the sync or async constructor. * * Note that the sync and async construct differ in one important aspect: @@ -456,125 +478,128 @@ _nmtstc_client_new (gboolean sync) g_source_attach (source, g_main_context_get_thread_default ()); } - if (nmtst_get_rand_bool ()) { + if ( gtype != NM_TYPE_CLIENT + || first_property_name + || nmtst_get_rand_bool ()) { gboolean success; - client = g_object_new (NM_TYPE_CLIENT, NULL); - g_assert (NM_IS_CLIENT (client)); + if ( first_property_name + || nmtst_get_rand_bool ()) + obj = g_object_new_valist (gtype, first_property_name, var_args); + else + obj = g_object_new (gtype, NULL); - success = g_initable_init (G_INITABLE (client), + success = g_initable_init (G_INITABLE (obj), NULL, nmtst_get_rand_bool () ? &error : NULL); nmtst_assert_success (success, error); } else { - client = nm_client_new (NULL, - nmtst_get_rand_bool () ? &error : NULL); + obj = G_OBJECT (nm_client_new (NULL, + nmtst_get_rand_bool () ? &error : NULL)); } } else { nm_auto_unref_gmainloop GMainLoop *loop = NULL; - NMTstcClientNewData d = { .loop = NULL, }; + NMTstcObjNewData d = { + .gtype = gtype, + .loop = NULL, + }; + gs_unref_object GObject *obj2 = NULL; loop = g_main_loop_new (g_main_context_get_thread_default (), FALSE); - d.loop = loop; - nm_client_new_async (NULL, - _nmtstc_client_new_cb, - &d); + + if ( gtype != NM_TYPE_CLIENT + || first_property_name + || nmtst_get_rand_bool ()) { + if ( first_property_name + || nmtst_get_rand_bool ()) + obj2 = g_object_new_valist (gtype, first_property_name, var_args); + else + obj2 = g_object_new (gtype, NULL); + + g_async_initable_init_async (G_ASYNC_INITABLE (obj2), + G_PRIORITY_DEFAULT, + NULL, + _context_object_new_do_cb, + &d); + } else { + d.call_nm_client_new_async = TRUE; + nm_client_new_async (NULL, + _context_object_new_do_cb, + &d); + } g_main_loop_run (loop); - g_assert (NM_IS_CLIENT (d.client)); - client = d.client; + obj = d.obj; + g_assert (!obj2 || obj == obj2); } - nmtst_assert_success (NM_IS_CLIENT (client), error); - return client; + nmtst_assert_success (G_IS_OBJECT (obj), error); + g_assert (G_OBJECT_TYPE (obj) == gtype); + return obj; } typedef struct { + GType gtype; + const char *first_property_name; + va_list var_args; GMainLoop *loop; - NMClient *client; + GObject *obj; bool sync; } NewSyncInsideDispatchedData; static gboolean -_nmtstc_client_new_inside_loop_do (gpointer user_data) +_context_object_new_inside_loop_do (gpointer user_data) { NewSyncInsideDispatchedData *d = user_data; g_assert (d->loop); - g_assert (!d->client); + g_assert (!d->obj); - d->client = nmtstc_client_new (d->sync); + d->obj = nmtstc_context_object_new_valist (d->gtype, d->sync, d->first_property_name, d->var_args); g_main_loop_quit (d->loop); return G_SOURCE_CONTINUE; } -static NMClient * -_nmtstc_client_new_inside_loop (gboolean sync) +static GObject * +_context_object_new_inside_loop (GType gtype, + gboolean sync, + const char *first_property_name, + va_list var_args) { GMainContext *context = g_main_context_get_thread_default (); nm_auto_unref_gmainloop GMainLoop *loop = g_main_loop_new (context, FALSE); NewSyncInsideDispatchedData d = { - .sync = sync, - .loop = loop, + .gtype = gtype, + .first_property_name = first_property_name, + .sync = sync, + .loop = loop, }; nm_auto_destroy_and_unref_gsource GSource *source = NULL; + va_copy (d.var_args, var_args); + source = g_idle_source_new (); - g_source_set_callback (source, _nmtstc_client_new_inside_loop_do, &d, NULL); + g_source_set_callback (source, _context_object_new_inside_loop_do, &d, NULL); g_source_attach (source, context); g_main_loop_run (loop); - g_assert (NM_IS_CLIENT (d.client)); - return d.client; -} - -static NMClient * -_nmtstc_client_new_extra_context (void) -{ - GMainContext *inner_context; - NMClient *client; - GSource *source; - guint key_idx; - - inner_context = g_main_context_new (); - g_main_context_push_thread_default (inner_context); - - client = nmtstc_client_new (TRUE); - source = nm_utils_g_main_context_create_integrate_source (inner_context); + va_end (d.var_args); - g_main_context_pop_thread_default (inner_context); - g_main_context_unref (inner_context); - - g_source_attach (source, g_main_context_get_thread_default ()); - - for (key_idx = 0; TRUE; key_idx++) { - char s[100]; - - /* nmtstc_client_new() may call _nmtstc_client_new_extra_context() repeatedly. We - * need to attach the source to a previously unused key. */ - nm_sprintf_buf (s, "nm-test-extra-context-%u", key_idx); - if (!g_object_get_data (G_OBJECT (client), s)) { - g_object_set_data_full (G_OBJECT (client), - s, - source, - (GDestroyNotify) nm_g_source_destroy_and_unref); - break; - } - } - - return client; + g_assert (G_IS_OBJECT (d.obj)); + g_assert (G_OBJECT_TYPE (d.obj) == gtype); + return d.obj; } -NMClient * -nmtstc_client_new (gboolean allow_iterate_main_context) +gpointer +nmtstc_context_object_new_valist (GType gtype, + gboolean allow_iterate_main_context, + const char *first_property_name, + va_list var_args) { gboolean inside_loop; gboolean sync; - if (nmtst_get_rand_uint32 () % 5 == 0) - return _nmtstc_client_new_extra_context (); - if (!allow_iterate_main_context) { sync = TRUE; inside_loop = FALSE; @@ -587,11 +612,26 @@ nmtstc_client_new (gboolean allow_iterate_main_context) } if (inside_loop) { - /* Create the client on an idle handler of the current context. + /* Create the obj on an idle handler of the current context. * In practice, it should make no difference, which this check * tries to prove. */ - return _nmtstc_client_new_inside_loop (sync); + return _context_object_new_inside_loop (gtype, sync, first_property_name, var_args); } - return _nmtstc_client_new (sync); + return _context_object_new_do (gtype, sync, first_property_name, var_args); +} + +gpointer +nmtstc_context_object_new (GType gtype, + gboolean allow_iterate_main_context, + const char *first_property_name, + ...) +{ + GObject *obj; + va_list var_args; + + va_start (var_args, first_property_name); + obj = nmtstc_context_object_new_valist (gtype, allow_iterate_main_context, first_property_name, var_args); + va_end (var_args); + return obj; } diff --git a/shared/nm-utils/nm-test-utils.h b/shared/nm-utils/nm-test-utils.h index 495aa0b35c..a3938e0121 100644 --- a/shared/nm-utils/nm-test-utils.h +++ b/shared/nm-utils/nm-test-utils.h @@ -968,6 +968,26 @@ nmtst_rand_perm_gslist (GRand *rand, GSList *list) /*****************************************************************************/ static inline gboolean +nmtst_g_source_assert_not_called (gpointer user_data) +{ + g_assert_not_reached (); + return G_SOURCE_CONTINUE; +} + +static inline gboolean +nmtst_g_source_set_boolean_true (gpointer user_data) +{ + gboolean *ptr = user_data; + + g_assert (ptr); + g_assert (!*ptr); + *ptr = TRUE; + return G_SOURCE_CONTINUE; +} + +/*****************************************************************************/ + +static inline gboolean _nmtst_main_loop_run_timeout (gpointer user_data) { GMainLoop **p_loop = user_data; @@ -1010,36 +1030,123 @@ _nmtst_main_loop_quit_on_notify (GObject *object, GParamSpec *pspec, gpointer us } #define nmtst_main_loop_quit_on_notify ((GCallback) _nmtst_main_loop_quit_on_notify) -static inline gboolean -_nmtst_main_context_iterate_until_timeout (gpointer user_data) -{ - gboolean *p_had_pointer = user_data; - - g_assert (!*p_had_pointer); - *p_had_pointer = TRUE; - return G_SOURCE_CONTINUE; -} - #define nmtst_main_context_iterate_until(context, timeout_msec, condition) \ - G_STMT_START { \ + ({ \ nm_auto_destroy_and_unref_gsource GSource *_source = NULL; \ GMainContext *_context = (context); \ gboolean _had_timeout = FALSE; \ \ _source = g_timeout_source_new (timeout_msec); \ - g_source_set_callback (_source, _nmtst_main_context_iterate_until_timeout, &_had_timeout, NULL); \ + g_source_set_callback (_source, nmtst_g_source_set_boolean_true, &_had_timeout, NULL); \ g_source_attach (_source, _context); \ \ while (TRUE) { \ if (condition) \ break; \ g_main_context_iteration (_context, TRUE); \ - g_assert (!_had_timeout && #condition); \ + if (_had_timeout) \ + break; \ } \ + \ + !_had_timeout; \ + }) + +#define nmtst_main_context_iterate_until_assert(context, timeout_msec, condition) \ + G_STMT_START { \ + if (!nmtst_main_context_iterate_until (context, timeout_msec, condition)) \ + g_assert (FALSE && #condition); \ } G_STMT_END /*****************************************************************************/ +static inline void +nmtst_main_context_assert_no_dispatch (GMainContext *context, + guint timeout_msec) +{ + nm_auto_destroy_and_unref_gsource GSource *source = NULL; + gboolean timeout_hit = FALSE; + + source = g_timeout_source_new (timeout_msec); + g_source_set_callback (source, nmtst_g_source_set_boolean_true, &timeout_hit, NULL); + g_source_attach (source, context); + + while (g_main_context_iteration (context, TRUE)) { + if (timeout_hit) + return; + g_assert_not_reached (); + } +} + +/*****************************************************************************/ + +typedef struct { + GMainLoop *_main_loop; + union { + GSList *_list; + const void *const is_waiting; + }; +} NMTstContextBusyWatcherData; + +static inline void +_nmtst_context_busy_watcher_add_cb (gpointer data, + GObject *where_the_object_was) +{ + NMTstContextBusyWatcherData *watcher_data = data; + GSList *l; + + g_assert (watcher_data); + + l = g_slist_find (watcher_data->_list, where_the_object_was); + g_assert (l); + + watcher_data->_list = g_slist_delete_link (watcher_data->_list, l); + if (!watcher_data->_list) + g_main_loop_quit (watcher_data->_main_loop); +} + +static inline void +nmtst_context_busy_watcher_add (NMTstContextBusyWatcherData *watcher_data, + GObject *object) +{ + g_assert (watcher_data); + g_assert (G_IS_OBJECT (object)); + + if (!watcher_data->_main_loop) { + watcher_data->_main_loop = g_main_loop_new (g_main_context_get_thread_default (), + FALSE); + g_assert (!watcher_data->_list); + } else { + g_assert ( g_main_loop_get_context (watcher_data->_main_loop) + == (g_main_context_get_thread_default () ?: g_main_context_default ())); + } + + g_object_weak_ref (object, + _nmtst_context_busy_watcher_add_cb, + watcher_data); + watcher_data->_list = g_slist_prepend (watcher_data->_list, object); +} + +static inline void +nmtst_context_busy_watcher_wait (NMTstContextBusyWatcherData *watcher_data) +{ + g_assert (watcher_data); + + if (!watcher_data->_main_loop) { + g_assert (!watcher_data->_list); + return; + } + + if (watcher_data->_list) { + if (!nmtst_main_loop_run (watcher_data->_main_loop, 5000)) + g_error ("timeout running mainloop waiting for GObject to destruct"); + } + + g_assert (!watcher_data->_list); + nm_clear_pointer (&watcher_data->_main_loop, g_main_loop_unref); +} + +/*****************************************************************************/ + static inline const char * nmtst_get_sudo_cmd (void) { @@ -2323,13 +2430,4 @@ nmtst_keyfile_get_num_keys (GKeyFile *keyfile, /*****************************************************************************/ -static inline gboolean -nmtst_g_source_assert_not_called (gpointer user_data) -{ - g_assert_not_reached (); - return G_SOURCE_CONTINUE; -} - -/*****************************************************************************/ - #endif /* __NM_TEST_UTILS_H__ */ |