Blob Blame History Raw
From dc41728b9c4ec35fb7bd41cdc646b77dcb41f6dd Mon Sep 17 00:00:00 2001
From: Ray Strode <rstrode@redhat.com>
Date: Mon, 2 Oct 2017 15:45:01 -0400
Subject: [PATCH 06/13] daemon: add new HasMultipleUsers and HasNoUsers
 properties

Every gnome-shell instance wants to know if the system has multiple
users or not, in order to know whether or not to show the
'Switch User' feature in the menu.

accountsservice doesn't provide this information directly, though,
so libaccountsservice instead requests a list of all users on the
system and counts the provided list, filtering out system users.

This is a lot of work for every gnome-shell instance to do, when
it doesn't actually need the list of users at all.

This adds a new property HasMultipleUsers which libaccountsservice
can watch for instead.  For good measure, this commit also adds a
HasNoUsers boolean which can be used to know whether or not to start
gnome-initial-setup.
---
 data/org.freedesktop.Accounts.xml | 20 ++++++++++++++++++++
 src/daemon.c                      | 24 +++++++++++++++++++++---
 2 files changed, 41 insertions(+), 3 deletions(-)

diff --git a/data/org.freedesktop.Accounts.xml b/data/org.freedesktop.Accounts.xml
index 692540a..ed7db50 100644
--- a/data/org.freedesktop.Accounts.xml
+++ b/data/org.freedesktop.Accounts.xml
@@ -197,32 +197,52 @@
           <doc:para>
             Emitted when a user is added.
           </doc:para>
         </doc:description>
       </doc:doc>
     </signal>
 
     <signal name="UserDeleted">
       <arg name="user" type="o">
         <doc:doc><doc:summary>Object path of the user that was deleted.</doc:summary></doc:doc>
       </arg>
       <doc:doc>
         <doc:description>
           <doc:para>
             Emitted when a user is deleted.
           </doc:para>
         </doc:description>
       </doc:doc>
     </signal>
 
    <property name="DaemonVersion" type="s" access="read">
      <doc:doc>
        <doc:description>
          <doc:para>
            The version of the running daemon.
          </doc:para>
        </doc:description>
      </doc:doc>
    </property>
 
+   <property name="HasNoUsers" type="b" access="read">
+     <doc:doc>
+       <doc:description>
+         <doc:para>
+           Whether or not the system has no users
+         </doc:para>
+       </doc:description>
+     </doc:doc>
+   </property>
+
+   <property name="HasMultipleUsers" type="b" access="read">
+     <doc:doc>
+       <doc:description>
+         <doc:para>
+           Whether or not the system has multiple users
+         </doc:para>
+       </doc:description>
+     </doc:doc>
+   </property>
+
   </interface>
 </node>
diff --git a/src/daemon.c b/src/daemon.c
index 6e3e4b3..a4e18df 100644
--- a/src/daemon.c
+++ b/src/daemon.c
@@ -35,60 +35,61 @@
 #include <unistd.h>
 #include <errno.h>
 #include <sys/types.h>
 
 #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 "wtmp-helper.h"
 #include "daemon.h"
 #include "util.h"
 
 #define PATH_PASSWD "/etc/passwd"
 #define PATH_SHADOW "/etc/shadow"
 #define PATH_GROUP "/etc/group"
 #define PATH_GDM_CUSTOM "/etc/gdm/custom.conf"
 
 enum {
         PROP_0,
         PROP_DAEMON_VERSION
 };
 
 struct DaemonPrivate {
         GDBusConnection *bus_connection;
 
         GHashTable *users;
+        gsize number_of_normal_users;
         GList *explicitly_requested_users;
 
         User *autologin;
 
         GFileMonitor *passwd_monitor;
         GFileMonitor *shadow_monitor;
         GFileMonitor *group_monitor;
         GFileMonitor *gdm_monitor;
         GFileMonitor *wtmp_monitor;
 
         guint reload_id;
         guint autologin_id;
 
         PolkitAuthority *authority;
         GHashTable *extension_ifaces;
 };
 
 typedef struct passwd * (* EntryGeneratorFunc) (Daemon *, GHashTable *, gpointer *, struct spwd **shadow_entry);
 
 static void daemon_accounts_accounts_iface_init (AccountsAccountsIface *iface);
 
 G_DEFINE_TYPE_WITH_CODE (Daemon, daemon, ACCOUNTS_TYPE_ACCOUNTS_SKELETON, G_IMPLEMENT_INTERFACE (ACCOUNTS_TYPE_ACCOUNTS, daemon_accounts_accounts_iface_init));
 
 #define DAEMON_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), TYPE_DAEMON, DaemonPrivate))
 
 static const GDBusErrorEntry accounts_error_entries[] =
 { 
         { ERROR_FAILED, "org.freedesktop.Accounts.Error.Failed" },
         { ERROR_USER_EXISTS, "org.freedesktop.Accounts.Error.UserExists" },
         { ERROR_USER_DOES_NOT_EXIST, "org.freedesktop.Accounts.Error.UserDoesNotExist" },
@@ -395,98 +396,115 @@ load_entries (Daemon             *daemon,
 
                         /* freeze & update users not already in the new list */
                         g_object_freeze_notify (G_OBJECT (user));
                         user_update_from_pwent (user, pwent, spent);
 
                         g_hash_table_insert (users, g_strdup (user_get_user_name (user)), user);
                         g_debug ("loaded user: %s", user_get_user_name (user));
                 }
 
                 if (!explicitly_requested) {
                         user_set_cached (user, TRUE);
                 }
         }
 
         /* Generator should have cleaned up */
         g_assert (generator_state == NULL);
 }
 
 static GHashTable *
 create_users_hash_table (void)
 {
         return g_hash_table_new_full (g_str_hash,
                                       g_str_equal,
                                       g_free,
                                       g_object_unref);
 }
 
 static void
 reload_users (Daemon *daemon)
 {
+        AccountsAccounts *accounts = ACCOUNTS_ACCOUNTS (daemon);
+        gboolean had_no_users, has_no_users, had_multiple_users, has_multiple_users;
         GHashTable *users;
         GHashTable *old_users;
         GHashTable *local;
         GHashTableIter iter;
+        gsize number_of_normal_users = 0;
         gpointer name;
         User *user;
 
         /* Track the users that we saw during our (re)load */
         users = create_users_hash_table ();
 
         /*
          * NOTE: As we load data from all the sources, notifies are
          * frozen in load_entries() and then thawed as we process
          * them below.
          */
 
         /* Load the local users into our hash table */
         load_entries (daemon, users, FALSE, entry_generator_fgetpwent);
         local = g_hash_table_new (g_str_hash, g_str_equal);
         g_hash_table_iter_init (&iter, users);
         while (g_hash_table_iter_next (&iter, &name, NULL))
                 g_hash_table_add (local, name);
 
         /* and add users to hash table that were explicitly requested  */
         load_entries (daemon, users, TRUE, entry_generator_requested_users);
 
         /* Now add/update users from other sources, possibly non-local */
         load_entries (daemon, users, FALSE, entry_generator_cachedir);
 
         wtmp_helper_update_login_frequencies (users);
 
-        /* Mark which users are local, which are not */
+        /* Count the non-system users. Mark which users are local, which are not. */
         g_hash_table_iter_init (&iter, users);
-        while (g_hash_table_iter_next (&iter, &name, (gpointer *)&user))
+        while (g_hash_table_iter_next (&iter, &name, (gpointer *)&user)) {
+                if (!user_get_system_account (user))
+                        number_of_normal_users++;
                 user_update_local_account_property (user, g_hash_table_lookup (local, name) != NULL);
-
+        }
         g_hash_table_destroy (local);
 
+        had_no_users = accounts_accounts_get_has_no_users (accounts);
+        has_no_users = number_of_normal_users == 0;
+
+        if (had_no_users != has_no_users)
+                accounts_accounts_set_has_no_users (accounts, has_no_users);
+
+        had_multiple_users = accounts_accounts_get_has_multiple_users (accounts);
+        has_multiple_users = number_of_normal_users > 1;
+
+        if (had_multiple_users != has_multiple_users)
+                accounts_accounts_set_has_multiple_users (accounts, has_multiple_users);
+
         /* Swap out the users */
         old_users = daemon->priv->users;
         daemon->priv->users = users;
 
         /* Remove all the old users */
         g_hash_table_iter_init (&iter, old_users);
         while (g_hash_table_iter_next (&iter, &name, (gpointer *)&user)) {
                 User *refreshed_user;
 
                 refreshed_user = g_hash_table_lookup (users, name);
 
                 if (!refreshed_user || !user_get_cached (refreshed_user)) {
                         user_unregister (user);
                         accounts_accounts_emit_user_deleted (ACCOUNTS_ACCOUNTS (daemon),
                                                              user_get_object_path (user));
                 }
         }
 
         /* Register all the new users */
         g_hash_table_iter_init (&iter, users);
         while (g_hash_table_iter_next (&iter, &name, (gpointer *)&user)) {
                 User *stale_user;
 
                 stale_user = g_hash_table_lookup (old_users, name);
 
                 if (!stale_user || !user_get_cached (stale_user) && user_get_cached (user)) {
                         user_register (user);
                         accounts_accounts_emit_user_added (ACCOUNTS_ACCOUNTS (daemon),
                                                            user_get_object_path (user));
                 }
-- 
2.14.1