diff --git a/.accountsservice.metadata b/.accountsservice.metadata new file mode 100644 index 0000000..794dd8f --- /dev/null +++ b/.accountsservice.metadata @@ -0,0 +1 @@ +e9d13e6970c52e168eb7d6dc8441a3abafed3dfa SOURCES/accountsservice-0.6.45.tar.xz diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..677f17f --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +SOURCES/accountsservice-0.6.45.tar.xz diff --git a/README.md b/README.md deleted file mode 100644 index 0e7897f..0000000 --- a/README.md +++ /dev/null @@ -1,5 +0,0 @@ -The master branch has no content - -Look at the c7 branch if you are working with CentOS-7, or the c4/c5/c6 branch for CentOS-4, 5 or 6 - -If you find this file in a distro specific branch, it means that no content has been checked in yet diff --git a/SOURCES/0001-daemon-don-t-treat-explicitly-requested-users-as-cac.patch b/SOURCES/0001-daemon-don-t-treat-explicitly-requested-users-as-cac.patch new file mode 100644 index 0000000..fba593f --- /dev/null +++ b/SOURCES/0001-daemon-don-t-treat-explicitly-requested-users-as-cac.patch @@ -0,0 +1,739 @@ +From 59235d291c9aac5f68e17cc927f142cf5e532e46 Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Thu, 4 May 2017 12:04:05 -0400 +Subject: [PATCH] daemon: don't treat explicitly requested users as "cached" + +The ListCachedUsers method currently returns users that have +been explicitly requested by a client. It's weird that merely +querying a user can make it show up in login screen user lists. +Furthermore, UncacheUser is broken since commit +177509e9460b149ecbf85e75c930be2ea00b7d05 because the user has +been explicitly requested in order to uncache it. So trying +to uncache a user inadvertently caches the user. + +This commit fixes that. +--- + src/daemon.c | 71 +++++++++++++++++++++++++++++++++++++++--------------------- + src/user.c | 17 +++++++++++++++ + src/user.h | 3 +++ + 3 files changed, 66 insertions(+), 25 deletions(-) + +diff --git a/src/daemon.c b/src/daemon.c +index 312394a..6e3e4b3 100644 +--- a/src/daemon.c ++++ b/src/daemon.c +@@ -329,100 +329,108 @@ entry_generator_requested_users (Daemon *daemon, + while (node != NULL) { + const char *name; + + name = node->data; + node = node->next; + + *state = node; + + if (!g_hash_table_lookup (users, name)) { + pwent = getpwnam (name); + if (pwent == NULL) { + g_debug ("user '%s' requested previously but not present on system", name); + } else { + *shadow_entry = getspnam (pwent->pw_name); + + return pwent; + } + } + } + } + + /* Last iteration */ + + *state = NULL; + return NULL; + } + + static void + load_entries (Daemon *daemon, + GHashTable *users, +- gboolean allow_system_users, ++ gboolean explicitly_requested, + EntryGeneratorFunc entry_generator) + { + gpointer generator_state = NULL; + struct passwd *pwent; + struct spwd *spent = NULL; + User *user = NULL; + + g_assert (entry_generator != NULL); + + for (;;) { + spent = NULL; + pwent = entry_generator (daemon, users, &generator_state, &spent); + if (pwent == NULL) + break; + + /* Skip system users... */ +- if (!allow_system_users && !user_classify_is_human (pwent->pw_uid, pwent->pw_name, pwent->pw_shell, spent? spent->sp_pwdp : NULL)) { ++ if (!explicitly_requested && !user_classify_is_human (pwent->pw_uid, pwent->pw_name, pwent->pw_shell, spent? spent->sp_pwdp : NULL)) { + g_debug ("skipping user: %s", pwent->pw_name); + continue; + } + +- /* ignore duplicate entries */ +- if (g_hash_table_lookup (users, pwent->pw_name)) { +- continue; +- } ++ /* Only process users that haven't been processed yet. ++ * We do always make sure entries get promoted ++ * to "cached" status if they are supposed to be ++ */ ++ ++ user = g_hash_table_lookup (users, pwent->pw_name); + +- user = g_hash_table_lookup (daemon->priv->users, pwent->pw_name); + if (user == NULL) { +- user = user_new (daemon, pwent->pw_uid); +- } else { +- g_object_ref (user); +- } ++ user = g_hash_table_lookup (daemon->priv->users, pwent->pw_name); ++ if (user == NULL) { ++ user = user_new (daemon, pwent->pw_uid); ++ } else { ++ g_object_ref (user); ++ } + +- /* freeze & update users not already in the new list */ +- g_object_freeze_notify (G_OBJECT (user)); +- user_update_from_pwent (user, pwent, spent); ++ /* freeze & update users not already in the new list */ ++ g_object_freeze_notify (G_OBJECT (user)); ++ user_update_from_pwent (user, pwent, spent); + +- g_hash_table_insert (users, g_strdup (user_get_user_name (user)), user); +- g_debug ("loaded user: %s", user_get_user_name (user)); ++ g_hash_table_insert (users, g_strdup (user_get_user_name (user)), user); ++ g_debug ("loaded user: %s", user_get_user_name (user)); ++ } ++ ++ if (!explicitly_requested) { ++ user_set_cached (user, TRUE); ++ } + } + + /* Generator should have cleaned up */ + g_assert (generator_state == NULL); + } + + static GHashTable * + create_users_hash_table (void) + { + return g_hash_table_new_full (g_str_hash, + g_str_equal, + g_free, + g_object_unref); + } + + static void + reload_users (Daemon *daemon) + { + GHashTable *users; + GHashTable *old_users; + GHashTable *local; + GHashTableIter iter; + gpointer name; + User *user; + + /* Track the users that we saw during our (re)load */ + users = create_users_hash_table (); + + /* + * NOTE: As we load data from all the sources, notifies are +@@ -432,71 +440,79 @@ reload_users (Daemon *daemon) + + /* Load the local users into our hash table */ + load_entries (daemon, users, FALSE, entry_generator_fgetpwent); + local = g_hash_table_new (g_str_hash, g_str_equal); + g_hash_table_iter_init (&iter, users); + while (g_hash_table_iter_next (&iter, &name, NULL)) + g_hash_table_add (local, name); + + /* and add users to hash table that were explicitly requested */ + load_entries (daemon, users, TRUE, entry_generator_requested_users); + + /* Now add/update users from other sources, possibly non-local */ + load_entries (daemon, users, FALSE, entry_generator_cachedir); + + wtmp_helper_update_login_frequencies (users); + + /* Mark which users are local, which are not */ + g_hash_table_iter_init (&iter, users); + while (g_hash_table_iter_next (&iter, &name, (gpointer *)&user)) + user_update_local_account_property (user, g_hash_table_lookup (local, name) != NULL); + + g_hash_table_destroy (local); + + /* Swap out the users */ + old_users = daemon->priv->users; + daemon->priv->users = users; + + /* Remove all the old users */ + g_hash_table_iter_init (&iter, old_users); + while (g_hash_table_iter_next (&iter, &name, (gpointer *)&user)) { +- if (!g_hash_table_lookup (users, name)) { ++ User *refreshed_user; ++ ++ refreshed_user = g_hash_table_lookup (users, name); ++ ++ if (!refreshed_user || !user_get_cached (refreshed_user)) { + user_unregister (user); + accounts_accounts_emit_user_deleted (ACCOUNTS_ACCOUNTS (daemon), + user_get_object_path (user)); + } + } + + /* Register all the new users */ + g_hash_table_iter_init (&iter, users); + while (g_hash_table_iter_next (&iter, &name, (gpointer *)&user)) { +- if (!g_hash_table_lookup (old_users, name)) { ++ User *stale_user; ++ ++ stale_user = g_hash_table_lookup (old_users, name); ++ ++ if (!stale_user || !user_get_cached (stale_user) && user_get_cached (user)) { + user_register (user); + accounts_accounts_emit_user_added (ACCOUNTS_ACCOUNTS (daemon), + user_get_object_path (user)); + } + g_object_thaw_notify (G_OBJECT (user)); + } + + g_hash_table_destroy (old_users); + } + + static gboolean + reload_users_timeout (Daemon *daemon) + { + reload_users (daemon); + daemon->priv->reload_id = 0; + + return FALSE; + } + + static gboolean load_autologin (Daemon *daemon, + gchar **name, + gboolean *enabled, + GError **error); + + static gboolean + reload_autologin_timeout (Daemon *daemon) + { + gboolean enabled; + gchar *name = NULL; + GError *error = NULL; +@@ -911,60 +927,65 @@ list_user_data_new (Daemon *daemon, + static void + list_user_data_free (ListUserData *data) + { + g_object_unref (data->daemon); + g_free (data); + } + + static gboolean + finish_list_cached_users (gpointer user_data) + { + ListUserData *data = user_data; + GPtrArray *object_paths; + GHashTableIter iter; + const gchar *name; + User *user; + uid_t uid; + const gchar *shell; + + object_paths = g_ptr_array_new (); + + g_hash_table_iter_init (&iter, data->daemon->priv->users); + while (g_hash_table_iter_next (&iter, (gpointer *)&name, (gpointer *)&user)) { + uid = user_get_uid (user); + shell = user_get_shell (user); + + if (!user_classify_is_human (uid, name, shell, NULL)) { + g_debug ("user %s %ld excluded", name, (long) uid); + continue; + } + ++ if (!user_get_cached (user)) { ++ g_debug ("user %s %ld not cached", name, (long) uid); ++ continue; ++ } ++ + g_debug ("user %s %ld not excluded", name, (long) uid); + g_ptr_array_add (object_paths, (gpointer) user_get_object_path (user)); + } + g_ptr_array_add (object_paths, NULL); + + accounts_accounts_complete_list_cached_users (NULL, data->context, (const gchar * const *) object_paths->pdata); + + g_ptr_array_free (object_paths, TRUE); + + list_user_data_free (data); + + return FALSE; + } + + static gboolean + daemon_list_cached_users (AccountsAccounts *accounts, + GDBusMethodInvocation *context) + { + Daemon *daemon = (Daemon*)accounts; + ListUserData *data; + + data = list_user_data_new (daemon, context); + + if (daemon->priv->reload_id > 0) { + /* reload in progress, wait a bit */ + g_idle_add (finish_list_cached_users, data); + } + else { + finish_list_cached_users (data); + } +@@ -1151,123 +1172,123 @@ daemon_cache_user (AccountsAccounts *accounts, + static void + daemon_uncache_user_authorized_cb (Daemon *daemon, + User *dummy, + GDBusMethodInvocation *context, + gpointer data) + { + const gchar *user_name = data; + gchar *filename; + User *user; + + sys_log (context, "uncache user '%s'", user_name); + + user = daemon_local_find_user_by_name (daemon, user_name); + if (user == NULL) { + throw_error (context, ERROR_USER_DOES_NOT_EXIST, + "No user with the name %s found", user_name); + return; + } + + /* Always use the canonical user name looked up */ + user_name = user_get_user_name (user); + + filename = g_build_filename (USERDIR, user_name, NULL); + g_remove (filename); + g_free (filename); + + filename = g_build_filename (ICONDIR, user_name, NULL); + g_remove (filename); + g_free (filename); + ++ user_set_cached (user, FALSE); ++ + accounts_accounts_complete_uncache_user (NULL, context); + + queue_reload_users (daemon); + } + + static gboolean + daemon_uncache_user (AccountsAccounts *accounts, + GDBusMethodInvocation *context, + const gchar *user_name) + { + Daemon *daemon = (Daemon*)accounts; + + daemon_local_check_auth (daemon, + NULL, + "org.freedesktop.accounts.user-administration", + TRUE, + daemon_uncache_user_authorized_cb, + context, + g_strdup (user_name), + g_free); + + return TRUE; + } + + typedef struct { + uid_t uid; + gboolean remove_files; + } DeleteUserData; + + static void + daemon_delete_user_authorized_cb (Daemon *daemon, + User *dummy, + GDBusMethodInvocation *context, + gpointer data) + + { + DeleteUserData *ud = data; + GError *error; + gchar *filename; + struct passwd *pwent; + const gchar *argv[6]; ++ User *user; + + pwent = getpwuid (ud->uid); + + if (pwent == NULL) { + throw_error (context, ERROR_USER_DOES_NOT_EXIST, "No user with uid %d found", ud->uid); + + return; + } + + sys_log (context, "delete user '%s' (%d)", pwent->pw_name, ud->uid); + +- if (daemon->priv->autologin != NULL) { +- User *user; ++ user = daemon_local_find_user_by_id (daemon, ud->uid); + +- user = daemon_local_find_user_by_id (daemon, ud->uid); ++ if (user != NULL) { ++ user_set_cached (user, FALSE); + +- g_assert (user != NULL); +- +- if (daemon->priv->autologin == user) { ++ if (daemon->priv->autologin == user) { + daemon_local_set_automatic_login (daemon, user, FALSE, NULL); + } +- + } + + filename = g_build_filename (USERDIR, pwent->pw_name, NULL); + g_remove (filename); + g_free (filename); + + filename = g_build_filename (ICONDIR, pwent->pw_name, NULL); + g_remove (filename); + g_free (filename); + + argv[0] = "/usr/sbin/userdel"; + if (ud->remove_files) { + argv[1] = "-f"; + argv[2] = "-r"; + argv[3] = "--"; + argv[4] = pwent->pw_name; + argv[5] = NULL; + } + else { + argv[1] = "-f"; + argv[2] = "--"; + argv[3] = pwent->pw_name; + argv[4] = NULL; + } + + error = NULL; + if (!spawn_with_login_uid (context, argv, &error)) { + throw_error (context, ERROR_FAILED, "running '%s' failed: %s", argv[0], error->message); + g_error_free (error); + return; +diff --git a/src/user.c b/src/user.c +index 802d07a..a83cfe4 100644 +--- a/src/user.c ++++ b/src/user.c +@@ -83,60 +83,61 @@ struct User { + GKeyFile *keyfile; + + uid_t uid; + gid_t gid; + gchar *user_name; + gchar *real_name; + AccountType account_type; + PasswordMode password_mode; + gchar *password_hint; + gchar *home_dir; + gchar *shell; + gchar *email; + gchar *language; + gchar *x_session; + gchar *location; + guint64 login_frequency; + gint64 login_time; + gint64 expiration_time; + gint64 last_change_time; + gint64 min_days_between_changes; + gint64 max_days_between_changes; + gint64 days_to_warn; + gint64 days_after_expiration_until_lock; + GVariant *login_history; + gchar *icon_file; + gchar *default_icon_file; + gboolean locked; + gboolean automatic_login; + gboolean system_account; + gboolean local_account; ++ gboolean cached; + + guint *extension_ids; + guint n_extension_ids; + }; + + typedef struct UserClass + { + AccountsUserSkeletonClass parent_class; + } UserClass; + + static void user_accounts_user_iface_init (AccountsUserIface *iface); + + G_DEFINE_TYPE_WITH_CODE (User, user, ACCOUNTS_TYPE_USER_SKELETON, G_IMPLEMENT_INTERFACE (ACCOUNTS_TYPE_USER, user_accounts_user_iface_init)); + + static gint + account_type_from_pwent (struct passwd *pwent) + { + struct group *grp; + gint i; + + if (pwent->pw_uid == 0) { + g_debug ("user is root so account type is administrator"); + return ACCOUNT_TYPE_ADMINISTRATOR; + } + + grp = getgrnam (ADMIN_GROUP); + if (grp == NULL) { + g_debug (ADMIN_GROUP " group not found"); + return ACCOUNT_TYPE_STANDARD; + } +@@ -339,109 +340,112 @@ user_update_from_keyfile (User *user, + user->location = s; + g_object_notify (G_OBJECT (user), "location"); + } + + s = g_key_file_get_string (keyfile, "User", "PasswordHint", NULL); + if (s != NULL) { + g_free (user->password_hint); + user->password_hint = s; + g_object_notify (G_OBJECT (user), "password-hint"); + } + + s = g_key_file_get_string (keyfile, "User", "Icon", NULL); + if (s != NULL) { + g_free (user->icon_file); + user->icon_file = s; + g_object_notify (G_OBJECT (user), "icon-file"); + } + + if (g_key_file_has_key (keyfile, "User", "SystemAccount", NULL)) { + gboolean system_account; + + system_account = g_key_file_get_boolean (keyfile, "User", "SystemAccount", NULL); + if (system_account != user->system_account) { + user->system_account = system_account; + g_object_notify (G_OBJECT (user), "system-account"); + } + } + + g_clear_pointer (&user->keyfile, g_key_file_unref); + user->keyfile = g_key_file_ref (keyfile); ++ user_set_cached (user, TRUE); + + g_object_thaw_notify (G_OBJECT (user)); + } + + void + user_update_local_account_property (User *user, + gboolean local) + { + if (local == user->local_account) + return; + user->local_account = local; + g_object_notify (G_OBJECT (user), "local-account"); + } + + void + user_update_system_account_property (User *user, + gboolean system) + { + if (system == user->system_account) + return; + user->system_account = system; + g_object_notify (G_OBJECT (user), "system-account"); + } + + static void + user_save_to_keyfile (User *user, + GKeyFile *keyfile) + { + g_key_file_remove_group (keyfile, "User", NULL); + + if (user->email) + g_key_file_set_string (keyfile, "User", "Email", user->email); + + if (user->language) + g_key_file_set_string (keyfile, "User", "Language", user->language); + + if (user->x_session) + g_key_file_set_string (keyfile, "User", "XSession", user->x_session); + + if (user->location) + g_key_file_set_string (keyfile, "User", "Location", user->location); + + if (user->password_hint) + g_key_file_set_string (keyfile, "User", "PasswordHint", user->password_hint); + + if (user->icon_file) + g_key_file_set_string (keyfile, "User", "Icon", user->icon_file); + + g_key_file_set_boolean (keyfile, "User", "SystemAccount", user->system_account); ++ ++ user_set_cached (user, TRUE); + } + + static void + save_extra_data (User *user) + { + gchar *filename; + gchar *data; + GError *error; + + user_save_to_keyfile (user, user->keyfile); + + error = NULL; + data = g_key_file_to_data (user->keyfile, NULL, &error); + if (error == NULL) { + filename = g_build_filename (USERDIR, + user->user_name, + NULL); + g_file_set_contents (filename, data, -1, &error); + g_free (filename); + g_free (data); + } + if (error) { + g_warning ("Saving data for user %s failed: %s", + user->user_name, error->message); + g_error_free (error); + } + } + + static void + move_extra_data (const gchar *old_name, +@@ -810,60 +814,73 @@ user_get_user_name (User *user) + gboolean + user_get_system_account (User *user) + { + return user->system_account; + } + + gboolean + user_get_local_account (User *user) + { + return user->local_account; + } + + const gchar * + user_get_object_path (User *user) + { + return user->object_path; + } + + uid_t + user_get_uid (User *user) + { + return user->uid; + } + + const gchar * + user_get_shell(User *user) + { + return user->shell; + } + ++gboolean ++user_get_cached (User *user) ++{ ++ return user->cached; ++} ++ ++void ++user_set_cached (User *user, ++ gboolean cached) ++{ ++ user->cached = cached; ++} ++ + static void + throw_error (GDBusMethodInvocation *context, + gint error_code, + const gchar *format, + ...) + { + va_list args; + gchar *message; + + va_start (args, format); + message = g_strdup_vprintf (format, args); + va_end (args); + + g_dbus_method_invocation_return_error (context, ERROR, error_code, "%s", message); + + g_free (message); + } + + static void + user_change_real_name_authorized_cb (Daemon *daemon, + User *user, + GDBusMethodInvocation *context, + gpointer data) + + { + gchar *name = data; + GError *error; + const gchar *argv[6]; + + if (g_strcmp0 (user->real_name, name) != 0) { +diff --git a/src/user.h b/src/user.h +index 22548f9..39c6f13 100644 +--- a/src/user.h ++++ b/src/user.h +@@ -36,47 +36,50 @@ G_BEGIN_DECLS + #define IS_USER(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), TYPE_USER)) + + typedef enum { + ACCOUNT_TYPE_STANDARD, + ACCOUNT_TYPE_ADMINISTRATOR, + #define ACCOUNT_TYPE_LAST ACCOUNT_TYPE_ADMINISTRATOR + } AccountType; + + typedef enum { + PASSWORD_MODE_REGULAR, + PASSWORD_MODE_SET_AT_LOGIN, + PASSWORD_MODE_NONE, + #define PASSWORD_MODE_LAST PASSWORD_MODE_NONE + } PasswordMode; + + /* local methods */ + + GType user_get_type (void) G_GNUC_CONST; + User * user_new (Daemon *daemon, + uid_t uid); + + void user_update_from_pwent (User *user, + struct passwd *pwent, + struct spwd *spent); + void user_update_from_keyfile (User *user, + GKeyFile *keyfile); + void user_update_local_account_property (User *user, + gboolean local); + void user_update_system_account_property (User *user, + gboolean system); ++gboolean user_get_cached (User *user); ++void user_set_cached (User *user, ++ gboolean cached); + + void user_register (User *user); + void user_unregister (User *user); + void user_changed (User *user); + + void user_save (User *user); + + const gchar * user_get_user_name (User *user); + gboolean user_get_system_account (User *user); + gboolean user_get_local_account (User *user); + const gchar * user_get_object_path (User *user); + uid_t user_get_uid (User *user); + const gchar * user_get_shell (User *user); + + G_END_DECLS + + #endif +-- +2.12.2 + diff --git a/SOURCES/0001-lib-move-g_object_ref-call-for-clarity.patch b/SOURCES/0001-lib-move-g_object_ref-call-for-clarity.patch new file mode 100644 index 0000000..e57aa0c --- /dev/null +++ b/SOURCES/0001-lib-move-g_object_ref-call-for-clarity.patch @@ -0,0 +1,92 @@ +From e1672162fc37c37fb09450991316fbfe22029882 Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Wed, 27 Sep 2017 10:39:05 -0400 +Subject: [PATCH 01/13] lib: move g_object_ref call for clarity + +create_new_user creates an ActUser object and returns it to +the caller. Before returning the object, it also adds the +object to a list of "new users" to track when it fully loads. + +It's idiomatic to return the original ref to the caller of +a constructor, but create_new_user instead gives the original +reference to the new_users list. Their's no practical difference, +but this commit moves the ref around for clarity. +--- + src/libaccountsservice/act-user-manager.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/src/libaccountsservice/act-user-manager.c b/src/libaccountsservice/act-user-manager.c +index 914a1c9..d866725 100644 +--- a/src/libaccountsservice/act-user-manager.c ++++ b/src/libaccountsservice/act-user-manager.c +@@ -816,65 +816,65 @@ static void + add_session_for_user (ActUserManager *manager, + ActUser *user, + const char *ssid, + gboolean is_ours) + { + g_hash_table_insert (manager->priv->sessions, + g_strdup (ssid), + g_object_ref (user)); + + _act_user_add_session (user, ssid, is_ours); + g_debug ("ActUserManager: added session for %s", describe_user (user)); + } + + static void + set_has_multiple_users (ActUserManager *manager, + gboolean has_multiple_users) + { + if (manager->priv->has_multiple_users != has_multiple_users) { + manager->priv->has_multiple_users = has_multiple_users; + g_object_notify (G_OBJECT (manager), "has-multiple-users"); + } + } + + static ActUser * + create_new_user (ActUserManager *manager) + { + ActUser *user; + + user = g_object_new (ACT_TYPE_USER, NULL); + +- manager->priv->new_users = g_slist_prepend (manager->priv->new_users, user); ++ manager->priv->new_users = g_slist_prepend (manager->priv->new_users, g_object_ref (user)); + + g_signal_connect_object (user, "notify::is-loaded", G_CALLBACK (on_new_user_loaded), manager, 0); + +- return g_object_ref (user); ++ return user; + } + + static void + add_user (ActUserManager *manager, + ActUser *user) + { + const char *object_path; + + g_debug ("ActUserManager: tracking user '%s'", act_user_get_user_name (user)); + if (act_user_is_system_account (user)) { + g_hash_table_insert (manager->priv->system_users_by_name, + g_strdup (act_user_get_user_name (user)), + g_object_ref (user)); + } else { + g_hash_table_insert (manager->priv->normal_users_by_name, + g_strdup (act_user_get_user_name (user)), + g_object_ref (user)); + } + + object_path = act_user_get_object_path (user); + if (object_path != NULL) { + g_hash_table_replace (manager->priv->users_by_object_path, + (gpointer) object_path, + g_object_ref (user)); + } + + g_signal_connect_object (user, + "sessions-changed", + G_CALLBACK (on_user_sessions_changed), + manager, 0); +-- +2.14.1 + diff --git a/SOURCES/0002-lib-leak-fix.patch b/SOURCES/0002-lib-leak-fix.patch new file mode 100644 index 0000000..93cb907 --- /dev/null +++ b/SOURCES/0002-lib-leak-fix.patch @@ -0,0 +1,93 @@ +From bb381997525a1ff74f1bbac3556d566b62f3ef1b Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Wed, 27 Sep 2017 10:41:47 -0400 +Subject: [PATCH 02/13] lib: leak fix + +If a new user is added by accounts service, we currently leak +a reference to it. That leads to transient user objects living +longer than they're supposed which uses memory and produces +excess bus traffic. +--- + src/libaccountsservice/act-user-manager.c | 5 ++++- + 1 file changed, 4 insertions(+), 1 deletion(-) + +diff --git a/src/libaccountsservice/act-user-manager.c b/src/libaccountsservice/act-user-manager.c +index d866725..c8d0443 100644 +--- a/src/libaccountsservice/act-user-manager.c ++++ b/src/libaccountsservice/act-user-manager.c +@@ -1088,68 +1088,71 @@ add_new_user_for_object_path (const char *object_path, + user = g_hash_table_lookup (manager->priv->users_by_object_path, object_path); + + if (user != NULL) { + g_debug ("ActUserManager: tracking existing %s with object path %s", + describe_user (user), object_path); + return user; + } + + user = find_new_user_with_object_path (manager, object_path); + + if (user != NULL) { + g_debug ("ActUserManager: tracking existing (but very recently added) %s with object path %s", + describe_user (user), object_path); + return user; + } + + g_debug ("ActUserManager: tracking new user with object path %s", object_path); + + user = create_new_user (manager); + _act_user_update_from_object_path (user, object_path); + + return user; + } + + static void + on_new_user_in_accounts_service (GDBusProxy *proxy, + const char *object_path, + gpointer user_data) + { + ActUserManager *manager = ACT_USER_MANAGER (user_data); ++ ActUser *user; + + if (!manager->priv->is_loaded) { + g_debug ("ActUserManager: ignoring new user in accounts service with object path %s since not loaded yet", object_path); + return; + } + + g_debug ("ActUserManager: new user in accounts service with object path %s", object_path); +- add_new_user_for_object_path (object_path, manager); ++ user = add_new_user_for_object_path (object_path, manager); ++ ++ g_object_unref (user); + } + + static void + on_user_removed_in_accounts_service (GDBusProxy *proxy, + const char *object_path, + gpointer user_data) + { + ActUserManager *manager = ACT_USER_MANAGER (user_data); + ActUser *user; + + user = g_hash_table_lookup (manager->priv->users_by_object_path, object_path); + + if (user == NULL) { + g_debug ("ActUserManager: ignoring untracked user %s", object_path); + return; + } else { + g_debug ("ActUserManager: tracked user %s removed from accounts service", object_path); + } + + manager->priv->new_users = g_slist_remove (manager->priv->new_users, user); + + remove_user (manager, user); + } + + static void + on_get_current_session_finished (GObject *object, + GAsyncResult *result, + gpointer data) + { + ConsoleKitManager *proxy = CONSOLE_KIT_MANAGER (object); +-- +2.14.1 + diff --git a/SOURCES/0003-lib-another-leak-fix.patch b/SOURCES/0003-lib-another-leak-fix.patch new file mode 100644 index 0000000..511d16f --- /dev/null +++ b/SOURCES/0003-lib-another-leak-fix.patch @@ -0,0 +1,99 @@ +From 889561310b794f0443dc5c520b09151fe4b260ec Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Wed, 27 Sep 2017 10:43:33 -0400 +Subject: [PATCH 03/13] lib: another leak fix + +If a user gets removed from account service before it's fully loaded, +then we neglect to drop its reference on the new_users lists. + +This commit fixes that. +--- + src/libaccountsservice/act-user-manager.c | 8 +++++++- + 1 file changed, 7 insertions(+), 1 deletion(-) + +diff --git a/src/libaccountsservice/act-user-manager.c b/src/libaccountsservice/act-user-manager.c +index c8d0443..ac06e24 100644 +--- a/src/libaccountsservice/act-user-manager.c ++++ b/src/libaccountsservice/act-user-manager.c +@@ -1108,71 +1108,77 @@ add_new_user_for_object_path (const char *object_path, + + return user; + } + + static void + on_new_user_in_accounts_service (GDBusProxy *proxy, + const char *object_path, + gpointer user_data) + { + ActUserManager *manager = ACT_USER_MANAGER (user_data); + ActUser *user; + + if (!manager->priv->is_loaded) { + g_debug ("ActUserManager: ignoring new user in accounts service with object path %s since not loaded yet", object_path); + return; + } + + g_debug ("ActUserManager: new user in accounts service with object path %s", object_path); + user = add_new_user_for_object_path (object_path, manager); + + g_object_unref (user); + } + + static void + on_user_removed_in_accounts_service (GDBusProxy *proxy, + const char *object_path, + gpointer user_data) + { + ActUserManager *manager = ACT_USER_MANAGER (user_data); + ActUser *user; ++ GSList *node; + + user = g_hash_table_lookup (manager->priv->users_by_object_path, object_path); + + if (user == NULL) { + g_debug ("ActUserManager: ignoring untracked user %s", object_path); + return; + } else { + g_debug ("ActUserManager: tracked user %s removed from accounts service", object_path); + } + +- manager->priv->new_users = g_slist_remove (manager->priv->new_users, user); ++ node = g_slist_find (manager->priv->new_users, user); ++ if (node != NULL) { ++ g_signal_handlers_disconnect_by_func (user, on_new_user_loaded, manager); ++ g_object_unref (user); ++ manager->priv->new_users = g_slist_delete_link (manager->priv->new_users, node); ++ } + + remove_user (manager, user); + } + + static void + on_get_current_session_finished (GObject *object, + GAsyncResult *result, + gpointer data) + { + ConsoleKitManager *proxy = CONSOLE_KIT_MANAGER (object); + ActUserManager *manager = data; + GError *error = NULL; + char *session_id; + + g_assert (manager->priv->seat.state == ACT_USER_MANAGER_SEAT_STATE_GET_SESSION_ID); + + if (!console_kit_manager_call_get_current_session_finish (proxy, &session_id, result, &error)) { + if (error != NULL) { + g_debug ("Failed to identify the current session: %s", + error->message); + g_error_free (error); + } else { + g_debug ("Failed to identify the current session"); + } + unload_seat (manager); + + goto out; + } + + manager->priv->seat.session_id = session_id; +-- +2.14.1 + diff --git a/SOURCES/0004-daemon-don-t-send-spurious-change-signals-when-wtmp-.patch b/SOURCES/0004-daemon-don-t-send-spurious-change-signals-when-wtmp-.patch new file mode 100644 index 0000000..98ea0c5 --- /dev/null +++ b/SOURCES/0004-daemon-don-t-send-spurious-change-signals-when-wtmp-.patch @@ -0,0 +1,116 @@ +From d93c4a3be8a56d55ecfc814eeef5c1bf1efe33de Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Wed, 27 Sep 2017 11:01:28 -0400 +Subject: [PATCH 04/13] daemon: don't send spurious change signals when wtmp + changes + +Right now, we unintentionally send out a changed signal for +every tracked user anytime wtmp changes. + +This commit fixes that. +--- + src/wtmp-helper.c | 22 +++++++++++++++++++--- + 1 file changed, 19 insertions(+), 3 deletions(-) + +diff --git a/src/wtmp-helper.c b/src/wtmp-helper.c +index 787480b..a1edffe 100644 +--- a/src/wtmp-helper.c ++++ b/src/wtmp-helper.c +@@ -138,75 +138,91 @@ wtmp_helper_update_login_frequencies (GHashTable *users) + &key, &value)) { + accounting = g_new (UserAccounting, 1); + accounting->frequency = 0; + accounting->previous_logins = NULL; + + g_hash_table_insert (login_hash, g_strdup (wtmp_entry->ut_user), accounting); + } else { + accounting = value; + } + + accounting->frequency++; + accounting->time = wtmp_entry->ut_tv.tv_sec; + + /* Add zero logout time to change it later on logout record */ + previous_login = g_new (UserPreviousLogin, 1); + previous_login->id = g_strdup (wtmp_entry->ut_line); + previous_login->login_time = wtmp_entry->ut_tv.tv_sec; + previous_login->logout_time = 0; + accounting->previous_logins = g_list_prepend (accounting->previous_logins, previous_login); + + g_hash_table_insert (logout_hash, g_strdup (wtmp_entry->ut_line), previous_login); + } + + /* Last iteration */ + endutxent (); + + g_hash_table_iter_init (&iter, login_hash); + while (g_hash_table_iter_next (&iter, &key, &value)) { + UserAccounting *accounting = (UserAccounting *) value; + UserPreviousLogin *previous_login; ++ gboolean changed = FALSE; ++ guint64 old_login_frequency; ++ guint64 old_login_time; + + user = g_hash_table_lookup (users, key); + if (user == NULL) { + g_list_free_full (accounting->previous_logins, (GDestroyNotify) user_previous_login_free); + continue; + } + +- g_object_set (user, "login-frequency", accounting->frequency, NULL); +- g_object_set (user, "login-time", accounting->time, NULL); ++ g_object_get (user, ++ "login-frequency", &old_login_frequency, ++ "login-time", &old_login_time, ++ NULL); ++ ++ if (old_login_frequency != accounting->frequency) { ++ g_object_set (user, "login-frequency", accounting->frequency, NULL); ++ changed = TRUE; ++ } ++ ++ if (old_login_time != accounting->time) { ++ g_object_set (user, "login-time", accounting->time, NULL); ++ changed = TRUE; ++ } + + builder = g_variant_builder_new (G_VARIANT_TYPE ("a(xxa{sv})")); + for (l = g_list_last (accounting->previous_logins); l != NULL; l = l->prev) { + previous_login = l->data; + + builder2 = g_variant_builder_new (G_VARIANT_TYPE ("a{sv}")); + g_variant_builder_add (builder2, "{sv}", "type", g_variant_new_string (previous_login->id)); + g_variant_builder_add (builder, "(xxa{sv})", previous_login->login_time, previous_login->logout_time, builder2); + g_variant_builder_unref (builder2); + } + g_object_set (user, "login-history", g_variant_new ("a(xxa{sv})", builder), NULL); + g_variant_builder_unref (builder); + g_list_free_full (accounting->previous_logins, (GDestroyNotify) user_previous_login_free); + +- user_changed (user); ++ if (changed) ++ user_changed (user); + } + + g_hash_table_unref (login_hash); + g_hash_table_unref (logout_hash); + } + + const gchar * + wtmp_helper_get_path_for_monitor (void) + { + return PATH_WTMP; + } + + #else /* HAVE_UTMPX_H */ + + const gchar * + wtmp_helper_get_path_for_monitor (void) + { + return NULL; + } + + #endif /* HAVE_UTMPX_H */ +-- +2.14.1 + diff --git a/SOURCES/0005-lib-consolidate-change-notification.patch b/SOURCES/0005-lib-consolidate-change-notification.patch new file mode 100644 index 0000000..1df5d42 --- /dev/null +++ b/SOURCES/0005-lib-consolidate-change-notification.patch @@ -0,0 +1,228 @@ +From 42bd3bd41d69100308a22df43482569f6ab53dbe Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Wed, 27 Sep 2017 14:44:48 -0400 +Subject: [PATCH 05/13] lib: consolidate change notification + +if the daemon sends out multiple change notifications for a user +because several properties changed in quick succession, try to +batch them up, and only update info at the end. +--- + src/libaccountsservice/act-user.c | 20 +++++++++++++++++++- + 1 file changed, 19 insertions(+), 1 deletion(-) + +diff --git a/src/libaccountsservice/act-user.c b/src/libaccountsservice/act-user.c +index dbb9b53..17acacb 100644 +--- a/src/libaccountsservice/act-user.c ++++ b/src/libaccountsservice/act-user.c +@@ -113,60 +113,62 @@ struct _ActUser { + char *object_path; + + uid_t uid; + char *user_name; + char *real_name; + char *password_hint; + char *home_dir; + char *shell; + char *email; + char *location; + char *icon_file; + char *language; + char *x_session; + GList *our_sessions; + GList *other_sessions; + int login_frequency; + gint64 login_time; + GVariant *login_history; + + ActUserAccountType account_type; + ActUserPasswordMode password_mode; + + guint uid_set : 1; + + guint is_loaded : 1; + guint locked : 1; + guint automatic_login : 1; + guint system_account : 1; + guint local_account : 1; + guint nonexistent : 1; ++ ++ guint update_info_timeout_id; + }; + + struct _ActUserClass + { + GObjectClass parent_class; + }; + + static void act_user_finalize (GObject *object); + + static guint signals[LAST_SIGNAL] = { 0 }; + + G_DEFINE_TYPE (ActUser, act_user, G_TYPE_OBJECT) + + static int + session_compare (const char *a, + const char *b) + { + if (a == NULL) { + return 1; + } else if (b == NULL) { + return -1; + } + + return strcmp (a, b); + } + + void + _act_user_add_session (ActUser *user, + const char *ssid, + gboolean is_ours) +@@ -573,60 +575,64 @@ act_user_finalize (GObject *object) + + g_free (user->user_name); + g_free (user->real_name); + g_free (user->icon_file); + g_free (user->language); + g_free (user->object_path); + g_free (user->password_hint); + g_free (user->home_dir); + g_free (user->shell); + g_free (user->email); + g_free (user->location); + if (user->login_history) + g_variant_unref (user->login_history); + + if (user->accounts_proxy != NULL) { + g_object_unref (user->accounts_proxy); + } + + if (user->object_proxy != NULL) { + g_object_unref (user->object_proxy); + } + + if (user->get_all_cancellable != NULL) { + g_object_unref (user->get_all_cancellable); + } + + if (user->connection != NULL) { + g_object_unref (user->connection); + } + ++ if (user->update_info_timeout_id != 0) { ++ g_source_remove (user->update_info_timeout_id); ++ } ++ + if (G_OBJECT_CLASS (act_user_parent_class)->finalize) + (*G_OBJECT_CLASS (act_user_parent_class)->finalize) (object); + } + + static void + set_is_loaded (ActUser *user, + gboolean is_loaded) + { + if (user->is_loaded != is_loaded) { + user->is_loaded = is_loaded; + g_object_notify (G_OBJECT (user), "is-loaded"); + } + } + + /** + * act_user_get_uid: + * @user: the user object to examine. + * + * Retrieves the ID of @user. + * + * Returns: (transfer none): a pointer to an array of characters which must not be modified or + * freed, or %NULL. + **/ + + uid_t + act_user_get_uid (ActUser *user) + { + g_return_val_if_fail (ACT_IS_USER (user), -1); + + return user->uid; +@@ -1339,67 +1345,79 @@ on_get_all_finished (GObject *object, + g_variant_unref (res); + + if (!user->is_loaded) { + set_is_loaded (user, TRUE); + } + + g_signal_emit (user, signals[CHANGED], 0); + } + + static void + update_info (ActUser *user) + { + g_assert (G_IS_DBUS_PROXY (user->object_proxy)); + + if (user->get_all_cancellable != NULL) { + g_cancellable_cancel (user->get_all_cancellable); + g_clear_object (&user->get_all_cancellable); + } + + user->get_all_cancellable = g_cancellable_new (); + g_dbus_proxy_call (user->object_proxy, + "GetAll", + g_variant_new ("(s)", ACCOUNTS_USER_INTERFACE), + G_DBUS_CALL_FLAGS_NONE, + -1, + user->get_all_cancellable, + on_get_all_finished, + user); + } + ++static gboolean ++on_timeout_update_info (ActUser *user) ++{ ++ update_info (user); ++ user->update_info_timeout_id = 0; ++ ++ return G_SOURCE_REMOVE; ++} ++ + static void + changed_handler (AccountsUser *object, + gpointer *data) + { + ActUser *user = ACT_USER (data); + +- update_info (user); ++ if (user->update_info_timeout_id != 0) ++ return; ++ ++ user->update_info_timeout_id = g_timeout_add (250, (GSourceFunc) on_timeout_update_info, user); + } + + /** + * _act_user_update_as_nonexistent: + * @user: the user object to update. + * + * Set's the 'non-existent' property of @user to #TRUE + * Can only be called before the user is loaded. + **/ + void + _act_user_update_as_nonexistent (ActUser *user) + { + g_return_if_fail (ACT_IS_USER (user)); + g_return_if_fail (!act_user_is_loaded (user)); + g_return_if_fail (user->object_path == NULL); + + user->nonexistent = TRUE; + g_object_notify (G_OBJECT (user), "nonexistent"); + + set_is_loaded (user, TRUE); + } + + /** + * _act_user_update_from_object_path: + * @user: the user object to update. + * @object_path: the object path of the user to use. + * + * Updates the properties of @user from the accounts service via + * the object path in @object_path. + **/ +-- +2.14.1 + diff --git a/SOURCES/0006-daemon-add-new-HasMultipleUsers-and-HasNoUsers-prope.patch b/SOURCES/0006-daemon-add-new-HasMultipleUsers-and-HasNoUsers-prope.patch new file mode 100644 index 0000000..ffe254c --- /dev/null +++ b/SOURCES/0006-daemon-add-new-HasMultipleUsers-and-HasNoUsers-prope.patch @@ -0,0 +1,271 @@ +From dc41728b9c4ec35fb7bd41cdc646b77dcb41f6dd Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Mon, 2 Oct 2017 15:45:01 -0400 +Subject: [PATCH 06/13] daemon: add new HasMultipleUsers and HasNoUsers + properties + +Every gnome-shell instance wants to know if the system has multiple +users or not, in order to know whether or not to show the +'Switch User' feature in the menu. + +accountsservice doesn't provide this information directly, though, +so libaccountsservice instead requests a list of all users on the +system and counts the provided list, filtering out system users. + +This is a lot of work for every gnome-shell instance to do, when +it doesn't actually need the list of users at all. + +This adds a new property HasMultipleUsers which libaccountsservice +can watch for instead. For good measure, this commit also adds a +HasNoUsers boolean which can be used to know whether or not to start +gnome-initial-setup. +--- + data/org.freedesktop.Accounts.xml | 20 ++++++++++++++++++++ + src/daemon.c | 24 +++++++++++++++++++++--- + 2 files changed, 41 insertions(+), 3 deletions(-) + +diff --git a/data/org.freedesktop.Accounts.xml b/data/org.freedesktop.Accounts.xml +index 692540a..ed7db50 100644 +--- a/data/org.freedesktop.Accounts.xml ++++ b/data/org.freedesktop.Accounts.xml +@@ -197,32 +197,52 @@ + + Emitted when a user is added. + + + + + + + + Object path of the user that was deleted. + + + + + Emitted when a user is deleted. + + + + + + + + + + The version of the running daemon. + + + + + ++ ++ ++ ++ ++ Whether or not the system has no users ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ Whether or not the system has multiple users ++ ++ ++ ++ ++ + + +diff --git a/src/daemon.c b/src/daemon.c +index 6e3e4b3..a4e18df 100644 +--- a/src/daemon.c ++++ b/src/daemon.c +@@ -35,60 +35,61 @@ + #include + #include + #include + + #include + #include + #include + #include + #include + #include + + #include "user-classify.h" + #include "wtmp-helper.h" + #include "daemon.h" + #include "util.h" + + #define PATH_PASSWD "/etc/passwd" + #define PATH_SHADOW "/etc/shadow" + #define PATH_GROUP "/etc/group" + #define PATH_GDM_CUSTOM "/etc/gdm/custom.conf" + + enum { + PROP_0, + PROP_DAEMON_VERSION + }; + + struct DaemonPrivate { + GDBusConnection *bus_connection; + + GHashTable *users; ++ gsize number_of_normal_users; + GList *explicitly_requested_users; + + User *autologin; + + GFileMonitor *passwd_monitor; + GFileMonitor *shadow_monitor; + GFileMonitor *group_monitor; + GFileMonitor *gdm_monitor; + GFileMonitor *wtmp_monitor; + + guint reload_id; + guint autologin_id; + + PolkitAuthority *authority; + GHashTable *extension_ifaces; + }; + + typedef struct passwd * (* EntryGeneratorFunc) (Daemon *, GHashTable *, gpointer *, struct spwd **shadow_entry); + + static void daemon_accounts_accounts_iface_init (AccountsAccountsIface *iface); + + G_DEFINE_TYPE_WITH_CODE (Daemon, daemon, ACCOUNTS_TYPE_ACCOUNTS_SKELETON, G_IMPLEMENT_INTERFACE (ACCOUNTS_TYPE_ACCOUNTS, daemon_accounts_accounts_iface_init)); + + #define DAEMON_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), TYPE_DAEMON, DaemonPrivate)) + + static const GDBusErrorEntry accounts_error_entries[] = + { + { ERROR_FAILED, "org.freedesktop.Accounts.Error.Failed" }, + { ERROR_USER_EXISTS, "org.freedesktop.Accounts.Error.UserExists" }, + { ERROR_USER_DOES_NOT_EXIST, "org.freedesktop.Accounts.Error.UserDoesNotExist" }, +@@ -395,98 +396,115 @@ load_entries (Daemon *daemon, + + /* freeze & update users not already in the new list */ + g_object_freeze_notify (G_OBJECT (user)); + user_update_from_pwent (user, pwent, spent); + + g_hash_table_insert (users, g_strdup (user_get_user_name (user)), user); + g_debug ("loaded user: %s", user_get_user_name (user)); + } + + if (!explicitly_requested) { + user_set_cached (user, TRUE); + } + } + + /* Generator should have cleaned up */ + g_assert (generator_state == NULL); + } + + static GHashTable * + create_users_hash_table (void) + { + return g_hash_table_new_full (g_str_hash, + g_str_equal, + g_free, + g_object_unref); + } + + static void + reload_users (Daemon *daemon) + { ++ AccountsAccounts *accounts = ACCOUNTS_ACCOUNTS (daemon); ++ gboolean had_no_users, has_no_users, had_multiple_users, has_multiple_users; + GHashTable *users; + GHashTable *old_users; + GHashTable *local; + GHashTableIter iter; ++ gsize number_of_normal_users = 0; + gpointer name; + User *user; + + /* Track the users that we saw during our (re)load */ + users = create_users_hash_table (); + + /* + * NOTE: As we load data from all the sources, notifies are + * frozen in load_entries() and then thawed as we process + * them below. + */ + + /* Load the local users into our hash table */ + load_entries (daemon, users, FALSE, entry_generator_fgetpwent); + local = g_hash_table_new (g_str_hash, g_str_equal); + g_hash_table_iter_init (&iter, users); + while (g_hash_table_iter_next (&iter, &name, NULL)) + g_hash_table_add (local, name); + + /* and add users to hash table that were explicitly requested */ + load_entries (daemon, users, TRUE, entry_generator_requested_users); + + /* Now add/update users from other sources, possibly non-local */ + load_entries (daemon, users, FALSE, entry_generator_cachedir); + + wtmp_helper_update_login_frequencies (users); + +- /* Mark which users are local, which are not */ ++ /* Count the non-system users. Mark which users are local, which are not. */ + g_hash_table_iter_init (&iter, users); +- while (g_hash_table_iter_next (&iter, &name, (gpointer *)&user)) ++ while (g_hash_table_iter_next (&iter, &name, (gpointer *)&user)) { ++ if (!user_get_system_account (user)) ++ number_of_normal_users++; + user_update_local_account_property (user, g_hash_table_lookup (local, name) != NULL); +- ++ } + g_hash_table_destroy (local); + ++ had_no_users = accounts_accounts_get_has_no_users (accounts); ++ has_no_users = number_of_normal_users == 0; ++ ++ if (had_no_users != has_no_users) ++ accounts_accounts_set_has_no_users (accounts, has_no_users); ++ ++ had_multiple_users = accounts_accounts_get_has_multiple_users (accounts); ++ has_multiple_users = number_of_normal_users > 1; ++ ++ if (had_multiple_users != has_multiple_users) ++ accounts_accounts_set_has_multiple_users (accounts, has_multiple_users); ++ + /* Swap out the users */ + old_users = daemon->priv->users; + daemon->priv->users = users; + + /* Remove all the old users */ + g_hash_table_iter_init (&iter, old_users); + while (g_hash_table_iter_next (&iter, &name, (gpointer *)&user)) { + User *refreshed_user; + + refreshed_user = g_hash_table_lookup (users, name); + + if (!refreshed_user || !user_get_cached (refreshed_user)) { + user_unregister (user); + accounts_accounts_emit_user_deleted (ACCOUNTS_ACCOUNTS (daemon), + user_get_object_path (user)); + } + } + + /* Register all the new users */ + g_hash_table_iter_init (&iter, users); + while (g_hash_table_iter_next (&iter, &name, (gpointer *)&user)) { + User *stale_user; + + stale_user = g_hash_table_lookup (old_users, name); + + if (!stale_user || !user_get_cached (stale_user) && user_get_cached (user)) { + user_register (user); + accounts_accounts_emit_user_added (ACCOUNTS_ACCOUNTS (daemon), + user_get_object_path (user)); + } +-- +2.14.1 + diff --git a/SOURCES/0007-lib-use-new-has-multiple-users-property-from-account.patch b/SOURCES/0007-lib-use-new-has-multiple-users-property-from-account.patch new file mode 100644 index 0000000..b623df0 --- /dev/null +++ b/SOURCES/0007-lib-use-new-has-multiple-users-property-from-account.patch @@ -0,0 +1,327 @@ +From 7849fa559cdcc197524135295bdb8e8aa13e2c13 Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Mon, 2 Oct 2017 16:17:43 -0400 +Subject: [PATCH 07/13] lib: use new has-multiple-users property from + accountsservice + +This commit changes accountsservice to use the new has-multiple-users +property provided by the daemon. +--- + src/libaccountsservice/act-user-manager.c | 27 ++++++++++----------------- + 1 file changed, 10 insertions(+), 17 deletions(-) + +diff --git a/src/libaccountsservice/act-user-manager.c b/src/libaccountsservice/act-user-manager.c +index ac06e24..30bbd78 100644 +--- a/src/libaccountsservice/act-user-manager.c ++++ b/src/libaccountsservice/act-user-manager.c +@@ -856,146 +856,130 @@ add_user (ActUserManager *manager, + { + const char *object_path; + + g_debug ("ActUserManager: tracking user '%s'", act_user_get_user_name (user)); + if (act_user_is_system_account (user)) { + g_hash_table_insert (manager->priv->system_users_by_name, + g_strdup (act_user_get_user_name (user)), + g_object_ref (user)); + } else { + g_hash_table_insert (manager->priv->normal_users_by_name, + g_strdup (act_user_get_user_name (user)), + g_object_ref (user)); + } + + object_path = act_user_get_object_path (user); + if (object_path != NULL) { + g_hash_table_replace (manager->priv->users_by_object_path, + (gpointer) object_path, + g_object_ref (user)); + } + + g_signal_connect_object (user, + "sessions-changed", + G_CALLBACK (on_user_sessions_changed), + manager, 0); + g_signal_connect_object (user, + "changed", + G_CALLBACK (on_user_changed), + manager, 0); + +- if (g_hash_table_size (manager->priv->normal_users_by_name) > 1) { +- set_has_multiple_users (manager, TRUE); +- } +- + if (manager->priv->is_loaded) { + g_debug ("ActUserManager: loaded, so emitting user-added signal"); + g_signal_emit (manager, signals[USER_ADDED], 0, user); + } else { + g_debug ("ActUserManager: not yet loaded, so not emitting user-added signal"); + } + } + + static void + remove_user (ActUserManager *manager, + ActUser *user) + { + g_debug ("ActUserManager: no longer tracking user '%s' (with object path %s)", + act_user_get_user_name (user), + act_user_get_object_path (user)); + + g_object_ref (user); + + g_signal_handlers_disconnect_by_func (user, on_user_changed, manager); + g_signal_handlers_disconnect_by_func (user, on_user_sessions_changed, manager); + if (act_user_get_object_path (user) != NULL) { + g_hash_table_remove (manager->priv->users_by_object_path, act_user_get_object_path (user)); + } + if (act_user_get_user_name (user) != NULL) { + g_hash_table_remove (manager->priv->normal_users_by_name, act_user_get_user_name (user)); + g_hash_table_remove (manager->priv->system_users_by_name, act_user_get_user_name (user)); + + } + +- if (g_hash_table_size (manager->priv->normal_users_by_name) <= 1) { +- set_has_multiple_users (manager, FALSE); +- } +- + if (manager->priv->is_loaded) { + g_debug ("ActUserManager: loaded, so emitting user-removed signal"); + g_signal_emit (manager, signals[USER_REMOVED], 0, user); + } else { + g_debug ("ActUserManager: not yet loaded, so not emitting user-removed signal"); + } + + g_debug ("ActUserManager: user '%s' (with object path %s) now removed", + act_user_get_user_name (user), + act_user_get_object_path (user)); + g_object_unref (user); + } + + static void + update_user (ActUserManager *manager, + ActUser *user) + { + const char *username; + + g_debug ("ActUserManager: updating %s", describe_user (user)); + + username = act_user_get_user_name (user); + if (g_hash_table_lookup (manager->priv->system_users_by_name, username) != NULL) { + if (!act_user_is_system_account (user)) { + g_debug ("ActUserManager: %s is no longer a system account, treating as normal user", + describe_user (user)); + g_hash_table_insert (manager->priv->normal_users_by_name, + g_strdup (act_user_get_user_name (user)), + g_object_ref (user)); + g_hash_table_remove (manager->priv->system_users_by_name, username); + g_signal_emit (manager, signals[USER_ADDED], 0, user); +- +- if (g_hash_table_size (manager->priv->normal_users_by_name) > 1) { +- set_has_multiple_users (manager, TRUE); +- } + } + } else { + if (act_user_is_system_account (user)) { + g_debug ("ActUserManager: %s is no longer a normal account, treating as system user", + describe_user (user)); + g_hash_table_insert (manager->priv->system_users_by_name, + g_strdup (act_user_get_user_name (user)), + g_object_ref (user)); + g_hash_table_remove (manager->priv->normal_users_by_name, username); + g_signal_emit (manager, signals[USER_REMOVED], 0, user); +- +- if (g_hash_table_size (manager->priv->normal_users_by_name) <= 1) { +- set_has_multiple_users (manager, FALSE); +- } + } + } + } + + static ActUser * + lookup_user_by_name (ActUserManager *manager, + const char *username) + { + ActUser *user; + + user = g_hash_table_lookup (manager->priv->normal_users_by_name, username); + + if (user == NULL) { + user = g_hash_table_lookup (manager->priv->system_users_by_name, username); + } + + return user; + } + + static void + on_new_user_loaded (ActUser *user, + GParamSpec *pspec, + ActUserManager *manager) + { + const char *username; + ActUser *old_user; + + if (!act_user_is_loaded (user)) { + g_debug ("ActUserManager: %s loaded function called when not loaded", + describe_user (user)); +@@ -2682,89 +2666,92 @@ set_include_usernames (ActUserManager *manager, + } + + static void + set_exclude_usernames (ActUserManager *manager, + GSList *list) + { + if (manager->priv->exclude_usernames != NULL) { + g_slist_foreach (manager->priv->exclude_usernames, (GFunc) g_free, NULL); + g_slist_free (manager->priv->exclude_usernames); + } + manager->priv->exclude_usernames = slist_deep_copy (list); + } + + static void + act_user_manager_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) + { + ActUserManager *self; + + self = ACT_USER_MANAGER (object); + + switch (prop_id) { + case PROP_INCLUDE_USERNAMES_LIST: + set_include_usernames (self, g_value_get_pointer (value)); + break; + case PROP_EXCLUDE_USERNAMES_LIST: + set_exclude_usernames (self, g_value_get_pointer (value)); + break; ++ case PROP_HAS_MULTIPLE_USERS: ++ set_has_multiple_users (self, g_value_get_boolean (value)); ++ break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } + } + + static void + act_user_manager_class_init (ActUserManagerClass *klass) + { + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->finalize = act_user_manager_finalize; + object_class->get_property = act_user_manager_get_property; + object_class->set_property = act_user_manager_set_property; + + g_object_class_install_property (object_class, + PROP_IS_LOADED, + g_param_spec_boolean ("is-loaded", + "Is loaded", + "Determines whether or not the manager object is loaded and ready to read from.", + FALSE, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (object_class, + PROP_HAS_MULTIPLE_USERS, + g_param_spec_boolean ("has-multiple-users", + "Has multiple users", + "Whether more than one normal user is present", + FALSE, +- G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); ++ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (object_class, + PROP_INCLUDE_USERNAMES_LIST, + g_param_spec_pointer ("include-usernames-list", + "Include usernames list", + "Usernames who are specifically included", + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (object_class, + PROP_EXCLUDE_USERNAMES_LIST, + g_param_spec_pointer ("exclude-usernames-list", + "Exclude usernames list", + "Usernames who are specifically excluded", + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + /** + * ActUserManager::user-added: + * @gobject: the object which received the signal + * @user: the #ActUser that was added + * + * Emitted when a user is added to the user manager. + */ + signals [USER_ADDED] = + g_signal_new ("user-added", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (ActUserManagerClass, user_added), + NULL, NULL, + g_cclosure_marshal_VOID__OBJECT, + G_TYPE_NONE, 1, ACT_TYPE_USER); + /** +@@ -2865,60 +2852,66 @@ act_user_manager_init (ActUserManager *manager) + + error = NULL; + manager->priv->connection = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, &error); + if (manager->priv->connection == NULL) { + if (error != NULL) { + g_warning ("Failed to connect to the D-Bus daemon: %s", error->message); + g_error_free (error); + } else { + g_warning ("Failed to connect to the D-Bus daemon"); + } + return; + } + + manager->priv->accounts_proxy = accounts_accounts_proxy_new_sync (manager->priv->connection, + G_DBUS_PROXY_FLAGS_NONE, + ACCOUNTS_NAME, + ACCOUNTS_PATH, + NULL, + &error); + if (manager->priv->accounts_proxy == NULL) { + if (error != NULL) { + g_warning ("Failed to create accounts proxy: %s", error->message); + g_error_free (error); + } else { + g_warning ("Failed to create_accounts_proxy"); + } + return; + } + g_dbus_proxy_set_default_timeout (G_DBUS_PROXY (manager->priv->accounts_proxy), G_MAXINT); + ++ g_object_bind_property (G_OBJECT (manager->priv->accounts_proxy), ++ "has-multiple-users", ++ G_OBJECT (manager), ++ "has-multiple-users", ++ G_BINDING_SYNC_CREATE); ++ + g_signal_connect (manager->priv->accounts_proxy, + "user-added", + G_CALLBACK (on_new_user_in_accounts_service), + manager); + g_signal_connect (manager->priv->accounts_proxy, + "user-deleted", + G_CALLBACK (on_user_removed_in_accounts_service), + manager); + + manager->priv->seat.state = ACT_USER_MANAGER_SEAT_STATE_UNLOADED; + } + + static void + act_user_manager_finalize (GObject *object) + { + ActUserManager *manager; + GSList *node; + + g_debug ("ActUserManager: finalizing user manager"); + + g_return_if_fail (object != NULL); + g_return_if_fail (ACT_IS_USER_MANAGER (object)); + + manager = ACT_USER_MANAGER (object); + + g_return_if_fail (manager->priv != NULL); + + g_slist_foreach (manager->priv->new_sessions, + (GFunc) unload_new_session, NULL); + g_slist_free (manager->priv->new_sessions); +-- +2.14.1 + diff --git a/SOURCES/0008-lib-factor-user-loading-functions-into-helpers.patch b/SOURCES/0008-lib-factor-user-loading-functions-into-helpers.patch new file mode 100644 index 0000000..df19f31 --- /dev/null +++ b/SOURCES/0008-lib-factor-user-loading-functions-into-helpers.patch @@ -0,0 +1,192 @@ +From 86048b515f2f2edcea0f2aab2abf5e15f7b777d6 Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Tue, 3 Oct 2017 09:58:42 -0400 +Subject: [PATCH 08/13] lib: factor user loading functions into helpers + +Right now, we process ListCachedUser results open coded. + +This commit moves the processing to helper functions. +--- + src/libaccountsservice/act-user-manager.c | 85 +++++++++++++++++-------------- + 1 file changed, 48 insertions(+), 37 deletions(-) + +diff --git a/src/libaccountsservice/act-user-manager.c b/src/libaccountsservice/act-user-manager.c +index 30bbd78..556b070 100644 +--- a/src/libaccountsservice/act-user-manager.c ++++ b/src/libaccountsservice/act-user-manager.c +@@ -1473,124 +1473,135 @@ find_user_in_accounts_service (ActUserManager *manager, + switch (request->type) { + case ACT_USER_MANAGER_FETCH_USER_FROM_USERNAME_REQUEST: + accounts_accounts_call_find_user_by_name (manager->priv->accounts_proxy, + request->username, + NULL, + on_find_user_by_name_finished, + request); + break; + case ACT_USER_MANAGER_FETCH_USER_FROM_ID_REQUEST: + accounts_accounts_call_find_user_by_id (manager->priv->accounts_proxy, + request->uid, + NULL, + on_find_user_by_id_finished, + request); + break; + + } + } + + static void + set_is_loaded (ActUserManager *manager, + gboolean is_loaded) + { + if (manager->priv->is_loaded != is_loaded) { + manager->priv->is_loaded = is_loaded; + g_object_notify (G_OBJECT (manager), "is-loaded"); + } + } + + static void +-on_list_cached_users_finished (GObject *object, +- GAsyncResult *result, +- gpointer data) ++load_user_paths (ActUserManager *manager, ++ const char * const * user_paths) + { +- AccountsAccounts *proxy = ACCOUNTS_ACCOUNTS (object); +- ActUserManager *manager = data; +- gchar **user_paths; +- GError *error = NULL; +- +- manager->priv->listing_cached_users = FALSE; +- if (!accounts_accounts_call_list_cached_users_finish (proxy, &user_paths, result, &error)) { +- g_debug ("ActUserManager: ListCachedUsers failed: %s", error->message); +- g_error_free (error); +- +- g_object_unref (manager->priv->accounts_proxy); +- manager->priv->accounts_proxy = NULL; +- +- g_debug ("ActUserManager: unrefing manager owned by failed ListCachedUsers call"); +- g_object_unref (manager); +- return; +- } +- + /* We now have a batch of unloaded users that we know about. Once that initial + * batch is loaded up, we can mark the manager as loaded. + * + * (see on_new_user_loaded) + */ +- if (g_strv_length (user_paths) > 0) { ++ if (g_strv_length ((char **) user_paths) > 0) { + int i; + + g_debug ("ActUserManager: ListCachedUsers finished, will set loaded property after list is fully loaded"); + for (i = 0; user_paths[i] != NULL; i++) { + ActUser *user; + + user = add_new_user_for_object_path (user_paths[i], manager); + if (!manager->priv->is_loaded) { + manager->priv->new_users_inhibiting_load = g_slist_prepend (manager->priv->new_users_inhibiting_load, user); + } + } + } else { + g_debug ("ActUserManager: ListCachedUsers finished with empty list, maybe setting loaded property now"); + maybe_set_is_loaded (manager); + } ++} + +- g_strfreev (user_paths); ++static void ++load_included_usernames (ActUserManager *manager) ++{ ++ GSList *l; + + /* Add users who are specifically included */ +- if (manager->priv->include_usernames != NULL) { +- GSList *l; +- +- for (l = manager->priv->include_usernames; l != NULL; l = l->next) { +- ActUser *user; ++ for (l = manager->priv->include_usernames; l != NULL; l = l->next) { ++ ActUser *user; + +- g_debug ("ActUserManager: Adding included user %s", (char *)l->data); +- /* +- * The call to act_user_manager_get_user will add the user if it is +- * valid and not already in the hash. +- */ +- user = act_user_manager_get_user (manager, l->data); +- if (user == NULL) { +- g_debug ("ActUserManager: unable to lookup user '%s'", (char *)l->data); +- } ++ g_debug ("ActUserManager: Adding included user %s", (char *)l->data); ++ /* ++ * The call to act_user_manager_get_user will add the user if it is ++ * valid and not already in the hash. ++ */ ++ user = act_user_manager_get_user (manager, l->data); ++ if (user == NULL) { ++ g_debug ("ActUserManager: unable to lookup user '%s'", (char *)l->data); + } + } ++} ++ ++static void ++on_list_cached_users_finished (GObject *object, ++ GAsyncResult *result, ++ gpointer data) ++{ ++ AccountsAccounts *proxy = ACCOUNTS_ACCOUNTS (object); ++ ActUserManager *manager = data; ++ gchar **user_paths; ++ GError *error = NULL; ++ ++ manager->priv->listing_cached_users = FALSE; ++ ++ if (!accounts_accounts_call_list_cached_users_finish (proxy, &user_paths, result, &error)) { ++ g_debug ("ActUserManager: ListCachedUsers failed: %s", error->message); ++ g_error_free (error); ++ ++ g_object_unref (manager->priv->accounts_proxy); ++ manager->priv->accounts_proxy = NULL; ++ ++ g_debug ("ActUserManager: unrefing manager owned by failed ListCachedUsers call"); ++ g_object_unref (manager); ++ return; ++ } ++ ++ load_user_paths (manager, (const char * const *) user_paths); ++ g_strfreev (user_paths); ++ ++ load_included_usernames (manager); + + g_debug ("ActUserManager: unrefing manager owned by finished ListCachedUsers call"); + g_object_unref (manager); + } + + static void + on_get_x11_display_finished (GObject *object, + GAsyncResult *result, + gpointer data) + { + ConsoleKitSession *proxy = CONSOLE_KIT_SESSION (object); + ActUserManagerNewSession *new_session = data; + GError *error = NULL; + char *x11_display; + + new_session->pending_calls--; + + if (new_session->cancellable == NULL || g_cancellable_is_cancelled (new_session->cancellable)) { + unload_new_session (new_session); + return; + } + + if (!console_kit_session_call_get_x11_display_finish (proxy, &x11_display, result, &error)) { + if (error != NULL) { + g_debug ("Failed to get the x11 display of session '%s': %s", + new_session->id, error->message); + g_error_free (error); + } else { + g_debug ("Failed to get the x11 display of session '%s'", + new_session->id); +-- +2.14.1 + diff --git a/SOURCES/0009-lib-move-accounts-proxy-creation-to-helper.patch b/SOURCES/0009-lib-move-accounts-proxy-creation-to-helper.patch new file mode 100644 index 0000000..d3bc83c --- /dev/null +++ b/SOURCES/0009-lib-move-accounts-proxy-creation-to-helper.patch @@ -0,0 +1,255 @@ +From c2b6a64834df38b493d946fab55667cebdb991b6 Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Tue, 3 Oct 2017 10:00:51 -0400 +Subject: [PATCH 09/13] lib: move accounts proxy creation to helper + +This commit factors out hte accounts proxy creation to a +helper function. +--- + src/libaccountsservice/act-user-manager.c | 74 ++++++++++++++++++------------- + 1 file changed, 43 insertions(+), 31 deletions(-) + +diff --git a/src/libaccountsservice/act-user-manager.c b/src/libaccountsservice/act-user-manager.c +index 556b070..5ac157d 100644 +--- a/src/libaccountsservice/act-user-manager.c ++++ b/src/libaccountsservice/act-user-manager.c +@@ -197,60 +197,61 @@ struct ActUserManagerPrivate + guint load_id; + + gboolean is_loaded; + gboolean has_multiple_users; + gboolean getting_sessions; + gboolean listing_cached_users; + }; + + enum { + PROP_0, + PROP_INCLUDE_USERNAMES_LIST, + PROP_EXCLUDE_USERNAMES_LIST, + PROP_IS_LOADED, + PROP_HAS_MULTIPLE_USERS + }; + + enum { + USER_ADDED, + USER_REMOVED, + USER_IS_LOGGED_IN_CHANGED, + USER_CHANGED, + LAST_SIGNAL + }; + + static guint signals [LAST_SIGNAL] = { 0, }; + + static void act_user_manager_class_init (ActUserManagerClass *klass); + static void act_user_manager_init (ActUserManager *user_manager); + static void act_user_manager_finalize (GObject *object); + ++static gboolean ensure_accounts_proxy (ActUserManager *manager); + static gboolean load_seat_incrementally (ActUserManager *manager); + static void unload_seat (ActUserManager *manager); + static void load_users (ActUserManager *manager); + static void act_user_manager_queue_load (ActUserManager *manager); + static void queue_load_seat_and_users (ActUserManager *manager); + + static void load_new_session_incrementally (ActUserManagerNewSession *new_session); + static void set_is_loaded (ActUserManager *manager, gboolean is_loaded); + + static void on_new_user_loaded (ActUser *user, + GParamSpec *pspec, + ActUserManager *manager); + static void give_up (ActUserManager *manager, + ActUserManagerFetchUserRequest *request); + static void fetch_user_incrementally (ActUserManagerFetchUserRequest *request); + + static void maybe_set_is_loaded (ActUserManager *manager); + static void update_user (ActUserManager *manager, + ActUser *user); + static gpointer user_manager_object = NULL; + + G_DEFINE_TYPE (ActUserManager, act_user_manager, G_TYPE_OBJECT) + + static const GDBusErrorEntry error_entries[] = { + { ACT_USER_MANAGER_ERROR_FAILED, "org.freedesktop.Accounts.Error.Failed" }, + { ACT_USER_MANAGER_ERROR_USER_EXISTS, "org.freedesktop.Accounts.Error.UserExists" }, + { ACT_USER_MANAGER_ERROR_USER_DOES_NOT_EXIST, "org.freedesktop.Accounts.Error.UserDoesNotExist" }, + { ACT_USER_MANAGER_ERROR_PERMISSION_DENIED, "org.freedesktop.Accounts.Error.PermissionDenied" }, + { ACT_USER_MANAGER_ERROR_NOT_SUPPORTED, "org.freedesktop.Accounts.Error.NotSupported" } + }; +@@ -2805,132 +2806,143 @@ act_user_manager_class_init (ActUserManagerClass *klass) + signals [USER_CHANGED] = + g_signal_new ("user-changed", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (ActUserManagerClass, user_changed), + NULL, NULL, + g_cclosure_marshal_VOID__OBJECT, + G_TYPE_NONE, 1, ACT_TYPE_USER); + + g_type_class_add_private (klass, sizeof (ActUserManagerPrivate)); + } + + /** + * act_user_manager_queue_load: + * @manager: a #ActUserManager + * + * Queue loading users into user manager. This must be called, and the + * #ActUserManager:is-loaded property must be %TRUE before calling + * act_user_manager_list_users() + */ + static void + act_user_manager_queue_load (ActUserManager *manager) + { + g_return_if_fail (ACT_IS_USER_MANAGER (manager)); + + if (! manager->priv->is_loaded) { + queue_load_seat_and_users (manager); + } + } + ++static gboolean ++ensure_accounts_proxy (ActUserManager *manager) ++{ ++ GError *error = NULL; ++ ++ if (manager->priv->accounts_proxy != NULL) { ++ return TRUE; ++ } ++ ++ manager->priv->accounts_proxy = accounts_accounts_proxy_new_sync (manager->priv->connection, ++ G_DBUS_PROXY_FLAGS_NONE, ++ ACCOUNTS_NAME, ++ ACCOUNTS_PATH, ++ NULL, ++ &error); ++ if (error != NULL) { ++ g_debug ("ActUserManager: getting account proxy failed: %s", error->message); ++ g_clear_error (&error); ++ return FALSE; ++ } ++ ++ g_dbus_proxy_set_default_timeout (G_DBUS_PROXY (manager->priv->accounts_proxy), G_MAXINT); ++ ++ g_object_bind_property (G_OBJECT (manager->priv->accounts_proxy), ++ "has-multiple-users", ++ G_OBJECT (manager), ++ "has-multiple-users", ++ G_BINDING_SYNC_CREATE); ++ ++ g_signal_connect (manager->priv->accounts_proxy, ++ "user-added", ++ G_CALLBACK (on_new_user_in_accounts_service), ++ manager); ++ g_signal_connect (manager->priv->accounts_proxy, ++ "user-deleted", ++ G_CALLBACK (on_user_removed_in_accounts_service), ++ manager); ++ ++ return TRUE; ++} ++ + static void + act_user_manager_init (ActUserManager *manager) + { + GError *error; + + manager->priv = ACT_USER_MANAGER_GET_PRIVATE (manager); + + act_user_manager_error_quark (); /* register dbus errors */ + + /* sessions */ + manager->priv->sessions = g_hash_table_new_full (g_str_hash, + g_str_equal, + g_free, + g_object_unref); + + /* users */ + manager->priv->normal_users_by_name = g_hash_table_new_full (g_str_hash, + g_str_equal, + g_free, + g_object_unref); + manager->priv->system_users_by_name = g_hash_table_new_full (g_str_hash, + g_str_equal, + g_free, + g_object_unref); + manager->priv->users_by_object_path = g_hash_table_new_full (g_str_hash, + g_str_equal, + NULL, + g_object_unref); + + error = NULL; + manager->priv->connection = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, &error); + if (manager->priv->connection == NULL) { + if (error != NULL) { + g_warning ("Failed to connect to the D-Bus daemon: %s", error->message); + g_error_free (error); + } else { + g_warning ("Failed to connect to the D-Bus daemon"); + } + return; + } + +- manager->priv->accounts_proxy = accounts_accounts_proxy_new_sync (manager->priv->connection, +- G_DBUS_PROXY_FLAGS_NONE, +- ACCOUNTS_NAME, +- ACCOUNTS_PATH, +- NULL, +- &error); +- if (manager->priv->accounts_proxy == NULL) { +- if (error != NULL) { +- g_warning ("Failed to create accounts proxy: %s", error->message); +- g_error_free (error); +- } else { +- g_warning ("Failed to create_accounts_proxy"); +- } +- return; +- } +- g_dbus_proxy_set_default_timeout (G_DBUS_PROXY (manager->priv->accounts_proxy), G_MAXINT); +- +- g_object_bind_property (G_OBJECT (manager->priv->accounts_proxy), +- "has-multiple-users", +- G_OBJECT (manager), +- "has-multiple-users", +- G_BINDING_SYNC_CREATE); +- +- g_signal_connect (manager->priv->accounts_proxy, +- "user-added", +- G_CALLBACK (on_new_user_in_accounts_service), +- manager); +- g_signal_connect (manager->priv->accounts_proxy, +- "user-deleted", +- G_CALLBACK (on_user_removed_in_accounts_service), +- manager); ++ ensure_accounts_proxy (manager); + + manager->priv->seat.state = ACT_USER_MANAGER_SEAT_STATE_UNLOADED; + } + + static void + act_user_manager_finalize (GObject *object) + { + ActUserManager *manager; + GSList *node; + + g_debug ("ActUserManager: finalizing user manager"); + + g_return_if_fail (object != NULL); + g_return_if_fail (ACT_IS_USER_MANAGER (object)); + + manager = ACT_USER_MANAGER (object); + + g_return_if_fail (manager->priv != NULL); + + g_slist_foreach (manager->priv->new_sessions, + (GFunc) unload_new_session, NULL); + g_slist_free (manager->priv->new_sessions); + + g_slist_foreach (manager->priv->fetch_user_requests, + (GFunc) free_fetch_user_request, NULL); + g_slist_free (manager->priv->fetch_user_requests); + + g_slist_free (manager->priv->new_users_inhibiting_load); + + node = manager->priv->new_users; +-- +2.14.1 + diff --git a/SOURCES/0010-lib-retry-connecting-to-accountsservice-when-loading.patch b/SOURCES/0010-lib-retry-connecting-to-accountsservice-when-loading.patch new file mode 100644 index 0000000..c9ef806 --- /dev/null +++ b/SOURCES/0010-lib-retry-connecting-to-accountsservice-when-loading.patch @@ -0,0 +1,84 @@ +From 9b03e52b2504f215f960549c53bd4f617797dedd Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Tue, 3 Oct 2017 10:04:28 -0400 +Subject: [PATCH 10/13] lib: retry connecting to accountsservice when loading + users + +If we were unable to connect to accountsservice and we need to +load users again, then try again. +--- + src/libaccountsservice/act-user-manager.c | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/src/libaccountsservice/act-user-manager.c b/src/libaccountsservice/act-user-manager.c +index 5ac157d..c8a0e20 100644 +--- a/src/libaccountsservice/act-user-manager.c ++++ b/src/libaccountsservice/act-user-manager.c +@@ -2544,60 +2544,64 @@ load_console_kit_sessions (ActUserManager *manager) + g_debug ("ActUserManager: no seat proxy; can't load sessions"); + return; + } + + manager->priv->getting_sessions = TRUE; + console_kit_seat_call_get_sessions (manager->priv->seat.seat_proxy, + NULL, + on_get_sessions_finished, + g_object_ref (manager)); + } + + static void + load_sessions (ActUserManager *manager) + { + #ifdef WITH_SYSTEMD + if (LOGIND_RUNNING()) { + reload_systemd_sessions (manager); + maybe_set_is_loaded (manager); + return; + } + #endif + load_console_kit_sessions (manager); + } + + static void + load_users (ActUserManager *manager) + { + g_assert (manager->priv->accounts_proxy != NULL); + g_debug ("ActUserManager: calling 'ListCachedUsers'"); + ++ if (!ensure_accounts_proxy (manager)) { ++ return; ++ } ++ + accounts_accounts_call_list_cached_users (manager->priv->accounts_proxy, + NULL, + on_list_cached_users_finished, + g_object_ref (manager)); + manager->priv->listing_cached_users = TRUE; + } + + static gboolean + load_seat_incrementally (ActUserManager *manager) + { + manager->priv->seat.load_idle_id = 0; + + switch (manager->priv->seat.state) { + case ACT_USER_MANAGER_SEAT_STATE_GET_SESSION_ID: + get_current_session_id (manager); + break; + case ACT_USER_MANAGER_SEAT_STATE_GET_SESSION_PROXY: + get_session_proxy (manager); + break; + case ACT_USER_MANAGER_SEAT_STATE_GET_ID: + get_seat_id_for_current_session (manager); + break; + case ACT_USER_MANAGER_SEAT_STATE_GET_SEAT_PROXY: + get_seat_proxy (manager); + break; + case ACT_USER_MANAGER_SEAT_STATE_LOADED: + g_debug ("ActUserManager: Seat loading sequence complete"); + break; + default: + g_assert_not_reached (); +-- +2.14.1 + diff --git a/SOURCES/0011-lib-simplify-code-dramatically.patch b/SOURCES/0011-lib-simplify-code-dramatically.patch new file mode 100644 index 0000000..e8d01db --- /dev/null +++ b/SOURCES/0011-lib-simplify-code-dramatically.patch @@ -0,0 +1,1478 @@ +From 1e2385077256bd5156d4269becc3a9e0a2d29c58 Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Tue, 3 Oct 2017 13:48:52 -0400 +Subject: [PATCH 11/13] lib: simplify code dramatically + +ActUser is a wrapper over the accountsservice daemon's +managed user objects. There's a nearly 1-to-1 correspondence +between properties on the proxy to the daemon and properties +on the ActUser object. + +This commit dramatically reduces the code, by leveraging the +proxies properties directly, rather than duplicating the values +on the ActUser object. + +At the same time, it drops manual GetAll() calls for synchronizing +the proxies properties, since it's completely redundant with the +work the proxy is doing under the hood anyway. +--- + src/libaccountsservice/act-user.c | 676 ++++++++------------------------------ + 1 file changed, 143 insertions(+), 533 deletions(-) + +diff --git a/src/libaccountsservice/act-user.c b/src/libaccountsservice/act-user.c +index 17acacb..94884a1 100644 +--- a/src/libaccountsservice/act-user.c ++++ b/src/libaccountsservice/act-user.c +@@ -81,94 +81,66 @@ enum { + PROP_PASSWORD_HINT, + PROP_HOME_DIR, + PROP_SHELL, + PROP_EMAIL, + PROP_LOCATION, + PROP_LOCKED, + PROP_AUTOMATIC_LOGIN, + PROP_SYSTEM_ACCOUNT, + PROP_NONEXISTENT, + PROP_LOCAL_ACCOUNT, + PROP_LOGIN_FREQUENCY, + PROP_LOGIN_TIME, + PROP_LOGIN_HISTORY, + PROP_ICON_FILE, + PROP_LANGUAGE, + PROP_X_SESSION, + PROP_IS_LOADED + }; + + enum { + CHANGED, + SESSIONS_CHANGED, + LAST_SIGNAL + }; + + struct _ActUser { + GObject parent; + + GDBusConnection *connection; + AccountsUser *accounts_proxy; +- GDBusProxy *object_proxy; +- GCancellable *get_all_cancellable; +- char *object_path; +- +- uid_t uid; +- char *user_name; +- char *real_name; +- char *password_hint; +- char *home_dir; +- char *shell; +- char *email; +- char *location; +- char *icon_file; +- char *language; +- char *x_session; ++ + GList *our_sessions; + GList *other_sessions; +- int login_frequency; +- gint64 login_time; +- GVariant *login_history; +- +- ActUserAccountType account_type; +- ActUserPasswordMode password_mode; +- +- guint uid_set : 1; + + guint is_loaded : 1; +- guint locked : 1; +- guint automatic_login : 1; +- guint system_account : 1; +- guint local_account : 1; + guint nonexistent : 1; +- +- guint update_info_timeout_id; + }; + + struct _ActUserClass + { + GObjectClass parent_class; + }; + + static void act_user_finalize (GObject *object); + + static guint signals[LAST_SIGNAL] = { 0 }; + + G_DEFINE_TYPE (ActUser, act_user, G_TYPE_OBJECT) + + static int + session_compare (const char *a, + const char *b) + { + if (a == NULL) { + return 1; + } else if (b == NULL) { + return -1; + } + + return strcmp (a, b); + } + + void + _act_user_add_session (ActUser *user, + const char *ssid, + gboolean is_ours) +@@ -238,128 +210,75 @@ act_user_get_num_sessions (ActUser *user) + /** + * act_user_get_num_sessions_anywhere: + * @user: a user + * + * Get the number of sessions for a user on any seat of any type. + * See also act_user_get_num_sessions(). + * + * (Currently, this function is only implemented for systemd-logind. + * For ConsoleKit, it is equivalent to act_user_get_num_sessions.) + * + * Returns: the number of sessions + */ + guint + act_user_get_num_sessions_anywhere (ActUser *user) + { + return (g_list_length (user->our_sessions) + + g_list_length (user->other_sessions)); + } + + static void + act_user_get_property (GObject *object, + guint param_id, + GValue *value, + GParamSpec *pspec) + { + ActUser *user; + + user = ACT_USER (object); + + switch (param_id) { +- case PROP_UID: +- g_value_set_int (value, user->uid); +- break; +- case PROP_USER_NAME: +- g_value_set_string (value, user->user_name); +- break; +- case PROP_REAL_NAME: +- g_value_set_string (value, user->real_name); +- break; +- case PROP_ACCOUNT_TYPE: +- g_value_set_int (value, user->account_type); +- break; +- case PROP_PASSWORD_MODE: +- g_value_set_int (value, user->password_mode); +- break; +- case PROP_PASSWORD_HINT: +- g_value_set_string (value, user->password_hint); +- break; +- case PROP_HOME_DIR: +- g_value_set_string (value, user->home_dir); +- break; +- case PROP_LOGIN_FREQUENCY: +- g_value_set_int (value, user->login_frequency); +- break; +- case PROP_LOGIN_TIME: +- g_value_set_int64 (value, user->login_time); +- break; +- case PROP_LOGIN_HISTORY: +- g_value_set_variant (value, user->login_history); +- break; +- case PROP_SHELL: +- g_value_set_string (value, user->shell); +- break; +- case PROP_EMAIL: +- g_value_set_string (value, user->email); +- break; +- case PROP_LOCATION: +- g_value_set_string (value, user->location); +- break; +- case PROP_ICON_FILE: +- g_value_set_string (value, user->icon_file); +- break; +- case PROP_LANGUAGE: +- g_value_set_string (value, user->language); +- break; +- case PROP_X_SESSION: +- g_value_set_string (value, user->x_session); +- break; +- case PROP_LOCKED: +- g_value_set_boolean (value, user->locked); +- break; +- case PROP_AUTOMATIC_LOGIN: +- g_value_set_boolean (value, user->automatic_login); +- break; +- case PROP_SYSTEM_ACCOUNT: +- g_value_set_boolean (value, user->system_account); +- break; +- case PROP_LOCAL_ACCOUNT: +- g_value_set_boolean (value, user->local_account); +- break; + case PROP_NONEXISTENT: + g_value_set_boolean (value, user->nonexistent); + break; + case PROP_IS_LOADED: + g_value_set_boolean (value, user->is_loaded); + break; + default: +- G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); ++ if (user->accounts_proxy != NULL) { ++ const char *property_name; ++ ++ property_name = g_param_spec_get_name (pspec); ++ ++ g_object_get_property (G_OBJECT (user->accounts_proxy), property_name, value); ++ ++ } + break; + } + } + + + static void + act_user_class_init (ActUserClass *class) + { + GObjectClass *gobject_class; + + gobject_class = G_OBJECT_CLASS (class); + + gobject_class->finalize = act_user_finalize; + gobject_class->get_property = act_user_get_property; + + g_object_class_install_property (gobject_class, + PROP_REAL_NAME, + g_param_spec_string ("real-name", + "Real Name", + "The real name to display for this user.", + NULL, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (gobject_class, + PROP_ACCOUNT_TYPE, + g_param_spec_int ("account-type", + "Account Type", + "The account type for this user.", + ACT_USER_ACCOUNT_TYPE_STANDARD, + ACT_USER_ACCOUNT_TYPE_ADMINISTRATOR, +@@ -525,1082 +444,773 @@ act_user_class_init (ActUserClass *class) + * Emitted when the user accounts changes in some way. + */ + signals [CHANGED] = + g_signal_new ("changed", + G_TYPE_FROM_CLASS (class), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); + /** + * ActUser::sessions-changed: + * + * Emitted when the list of sessions for this user changes. + */ + signals [SESSIONS_CHANGED] = + g_signal_new ("sessions-changed", + G_TYPE_FROM_CLASS (class), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); + } + + static void + act_user_init (ActUser *user) + { + GError *error = NULL; + +- user->local_account = TRUE; +- user->user_name = NULL; +- user->real_name = NULL; + user->our_sessions = NULL; + user->other_sessions = NULL; +- user->login_history = NULL; + + user->connection = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, &error); + if (user->connection == NULL) { + g_warning ("Couldn't connect to system bus: %s", error->message); + g_error_free (error); + } + } + + static void + act_user_finalize (GObject *object) + { + ActUser *user; + + user = ACT_USER (object); + +- g_free (user->user_name); +- g_free (user->real_name); +- g_free (user->icon_file); +- g_free (user->language); +- g_free (user->object_path); +- g_free (user->password_hint); +- g_free (user->home_dir); +- g_free (user->shell); +- g_free (user->email); +- g_free (user->location); +- if (user->login_history) +- g_variant_unref (user->login_history); +- + if (user->accounts_proxy != NULL) { + g_object_unref (user->accounts_proxy); + } + +- if (user->object_proxy != NULL) { +- g_object_unref (user->object_proxy); +- } +- +- if (user->get_all_cancellable != NULL) { +- g_object_unref (user->get_all_cancellable); +- } +- + if (user->connection != NULL) { + g_object_unref (user->connection); + } + +- if (user->update_info_timeout_id != 0) { +- g_source_remove (user->update_info_timeout_id); +- } +- + if (G_OBJECT_CLASS (act_user_parent_class)->finalize) + (*G_OBJECT_CLASS (act_user_parent_class)->finalize) (object); + } + + static void + set_is_loaded (ActUser *user, + gboolean is_loaded) + { + if (user->is_loaded != is_loaded) { + user->is_loaded = is_loaded; + g_object_notify (G_OBJECT (user), "is-loaded"); + } + } + + /** + * act_user_get_uid: + * @user: the user object to examine. + * + * Retrieves the ID of @user. + * + * Returns: (transfer none): a pointer to an array of characters which must not be modified or + * freed, or %NULL. + **/ + + uid_t + act_user_get_uid (ActUser *user) + { + g_return_val_if_fail (ACT_IS_USER (user), -1); + +- return user->uid; ++ if (user->accounts_proxy == NULL) ++ return -1; ++ ++ return accounts_user_get_uid (user->accounts_proxy); + } + + /** + * act_user_get_real_name: + * @user: the user object to examine. + * + * Retrieves the display name of @user. + * + * Returns: (transfer none): a pointer to an array of characters which must not be modified or + * freed, or %NULL. + **/ + const char * + act_user_get_real_name (ActUser *user) + { ++ const char *real_name = NULL; ++ + g_return_val_if_fail (ACT_IS_USER (user), NULL); + +- if (user->real_name == NULL || +- user->real_name[0] == '\0') { +- return user->user_name; ++ real_name = accounts_user_get_real_name (user->accounts_proxy); ++ ++ if (real_name == NULL || real_name[0] == '\0') { ++ real_name = accounts_user_get_user_name (user->accounts_proxy); + } + +- return user->real_name; ++ return real_name; + } + + /** + * act_user_get_account_type: + * @user: the user object to examine. + * + * Retrieves the account type of @user. + * + * Returns: a #ActUserAccountType + **/ + ActUserAccountType + act_user_get_account_type (ActUser *user) + { + g_return_val_if_fail (ACT_IS_USER (user), ACT_USER_ACCOUNT_TYPE_STANDARD); + +- return user->account_type; ++ if (user->accounts_proxy == NULL) ++ return ACT_USER_ACCOUNT_TYPE_STANDARD; ++ ++ return accounts_user_get_account_type (user->accounts_proxy); + } + + /** + * act_user_get_password_mode: + * @user: the user object to examine. + * + * Retrieves the password mode of @user. + * + * Returns: a #ActUserPasswordMode + **/ + ActUserPasswordMode + act_user_get_password_mode (ActUser *user) + { + g_return_val_if_fail (ACT_IS_USER (user), ACT_USER_PASSWORD_MODE_REGULAR); + +- return user->password_mode; ++ if (user->accounts_proxy == NULL) ++ return ACT_USER_PASSWORD_MODE_REGULAR; ++ ++ return accounts_user_get_password_mode (user->accounts_proxy); + } + + /** + * act_user_get_password_hint: + * @user: the user object to examine. + * + * Retrieves the password hint set by @user. + * + * Returns: (transfer none): a pointer to an array of characters which must not be modified or + * freed, or %NULL. + **/ + const char * + act_user_get_password_hint (ActUser *user) + { + g_return_val_if_fail (ACT_IS_USER (user), NULL); + +- return user->password_hint; ++ if (user->accounts_proxy == NULL) ++ return NULL; ++ ++ return accounts_user_get_password_hint (user->accounts_proxy); + } + + /** + * act_user_get_home_dir: + * @user: the user object to examine. + * + * Retrieves the home directory for @user. + * + * Returns: (transfer none): a pointer to an array of characters which must not be modified or + * freed, or %NULL. + **/ + const char * + act_user_get_home_dir (ActUser *user) + { + g_return_val_if_fail (ACT_IS_USER (user), NULL); + +- return user->home_dir; ++ if (user->accounts_proxy == NULL) ++ return NULL; ++ ++ return accounts_user_get_home_directory (user->accounts_proxy); + } + + /** + * act_user_get_shell: + * @user: the user object to examine. + * + * Retrieves the shell assigned to @user. + * + * Returns: (transfer none): a pointer to an array of characters which must not be modified or + * freed, or %NULL. + **/ + const char * + act_user_get_shell (ActUser *user) + { + g_return_val_if_fail (ACT_IS_USER (user), NULL); + +- return user->shell; ++ if (user->accounts_proxy == NULL) ++ return NULL; ++ ++ return accounts_user_get_shell (user->accounts_proxy); + } + + /** + * act_user_get_email: + * @user: the user object to examine. + * + * Retrieves the email address set by @user. + * + * Returns: (transfer none): a pointer to an array of characters which must not be modified or + * freed, or %NULL. + **/ + const char * + act_user_get_email (ActUser *user) + { + g_return_val_if_fail (ACT_IS_USER (user), NULL); + +- return user->email; ++ if (user->accounts_proxy == NULL) ++ return NULL; ++ ++ return accounts_user_get_email (user->accounts_proxy); + } + + /** + * act_user_get_location: + * @user: the user object to examine. + * + * Retrieves the location set by @user. + * + * Returns: (transfer none): a pointer to an array of characters which must not be modified or + * freed, or %NULL. + **/ + const char * + act_user_get_location (ActUser *user) + { + g_return_val_if_fail (ACT_IS_USER (user), NULL); + +- return user->location; ++ if (user->accounts_proxy == NULL) ++ return NULL; ++ ++ return accounts_user_get_location (user->accounts_proxy); + } + + /** + * act_user_get_user_name: + * @user: the user object to examine. + * + * Retrieves the login name of @user. + * + * Returns: (transfer none): a pointer to an array of characters which must not be modified or + * freed, or %NULL. + **/ + + const char * + act_user_get_user_name (ActUser *user) + { + g_return_val_if_fail (ACT_IS_USER (user), NULL); + +- return user->user_name; ++ if (user->accounts_proxy == NULL) ++ return NULL; ++ ++ return accounts_user_get_user_name (user->accounts_proxy); + } + + /** + * act_user_get_login_frequency: + * @user: a #ActUser + * + * Returns the number of times @user has logged in. + * + * Returns: the login frequency + */ + int + act_user_get_login_frequency (ActUser *user) + { + g_return_val_if_fail (ACT_IS_USER (user), 0); + +- return user->login_frequency; ++ if (user->accounts_proxy == NULL) ++ return 0; ++ ++ return accounts_user_get_login_frequency (user->accounts_proxy); + } + + /** + * act_user_get_login_time: + * @user: a #ActUser + * + * Returns the last login time for @user. + * + * Returns: (transfer none): the login time + */ + gint64 +-act_user_get_login_time (ActUser *user) { ++act_user_get_login_time (ActUser *user) ++{ + g_return_val_if_fail (ACT_IS_USER (user), 0); + +- return user->login_time; ++ if (user->accounts_proxy == NULL) ++ return 0; ++ ++ return accounts_user_get_login_time (user->accounts_proxy); + } + + /** + * act_user_get_login_history: + * @user: a #ActUser + * + * Returns the login history for @user. + * + * Returns: (transfer none): a pointer to GVariant of type "a(xxa{sv})" + * which must not be modified or freed, or %NULL. + */ + const GVariant * +-act_user_get_login_history (ActUser *user) { ++act_user_get_login_history (ActUser *user) ++{ + g_return_val_if_fail (ACT_IS_USER (user), NULL); + +- return user->login_history; ++ if (user->accounts_proxy == NULL) ++ return NULL; ++ ++ return accounts_user_get_login_history (user->accounts_proxy); + } + + /** + * act_user_collate: + * @user1: a user + * @user2: a user + * + * Organize the user by login frequency and names. + * + * Returns: negative if @user1 is before @user2, zero if equal + * or positive if @user1 is after @user2 + */ + int + act_user_collate (ActUser *user1, + ActUser *user2) + { + const char *str1; + const char *str2; + int num1; + int num2; + guint len1; + guint len2; + + g_return_val_if_fail (ACT_IS_USER (user1), 0); + g_return_val_if_fail (ACT_IS_USER (user2), 0); + +- num1 = user1->login_frequency; +- num2 = user2->login_frequency; ++ num1 = act_user_get_login_frequency (user1); ++ num2 = act_user_get_login_frequency (user2); + + if (num1 > num2) { + return -1; + } + + if (num1 < num2) { + return 1; + } + + + len1 = g_list_length (user1->our_sessions); + len2 = g_list_length (user2->our_sessions); + + if (len1 > len2) { + return -1; + } + + if (len1 < len2) { + return 1; + } + + /* if login frequency is equal try names */ +- if (user1->real_name != NULL) { +- str1 = user1->real_name; +- } else { +- str1 = user1->user_name; +- } +- +- if (user2->real_name != NULL) { +- str2 = user2->real_name; +- } else { +- str2 = user2->user_name; +- } ++ str1 = act_user_get_real_name (user1); ++ str2 = act_user_get_real_name (user2); + + if (str1 == NULL && str2 != NULL) { + return -1; + } + + if (str1 != NULL && str2 == NULL) { + return 1; + } + + if (str1 == NULL && str2 == NULL) { + return 0; + } + + return g_utf8_collate (str1, str2); + } + + /** + * act_user_is_logged_in: + * @user: a #ActUser + * + * Returns whether or not #ActUser is currently graphically logged in + * on the same seat as the seat of the session of the calling process. + * + * Returns: %TRUE or %FALSE + */ + gboolean + act_user_is_logged_in (ActUser *user) + { + return user->our_sessions != NULL; + } + + /** + * act_user_is_logged_in_anywhere: + * @user: a #ActUser + * + * Returns whether or not #ActUser is currently logged in in any way + * whatsoever. See also act_user_is_logged_in(). + * + * (Currently, this function is only implemented for systemd-logind. + * For ConsoleKit, it is equivalent to act_user_is_logged_in.) + * + * Returns: %TRUE or %FALSE + */ + gboolean + act_user_is_logged_in_anywhere (ActUser *user) + { + return user->our_sessions != NULL || user->other_sessions != NULL; + } + + /** + * act_user_get_locked: + * @user: a #ActUser + * + * Returns whether or not the #ActUser account is locked. + * + * Returns: %TRUE or %FALSE + */ + gboolean + act_user_get_locked (ActUser *user) + { +- return user->locked;; ++ return accounts_user_get_locked (user->accounts_proxy); + } + + /** + * act_user_get_automatic_login: + * @user: a #ActUser + * + * Returns whether or not #ActUser is automatically logged in at boot time. + * + * Returns: %TRUE or %FALSE + */ + gboolean + act_user_get_automatic_login (ActUser *user) + { +- return user->automatic_login; ++ g_return_val_if_fail (ACT_IS_USER (user), FALSE); ++ ++ if (user->accounts_proxy == NULL) ++ return FALSE; ++ ++ return accounts_user_get_automatic_login (user->accounts_proxy); + } + + /** + * act_user_is_system_account: + * @user: a #ActUser + * + * Returns whether or not #ActUser represents a 'system account' like + * 'root' or 'nobody'. + * + * Returns: %TRUE or %FALSE + */ + gboolean + act_user_is_system_account (ActUser *user) + { +- return user->system_account; ++ g_return_val_if_fail (ACT_IS_USER (user), TRUE); ++ ++ if (user->accounts_proxy == NULL) ++ return TRUE; ++ ++ return accounts_user_get_system_account (user->accounts_proxy); + } + + /** + * act_user_is_local_account: + * @user: the user object to examine. + * + * Retrieves whether the user is a local account or not. + * + * Returns: (transfer none): %TRUE if the user is local + **/ + gboolean + act_user_is_local_account (ActUser *user) + { + g_return_val_if_fail (ACT_IS_USER (user), FALSE); + +- return user->local_account; ++ if (user->accounts_proxy == NULL) ++ return FALSE; ++ ++ return accounts_user_get_local_account (user->accounts_proxy); + } + + /** + * act_user_is_nonexistent: + * @user: the user object to examine. + * + * Retrieves whether the user is nonexistent or not. + * + * Returns: (transfer none): %TRUE if the user is nonexistent + **/ + gboolean + act_user_is_nonexistent (ActUser *user) + { + g_return_val_if_fail (ACT_IS_USER (user), FALSE); + + return user->nonexistent; + } + + /** + * act_user_get_icon_file: + * @user: a #ActUser + * + * Returns the path to the account icon belonging to @user. + * + * Returns: (transfer none): a path to an icon + */ + const char * + act_user_get_icon_file (ActUser *user) + { + g_return_val_if_fail (ACT_IS_USER (user), NULL); + +- return user->icon_file; ++ if (user->accounts_proxy == NULL) ++ return NULL; ++ ++ return accounts_user_get_icon_file (user->accounts_proxy); + } + + /** + * act_user_get_language: + * @user: a #ActUser + * + * Returns the path to the configured locale of @user. + * + * Returns: (transfer none): a path to an icon + */ + const char * + act_user_get_language (ActUser *user) + { + g_return_val_if_fail (ACT_IS_USER (user), NULL); + +- return user->language; ++ if (user->accounts_proxy == NULL) ++ return NULL; ++ ++ return accounts_user_get_language (user->accounts_proxy); + } + + /** + * act_user_get_x_session: + * @user: a #ActUser + * + * Returns the path to the configured X session for @user. + * + * Returns: (transfer none): a path to an icon + */ + const char * + act_user_get_x_session (ActUser *user) + { + g_return_val_if_fail (ACT_IS_USER (user), NULL); + +- return user->x_session; ++ if (user->accounts_proxy == NULL) ++ return NULL; ++ ++ return accounts_user_get_xsession (user->accounts_proxy); + } + + /** + * act_user_get_object_path: + * @user: a #ActUser + * + * Returns the user accounts service object path of @user, + * or %NULL if @user doesn't have an object path associated + * with it. + * + * Returns: (transfer none): the object path of the user + */ + const char * + act_user_get_object_path (ActUser *user) + { + g_return_val_if_fail (ACT_IS_USER (user), NULL); + +- return user->object_path; ++ if (user->accounts_proxy == NULL) ++ return NULL; ++ ++ return g_dbus_proxy_get_object_path (G_DBUS_PROXY (user->accounts_proxy)); + } + + /** + * act_user_get_primary_session_id: + * @user: a #ActUser + * + * Returns the id of the primary session of @user, or %NULL if @user + * has no primary session. The primary session will always be + * graphical and will be chosen from the sessions on the same seat as + * the seat of the session of the calling process. + * + * Returns: (transfer none): the id of the primary session of the user + */ + const char * + act_user_get_primary_session_id (ActUser *user) + { + if (user->our_sessions == NULL) { + g_debug ("User %s is not logged in here, so has no primary session", + act_user_get_user_name (user)); + return NULL; + } + + /* FIXME: better way to choose? */ + return user->our_sessions->data; + } + +-static void +-collect_props (const gchar *key, +- GVariant *value, +- ActUser *user) +-{ +- gboolean handled = TRUE; +- +- if (strcmp (key, "Uid") == 0) { +- guint64 new_uid; +- +- new_uid = g_variant_get_uint64 (value); +- if (!user->uid_set || (guint64) user->uid != new_uid) { +- user->uid = (uid_t) new_uid; +- user->uid_set = TRUE; +- g_object_notify (G_OBJECT (user), "uid"); +- } +- } else if (strcmp (key, "UserName") == 0) { +- const char *new_user_name; +- +- new_user_name = g_variant_get_string (value, NULL); +- if (g_strcmp0 (user->user_name, new_user_name) != 0) { +- g_free (user->user_name); +- user->user_name = g_strdup (new_user_name); +- g_object_notify (G_OBJECT (user), "user-name"); +- } +- } else if (strcmp (key, "RealName") == 0) { +- const char *new_real_name; +- +- new_real_name = g_variant_get_string (value, NULL); +- if (g_strcmp0 (user->real_name, new_real_name) != 0) { +- g_free (user->real_name); +- user->real_name = g_strdup (new_real_name); +- g_object_notify (G_OBJECT (user), "real-name"); +- } +- } else if (strcmp (key, "AccountType") == 0) { +- int new_account_type; +- +- new_account_type = g_variant_get_int32 (value); +- if ((int) user->account_type != new_account_type) { +- user->account_type = (ActUserAccountType) new_account_type; +- g_object_notify (G_OBJECT (user), "account-type"); +- } +- } else if (strcmp (key, "PasswordMode") == 0) { +- int new_password_mode; +- +- new_password_mode = g_variant_get_int32 (value); +- if ((int) user->password_mode != new_password_mode) { +- user->password_mode = (ActUserPasswordMode) new_password_mode; +- g_object_notify (G_OBJECT (user), "password-mode"); +- } +- } else if (strcmp (key, "PasswordHint") == 0) { +- const char *new_password_hint; +- +- new_password_hint = g_variant_get_string (value, NULL); +- if (g_strcmp0 (user->password_hint, new_password_hint) != 0) { +- g_free (user->password_hint); +- user->password_hint = g_strdup (new_password_hint); +- g_object_notify (G_OBJECT (user), "password-hint"); +- } +- } else if (strcmp (key, "HomeDirectory") == 0) { +- const char *new_home_dir; +- +- new_home_dir = g_variant_get_string (value, NULL); +- if (g_strcmp0 (user->home_dir, new_home_dir) != 0) { +- g_free (user->home_dir); +- user->home_dir = g_strdup (new_home_dir); +- g_object_notify (G_OBJECT (user), "home-directory"); +- } +- } else if (strcmp (key, "Shell") == 0) { +- const char *new_shell; +- +- new_shell = g_variant_get_string (value, NULL); +- if (g_strcmp0 (user->shell, new_shell) != 0) { +- g_free (user->shell); +- user->shell = g_strdup (new_shell); +- g_object_notify (G_OBJECT (user), "shell"); +- } +- } else if (strcmp (key, "Email") == 0) { +- const char *new_email; +- +- new_email = g_variant_get_string (value, NULL); +- if (g_strcmp0 (user->email, new_email) != 0) { +- g_free (user->email); +- user->email = g_strdup (new_email); +- g_object_notify (G_OBJECT (user), "email"); +- } +- } else if (strcmp (key, "Location") == 0) { +- const char *new_location; +- +- new_location = g_variant_get_string (value, NULL); +- if (g_strcmp0 (user->location, new_location) != 0) { +- g_free (user->location); +- user->location = g_strdup (new_location); +- g_object_notify (G_OBJECT (user), "location"); +- } +- } else if (strcmp (key, "Locked") == 0) { +- gboolean new_locked_state; +- +- new_locked_state = g_variant_get_boolean (value); +- if (new_locked_state != user->locked) { +- user->locked = new_locked_state; +- g_object_notify (G_OBJECT (user), "locked"); +- } +- } else if (strcmp (key, "AutomaticLogin") == 0) { +- gboolean new_automatic_login_state; +- +- new_automatic_login_state = g_variant_get_boolean (value); +- if (new_automatic_login_state != user->automatic_login) { +- user->automatic_login = new_automatic_login_state; +- g_object_notify (G_OBJECT (user), "automatic-login"); +- } +- } else if (strcmp (key, "SystemAccount") == 0) { +- gboolean new_system_account_state; +- +- new_system_account_state = g_variant_get_boolean (value); +- if (new_system_account_state != user->system_account) { +- user->system_account = new_system_account_state; +- g_object_notify (G_OBJECT (user), "system-account"); +- } +- } else if (strcmp (key, "LocalAccount") == 0) { +- gboolean new_local; +- +- new_local = g_variant_get_boolean (value); +- if (user->local_account != new_local) { +- user->local_account = new_local; +- g_object_notify (G_OBJECT (user), "local-account"); +- } +- } else if (strcmp (key, "LoginFrequency") == 0) { +- int new_login_frequency; +- +- new_login_frequency = (int) g_variant_get_uint64 (value); +- if ((int) user->login_frequency != (int) new_login_frequency) { +- user->login_frequency = new_login_frequency; +- g_object_notify (G_OBJECT (user), "login-frequency"); +- } +- } else if (strcmp (key, "LoginTime") == 0) { +- gint64 new_login_time = g_variant_get_int64 (value); +- +- if (user->login_time != new_login_time) { +- user->login_time = new_login_time; +- g_object_notify (G_OBJECT (user), "login-time"); +- } +- } else if (strcmp (key, "LoginHistory") == 0) { +- GVariant *new_login_history = value; +- +- if (user->login_history == NULL || +- !g_variant_equal (user->login_history, new_login_history)) { +- if (user->login_history) +- g_variant_unref (user->login_history); +- user->login_history = g_variant_ref (new_login_history); +- g_object_notify (G_OBJECT (user), "login-history"); +- } +- } else if (strcmp (key, "IconFile") == 0) { +- const char *new_icon_file; +- +- new_icon_file = g_variant_get_string (value, NULL); +- if (g_strcmp0 (user->icon_file, new_icon_file) != 0) { +- g_free (user->icon_file); +- user->icon_file = g_strdup (new_icon_file); +- g_object_notify (G_OBJECT (user), "icon-file"); +- } +- } else if (strcmp (key, "Language") == 0) { +- const char *new_language; +- +- new_language = g_variant_get_string (value, NULL); +- if (g_strcmp0 (user->language, new_language) != 0) { +- g_free (user->language); +- user->language = g_strdup (new_language); +- g_object_notify (G_OBJECT (user), "language"); +- } +- } else if (strcmp (key, "XSession") == 0) { +- const char *new_x_session; +- +- new_x_session = g_variant_get_string (value, NULL); +- if (g_strcmp0 (user->x_session, new_x_session) != 0) { +- g_free (user->x_session); +- user->x_session = g_strdup (new_x_session); +- g_object_notify (G_OBJECT (user), "x-session"); +- } +- } else { +- handled = FALSE; +- } +- +- if (!handled) { +- g_debug ("unhandled property %s", key); +- } +-} +- +-static void +-on_get_all_finished (GObject *object, +- GAsyncResult *result, +- gpointer data) +-{ +- GDBusProxy *proxy = G_DBUS_PROXY (object); +- ActUser *user = data; +- GError *error; +- GVariant *res; +- GVariantIter *iter; +- gchar *key; +- GVariant *value; +- +- g_assert (G_IS_DBUS_PROXY (user->object_proxy)); +- g_assert (user->object_proxy == proxy); +- +- error = NULL; +- res = g_dbus_proxy_call_finish (proxy, result, &error); +- +- g_clear_object (&user->get_all_cancellable); +- +- if (! res) { +- g_debug ("Error calling GetAll() when retrieving properties for %s: %s", +- user->object_path, error->message); +- g_error_free (error); +- +- if (!user->is_loaded) { +- set_is_loaded (user, TRUE); +- } +- return; +- } +- +- g_variant_get (res, "(a{sv})", &iter); +- while (g_variant_iter_next (iter, "{sv}", &key, &value)) { +- collect_props (key, value, user); +- g_free (key); +- g_variant_unref (value); +- } +- g_variant_iter_free (iter); +- g_variant_unref (res); +- +- if (!user->is_loaded) { +- set_is_loaded (user, TRUE); +- } +- +- g_signal_emit (user, signals[CHANGED], 0); +-} +- +-static void +-update_info (ActUser *user) +-{ +- g_assert (G_IS_DBUS_PROXY (user->object_proxy)); +- +- if (user->get_all_cancellable != NULL) { +- g_cancellable_cancel (user->get_all_cancellable); +- g_clear_object (&user->get_all_cancellable); +- } +- +- user->get_all_cancellable = g_cancellable_new (); +- g_dbus_proxy_call (user->object_proxy, +- "GetAll", +- g_variant_new ("(s)", ACCOUNTS_USER_INTERFACE), +- G_DBUS_CALL_FLAGS_NONE, +- -1, +- user->get_all_cancellable, +- on_get_all_finished, +- user); +-} +- +-static gboolean +-on_timeout_update_info (ActUser *user) +-{ +- update_info (user); +- user->update_info_timeout_id = 0; +- +- return G_SOURCE_REMOVE; +-} +- +-static void +-changed_handler (AccountsUser *object, +- gpointer *data) +-{ +- ActUser *user = ACT_USER (data); +- +- if (user->update_info_timeout_id != 0) +- return; +- +- user->update_info_timeout_id = g_timeout_add (250, (GSourceFunc) on_timeout_update_info, user); +-} +- + /** + * _act_user_update_as_nonexistent: + * @user: the user object to update. + * + * Set's the 'non-existent' property of @user to #TRUE + * Can only be called before the user is loaded. + **/ + void + _act_user_update_as_nonexistent (ActUser *user) + { + g_return_if_fail (ACT_IS_USER (user)); + g_return_if_fail (!act_user_is_loaded (user)); +- g_return_if_fail (user->object_path == NULL); ++ g_return_if_fail (act_user_get_object_path (user) == NULL); + + user->nonexistent = TRUE; + g_object_notify (G_OBJECT (user), "nonexistent"); + + set_is_loaded (user, TRUE); + } + ++static void ++on_accounts_proxy_changed (ActUser *user) ++{ ++ g_signal_emit (user, signals[CHANGED], 0); ++} ++ + /** + * _act_user_update_from_object_path: + * @user: the user object to update. + * @object_path: the object path of the user to use. + * + * Updates the properties of @user from the accounts service via + * the object path in @object_path. + **/ + void + _act_user_update_from_object_path (ActUser *user, + const char *object_path) + { +- GError *error = NULL; ++ AccountsUser *accounts_proxy; ++ GError *error = NULL; + + g_return_if_fail (ACT_IS_USER (user)); + g_return_if_fail (object_path != NULL); +- g_return_if_fail (user->object_path == NULL); +- +- user->object_path = g_strdup (object_path); +- +- user->accounts_proxy = accounts_user_proxy_new_sync (user->connection, +- G_DBUS_PROXY_FLAGS_NONE, +- ACCOUNTS_NAME, +- user->object_path, +- NULL, +- &error); +- if (!user->accounts_proxy) { ++ g_return_if_fail (act_user_get_object_path (user) == NULL); ++ ++ accounts_proxy = accounts_user_proxy_new_sync (user->connection, ++ G_DBUS_PROXY_FLAGS_NONE, ++ ACCOUNTS_NAME, ++ object_path, ++ NULL, ++ &error); ++ if (!accounts_proxy) { + g_warning ("Couldn't create accounts proxy: %s", error->message); + g_error_free (error); + return; + } +- g_dbus_proxy_set_default_timeout (G_DBUS_PROXY (user->accounts_proxy), INT_MAX); + +- g_signal_connect (user->accounts_proxy, "changed", G_CALLBACK (changed_handler), user); ++ user->accounts_proxy = accounts_proxy; + +- user->object_proxy = g_dbus_proxy_new_sync (user->connection, +- G_DBUS_PROXY_FLAGS_NONE, +- 0, +- ACCOUNTS_NAME, +- user->object_path, +- "org.freedesktop.DBus.Properties", +- NULL, +- &error); +- if (!user->object_proxy) { +- g_warning ("Couldn't create accounts property proxy: %s", error->message); +- g_error_free (error); +- return; +- } ++ g_signal_connect_object (user->accounts_proxy, ++ "changed", ++ G_CALLBACK (on_accounts_proxy_changed), ++ user, ++ G_CONNECT_SWAPPED); + +- update_info (user); ++ g_dbus_proxy_set_default_timeout (G_DBUS_PROXY (user->accounts_proxy), INT_MAX); ++ ++ set_is_loaded (user, TRUE); + } + + void + _act_user_update_login_frequency (ActUser *user, + int login_frequency) + { +- if (user->login_frequency != login_frequency) { +- user->login_frequency = login_frequency; +- g_object_notify (G_OBJECT (user), "login-frequency"); ++ if (act_user_get_login_frequency (user) == login_frequency) { ++ return; + } ++ ++ accounts_user_set_login_frequency (user->accounts_proxy, ++ login_frequency); + } + + static void + copy_sessions_lists (ActUser *user, + ActUser *user_to_copy) + { + GList *node; + + for (node = g_list_last (user_to_copy->our_sessions); + node != NULL; + node = node->prev) { + user->our_sessions = g_list_prepend (user->our_sessions, g_strdup (node->data)); + } + + for (node = g_list_last (user_to_copy->other_sessions); + node != NULL; + node = node->prev) { + user->other_sessions = g_list_prepend (user->other_sessions, g_strdup (node->data)); + } + } + + void + _act_user_load_from_user (ActUser *user, + ActUser *user_to_copy) + { + if (!user_to_copy->is_loaded) { + return; + } + +- /* loading users may already have a uid, user name, or session list +- * from creation, so only update them if necessary +- */ +- if (!user->uid_set) { +- user->uid = user_to_copy->uid; +- g_object_notify (G_OBJECT (user), "uid"); +- } ++ user->accounts_proxy = g_object_ref (user_to_copy->accounts_proxy); + +- if (user->user_name == NULL) { +- user->user_name = g_strdup (user_to_copy->user_name); +- g_object_notify (G_OBJECT (user), "user-name"); +- } ++ g_signal_connect_object (user->accounts_proxy, ++ "changed", ++ G_CALLBACK (on_accounts_proxy_changed), ++ user, ++ G_CONNECT_SWAPPED); + + if (user->our_sessions == NULL && user->other_sessions == NULL) { + copy_sessions_lists (user, user_to_copy); + g_signal_emit (user, signals[SESSIONS_CHANGED], 0); + } + +- g_free (user->real_name); +- user->real_name = g_strdup (user_to_copy->real_name); +- g_object_notify (G_OBJECT (user), "real-name"); +- +- g_free (user->password_hint); +- user->password_hint = g_strdup (user_to_copy->password_hint); +- g_object_notify (G_OBJECT (user), "password-hint"); +- +- g_free (user->home_dir); +- user->home_dir = g_strdup (user_to_copy->home_dir); +- g_object_notify (G_OBJECT (user), "home-directory"); +- +- g_free (user->shell); +- user->shell = g_strdup (user_to_copy->shell); +- g_object_notify (G_OBJECT (user), "shell"); +- +- g_free (user->email); +- user->email = g_strdup (user_to_copy->email); +- g_object_notify (G_OBJECT (user), "email"); +- +- g_free (user->location); +- user->location = g_strdup (user_to_copy->location); +- g_object_notify (G_OBJECT (user), "location"); +- +- g_free (user->icon_file); +- user->icon_file = g_strdup (user_to_copy->icon_file); +- g_object_notify (G_OBJECT (user), "icon-file"); +- +- g_free (user->language); +- user->language = g_strdup (user_to_copy->language); +- g_object_notify (G_OBJECT (user), "language"); +- +- g_free (user->x_session); +- user->x_session = g_strdup (user_to_copy->x_session); +- g_object_notify (G_OBJECT (user), "x-session"); +- +- user->login_frequency = user_to_copy->login_frequency; +- g_object_notify (G_OBJECT (user), "login-frequency"); +- +- user->login_time = user_to_copy->login_time; +- g_object_notify (G_OBJECT (user), "login-time"); +- +- user->login_history = user_to_copy->login_history ? g_variant_ref (user_to_copy->login_history) : NULL; +- g_object_notify (G_OBJECT (user), "login-history"); +- +- user->account_type = user_to_copy->account_type; +- g_object_notify (G_OBJECT (user), "account-type"); +- +- user->password_mode = user_to_copy->password_mode; +- g_object_notify (G_OBJECT (user), "password-mode"); +- +- user->nonexistent = user_to_copy->nonexistent; +- g_object_notify (G_OBJECT (user), "nonexistent"); +- + set_is_loaded (user, TRUE); + } + + /** + * act_user_is_loaded: + * @user: a #ActUser + * + * Determines whether or not the user object is loaded and ready to read from. + * #ActUserManager:is-loaded property must be %TRUE before calling + * act_user_manager_list_users() + * + * Returns: %TRUE or %FALSE + */ + gboolean + act_user_is_loaded (ActUser *user) + { + return user->is_loaded; + } + + /** + * act_user_get_login_history: + * @user: the user object to query. + * @expiration_time: time users passwor expires + * @last_change_time, + * @min_days_between_changes, + * @max_days_between_changes, + * @days_to_warn, + * @days_after_expiration_until_lock) + * + * Assigns a new email to @user. +-- +2.14.1 + diff --git a/SOURCES/0012-lib-don-t-track-user-added-user-removed-until-we-get.patch b/SOURCES/0012-lib-don-t-track-user-added-user-removed-until-we-get.patch new file mode 100644 index 0000000..31cf17b --- /dev/null +++ b/SOURCES/0012-lib-don-t-track-user-added-user-removed-until-we-get.patch @@ -0,0 +1,237 @@ +From f674b04a79a7974a697ad69a997bec21742d3299 Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Wed, 4 Oct 2017 11:21:44 -0400 +Subject: [PATCH 12/13] lib: don't track user-added/user-removed until we get + initial list + +There's no reason to process user-added and user-removed signals +until we have our starting list. Those signals are supposed to +be a delta off that list anyway. +--- + src/libaccountsservice/act-user-manager.c | 14 ++++++++++++++ + 1 file changed, 14 insertions(+) + +diff --git a/src/libaccountsservice/act-user-manager.c b/src/libaccountsservice/act-user-manager.c +index c8a0e20..11049e0 100644 +--- a/src/libaccountsservice/act-user-manager.c ++++ b/src/libaccountsservice/act-user-manager.c +@@ -173,60 +173,61 @@ typedef struct + char *object_path; + char *description; + } ActUserManagerFetchUserRequest; + + struct ActUserManagerPrivate + { + GHashTable *normal_users_by_name; + GHashTable *system_users_by_name; + GHashTable *users_by_object_path; + GHashTable *sessions; + GDBusConnection *connection; + AccountsAccounts *accounts_proxy; + ConsoleKitManager *ck_manager_proxy; + + ActUserManagerSeat seat; + + GSList *new_sessions; + GSList *new_users; + GSList *new_users_inhibiting_load; + GSList *fetch_user_requests; + + GSList *exclude_usernames; + GSList *include_usernames; + + guint load_id; + + gboolean is_loaded; + gboolean has_multiple_users; + gboolean getting_sessions; + gboolean listing_cached_users; ++ gboolean list_cached_users_done; + }; + + enum { + PROP_0, + PROP_INCLUDE_USERNAMES_LIST, + PROP_EXCLUDE_USERNAMES_LIST, + PROP_IS_LOADED, + PROP_HAS_MULTIPLE_USERS + }; + + enum { + USER_ADDED, + USER_REMOVED, + USER_IS_LOGGED_IN_CHANGED, + USER_CHANGED, + LAST_SIGNAL + }; + + static guint signals [LAST_SIGNAL] = { 0, }; + + static void act_user_manager_class_init (ActUserManagerClass *klass); + static void act_user_manager_init (ActUserManager *user_manager); + static void act_user_manager_finalize (GObject *object); + + static gboolean ensure_accounts_proxy (ActUserManager *manager); + static gboolean load_seat_incrementally (ActUserManager *manager); + static void unload_seat (ActUserManager *manager); + static void load_users (ActUserManager *manager); + static void act_user_manager_queue_load (ActUserManager *manager); + static void queue_load_seat_and_users (ActUserManager *manager); +@@ -1075,80 +1076,92 @@ add_new_user_for_object_path (const char *object_path, + if (user != NULL) { + g_debug ("ActUserManager: tracking existing %s with object path %s", + describe_user (user), object_path); + return user; + } + + user = find_new_user_with_object_path (manager, object_path); + + if (user != NULL) { + g_debug ("ActUserManager: tracking existing (but very recently added) %s with object path %s", + describe_user (user), object_path); + return user; + } + + g_debug ("ActUserManager: tracking new user with object path %s", object_path); + + user = create_new_user (manager); + _act_user_update_from_object_path (user, object_path); + + return user; + } + + static void + on_new_user_in_accounts_service (GDBusProxy *proxy, + const char *object_path, + gpointer user_data) + { + ActUserManager *manager = ACT_USER_MANAGER (user_data); + ActUser *user; + ++ /* Only track user changes if the user has requested a list ++ * of users */ ++ if (!manager->priv->list_cached_users_done) { ++ return; ++ } ++ + if (!manager->priv->is_loaded) { + g_debug ("ActUserManager: ignoring new user in accounts service with object path %s since not loaded yet", object_path); + return; + } + + g_debug ("ActUserManager: new user in accounts service with object path %s", object_path); + user = add_new_user_for_object_path (object_path, manager); + + g_object_unref (user); + } + + static void + on_user_removed_in_accounts_service (GDBusProxy *proxy, + const char *object_path, + gpointer user_data) + { + ActUserManager *manager = ACT_USER_MANAGER (user_data); + ActUser *user; + GSList *node; + ++ /* Only track user changes if the user has requested a list ++ * of users */ ++ if (!manager->priv->list_cached_users_done) { ++ return; ++ } ++ + user = g_hash_table_lookup (manager->priv->users_by_object_path, object_path); + + if (user == NULL) { + g_debug ("ActUserManager: ignoring untracked user %s", object_path); + return; + } else { + g_debug ("ActUserManager: tracked user %s removed from accounts service", object_path); + } + + node = g_slist_find (manager->priv->new_users, user); + if (node != NULL) { + g_signal_handlers_disconnect_by_func (user, on_new_user_loaded, manager); + g_object_unref (user); + manager->priv->new_users = g_slist_delete_link (manager->priv->new_users, node); + } + + remove_user (manager, user); + } + + static void + on_get_current_session_finished (GObject *object, + GAsyncResult *result, + gpointer data) + { + ConsoleKitManager *proxy = CONSOLE_KIT_MANAGER (object); + ActUserManager *manager = data; + GError *error = NULL; + char *session_id; + + g_assert (manager->priv->seat.state == ACT_USER_MANAGER_SEAT_STATE_GET_SESSION_ID); +@@ -1532,60 +1545,61 @@ load_included_usernames (ActUserManager *manager) + { + GSList *l; + + /* Add users who are specifically included */ + for (l = manager->priv->include_usernames; l != NULL; l = l->next) { + ActUser *user; + + g_debug ("ActUserManager: Adding included user %s", (char *)l->data); + /* + * The call to act_user_manager_get_user will add the user if it is + * valid and not already in the hash. + */ + user = act_user_manager_get_user (manager, l->data); + if (user == NULL) { + g_debug ("ActUserManager: unable to lookup user '%s'", (char *)l->data); + } + } + } + + static void + on_list_cached_users_finished (GObject *object, + GAsyncResult *result, + gpointer data) + { + AccountsAccounts *proxy = ACCOUNTS_ACCOUNTS (object); + ActUserManager *manager = data; + gchar **user_paths; + GError *error = NULL; + + manager->priv->listing_cached_users = FALSE; ++ manager->priv->list_cached_users_done = TRUE; + + if (!accounts_accounts_call_list_cached_users_finish (proxy, &user_paths, result, &error)) { + g_debug ("ActUserManager: ListCachedUsers failed: %s", error->message); + g_error_free (error); + + g_object_unref (manager->priv->accounts_proxy); + manager->priv->accounts_proxy = NULL; + + g_debug ("ActUserManager: unrefing manager owned by failed ListCachedUsers call"); + g_object_unref (manager); + return; + } + + load_user_paths (manager, (const char * const *) user_paths); + g_strfreev (user_paths); + + load_included_usernames (manager); + + g_debug ("ActUserManager: unrefing manager owned by finished ListCachedUsers call"); + g_object_unref (manager); + } + + static void + on_get_x11_display_finished (GObject *object, + GAsyncResult *result, + gpointer data) + { + ConsoleKitSession *proxy = CONSOLE_KIT_SESSION (object); + ActUserManagerNewSession *new_session = data; + GError *error = NULL; +-- +2.14.1 + diff --git a/SOURCES/0013-lib-only-track-users-after-act_user_manager_list_use.patch b/SOURCES/0013-lib-only-track-users-after-act_user_manager_list_use.patch new file mode 100644 index 0000000..a0cce3e --- /dev/null +++ b/SOURCES/0013-lib-only-track-users-after-act_user_manager_list_use.patch @@ -0,0 +1,741 @@ +From b1ec3114a49c9e5331a4f7c106a58867499ac1ce Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Wed, 4 Oct 2017 11:28:50 -0400 +Subject: [PATCH 13/13] lib: only track users after act_user_manager_list_users + +At the moment, as soon as someone calls +act_user_manager_get_default() we end up firing of an asynchronous +call to get a list of all users. This is problematic since not all +programs using accountsservice want a list of users. + +This commit changes the code to only get a list of users when the +caller invokes act_user_manager_list_users. This does mean some calls +that were async before are synchronous now, but user proxies were +always obtained synchronously, and they're by far the slowest part +of listing users, so I don't expect this introduce any noticeable +blocking. + +Longer term, to fix the sync i/o bits, I think we should +probably ditch libaccountsservice and just make accountsservice use +ObjectManager interfaces over d-bus. +--- + src/libaccountsservice/act-user-manager.c | 145 +++++++++++++++--------------- + 1 file changed, 75 insertions(+), 70 deletions(-) + +diff --git a/src/libaccountsservice/act-user-manager.c b/src/libaccountsservice/act-user-manager.c +index 11049e0..d0b38e2 100644 +--- a/src/libaccountsservice/act-user-manager.c ++++ b/src/libaccountsservice/act-user-manager.c +@@ -172,92 +172,93 @@ typedef struct + }; + char *object_path; + char *description; + } ActUserManagerFetchUserRequest; + + struct ActUserManagerPrivate + { + GHashTable *normal_users_by_name; + GHashTable *system_users_by_name; + GHashTable *users_by_object_path; + GHashTable *sessions; + GDBusConnection *connection; + AccountsAccounts *accounts_proxy; + ConsoleKitManager *ck_manager_proxy; + + ActUserManagerSeat seat; + + GSList *new_sessions; + GSList *new_users; + GSList *new_users_inhibiting_load; + GSList *fetch_user_requests; + + GSList *exclude_usernames; + GSList *include_usernames; + + guint load_id; + + gboolean is_loaded; + gboolean has_multiple_users; + gboolean getting_sessions; +- gboolean listing_cached_users; + gboolean list_cached_users_done; + }; + + enum { + PROP_0, + PROP_INCLUDE_USERNAMES_LIST, + PROP_EXCLUDE_USERNAMES_LIST, + PROP_IS_LOADED, + PROP_HAS_MULTIPLE_USERS + }; + + enum { + USER_ADDED, + USER_REMOVED, + USER_IS_LOGGED_IN_CHANGED, + USER_CHANGED, + LAST_SIGNAL + }; + + static guint signals [LAST_SIGNAL] = { 0, }; + + static void act_user_manager_class_init (ActUserManagerClass *klass); + static void act_user_manager_init (ActUserManager *user_manager); + static void act_user_manager_finalize (GObject *object); + + static gboolean ensure_accounts_proxy (ActUserManager *manager); + static gboolean load_seat_incrementally (ActUserManager *manager); + static void unload_seat (ActUserManager *manager); + static void load_users (ActUserManager *manager); ++static void load_user (ActUserManager *manager, ++ const char *username); + static void act_user_manager_queue_load (ActUserManager *manager); +-static void queue_load_seat_and_users (ActUserManager *manager); ++static void queue_load_seat (ActUserManager *manager); + + static void load_new_session_incrementally (ActUserManagerNewSession *new_session); + static void set_is_loaded (ActUserManager *manager, gboolean is_loaded); + + static void on_new_user_loaded (ActUser *user, + GParamSpec *pspec, + ActUserManager *manager); + static void give_up (ActUserManager *manager, + ActUserManagerFetchUserRequest *request); + static void fetch_user_incrementally (ActUserManagerFetchUserRequest *request); + + static void maybe_set_is_loaded (ActUserManager *manager); + static void update_user (ActUserManager *manager, + ActUser *user); + static gpointer user_manager_object = NULL; + + G_DEFINE_TYPE (ActUserManager, act_user_manager, G_TYPE_OBJECT) + + static const GDBusErrorEntry error_entries[] = { + { ACT_USER_MANAGER_ERROR_FAILED, "org.freedesktop.Accounts.Error.Failed" }, + { ACT_USER_MANAGER_ERROR_USER_EXISTS, "org.freedesktop.Accounts.Error.UserExists" }, + { ACT_USER_MANAGER_ERROR_USER_DOES_NOT_EXIST, "org.freedesktop.Accounts.Error.UserDoesNotExist" }, + { ACT_USER_MANAGER_ERROR_PERMISSION_DENIED, "org.freedesktop.Accounts.Error.PermissionDenied" }, + { ACT_USER_MANAGER_ERROR_NOT_SUPPORTED, "org.freedesktop.Accounts.Error.NotSupported" } + }; + + GQuark + act_user_manager_error_quark (void) + { + static volatile gsize ret = 0; +@@ -711,91 +712,87 @@ on_get_seat_id_finished (GObject *object, + GAsyncResult *result, + gpointer data) + { + ConsoleKitSession *proxy = CONSOLE_KIT_SESSION (object); + ActUserManager *manager = data; + GError *error = NULL; + char *seat_id; + + if (!console_kit_session_call_get_seat_id_finish (proxy, &seat_id, result, &error)) { + if (error != NULL) { + g_debug ("Failed to identify the seat of the " + "current session: %s", + error->message); + g_error_free (error); + } else { + g_debug ("Failed to identify the seat of the " + "current session"); + } + + g_debug ("ActUserManager: GetSeatId call failed, so unloading seat"); + unload_seat (manager); + + goto out; + } + + g_debug ("ActUserManager: Found current seat: %s", seat_id); + + manager->priv->seat.id = seat_id; + manager->priv->seat.state++; + +- load_seat_incrementally (manager); +- + out: + g_debug ("ActUserManager: unrefing manager owned by GetSeatId request"); + g_object_unref (manager); + } + + #ifdef WITH_SYSTEMD + static void + _get_systemd_seat_id (ActUserManager *manager) + { + int res; + char *seat_id; + + res = sd_session_get_seat (NULL, &seat_id); + + if (res == -ENOENT) { + seat_id = NULL; + } else if (res < 0) { + g_warning ("Could not get current seat: %s", + strerror (-res)); + unload_seat (manager); + return; + } + + manager->priv->seat.id = g_strdup (seat_id); + free (seat_id); + + manager->priv->seat.state++; +- +- queue_load_seat_incrementally (manager); + } + #endif + + static void + get_seat_id_for_current_session (ActUserManager *manager) + { + #ifdef WITH_SYSTEMD + if (LOGIND_RUNNING()) { + _get_systemd_seat_id (manager); + return; + } + #endif + console_kit_session_call_get_seat_id (manager->priv->seat.session_proxy, + NULL, + on_get_seat_id_finished, + g_object_ref (manager)); + } + + static gint + match_name_cmpfunc (gconstpointer a, + gconstpointer b) + { + return g_strcmp0 ((char *) a, + (char *) b); + } + + static gboolean + username_in_exclude_list (ActUserManager *manager, + const char *username) + { +@@ -1520,106 +1517,64 @@ load_user_paths (ActUserManager *manager, + /* We now have a batch of unloaded users that we know about. Once that initial + * batch is loaded up, we can mark the manager as loaded. + * + * (see on_new_user_loaded) + */ + if (g_strv_length ((char **) user_paths) > 0) { + int i; + + g_debug ("ActUserManager: ListCachedUsers finished, will set loaded property after list is fully loaded"); + for (i = 0; user_paths[i] != NULL; i++) { + ActUser *user; + + user = add_new_user_for_object_path (user_paths[i], manager); + if (!manager->priv->is_loaded) { + manager->priv->new_users_inhibiting_load = g_slist_prepend (manager->priv->new_users_inhibiting_load, user); + } + } + } else { + g_debug ("ActUserManager: ListCachedUsers finished with empty list, maybe setting loaded property now"); + maybe_set_is_loaded (manager); + } + } + + static void + load_included_usernames (ActUserManager *manager) + { + GSList *l; + + /* Add users who are specifically included */ + for (l = manager->priv->include_usernames; l != NULL; l = l->next) { +- ActUser *user; +- + g_debug ("ActUserManager: Adding included user %s", (char *)l->data); +- /* +- * The call to act_user_manager_get_user will add the user if it is +- * valid and not already in the hash. +- */ +- user = act_user_manager_get_user (manager, l->data); +- if (user == NULL) { +- g_debug ("ActUserManager: unable to lookup user '%s'", (char *)l->data); +- } +- } +-} +- +-static void +-on_list_cached_users_finished (GObject *object, +- GAsyncResult *result, +- gpointer data) +-{ +- AccountsAccounts *proxy = ACCOUNTS_ACCOUNTS (object); +- ActUserManager *manager = data; +- gchar **user_paths; +- GError *error = NULL; +- +- manager->priv->listing_cached_users = FALSE; +- manager->priv->list_cached_users_done = TRUE; +- +- if (!accounts_accounts_call_list_cached_users_finish (proxy, &user_paths, result, &error)) { +- g_debug ("ActUserManager: ListCachedUsers failed: %s", error->message); +- g_error_free (error); +- +- g_object_unref (manager->priv->accounts_proxy); +- manager->priv->accounts_proxy = NULL; + +- g_debug ("ActUserManager: unrefing manager owned by failed ListCachedUsers call"); +- g_object_unref (manager); +- return; ++ load_user (manager, l->data); + } +- +- load_user_paths (manager, (const char * const *) user_paths); +- g_strfreev (user_paths); +- +- load_included_usernames (manager); +- +- g_debug ("ActUserManager: unrefing manager owned by finished ListCachedUsers call"); +- g_object_unref (manager); + } + + static void + on_get_x11_display_finished (GObject *object, + GAsyncResult *result, + gpointer data) + { + ConsoleKitSession *proxy = CONSOLE_KIT_SESSION (object); + ActUserManagerNewSession *new_session = data; + GError *error = NULL; + char *x11_display; + + new_session->pending_calls--; + + if (new_session->cancellable == NULL || g_cancellable_is_cancelled (new_session->cancellable)) { + unload_new_session (new_session); + return; + } + + if (!console_kit_session_call_get_x11_display_finish (proxy, &x11_display, result, &error)) { + if (error != NULL) { + g_debug ("Failed to get the x11 display of session '%s': %s", + new_session->id, error->message); + g_error_free (error); + } else { + g_debug ("Failed to get the x11 display of session '%s'", + new_session->id); + } + unload_new_session (new_session); + return; +@@ -2361,60 +2316,99 @@ fetch_user_with_id_from_accounts_service (ActUserManager *manager, + * from @manager. Trying to use this object before its + * #ActUser:is-loaded property is %TRUE will result in undefined + * behavior. + * + * Returns: (transfer none): #ActUser object + **/ + ActUser * + act_user_manager_get_user (ActUserManager *manager, + const char *username) + { + ActUser *user; + + g_return_val_if_fail (ACT_IS_USER_MANAGER (manager), NULL); + g_return_val_if_fail (username != NULL && username[0] != '\0', NULL); + + user = lookup_user_by_name (manager, username); + + /* if we don't have it loaded try to load it now */ + if (user == NULL) { + g_debug ("ActUserManager: trying to track new user with username %s", username); + user = create_new_user (manager); + + if (manager->priv->accounts_proxy != NULL) { + fetch_user_with_username_from_accounts_service (manager, user, username); + } + } + + return user; + } + ++static void ++load_user (ActUserManager *manager, ++ const char *username) ++{ ++ ActUser *user; ++ GError *error = NULL; ++ char *object_path = NULL; ++ gboolean user_found; ++ ++ g_return_if_fail (ACT_IS_USER_MANAGER (manager)); ++ g_return_if_fail (username != NULL && username[0] != '\0'); ++ ++ user = lookup_user_by_name (manager, username); ++ ++ if (user == NULL) { ++ g_debug ("ActUserManager: trying to track new user with username %s", username); ++ user = create_new_user (manager); ++ } ++ ++ user_found = accounts_accounts_call_find_user_by_name_sync (manager->priv->accounts_proxy, ++ username, ++ &object_path, ++ NULL, ++ &error); ++ ++ if (!user_found) { ++ if (error != NULL) { ++ g_debug ("ActUserManager: Failed to find user '%s': %s", ++ username, error->message); ++ g_clear_error (&error); ++ } else { ++ g_debug ("ActUserManager: Failed to find user '%s'", ++ username); ++ } ++ } ++ ++ _act_user_update_from_object_path (user, object_path); ++} ++ + /** + * act_user_manager_get_user_by_id: + * @manager: the manager to query. + * @id: the uid of the user to get. + * + * Retrieves a pointer to the #ActUser object for the user with the + * given uid from @manager. Trying to use this object before its + * #ActUser:is-loaded property is %TRUE will result in undefined + * behavior. + * + * Returns: (transfer none): #ActUser object + */ + ActUser * + act_user_manager_get_user_by_id (ActUserManager *manager, + uid_t id) + { + ActUser *user; + gchar *object_path; + + g_return_val_if_fail (ACT_IS_USER_MANAGER (manager), NULL); + + object_path = g_strdup_printf ("/org/freedesktop/Accounts/User%lu", (gulong) id); + user = g_hash_table_lookup (manager->priv->users_by_object_path, object_path); + g_free (object_path); + + if (user != NULL) { + return g_object_ref (user); + } else { + g_debug ("ActUserManager: trying to track new user with uid %lu", (gulong) id); + user = create_new_user (manager); +@@ -2425,93 +2419,95 @@ act_user_manager_get_user_by_id (ActUserManager *manager, + } + + return user; + } + + static void + listify_hash_values_hfunc (gpointer key, + gpointer value, + gpointer user_data) + { + GSList **list = user_data; + + *list = g_slist_prepend (*list, value); + } + + /** + * act_user_manager_list_users: + * @manager: a #ActUserManager + * + * Get a list of system user accounts + * + * Returns: (element-type ActUser) (transfer container): List of #ActUser objects + */ + GSList * + act_user_manager_list_users (ActUserManager *manager) + { + GSList *retval; + + g_return_val_if_fail (ACT_IS_USER_MANAGER (manager), NULL); + ++ if (!manager->priv->list_cached_users_done) { ++ load_users (manager); ++ ++ if (manager->priv->seat.state == ACT_USER_MANAGER_SEAT_STATE_GET_SEAT_PROXY) ++ queue_load_seat_incrementally (manager); ++ } ++ + retval = NULL; + g_hash_table_foreach (manager->priv->normal_users_by_name, listify_hash_values_hfunc, &retval); + + return g_slist_sort (retval, (GCompareFunc) act_user_collate); + } + + static void + maybe_set_is_loaded (ActUserManager *manager) + { + if (manager->priv->is_loaded) { + g_debug ("ActUserManager: already loaded, so not setting loaded property"); + return; + } + + if (manager->priv->getting_sessions) { + g_debug ("ActUserManager: GetSessions call pending, so not setting loaded property"); + return; + } + +- if (manager->priv->listing_cached_users) { +- g_debug ("ActUserManager: Listing cached users, so not setting loaded property"); +- return; +- } +- + if (manager->priv->new_users_inhibiting_load != NULL) { + g_debug ("ActUserManager: Loading new users, so not setting loaded property"); + return; + } + +- /* Don't set is_loaded yet unless the seat is already loaded ++ /* Don't set is_loaded yet unless the seat is already loaded enough + * or failed to load. + */ +- if (manager->priv->seat.state == ACT_USER_MANAGER_SEAT_STATE_LOADED) { ++ if (manager->priv->seat.state >= ACT_USER_MANAGER_SEAT_STATE_GET_ID) { + g_debug ("ActUserManager: Seat loaded, so now setting loaded property"); + } else if (manager->priv->seat.state == ACT_USER_MANAGER_SEAT_STATE_UNLOADED) { + g_debug ("ActUserManager: Seat wouldn't load, so giving up on it and setting loaded property"); + } else { + g_debug ("ActUserManager: Seat still actively loading, so not setting loaded property"); + return; + } + + set_is_loaded (manager, TRUE); + } + + + static GSList * + slist_deep_copy (const GSList *list) + { + GSList *retval; + GSList *l; + + if (list == NULL) + return NULL; + + retval = g_slist_copy ((GSList *) list); + for (l = retval; l != NULL; l = l->next) { + l->data = g_strdup (l->data); + } + + return retval; + } + + static void +@@ -2555,125 +2551,134 @@ static void + load_console_kit_sessions (ActUserManager *manager) + { + if (manager->priv->seat.seat_proxy == NULL) { + g_debug ("ActUserManager: no seat proxy; can't load sessions"); + return; + } + + manager->priv->getting_sessions = TRUE; + console_kit_seat_call_get_sessions (manager->priv->seat.seat_proxy, + NULL, + on_get_sessions_finished, + g_object_ref (manager)); + } + + static void + load_sessions (ActUserManager *manager) + { + #ifdef WITH_SYSTEMD + if (LOGIND_RUNNING()) { + reload_systemd_sessions (manager); + maybe_set_is_loaded (manager); + return; + } + #endif + load_console_kit_sessions (manager); + } + + static void + load_users (ActUserManager *manager) + { +- g_assert (manager->priv->accounts_proxy != NULL); +- g_debug ("ActUserManager: calling 'ListCachedUsers'"); ++ GError *error = NULL; ++ char **user_paths = NULL; ++ gboolean could_list = FALSE; + + if (!ensure_accounts_proxy (manager)) { + return; + } + +- accounts_accounts_call_list_cached_users (manager->priv->accounts_proxy, +- NULL, +- on_list_cached_users_finished, +- g_object_ref (manager)); +- manager->priv->listing_cached_users = TRUE; ++ g_debug ("ActUserManager: calling 'ListCachedUsers'"); ++ ++ could_list = accounts_accounts_call_list_cached_users_sync (manager->priv->accounts_proxy, ++ &user_paths, ++ NULL, &error); ++ ++ if (!could_list) { ++ g_debug ("ActUserManager: ListCachedUsers failed: %s", error->message); ++ g_clear_error (&error); ++ return; ++ } ++ ++ load_user_paths (manager, (const char * const *) user_paths); ++ g_strfreev (user_paths); ++ ++ load_included_usernames (manager); ++ ++ manager->priv->list_cached_users_done = TRUE; + } + + static gboolean + load_seat_incrementally (ActUserManager *manager) + { + manager->priv->seat.load_idle_id = 0; + + switch (manager->priv->seat.state) { + case ACT_USER_MANAGER_SEAT_STATE_GET_SESSION_ID: + get_current_session_id (manager); + break; + case ACT_USER_MANAGER_SEAT_STATE_GET_SESSION_PROXY: + get_session_proxy (manager); + break; + case ACT_USER_MANAGER_SEAT_STATE_GET_ID: + get_seat_id_for_current_session (manager); + break; + case ACT_USER_MANAGER_SEAT_STATE_GET_SEAT_PROXY: + get_seat_proxy (manager); + break; + case ACT_USER_MANAGER_SEAT_STATE_LOADED: + g_debug ("ActUserManager: Seat loading sequence complete"); + break; + default: + g_assert_not_reached (); + } + + if (manager->priv->seat.state == ACT_USER_MANAGER_SEAT_STATE_LOADED) { + load_sessions (manager); + } + + maybe_set_is_loaded (manager); + + return FALSE; + } + + static gboolean + load_idle (ActUserManager *manager) + { +- /* The order below is important: load_seat_incrementally might +- set "is-loaded" immediately and we thus need to call +- load_users before it. +- */ +- load_users (manager); + manager->priv->seat.state = ACT_USER_MANAGER_SEAT_STATE_UNLOADED + 1; + load_seat_incrementally (manager); + manager->priv->load_id = 0; + + return FALSE; + } + + static void +-queue_load_seat_and_users (ActUserManager *manager) ++queue_load_seat (ActUserManager *manager) + { + if (manager->priv->load_id > 0) { + return; + } + + manager->priv->load_id = g_idle_add ((GSourceFunc)load_idle, manager); + } + + static void + act_user_manager_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) + { + ActUserManager *manager; + + manager = ACT_USER_MANAGER (object); + + switch (prop_id) { + case PROP_IS_LOADED: + g_value_set_boolean (value, manager->priv->is_loaded); + break; + case PROP_HAS_MULTIPLE_USERS: + g_value_set_boolean (value, manager->priv->has_multiple_users); + break; + case PROP_INCLUDE_USERNAMES_LIST: + g_value_set_pointer (value, manager->priv->include_usernames); + break; + case PROP_EXCLUDE_USERNAMES_LIST: + g_value_set_pointer (value, manager->priv->exclude_usernames); +@@ -2820,61 +2825,61 @@ act_user_manager_class_init (ActUserManagerClass *klass) + * @user: the #ActUser that changed + * + * One of the users has changed + */ + signals [USER_CHANGED] = + g_signal_new ("user-changed", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (ActUserManagerClass, user_changed), + NULL, NULL, + g_cclosure_marshal_VOID__OBJECT, + G_TYPE_NONE, 1, ACT_TYPE_USER); + + g_type_class_add_private (klass, sizeof (ActUserManagerPrivate)); + } + + /** + * act_user_manager_queue_load: + * @manager: a #ActUserManager + * + * Queue loading users into user manager. This must be called, and the + * #ActUserManager:is-loaded property must be %TRUE before calling + * act_user_manager_list_users() + */ + static void + act_user_manager_queue_load (ActUserManager *manager) + { + g_return_if_fail (ACT_IS_USER_MANAGER (manager)); + + if (! manager->priv->is_loaded) { +- queue_load_seat_and_users (manager); ++ queue_load_seat (manager); + } + } + + static gboolean + ensure_accounts_proxy (ActUserManager *manager) + { + GError *error = NULL; + + if (manager->priv->accounts_proxy != NULL) { + return TRUE; + } + + manager->priv->accounts_proxy = accounts_accounts_proxy_new_sync (manager->priv->connection, + G_DBUS_PROXY_FLAGS_NONE, + ACCOUNTS_NAME, + ACCOUNTS_PATH, + NULL, + &error); + if (error != NULL) { + g_debug ("ActUserManager: getting account proxy failed: %s", error->message); + g_clear_error (&error); + return FALSE; + } + + g_dbus_proxy_set_default_timeout (G_DBUS_PROXY (manager->priv->accounts_proxy), G_MAXINT); + + g_object_bind_property (G_OBJECT (manager->priv->accounts_proxy), + "has-multiple-users", + G_OBJECT (manager), + "has-multiple-users", +-- +2.14.1 + diff --git a/SOURCES/0014-lib-fix-crasher-if-accounts-proxy-is-unavailable.patch b/SOURCES/0014-lib-fix-crasher-if-accounts-proxy-is-unavailable.patch new file mode 100644 index 0000000..68b8d1a --- /dev/null +++ b/SOURCES/0014-lib-fix-crasher-if-accounts-proxy-is-unavailable.patch @@ -0,0 +1,86 @@ +From 4e22d1a68e8ddcdea761fc51ea8c2ced0055fe60 Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Fri, 3 Nov 2017 09:47:04 -0400 +Subject: [PATCH 14/14] lib: fix crasher if accounts proxy is unavailable + +Right now if the accounts proxy is unavailable act_user_get_real_name +crashes instead of returning NULL as advertised. + +This commit makes it return NULL. + +https://bugs.freedesktop.org/show_bug.cgi?id=103560 +--- + src/libaccountsservice/act-user.c | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/src/libaccountsservice/act-user.c b/src/libaccountsservice/act-user.c +index 94884a1..a24f25a 100644 +--- a/src/libaccountsservice/act-user.c ++++ b/src/libaccountsservice/act-user.c +@@ -520,60 +520,63 @@ set_is_loaded (ActUser *user, + * freed, or %NULL. + **/ + + uid_t + act_user_get_uid (ActUser *user) + { + g_return_val_if_fail (ACT_IS_USER (user), -1); + + if (user->accounts_proxy == NULL) + return -1; + + return accounts_user_get_uid (user->accounts_proxy); + } + + /** + * act_user_get_real_name: + * @user: the user object to examine. + * + * Retrieves the display name of @user. + * + * Returns: (transfer none): a pointer to an array of characters which must not be modified or + * freed, or %NULL. + **/ + const char * + act_user_get_real_name (ActUser *user) + { + const char *real_name = NULL; + + g_return_val_if_fail (ACT_IS_USER (user), NULL); + ++ if (user->accounts_proxy == NULL) ++ return NULL; ++ + real_name = accounts_user_get_real_name (user->accounts_proxy); + + if (real_name == NULL || real_name[0] == '\0') { + real_name = accounts_user_get_user_name (user->accounts_proxy); + } + + return real_name; + } + + /** + * act_user_get_account_type: + * @user: the user object to examine. + * + * Retrieves the account type of @user. + * + * Returns: a #ActUserAccountType + **/ + ActUserAccountType + act_user_get_account_type (ActUser *user) + { + g_return_val_if_fail (ACT_IS_USER (user), ACT_USER_ACCOUNT_TYPE_STANDARD); + + if (user->accounts_proxy == NULL) + return ACT_USER_ACCOUNT_TYPE_STANDARD; + + return accounts_user_get_account_type (user->accounts_proxy); + } + + /** + * act_user_get_password_mode: +-- +2.14.2 + diff --git a/SPECS/accountsservice.spec b/SPECS/accountsservice.spec new file mode 100644 index 0000000..a27e700 --- /dev/null +++ b/SPECS/accountsservice.spec @@ -0,0 +1,325 @@ +%global _hardened_build 1 + +Name: accountsservice +Version: 0.6.45 +Release: 3%{?dist}.1 +Summary: D-Bus interfaces for querying and manipulating user account information + +Group: System Environment/Daemons +License: GPLv3+ +URL: http://www.fedoraproject.org/wiki/Features/UserAccountDialog +#VCS: git:git://git.freedesktop.org/accountsservice +Source0: http://www.freedesktop.org/software/accountsservice/accountsservice-%{version}.tar.xz + +BuildRequires: glib2-devel +BuildRequires: polkit-devel +BuildRequires: intltool +BuildRequires: systemd-units +BuildRequires: systemd-devel +BuildRequires: gobject-introspection-devel +BuildRequires: gtk-doc +BuildRequires: git + +Requires: polkit +Requires: shadow-utils + +Requires(post): systemd-units +Requires(preun): systemd-units +Requires(postun): systemd-units + +Patch0: 0001-daemon-don-t-treat-explicitly-requested-users-as-cac.patch +Patch1: 0001-lib-move-g_object_ref-call-for-clarity.patch +Patch2: 0002-lib-leak-fix.patch +Patch3: 0003-lib-another-leak-fix.patch +Patch4: 0004-daemon-don-t-send-spurious-change-signals-when-wtmp-.patch +Patch5: 0005-lib-consolidate-change-notification.patch +Patch6: 0006-daemon-add-new-HasMultipleUsers-and-HasNoUsers-prope.patch +Patch7: 0007-lib-use-new-has-multiple-users-property-from-account.patch +Patch8: 0008-lib-factor-user-loading-functions-into-helpers.patch +Patch9: 0009-lib-move-accounts-proxy-creation-to-helper.patch +Patch10: 0010-lib-retry-connecting-to-accountsservice-when-loading.patch +Patch11: 0011-lib-simplify-code-dramatically.patch +Patch12: 0012-lib-don-t-track-user-added-user-removed-until-we-get.patch +Patch13: 0013-lib-only-track-users-after-act_user_manager_list_use.patch +Patch14: 0014-lib-fix-crasher-if-accounts-proxy-is-unavailable.patch + +%package libs +Summary: Client-side library to talk to accountsservice +Group: Development/Libraries +Requires: %{name} = %{version}-%{release} + +%description libs +The accountsservice-libs package contains a library that can +be used by applications that want to interact with the accountsservice +daemon. + + +%package devel +Summary: Development files for accountsservice-libs +Group: Development/Libraries +Requires: %{name}-libs = %{version}-%{release} + +%description devel +The accountsservice-devel package contains headers and other +files needed to build applications that use accountsservice-libs. + + +%description +The accountsservice project provides a set of D-Bus interfaces for +querying and manipulating user account information and an implementation +of these interfaces, based on the useradd, usermod and userdel commands. + + +%prep +%autosetup -S git + +%build +%configure --enable-user-heuristics +make %{?_smp_mflags} + + +%install +make install DESTDIR=$RPM_BUILD_ROOT +rm $RPM_BUILD_ROOT%{_libdir}/*.la +rm $RPM_BUILD_ROOT%{_libdir}/*.a +%find_lang accounts-service + + +%post libs -p /sbin/ldconfig + +%postun libs -p /sbin/ldconfig + +%post +%systemd_post accounts-daemon.service + +%preun +%systemd_preun accounts-daemon.service + +%postun +%systemd_postun accounts-daemon.service + +%files -f accounts-service.lang +%doc COPYING README AUTHORS +%{_sysconfdir}/dbus-1/system.d/org.freedesktop.Accounts.conf +%{_libexecdir}/accounts-daemon +%{_datadir}/dbus-1/interfaces/org.freedesktop.Accounts.xml +%{_datadir}/dbus-1/interfaces/org.freedesktop.Accounts.User.xml +%{_datadir}/dbus-1/system-services/org.freedesktop.Accounts.service +%{_datadir}/polkit-1/actions/org.freedesktop.accounts.policy +%dir %{_localstatedir}/lib/AccountsService/ +%dir %{_localstatedir}/lib/AccountsService/users +%dir %{_localstatedir}/lib/AccountsService/icons +%{_unitdir}/accounts-daemon.service + +%files libs +%{_libdir}/libaccountsservice.so.* +%{_libdir}/girepository-1.0/AccountsService-1.0.typelib + +%files devel +%{_includedir}/accountsservice-1.0 +%{_libdir}/libaccountsservice.so +%{_libdir}/pkgconfig/accountsservice.pc +%{_datadir}/gir-1.0/AccountsService-1.0.gir +%dir %{_datadir}/gtk-doc/html/libaccountsservice +%{_datadir}/gtk-doc/html/libaccountsservice/* + +%changelog +* Fri Nov 03 2017 Ray Strode - 0.6.45-3.1 +- Fix crasher introduced in -3 + Related: #1474579, 1509261 + +* Wed Sep 27 2017 Ray Strode - 0.6.45-3 +- Various fixes to increase scalability + Related: #1474579 + +* Mon May 15 2017 Ray Strode - 0.6.45-2 +- Don't tread explicitly requested users as cached + Fixes UncacheUser operation. + Resolves: 1446620 + Related: #1432602 + +* Mon Mar 27 2017 Ray Strode - 0.6.45-1 +- Update to 0.6.45 so password expiration policy is wrapped by libaccountsservice + Related: #1424623 #1334464 + +* Mon Mar 27 2017 Ray Strode - 0.6.42-2 +- Update to 0.6.44 + Related: #1424623 +- export password expiration policy from shadow + Resolves: #1334464 + +* Thu Oct 20 2016 Kalev Lember - 0.6.42-1 +- Update to 0.6.42 +- Resolves: #1424623 + +* Tue Sep 06 2016 Ray Strode - 0.6.35-12 +- hide users with /sbin/nologin shell + Resolves: #1341276 + +* Fri Jul 01 2016 Ray Strode - 0.6.35-11 +- address minor bug in previous patch + Related: #1220904 + +- fix memory leak in log handler + Resolves: #1292543 + +* Thu Jun 30 2016 Ray Strode - 0.6.35-10 +- scale to a large number of remote users better + Resolves: #1220904 + +* Mon Jun 08 2015 Ray Strode 0.6.35-9 +- Start after nsswitch and ypbind + Resolves: #1217439 + +* Tue Jun 2 2015 Ondrej Holy 0.6.35-8 +- Add asynchronous api for user uncaching + Resolves: #1147999 + +* Wed Jan 29 2014 Ray Strode 0.6.35-7 +- Fix memory leak + Resolves: #1003033 + +* Tue Jan 28 2014 Daniel Mach - 0.6.35-6 +- Mass rebuild 2014-01-24 + +* Fri Jan 10 2014 Matthias Clasen 0.6.35-5 +- Consistently call userdel -f +Resolves: #1051629 + +* Fri Dec 27 2013 Daniel Mach - 0.6.35-4 +- Mass rebuild 2013-12-27 + +* Tue Nov 19 2013 Ray Strode 0.6.35-3 +- Fix up user classification logic to work better with + remote users. + Resolves: #1029195 + +* Fri Nov 1 2013 Matthias Clasen 0.6.35-2 +- Refuse to delete the root user +- Resolves: #1002973 + +* Tue Oct 22 2013 Ray Strode 0.6.35-1 +- Update to 0.6.35 + Resolves: #1019488 + +* Tue Jun 11 2013 Ray Strode 0.6.34-1 +- Update to 0.6.34 + +* Tue Jun 11 2013 Matthias Clasen - 0.6.33-1 +- Update to 0.6.33 + +* Tue May 14 2013 Matthias Clasen - 0.6.32-1 +- Update to 0.6.32 + +* Thu Apr 18 2013 Matthias Clasen - 0.6.31-2 +- Hardened build + +* Tue Apr 16 2013 Matthias Clasen - 0.6.31-1 +- Update to 0.6.31 + +* Wed Feb 13 2013 Fedora Release Engineering - 0.6.30-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_19_Mass_Rebuild + +* Wed Jan 16 2013 Richard Hughes - 0.6.30-1 +- Update to 0.6.30 + +* Fri Nov 16 2012 Matthias Clasen - 0.6.26-1 +- Update to 0.6.26 + +* Tue Oct 2 2012 Matthias Clasen - 0.6.25-2 +- Update to 0.6.25 +- Use systemd scriptlets (#856649) + +* Wed Jul 18 2012 Fedora Release Engineering - 0.6.22-3 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_18_Mass_Rebuild + +* Sat Jul 14 2012 Ville Skyttä - 0.6.22-2 +- Add ldconfig scriptlets to -libs. + +* Thu Jun 28 2012 Ray Strode 0.6.22-1 +- Update to 0.6.22. +- Fixes CVE-2012-2737 - local file disclosure + Related: #832532 + +* Thu May 30 2012 Matthias Clasen 0.6.21-1 +- Update to 0.6.21 + +* Fri May 04 2012 Ray Strode 0.6.20-1 +- Update to 0.6.20. Should fix user list. + Related: #814690 + +* Thu May 03 2012 Ray Strode 0.6.19-1 +- Update to 0.6.19 + Allows user deletion of logged in users + Related: #814690 + +* Wed Apr 11 2012 Matthias Clasen - 0.6.18-1 +- Update to 0.6.18 + +* Tue Mar 27 2012 Ray Strode 0.6.17-1 +- Update to latest release + +* Sun Mar 4 2012 Peter Robinson - 0.6.15-4 +- Fix unitdir with usrmove + +* Thu Jan 12 2012 Fedora Release Engineering - 0.6.15-3 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_17_Mass_Rebuild + +* Tue Nov 29 2011 Matthias Clasen 0.6.15-2 +- Make resetting user icons work +- Update to 0.6.15 +- Fixes session chooser at login screen when logged into vt + +* Wed Sep 21 2011 Ray Strode 0.6.14-2 +- Fix wtmp loading so users coming from the network are + remembered in the user list in subsequent boots + +* Wed Sep 21 2011 Ray Strode 0.6.14-1 +- Update to 0.6.14 + +* Sun Sep 4 2011 Matthias Clasen - 0.6.13-3 +- Fix fast user switching + +* Mon Aug 15 2011 Kalev Lember - 0.6.13-2 +- Rebuilt for rpm bug #728707 + +* Tue Jul 19 2011 Matthias Clasen - 0.6.13-1 +- Update to 0.6.13 +- Drop ConsoleKit dependency + +* Mon Jun 06 2011 Ray Strode 0.6.12-1 +- Update to latest release + +* Wed May 18 2011 Matthias Clasen 0.6.11-1 +- Update to 0.6.11 + +* Mon Feb 07 2011 Fedora Release Engineering - 0.6.3-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_15_Mass_Rebuild + +* Wed Feb 02 2011 Ray Strode 0.6.3-1 +- Update to 0.6.3 + +* Thu Jan 27 2011 Matthias Clasen 0.6.2-1 +- Update to 0.6.2 + +* Wed Jul 21 2010 Matthias Clasen 0.6.1-1 +- Update to 0.6.1 +- Install systemd unit file + +* Mon Apr 5 2010 Matthias Clasen 0.6-2 +- Always emit changed signal on icon change + +* Tue Mar 30 2010 Matthias Clasen 0.6-1 +- Update to 0.6 + +* Mon Mar 22 2010 Matthias Clasen 0.5-1 +- Update to 0.5 + +* Mon Feb 22 2010 Bastien Nocera 0.4-3 +- Fix directory ownership + +* Mon Feb 22 2010 Bastien Nocera 0.4-2 +- Add missing directories to the filelist + +* Fri Jan 29 2010 Matthias Clasen 0.4-1 +- Initial packaging, based on work by Richard Hughes