Blame SOURCES/scale-better.patch

44ca26
From 2b23e50057fc92da30093fbebb78b320fc4107d0 Mon Sep 17 00:00:00 2001
44ca26
From: Ray Strode <rstrode@redhat.com>
44ca26
Date: Wed, 29 Jun 2016 10:50:37 -0400
44ca26
Subject: [PATCH 1/5] user: check if user is in wheel more efficiently
44ca26
44ca26
We currently get all the groups a user belongs to in one pass,
44ca26
then check each one to see if it's wheel.
44ca26
44ca26
It's much more efficient to just get the wheel group and check if
44ca26
any of its members are the user.
44ca26
44ca26
https://bugs.freedesktop.org/show_bug.cgi?id=48177
44ca26
---
44ca26
 src/user.c | 13 ++-----------
44ca26
 1 file changed, 2 insertions(+), 11 deletions(-)
44ca26
44ca26
diff --git a/src/user.c b/src/user.c
44ca26
index de30090..52f57d0 100644
44ca26
--- a/src/user.c
44ca26
+++ b/src/user.c
44ca26
@@ -92,88 +92,79 @@ struct User {
44ca26
         gchar        *home_dir;
44ca26
         gchar        *shell;
44ca26
         gchar        *email;
44ca26
         gchar        *language;
44ca26
         gchar        *x_session;
44ca26
         gchar        *location;
44ca26
         guint64       login_frequency;
44ca26
         gint64        login_time;
44ca26
         GVariant     *login_history;
44ca26
         gchar        *icon_file;
44ca26
         gchar        *default_icon_file;
44ca26
         gboolean      locked;
44ca26
         gboolean      automatic_login;
44ca26
         gboolean      system_account;
44ca26
         gboolean      local_account;
44ca26
 };
44ca26
 
44ca26
 typedef struct UserClass
44ca26
 {
44ca26
         AccountsUserSkeletonClass parent_class;
44ca26
 } UserClass;
44ca26
 
44ca26
 static void user_accounts_user_iface_init (AccountsUserIface *iface);
44ca26
 
44ca26
 G_DEFINE_TYPE_WITH_CODE (User, user, ACCOUNTS_TYPE_USER_SKELETON, G_IMPLEMENT_INTERFACE (ACCOUNTS_TYPE_USER, user_accounts_user_iface_init));
44ca26
 
44ca26
 static gint
44ca26
 account_type_from_pwent (struct passwd *pwent)
