diff --git a/SOURCES/0001-configure-actually-define-HAVE_GETUSERSHELL.patch b/SOURCES/0001-configure-actually-define-HAVE_GETUSERSHELL.patch
new file mode 100644
index 0000000..2e39df9
--- /dev/null
+++ b/SOURCES/0001-configure-actually-define-HAVE_GETUSERSHELL.patch
@@ -0,0 +1,86 @@
+From 8e3bad7b7eb7212aa7554fa445b2e8e29daaacaa Mon Sep 17 00:00:00 2001
+From: Ray Strode <rstrode@redhat.com>
+Date: Tue, 6 Sep 2016 15:33:30 -0400
+Subject: [PATCH] configure: actually define HAVE_GETUSERSHELL
+
+currently we check for it but never actually set it, leading to
+dead code.
+
+This commit fixes that.
+---
+ configure.ac | 5 +++++
+ 1 file changed, 5 insertions(+)
+
+diff --git a/configure.ac b/configure.ac
+index eb5360e..54f4eb4 100644
+--- a/configure.ac
++++ b/configure.ac
+@@ -160,60 +160,65 @@ AC_CACHE_CHECK([for supported warning flags], accountsservice_cv_warn_cflags, [
+     ACCOUNTSSERVICE_CC_TRY_FLAG([$W], [WARN_CFLAGS="$WARN_CFLAGS $W"])
+   done
+ 
+   accountsservice_cv_warn_cflags=$WARN_CFLAGS
+   accountsservice_cv_warn_maybe=$MAYBE_WARN
+ 
+   AC_MSG_CHECKING([which warning flags were supported])
+ ])
+ 
+ WARN_CFLAGS="$accountsservice_cv_warn_cflags"
+ 
+ if test "$GCC" = "yes" -a "$set_more_warnings" != "no"; then
+   # Only add this when optimizing is enabled (default)
+   AC_MSG_CHECKING([whether optimization is enabled])
+   AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#if __OPTIMIZE__ == 0
+                                        #error No optimization
+                                        #endif
+                                      ]], [[]])],
+                     [has_optimization=yes],
+                     [has_optimization=no])
+   if test $has_optimization = yes; then
+     WARN_CFLAGS="$WARN_CFLAGS -Wp,-D_FORTIFY_SOURCE=2"
+   fi
+   AC_MSG_RESULT($has_optimization)
+ fi
+ 
+ AC_SUBST(WARN_CFLAGS)
+ 
+ AC_CHECK_HEADERS([shadow.h utmpx.h])
+ 
++AC_CHECK_LIB(c, getusershell, have_getusershell=yes, have_getusershell=no)
++if test x$have_getusershell = xyes; then
++  AC_DEFINE(HAVE_GETUSERSHELL, 1, [Define i getusershell() is available])
++fi
++
+ dnl ---------------------------------------------------------------------------
+ dnl - gtk-doc Documentation
+ dnl ---------------------------------------------------------------------------
+ 
+ m4_ifdef([GTK_DOC_CHECK], [
+ GTK_DOC_CHECK([1.15], [--flavour no-tmpl])
+ ],[
+ AM_CONDITIONAL([ENABLE_GTK_DOC],[false])
+ ])
+ 
+ dnl ---------------------------------------------------------------------------
+ dnl - DocBook Documentation
+ dnl ---------------------------------------------------------------------------
+ 
+ AC_ARG_ENABLE(docbook-docs,
+         [AS_HELP_STRING([--enable-docbook-docs],[build documentation (requires xmlto)])],
+         enable_docbook_docs=$enableval,enable_docbook_docs=no)
+ AC_PATH_PROG(XMLTO, xmlto, no)
+ AC_MSG_CHECKING([whether to build DocBook documentation])
+ if test x$XMLTO = xno ; then
+         have_docbook=no
+ else
+         have_docbook=yes
+ fi
+ if test x$enable_docbook_docs = xauto ; then
+         if test x$have_docbook = xno ; then
+                 enable_docbook_docs=no
+         else
+                 enable_docbook_docs=yes
+         fi
+-- 
+2.7.4
+
diff --git a/SOURCES/0001-user-classify-exclude-nologin-users.patch b/SOURCES/0001-user-classify-exclude-nologin-users.patch
new file mode 100644
index 0000000..40f2963
--- /dev/null
+++ b/SOURCES/0001-user-classify-exclude-nologin-users.patch
@@ -0,0 +1,231 @@
+From 450731558cbd5c77aa6932d35f27abf898553db6 Mon Sep 17 00:00:00 2001
+From: Ray Strode <rstrode@redhat.com>
+Date: Tue, 6 Sep 2016 13:54:48 -0400
+Subject: [PATCH] user-classify: exclude nologin users
+
+Sometimes admins set a user's shell to nologin to hide it from
+the user list.  This commit fixes accountsservice so that behavior
+works again.
+---
+ src/user-classify.c | 77 +++++++++++++++++++++++++++++------------------------
+ 1 file changed, 42 insertions(+), 35 deletions(-)
+
+diff --git a/src/user-classify.c b/src/user-classify.c
+index 69e6809..b79a35f 100644
+--- a/src/user-classify.c
++++ b/src/user-classify.c
+@@ -1,169 +1,176 @@
+ /*
+  * Copyright (C) 2009-2010 Red Hat, Inc.
+  * Copyright (C) 2013 Canonical Limited
+  *
+  * This program is free software; you can redistribute it and/or modify
+  * it under the terms of the GNU General Public License as published by
+  * the Free Software Foundation; either version 3 of the licence, or
+  * (at your option) any later version.
+  *
+  * This program is distributed in the hope that it will be useful,
+  * but WITHOUT ANY WARRANTY; without even the implied warranty of
+  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  * GNU General Public License for more details.
+  *
+  * You should have received a copy of the GNU General Public License
+  * along with this program; if not, write to the Free Software
+  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+  *
+  * Authors: Ryan Lortie <desrt@desrt.ca>
+  *          Matthias Clasen <mclasen@redhat.com>
+  */
+ 
+ #include "config.h"
+ 
+ #include "user-classify.h"
+ 
+ #include <string.h>
++#include <unistd.h>
+ 
+ static const char *default_excludes[] = {
+         "bin",
+         "root",
+         "daemon",
+         "adm",
+         "lp",
+         "sync",
+         "shutdown",
+         "halt",
+         "mail",
+         "news",
+         "uucp",
+         "operator",
+         "nobody",
+         "nobody4",
+         "noaccess",
+         "postgres",
+         "pvm",
+         "rpm",
+         "nfsnobody",
+         "pcap",
+         "mysql",
+         "ftp",
+         "games",
+         "man",
+         "at",
+         "gdm",
+         "gnome-initial-setup"
+ };
+ 
+ static gboolean
+ user_classify_is_blacklisted (const char *username)
+ {
+         static GHashTable *exclusions;
+ 
+         if (exclusions == NULL) {
+                 guint i;
+ 
+                 exclusions = g_hash_table_new (g_str_hash, g_str_equal);
+ 
+                 for (i = 0; i < G_N_ELEMENTS (default_excludes); i++) {
+                         g_hash_table_add (exclusions, (gpointer) default_excludes[i]);
+                 }
+         }
+ 
+         if (g_hash_table_contains (exclusions, username)) {
+                 return TRUE;
+         }
+ 
+         return FALSE;
+ }
+ 
+ #define PATH_NOLOGIN "/sbin/nologin"
+ #define PATH_FALSE "/bin/false"
+ 
+ #ifdef ENABLE_USER_HEURISTICS
+ static gboolean
+ user_classify_is_excluded_by_heuristics (const gchar *username,
+-                                         const gchar *shell,
+                                          const gchar *password_hash)
+ {
+         gboolean ret = FALSE;
+ 
+-        if (shell != NULL) {
+-                char *basename, *nologin_basename, *false_basename;
+-
+-#ifdef HAVE_GETUSERSHELL
+-                char *valid_shell;
+-
+-                ret = TRUE;
+-                setusershell ();
+-                while ((valid_shell = getusershell ()) != NULL) {
+-                        if (g_strcmp0 (shell, valid_shell) != 0)
+-                                continue;
+-                        ret = FALSE;
+-                }
+-                endusershell ();
+-#endif
+-
+-                basename = g_path_get_basename (shell);
+-                nologin_basename = g_path_get_basename (PATH_NOLOGIN);
+-                false_basename = g_path_get_basename (PATH_FALSE);
+-
+-                if (shell[0] == '\0') {
+-                        ret = TRUE;
+-                } else if (g_strcmp0 (basename, nologin_basename) == 0) {
+-                        ret = TRUE;
+-                } else if (g_strcmp0 (basename, false_basename) == 0) {
+-                        ret = TRUE;
+-                }
+-
+-                g_free (basename);
+-                g_free (nologin_basename);
+-                g_free (false_basename);
+-        }
+-
+         if (password_hash != NULL) {
+                 /* skip over the account-is-locked '!' prefix if present */
+                 if (password_hash[0] == '!')
+                     password_hash++;
+ 
+                 if (password_hash[0] != '\0') {
+                         /* modern hashes start with "$n$" */
+                         if (password_hash[0] == '$') {
+                                 if (strlen (password_hash) < 4)
+                                     ret = TRUE;
+ 
+                         /* DES crypt is base64 encoded [./A-Za-z0-9]*
+                          */
+                         } else if (!g_ascii_isalnum (password_hash[0]) &&
+                                    password_hash[0] != '.' &&
+                                    password_hash[0] != '/') {
+                                 ret = TRUE;
+                         }
+                 }
+ 
+         }
+ 
+         return ret;
+ }
+ #endif /* ENABLE_USER_HEURISTICS */
+ 
++static gboolean
++is_invalid_shell (const char *shell)
++{
++        char *basename, *nologin_basename, *false_basename;
++        int ret = FALSE;
++
++#ifdef HAVE_GETUSERSHELL
++        char *valid_shell;
++
++        setusershell ();
++        while ((valid_shell = getusershell ()) != NULL) {
++                if (g_strcmp0 (shell, valid_shell) != 0)
++                        continue;
++                ret = FALSE;
++        }
++        endusershell ();
++#endif
++
++        basename = g_path_get_basename (shell);
++        nologin_basename = g_path_get_basename (PATH_NOLOGIN);
++        false_basename = g_path_get_basename (PATH_FALSE);
++
++        if (shell[0] == '\0') {
++                ret = TRUE;
++        } else if (g_strcmp0 (basename, nologin_basename) == 0) {
++                ret = TRUE;
++        } else if (g_strcmp0 (basename, false_basename) == 0) {
++                ret = TRUE;
++        }
++
++        g_free (basename);
++        g_free (nologin_basename);
++        g_free (false_basename);
++
++        return ret;
++}
++
+ gboolean
+ user_classify_is_human (uid_t        uid,
+                         const gchar *username,
+                         const gchar *shell,
+                         const gchar *password_hash)
+ {
+         if (user_classify_is_blacklisted (username))
+                 return FALSE;
+ 
++        if (shell != NULL && is_invalid_shell (shell))
++                return FALSE;
++
+ #ifdef ENABLE_USER_HEURISTICS
+         /* only do heuristics on the range 500-1000 to catch one off migration problems in Fedora */
+         if (uid >= 500 && uid < MINIMUM_UID) {
+-                if (!user_classify_is_excluded_by_heuristics (username, shell, password_hash))
++                if (!user_classify_is_excluded_by_heuristics (username, password_hash))
+                         return TRUE;
+         }
+ #endif
+ 
+         return uid >= MINIMUM_UID;
+ }
+-- 
+2.7.4
+
diff --git a/SOURCES/fix-log-leak.patch b/SOURCES/fix-log-leak.patch
new file mode 100644
index 0000000..1e59714
--- /dev/null
+++ b/SOURCES/fix-log-leak.patch
@@ -0,0 +1,79 @@
+From 1ff7d912fe329867a40c8e473cf0bde8d55202ef Mon Sep 17 00:00:00 2001
+From: Ray Strode <rstrode@redhat.com>
+Date: Fri, 1 Jul 2016 08:13:09 -0400
+Subject: [PATCH] main: fix leak in log handler
+
+---
+ src/main.c | 2 ++
+ 1 file changed, 2 insertions(+)
+
+diff --git a/src/main.c b/src/main.c
+index 2f799e5..cc62e05 100644
+--- a/src/main.c
++++ b/src/main.c
+@@ -94,60 +94,62 @@ on_bus_acquired (GDBusConnection  *connection,
+ static void
+ on_name_lost (GDBusConnection  *connection,
+               const gchar      *name,
+               gpointer          user_data)
+ {
+         g_debug ("got NameLost, exiting");
+         g_main_loop_quit (loop);
+ }
+ 
+ static gboolean debug;
+ 
+ static void
+ on_log_debug (const gchar *log_domain,
+               GLogLevelFlags log_level,
+               const gchar *message,
+               gpointer user_data)
+ {
+         GString *string;
+         const gchar *progname;
+         int ret G_GNUC_UNUSED;
+ 
+         string = g_string_new (NULL);
+ 
+         progname = g_get_prgname ();
+         g_string_append_printf (string, "(%s:%lu): %s%sDEBUG: %s\n",
+                                 progname ? progname : "process", (gulong)getpid (),
+                                 log_domain ? log_domain : "", log_domain ? "-" : "",
+                                 message ? message : "(NULL) message");
+ 
+         ret = write (1, string->str, string->len);
++
++        g_string_free (string, TRUE);
+ }
+ 
+ static void
+ log_handler (const gchar   *domain,
+              GLogLevelFlags level,
+              const gchar   *message,
+              gpointer       data)
+ {
+         /* filter out DEBUG messages if debug isn't set */
+         if ((level & G_LOG_LEVEL_MASK) == G_LOG_LEVEL_DEBUG && !debug)
+                 return;
+ 
+         g_log_default_handler (domain, level, message, data);
+ }
+ 
+ static gboolean
+ on_signal_quit (gpointer data)
+ {
+         g_main_loop_quit (data);
+         return FALSE;
+ }
+ 
+ int
+ main (int argc, char *argv[])
+ {
+         GError *error;
+         gint ret;
+         GBusNameOwnerFlags flags;
+         GOptionContext *context;
+         static gboolean replace;
+-- 
+2.7.4
+
diff --git a/SOURCES/scale-better.patch b/SOURCES/scale-better.patch
new file mode 100644
index 0000000..98a0517
--- /dev/null
+++ b/SOURCES/scale-better.patch
@@ -0,0 +1,1667 @@
+From 2b23e50057fc92da30093fbebb78b320fc4107d0 Mon Sep 17 00:00:00 2001
+From: Ray Strode <rstrode@redhat.com>
+Date: Wed, 29 Jun 2016 10:50:37 -0400
+Subject: [PATCH 1/5] user: check if user is in wheel more efficiently
+
+We currently get all the groups a user belongs to in one pass,
+then check each one to see if it's wheel.
+
+It's much more efficient to just get the wheel group and check if
+any of its members are the user.
+
+https://bugs.freedesktop.org/show_bug.cgi?id=48177
+---
+ src/user.c | 13 ++-----------
+ 1 file changed, 2 insertions(+), 11 deletions(-)
+
+diff --git a/src/user.c b/src/user.c
+index de30090..52f57d0 100644
+--- a/src/user.c
++++ b/src/user.c
+@@ -92,88 +92,79 @@ struct User {
+         gchar        *home_dir;
+         gchar        *shell;
+         gchar        *email;
+         gchar        *language;
+         gchar        *x_session;
+         gchar        *location;
+         guint64       login_frequency;
+         gint64        login_time;
+         GVariant     *login_history;
+         gchar        *icon_file;
+         gchar        *default_icon_file;
+         gboolean      locked;
+         gboolean      automatic_login;
+         gboolean      system_account;
+         gboolean      local_account;
+ };
+ 
+ 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;
+-        gid_t wheel;
+-        gid_t *groups;
+-        gint ngroups;
+         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;
+         }
+-        wheel = grp->gr_gid;
+ 
+-        ngroups = get_user_groups (pwent->pw_name, pwent->pw_gid, &groups);
+-
+-        for (i = 0; i < ngroups; i++) {
+-                if (groups[i] == wheel) {
+-                        g_free (groups);
++        for (i = 0; grp->gr_mem[i] != NULL; i++) {
++                if (g_strcmp0 (grp->gr_mem[i], pwent->pw_name) == 0) {
+                         return ACCOUNT_TYPE_ADMINISTRATOR;
+                 }
+         }
+ 
+-        g_free (groups);
+-
+         return ACCOUNT_TYPE_STANDARD;
+ }
+ 
+ void
+ user_update_from_pwent (User          *user,
+                         struct passwd *pwent)
+ {
+ #ifdef HAVE_SHADOW_H
+         struct spwd *spent;
+ #endif
+         gchar *real_name;
+         gboolean changed;
+         const gchar *passwd;
+         gboolean locked;
+         PasswordMode mode;
+         AccountType account_type;
+ 
+         g_object_freeze_notify (G_OBJECT (user));
+ 
+         changed = FALSE;
+ 
+         if (pwent->pw_gecos && pwent->pw_gecos[0] != '\0') {
+                 gchar *first_comma = NULL;
+                 gchar *valid_utf8_name = NULL;
+ 
+                 if (g_utf8_validate (pwent->pw_gecos, -1, NULL)) {
+                         valid_utf8_name = pwent->pw_gecos;
+                         first_comma = g_utf8_strchr (valid_utf8_name, -1, ',');
+                 }
+                 else {
+-- 
+2.7.4
+
+
+From c2b87f89a85ffa5465a523aa291ea0018a050cc5 Mon Sep 17 00:00:00 2001
+From: Ray Strode <rstrode@redhat.com>
+Date: Tue, 28 Jun 2016 15:43:08 -0400
+Subject: [PATCH 2/5] daemon: get local users from /etc/shadow not /etc/passwd
+
+For some sites, it's common practice to rsync around large
+/etc/passwd files containing the password entries for remote
+users. That means accountsservices' "assume /etc/passwd is local
+users" heuristic falls over.
+
+This commit changes it to only treat users in /etc/shadow as local.
+
+https://bugs.freedesktop.org/show_bug.cgi?id=48177
+---
+ src/daemon.c | 59 +++++++++++++++++++++++++++++++++++++++++++++++++++++------
+ 1 file changed, 53 insertions(+), 6 deletions(-)
+
+diff --git a/src/daemon.c b/src/daemon.c
+index 38f6a47..5c269af 100644
+--- a/src/daemon.c
++++ b/src/daemon.c
+@@ -2,60 +2,63 @@
+  *
+  * Copyright (C) 2009-2010 Red Hat, Inc.
+  * Copyright (c) 2013 Canonical Limited
+  *
+  * This program is free software; you can redistribute it and/or modify
+  * it under the terms of the GNU General Public License as published by
+  * the Free Software Foundation; either version 3 of the License, or
+  * (at your option) any later version.
+  *
+  * This program is distributed in the hope that it will be useful,
+  * but WITHOUT ANY WARRANTY; without even the implied warranty of
+  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  * GNU General Public License for more details.
+  *
+  * You should have received a copy of the GNU General Public License
+  * along with this program; if not, write to the Free Software
+  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+  *
+  * Written by: Matthias Clasen <mclasen@redhat.com>
+  */
+ 
+ #include "config.h"
+ 
+ #include <stdlib.h>
+ #include <stdio.h>
+ #include <sys/types.h>
+ #include <sys/stat.h>
+ #include <fcntl.h>
+ #include <sys/wait.h>
+ #include <pwd.h>
++#ifdef HAVE_SHADOW_H
++#include <shadow.h>
++#endif
+ #include <unistd.h>
+ #include <errno.h>
+ #include <sys/types.h>
+ #ifdef HAVE_UTMPX_H
+ #include <utmpx.h>
+ #endif
+ 
+ #include <glib.h>
+ #include <glib/gi18n.h>
+ #include <glib-object.h>
+ #include <glib/gstdio.h>
+ #include <gio/gio.h>
+ #include <polkit/polkit.h>
+ 
+ #include "user-classify.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"
+ #ifdef HAVE_UTMPX_H
+ #define PATH_WTMP _PATH_WTMPX
+ #endif
+ 
+ enum {
+         PROP_0,
+         PROP_DAEMON_VERSION
+ };
+@@ -279,81 +282,125 @@ entry_generator_wtmp (GHashTable *users,
+ 
+                 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);
+         }
+ 
+         g_hash_table_unref (login_hash);
+         g_hash_table_unref (logout_hash);
+         g_free (state_data);
+         *state = NULL;
+         return NULL;
+ }
+ #endif /* HAVE_UTMPX_H */
+ 
+ static struct passwd *
+ entry_generator_fgetpwent (GHashTable *users,
+                            gpointer   *state)
+ {
+         struct passwd *pwent;
+-        FILE *fp;
++        struct {
++                FILE *fp;
++                GHashTable *users;
++        } *generator_state;
+ 
+         /* First iteration */
+         if (*state == NULL) {
+-                *state = fp = fopen (PATH_PASSWD, "r");
++                GHashTable *shadow_users = NULL;
++                FILE *fp;
++#ifdef HAVE_SHADOW_H
++                struct spwd *shadow_entry;
++
++                fp = fopen (PATH_SHADOW, "r");
++                if (fp == NULL) {
++                        g_warning ("Unable to open %s: %s", PATH_SHADOW, g_strerror (errno));
++                        return NULL;
++                }
++
++                shadow_users = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
++
++                do {
++                        shadow_entry = fgetspent (fp);
++                        if (shadow_entry != NULL) {
++                                g_hash_table_add (shadow_users, g_strdup (shadow_entry->sp_namp));
++                        } else if (errno != EINTR) {
++                                break;
++                        }
++                } while (shadow_entry != NULL);
++
++                fclose (fp);
++
++                if (g_hash_table_size (shadow_users) == 0) {
++                        g_clear_pointer (&shadow_users, g_hash_table_unref);
++                        return NULL;
++                }
++#endif
++
++                fp = fopen (PATH_PASSWD, "r");
+                 if (fp == NULL) {
++                        g_clear_pointer (&shadow_users, g_hash_table_unref);
+                         g_warning ("Unable to open %s: %s", PATH_PASSWD, g_strerror (errno));
+                         return NULL;
+                 }
++
++                generator_state = g_malloc0 (sizeof (*generator_state));
++                generator_state->fp = fp;
++                generator_state->users = shadow_users;
++
++                *state = generator_state;
+         }
+ 
+         /* Every iteration */
+-        fp = *state;
+-        pwent = fgetpwent (fp);
++        generator_state = *state;
++        pwent = fgetpwent (generator_state->fp);
+         if (pwent != NULL) {
+-                return pwent;
++                if (!generator_state->users || g_hash_table_lookup (generator_state->users, pwent->pw_name))
++                        return pwent;
+         }
+ 
+         /* Last iteration */
+-        fclose (fp);
++        fclose (generator_state->fp);
++        g_hash_table_unref (generator_state->users);
++        g_free (generator_state);
+         *state = NULL;
++
+         return NULL;
+ }
+ 
+ static struct passwd *
+ entry_generator_cachedir (GHashTable *users,
+                           gpointer   *state)
+ {
+         struct passwd *pwent;
+         const gchar *name;
+         GError *error = NULL;
+         gchar *filename;
+         gboolean regular;
+         GHashTableIter iter;
+         GKeyFile *key_file;
+         User *user;
+         GDir *dir;
+ 
+         /* First iteration */
+         if (*state == NULL) {
+                 *state = g_dir_open (USERDIR, 0, &error);
+                 if (error != NULL) {
+                         if (!g_error_matches (error, G_FILE_ERROR, G_FILE_ERROR_NOENT))
+                                 g_warning ("couldn't list user cache directory: %s", USERDIR);
+                         g_error_free (error);
+                         return NULL;
+                 }
+         }
+ 
+         /* Every iteration */
+ 
+-- 
+2.7.4
+
+
+From 011ef555b0db601186a38c43f9359589ed61e230 Mon Sep 17 00:00:00 2001
+From: Ray Strode <rstrode@redhat.com>
+Date: Wed, 29 Jun 2016 15:57:38 -0400
+Subject: [PATCH 3/5] daemon: don't call getspnam for local users
+
+We're already iterating over the whole shadow file, so
+just cache the entries instead of calling getspname a
+few lines later.
+
+https://bugs.freedesktop.org/show_bug.cgi?id=48177
+---
+ src/daemon.c | 86 +++++++++++++++++++++++++++++++++++++++++-------------------
+ src/user.c   | 11 ++------
+ src/user.h   |  4 ++-
+ 3 files changed, 64 insertions(+), 37 deletions(-)
+
+diff --git a/src/daemon.c b/src/daemon.c
+index 5c269af..71a3ea4 100644
+--- a/src/daemon.c
++++ b/src/daemon.c
+@@ -58,61 +58,61 @@
+ #define PATH_WTMP _PATH_WTMPX
+ #endif
+ 
+ enum {
+         PROP_0,
+         PROP_DAEMON_VERSION
+ };
+ 
+ struct DaemonPrivate {
+         GDBusConnection *bus_connection;
+         GDBusProxy *bus_proxy;
+ 
+         GHashTable *users;
+ 
+         User *autologin;
+ 
+         GFileMonitor *passwd_monitor;
+         GFileMonitor *shadow_monitor;
+         GFileMonitor *group_monitor;
+         GFileMonitor *gdm_monitor;
+ #ifdef HAVE_UTMPX_H
+         GFileMonitor *wtmp_monitor;
+ #endif
+ 
+         guint reload_id;
+         guint autologin_id;
+ 
+         PolkitAuthority *authority;
+ };
+ 
+-typedef struct passwd * (* EntryGeneratorFunc) (GHashTable *, gpointer *);
++typedef struct passwd * (* EntryGeneratorFunc) (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" },
+         { ERROR_PERMISSION_DENIED, "org.freedesktop.Accounts.Error.PermissionDenied" },
+         { ERROR_NOT_SUPPORTED, "org.freedesktop.Accounts.Error.NotSupported" }
+ };
+ 
+ GQuark
+ error_quark (void)
+ {
+         static volatile gsize quark_volatile = 0;
+ 
+         g_dbus_error_register_error_domain ("accounts_error",
+                                             &quark_volatile,
+                                             accounts_error_entries,
+                                             G_N_ELEMENTS (accounts_error_entries));
+ 
+         return (GQuark) quark_volatile;
+ }
+ #define ENUM_ENTRY(NAME, DESC) { NAME, "" #NAME "", DESC }
+ 
+@@ -138,62 +138,63 @@ error_get_type (void)
+   return etype;
+ }
+ 
+ #ifdef HAVE_UTMPX_H
+ 
+ typedef struct {
+         guint64 frequency;
+         gint64 time;
+         GList *previous_logins;
+ } UserAccounting;
+ 
+ typedef struct {
+         gchar  *id;
+         gint64  login_time;
+         gint64  logout_time;
+ } UserPreviousLogin;
+ 
+ typedef struct {
+         GHashTable *login_hash;
+         GHashTable *logout_hash;
+ } WTmpGeneratorState;
+ 
+ static void
+ user_previous_login_free (UserPreviousLogin *previous_login)
+ {
+         g_free (previous_login->id);
+         g_free (previous_login);
+ }
+ 
+ static struct passwd *
+-entry_generator_wtmp (GHashTable *users,
+-                      gpointer   *state)
++entry_generator_wtmp (GHashTable   *users,
++                      gpointer     *state,
++                      struct spwd **spent)
+ {
+         GHashTable *login_hash, *logout_hash;
+         struct utmpx *wtmp_entry;
+         GHashTableIter iter;
+         gpointer key, value;
+         struct passwd *pwent;
+         User *user;
+         WTmpGeneratorState *state_data;
+         GVariantBuilder *builder, *builder2;
+         GList *l;
+ 
+         if (*state == NULL) {
+                 /* First iteration */
+ #ifdef UTXDB_LOG
+                 if (setutxdb (UTXDB_LOG, NULL) != 0) {
+                         return NULL;
+                 }
+ #else
+                 utmpxname (PATH_WTMP);
+                 setutxent ();
+ #endif
+                 *state = g_new (WTmpGeneratorState, 1);
+                 state_data = *state;
+                 state_data->login_hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
+                 state_data->logout_hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
+         }
+ 
+         /* Every iteration */
+         state_data = *state;
+         login_hash = state_data->login_hash;
+@@ -232,284 +233,308 @@ entry_generator_wtmp (GHashTable *users,
+                 }
+ 
+                 pwent = getpwnam (wtmp_entry->ut_user);
+                 if (pwent == NULL) {
+                         continue;
+                 }
+ 
+                 if (!g_hash_table_lookup_extended (login_hash,
+                                                    wtmp_entry->ut_user,
+                                                    &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);
++                *spent = getspnam (pwent->pw_name);
+ 
+                 return pwent;
+         }
+ 
+         /* 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;
+ 
+                 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);
+ 
+                 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);
+         }
+ 
+         g_hash_table_unref (login_hash);
+         g_hash_table_unref (logout_hash);
+         g_free (state_data);
+         *state = NULL;
+         return NULL;
+ }
+ #endif /* HAVE_UTMPX_H */
+ 
+ static struct passwd *
+-entry_generator_fgetpwent (GHashTable *users,
+-                           gpointer   *state)
++entry_generator_fgetpwent (GHashTable   *users,
++                           gpointer     *state,
++                           struct spwd **spent)
+ {
+         struct passwd *pwent;
++
++        struct {
++                struct spwd spbuf;
++                char buf[1024];
++        } *shadow_entry_buffers;
++
+         struct {
+                 FILE *fp;
+                 GHashTable *users;
+         } *generator_state;
+ 
+         /* First iteration */
+         if (*state == NULL) {
+                 GHashTable *shadow_users = NULL;
+                 FILE *fp;
+-#ifdef HAVE_SHADOW_H
+                 struct spwd *shadow_entry;
+ 
+                 fp = fopen (PATH_SHADOW, "r");
+                 if (fp == NULL) {
+                         g_warning ("Unable to open %s: %s", PATH_SHADOW, g_strerror (errno));
+                         return NULL;
+                 }
+ 
+-                shadow_users = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
++                shadow_users = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
+ 
+                 do {
+-                        shadow_entry = fgetspent (fp);
+-                        if (shadow_entry != NULL) {
+-                                g_hash_table_add (shadow_users, g_strdup (shadow_entry->sp_namp));
+-                        } else if (errno != EINTR) {
+-                                break;
++                        int ret = 0;
++
++                        shadow_entry_buffers = g_malloc0 (sizeof (*shadow_entry_buffers));
++
++                        ret = fgetspent_r (fp, &shadow_entry_buffers->spbuf, shadow_entry_buffers->buf, sizeof (shadow_entry_buffers->buf), &shadow_entry);
++                        if (ret == 0) {
++                                g_hash_table_insert (shadow_users, g_strdup (shadow_entry->sp_namp), shadow_entry_buffers);
++                        } else {
++                                g_free (shadow_entry_buffers);
++
++                                if (errno != EINTR) {
++                                        break;
++                                }
+                         }
+                 } while (shadow_entry != NULL);
+ 
+                 fclose (fp);
+ 
+                 if (g_hash_table_size (shadow_users) == 0) {
+                         g_clear_pointer (&shadow_users, g_hash_table_unref);
+                         return NULL;
+                 }
+-#endif
+ 
+                 fp = fopen (PATH_PASSWD, "r");
+                 if (fp == NULL) {
+                         g_clear_pointer (&shadow_users, g_hash_table_unref);
+                         g_warning ("Unable to open %s: %s", PATH_PASSWD, g_strerror (errno));
+                         return NULL;
+                 }
+ 
+                 generator_state = g_malloc0 (sizeof (*generator_state));
+                 generator_state->fp = fp;
+                 generator_state->users = shadow_users;
+ 
+                 *state = generator_state;
+         }
+ 
+         /* Every iteration */
+         generator_state = *state;
+         pwent = fgetpwent (generator_state->fp);
+         if (pwent != NULL) {
+-                if (!generator_state->users || g_hash_table_lookup (generator_state->users, pwent->pw_name))
++                shadow_entry_buffers = g_hash_table_lookup (generator_state->users, pwent->pw_name);
++
++                if (shadow_entry_buffers != NULL) {
++                        *spent = &shadow_entry_buffers->spbuf;
+                         return pwent;
++                }
+         }
+ 
+         /* Last iteration */
+         fclose (generator_state->fp);
+         g_hash_table_unref (generator_state->users);
+         g_free (generator_state);
+         *state = NULL;
+ 
+         return NULL;
+ }
+ 
+ static struct passwd *
+-entry_generator_cachedir (GHashTable *users,
+-                          gpointer   *state)
++entry_generator_cachedir (GHashTable   *users,
++                          gpointer     *state,
++                          struct spwd **shadow_entry)
+ {
+         struct passwd *pwent;
+         const gchar *name;
+         GError *error = NULL;
+         gchar *filename;
+         gboolean regular;
+         GHashTableIter iter;
+         GKeyFile *key_file;
+         User *user;
+         GDir *dir;
+ 
+         /* First iteration */
+         if (*state == NULL) {
+                 *state = g_dir_open (USERDIR, 0, &error);
+                 if (error != NULL) {
+                         if (!g_error_matches (error, G_FILE_ERROR, G_FILE_ERROR_NOENT))
+                                 g_warning ("couldn't list user cache directory: %s", USERDIR);
+                         g_error_free (error);
+                         return NULL;
+                 }
+         }
+ 
+         /* Every iteration */
+ 
+         /*
+          * Use names of files of regular type to lookup information
+          * about each user. Loop until we find something valid.
+          */
+         dir = *state;
+         while (TRUE) {
+                 name = g_dir_read_name (dir);
+                 if (name == NULL)
+                         break;
+ 
+                 /* Only load files in this directory */
+                 filename = g_build_filename (USERDIR, name, NULL);
+                 regular = g_file_test (filename, G_FILE_TEST_IS_REGULAR);
+                 g_free (filename);
+ 
+                 if (regular) {
+                         pwent = getpwnam (name);
+-                        if (pwent == NULL)
++                        if (pwent == NULL) {
+                                 g_debug ("user '%s' in cache dir but not present on system", name);
+-                        else
++                        } else {
++                                *shadow_entry = getspnam (pwent->pw_name);
++
+                                 return pwent;
++                        }
+                 }
+         }
+ 
+         /* Last iteration */
+         g_dir_close (dir);
+ 
+         /* Update all the users from the files in the cache dir */
+         g_hash_table_iter_init (&iter, users);
+         while (g_hash_table_iter_next (&iter, (gpointer *)&name, (gpointer *)&user)) {
+                 filename = g_build_filename (USERDIR, name, NULL);
+                 key_file = g_key_file_new ();
+                 if (g_key_file_load_from_file (key_file, filename, 0, NULL))
+                         user_update_from_keyfile (user, key_file);
+                 g_key_file_unref (key_file);
+                 g_free (filename);
+         }
+ 
+         *state = NULL;
+         return NULL;
+ }
+ 
+ static void
+ load_entries (Daemon             *daemon,
+               GHashTable         *users,
+               EntryGeneratorFunc  entry_generator)
+ {
+         gpointer generator_state = NULL;
+         struct passwd *pwent;
++        struct spwd *spent = NULL;
+         User *user = NULL;
+ 
+         g_assert (entry_generator != NULL);
+ 
+         for (;;) {
+-                pwent = entry_generator (users, &generator_state);
++                spent = NULL;
++                pwent = entry_generator (users, &generator_state, &spent);
+                 if (pwent == NULL)
+                         break;
+ 
+                 /* Skip system users... */
+-                if (!user_classify_is_human (pwent->pw_uid, pwent->pw_name, pwent->pw_shell, NULL)) {
++                if (!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;
+                 }
+ 
+                 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);
++                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));
+         }
+ 
+         /* 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 ();
+@@ -827,115 +852,122 @@ daemon_new (void)
+                 g_object_unref (daemon);
+                 goto error;
+         }
+ 
+         return daemon;
+ 
+  error:
+         return NULL;
+ }
+ 
+ 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 User *
+ add_new_user_for_pwent (Daemon        *daemon,
+-                        struct passwd *pwent)
++                        struct passwd *pwent,
++                        struct spwd   *spent)
+ {
+         User *user;
+ 
+         user = user_new (daemon, pwent->pw_uid);
+-        user_update_from_pwent (user, pwent);
++        user_update_from_pwent (user, pwent, spent);
+         user_register (user);
+ 
+         g_hash_table_insert (daemon->priv->users,
+                              g_strdup (user_get_user_name (user)),
+                              user);
+ 
+         accounts_accounts_emit_user_added (ACCOUNTS_ACCOUNTS (daemon), user_get_object_path (user));
+ 
+         return user;
+ }
+ 
+ User *
+ daemon_local_find_user_by_id (Daemon *daemon,
+                               uid_t   uid)
+ {
+         User *user;
+         struct passwd *pwent;
+ 
+         pwent = getpwuid (uid);
+         if (pwent == NULL) {
+                 g_debug ("unable to lookup uid %d", (int)uid);
+                 return NULL;
+         }
+ 
+         user = g_hash_table_lookup (daemon->priv->users, pwent->pw_name);
+ 
+-        if (user == NULL)
+-                user = add_new_user_for_pwent (daemon, pwent);
++        if (user == NULL) {
++                struct spwd *spent;
++                spent = getspnam (pwent->pw_name);
++                user = add_new_user_for_pwent (daemon, pwent, spent);
++        }
+ 
+         return user;
+ }
+ 
+ User *
+ daemon_local_find_user_by_name (Daemon      *daemon,
+                                 const gchar *name)
+ {
+         User *user;
+         struct passwd *pwent;
+ 
+         pwent = getpwnam (name);
+         if (pwent == NULL) {
+                 g_debug ("unable to lookup name %s: %s", name, g_strerror (errno));
+                 return NULL;
+         }
+ 
+         user = g_hash_table_lookup (daemon->priv->users, pwent->pw_name);
+ 
+-        if (user == NULL)
+-                user = add_new_user_for_pwent (daemon, pwent);
++        if (user == NULL) {
++                struct spwd *spent;
++                spent = getspnam (pwent->pw_name);
++                user = add_new_user_for_pwent (daemon, pwent, spent);
++        }
+ 
+         return user;
+ }
+ 
+ User *
+ daemon_local_get_automatic_login_user (Daemon *daemon)
+ {
+         return daemon->priv->autologin;
+ }
+ 
+ static gboolean
+ daemon_find_user_by_id (AccountsAccounts      *accounts,
+                         GDBusMethodInvocation *context,
+                         gint64                 uid)
+ {
+         Daemon *daemon = (Daemon*)accounts;
+         User *user;
+ 
+         user = daemon_local_find_user_by_id (daemon, uid);
+ 
+         if (user) {
+                 accounts_accounts_complete_find_user_by_id (NULL, context, user_get_object_path (user));
+         }
+         else {
+                 throw_error (context, ERROR_FAILED, "Failed to look up user with uid %d.", (int)uid);
+         }
+ 
+         return TRUE;
+ }
+ 
+diff --git a/src/user.c b/src/user.c
+index 52f57d0..247ca2f 100644
+--- a/src/user.c
++++ b/src/user.c
+@@ -116,65 +116,63 @@ 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;
+         }
+ 
+         for (i = 0; grp->gr_mem[i] != NULL; i++) {
+                 if (g_strcmp0 (grp->gr_mem[i], pwent->pw_name) == 0) {
+                         return ACCOUNT_TYPE_ADMINISTRATOR;
+                 }
+         }
+ 
+         return ACCOUNT_TYPE_STANDARD;
+ }
+ 
+ void
+ user_update_from_pwent (User          *user,
+-                        struct passwd *pwent)
++                        struct passwd *pwent,
++                        struct spwd   *spent)
+ {
+-#ifdef HAVE_SHADOW_H
+-        struct spwd *spent;
+-#endif
+         gchar *real_name;
+         gboolean changed;
+         const gchar *passwd;
+         gboolean locked;
+         PasswordMode mode;
+         AccountType account_type;
+ 
+         g_object_freeze_notify (G_OBJECT (user));
+ 
+         changed = FALSE;
+ 
+         if (pwent->pw_gecos && pwent->pw_gecos[0] != '\0') {
+                 gchar *first_comma = NULL;
+                 gchar *valid_utf8_name = NULL;
+ 
+                 if (g_utf8_validate (pwent->pw_gecos, -1, NULL)) {
+                         valid_utf8_name = pwent->pw_gecos;
+                         first_comma = g_utf8_strchr (valid_utf8_name, -1, ',');
+                 }
+                 else {
+                         g_warning ("User %s has invalid UTF-8 in GECOS field. "
+                                    "It would be a good thing to check /etc/passwd.",
+                                    pwent->pw_name ? pwent->pw_name : "");
+                 }
+ 
+                 if (first_comma) {
+                         real_name = g_strndup (valid_utf8_name,
+                                                   (first_comma - valid_utf8_name));
+                 }
+                 else if (valid_utf8_name) {
+@@ -219,93 +217,88 @@ user_update_from_pwent (User          *user,
+                 g_object_notify (G_OBJECT (user), "account-type");
+         }
+ 
+         /* Username */
+         if (g_strcmp0 (user->user_name, pwent->pw_name) != 0) {
+                 g_free (user->user_name);
+                 user->user_name = g_strdup (pwent->pw_name);
+                 changed = TRUE;
+                 g_object_notify (G_OBJECT (user), "user-name");
+         }
+ 
+         /* Home Directory */
+         if (g_strcmp0 (user->home_dir, pwent->pw_dir) != 0) {
+                 g_free (user->home_dir);
+                 user->home_dir = g_strdup (pwent->pw_dir);
+                 g_free (user->default_icon_file);
+                 user->default_icon_file = g_build_filename (user->home_dir, ".face", NULL);
+                 changed = TRUE;
+                 g_object_notify (G_OBJECT (user), "home-directory");
+         }
+ 
+         /* Shell */
+         if (g_strcmp0 (user->shell, pwent->pw_shell) != 0) {
+                 g_free (user->shell);
+                 user->shell = g_strdup (pwent->pw_shell);
+                 changed = TRUE;
+                 g_object_notify (G_OBJECT (user), "shell");
+         }
+ 
+         passwd = NULL;
+-#ifdef HAVE_SHADOW_H
+-        spent = getspnam (pwent->pw_name);
+         if (spent)
+                 passwd = spent->sp_pwdp;
+-#endif
+ 
+         if (passwd && passwd[0] == '!') {
+                 locked = TRUE;
+         }
+         else {
+                 locked = FALSE;
+         }
+ 
+         if (user->locked != locked) {
+                 user->locked = locked;
+                 changed = TRUE;
+                 g_object_notify (G_OBJECT (user), "locked");
+         }
+ 
+         if (passwd == NULL || passwd[0] != 0) {
+                 mode = PASSWORD_MODE_REGULAR;
+         }
+         else {
+                 mode = PASSWORD_MODE_NONE;
+         }
+ 
+-#ifdef HAVE_SHADOW_H
+         if (spent) {
+                 if (spent->sp_lstchg == 0) {
+                         mode = PASSWORD_MODE_SET_AT_LOGIN;
+                 }
+         }
+-#endif
+ 
+         if (user->password_mode != mode) {
+                 user->password_mode = mode;
+                 changed = TRUE;
+                 g_object_notify (G_OBJECT (user), "password-mode");
+         }
+ 
+         user->system_account = !user_classify_is_human (user->uid, user->user_name, pwent->pw_shell, passwd);
+ 
+         g_object_thaw_notify (G_OBJECT (user));
+ 
+         if (changed)
+                 accounts_user_emit_changed (ACCOUNTS_USER (user));
+ }
+ 
+ void
+ user_update_from_keyfile (User     *user,
+                           GKeyFile *keyfile)
+ {
+         gchar *s;
+ 
+         g_object_freeze_notify (G_OBJECT (user));
+ 
+         s = g_key_file_get_string (keyfile, "User", "Language", NULL);
+         if (s != NULL) {
+                 /* TODO: validate / normalize */
+                 g_free (user->language);
+                 user->language = s;
+                 g_object_notify (G_OBJECT (user), "language");
+         }
+diff --git a/src/user.h b/src/user.h
+index 0848b50..22548f9 100644
+--- a/src/user.h
++++ b/src/user.h
+@@ -1,80 +1,82 @@
+ /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+  *
+  * Copyright (C) 2009-2010 Red Hat, Inc.
+  *
+  * This program is free software; you can redistribute it and/or modify
+  * it under the terms of the GNU General Public License as published by
+  * the Free Software Foundation; either version 3 of the License, or
+  * (at your option) any later version.
+  *
+  * This program is distributed in the hope that it will be useful,
+  * but WITHOUT ANY WARRANTY; without even the implied warranty of
+  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  * GNU General Public License for more details.
+  *
+  * You should have received a copy of the GNU General Public License
+  * along with this program; if not, write to the Free Software
+  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+  */
+ 
+ #ifndef __USER__
+ #define __USER__
+ 
+ #include <sys/types.h>
+ #include <pwd.h>
++#include <shadow.h>
+ 
+ #include <glib.h>
+ #include <gio/gio.h>
+ 
+ #include "types.h"
+ 
+ G_BEGIN_DECLS
+ 
+ #define TYPE_USER (user_get_type ())
+ #define USER(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), TYPE_USER, User))
+ #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 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);
+ 
+ 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.7.4
+
+
+From 2accf123c55f3c6a9596e9fc2d614fcb07c88559 Mon Sep 17 00:00:00 2001
+From: Ray Strode <rstrode@redhat.com>
+Date: Wed, 29 Jun 2016 16:22:42 -0400
+Subject: [PATCH 4/5] daemon: constrain max local users to 50
+
+Systems with tens of thousands of users don't want all those users
+showing up in the user list.
+
+Set a cap at an even 50, which should cover the lion's share of use
+cases well. Of course, if a user not in the list explicitly
+logs in (from Not Listed? or whatever) they get added to the list.
+
+https://bugs.freedesktop.org/show_bug.cgi?id=48177
+---
+ src/daemon.c | 19 +++++++++++++------
+ 1 file changed, 13 insertions(+), 6 deletions(-)
+
+diff --git a/src/daemon.c b/src/daemon.c
+index 71a3ea4..cb586bb 100644
+--- a/src/daemon.c
++++ b/src/daemon.c
+@@ -279,60 +279,64 @@ entry_generator_wtmp (GHashTable   *users,
+                         continue;
+                 }
+ 
+                 g_object_set (user, "login-frequency", accounting->frequency, NULL);
+                 g_object_set (user, "login-time", accounting->time, NULL);
+ 
+                 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);
+         }
+ 
+         g_hash_table_unref (login_hash);
+         g_hash_table_unref (logout_hash);
+         g_free (state_data);
+         *state = NULL;
+         return NULL;
+ }
+ #endif /* HAVE_UTMPX_H */
+ 
++#ifndef MAX_LOCAL_USERS
++#define MAX_LOCAL_USERS 50
++#endif
++
+ static struct passwd *
+ entry_generator_fgetpwent (GHashTable   *users,
+                            gpointer     *state,
+                            struct spwd **spent)
+ {
+         struct passwd *pwent;
+ 
+         struct {
+                 struct spwd spbuf;
+                 char buf[1024];
+         } *shadow_entry_buffers;
+ 
+         struct {
+                 FILE *fp;
+                 GHashTable *users;
+         } *generator_state;
+ 
+         /* First iteration */
+         if (*state == NULL) {
+                 GHashTable *shadow_users = NULL;
+                 FILE *fp;
+                 struct spwd *shadow_entry;
+ 
+                 fp = fopen (PATH_SHADOW, "r");
+                 if (fp == NULL) {
+                         g_warning ("Unable to open %s: %s", PATH_SHADOW, g_strerror (errno));
+                         return NULL;
+                 }
+ 
+                 shadow_users = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
+@@ -350,67 +354,70 @@ entry_generator_fgetpwent (GHashTable   *users,
+ 
+                                 if (errno != EINTR) {
+                                         break;
+                                 }
+                         }
+                 } while (shadow_entry != NULL);
+ 
+                 fclose (fp);
+ 
+                 if (g_hash_table_size (shadow_users) == 0) {
+                         g_clear_pointer (&shadow_users, g_hash_table_unref);
+                         return NULL;
+                 }
+ 
+                 fp = fopen (PATH_PASSWD, "r");
+                 if (fp == NULL) {
+                         g_clear_pointer (&shadow_users, g_hash_table_unref);
+                         g_warning ("Unable to open %s: %s", PATH_PASSWD, g_strerror (errno));
+                         return NULL;
+                 }
+ 
+                 generator_state = g_malloc0 (sizeof (*generator_state));
+                 generator_state->fp = fp;
+                 generator_state->users = shadow_users;
+ 
+                 *state = generator_state;
+         }
+ 
+         /* Every iteration */
+         generator_state = *state;
+-        pwent = fgetpwent (generator_state->fp);
+-        if (pwent != NULL) {
+-                shadow_entry_buffers = g_hash_table_lookup (generator_state->users, pwent->pw_name);
+ 
+-                if (shadow_entry_buffers != NULL) {
+-                        *spent = &shadow_entry_buffers->spbuf;
+-                        return pwent;
++        if (g_hash_table_size (users) < MAX_LOCAL_USERS) {
++                pwent = fgetpwent (generator_state->fp);
++                if (pwent != NULL) {
++                        shadow_entry_buffers = g_hash_table_lookup (generator_state->users, pwent->pw_name);
++
++                        if (shadow_entry_buffers != NULL) {
++                            *spent = &shadow_entry_buffers->spbuf;
++                            return pwent;
++                        }
+                 }
+         }
+ 
+         /* Last iteration */
+         fclose (generator_state->fp);
+         g_hash_table_unref (generator_state->users);
+         g_free (generator_state);
+         *state = NULL;
+ 
+         return NULL;
+ }
+ 
+ static struct passwd *
+ entry_generator_cachedir (GHashTable   *users,
+                           gpointer     *state,
+                           struct spwd **shadow_entry)
+ {
+         struct passwd *pwent;
+         const gchar *name;
+         GError *error = NULL;
+         gchar *filename;
+         gboolean regular;
+         GHashTableIter iter;
+         GKeyFile *key_file;
+         User *user;
+         GDir *dir;
+ 
+         /* First iteration */
+         if (*state == NULL) {
+                 *state = g_dir_open (USERDIR, 0, &error);
+-- 
+2.7.4
+
+
+From ed58ad3210010a09b6f114b4d392afb66ad0bbfa Mon Sep 17 00:00:00 2001
+From: Ray Strode <rstrode@redhat.com>
+Date: Wed, 29 Jun 2016 16:32:17 -0400
+Subject: [PATCH 5/5] daemon: don't source user list from wtmp
+
+wtmp can get rather large on some systems from ssh logins.
+Furthermore it's pretty much completely redundant given the user
+cache in /var/lib/AccountService
+
+This commit changes the wtmp code to only get used for maintaining
+login frequency and accounting, not for generating new users.
+
+https://bugs.freedesktop.org/show_bug.cgi?id=48177
+---
+ src/daemon.c | 42 ++++++++++++------------------------------
+ 1 file changed, 12 insertions(+), 30 deletions(-)
+
+diff --git a/src/daemon.c b/src/daemon.c
+index cb586bb..815e2c9 100644
+--- a/src/daemon.c
++++ b/src/daemon.c
+@@ -137,95 +137,83 @@ error_get_type (void)
+     }
+   return etype;
+ }
+ 
+ #ifdef HAVE_UTMPX_H
+ 
+ typedef struct {
+         guint64 frequency;
+         gint64 time;
+         GList *previous_logins;
+ } UserAccounting;
+ 
+ typedef struct {
+         gchar  *id;
+         gint64  login_time;
+         gint64  logout_time;
+ } UserPreviousLogin;
+ 
+ typedef struct {
+         GHashTable *login_hash;
+         GHashTable *logout_hash;
+ } WTmpGeneratorState;
+ 
+ static void
+ user_previous_login_free (UserPreviousLogin *previous_login)
+ {
+         g_free (previous_login->id);
+         g_free (previous_login);
+ }
+ 
+-static struct passwd *
+-entry_generator_wtmp (GHashTable   *users,
+-                      gpointer     *state,
+-                      struct spwd **spent)
++static void
++wtmp_update_login_frequencies (GHashTable *users)
+ {
+         GHashTable *login_hash, *logout_hash;
+         struct utmpx *wtmp_entry;
+         GHashTableIter iter;
+         gpointer key, value;
+         struct passwd *pwent;
+         User *user;
+-        WTmpGeneratorState *state_data;
+         GVariantBuilder *builder, *builder2;
+         GList *l;
+ 
+-        if (*state == NULL) {
+-                /* First iteration */
+ #ifdef UTXDB_LOG
+-                if (setutxdb (UTXDB_LOG, NULL) != 0) {
+-                        return NULL;
+-                }
++        if (setutxdb (UTXDB_LOG, NULL) != 0) {
++                return NULL;
++        }
+ #else
+-                utmpxname (PATH_WTMP);
+-                setutxent ();
++        utmpxname (PATH_WTMP);
++        setutxent ();
+ #endif
+-                *state = g_new (WTmpGeneratorState, 1);
+-                state_data = *state;
+-                state_data->login_hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
+-                state_data->logout_hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
+-        }
+ 
+-        /* Every iteration */
+-        state_data = *state;
+-        login_hash = state_data->login_hash;
+-        logout_hash = state_data->logout_hash;
++        login_hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
++        logout_hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
+         while ((wtmp_entry = getutxent ())) {
+                 UserAccounting    *accounting;
+                 UserPreviousLogin *previous_login;
+ 
+                 if (wtmp_entry->ut_type == BOOT_TIME) {
+                         /* Set boot time for missing logout records */
+                         g_hash_table_iter_init (&iter, logout_hash);
+                         while (g_hash_table_iter_next (&iter, &key, &value)) {
+                                 previous_login = (UserPreviousLogin *) value;
+ 
+                                 if (previous_login->logout_time == 0) {
+                                         previous_login->logout_time = wtmp_entry->ut_tv.tv_sec;
+                                 }
+                         }
+                         g_hash_table_remove_all (logout_hash);
+                 } else if (wtmp_entry->ut_type == DEAD_PROCESS) {
+                         /* Save corresponding logout time */
+                         if (g_hash_table_lookup_extended (logout_hash, wtmp_entry->ut_line, &key, &value)) {
+                                 previous_login = (UserPreviousLogin *) value;
+                                 previous_login->logout_time = wtmp_entry->ut_tv.tv_sec;
+ 
+                                 g_hash_table_remove (logout_hash, previous_login->id);
+                         }
+                 }
+ 
+                 if (wtmp_entry->ut_type != USER_PROCESS) {
+                         continue;
+                 }
+ 
+                 if (wtmp_entry->ut_user[0] == 0) {
+@@ -233,103 +221,96 @@ entry_generator_wtmp (GHashTable   *users,
+                 }
+ 
+                 pwent = getpwnam (wtmp_entry->ut_user);
+                 if (pwent == NULL) {
+                         continue;
+                 }
+ 
+                 if (!g_hash_table_lookup_extended (login_hash,
+                                                    wtmp_entry->ut_user,
+                                                    &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);
+-                *spent = getspnam (pwent->pw_name);
+-
+-                return pwent;
+         }
+ 
+-        /* 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;
+ 
+                 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);
+ 
+                 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);
+         }
+ 
+         g_hash_table_unref (login_hash);
+         g_hash_table_unref (logout_hash);
+-        g_free (state_data);
+-        *state = NULL;
+-        return NULL;
+ }
+ #endif /* HAVE_UTMPX_H */
+ 
+ #ifndef MAX_LOCAL_USERS
+ #define MAX_LOCAL_USERS 50
+ #endif
+ 
+ static struct passwd *
+ entry_generator_fgetpwent (GHashTable   *users,
+                            gpointer     *state,
+                            struct spwd **spent)
+ {
+         struct passwd *pwent;
+ 
+         struct {
+                 struct spwd spbuf;
+                 char buf[1024];
+         } *shadow_entry_buffers;
+ 
+         struct {
+                 FILE *fp;
+                 GHashTable *users;
+         } *generator_state;
+ 
+         /* First iteration */
+         if (*state == NULL) {
+                 GHashTable *shadow_users = NULL;
+                 FILE *fp;
+                 struct spwd *shadow_entry;
+ 
+@@ -533,64 +514,65 @@ create_users_hash_table (void)
+                                       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
+          * frozen in load_entries() and then thawed as we process
+          * them below.
+          */
+ 
+         /* Load the local users into our hash table */
+         load_entries (daemon, users, 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);
+ 
+         /* Now add/update users from other sources, possibly non-local */
++        load_entries (daemon, users, entry_generator_cachedir);
++
+ #ifdef HAVE_UTMPX_H
+-        load_entries (daemon, users, entry_generator_wtmp);
++        wtmp_update_login_frequencies (users);
+ #endif
+-        load_entries (daemon, users, entry_generator_cachedir);
+ 
+         /* 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_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_register (user);
+                         accounts_accounts_emit_user_added (ACCOUNTS_ACCOUNTS (daemon),
+                                                            user_get_object_path (user));
+                 }
+-- 
+2.7.4
+
diff --git a/SPECS/accountsservice.spec b/SPECS/accountsservice.spec
index e21e4fa..cdce0e5 100644
--- a/SPECS/accountsservice.spec
+++ b/SPECS/accountsservice.spec
@@ -2,7 +2,7 @@
 
 Name:           accountsservice
 Version:        0.6.35
-Release:        9%{?dist}
+Release:        12%{?dist}
 Summary:        D-Bus interfaces for querying and manipulating user account information
 
 Group:          System Environment/Daemons
@@ -35,6 +35,10 @@ Patch3: userdel-f.patch
 Patch4: fix-leak.patch
 Patch5: 0001-Add-asynchronous-api-for-user-uncaching.patch
 Patch6: 0001-systemd-ensure-that-accounts-service-starts-after-NS.patch
+Patch7: scale-better.patch
+Patch8: fix-log-leak.patch
+Patch9: 0001-user-classify-exclude-nologin-users.patch
+Patch10: 0001-configure-actually-define-HAVE_GETUSERSHELL.patch
 
 %package libs
 Summary: Client-side library to talk to accountsservice
@@ -72,6 +76,10 @@ of these interfaces, based on the useradd, usermod and userdel commands.
 %patch4 -p1 -b .fix-leak
 %patch5 -p1 -b .async-api-uncaching
 %patch6 -p1 -b .start-after-nsswitch
+%patch7 -p1 -b .scale-better
+%patch8 -p1 -b .fix-log-leak
+%patch9 -p1 -b .hide-nologin-users
+%patch10 -p1 -b .define-HAVE_GETUSERSHELL
 
 %build
 autoreconf -f -i
@@ -126,6 +134,21 @@ rm $RPM_BUILD_ROOT%{_libdir}/*.a
 %{_datadir}/gtk-doc/html/libaccountsservice/*
 
 %changelog
+* Tue Sep 06 2016 Ray Strode <rstrode@redhat.com> - 0.6.35-12
+- hide users with /sbin/nologin shell
+  Resolves: #1341276
+
+* Fri Jul 01 2016 Ray Strode <rstrode@redhat.com> - 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 <rstrode@redhat.com> - 0.6.35-10
+- scale to a large number of remote users better
+  Resolves: #1220904
+
 * Mon Jun 08 2015 Ray Strode <rstrode@redhat.com> 0.6.35-9
 - Start after nsswitch and ypbind
   Resolves: #1217439