daemon: Wait for reload before servicing list_cached_users

When /etc/passwd, /etc/shadow or /etc/group are changed outside of
AccountsService, the cache reload is delayed by 500 ms so subsequent
changes to these files are process seen together and AccountsService has
a consistent view of the data (since after one of these files is changed
the others may change too).

If ListCachedUsers is called in this 500 ms window,
finish_list_cached_users will be executed before reload_users_timeout
has been dispatched, since its added to the mainloop as an idler and at
point there is nothing preventing it from being executed. This makes
finish_list_cached_users only be attached to the mainloop after
reload_users_timeout has been dispatched.

This bug was introduced by commit 4e3fad33 when the 500 ms delay was
implemented.

Closes: #71
parent f381c683
Pipeline #31491 passed with stage
in 2 minutes and 30 seconds
...@@ -72,6 +72,8 @@ typedef struct { ...@@ -72,6 +72,8 @@ typedef struct {
GFileMonitor *gdm_monitor; GFileMonitor *gdm_monitor;
GFileMonitor *wtmp_monitor; GFileMonitor *wtmp_monitor;
GQueue *pending_list_cached_users;
guint reload_id; guint reload_id;
guint autologin_id; guint autologin_id;
...@@ -81,6 +83,15 @@ typedef struct { ...@@ -81,6 +83,15 @@ typedef struct {
typedef struct passwd * (* EntryGeneratorFunc) (Daemon *, GHashTable *, gpointer *, struct spwd **shadow_entry); typedef struct passwd * (* EntryGeneratorFunc) (Daemon *, GHashTable *, gpointer *, struct spwd **shadow_entry);
typedef struct {
Daemon *daemon;
GDBusMethodInvocation *context;
} ListUserData;
static void finish_list_cached_users (ListUserData *data);
static void list_user_data_free (ListUserData *data);
static void daemon_accounts_accounts_iface_init (AccountsAccountsIface *iface); static void daemon_accounts_accounts_iface_init (AccountsAccountsIface *iface);
G_DEFINE_TYPE_WITH_CODE (Daemon, daemon, ACCOUNTS_TYPE_ACCOUNTS_SKELETON, G_ADD_PRIVATE (Daemon) G_IMPLEMENT_INTERFACE (ACCOUNTS_TYPE_ACCOUNTS, daemon_accounts_accounts_iface_init)); G_DEFINE_TYPE_WITH_CODE (Daemon, daemon, ACCOUNTS_TYPE_ACCOUNTS_SKELETON, G_ADD_PRIVATE (Daemon) G_IMPLEMENT_INTERFACE (ACCOUNTS_TYPE_ACCOUNTS, daemon_accounts_accounts_iface_init));
...@@ -546,6 +557,10 @@ reload_users_timeout (Daemon *daemon) ...@@ -546,6 +557,10 @@ reload_users_timeout (Daemon *daemon)
reload_users (daemon); reload_users (daemon);
priv->reload_id = 0; priv->reload_id = 0;
g_queue_foreach (priv->pending_list_cached_users,
(GFunc) finish_list_cached_users, NULL);
g_queue_clear (priv->pending_list_cached_users);
return FALSE; return FALSE;
} }
...@@ -716,6 +731,8 @@ daemon_init (Daemon *daemon) ...@@ -716,6 +731,8 @@ daemon_init (Daemon *daemon)
priv->users = create_users_hash_table (); priv->users = create_users_hash_table ();
priv->pending_list_cached_users = g_queue_new ();
priv->passwd_monitor = setup_monitor (daemon, priv->passwd_monitor = setup_monitor (daemon,
PATH_PASSWD, PATH_PASSWD,
on_users_monitor_changed); on_users_monitor_changed);
...@@ -751,6 +768,9 @@ daemon_finalize (GObject *object) ...@@ -751,6 +768,9 @@ daemon_finalize (GObject *object)
if (priv->bus_connection != NULL) if (priv->bus_connection != NULL)
g_object_unref (priv->bus_connection); g_object_unref (priv->bus_connection);
g_queue_free_full (priv->pending_list_cached_users,
(GDestroyNotify) list_user_data_free);
g_list_free_full (priv->explicitly_requested_users, g_free); g_list_free_full (priv->explicitly_requested_users, g_free);
g_hash_table_destroy (priv->users); g_hash_table_destroy (priv->users);
...@@ -946,12 +966,6 @@ daemon_find_user_by_name (AccountsAccounts *accounts, ...@@ -946,12 +966,6 @@ daemon_find_user_by_name (AccountsAccounts *accounts,
return TRUE; return TRUE;
} }
typedef struct {
Daemon *daemon;
GDBusMethodInvocation *context;
} ListUserData;
static ListUserData * static ListUserData *
list_user_data_new (Daemon *daemon, list_user_data_new (Daemon *daemon,
GDBusMethodInvocation *context) GDBusMethodInvocation *context)
...@@ -973,10 +987,9 @@ list_user_data_free (ListUserData *data) ...@@ -973,10 +987,9 @@ list_user_data_free (ListUserData *data)
g_free (data); g_free (data);
} }
static gboolean static void
finish_list_cached_users (gpointer user_data) finish_list_cached_users (ListUserData *data)
{ {
ListUserData *data = user_data;
DaemonPrivate *priv = daemon_get_instance_private (data->daemon); DaemonPrivate *priv = daemon_get_instance_private (data->daemon);
g_autoptr(GPtrArray) object_paths = NULL; g_autoptr(GPtrArray) object_paths = NULL;
GHashTableIter iter; GHashTableIter iter;
...@@ -1012,8 +1025,6 @@ finish_list_cached_users (gpointer user_data) ...@@ -1012,8 +1025,6 @@ finish_list_cached_users (gpointer user_data)
accounts_accounts_complete_list_cached_users (NULL, data->context, (const gchar * const *) object_paths->pdata); accounts_accounts_complete_list_cached_users (NULL, data->context, (const gchar * const *) object_paths->pdata);
list_user_data_free (data); list_user_data_free (data);
return FALSE;
} }
static gboolean static gboolean
...@@ -1027,8 +1038,8 @@ daemon_list_cached_users (AccountsAccounts *accounts, ...@@ -1027,8 +1038,8 @@ daemon_list_cached_users (AccountsAccounts *accounts,
data = list_user_data_new (daemon, context); data = list_user_data_new (daemon, context);
if (priv->reload_id > 0) { if (priv->reload_id > 0) {
/* reload in progress, wait a bit */ /* reload pending -- finish call in reload_users_timeout */
g_idle_add (finish_list_cached_users, data); g_queue_push_tail (priv->pending_list_cached_users, data);
} }
else { else {
finish_list_cached_users (data); finish_list_cached_users (data);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment