Blame SOURCES/0001-lib-save-os-when-creating-user.patch

70a90c
From 5124220f12a157ff285072a4f786db2c94c3ca8a Mon Sep 17 00:00:00 2001
70a90c
From: Ray Strode <rstrode@redhat.com>
70a90c
Date: Thu, 14 Jan 2021 15:07:34 -0500
70a90c
Subject: [PATCH] lib: save os when creating user
70a90c
70a90c
In order to identify that a user has upgraded from rhel 7 to
70a90c
rhel 8, we need to know what os they were using when created.
70a90c
70a90c
This commit saves that information using a red hat specific
70a90c
extension to accountsservice.
70a90c
---
70a90c
 .../com.redhat.AccountsServiceUser.System.xml |  10 ++
70a90c
 data/meson.build                              |   1 +
70a90c
 meson.build                                   |   1 +
70a90c
 meson_post_install.py                         |  15 +++
70a90c
 src/libaccountsservice/act-user-manager.c     | 123 ++++++++++++++++++
70a90c
 src/libaccountsservice/meson.build            |   7 +
70a90c
 6 files changed, 157 insertions(+)
70a90c
 create mode 100644 data/com.redhat.AccountsServiceUser.System.xml
70a90c
70a90c
diff --git a/data/com.redhat.AccountsServiceUser.System.xml b/data/com.redhat.AccountsServiceUser.System.xml
70a90c
new file mode 100644
70a90c
index 0000000..67f5f30
70a90c
--- /dev/null
70a90c
+++ b/data/com.redhat.AccountsServiceUser.System.xml
70a90c
@@ -0,0 +1,10 @@
70a90c
+<node>
70a90c
+  <interface name="com.redhat.AccountsServiceUser.System">
70a90c
+
70a90c
+    <annotation name="org.freedesktop.Accounts.VendorExtension" value="true"/>
70a90c
+
70a90c
+    <property name="id" type="s" access="readwrite"/>
70a90c
+    <property name="version-id" type="s" access="readwrite"/>
70a90c
+
70a90c
+  </interface>
70a90c
+</node>
70a90c
diff --git a/data/meson.build b/data/meson.build
70a90c
index 4987937..2dc57c2 100644
70a90c
--- a/data/meson.build
70a90c
+++ b/data/meson.build
70a90c
@@ -1,33 +1,34 @@
70a90c
 ifaces = files(
70a90c
   act_namespace + '.xml',
70a90c
   act_namespace + '.User.xml',
70a90c
+  'com.redhat.AccountsServiceUser.System.xml',
70a90c
 )
70a90c
 
70a90c
 install_data(
70a90c
   ifaces,
70a90c
   install_dir: dbus_ifaces_dir,
70a90c
 )
70a90c
 
70a90c
 install_data(
70a90c
   act_namespace + '.conf',
70a90c
   install_dir: dbus_conf_dir,
70a90c
 )
70a90c
 
70a90c
 service_conf = configuration_data()
70a90c
 service_conf.set('libexecdir', act_libexecdir)
70a90c
 
70a90c
 service = act_namespace + '.service'
70a90c
 
70a90c
 configure_file(
70a90c
   input: service + '.in',
70a90c
   output: service,
70a90c
   configuration: service_conf,
70a90c
   install: true,
70a90c
   install_dir: dbus_sys_dir,
70a90c
 )
70a90c
 
70a90c
 policy = act_namespace.to_lower() + '.policy'
70a90c
 
70a90c
 i18n.merge_file(
70a90c
   policy,
70a90c
   input: policy + '.in',
70a90c
diff --git a/meson.build b/meson.build
70a90c
index 4465a26..e37c451 100644
70a90c
--- a/meson.build
70a90c
+++ b/meson.build
70a90c
@@ -174,38 +174,39 @@ assert(not enable_systemd or not enable_elogind, 'systemd and elogind support re
70a90c
 if enable_systemd
70a90c
   logind_dep = dependency('libsystemd', version: '>= 186')
70a90c
 endif
70a90c
 
70a90c
 if enable_elogind
70a90c
   logind_dep = dependency('libelogind', version: '>= 229.4')
70a90c
 endif
70a90c
 config_h.set('WITH_SYSTEMD', enable_systemd or enable_elogind)
70a90c
 
70a90c
 subdir('data')
70a90c
 subdir('src')
70a90c
 subdir('po')
70a90c
 
70a90c
 enable_docbook = get_option('docbook')
70a90c
 if enable_docbook
70a90c
   subdir('doc/dbus')
70a90c
 endif
70a90c
 
70a90c
 if get_option('gtk_doc')
70a90c
   subdir('doc/libaccountsservice')
70a90c
 endif
70a90c
 
70a90c
 configure_file(
70a90c
   output: 'config.h',
70a90c
   configuration: config_h,
70a90c
 )
70a90c
 
70a90c
 meson.add_install_script(
70a90c
   'meson_post_install.py',
70a90c
   act_localstatedir,
70a90c
+  act_datadir,
70a90c
 )
70a90c
 
70a90c
 output = '\n' + meson.project_name() + ' was configured with the following options:\n'
70a90c
 output += '** DocBook documentation build: ' + enable_docbook.to_string() + '\n'
70a90c
 output += '** Administrator group: ' + admin_group + '\n'
70a90c
 output += '** Extra administrator groups: ' + extra_admin_groups + '\n'
70a90c
 output += '** GDM configuration: ' + gdm_conf_file
70a90c
 message(output)
70a90c
diff --git a/meson_post_install.py b/meson_post_install.py
70a90c
index 5cc2dc4..e1d5a71 100644
70a90c
--- a/meson_post_install.py
70a90c
+++ b/meson_post_install.py
70a90c
@@ -1,18 +1,33 @@
70a90c
 #!/usr/bin/env python3
70a90c
 
70a90c
 import os
70a90c
 import sys
70a90c
 
70a90c
 destdir = os.environ.get('DESTDIR', '')
70a90c
 localstatedir = os.path.normpath(destdir + os.sep + sys.argv[1])
70a90c
+datadir = os.path.normpath(destdir + os.sep + sys.argv[2])
70a90c
+interfacedir = os.path.join(datadir, 'accountsservice', 'interfaces')
70a90c
 
70a90c
 # FIXME: meson will not track the creation of these directories
70a90c
 #        https://github.com/mesonbuild/meson/blob/master/mesonbuild/scripts/uninstall.py#L39
70a90c
 dst_dirs = [
70a90c
   (os.path.join(localstatedir, 'lib', 'AccountsService', 'icons'), 0o775),
70a90c
   (os.path.join(localstatedir, 'lib', 'AccountsService', 'users'), 0o700),
70a90c
+  (interfacedir, 0o775),
70a90c
 ]
70a90c
 
70a90c
 for (dst_dir, dst_dir_mode) in dst_dirs:
70a90c
   if not os.path.exists(dst_dir):
70a90c
     os.makedirs(dst_dir, mode=dst_dir_mode)
70a90c
+
70a90c
+interface_files = [
70a90c
+  'com.redhat.AccountsServiceUser.System.xml',
70a90c
+]
70a90c
+
70a90c
+for interface_file in interface_files:
70a90c
+    src_path = os.path.join('../../dbus-1/interfaces', interface_file)
70a90c
+    dst_path = os.path.join(interfacedir, interface_file)
70a90c
+    if not os.path.exists(dst_path):
70a90c
+        os.symlink(src_path, dst_path)
70a90c
+
70a90c
+
70a90c
diff --git a/src/libaccountsservice/act-user-manager.c b/src/libaccountsservice/act-user-manager.c
70a90c
index 1b5298d..f4598c4 100644
70a90c
--- a/src/libaccountsservice/act-user-manager.c
70a90c
+++ b/src/libaccountsservice/act-user-manager.c
70a90c
@@ -27,60 +27,61 @@
70a90c
 #include <string.h>
70a90c
 #include <signal.h>
70a90c
 #include <errno.h>
70a90c
 #include <sys/stat.h>
70a90c
 #include <sys/types.h>
70a90c
 
70a90c
 #ifdef HAVE_PATHS_H
70a90c
 #include <paths.h>
70a90c
 #endif /* HAVE_PATHS_H */
70a90c
 
70a90c
 #include <glib.h>
70a90c
 #include <glib/gi18n-lib.h>
70a90c
 #include <glib/gstdio.h>
70a90c
 #include <glib-object.h>
70a90c
 #include <gio/gio.h>
70a90c
 #include <gio/gunixinputstream.h>
70a90c
 
70a90c
 #ifdef WITH_SYSTEMD
70a90c
 #include <systemd/sd-login.h>
70a90c
 
70a90c
 /* check if logind is running */
70a90c
 #define LOGIND_RUNNING() (access("/run/systemd/seats/", F_OK) >= 0)
70a90c
 #endif
70a90c
 
70a90c
 #include "act-user-manager.h"
70a90c
 #include "act-user-private.h"
70a90c
 #include "accounts-generated.h"
70a90c
 #include "ck-manager-generated.h"
70a90c
 #include "ck-seat-generated.h"
70a90c
 #include "ck-session-generated.h"
70a90c
+#include "com.redhat.AccountsServiceUser.System.h"
70a90c
 
70a90c
 /**
70a90c
  * SECTION:act-user-manager
70a90c
  * @title: ActUserManager
70a90c
  * @short_description: manages ActUser objects
70a90c
  *
70a90c
  * ActUserManager is a manager object that gives access to user
70a90c
  * creation, deletion, enumeration, etc.
70a90c
  *
70a90c
  * There is typically a singleton ActUserManager object, which
70a90c
  * can be obtained by act_user_manager_get_default().
70a90c
  */
70a90c
 
70a90c
 /**
70a90c
  * ActUserManager:
70a90c
  *
70a90c
  * A user manager object.
70a90c
  */
70a90c
 
70a90c
 /**
70a90c
  * ACT_USER_MANAGER_ERROR:
70a90c
  *
70a90c
  * The GError domain for #ActUserManagerError errors
70a90c
  */
70a90c
 
70a90c
 /**
70a90c
  * ActUserManagerError:
70a90c
  * @ACT_USER_MANAGER_ERROR_FAILED: Generic failure
70a90c
  * @ACT_USER_MANAGER_ERROR_USER_EXISTS: The user already exists
70a90c
  * @ACT_USER_MANAGER_ERROR_USER_DOES_NOT_EXIST: The user does not exist
70a90c
@@ -165,60 +166,63 @@ typedef struct
70a90c
         ActUser                    *user;
70a90c
         ActUserManagerFetchUserRequestType type;
70a90c
         union {
70a90c
                 char               *username;
70a90c
                 uid_t               uid;
70a90c
         };
70a90c
         char                       *object_path;
70a90c
         char                       *description;
70a90c
 } ActUserManagerFetchUserRequest;
70a90c
 
70a90c
 typedef struct
70a90c
 {
70a90c
         GHashTable            *normal_users_by_name;
70a90c
         GHashTable            *system_users_by_name;
70a90c
         GHashTable            *users_by_object_path;
70a90c
         GHashTable            *sessions;
70a90c
         GDBusConnection       *connection;
70a90c
         AccountsAccounts      *accounts_proxy;
70a90c
         ConsoleKitManager     *ck_manager_proxy;
70a90c
 
70a90c
         ActUserManagerSeat     seat;
70a90c
 
70a90c
         GSList                *new_sessions;
70a90c
         GSList                *new_users;
70a90c
         GSList                *new_users_inhibiting_load;
70a90c
         GSList                *fetch_user_requests;
70a90c
 
70a90c
         GSList                *exclude_usernames;
70a90c
         GSList                *include_usernames;
70a90c
 
70a90c
+        char                  *os_id;
70a90c
+        char                  *os_version_id;
70a90c
+
70a90c
         guint                  load_id;
70a90c
 
70a90c
         gboolean               is_loaded;
70a90c
         gboolean               has_multiple_users;
70a90c
         gboolean               getting_sessions;
70a90c
         gboolean               list_cached_users_done;
70a90c
 } ActUserManagerPrivate;
70a90c
 
70a90c
 enum {
70a90c
         PROP_0,
70a90c
         PROP_INCLUDE_USERNAMES_LIST,
70a90c
         PROP_EXCLUDE_USERNAMES_LIST,
70a90c
         PROP_IS_LOADED,
70a90c
         PROP_HAS_MULTIPLE_USERS
70a90c
 };
70a90c
 
70a90c
 enum {
70a90c
         USER_ADDED,
70a90c
         USER_REMOVED,
70a90c
         USER_IS_LOGGED_IN_CHANGED,
70a90c
         USER_CHANGED,
70a90c
         LAST_SIGNAL
70a90c
 };
70a90c
 
70a90c
 static guint signals [LAST_SIGNAL] = { 0, };
70a90c
 
70a90c
 static void     act_user_manager_class_init (ActUserManagerClass *klass);
70a90c
 static void     act_user_manager_init       (ActUserManager      *user_manager);
70a90c
 static void     act_user_manager_finalize   (GObject             *object);
70a90c
 
70a90c
@@ -2942,100 +2946,173 @@ ensure_accounts_proxy (ActUserManager *manager)
70a90c
                                                                  G_DBUS_PROXY_FLAGS_NONE,
70a90c
                                                                  ACCOUNTS_NAME,
70a90c
                                                                  ACCOUNTS_PATH,
70a90c
                                                                  NULL,
70a90c
                                                                  &error);
70a90c
         if (error != NULL) {
70a90c
                 g_debug ("ActUserManager: getting account proxy failed: %s", error->message);
70a90c
                 return FALSE;
70a90c
         }
70a90c
 
70a90c
         g_dbus_proxy_set_default_timeout (G_DBUS_PROXY (priv->accounts_proxy), G_MAXINT);
70a90c
 
70a90c
         g_object_bind_property (G_OBJECT (priv->accounts_proxy),
70a90c
                                 "has-multiple-users",
70a90c
                                 G_OBJECT (manager),
70a90c
                                 "has-multiple-users",
70a90c
                                 G_BINDING_SYNC_CREATE);
70a90c
 
70a90c
         g_signal_connect (priv->accounts_proxy,
70a90c
                           "user-added",
70a90c
                           G_CALLBACK (on_new_user_in_accounts_service),
70a90c
                           manager);
70a90c
         g_signal_connect (priv->accounts_proxy,
70a90c
                           "user-deleted",
70a90c
                           G_CALLBACK (on_user_removed_in_accounts_service),
70a90c
                           manager);
70a90c
 
70a90c
         return TRUE;
70a90c
 }
70a90c
 
70a90c
+static inline gboolean
70a90c
+is_valid_char (gchar    c,
70a90c
+               gboolean first)
70a90c
+{
70a90c
+        return (!first && g_ascii_isdigit (c)) ||
70a90c
+                c == '_' ||
70a90c
+                g_ascii_isalpha (c);
70a90c
+}
70a90c
+
70a90c
+static void
70a90c
+load_os_release (ActUserManager *manager)
70a90c
+{
70a90c
+        ActUserManagerPrivate *priv = act_user_manager_get_instance_private (manager);
70a90c
+        g_autoptr(GFile) file = NULL;
70a90c
+        g_autoptr(GError) error = NULL;
70a90c
+        g_autofree char *contents = NULL;
70a90c
+        g_auto(GStrv) lines = NULL;
70a90c
+        size_t i;
70a90c
+
70a90c
+        file = g_file_new_for_path ("/etc/os-release");
70a90c
+
70a90c
+        if (!g_file_load_contents (file, NULL, &contents, NULL, NULL, &error)) {
70a90c
+                g_debug ("ActUserManager: couldn't load /etc/os-release: %s", error->message);
70a90c
+                return;
70a90c
+        }
70a90c
+
70a90c
+        lines = g_strsplit (contents, "\n", -1);
70a90c
+        for (i = 0; lines[i] != NULL; i++) {
70a90c
+                char *p, *name, *name_end, *value, *value_end;
70a90c
+
70a90c
+                p = lines[i];
70a90c
+
70a90c
+                while (g_ascii_isspace (*p))
70a90c
+                        p++;
70a90c
+
70a90c
+                if (*p == '#' || *p == '\0')
70a90c
+                        continue;
70a90c
+                name = p;
70a90c
+                while (is_valid_char (*p, p == name))
70a90c
+                        p++;
70a90c
+                name_end = p;
70a90c
+                while (g_ascii_isspace (*p))
70a90c
+                        p++;
70a90c
+                if (name == name_end || *p != '=') {
70a90c
+                        continue;
70a90c
+                }
70a90c
+                *name_end = '\0';
70a90c
+
70a90c
+                p++;
70a90c
+
70a90c
+                while (g_ascii_isspace (*p))
70a90c
+                        p++;
70a90c
+
70a90c
+                value = p;
70a90c
+                value_end = value + strlen (value) - 1;
70a90c
+
70a90c
+                if (value != value_end && *value == '"' && *value_end == '"') {
70a90c
+                        value++;
70a90c
+                        *value_end = '\0';
70a90c
+                }
70a90c
+
70a90c
+                if (strcmp (name, "ID") == 0) {
70a90c
+                        g_debug ("ActUserManager: system OS is '%s'", value);
70a90c
+                        priv->os_id = g_strdup (value);
70a90c
+                } else if (strcmp (name, "VERSION_ID") == 0) {
70a90c
+                        g_debug ("ActUserManager: system OS version is '%s'", value);
70a90c
+                        priv->os_version_id = g_strdup (value);
70a90c
+                }
70a90c
+        }
70a90c
+}
70a90c
+
70a90c
 static void
70a90c
 act_user_manager_init (ActUserManager *manager)
70a90c
 {
70a90c
         ActUserManagerPrivate *priv = act_user_manager_get_instance_private (manager);
70a90c
         g_autoptr(GError) error = NULL;
70a90c
 
70a90c
         act_user_manager_error_quark (); /* register dbus errors */
70a90c
 
70a90c
         /* sessions */
70a90c
         priv->sessions = g_hash_table_new_full (g_str_hash,
70a90c
                                                 g_str_equal,
70a90c
                                                 g_free,
70a90c
                                                 g_object_unref);
70a90c
 
70a90c
         /* users */
70a90c
         priv->normal_users_by_name = g_hash_table_new_full (g_str_hash,
70a90c
                                                             g_str_equal,
70a90c
                                                             g_free,
70a90c
                                                             g_object_unref);
70a90c
         priv->system_users_by_name = g_hash_table_new_full (g_str_hash,
70a90c
                                                             g_str_equal,
70a90c
                                                             g_free,
70a90c
                                                             g_object_unref);
70a90c
         priv->users_by_object_path = g_hash_table_new_full (g_str_hash,
70a90c
                                                             g_str_equal,
70a90c
                                                             NULL,
70a90c
                                                             g_object_unref);
70a90c
 
70a90c
         priv->connection = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, &error);
70a90c
         if (priv->connection == NULL) {
70a90c
                 if (error != NULL) {
70a90c
                         g_warning ("Failed to connect to the D-Bus daemon: %s", error->message);
70a90c
                 } else {
70a90c
                         g_warning ("Failed to connect to the D-Bus daemon");
70a90c
                 }
70a90c
                 return;
70a90c
         }
70a90c
 
70a90c
         ensure_accounts_proxy (manager);
70a90c
 
70a90c
+        load_os_release (manager);
70a90c
+
70a90c
         priv->seat.state = ACT_USER_MANAGER_SEAT_STATE_UNLOADED;
70a90c
 }
