diff options
author | William Jon McCann <jmccann@redhat.com> | 2010-06-09 16:37:20 -0400 |
---|---|---|
committer | William Jon McCann <jmccann@redhat.com> | 2010-06-10 19:25:55 -0400 |
commit | 57f65564a27bc7cc5dd2912134c115c759b5f8a4 (patch) | |
tree | f17b1cbf9e66b508a9f341a015c9811ba4bea819 | |
parent | def76452c929222791aa6ff82b329c2307b52809 (diff) | |
download | gdm-57f65564a27bc7cc5dd2912134c115c759b5f8a4.tar.gz |
Load passwd file in thread
https://bugzilla.gnome.org/show_bug.cgi?id=621131
-rw-r--r-- | gui/simple-greeter/gdm-user-manager.c | 321 |
1 files changed, 224 insertions, 97 deletions
diff --git a/gui/simple-greeter/gdm-user-manager.c b/gui/simple-greeter/gdm-user-manager.c index 8e225c16..ee2da657 100644 --- a/gui/simple-greeter/gdm-user-manager.c +++ b/gui/simple-greeter/gdm-user-manager.c @@ -92,6 +92,8 @@ struct GdmUserManagerPrivate GSList *include; gboolean include_all; + GCancellable *cancellable; + guint load_id; guint reload_passwd_id; guint ck_history_id; @@ -120,6 +122,9 @@ static void gdm_user_manager_class_init (GdmUserManagerClass *klass); static void gdm_user_manager_init (GdmUserManager *user_manager); static void gdm_user_manager_finalize (GObject *object); +static void load_users (GdmUserManager *manager); +static void monitor_local_users (GdmUserManager *manager); + static gpointer user_manager_object = NULL; G_DEFINE_TYPE (GdmUserManager, gdm_user_manager, G_TYPE_OBJECT) @@ -1172,8 +1177,6 @@ ck_history_watch (GIOChannel *source, } if (done) { - set_is_loaded (manager, TRUE); - manager->priv->ck_history_id = 0; return FALSE; } @@ -1264,45 +1267,24 @@ load_ck_history (GdmUserManager *manager) } static void -add_included_user (char *username, GdmUserManager *manager) +reload_passwd_file (GHashTable *valid_shells, + GSList *exclude_users, + GSList *include_users, + gboolean include_all, + GHashTable *current_users_by_name, + GSList **added_users, + GSList **removed_users) { - GdmUser *user; - - g_debug ("Adding included user %s", username); - /* - * The call to gdm_user_manager_get_user will add the user if it is - * valid and not already in the hash. - */ - user = gdm_user_manager_get_user (manager, username); - if (user == NULL) { - g_debug ("GdmUserManager: unable to lookup user '%s'", username); - g_free (username); - return; - } -} + FILE *fp; + GHashTableIter iter; + GHashTable *new_users_by_name; + GdmUser *user; + char *name; -static void -add_included_users (GdmUserManager *manager) -{ - /* Add users who are specifically included */ - if (manager->priv->include != NULL) { - g_slist_foreach (manager->priv->include, - (GFunc)add_included_user, - (gpointer)manager); - } -} - -static void -reload_passwd (GdmUserManager *manager) -{ - struct passwd *pwent; - GSList *old_users; - GSList *new_users; - GSList *list; - FILE *fp; - - old_users = NULL; - new_users = NULL; + new_users_by_name = g_hash_table_new_full (g_str_hash, + g_str_equal, + NULL, + g_object_unref); errno = 0; fp = fopen (PATH_PASSWD, "r"); @@ -1311,28 +1293,57 @@ reload_passwd (GdmUserManager *manager) goto out; } - g_hash_table_foreach (manager->priv->users_by_name, listify_hash_values_hfunc, &old_users); - g_slist_foreach (old_users, (GFunc) g_object_ref, NULL); - /* Make sure we keep users who are logged in no matter what. */ - for (list = old_users; list; list = list->next) { - if (gdm_user_get_num_sessions (list->data) > 0) { - g_object_freeze_notify (G_OBJECT (list->data)); - new_users = g_slist_prepend (new_users, g_object_ref (list->data)); + g_hash_table_iter_init (&iter, current_users_by_name); + while (g_hash_table_iter_next (&iter, (gpointer *) &name, (gpointer *) &user)) { + struct passwd *pwent; + pwent = getpwnam (name); + if (pwent == NULL) { + continue; + } + + g_object_freeze_notify (G_OBJECT (user)); + _gdm_user_update_from_pwent (user, pwent); + g_hash_table_insert (new_users_by_name, (char *)gdm_user_get_user_name (user), g_object_ref (user)); + } + + if (include_users != NULL) { + GSList *l; + for (l = include_users; l != NULL; l = l->next) { + struct passwd *pwent; + pwent = getpwnam (l->data); + if (pwent == NULL) { + continue; + } + + user = g_hash_table_lookup (new_users_by_name, pwent->pw_name); + if (user != NULL) { + /* already there */ + continue; + } + + user = g_hash_table_lookup (current_users_by_name, pwent->pw_name); + if (user == NULL) { + user = g_object_new (GDM_TYPE_USER, NULL); + } else { + g_object_ref (user); + } + g_object_freeze_notify (G_OBJECT (user)); + _gdm_user_update_from_pwent (user, pwent); + g_hash_table_insert (new_users_by_name, (char *)gdm_user_get_user_name (user), user); } } - if (manager->priv->include_all != TRUE) { + if (include_all != TRUE) { g_debug ("GdmUserManager: include_all is FALSE"); } else { + struct passwd *pwent; + g_debug ("GdmUserManager: include_all is TRUE"); for (pwent = fgetpwent (fp); pwent != NULL; pwent = fgetpwent (fp)) { - GdmUser *user; - - user = NULL; /* Skip users below MinimalUID... */ if (pwent->pw_uid < DEFAULT_MINIMAL_UID) { @@ -1340,30 +1351,39 @@ reload_passwd (GdmUserManager *manager) } /* ...And users w/ invalid shells... */ - if (pwent->pw_shell == NULL || - !g_hash_table_lookup (manager->priv->shells, - pwent->pw_shell)) { + if (pwent->pw_shell == NULL + || !g_hash_table_lookup (valid_shells, pwent->pw_shell)) { g_debug ("GdmUserManager: skipping user with bad shell: %s", pwent->pw_name); continue; } - /* ...And explicitly excluded users */ - if (user_in_exclude_list (manager, pwent->pw_name)) { - g_debug ("GdmUserManager: explicitly skipping user: %s", pwent->pw_name); + /* always exclude the "gdm" user. */ + if (strcmp (pwent->pw_name, GDM_USERNAME) == 0) { continue; } - user = g_hash_table_lookup (manager->priv->users_by_name, - pwent->pw_name); + /* ...And explicitly excluded users */ + if (exclude_users != NULL) { + GSList *found; + + found = g_slist_find_custom (exclude_users, + pwent->pw_name, + match_name_cmpfunc); + if (found != NULL) { + g_debug ("GdmUserManager: explicitly skipping user: %s", pwent->pw_name); + continue; + } + } - /* Update users already in the *new* list */ - if (g_slist_find (new_users, user)) { - _gdm_user_update_from_pwent (user, pwent); + user = g_hash_table_lookup (new_users_by_name, pwent->pw_name); + if (user != NULL) { + /* already there */ continue; } + user = g_hash_table_lookup (current_users_by_name, pwent->pw_name); if (user == NULL) { - user = create_user (manager); + user = g_object_new (GDM_TYPE_USER, NULL); } else { g_object_ref (user); } @@ -1371,38 +1391,140 @@ reload_passwd (GdmUserManager *manager) /* Freeze & update users not already in the new list */ g_object_freeze_notify (G_OBJECT (user)); _gdm_user_update_from_pwent (user, pwent); - - new_users = g_slist_prepend (new_users, user); + g_hash_table_insert (new_users_by_name, (char *)gdm_user_get_user_name (user), user); } } /* Go through and handle added users */ - for (list = new_users; list; list = list->next) { - if (! g_slist_find (old_users, list->data)) { - add_user (manager, list->data); + g_hash_table_iter_init (&iter, new_users_by_name); + while (g_hash_table_iter_next (&iter, (gpointer *) &name, (gpointer *) &user)) { + GdmUser *user2; + user2 = g_hash_table_lookup (current_users_by_name, name); + if (user2 == NULL) { + *added_users = g_slist_prepend (*added_users, g_object_ref (user)); } } /* Go through and handle removed users */ - for (list = old_users; list; list = list->next) { - if (! g_slist_find (new_users, list->data)) { - remove_user (manager, list->data); + g_hash_table_iter_init (&iter, current_users_by_name); + while (g_hash_table_iter_next (&iter, (gpointer *) &name, (gpointer *) &user)) { + GdmUser *user2; + user2 = g_hash_table_lookup (new_users_by_name, name); + if (user2 == NULL) { + *removed_users = g_slist_prepend (*removed_users, g_object_ref (user)); } } - add_included_users (manager); - out: /* Cleanup */ fclose (fp); - g_slist_foreach (new_users, (GFunc) g_object_thaw_notify, NULL); - g_slist_foreach (new_users, (GFunc) g_object_unref, NULL); - g_slist_free (new_users); + g_hash_table_destroy (new_users_by_name); +} + +typedef struct { + GdmUserManager *manager; + GSList *exclude_users; + GSList *include_users; + gboolean include_all; + GHashTable *shells; + GHashTable *current_users_by_name; + GSList *added_users; + GSList *removed_users; +} PasswdData; + +static void +passwd_data_free (PasswdData *data) +{ + if (data->manager != NULL) { + g_object_unref (data->manager); + } + + g_slist_foreach (data->added_users, (GFunc) g_object_ref, NULL); + g_slist_free (data->added_users); + + g_slist_foreach (data->removed_users, (GFunc) g_object_ref, NULL); + g_slist_free (data->removed_users); + + g_slice_free (PasswdData, data); +} + +static gboolean +reload_passwd_job_done (PasswdData *data) +{ + GSList *l; + + g_debug ("GdmUserManager: done reloading passwd file"); + + /* Go through and handle added users */ + for (l = data->added_users; l != NULL; l = l->next) { + add_user (data->manager, l->data); + } + + /* Go through and handle removed users */ + for (l = data->removed_users; l != NULL; l = l->next) { + remove_user (data->manager, l->data); + } + + if (! data->manager->priv->is_loaded) { + set_is_loaded (data->manager, TRUE); + + if (data->manager->priv->include_all == TRUE) { + monitor_local_users (data->manager); + } + } + + passwd_data_free (data); - g_slist_foreach (old_users, (GFunc) g_object_unref, NULL); - g_slist_free (old_users); + return FALSE; +} + +static gboolean +do_reload_passwd_job (GIOSchedulerJob *job, + GCancellable *cancellable, + PasswdData *data) +{ + g_debug ("GdmUserManager: reloading passwd file worker"); + + reload_passwd_file (data->shells, + data->exclude_users, + data->include_users, + data->include_all, + data->current_users_by_name, + &data->added_users, + &data->removed_users); + + g_io_scheduler_job_send_to_mainloop_async (job, + (GSourceFunc) reload_passwd_job_done, + data, + NULL); + + return FALSE; +} + +static void +schedule_reload_passwd (GdmUserManager *manager) +{ + PasswdData *passwd_data; + + passwd_data = g_slice_new0 (PasswdData); + passwd_data->manager = g_object_ref (manager); + passwd_data->shells = manager->priv->shells; + passwd_data->exclude_users = manager->priv->exclude; + passwd_data->include_users = manager->priv->include; + passwd_data->include_all = manager->priv->include_all; + passwd_data->current_users_by_name = manager->priv->users_by_name; + passwd_data->added_users = NULL; + passwd_data->removed_users = NULL; + + g_debug ("GdmUserManager: scheduling a passwd file update"); + + g_io_scheduler_push_job ((GIOSchedulerJobFunc) do_reload_passwd_job, + passwd_data, + NULL, + G_PRIORITY_DEFAULT, + manager->priv->cancellable); } static void @@ -1448,20 +1570,6 @@ load_sessions (GdmUserManager *manager) g_ptr_array_free (sessions, TRUE); } -static void -load_users (GdmUserManager *manager) -{ - gboolean res; - - load_sessions (manager); - - res = load_ck_history (manager); - reload_passwd (manager); - if (! res) { - set_is_loaded (manager, TRUE); - } -} - static gboolean load_users_idle (GdmUserManager *manager) { @@ -1484,7 +1592,7 @@ queue_load_users (GdmUserManager *manager) static gboolean reload_passwd_idle (GdmUserManager *manager) { - reload_passwd (manager); + schedule_reload_passwd (manager); manager->priv->reload_passwd_id = 0; return FALSE; @@ -1587,11 +1695,6 @@ monitor_local_users (GdmUserManager *manager) g_debug ("Monitoring local users"); /* /etc/shells */ - manager->priv->shells = g_hash_table_new_full (g_str_hash, - g_str_equal, - g_free, - NULL); - reload_shells (manager); file = g_file_new_for_path (_PATH_SHELLS); error = NULL; manager->priv->shells_monitor = g_file_monitor_file (file, @@ -1628,6 +1731,23 @@ monitor_local_users (GdmUserManager *manager) } static void +load_users (GdmUserManager *manager) +{ + gboolean res; + + manager->priv->shells = g_hash_table_new_full (g_str_hash, + g_str_equal, + g_free, + NULL); + reload_shells (manager); + + load_sessions (manager); + + res = load_ck_history (manager); + schedule_reload_passwd (manager); +} + +static void gdm_user_manager_class_init (GdmUserManagerClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); @@ -1773,6 +1893,8 @@ gdm_user_manager_init (GdmUserManager *manager) return; } + manager->priv->cancellable = g_cancellable_new (); + get_seat_proxy (manager); queue_load_users (manager); @@ -1790,6 +1912,11 @@ gdm_user_manager_finalize (GObject *object) g_return_if_fail (manager->priv != NULL); + if (manager->priv->cancellable != NULL) { + g_object_unref (manager->priv->cancellable); + manager->priv->cancellable = NULL; + } + if (manager->priv->exclude != NULL) { g_slist_free (manager->priv->exclude); } |