44ca26
 {
44ca26
         struct group *grp;
44ca26
-        gid_t wheel;
44ca26
-        gid_t *groups;
44ca26
-        gint ngroups;
44ca26
         gint i;
44ca26
 
44ca26
         if (pwent->pw_uid == 0) {
44ca26
                 g_debug ("user is root so account type is administrator");
44ca26
                 return ACCOUNT_TYPE_ADMINISTRATOR;
44ca26
         }
44ca26
 
44ca26
         grp = getgrnam (ADMIN_GROUP);
44ca26
         if (grp == NULL) {
44ca26
                 g_debug (ADMIN_GROUP " group not found");
44ca26
                 return ACCOUNT_TYPE_STANDARD;
44ca26
         }
44ca26
-        wheel = grp->gr_gid;
44ca26
 
44ca26
-        ngroups = get_user_groups (pwent->pw_name, pwent->pw_gid, &groups);
44ca26
-
44ca26
-        for (i = 0; i < ngroups; i++) {
44ca26
-                if (groups[i] == wheel) {
44ca26
-                        g_free (groups);
44ca26
+        for (i = 0; grp->gr_mem[i] != NULL; i++) {
44ca26
+                if (g_strcmp0 (grp->gr_mem[i], pwent->pw_name) == 0) {
44ca26
                         return ACCOUNT_TYPE_ADMINISTRATOR;
44ca26
                 }
44ca26
         }
44ca26
 
44ca26
-        g_free (groups);
44ca26
-
44ca26
         return ACCOUNT_TYPE_STANDARD;
44ca26
 }
44ca26
 
44ca26
 void
44ca26
 user_update_from_pwent (User          *user,
44ca26
                         struct passwd *pwent)
44ca26
 {
44ca26
 #ifdef HAVE_SHADOW_H
44ca26
         struct spwd *spent;
44ca26
 #endif
44ca26
         gchar *real_name;
44ca26
         gboolean changed;
44ca26
         const gchar *passwd;
44ca26
         gboolean locked;
44ca26
         PasswordMode mode;
44ca26
         AccountType account_type;
44ca26
 
44ca26
         g_object_freeze_notify (G_OBJECT (user));
44ca26
 
44ca26
         changed = FALSE;
44ca26
 
44ca26
         if (pwent->pw_gecos && pwent->pw_gecos[0] != '\0') {
44ca26
                 gchar *first_comma = NULL;
44ca26
                 gchar *valid_utf8_name = NULL;
44ca26
 
44ca26
                 if (g_utf8_validate (pwent->pw_gecos, -1, NULL)) {
44ca26
                         valid_utf8_name = pwent->pw_gecos;
44ca26
                         first_comma = g_utf8_strchr (valid_utf8_name, -1, ',');
44ca26
                 }
44ca26
                 else {
44ca26
-- 
44ca26
2.7.4
44ca26
44ca26
44ca26
From c2b87f89a85ffa5465a523aa291ea0018a050cc5 Mon Sep 17 00:00:00 2001
44ca26
From: Ray Strode <rstrode@redhat.com>
44ca26
Date: Tue, 28 Jun 2016 15:43:08 -0400
44ca26
Subject: [PATCH 2/5] daemon: get local users from /etc/shadow not /etc/passwd
44ca26
44ca26
For some sites, it's common practice to rsync around large
44ca26
/etc/passwd files containing the password entries for remote
44ca26
users. That means accountsservices' "assume /etc/passwd is local
44ca26
users" heuristic falls over.
44ca26
44ca26
This commit changes it to only treat users in /etc/shadow as local.
44ca26
44ca26
https://bugs.freedesktop.org/show_bug.cgi?id=48177
44ca26
---
44ca26
 src/daemon.c | 59 +++++++++++++++++++++++++++++++++++++++++++++++++++++------
44ca26
 1 file changed, 53 insertions(+), 6 deletions(-)
44ca26
44ca26
diff --git a/src/daemon.c b/src/daemon.c
44ca26
index 38f6a47..5c269af 100644
44ca26
--- a/src/daemon.c
44ca26
+++ b/src/daemon.c
44ca26
@@ -2,60 +2,63 @@
44ca26
  *
44ca26
  * Copyright (C) 2009-2010 Red Hat, Inc.
44ca26
  * Copyright (c) 2013 Canonical Limited
44ca26
  *
44ca26
  * This program is free software; you can redistribute it and/or modify
44ca26
  * it under the terms of the GNU General Public License as published by
44ca26
  * the Free Software Foundation; either version 3 of the License, or
44ca26
  * (at your option) any later version.
44ca26
  *
44ca26
  * This program is distributed in the hope that it will be useful,
44ca26
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
44ca26
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
44ca26
  * GNU General Public License for more details.
44ca26
  *
44ca26
  * You should have received a copy of the GNU General Public License
44ca26
  * along with this program; if not, write to the Free Software
44ca26
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
44ca26
  *
44ca26
  * Written by: Matthias Clasen <mclasen@redhat.com>
44ca26
  */
44ca26
 
44ca26
 #include "config.h"
44ca26
 
44ca26
 #include <stdlib.h>
44ca26
 #include <stdio.h>
44ca26
 #include <sys/types.h>
44ca26
 #include <sys/stat.h>
44ca26
 #include <fcntl.h>
44ca26
 #include <sys/wait.h>
44ca26
 #include <pwd.h>
44ca26
+#ifdef HAVE_SHADOW_H
44ca26
+#include <shadow.h>
44ca26
+#endif
44ca26
 #include <unistd.h>
44ca26
 #include <errno.h>
44ca26
 #include <sys/types.h>
44ca26
 #ifdef HAVE_UTMPX_H
44ca26
 #include <utmpx.h>
44ca26
 #endif
44ca26
 
44ca26
 #include <glib.h>
44ca26
 #include <glib/gi18n.h>
44ca26
 #include <glib-object.h>
44ca26
 #include <glib/gstdio.h>
44ca26
 #include <gio/gio.h>
44ca26
 #include <polkit/polkit.h>
44ca26
 
44ca26
 #include "user-classify.h"
44ca26
 #include "daemon.h"
44ca26
 #include "util.h"
44ca26
 
44ca26
 #define PATH_PASSWD "/etc/passwd"
44ca26
 #define PATH_SHADOW "/etc/shadow"
44ca26
 #define PATH_GROUP "/etc/group"
44ca26
 #define PATH_GDM_CUSTOM "/etc/gdm/custom.conf"
44ca26
 #ifdef HAVE_UTMPX_H
44ca26
 #define PATH_WTMP _PATH_WTMPX
44ca26
 #endif
44ca26
 
44ca26
 enum {
44ca26
         PROP_0,
44ca26
         PROP_DAEMON_VERSION
44ca26
 };
44ca26
@@ -279,81 +282,125 @@ entry_generator_wtmp (GHashTable *users,
44ca26
 
44ca26
                 builder = g_variant_builder_new (G_VARIANT_TYPE ("a(xxa{sv})"));
44ca26
                 for (l = g_list_last (accounting->previous_logins); l != NULL; l = l->prev) {
44ca26
                         previous_login = l->data;
44ca26
 
44ca26
                         builder2 = g_variant_builder_new (G_VARIANT_TYPE ("a{sv}"));
44ca26
                         g_variant_builder_add (builder2, "{sv}", "type", g_variant_new_string (previous_login->id));
44ca26
                         g_variant_builder_add (builder, "(xxa{sv})", previous_login->login_time, previous_login->logout_time, builder2);
44ca26
                         g_variant_builder_unref (builder2);
44ca26
                 }
44ca26
                 g_object_set (user, "login-history", g_variant_new ("a(xxa{sv})", builder), NULL);
44ca26
                 g_variant_builder_unref (builder);
44ca26
                 g_list_free_full (accounting->previous_logins, (GDestroyNotify) user_previous_login_free);
44ca26
 
44ca26
                 user_changed (user);
44ca26
         }
44ca26
 
44ca26
         g_hash_table_unref (login_hash);
44ca26
         g_hash_table_unref (logout_hash);
44ca26
         g_free (state_data);
44ca26
         *state = NULL;
44ca26
         return NULL;
44ca26
 }
44ca26
 #endif /* HAVE_UTMPX_H */
44ca26
 
44ca26
 static struct passwd *
44ca26
 entry_generator_fgetpwent (GHashTable *users,
44ca26
                            gpointer   *state)
44ca26
 {
44ca26
         struct passwd *pwent;
44ca26
-        FILE *fp;
44ca26
+        struct {
44ca26
+                FILE *fp;
44ca26
+                GHashTable *users;
44ca26
+        } *generator_state;
44ca26
 
44ca26
         /* First iteration */
44ca26
         if (*state == NULL) {
44ca26
-                *state = fp = fopen (PATH_PASSWD, "r");
44ca26
+                GHashTable *shadow_users = NULL;
44ca26
+                FILE *fp;
44ca26
+#ifdef HAVE_SHADOW_H
44ca26
+                struct spwd *shadow_entry;
44ca26
+
44ca26
+                fp = fopen (PATH_SHADOW, "r");
44ca26
+                if (fp == NULL) {
44ca26
+                        g_warning ("Unable to open %s: %s", PATH_SHADOW, g_strerror (errno));
44ca26
+                        return NULL;
44ca26
+                }
44ca26
+
44ca26
+                shadow_users = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
44ca26
+
44ca26
+                do {
44ca26
+                        shadow_entry = fgetspent (fp);
44ca26
+                        if (shadow_entry != NULL) {
44ca26
+                                g_hash_table_add (shadow_users, g_strdup (shadow_entry->sp_namp));
44ca26
+                        } else if (errno != EINTR) {
44ca26
+                                break;
44ca26
+                        }
44ca26
+                } while (shadow_entry != NULL);
44ca26
+
44ca26
+                fclose (fp);
44ca26
+
44ca26
+                if (g_hash_table_size (shadow_users) == 0) {
44ca26
+                        g_clear_pointer (&shadow_users, g_hash_table_unref);
44ca26
+                        return NULL;
44ca26
+                }
44ca26
+#endif
44ca26
+
44ca26
+                fp = fopen (PATH_PASSWD, "r");
44ca26
                 if (fp == NULL) {
44ca26
+                        g_clear_pointer (&shadow_users, g_hash_table_unref);
44ca26
                         g_warning ("Unable to open %s: %s", PATH_PASSWD, g_strerror (errno));
44ca26
                         return NULL;
44ca26
                 }
44ca26
+
44ca26
+                generator_state = g_malloc0 (sizeof (*generator_state));
44ca26
+                generator_state->fp = fp;
44ca26
+                generator_state->users = shadow_users;
44ca26
+
44ca26
+                *state = generator_state;
44ca26
         }
44ca26
 
44ca26
         /* Every iteration */
44ca26
-        fp = *state;
44ca26
-        pwent = fgetpwent (fp);
44ca26
+        generator_state = *state;
44ca26
+        pwent = fgetpwent (generator_state->fp);
44ca26
         if (pwent != NULL) {
44ca26
-                return pwent;
44ca26
+                if (!generator_state->users || g_hash_table_lookup (generator_state->users, pwent->pw_name))
44ca26
+                        return pwent;
44ca26
         }
44ca26
 
44ca26
         /* Last iteration */
44ca26
-        fclose (fp);
44ca26
+        fclose (generator_state->fp);
44ca26
+        g_hash_table_unref (generator_state->users);
44ca26
+        g_free (generator_state);
44ca26
         *state = NULL;
44ca26
+
44ca26
         return NULL;
44ca26
 }
44ca26
 
44ca26
 static struct passwd *
44ca26
 entry_generator_cachedir (GHashTable *users,
44ca26
                           gpointer   *state)
44ca26
 {
44ca26
         struct passwd *pwent;
44ca26
         const gchar *name;
44ca26
         GError *error = NULL;
44ca26
         gchar *filename;
44ca26
         gboolean regular;
44ca26
         GHashTableIter iter;
44ca26
         GKeyFile *key_file;
44ca26
         User *user;
44ca26
         GDir *dir;
44ca26
 
44ca26
         /* First iteration */
44ca26
         if (*state == NULL) {
44ca26
                 *state = g_dir_open (USERDIR, 0, &error);
44ca26
                 if (error != NULL) {
44ca26
                         if (!g_error_matches (error, G_FILE_ERROR, G_FILE_ERROR_NOENT))
44ca26
                                 g_warning ("couldn't list user cache directory: %s", USERDIR);
44ca26
                         g_error_free (error);
44ca26
                         return NULL;
44ca26
                 }
44ca26
         }
44ca26
 
44ca26
         /* Every iteration */
44ca26
 
44ca26
-- 
44ca26
2.7.4
44ca26
44ca26
44ca26
From 011ef555b0db601186a38c43f9359589ed61e230 Mon Sep 17 00:00:00 2001
44ca26
From: Ray Strode <rstrode@redhat.com>
44ca26
Date: Wed, 29 Jun 2016 15:57:38 -0400
44ca26
Subject: [PATCH 3/5] daemon: don't call getspnam for local users
44ca26
44ca26
We're already iterating over the whole shadow file, so
44ca26
just cache the entries instead of calling getspname a
44ca26
few lines later.
44ca26
44ca26
https://bugs.freedesktop.org/show_bug.cgi?id=48177
44ca26
---
44ca26
 src/daemon.c | 86 +++++++++++++++++++++++++++++++++++++++++-------------------
44ca26
 src/user.c   | 11 ++------
44ca26
 src/user.h   |  4 ++-
44ca26
 3 files changed, 64 insertions(+), 37 deletions(-)
44ca26
44ca26
diff --git a/src/daemon.c b/src/daemon.c
44ca26
index 5c269af..71a3ea4 100644
44ca26
--- a/src/daemon.c
44ca26
+++ b/src/daemon.c
44ca26
@@ -58,61 +58,61 @@
44ca26
 #define PATH_WTMP _PATH_WTMPX
44ca26
 #endif
44ca26
 
44ca26
 enum {
44ca26
         PROP_0,
44ca26
         PROP_DAEMON_VERSION
44ca26
 };
44ca26
 
44ca26
 struct DaemonPrivate {
44ca26
         GDBusConnection *bus_connection;
44ca26
         GDBusProxy *bus_proxy;
44ca26
 
44ca26
         GHashTable *users;
44ca26
 
44ca26
         User *autologin;
44ca26
 
44ca26
         GFileMonitor *passwd_monitor;
44ca26
         GFileMonitor *shadow_monitor;
44ca26
         GFileMonitor *group_monitor;
44ca26
         GFileMonitor *gdm_monitor;
44ca26
 #ifdef HAVE_UTMPX_H
44ca26
         GFileMonitor *wtmp_monitor;
44ca26
 #endif
44ca26
 
44ca26
         guint reload_id;
44ca26
         guint autologin_id;
44ca26
 
44ca26
         PolkitAuthority *authority;
44ca26
 };
44ca26
 
44ca26
-typedef struct passwd * (* EntryGeneratorFunc) (GHashTable *, gpointer *);
44ca26
+typedef struct passwd * (* EntryGeneratorFunc) (GHashTable *, gpointer *, struct spwd **shadow_entry);
44ca26
 
44ca26
 static void daemon_accounts_accounts_iface_init (AccountsAccountsIface *iface);
44ca26
 
44ca26
 G_DEFINE_TYPE_WITH_CODE (Daemon, daemon, ACCOUNTS_TYPE_ACCOUNTS_SKELETON, G_IMPLEMENT_INTERFACE (ACCOUNTS_TYPE_ACCOUNTS, daemon_accounts_accounts_iface_init));
44ca26
 
44ca26
 #define DAEMON_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), TYPE_DAEMON, DaemonPrivate))
44ca26
 
44ca26
 static const GDBusErrorEntry accounts_error_entries[] =
44ca26
 { 
44ca26
         { ERROR_FAILED, "org.freedesktop.Accounts.Error.Failed" },
44ca26
         { ERROR_USER_EXISTS, "org.freedesktop.Accounts.Error.UserExists" },
44ca26
         { ERROR_USER_DOES_NOT_EXIST, "org.freedesktop.Accounts.Error.UserDoesNotExist" },
44ca26
         { ERROR_PERMISSION_DENIED, "org.freedesktop.Accounts.Error.PermissionDenied" },
44ca26
         { ERROR_NOT_SUPPORTED, "org.freedesktop.Accounts.Error.NotSupported" }
44ca26
 };
44ca26
 
44ca26
 GQuark
44ca26
 error_quark (void)
44ca26
 {
44ca26
         static volatile gsize quark_volatile = 0;
44ca26
 
44ca26
         g_dbus_error_register_error_domain ("accounts_error",
44ca26
                                             &quark_volatile,
44ca26
                                             accounts_error_entries,
44ca26
                                             G_N_ELEMENTS (accounts_error_entries));
44ca26
 
44ca26
         return (GQuark) quark_volatile;
44ca26
 }
44ca26
 #define ENUM_ENTRY(NAME, DESC) { NAME, "" #NAME "", DESC }
44ca26
 
44ca26
@@ -138,62 +138,63 @@ error_get_type (void)
44ca26
   return etype;
44ca26
 }
44ca26
 
44ca26
 #ifdef HAVE_UTMPX_H
44ca26
 
44ca26
 typedef struct {
44ca26
         guint64 frequency;
44ca26
         gint64 time;
44ca26
         GList *previous_logins;
44ca26
 } UserAccounting;
44ca26
 
44ca26
 typedef struct {
44ca26
         gchar  *id;
44ca26
         gint64  login_time;
44ca26
         gint64  logout_time;
44ca26
 } UserPreviousLogin;
44ca26
 
44ca26
 typedef struct {
44ca26
         GHashTable *login_hash;
44ca26
         GHashTable *logout_hash;
44ca26
 } WTmpGeneratorState;
44ca26
 
44ca26
 static void
44ca26
 user_previous_login_free (UserPreviousLogin *previous_login)
44ca26
 {
44ca26
         g_free (previous_login->id);
44ca26
         g_free (previous_login);
44ca26
 }
44ca26
 
44ca26
 static struct passwd *
44ca26
-entry_generator_wtmp (GHashTable *users,
44ca26
-                      gpointer   *state)
44ca26
+entry_generator_wtmp (GHashTable   *users,
44ca26
+                      gpointer     *state,
44ca26
+                      struct spwd **spent)
44ca26
 {
44ca26
         GHashTable *login_hash, *logout_hash;
44ca26
         struct utmpx *wtmp_entry;
44ca26
         GHashTableIter iter;
44ca26
         gpointer key, value;
44ca26
         struct passwd *pwent;
44ca26
         User *user;
44ca26
         WTmpGeneratorState *state_data;
44ca26
         GVariantBuilder *builder, *builder2;
44ca26
         GList *l;
44ca26
 
44ca26
         if (*state == NULL) {
44ca26
                 /* First iteration */
44ca26
 #ifdef UTXDB_LOG
44ca26
                 if (setutxdb (UTXDB_LOG, NULL) != 0) {
44ca26
                         return NULL;
44ca26
                 }
44ca26
 #else
44ca26
                 utmpxname (PATH_WTMP);
44ca26
                 setutxent ();
44ca26
 #endif
44ca26
                 *state = g_new (WTmpGeneratorState, 1);
44ca26
                 state_data = *state;
44ca26
                 state_data->login_hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
44ca26
                 state_data->logout_hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
44ca26
         }
44ca26
 
44ca26
         /* Every iteration */
44ca26
         state_data = *state;
44ca26
         login_hash = state_data->login_hash;
44ca26
@@ -232,284 +233,308 @@ entry_generator_wtmp (GHashTable *users,
44ca26
                 }
44ca26
 
44ca26
                 pwent = getpwnam (wtmp_entry->ut_user);
44ca26
                 if (pwent == NULL) {
44ca26
                         continue;
44ca26
                 }
44ca26
 
44ca26
                 if (!g_hash_table_lookup_extended (login_hash,
44ca26
                                                    wtmp_entry->ut_user,
44ca26
                                                    &key, &value)) {
44ca26
                         accounting = g_new (UserAccounting, 1);
44ca26
                         accounting->frequency = 0;
44ca26
                         accounting->previous_logins = NULL;
44ca26
 
44ca26
                         g_hash_table_insert (login_hash, g_strdup (wtmp_entry->ut_user), accounting);
44ca26
                 } else {
44ca26
                         accounting = value;
44ca26
                 }
44ca26
 
44ca26
                 accounting->frequency++;
44ca26
                 accounting->time = wtmp_entry->ut_tv.tv_sec;
44ca26
 
44ca26
                 /* Add zero logout time to change it later on logout record */
44ca26
                 previous_login = g_new (UserPreviousLogin, 1);
44ca26
                 previous_login->id = g_strdup (wtmp_entry->ut_line);
44ca26
                 previous_login->login_time = wtmp_entry->ut_tv.tv_sec;
44ca26
                 previous_login->logout_time = 0;
44ca26
                 accounting->previous_logins = g_list_prepend (accounting->previous_logins, previous_login);
44ca26
 
44ca26
                 g_hash_table_insert (logout_hash, g_strdup (wtmp_entry->ut_line), previous_login);
44ca26
+                *spent = getspnam (pwent->pw_name);
44ca26
 
44ca26
                 return pwent;
44ca26
         }
44ca26
 
44ca26
         /* Last iteration */
44ca26
         endutxent ();
44ca26
 
44ca26
         g_hash_table_iter_init (&iter, login_hash);
44ca26
         while (g_hash_table_iter_next (&iter, &key, &value)) {
44ca26
                 UserAccounting    *accounting = (UserAccounting *) value;
44ca26
                 UserPreviousLogin *previous_login;
44ca26
 
44ca26
                 user = g_hash_table_lookup (users, key);
44ca26
                 if (user == NULL) {
44ca26
                         g_list_free_full (accounting->previous_logins, (GDestroyNotify) user_previous_login_free);
44ca26
                         continue;
44ca26
                 }
44ca26
 
44ca26
                 g_object_set (user, "login-frequency", accounting->frequency, NULL);
44ca26
                 g_object_set (user, "login-time", accounting->time, NULL);
44ca26
 
44ca26
                 builder = g_variant_builder_new (G_VARIANT_TYPE ("a(xxa{sv})"));
44ca26
                 for (l = g_list_last (accounting->previous_logins); l != NULL; l = l->prev) {
44ca26
                         previous_login = l->data;
44ca26
 
44ca26
                         builder2 = g_variant_builder_new (G_VARIANT_TYPE ("a{sv}"));
44ca26
                         g_variant_builder_add (builder2, "{sv}", "type", g_variant_new_string (previous_login->id));
44ca26
                         g_variant_builder_add (builder, "(xxa{sv})", previous_login->login_time, previous_login->logout_time, builder2);
44ca26
                         g_variant_builder_unref (builder2);
44ca26
                 }
44ca26
                 g_object_set (user, "login-history", g_variant_new ("a(xxa{sv})", builder), NULL);
44ca26
                 g_variant_builder_unref (builder);
44ca26
                 g_list_free_full (accounting->previous_logins, (GDestroyNotify) user_previous_login_free);
44ca26
 
44ca26
                 user_changed (user);
44ca26
         }
44ca26
 
44ca26
         g_hash_table_unref (login_hash);
44ca26
         g_hash_table_unref (logout_hash);
44ca26
         g_free (state_data);
44ca26
         *state = NULL;
44ca26
         return NULL;
44ca26
 }
44ca26
 #endif /* HAVE_UTMPX_H */
44ca26
 
44ca26
 static struct passwd *
44ca26
-entry_generator_fgetpwent (GHashTable *users,
44ca26
-                           gpointer   *state)
44ca26
+entry_generator_fgetpwent (GHashTable   *users,
44ca26
+                           gpointer     *state,
44ca26
+                           struct spwd **spent)
44ca26
 {
44ca26
         struct passwd *pwent;
44ca26
+
44ca26
+        struct {
44ca26
+                struct spwd spbuf;
44ca26
+                char buf[1024];
44ca26
+        } *shadow_entry_buffers;
44ca26
+
44ca26
         struct {
44ca26
                 FILE *fp;
44ca26
                 GHashTable *users;
44ca26
         } *generator_state;
44ca26
 
44ca26
         /* First iteration */
44ca26
         if (*state == NULL) {
44ca26
                 GHashTable *shadow_users = NULL;
44ca26
                 FILE *fp;
44ca26
-#ifdef HAVE_SHADOW_H
44ca26
                 struct spwd *shadow_entry;
44ca26
 
44ca26
                 fp = fopen (PATH_SHADOW, "r");
44ca26
                 if (fp == NULL) {
44ca26
                         g_warning ("Unable to open %s: %s", PATH_SHADOW, g_strerror (errno));
44ca26
                         return NULL;
44ca26
                 }
44ca26
 
44ca26
-                shadow_users = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
44ca26
+                shadow_users = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
44ca26
 
44ca26
                 do {
44ca26
-                        shadow_entry = fgetspent (fp);
44ca26
-                        if (shadow_entry != NULL) {
44ca26
-                                g_hash_table_add (shadow_users, g_strdup (shadow_entry->sp_namp));
44ca26
-                        } else if (errno != EINTR) {
44ca26
-                                break;
44ca26
+                        int ret = 0;
44ca26
+
44ca26
+                        shadow_entry_buffers = g_malloc0 (sizeof (*shadow_entry_buffers));
44ca26
+
44ca26
+                        ret = fgetspent_r (fp, &shadow_entry_buffers->spbuf, shadow_entry_buffers->buf, sizeof (shadow_entry_buffers->buf), &shadow_entry);
44ca26
+                        if (ret == 0) {
44ca26
+                                g_hash_table_insert (shadow_users, g_strdup (shadow_entry->sp_namp), shadow_entry_buffers);
44ca26
+                        } else {
44ca26
+                                g_free (shadow_entry_buffers);
44ca26
+
44ca26
+                                if (errno != EINTR) {
44ca26
+                                        break;
44ca26
+                                }
44ca26
                         }
44ca26
                 } while (shadow_entry != NULL);
44ca26
 
44ca26
                 fclose (fp);
44ca26
 
44ca26
                 if (g_hash_table_size (shadow_users) == 0) {
44ca26
                         g_clear_pointer (&shadow_users, g_hash_table_unref);
44ca26
                         return NULL;
44ca26
                 }
44ca26
-#endif
44ca26
 
44ca26
                 fp = fopen (PATH_PASSWD, "r");
44ca26
                 if (fp == NULL) {
44ca26
                         g_clear_pointer (&shadow_users, g_hash_table_unref);
44ca26
                         g_warning ("Unable to open %s: %s", PATH_PASSWD, g_strerror (errno));
44ca26
                         return NULL;
44ca26
                 }
44ca26
 
44ca26
                 generator_state = g_malloc0 (sizeof (*generator_state));
44ca26
                 generator_state->fp = fp;
44ca26
                 generator_state->users = shadow_users;
44ca26
 
44ca26
                 *state = generator_state;
44ca26
         }
44ca26
 
44ca26
         /* Every iteration */
44ca26
         generator_state = *state;
44ca26
         pwent = fgetpwent (generator_state->fp);
44ca26
         if (pwent != NULL) {
44ca26
-                if (!generator_state->users || g_hash_table_lookup (generator_state->users, pwent->pw_name))
44ca26
+                shadow_entry_buffers = g_hash_table_lookup (generator_state->users, pwent->pw_name);
44ca26
+
44ca26
+                if (shadow_entry_buffers != NULL) {
44ca26
+                        *spent = &shadow_entry_buffers->spbuf;
44ca26
                         return pwent;
44ca26
+                }
44ca26
         }
44ca26
 
44ca26
         /* Last iteration */
44ca26
         fclose (generator_state->fp);
44ca26
         g_hash_table_unref (generator_state->users);
44ca26
         g_free (generator_state);
44ca26
         *state = NULL;
44ca26
 
44ca26
         return NULL;
44ca26
 }
44ca26
 
44ca26
 static struct passwd *
44ca26
-entry_generator_cachedir (GHashTable *users,
44ca26
-                          gpointer   *state)
44ca26
+entry_generator_cachedir (GHashTable   *users,
44ca26
+                          gpointer     *state,
44ca26
+                          struct spwd **shadow_entry)
44ca26
 {
44ca26
         struct passwd *pwent;
44ca26
         const gchar *name;
44ca26
         GError *error = NULL;
44ca26
         gchar *filename;
44ca26
         gboolean regular;
44ca26
         GHashTableIter iter;
44ca26
         GKeyFile *key_file;
44ca26
         User *user;
44ca26
         GDir *dir;
44ca26
 
44ca26
         /* First iteration */
44ca26
         if (*state == NULL) {
44ca26
                 *state = g_dir_open (USERDIR, 0, &error);
44ca26
                 if (error != NULL) {
44ca26
                         if (!g_error_matches (error, G_FILE_ERROR, G_FILE_ERROR_NOENT))
44ca26
                                 g_warning ("couldn't list user cache directory: %s", USERDIR);
44ca26
                         g_error_free (error);
44ca26
                         return NULL;
44ca26
                 }
44ca26
         }
44ca26
 
44ca26
         /* Every iteration */
44ca26
 
44ca26
         /*
44ca26
          * Use names of files of regular type to lookup information
44ca26
          * about each user. Loop until we find something valid.
44ca26
          */
44ca26
         dir = *state;
44ca26
         while (TRUE) {
44ca26
                 name = g_dir_read_name (dir);
44ca26
                 if (name == NULL)
44ca26
                         break;
44ca26
 
44ca26
                 /* Only load files in this directory */
44ca26
                 filename = g_build_filename (USERDIR, name, NULL);
44ca26
                 regular = g_file_test (filename, G_FILE_TEST_IS_REGULAR);
44ca26
                 g_free (filename);
44ca26
 
44ca26
                 if (regular) {
44ca26
                         pwent = getpwnam (name);
44ca26
-                        if (pwent == NULL)
44ca26
+                        if (pwent == NULL) {
44ca26
                                 g_debug ("user '%s' in cache dir but not present on system", name);
44ca26
-                        else
44ca26
+                        } else {
44ca26
+                                *shadow_entry = getspnam (pwent->pw_name);
44ca26
+
44ca26
                                 return pwent;
44ca26
+                        }
44ca26
                 }
44ca26
         }
44ca26
 
44ca26
         /* Last iteration */
44ca26
         g_dir_close (dir);
44ca26
 
44ca26
         /* Update all the users from the files in the cache dir */
44ca26
         g_hash_table_iter_init (&iter, users);
44ca26
         while (g_hash_table_iter_next (&iter, (gpointer *)&name, (gpointer *)&user)) {
44ca26
                 filename = g_build_filename (USERDIR, name, NULL);
44ca26
                 key_file = g_key_file_new ();
44ca26
                 if (g_key_file_load_from_file (key_file, filename, 0, NULL))
44ca26
                         user_update_from_keyfile (user, key_file);
44ca26
                 g_key_file_unref (key_file);
44ca26
                 g_free (filename);
44ca26
         }
44ca26
 
44ca26
         *state = NULL;
44ca26
         return NULL;
44ca26
 }
44ca26
 
44ca26
 static void
44ca26
 load_entries (Daemon             *daemon,
44ca26
               GHashTable         *users,
44ca26
               EntryGeneratorFunc  entry_generator)
44ca26
 {
44ca26
         gpointer generator_state = NULL;
44ca26
         struct passwd *pwent;
44ca26
+        struct spwd *spent = NULL;
44ca26
         User *user = NULL;
44ca26
 
44ca26
         g_assert (entry_generator != NULL);
44ca26
 
44ca26
         for (;;) {
44ca26
-                pwent = entry_generator (users, &generator_state);
44ca26
+                spent = NULL;
44ca26
+                pwent = entry_generator (users, &generator_state, &spent);
44ca26
                 if (pwent == NULL)
44ca26
                         break;
44ca26
 
44ca26
                 /* Skip system users... */
44ca26
-                if (!user_classify_is_human (pwent->pw_uid, pwent->pw_name, pwent->pw_shell, NULL)) {
44ca26
+                if (!user_classify_is_human (pwent->pw_uid, pwent->pw_name, pwent->pw_shell, spent? spent->sp_pwdp : NULL)) {
44ca26
                         g_debug ("skipping user: %s", pwent->pw_name);
44ca26
                         continue;
44ca26
                 }
44ca26
 
44ca26
                 /* ignore duplicate entries */
44ca26
                 if (g_hash_table_lookup (users, pwent->pw_name)) {
44ca26
                         continue;
44ca26
                 }
44ca26
 
44ca26
                 user = g_hash_table_lookup (daemon->priv->users, pwent->pw_name);
44ca26
                 if (user == NULL) {
44ca26
                         user = user_new (daemon, pwent->pw_uid);
44ca26
                 } else {
44ca26
                         g_object_ref (user);
44ca26
                 }
44ca26
 
44ca26
                 /* freeze & update users not already in the new list */
44ca26
                 g_object_freeze_notify (G_OBJECT (user));
44ca26
-                user_update_from_pwent (user, pwent);
44ca26
+                user_update_from_pwent (user, pwent, spent);
44ca26
 
44ca26
                 g_hash_table_insert (users, g_strdup (user_get_user_name (user)), user);
44ca26
                 g_debug ("loaded user: %s", user_get_user_name (user));
44ca26
         }
44ca26
 
44ca26
         /* Generator should have cleaned up */
44ca26
         g_assert (generator_state == NULL);
44ca26
 }
44ca26
 
44ca26
 static GHashTable *
44ca26
 create_users_hash_table (void)
44ca26
 {
44ca26
         return g_hash_table_new_full (g_str_hash,
44ca26
                                       g_str_equal,
44ca26
                                       g_free,
44ca26
                                       g_object_unref);
44ca26
 }
44ca26
 
44ca26
 static void
44ca26
 reload_users (Daemon *daemon)
44ca26
 {
44ca26
         GHashTable *users;
44ca26
         GHashTable *old_users;
44ca26
         GHashTable *local;
44ca26
         GHashTableIter iter;
44ca26
         gpointer name;
44ca26
         User *user;
44ca26
 
44ca26
         /* Track the users that we saw during our (re)load */
44ca26
         users = create_users_hash_table ();
44ca26
@@ -827,115 +852,122 @@ daemon_new (void)
44ca26
                 g_object_unref (daemon);
44ca26
                 goto error;
44ca26
         }
44ca26
 
44ca26
         return daemon;
44ca26
 
44ca26
  error:
44ca26
         return NULL;
44ca26
 }
44ca26
 
44ca26
 static void
44ca26
 throw_error (GDBusMethodInvocation *context,
44ca26
              gint                   error_code,
44ca26
              const gchar           *format,
44ca26
              ...)
44ca26
 {
44ca26
         va_list args;
44ca26
         gchar *message;
44ca26
 
44ca26
         va_start (args, format);
44ca26
         message = g_strdup_vprintf (format, args);
44ca26
         va_end (args);
44ca26
 
44ca26
         g_dbus_method_invocation_return_error (context, ERROR, error_code, "%s", message);
44ca26
 
44ca26
         g_free (message);
44ca26
 }
44ca26
 
44ca26
 static User *
44ca26
 add_new_user_for_pwent (Daemon        *daemon,
44ca26
-                        struct passwd *pwent)
44ca26
+                        struct passwd *pwent,
44ca26
+                        struct spwd   *spent)
44ca26
 {
44ca26
         User *user;
44ca26
 
44ca26
         user = user_new (daemon, pwent->pw_uid);
44ca26
-        user_update_from_pwent (user, pwent);
44ca26
+        user_update_from_pwent (user, pwent, spent);
44ca26
         user_register (user);
44ca26
 
44ca26
         g_hash_table_insert (daemon->priv->users,
44ca26
                              g_strdup (user_get_user_name (user)),
44ca26
                              user);
44ca26
 
44ca26
         accounts_accounts_emit_user_added (ACCOUNTS_ACCOUNTS (daemon), user_get_object_path (user));
44ca26
 
44ca26
         return user;
44ca26
 }
44ca26
 
44ca26
 User *
44ca26
 daemon_local_find_user_by_id (Daemon *daemon,
44ca26
                               uid_t   uid)
44ca26
 {
44ca26
         User *user;
44ca26
         struct passwd *pwent;
44ca26
 
44ca26
         pwent = getpwuid (uid);
44ca26
         if (pwent == NULL) {
44ca26
                 g_debug ("unable to lookup uid %d", (int)uid);
44ca26
                 return NULL;
44ca26
         }
44ca26
 
44ca26
         user = g_hash_table_lookup (daemon->priv->users, pwent->pw_name);
44ca26
 
44ca26
-        if (user == NULL)
44ca26
-                user = add_new_user_for_pwent (daemon, pwent);
44ca26
+        if (user == NULL) {
44ca26
+                struct spwd *spent;
44ca26
+                spent = getspnam (pwent->pw_name);
44ca26
+                user = add_new_user_for_pwent (daemon, pwent, spent);
44ca26
+        }
44ca26
 
44ca26
         return user;
44ca26
 }
44ca26
 
44ca26
 User *
44ca26
 daemon_local_find_user_by_name (Daemon      *daemon,
44ca26
                                 const gchar *name)
44ca26
 {
44ca26
         User *user;
44ca26
         struct passwd *pwent;
44ca26
 
44ca26
         pwent = getpwnam (name);
44ca26
         if (pwent == NULL) {
44ca26
                 g_debug ("unable to lookup name %s: %s", name, g_strerror (errno));
44ca26
                 return NULL;
44ca26
         }
44ca26
 
44ca26
         user = g_hash_table_lookup (daemon->priv->users, pwent->pw_name);
44ca26
 
44ca26
-        if (user == NULL)
44ca26
-                user = add_new_user_for_pwent (daemon, pwent);
44ca26
+        if (user == NULL) {
44ca26
+                struct spwd *spent;
44ca26
+                spent = getspnam (pwent->pw_name);
44ca26
+                user = add_new_user_for_pwent (daemon, pwent, spent);
44ca26
+        }
44ca26
 
44ca26
         return user;
44ca26
 }
44ca26
 
44ca26
 User *
44ca26
 daemon_local_get_automatic_login_user (Daemon *daemon)
44ca26
 {
44ca26
         return daemon->priv->autologin;
44ca26
 }
44ca26
 
44ca26
 static gboolean
44ca26
 daemon_find_user_by_id (AccountsAccounts      *accounts,
44ca26
                         GDBusMethodInvocation *context,
44ca26
                         gint64                 uid)
44ca26
 {
44ca26
         Daemon *daemon = (Daemon*)accounts;
44ca26
         User *user;
44ca26
 
44ca26
         user = daemon_local_find_user_by_id (daemon, uid);
44ca26
 
44ca26
         if (user) {
44ca26
                 accounts_accounts_complete_find_user_by_id (NULL, context, user_get_object_path (user));
44ca26
         }
44ca26
         else {
44ca26
                 throw_error (context, ERROR_FAILED, "Failed to look up user with uid %d.", (int)uid);
44ca26
         }
44ca26
 
44ca26
         return TRUE;
44ca26
 }
44ca26
 
44ca26
diff --git a/src/user.c b/src/user.c
44ca26
index 52f57d0..247ca2f 100644
44ca26
--- a/src/user.c
44ca26
+++ b/src/user.c
44ca26
@@ -116,65 +116,63 @@ static void user_accounts_user_iface_init (AccountsUserIface *iface);
44ca26
 G_DEFINE_TYPE_WITH_CODE (User, user, ACCOUNTS_TYPE_USER_SKELETON, G_IMPLEMENT_INTERFACE (ACCOUNTS_TYPE_USER, user_accounts_user_iface_init));
44ca26
 
44ca26
 static gint
44ca26
 account_type_from_pwent (struct passwd *pwent)
44ca26
 {
44ca26
         struct group *grp;
44ca26
         gint i;
44ca26
 
44ca26
         if (pwent->pw_uid == 0) {
44ca26
                 g_debug ("user is root so account type is administrator");
44ca26
                 return ACCOUNT_TYPE_ADMINISTRATOR;
44ca26
         }
44ca26
 
44ca26
         grp = getgrnam (ADMIN_GROUP);
44ca26
         if (grp == NULL) {
44ca26
                 g_debug (ADMIN_GROUP " group not found");
44ca26
                 return ACCOUNT_TYPE_STANDARD;
44ca26
         }
44ca26
 
44ca26
         for (i = 0; grp->gr_mem[i] != NULL; i++) {
44ca26
                 if (g_strcmp0 (grp->gr_mem[i], pwent->pw_name) == 0) {
44ca26
                         return ACCOUNT_TYPE_ADMINISTRATOR;
44ca26
                 }
44ca26
         }
44ca26
 
44ca26
         return ACCOUNT_TYPE_STANDARD;
44ca26
 }
44ca26
 
44ca26
 void
44ca26
 user_update_from_pwent (User          *user,
44ca26
-                        struct passwd *pwent)
44ca26
+                        struct passwd *pwent,
44ca26
+                        struct spwd   *spent)
44ca26
 {
44ca26
-#ifdef HAVE_SHADOW_H
44ca26
-        struct spwd *spent;
44ca26
-#endif
44ca26
         gchar *real_name;
44ca26
         gboolean changed;
44ca26
         const gchar *passwd;
44ca26
         gboolean locked;
44ca26
         PasswordMode mode;
44ca26
         AccountType account_type;
44ca26
 
44ca26
         g_object_freeze_notify (G_OBJECT (user));
44ca26
 
44ca26
         changed = FALSE;
44ca26
 
44ca26
         if (pwent->pw_gecos && pwent->pw_gecos[0] != '\0') {
44ca26
                 gchar *first_comma = NULL;
44ca26
                 gchar *valid_utf8_name = NULL;
44ca26
 
44ca26
                 if (g_utf8_validate (pwent->pw_gecos, -1, NULL)) {
44ca26
                         valid_utf8_name = pwent->pw_gecos;
44ca26
                         first_comma = g_utf8_strchr (valid_utf8_name, -1, ',');
44ca26
                 }
44ca26
                 else {
44ca26
                         g_warning ("User %s has invalid UTF-8 in GECOS field. "
44ca26
                                    "It would be a good thing to check /etc/passwd.",
44ca26
                                    pwent->pw_name ? pwent->pw_name : "");
44ca26
                 }
44ca26
 
44ca26
                 if (first_comma) {
44ca26
                         real_name = g_strndup (valid_utf8_name,
44ca26
                                                   (first_comma - valid_utf8_name));
44ca26
                 }
44ca26
                 else if (valid_utf8_name) {
44ca26
@@ -219,93 +217,88 @@ user_update_from_pwent (User          *user,
44ca26
                 g_object_notify (G_OBJECT (user), "account-type");
44ca26
         }
44ca26
 
44ca26
         /* Username */
44ca26
         if (g_strcmp0 (user->user_name, pwent->pw_name) != 0) {
44ca26
                 g_free (user->user_name);
44ca26
                 user->user_name = g_strdup (pwent->pw_name);
44ca26
                 changed = TRUE;
44ca26
                 g_object_notify (G_OBJECT (user), "user-name");
44ca26
         }
44ca26
 
44ca26
         /* Home Directory */
44ca26
         if (g_strcmp0 (user->home_dir, pwent->pw_dir) != 0) {
44ca26
                 g_free (user->home_dir);
44ca26
                 user->home_dir = g_strdup (pwent->pw_dir);
44ca26
                 g_free (user->default_icon_file);
44ca26
                 user->default_icon_file = g_build_filename (user->home_dir, ".face", NULL);
44ca26
                 changed = TRUE;
44ca26
                 g_object_notify (G_OBJECT (user), "home-directory");
44ca26
         }
44ca26
 
44ca26
         /* Shell */
44ca26
         if (g_strcmp0 (user->shell, pwent->pw_shell) != 0) {
44ca26
                 g_free (user->shell);
44ca26
                 user->shell = g_strdup (pwent->pw_shell);
44ca26
                 changed = TRUE;
44ca26
                 g_object_notify (G_OBJECT (user), "shell");
44ca26
         }
44ca26
 
44ca26
         passwd = NULL;
44ca26
-#ifdef HAVE_SHADOW_H
44ca26
-        spent = getspnam (pwent->pw_name);
44ca26
         if (spent)
44ca26
                 passwd = spent->sp_pwdp;
44ca26
-#endif
44ca26
 
44ca26
         if (passwd && passwd[0] == '!') {
44ca26
                 locked = TRUE;
44ca26
         }
44ca26
         else {
44ca26
                 locked = FALSE;
44ca26
         }
44ca26
 
44ca26
         if (user->locked != locked) {
44ca26
                 user->locked = locked;
44ca26
                 changed = TRUE;
44ca26
                 g_object_notify (G_OBJECT (user), "locked");
44ca26
         }
44ca26
 
44ca26
         if (passwd == NULL || passwd[0] != 0) {
44ca26
                 mode = PASSWORD_MODE_REGULAR;
44ca26
         }
44ca26
         else {
44ca26
                 mode = PASSWORD_MODE_NONE;
44ca26
         }
44ca26
 
44ca26
-#ifdef HAVE_SHADOW_H
44ca26
         if (spent) {
44ca26
                 if (spent->sp_lstchg == 0) {
44ca26
                         mode = PASSWORD_MODE_SET_AT_LOGIN;
44ca26
                 }
44ca26
         }
44ca26
-#endif
44ca26
 
44ca26
         if (user->password_mode != mode) {
44ca26
                 user->password_mode = mode;
44ca26
                 changed = TRUE;
44ca26
                 g_object_notify (G_OBJECT (user), "password-mode");
44ca26
         }
44ca26
 
44ca26
         user->system_account = !user_classify_is_human (user->uid, user->user_name, pwent->pw_shell, passwd);
44ca26
 
44ca26
         g_object_thaw_notify (G_OBJECT (user));
44ca26
 
44ca26
         if (changed)
44ca26
                 accounts_user_emit_changed (ACCOUNTS_USER (user));
44ca26
 }
44ca26
 
44ca26
 void
44ca26
 user_update_from_keyfile (User     *user,
44ca26
                           GKeyFile *keyfile)
44ca26
 {
44ca26
         gchar *s;
44ca26
 
44ca26
         g_object_freeze_notify (G_OBJECT (user));
44ca26
 
44ca26
         s = g_key_file_get_string (keyfile, "User", "Language", NULL);
44ca26
         if (s != NULL) {
44ca26
                 /* TODO: validate / normalize */
44ca26
                 g_free (user->language);
44ca26
                 user->language = s;
44ca26
                 g_object_notify (G_OBJECT (user), "language");
44ca26
         }
44ca26
diff --git a/src/user.h b/src/user.h
44ca26
index 0848b50..22548f9 100644
44ca26
--- a/src/user.h
44ca26
+++ b/src/user.h
44ca26
@@ -1,80 +1,82 @@
44ca26
 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
44ca26
  *
44ca26
  * Copyright (C) 2009-2010 Red Hat, Inc.
44ca26
  *
44ca26
  * This program is free software; you can redistribute it and/or modify
44ca26
  * it under the terms of the GNU General Public License as published by
44ca26
  * the Free Software Foundation; either version 3 of the License, or
44ca26
  * (at your option) any later version.
44ca26
  *
44ca26
  * This program is distributed in the hope that it will be useful,
44ca26
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
44ca26
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
44ca26
  * GNU General Public License for more details.
44ca26
  *
44ca26
  * You should have received a copy of the GNU General Public License
44ca26
  * along with this program; if not, write to the Free Software
44ca26
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
44ca26
  */
44ca26
 
44ca26
 #ifndef __USER__
44ca26
 #define __USER__
44ca26
 
44ca26
 #include <sys/types.h>
44ca26
 #include <pwd.h>
44ca26
+#include <shadow.h>
44ca26
 
44ca26
 #include <glib.h>
44ca26
 #include <gio/gio.h>
44ca26
 
44ca26
 #include "types.h"
44ca26
 
44ca26
 G_BEGIN_DECLS
44ca26
 
44ca26
 #define TYPE_USER (user_get_type ())
44ca26
 #define USER(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), TYPE_USER, User))
44ca26
 #define IS_USER(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), TYPE_USER))
44ca26
 
44ca26
 typedef enum {
44ca26
         ACCOUNT_TYPE_STANDARD,
44ca26
         ACCOUNT_TYPE_ADMINISTRATOR,
44ca26
 #define ACCOUNT_TYPE_LAST ACCOUNT_TYPE_ADMINISTRATOR
44ca26
 } AccountType;
44ca26
 
44ca26
 typedef enum {
44ca26
         PASSWORD_MODE_REGULAR,
44ca26
         PASSWORD_MODE_SET_AT_LOGIN,
44ca26
         PASSWORD_MODE_NONE,
44ca26
 #define PASSWORD_MODE_LAST PASSWORD_MODE_NONE
44ca26
 } PasswordMode;
44ca26
 
44ca26
 /* local methods */
44ca26
 
44ca26
 GType          user_get_type                (void) G_GNUC_CONST;
44ca26
 User *         user_new                     (Daemon        *daemon,
44ca26
                                              uid_t          uid);
44ca26
 
44ca26
 void           user_update_from_pwent       (User          *user,
44ca26
-                                             struct passwd *pwent);
44ca26
+                                             struct passwd *pwent,
44ca26
+                                             struct spwd   *spent);
44ca26
 void           user_update_from_keyfile     (User          *user,
44ca26
                                              GKeyFile      *keyfile);
44ca26
 void           user_update_local_account_property (User          *user,
44ca26
                                                    gboolean       local);
44ca26
 void           user_update_system_account_property (User          *user,
44ca26
                                                     gboolean       system);
44ca26
 
44ca26
 void           user_register                (User          *user);
44ca26
 void           user_unregister              (User          *user);
44ca26
 void           user_changed                 (User          *user);
44ca26
 
44ca26
 void           user_save                    (User          *user);
44ca26
 
44ca26
 const gchar *  user_get_user_name           (User          *user);
44ca26
 gboolean       user_get_system_account      (User          *user);
44ca26
 gboolean       user_get_local_account       (User          *user);
44ca26
 const gchar *  user_get_object_path         (User          *user);
44ca26
 uid_t          user_get_uid                 (User          *user);
44ca26
 const gchar *  user_get_shell               (User          *user);
44ca26
 
44ca26
 G_END_DECLS
44ca26
 
44ca26
 #endif
44ca26
-- 
44ca26
2.7.4
44ca26
44ca26
44ca26
From 2accf123c55f3c6a9596e9fc2d614fcb07c88559 Mon Sep 17 00:00:00 2001
44ca26
From: Ray Strode <rstrode@redhat.com>
44ca26
Date: Wed, 29 Jun 2016 16:22:42 -0400
44ca26
Subject: [PATCH 4/5] daemon: constrain max local users to 50
44ca26
44ca26
Systems with tens of thousands of users don't want all those users
44ca26
showing up in the user list.
44ca26
44ca26
Set a cap at an even 50, which should cover the lion's share of use
44ca26
cases well. Of course, if a user not in the list explicitly
44ca26
logs in (from Not Listed? or whatever) they get added to the list.
44ca26
44ca26
https://bugs.freedesktop.org/show_bug.cgi?id=48177
44ca26
---
44ca26
 src/daemon.c | 19 +++++++++++++------
44ca26
 1 file changed, 13 insertions(+), 6 deletions(-)
44ca26
44ca26
diff --git a/src/daemon.c b/src/daemon.c
44ca26
index 71a3ea4..cb586bb 100644
44ca26
--- a/src/daemon.c
44ca26
+++ b/src/daemon.c
44ca26
@@ -279,60 +279,64 @@ entry_generator_wtmp (GHashTable   *users,
44ca26
                         continue;
44ca26
                 }
44ca26
 
44ca26
                 g_object_set (user, "login-frequency", accounting->frequency, NULL);
44ca26
                 g_object_set (user, "login-time", accounting->time, NULL);
44ca26
 
44ca26
                 builder = g_variant_builder_new (G_VARIANT_TYPE ("a(xxa{sv})"));
44ca26
                 for (l = g_list_last (accounting->previous_logins); l != NULL; l = l->prev) {
44ca26
                         previous_login = l->data;
44ca26
 
44ca26
                         builder2 = g_variant_builder_new (G_VARIANT_TYPE ("a{sv}"));
44ca26
                         g_variant_builder_add (builder2, "{sv}", "type", g_variant_new_string (previous_login->id));
44ca26
                         g_variant_builder_add (builder, "(xxa{sv})", previous_login->login_time, previous_login->logout_time, builder2);
44ca26
                         g_variant_builder_unref (builder2);
44ca26
                 }
44ca26
                 g_object_set (user, "login-history", g_variant_new ("a(xxa{sv})", builder), NULL);
44ca26
                 g_variant_builder_unref (builder);
44ca26
                 g_list_free_full (accounting->previous_logins, (GDestroyNotify) user_previous_login_free);
44ca26
 
44ca26
                 user_changed (user);
44ca26
         }
44ca26
 
44ca26
         g_hash_table_unref (login_hash);
44ca26
         g_hash_table_unref (logout_hash);
44ca26
         g_free (state_data);
44ca26
         *state = NULL;
44ca26
         return NULL;
44ca26
 }
44ca26
 #endif /* HAVE_UTMPX_H */
44ca26
 
44ca26
+#ifndef MAX_LOCAL_USERS
44ca26
+#define MAX_LOCAL_USERS 50
44ca26
+#endif
44ca26
+
44ca26
 static struct passwd *
44ca26
 entry_generator_fgetpwent (GHashTable   *users,
44ca26
                            gpointer     *state,
44ca26
                            struct spwd **spent)
44ca26
 {
44ca26
         struct passwd *pwent;
44ca26
 
44ca26
         struct {
44ca26
                 struct spwd spbuf;
44ca26
                 char buf[1024];
44ca26
         } *shadow_entry_buffers;
44ca26
 
44ca26
         struct {
44ca26
                 FILE *fp;
44ca26
                 GHashTable *users;
44ca26
         } *generator_state;
44ca26
 
44ca26
         /* First iteration */
44ca26
         if (*state == NULL) {
44ca26
                 GHashTable *shadow_users = NULL;
44ca26
                 FILE *fp;
44ca26
                 struct spwd *shadow_entry;
44ca26
 
44ca26
                 fp = fopen (PATH_SHADOW, "r");
44ca26
                 if (fp == NULL) {
44ca26
                         g_warning ("Unable to open %s: %s", PATH_SHADOW, g_strerror (errno));
44ca26
                         return NULL;
44ca26
                 }
44ca26
 
44ca26
                 shadow_users = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
44ca26
@@ -350,67 +354,70 @@ entry_generator_fgetpwent (GHashTable   *users,
44ca26
 
44ca26
                                 if (errno != EINTR) {
44ca26
                                         break;
44ca26
                                 }
44ca26
                         }
44ca26
                 } while (shadow_entry != NULL);
44ca26
 
44ca26
                 fclose (fp);
44ca26
 
44ca26
                 if (g_hash_table_size (shadow_users) == 0) {
44ca26
                         g_clear_pointer (&shadow_users, g_hash_table_unref);
44ca26
                         return NULL;
44ca26
                 }
44ca26
 
44ca26
                 fp = fopen (PATH_PASSWD, "r");
44ca26
                 if (fp == NULL) {
44ca26
                         g_clear_pointer (&shadow_users, g_hash_table_unref);
44ca26
                         g_warning ("Unable to open %s: %s", PATH_PASSWD, g_strerror (errno));
44ca26
                         return NULL;
44ca26
                 }
44ca26
 
44ca26
                 generator_state = g_malloc0 (sizeof (*generator_state));
44ca26
                 generator_state->fp = fp;
44ca26
                 generator_state->users = shadow_users;
44ca26
 
44ca26
                 *state = generator_state;
44ca26
         }
44ca26
 
44ca26
         /* Every iteration */
44ca26
         generator_state = *state;
44ca26
-        pwent = fgetpwent (generator_state->fp);
44ca26
-        if (pwent != NULL) {
44ca26
-                shadow_entry_buffers = g_hash_table_lookup (generator_state->users, pwent->pw_name);
44ca26
 
44ca26
-                if (shadow_entry_buffers != NULL) {
44ca26
-                        *spent = &shadow_entry_buffers->spbuf;
44ca26
-                        return pwent;
44ca26
+        if (g_hash_table_size (users) < MAX_LOCAL_USERS) {
44ca26
+                pwent = fgetpwent (generator_state->fp);
44ca26
+                if (pwent != NULL) {
44ca26
+                        shadow_entry_buffers = g_hash_table_lookup (generator_state->users, pwent->pw_name);
44ca26
+
44ca26
+                        if (shadow_entry_buffers != NULL) {
44ca26
+                            *spent = &shadow_entry_buffers->spbuf;
44ca26
+                            return pwent;
44ca26
+                        }
44ca26
                 }
44ca26
         }
44ca26
 
44ca26
         /* Last iteration */
44ca26
         fclose (generator_state->fp);
44ca26
         g_hash_table_unref (generator_state->users);
44ca26
         g_free (generator_state);
44ca26
         *state = NULL;
44ca26
 
44ca26
         return NULL;
44ca26
 }
44ca26
 
44ca26
 static struct passwd *
44ca26
 entry_generator_cachedir (GHashTable   *users,
44ca26
                           gpointer     *state,
44ca26
                           struct spwd **shadow_entry)
44ca26
 {
44ca26
         struct passwd *pwent;
44ca26
         const gchar *name;
44ca26
         GError *error = NULL;
44ca26
         gchar *filename;
44ca26
         gboolean regular;
44ca26
         GHashTableIter iter;
44ca26
         GKeyFile *key_file;
44ca26
         User *user;
44ca26
         GDir *dir;
44ca26
 
44ca26
         /* First iteration */
44ca26
         if (*state == NULL) {
44ca26
                 *state = g_dir_open (USERDIR, 0, &error);
44ca26
-- 
44ca26
2.7.4
44ca26
44ca26
44ca26
From ed58ad3210010a09b6f114b4d392afb66ad0bbfa Mon Sep 17 00:00:00 2001
44ca26
From: Ray Strode <rstrode@redhat.com>
44ca26
Date: Wed, 29 Jun 2016 16:32:17 -0400
44ca26
Subject: [PATCH 5/5] daemon: don't source user list from wtmp
44ca26
44ca26
wtmp can get rather large on some systems from ssh logins.
44ca26
Furthermore it's pretty much completely redundant given the user
44ca26
cache in /var/lib/AccountService
44ca26
44ca26
This commit changes the wtmp code to only get used for maintaining
44ca26
login frequency and accounting, not for generating new users.
44ca26
44ca26
https://bugs.freedesktop.org/show_bug.cgi?id=48177
44ca26
---
44ca26
 src/daemon.c | 42 ++++++++++++------------------------------
44ca26
 1 file changed, 12 insertions(+), 30 deletions(-)
44ca26
44ca26
diff --git a/src/daemon.c b/src/daemon.c
44ca26
index cb586bb..815e2c9 100644
44ca26
--- a/src/daemon.c
44ca26
+++ b/src/daemon.c
44ca26
@@ -137,95 +137,83 @@ error_get_type (void)
44ca26
     }
44ca26
   return etype;
44ca26
 }
44ca26
 
44ca26
 #ifdef HAVE_UTMPX_H
44ca26
 
44ca26
 typedef struct {
44ca26
         guint64 frequency;
44ca26
         gint64 time;
44ca26
         GList *previous_logins;
44ca26
 } UserAccounting;
44ca26
 
44ca26
 typedef struct {
44ca26
         gchar  *id;
44ca26
         gint64  login_time;
44ca26
         gint64  logout_time;
44ca26
 } UserPreviousLogin;
44ca26
 
44ca26
 typedef struct {
44ca26
         GHashTable *login_hash;
44ca26
         GHashTable *logout_hash;
44ca26
 } WTmpGeneratorState;
44ca26
 
44ca26
 static void
44ca26
 user_previous_login_free (UserPreviousLogin *previous_login)
44ca26
 {
44ca26
         g_free (previous_login->id);
44ca26
         g_free (previous_login);
44ca26
 }
44ca26
 
44ca26
-static struct passwd *
44ca26
-entry_generator_wtmp (GHashTable   *users,
44ca26
-                      gpointer     *state,
44ca26
-                      struct spwd **spent)
44ca26
+static void
44ca26
+wtmp_update_login_frequencies (GHashTable *users)
44ca26
 {
44ca26
         GHashTable *login_hash, *logout_hash;
44ca26
         struct utmpx *wtmp_entry;
44ca26
         GHashTableIter iter;
44ca26
         gpointer key, value;
44ca26
         struct passwd *pwent;
44ca26
         User *user;
44ca26
-        WTmpGeneratorState *state_data;
44ca26
         GVariantBuilder *builder, *builder2;
44ca26
         GList *l;
44ca26
 
44ca26
-        if (*state == NULL) {
44ca26
-                /* First iteration */
44ca26
 #ifdef UTXDB_LOG
44ca26
-                if (setutxdb (UTXDB_LOG, NULL) != 0) {
44ca26
-                        return NULL;
44ca26
-                }
44ca26
+        if (setutxdb (UTXDB_LOG, NULL) != 0) {
44ca26
+                return NULL;
44ca26
+        }
44ca26
 #else
44ca26
-                utmpxname (PATH_WTMP);
44ca26
-                setutxent ();
44ca26
+        utmpxname (PATH_WTMP);
44ca26
+        setutxent ();
44ca26
 #endif
44ca26
-                *state = g_new (WTmpGeneratorState, 1);
44ca26
-                state_data = *state;
44ca26
-                state_data->login_hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
44ca26
-                state_data->logout_hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
44ca26
-        }
44ca26
 
44ca26
-        /* Every iteration */
44ca26
-        state_data = *state;
44ca26
-        login_hash = state_data->login_hash;
44ca26
-        logout_hash = state_data->logout_hash;
44ca26
+        login_hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
44ca26
+        logout_hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
44ca26
         while ((wtmp_entry = getutxent ())) {
44ca26
                 UserAccounting    *accounting;
44ca26
                 UserPreviousLogin *previous_login;
44ca26
 
44ca26
                 if (wtmp_entry->ut_type == BOOT_TIME) {
44ca26
                         /* Set boot time for missing logout records */
44ca26
                         g_hash_table_iter_init (&iter, logout_hash);
44ca26
                         while (g_hash_table_iter_next (&iter, &key, &value)) {
44ca26
                                 previous_login = (UserPreviousLogin *) value;
44ca26
 
44ca26
                                 if (previous_login->logout_time == 0) {
44ca26
                                         previous_login->logout_time = wtmp_entry->ut_tv.tv_sec;
44ca26
                                 }
44ca26
                         }
44ca26
                         g_hash_table_remove_all (logout_hash);
44ca26
                 } else if (wtmp_entry->ut_type == DEAD_PROCESS) {
44ca26
                         /* Save corresponding logout time */
44ca26
                         if (g_hash_table_lookup_extended (logout_hash, wtmp_entry->ut_line, &key, &value)) {
44ca26
                                 previous_login = (UserPreviousLogin *) value;
44ca26
                                 previous_login->logout_time = wtmp_entry->ut_tv.tv_sec;
44ca26
 
44ca26
                                 g_hash_table_remove (logout_hash, previous_login->id);
44ca26
                         }
44ca26
                 }
44ca26
 
44ca26
                 if (wtmp_entry->ut_type != USER_PROCESS) {
44ca26
                         continue;
44ca26
                 }
44ca26
 
44ca26
                 if (wtmp_entry->ut_user[0] == 0) {
44ca26
@@ -233,103 +221,96 @@ entry_generator_wtmp (GHashTable   *users,
44ca26
                 }
44ca26
 
44ca26
                 pwent = getpwnam (wtmp_entry->ut_user);
44ca26
                 if (pwent == NULL) {
44ca26
                         continue;
44ca26
                 }
44ca26
 
44ca26
                 if (!g_hash_table_lookup_extended (login_hash,
44ca26
                                                    wtmp_entry->ut_user,
44ca26
                                                    &key, &value)) {
44ca26
                         accounting = g_new (UserAccounting, 1);
44ca26
                         accounting->frequency = 0;
44ca26
                         accounting->previous_logins = NULL;
44ca26
 
44ca26
                         g_hash_table_insert (login_hash, g_strdup (wtmp_entry->ut_user), accounting);
44ca26
                 } else {
44ca26
                         accounting = value;
44ca26
                 }
44ca26
 
44ca26
                 accounting->frequency++;
44ca26
                 accounting->time = wtmp_entry->ut_tv.tv_sec;
44ca26
 
44ca26
                 /* Add zero logout time to change it later on logout record */
44ca26
                 previous_login = g_new (UserPreviousLogin, 1);
44ca26
                 previous_login->id = g_strdup (wtmp_entry->ut_line);
44ca26
                 previous_login->login_time = wtmp_entry->ut_tv.tv_sec;
44ca26
                 previous_login->logout_time = 0;
44ca26
                 accounting->previous_logins = g_list_prepend (accounting->previous_logins, previous_login);
44ca26
 
44ca26
                 g_hash_table_insert (logout_hash, g_strdup (wtmp_entry->ut_line), previous_login);
44ca26
-                *spent = getspnam (pwent->pw_name);
44ca26
-
44ca26
-                return pwent;
44ca26
         }
44ca26
 
44ca26
-        /* Last iteration */
44ca26
         endutxent ();
44ca26
 
44ca26
         g_hash_table_iter_init (&iter, login_hash);
44ca26
         while (g_hash_table_iter_next (&iter, &key, &value)) {
44ca26
                 UserAccounting    *accounting = (UserAccounting *) value;
44ca26
                 UserPreviousLogin *previous_login;
44ca26
 
44ca26
                 user = g_hash_table_lookup (users, key);
44ca26
                 if (user == NULL) {
44ca26
                         g_list_free_full (accounting->previous_logins, (GDestroyNotify) user_previous_login_free);
44ca26
                         continue;
44ca26
                 }
44ca26
 
44ca26
                 g_object_set (user, "login-frequency", accounting->frequency, NULL);
44ca26
                 g_object_set (user, "login-time", accounting->time, NULL);
44ca26
 
44ca26
                 builder = g_variant_builder_new (G_VARIANT_TYPE ("a(xxa{sv})"));
44ca26
                 for (l = g_list_last (accounting->previous_logins); l != NULL; l = l->prev) {
44ca26
                         previous_login = l->data;
44ca26
 
44ca26
                         builder2 = g_variant_builder_new (G_VARIANT_TYPE ("a{sv}"));
44ca26
                         g_variant_builder_add (builder2, "{sv}", "type", g_variant_new_string (previous_login->id));
44ca26
                         g_variant_builder_add (builder, "(xxa{sv})", previous_login->login_time, previous_login->logout_time, builder2);
44ca26
                         g_variant_builder_unref (builder2);
44ca26
                 }
44ca26
                 g_object_set (user, "login-history", g_variant_new ("a(xxa{sv})", builder), NULL);
44ca26
                 g_variant_builder_unref (builder);
44ca26
                 g_list_free_full (accounting->previous_logins, (GDestroyNotify) user_previous_login_free);
44ca26
 
44ca26
                 user_changed (user);
44ca26
         }
44ca26
 
44ca26
         g_hash_table_unref (login_hash);
44ca26
         g_hash_table_unref (logout_hash);
44ca26
-        g_free (state_data);
44ca26
-        *state = NULL;
44ca26
-        return NULL;
44ca26
 }
44ca26
 #endif /* HAVE_UTMPX_H */
44ca26
 
44ca26
 #ifndef MAX_LOCAL_USERS
44ca26
 #define MAX_LOCAL_USERS 50
44ca26
 #endif
44ca26
 
44ca26
 static struct passwd *
44ca26
 entry_generator_fgetpwent (GHashTable   *users,
44ca26
                            gpointer     *state,
44ca26
                            struct spwd **spent)
44ca26
 {
44ca26
         struct passwd *pwent;
44ca26
 
44ca26
         struct {
44ca26
                 struct spwd spbuf;
44ca26
                 char buf[1024];
44ca26
         } *shadow_entry_buffers;
44ca26
 
44ca26
         struct {
44ca26
                 FILE *fp;
44ca26
                 GHashTable *users;
44ca26
         } *generator_state;
44ca26
 
44ca26
         /* First iteration */
44ca26
         if (*state == NULL) {
44ca26
                 GHashTable *shadow_users = NULL;
44ca26
                 FILE *fp;
44ca26
                 struct spwd *shadow_entry;
44ca26
 
44ca26
@@ -533,64 +514,65 @@ create_users_hash_table (void)
44ca26
                                       g_object_unref);
44ca26
 }
44ca26
 
44ca26
 static void
44ca26
 reload_users (Daemon *daemon)
44ca26
 {
44ca26
         GHashTable *users;
44ca26
         GHashTable *old_users;
44ca26
         GHashTable *local;
44ca26
         GHashTableIter iter;
44ca26
         gpointer name;
44ca26
         User *user;
44ca26
 
44ca26
         /* Track the users that we saw during our (re)load */
44ca26
         users = create_users_hash_table ();
44ca26
 
44ca26
         /*
44ca26
          * NOTE: As we load data from all the sources, notifies are
44ca26
          * frozen in load_entries() and then thawed as we process
44ca26
          * them below.
44ca26
          */
44ca26
 
44ca26
         /* Load the local users into our hash table */
44ca26
         load_entries (daemon, users, entry_generator_fgetpwent);
44ca26
         local = g_hash_table_new (g_str_hash, g_str_equal);
44ca26
         g_hash_table_iter_init (&iter, users);
44ca26
         while (g_hash_table_iter_next (&iter, &name, NULL))
44ca26
                 g_hash_table_add (local, name);
44ca26
 
44ca26
         /* Now add/update users from other sources, possibly non-local */
44ca26
+        load_entries (daemon, users, entry_generator_cachedir);
44ca26
+
44ca26
 #ifdef HAVE_UTMPX_H
44ca26
-        load_entries (daemon, users, entry_generator_wtmp);
44ca26
+        wtmp_update_login_frequencies (users);
44ca26
 #endif
44ca26
-        load_entries (daemon, users, entry_generator_cachedir);
44ca26
 
44ca26
         /* Mark which users are local, which are not */
44ca26
         g_hash_table_iter_init (&iter, users);
44ca26
         while (g_hash_table_iter_next (&iter, &name, (gpointer *)&user))
44ca26
                 user_update_local_account_property (user, g_hash_table_lookup (local, name) != NULL);
44ca26
 
44ca26
         g_hash_table_destroy (local);
44ca26
 
44ca26
         /* Swap out the users */
44ca26
         old_users = daemon->priv->users;
44ca26
         daemon->priv->users = users;
44ca26
 
44ca26
         /* Remove all the old users */
44ca26
         g_hash_table_iter_init (&iter, old_users);
44ca26
         while (g_hash_table_iter_next (&iter, &name, (gpointer *)&user)) {
44ca26
                 if (!g_hash_table_lookup (users, name)) {
44ca26
                         user_unregister (user);
44ca26
                         accounts_accounts_emit_user_deleted (ACCOUNTS_ACCOUNTS (daemon),
44ca26
                                                              user_get_object_path (user));
44ca26
                 }
44ca26
         }
44ca26
 
44ca26
         /* Register all the new users */
44ca26
         g_hash_table_iter_init (&iter, users);
44ca26
         while (g_hash_table_iter_next (&iter, &name, (gpointer *)&user)) {
44ca26
                 if (!g_hash_table_lookup (old_users, name)) {
44ca26
                         user_register (user);
44ca26
                         accounts_accounts_emit_user_added (ACCOUNTS_ACCOUNTS (daemon),
44ca26
                                                            user_get_object_path (user));
44ca26
                 }
44ca26
-- 
44ca26
2.7.4
44ca26