70a90c
 
70a90c
 static void
70a90c
 act_user_manager_finalize (GObject *object)
70a90c
 {
70a90c
         ActUserManager *manager = ACT_USER_MANAGER (object);
70a90c
         ActUserManagerPrivate *priv = act_user_manager_get_instance_private (manager);
70a90c
         GSList         *node;
70a90c
 
70a90c
         g_debug ("ActUserManager: finalizing user manager");
70a90c
 
70a90c
         g_slist_foreach (priv->new_sessions,
70a90c
                          (GFunc) unload_new_session, NULL);
70a90c
         g_slist_free (priv->new_sessions);
70a90c
 
70a90c
         g_slist_foreach (priv->fetch_user_requests,
70a90c
                          (GFunc) free_fetch_user_request, NULL);
70a90c
         g_slist_free (priv->fetch_user_requests);
70a90c
 
70a90c
         g_slist_free (priv->new_users_inhibiting_load);
70a90c
 
70a90c
         node = priv->new_users;
70a90c
         while (node != NULL) {
70a90c
                 ActUser *user;
70a90c
                 GSList  *next_node;
70a90c
 
70a90c
                 user = ACT_USER (node->data);
70a90c
                 next_node = node->next;
70a90c
 
70a90c
@@ -3071,143 +3148,181 @@ act_user_manager_finalize (GObject *object)
70a90c
 
70a90c
 #ifdef WITH_SYSTEMD
70a90c
         if (priv->seat.session_monitor != NULL) {
70a90c
                 sd_login_monitor_unref (priv->seat.session_monitor);
70a90c
         }
70a90c
 
70a90c
         if (priv->seat.session_monitor_stream != NULL) {
70a90c
                 g_object_unref (priv->seat.session_monitor_stream);
70a90c
         }
70a90c
 
70a90c
         if (priv->seat.session_monitor_source_id != 0) {
70a90c
                 g_source_remove (priv->seat.session_monitor_source_id);
70a90c
         }
70a90c
 #endif
70a90c
 
70a90c
         if (priv->accounts_proxy != NULL) {
70a90c
                 g_object_unref (priv->accounts_proxy);
70a90c
         }
70a90c
 
70a90c
         if (priv->load_id > 0) {
70a90c
                 g_source_remove (priv->load_id);
70a90c
                 priv->load_id = 0;
70a90c
         }
70a90c
 
70a90c
         g_hash_table_destroy (priv->sessions);
70a90c
 
70a90c
         g_hash_table_destroy (priv->normal_users_by_name);
70a90c
         g_hash_table_destroy (priv->system_users_by_name);
70a90c
         g_hash_table_destroy (priv->users_by_object_path);
70a90c
 
70a90c
+        g_free (priv->os_id);
70a90c
+        g_free (priv->os_version_id);
70a90c
+
70a90c
         G_OBJECT_CLASS (act_user_manager_parent_class)->finalize (object);
70a90c
 }
70a90c
 
70a90c
 /**
70a90c
  * act_user_manager_get_default:
70a90c
  *
70a90c
  * Returns the user manager singleton instance.  Calling this function will
70a90c
  * automatically being loading the user list if it isn't loaded already.
70a90c
  * The #ActUserManager:is-loaded property will be set to %TRUE when the users
70a90c
  * are finished loading and then act_user_manager_list_users() can be called.
70a90c
  *
70a90c
  * Returns: (transfer none): user manager object
70a90c
  */
70a90c
 ActUserManager *
70a90c
 act_user_manager_get_default (void)
70a90c
 {
70a90c
         if (user_manager_object == NULL) {
70a90c
                 user_manager_object = g_object_new (ACT_TYPE_USER_MANAGER, NULL);
70a90c
                 g_object_add_weak_pointer (user_manager_object,
70a90c
                                            (gpointer *) &user_manager_object);
70a90c
                 act_user_manager_queue_load (user_manager_object);
70a90c
         }
70a90c
 
70a90c
         return ACT_USER_MANAGER (user_manager_object);
70a90c
 }
70a90c
 
70a90c
 /**
70a90c
  * act_user_manager_no_service:
70a90c
  * @manager: a #ActUserManager
70a90c
  *
70a90c
  * Check whether or not the accounts service is running.
70a90c
  *
70a90c
  * Returns: whether or not accounts service is running
70a90c
  */
70a90c
 gboolean
70a90c
 act_user_manager_no_service (ActUserManager *manager)
70a90c
 {
70a90c
         ActUserManagerPrivate *priv = act_user_manager_get_instance_private (manager);
70a90c
         return priv->accounts_proxy == NULL;
70a90c
 }
70a90c
 
70a90c
+static void
70a90c
+save_system_info (ActUserManager *manager,
70a90c
+                  const char     *user_path)
70a90c
+{
70a90c
+        ActUserManagerPrivate *priv = act_user_manager_get_instance_private (manager);
70a90c
+        ActUserSystem *user_system_proxy = NULL;
70a90c
+        g_autoptr(GError) error = NULL;
70a90c
+
70a90c
+        if (priv->os_id == NULL && priv->os_version_id == NULL)
70a90c
+                return;
70a90c
+
70a90c
+        user_system_proxy = act_user_system_proxy_new_sync (priv->connection,
70a90c
+                                                            G_DBUS_PROXY_FLAGS_NONE,
70a90c
+                                                            ACCOUNTS_NAME,
70a90c
+                                                            user_path,
70a90c
+                                                            NULL,
70a90c
+                                                            &error);
70a90c
+        if (user_system_proxy != NULL) {
70a90c
+                if (priv->os_id != NULL)
70a90c
+                        act_user_system_set_id (user_system_proxy, priv->os_id);
70a90c
+
70a90c
+                if (priv->os_version_id != NULL)
70a90c
+                        act_user_system_set_version_id (user_system_proxy, priv->os_version_id);
70a90c
+        } else {
70a90c
+                /* probably means accountsservice and lib are out of sync */
70a90c
+                g_debug ("ActUserManager: failed to create user system proxy: %s",
70a90c
+                         error? error->message: "");
70a90c
+                g_clear_error (&error);
70a90c
+        }
70a90c
+
70a90c
+        g_clear_object (&user_system_proxy);
70a90c
+}
70a90c
+
70a90c
 /**
70a90c
  * act_user_manager_create_user:
70a90c
  * @manager: a #ActUserManager
70a90c
  * @username: a unix user name
70a90c
  * @fullname: a unix GECOS value
70a90c
  * @accounttype: a #ActUserAccountType
70a90c
  * @error: a #GError
70a90c
  *
70a90c
  * Creates a user account on the system.
70a90c
  *
70a90c
  * Returns: (transfer full): user object
70a90c
  */
70a90c
 ActUser *
70a90c
 act_user_manager_create_user (ActUserManager      *manager,
70a90c
                               const char          *username,
70a90c
                               const char          *fullname,
70a90c
                               ActUserAccountType   accounttype,
70a90c
                               GError             **error)
70a90c
 {
70a90c
         ActUserManagerPrivate *priv = act_user_manager_get_instance_private (manager);
70a90c
         GError *local_error = NULL;
70a90c
         gboolean res;
70a90c
         g_autofree gchar *path = NULL;
70a90c
         ActUser *user;
70a90c
 
70a90c
         g_debug ("ActUserManager: Creating user '%s', '%s', %d",
70a90c
                  username, fullname, accounttype);
70a90c
 
70a90c
         g_assert (priv->accounts_proxy != NULL);
70a90c
 
70a90c
         res = accounts_accounts_call_create_user_sync (priv->accounts_proxy,
70a90c
                                                        username,
70a90c
                                                        fullname,
70a90c
                                                        accounttype,
70a90c
                                                        &path,
70a90c
                                                        NULL,
70a90c
                                                        &local_error);
70a90c
         if (!res) {
70a90c
                 g_propagate_error (error, local_error);
70a90c
                 return NULL;
70a90c
         }
70a90c
 
70a90c
+        save_system_info (manager, path);
70a90c
+
70a90c
         user = add_new_user_for_object_path (path, manager);
70a90c
 
70a90c
         return user;
70a90c
 }
70a90c
 
70a90c
 static void
70a90c
 act_user_manager_async_complete_handler (GObject      *source,
70a90c
                                          GAsyncResult *result,
70a90c
                                          gpointer      user_data)
70a90c
 {
70a90c
         GTask *task = user_data;
70a90c
 
70a90c
         g_task_return_pointer (task, g_object_ref (result), g_object_unref);
70a90c
         g_object_unref (task);
70a90c
 }
70a90c
 
70a90c
 /**
70a90c
  * act_user_manager_create_user_async:
70a90c
  * @manager: a #ActUserManager
70a90c
  * @username: a unix user name
70a90c
  * @fullname: a unix GECOS value
70a90c
  * @accounttype: a #ActUserAccountType
70a90c
  * @cancellable: (allow-none): optional #GCancellable object,
70a90c
  *     %NULL to ignore
70a90c
  * @callback: (scope async): a #GAsyncReadyCallback to call
70a90c
  *     when the request is satisfied
70a90c
  * @user_data: (closure): the data to pass to @callback
70a90c
  *
70a90c
  * Asynchronously creates a user account on the system.
70a90c
  *
70a90c
@@ -3253,106 +3368,111 @@ act_user_manager_create_user_async (ActUserManager      *manager,
70a90c
  * @manager: a #ActUserManager
70a90c
  * @result: a #GAsyncResult
70a90c
  * @error: a #GError
70a90c
  *
70a90c
  * Finishes an asynchronous user creation.
70a90c
  *
70a90c
  * See act_user_manager_create_user_async().
70a90c
  *
70a90c
  * Returns: (transfer full): user object
70a90c
  *
70a90c
  * Since: 0.6.27
70a90c
  */
70a90c
 ActUser *
70a90c
 act_user_manager_create_user_finish (ActUserManager  *manager,
70a90c
                                      GAsyncResult    *result,
70a90c
                                      GError         **error)
70a90c
 {
70a90c
         ActUserManagerPrivate *priv = act_user_manager_get_instance_private (manager);
70a90c
         GAsyncResult *inner_result;
70a90c
         ActUser *user = NULL;
70a90c
         g_autofree gchar *path = NULL;
70a90c
         GError *remote_error = NULL;
70a90c
 
70a90c
         inner_result = g_task_propagate_pointer (G_TASK (result), error);
70a90c
         if (inner_result == NULL) {
70a90c
                 return FALSE;
70a90c
         }
70a90c
 
70a90c
         if (accounts_accounts_call_create_user_finish (priv->accounts_proxy,
70a90c
                                                        &path, inner_result, &remote_error)) {
70a90c
+
70a90c
+                save_system_info (manager, path);
70a90c
+
70a90c
                 user = add_new_user_for_object_path (path, manager);
70a90c
         }
70a90c
 
70a90c
         if (remote_error) {
70a90c
                 g_dbus_error_strip_remote_error (remote_error);
70a90c
                 g_propagate_error (error, remote_error);
70a90c
         }
70a90c
 
70a90c
         return user;
70a90c
 }
70a90c
 
70a90c
 /**
70a90c
  * act_user_manager_cache_user:
70a90c
  * @manager: a #ActUserManager
70a90c
  * @username: a user name
70a90c
  * @error: a #GError
70a90c
  *
70a90c
  * Caches a user account so it shows up via act_user_manager_list_users().
70a90c
  *
70a90c
  * Returns: (transfer full): user object
70a90c
  */
70a90c
 ActUser *
70a90c
 act_user_manager_cache_user (ActUserManager     *manager,
70a90c
                              const char         *username,
70a90c
                              GError            **error)
70a90c
 {
70a90c
         ActUserManagerPrivate *priv = act_user_manager_get_instance_private (manager);
70a90c
         GError *local_error = NULL;
70a90c
         gboolean res;
70a90c
         g_autofree gchar *path = NULL;
70a90c
 
70a90c
         g_debug ("ActUserManager: Caching user '%s'",
70a90c
                  username);
70a90c
 
70a90c
         g_assert (priv->accounts_proxy != NULL);
70a90c
 
70a90c
         res = accounts_accounts_call_cache_user_sync (priv->accounts_proxy,
70a90c
                                                       username,
70a90c
                                                       &path,
70a90c
                                                       NULL,
70a90c
                                                       &local_error);
70a90c
         if (!res) {
70a90c
                 g_propagate_error (error, local_error);
70a90c
                 return NULL;
70a90c
         }
70a90c
 
70a90c
+        save_system_info (manager, path);
70a90c
+
70a90c
         return add_new_user_for_object_path (path, manager);
70a90c
 }
70a90c
 
70a90c
 
70a90c
 /**
70a90c
  * act_user_manager_cache_user_async:
70a90c
  * @manager: a #ActUserManager
70a90c
  * @username: a unix user name
70a90c
  * @cancellable: (allow-none): optional #GCancellable object,
70a90c
  *     %NULL to ignore
70a90c
  * @callback: (scope async): a #GAsyncReadyCallback to call
70a90c
  *     when the request is satisfied
70a90c
  * @user_data: (closure): the data to pass to @callback
70a90c
  *
70a90c
  * Asynchronously caches a user account so it shows up via
70a90c
  * act_user_manager_list_users().
70a90c
  *
70a90c
  * For more details, see act_user_manager_cache_user(), which
70a90c
  * is the synchronous version of this call.
70a90c
  *
70a90c
  * Since: 0.6.27
70a90c
  */
70a90c
 void
70a90c
 act_user_manager_cache_user_async (ActUserManager      *manager,
70a90c
                                    const char          *username,
70a90c
                                    GCancellable        *cancellable,
70a90c
                                    GAsyncReadyCallback  callback,
70a90c
                                    gpointer             user_data)
70a90c
 {
70a90c
         ActUserManagerPrivate *priv = act_user_manager_get_instance_private (manager);
70a90c
@@ -3378,60 +3498,63 @@ act_user_manager_cache_user_async (ActUserManager      *manager,
70a90c
  * @manager: a #ActUserManager
70a90c
  * @result: a #GAsyncResult
70a90c
  * @error: a #GError
70a90c
  *
70a90c
  * Finishes an asynchronous user caching.
70a90c
  *
70a90c
  * See act_user_manager_cache_user_async().
70a90c
  *
70a90c
  * Returns: (transfer full): user object
70a90c
  *
70a90c
  * Since: 0.6.27
70a90c
  */
70a90c
 ActUser *
70a90c
 act_user_manager_cache_user_finish (ActUserManager  *manager,
70a90c
                                     GAsyncResult    *result,
70a90c
                                     GError         **error)
70a90c
 {
70a90c
         ActUserManagerPrivate *priv = act_user_manager_get_instance_private (manager);
70a90c
         GAsyncResult *inner_result;
70a90c
         ActUser *user = NULL;
70a90c
         g_autofree gchar *path = NULL;
70a90c
         GError *remote_error = NULL;
70a90c
 
70a90c
         inner_result = g_task_propagate_pointer (G_TASK (result), error);
70a90c
         if (inner_result == NULL) {
70a90c
                 return FALSE;
70a90c
         }
70a90c
 
70a90c
         if (accounts_accounts_call_cache_user_finish (priv->accounts_proxy,
70a90c
                                                       &path, inner_result, &remote_error)) {
70a90c
+
70a90c
+                save_system_info (manager, path);
70a90c
+
70a90c
                 user = add_new_user_for_object_path (path, manager);
70a90c
         }
70a90c
 
70a90c
         if (remote_error) {
70a90c
                 g_dbus_error_strip_remote_error (remote_error);
70a90c
                 g_propagate_error (error, remote_error);
70a90c
         }
70a90c
 
70a90c
         return user;
70a90c
 }
70a90c
 
70a90c
 /**
70a90c
  * act_user_manager_uncache_user:
70a90c
  * @manager: a #ActUserManager
70a90c
  * @username: a user name
70a90c
  * @error: a #GError
70a90c
  *
70a90c
  * Releases all metadata about a user account, including icon,
70a90c
  * language and session. If the user account is from a remote
70a90c
  * server and the user has never logged in before, then that
70a90c
  * account will no longer show up in ListCachedUsers() output.
70a90c
  *
70a90c
  * Returns: %TRUE if successful, otherwise %FALSE
70a90c
  */
70a90c
 gboolean
70a90c
 act_user_manager_uncache_user (ActUserManager     *manager,
70a90c
                                const char         *username,
70a90c
                                GError            **error)
70a90c
 {
70a90c
         ActUserManagerPrivate *priv = act_user_manager_get_instance_private (manager);
70a90c
diff --git a/src/libaccountsservice/meson.build b/src/libaccountsservice/meson.build
70a90c
index 4e134db..9e85bba 100644
70a90c
--- a/src/libaccountsservice/meson.build
70a90c
+++ b/src/libaccountsservice/meson.build
70a90c
@@ -21,60 +21,67 @@ enum_types = 'act-user-enum-types'
70a90c
 
70a90c
 enum_sources = gnome.mkenums(
70a90c
   enum_types,
70a90c
   sources: headers,
70a90c
   c_template: enum_types + '.c.template',
70a90c
   h_template: enum_types + '.h.template',
70a90c
   install_header: true,
70a90c
   install_dir: join_paths(act_pkgincludedir, subdir),
70a90c
 )
70a90c
 
70a90c
 dbus_sources = []
70a90c
 
70a90c
 ifaces = [
70a90c
   'Manager',
70a90c
   'Seat',
70a90c
   'Session',
70a90c
 ]
70a90c
 
70a90c
 namespace = 'ConsoleKit'
70a90c
 prefix = 'org.freedesktop.' + namespace
70a90c
 
70a90c
 foreach iface: ifaces
70a90c
   dbus_sources += gnome.gdbus_codegen(
70a90c
     'ck-@0@-generated'.format(iface.to_lower()),
70a90c
     '@0@.@1@.xml'.format(prefix, iface),
70a90c
     interface_prefix: prefix,
70a90c
     namespace: namespace,
70a90c
   )
70a90c
 endforeach
70a90c
 
70a90c
+dbus_sources += gnome.gdbus_codegen(
70a90c
+   'com.redhat.AccountsServiceUser.System',
70a90c
+   join_paths(data_dir, 'com.redhat.AccountsServiceUser.System.xml'),
70a90c
+   interface_prefix: 'com.redhat.AccountsService',
70a90c
+   namespace: 'Act',
70a90c
+)
70a90c
+
70a90c
 deps = [
70a90c
   crypt_dep,
70a90c
   gio_unix_dep,
70a90c
   glib_dep,
70a90c
   libaccounts_generated_dep,
70a90c
 ]
70a90c
 
70a90c
 symbol_map = join_paths(meson.current_source_dir(), 'symbol.map')
70a90c
 ldflags = cc.get_supported_link_arguments('-Wl,--version-script,@0@'.format(symbol_map))
70a90c
 
70a90c
 if enable_systemd or enable_elogind
70a90c
   deps += logind_dep
70a90c
 endif
70a90c
 
70a90c
 libaccountsservice = shared_library(
70a90c
   act_name,
70a90c
   sources: sources + enum_sources + dbus_sources,
70a90c
   version: libversion,
70a90c
   include_directories: top_inc,
70a90c
   dependencies: deps,
70a90c
   c_args: '-DG_LOG_DOMAIN="@0@"'.format(meson.project_name()),
70a90c
   link_args: ldflags,
70a90c
   link_depends: symbol_map,
70a90c
   install: true,
70a90c
 )
70a90c
 
70a90c
 libaccountsservice_dep = declare_dependency(
70a90c
   sources: enum_sources[1],
70a90c
   include_directories: include_directories('.'),
70a90c
   dependencies: [gio_dep, glib_dep],
70a90c
-- 
70a90c
2.27.0
70a90c