From ece2828cb3b58ac2aa9febb87a9c83f4558288cc Mon Sep 17 00:00:00 2001 From: CentOS Sources Date: Aug 01 2019 17:29:16 +0000 Subject: import accountsservice-0.6.50-7.el8 --- diff --git a/.accountsservice.metadata b/.accountsservice.metadata new file mode 100644 index 0000000..9ffdd86 --- /dev/null +++ b/.accountsservice.metadata @@ -0,0 +1 @@ +8d59b9cdc4121b34748442ee653b92d60607f2cb SOURCES/accountsservice-0.6.50.tar.xz diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..32db008 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +SOURCES/accountsservice-0.6.50.tar.xz diff --git a/SOURCES/0001-daemon-Fix-warnings-about-type-punning.patch b/SOURCES/0001-daemon-Fix-warnings-about-type-punning.patch new file mode 100644 index 0000000..8a514cc --- /dev/null +++ b/SOURCES/0001-daemon-Fix-warnings-about-type-punning.patch @@ -0,0 +1,349 @@ +From dee5f443807fee3b5b279d0488df617eeed52230 Mon Sep 17 00:00:00 2001 +From: Robert Ancell +Date: Thu, 6 Sep 2018 14:37:39 +1200 +Subject: [PATCH] daemon: Fix warnings about type-punning + +--- + src/daemon.c | 26 +++++++++++++++----------- + 1 file changed, 15 insertions(+), 11 deletions(-) + +diff --git a/src/daemon.c b/src/daemon.c +index 2587b8a..00dff51 100644 +--- a/src/daemon.c ++++ b/src/daemon.c +@@ -232,117 +232,118 @@ entry_generator_fgetpwent (Daemon *daemon, + 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 (Daemon *daemon, + GHashTable *users, + gpointer *state, + struct spwd **shadow_entry) + { + struct passwd *pwent; + g_autoptr(GError) error = NULL; + gboolean regular; + GHashTableIter iter; +- const gchar *name; +- User *user; ++ gpointer key, value; + 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); + 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) { + const gchar *name; + g_autofree gchar *filename = NULL; + + 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); + + if (regular) { + errno = 0; + pwent = getpwnam (name); + if (pwent != NULL) { + *shadow_entry = getspnam (pwent->pw_name); + + return pwent; + } else if (errno == 0) { + g_debug ("user '%s' in cache dir but not present on system, removing", name); + remove_cache_files (name); + } + else { + g_warning ("failed to check if user '%s' in cache dir is present on system: %s", + name, g_strerror (errno)); + } + } + } + + /* 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)) { ++ while (g_hash_table_iter_next (&iter, &key, &value)) { ++ const gchar *name = key; ++ User *user = value; + g_autofree gchar *filename = NULL; + g_autoptr(GKeyFile) key_file = NULL; + + 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); + } + + *state = NULL; + return NULL; + } + + static struct passwd * + entry_generator_requested_users (Daemon *daemon, + GHashTable *users, + gpointer *state, + struct spwd **shadow_entry) + { + struct passwd *pwent; + GList *node; + + /* First iteration */ + if (*state == NULL) { + *state = daemon->priv->explicitly_requested_users; + } + + /* Every iteration */ + + if (g_hash_table_size (users) < MAX_LOCAL_USERS) { +@@ -423,129 +424,131 @@ load_entries (Daemon *daemon, + } + + 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; ++ gpointer name, value; + + /* 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); + + /* 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, &value)) { ++ User *user = value; + 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)) { ++ while (g_hash_table_iter_next (&iter, &name, &value)) { ++ User *user = value; + User *refreshed_user; + + refreshed_user = g_hash_table_lookup (users, name); + + if (!refreshed_user || (user_get_cached (user) && !user_get_cached (refreshed_user))) { + accounts_accounts_emit_user_deleted (ACCOUNTS_ACCOUNTS (daemon), + user_get_object_path (user)); + user_unregister (user); + } + } + + /* Register all the new users */ + 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, &value)) { ++ User *user = value; + 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)); + } + g_object_thaw_notify (G_OBJECT (user)); + } + + g_hash_table_destroy (old_users); + } + + static gboolean + reload_users_timeout (Daemon *daemon) + { + reload_users (daemon); + daemon->priv->reload_id = 0; + + return FALSE; + } + + static gboolean load_autologin (Daemon *daemon, + gchar **name, + gboolean *enabled, + GError **error); + + static gboolean +@@ -932,69 +935,70 @@ typedef struct { + } ListUserData; + + + static ListUserData * + list_user_data_new (Daemon *daemon, + GDBusMethodInvocation *context) + { + ListUserData *data; + + data = g_new0 (ListUserData, 1); + + data->daemon = g_object_ref (daemon); + data->context = context; + + return data; + } + + static void + list_user_data_free (ListUserData *data) + { + g_object_unref (data->daemon); + g_free (data); + } + + static gboolean + finish_list_cached_users (gpointer user_data) + { + ListUserData *data = user_data; + g_autoptr(GPtrArray) object_paths = NULL; + GHashTableIter iter; +- const gchar *name; +- User *user; ++ gpointer key, value; + uid_t uid; + const gchar *shell; + + object_paths = g_ptr_array_new (); + + g_hash_table_iter_init (&iter, data->daemon->priv->users); +- while (g_hash_table_iter_next (&iter, (gpointer *)&name, (gpointer *)&user)) { ++ while (g_hash_table_iter_next (&iter, &key, &value)) { ++ const gchar *name = key; ++ User *user = value; + uid = user_get_uid (user); + shell = user_get_shell (user); + + if (!user_classify_is_human (uid, name, shell, NULL)) { + g_debug ("user %s %ld excluded", name, (long) uid); + continue; + } + + if (!user_get_cached (user)) { + g_debug ("user %s %ld not cached", name, (long) uid); + continue; + } + + g_debug ("user %s %ld not excluded", name, (long) uid); + g_ptr_array_add (object_paths, (gpointer) user_get_object_path (user)); + } + g_ptr_array_add (object_paths, NULL); + + accounts_accounts_complete_list_cached_users (NULL, data->context, (const gchar * const *) object_paths->pdata); + + list_user_data_free (data); + + return FALSE; + } + + static gboolean + daemon_list_cached_users (AccountsAccounts *accounts, + GDBusMethodInvocation *context) + { + Daemon *daemon = (Daemon*)accounts; +-- +2.17.1 + diff --git a/SOURCES/0001-data-don-t-send-change-updates-for-login-history.patch b/SOURCES/0001-data-don-t-send-change-updates-for-login-history.patch new file mode 100644 index 0000000..56489a9 --- /dev/null +++ b/SOURCES/0001-data-don-t-send-change-updates-for-login-history.patch @@ -0,0 +1,88 @@ +From b4f85d66280affcb52e998661f782c2ab4f806a7 Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Thu, 9 May 2019 14:58:34 -0400 +Subject: [PATCH] data: don't send change updates for login-history + +The login-history property of user objects can be quite large. +If wtmp is changed frequently, that can lead to memory fragmentation +in clients. + +Furthermore, most clients never check login-history, so it's +wasted memory and wasted cpu. + +This commit disables change notification for that property. If +a client really needs to get updates, they can manually refresh +their cache when appropriate. +--- + data/org.freedesktop.Accounts.User.xml | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/data/org.freedesktop.Accounts.User.xml b/data/org.freedesktop.Accounts.User.xml +index 8d3fe1c..3b839a3 100644 +--- a/data/org.freedesktop.Accounts.User.xml ++++ b/data/org.freedesktop.Accounts.User.xml +@@ -785,60 +785,61 @@ + + + + The users location. + + + + + + + + + + How often the user has logged in. + + + + + + + + + + The last login time. + + + + + + ++ + + + + The login history for this user. + Each entry in the array represents a login session. The first two + members are the login time and logout time, as timestamps (seconds since the epoch). If the session is still running, the logout time + is 0. + + + The a{sv} member is a dictionary containing additional information + about the session. Possible members include 'type' (with values like ':0', 'tty0', 'pts/0' etc). + + + + + + + + + + The filename of a png file containing the users icon. + + + + + + + + + +-- +2.21.0 + diff --git a/SOURCES/0001-lib-don-t-set-loaded-state-until-seat-is-fetched.patch b/SOURCES/0001-lib-don-t-set-loaded-state-until-seat-is-fetched.patch new file mode 100644 index 0000000..5d6777d --- /dev/null +++ b/SOURCES/0001-lib-don-t-set-loaded-state-until-seat-is-fetched.patch @@ -0,0 +1,90 @@ +From c7fa612023a163e8b2352e1170c6df3fceb19b27 Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Thu, 19 Jul 2018 13:14:09 -0400 +Subject: [PATCH] lib: don't set loaded state until seat is fetched + +At the moment we set is-loaded on the user-manager +object as soon as we start fetching the seat, but +we should waiting until the seat is fetched, so +that can_switch() will return the correct value +if the caller waited until the loaded signal +to use it. + +This commit changes the >= to > which I believe +was the original intention anyway. + +https://bugs.freedesktop.org/show_bug.cgi?id=107298 +--- + src/libaccountsservice/act-user-manager.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/libaccountsservice/act-user-manager.c b/src/libaccountsservice/act-user-manager.c +index 325421b..e7e26b1 100644 +--- a/src/libaccountsservice/act-user-manager.c ++++ b/src/libaccountsservice/act-user-manager.c +@@ -2355,61 +2355,61 @@ act_user_manager_list_users (ActUserManager *manager) + queue_load_seat_incrementally (manager); + } + + retval = NULL; + g_hash_table_foreach (manager->priv->normal_users_by_name, listify_hash_values_hfunc, &retval); + + return g_slist_sort (retval, (GCompareFunc) act_user_collate); + } + + static void + maybe_set_is_loaded (ActUserManager *manager) + { + if (manager->priv->is_loaded) { + g_debug ("ActUserManager: already loaded, so not setting loaded property"); + return; + } + + if (manager->priv->getting_sessions) { + g_debug ("ActUserManager: GetSessions call pending, so not setting loaded property"); + return; + } + + if (manager->priv->new_users_inhibiting_load != NULL) { + g_debug ("ActUserManager: Loading new users, so not setting loaded property"); + return; + } + + /* Don't set is_loaded yet unless the seat is already loaded enough + * or failed to load. + */ +- if (manager->priv->seat.state >= ACT_USER_MANAGER_SEAT_STATE_GET_ID) { ++ if (manager->priv->seat.state > ACT_USER_MANAGER_SEAT_STATE_GET_ID) { + g_debug ("ActUserManager: Seat loaded, so now setting loaded property"); + } else if (manager->priv->seat.state == ACT_USER_MANAGER_SEAT_STATE_UNLOADED) { + g_debug ("ActUserManager: Seat wouldn't load, so giving up on it and setting loaded property"); + } else { + g_debug ("ActUserManager: Seat still actively loading, so not setting loaded property"); + return; + } + + set_is_loaded (manager, TRUE); + } + + + static GSList * + slist_deep_copy (const GSList *list) + { + GSList *retval; + GSList *l; + + if (list == NULL) + return NULL; + + retval = g_slist_copy ((GSList *) list); + for (l = retval; l != NULL; l = l->next) { + l->data = g_strdup (l->data); + } + + return retval; + } + + static void +-- +2.17.1 + diff --git a/SOURCES/0001-lib-save-os-when-creating-user.patch b/SOURCES/0001-lib-save-os-when-creating-user.patch new file mode 100644 index 0000000..6d11163 --- /dev/null +++ b/SOURCES/0001-lib-save-os-when-creating-user.patch @@ -0,0 +1,844 @@ +From 4ff9bc526fec4be51f42739b4258529d7e4695a1 Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Fri, 12 Oct 2018 15:53:52 -0400 +Subject: [PATCH] lib: save os when creating user + +In order to identify that a user has upgraded from rhel 7 to +rhel 8, we need to know what os they were using when created. + +This commit saves that information using a red hat specific +extension to accountsservice. +--- + data/Makefile.am | 14 +- + .../com.redhat.AccountsServiceUser.System.xml | 10 ++ + src/libaccountsservice/Makefile.am | 12 +- + src/libaccountsservice/act-user-manager.c | 123 ++++++++++++++++++ + 4 files changed, 157 insertions(+), 2 deletions(-) + create mode 100644 data/com.redhat.AccountsServiceUser.System.xml + +diff --git a/data/Makefile.am b/data/Makefile.am +index 521c6c2..a441452 100644 +--- a/data/Makefile.am ++++ b/data/Makefile.am +@@ -1,44 +1,56 @@ + + dbusifdir = $(datadir)/dbus-1/interfaces + dbusif_DATA = \ + org.freedesktop.Accounts.xml \ +- org.freedesktop.Accounts.User.xml ++ org.freedesktop.Accounts.User.xml \ ++ com.redhat.AccountsServiceUser.System.xml + + dbusconfdir = $(sysconfdir)/dbus-1/system.d + dbusconf_DATA = org.freedesktop.Accounts.conf + ++accountsserviceifdir = $(datadir)/accountsservice/interfaces ++ + servicedir = $(datadir)/dbus-1/system-services + service_in_files = org.freedesktop.Accounts.service.in + service_DATA = $(service_in_files:.service.in=.service) + + $(service_DATA): $(service_in_files) Makefile + @sed -e "s|\@libexecdir\@|$(libexecdir)|" $< >$@ + + policydir = $(datadir)/polkit-1/actions + policy_in_files = org.freedesktop.accounts.policy.in + policy_DATA = $(policy_in_files:.policy.in=.policy) + + @INTLTOOL_POLICY_RULE@ + + if HAVE_SYSTEMD + systemdsystemunit_DATA = \ + accounts-daemon.service + + accounts-daemon.service: accounts-daemon.service.in + @sed -e "s|\@libexecdir\@|$(libexecdir)|" $< >$@ + + endif + + EXTRA_DIST = \ + $(dbusif_DATA) \ + $(dbusconf_DATA) \ + $(service_in_files) \ + $(policy_in_files) \ + accounts-daemon.service.in + + DISTCLEANFILES = \ + $(service_DATA) \ + $(policy_DATA) + + CLEANFILES = \ + accounts-daemon.service ++ ++install-data-hook: com.redhat.AccountsServiceUser.System.xml ++ if test '!' -d $(DESTDIR)$(accountsserviceifdir); then \ ++ $(mkinstalldirs) $(DESTDIR)$(accountsserviceifdir); \ ++ chmod 755 $(DESTDIR)$(accountsserviceifdir); \ ++ fi ++ if test '!' -L $(DESTDIR)$(accountsserviceifdir)/com.redhat.AccountsServiceUser.System.xml; then \ ++ (cd $(DESTDIR)$(accountsserviceifdir); $(LN_S) ../../dbus-1/interfaces/com.redhat.AccountsServiceUser.System.xml) \ ++ fi +diff --git a/data/com.redhat.AccountsServiceUser.System.xml b/data/com.redhat.AccountsServiceUser.System.xml +new file mode 100644 +index 0000000..67f5f30 +--- /dev/null ++++ b/data/com.redhat.AccountsServiceUser.System.xml +@@ -0,0 +1,10 @@ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ +diff --git a/src/libaccountsservice/Makefile.am b/src/libaccountsservice/Makefile.am +index 408d91f..d711d65 100644 +--- a/src/libaccountsservice/Makefile.am ++++ b/src/libaccountsservice/Makefile.am +@@ -20,69 +20,79 @@ act-user-enum-types.h: act-user.h act-user-manager.h + $(AM_V_GEN) (cd $(srcdir) && \ + glib-mkenums \ + --fhead "#ifndef __ACT_USER_ENUM_TYPES_H__\n#define __ACT_USER_ENUM_TYPES_H__\n\n#include \n\nG_BEGIN_DECLS\n" \ + --fprod "/* enumerations from \"@filename@\" */\n" \ + --vhead "GType @enum_name@_get_type (void);\n#define ACT_USER_TYPE_@ENUMSHORT@ (@enum_name@_get_type())\n" \ + --ftail "G_END_DECLS\n\n#endif /* __ACT_USER_ENUM_TYPES_H__ */" \ + $(^F) ) > $@ + + act-user-enum-types.c: act-user.h act-user-manager.h act-user-enum-types.h + $(AM_V_GEN) (cd $(srcdir) && \ + glib-mkenums \ + --fhead "#include \"act-user.h\"\n" \ + --fhead "#include \"act-user-manager.h\"\n" \ + --fhead "#include \"act-user-enum-types.h\"\n" \ + --fhead "#include " \ + --fprod "\n/* enumerations from \"@filename@\" */" \ + --vhead "GType\n@enum_name@_get_type (void)\n{\n static GType etype = 0;\n if (etype == 0) {\n static const G@Type@Value values[] = {" \ + --vprod " { @VALUENAME@, \"@VALUENAME@\", \"@valuenick@\" }," \ + --vtail " { 0, NULL, NULL }\n };\n etype = g_@type@_register_static (\"@EnumName@\", values);\n }\n return etype;\n}\n" \ + $(^F) ) > $@ + + ck-manager-generated.c ck-manager-generated.h: org.freedesktop.ConsoleKit.Manager.xml Makefile + $(AM_V_GEN) gdbus-codegen --generate-c-code ck-manager-generated --c-namespace ConsoleKit --interface-prefix=org.freedesktop.ConsoleKit $(srcdir)/org.freedesktop.ConsoleKit.Manager.xml + + ck-seat-generated.c ck-seat-generated.h: org.freedesktop.ConsoleKit.Seat.xml Makefile + $(AM_V_GEN) gdbus-codegen --generate-c-code ck-seat-generated --c-namespace ConsoleKit --interface-prefix=org.freedesktop.ConsoleKit $(srcdir)/org.freedesktop.ConsoleKit.Seat.xml + + ck-session-generated.c ck-session-generated.h: org.freedesktop.ConsoleKit.Session.xml Makefile + $(AM_V_GEN) gdbus-codegen --generate-c-code ck-session-generated --c-namespace ConsoleKit --interface-prefix=org.freedesktop.ConsoleKit $(srcdir)/org.freedesktop.ConsoleKit.Session.xml + ++com.redhat.AccountsServiceUser.System.c com.redhat.AccountsServiceUser.System.h: $(top_srcdir)/data/com.redhat.AccountsServiceUser.System.xml Makefile ++ $(AM_V_GEN)gdbus-codegen \ ++ --c-namespace=Act \ ++ --interface-prefix=com.redhat.AccountsService \ ++ --generate-c-code=com.redhat.AccountsServiceUser.System \ ++ $(top_srcdir)/data/com.redhat.AccountsServiceUser.System.xml ++ ++ + BUILT_SOURCES += \ + act-user-enum-types.c \ + act-user-enum-types.h \ + ck-manager-generated.c \ + ck-manager-generated.h \ + ck-seat-generated.c \ + ck-seat-generated.h \ + ck-session-generated.c \ +- ck-session-generated.h ++ ck-session-generated.h \ ++ com.redhat.AccountsServiceUser.System.h \ ++ com.redhat.AccountsServiceUser.System.c + + CLEANFILES += $(BUILT_SOURCES) + + libaccountsservicedir = $(includedir)/accountsservice-1.0/act + libaccountsservice_headers = \ + act-user.h \ + act-user-manager.h \ + act-user-enum-types.h \ + $(END_OF_LIST) + + libaccountsservice_HEADERS = \ + act.h \ + $(libaccountsservice_headers) \ + $(END_OF_LIST) + + + libaccountsservice_la_CFLAGS = $(LIBACCOUNTSSERVICE_CFLAGS) + libaccountsservice_la_LDFLAGS = \ + -export-symbols-regex '^[^_].*' \ + -version-info $(LT_CURRENT):$(LT_REVISION):$(LT_AGE) \ + -no-undefined \ + $(END_OF_LIST) + + libaccountsservice_la_LIBADD = \ + ../libaccounts-generated.la \ + $(LIBACCOUNTSSERVICE_LIBS) \ + -lcrypt \ + $(END_OF_LIST) + + libaccountsservice_la_sources = \ +diff --git a/src/libaccountsservice/act-user-manager.c b/src/libaccountsservice/act-user-manager.c +index 325421b..091b46a 100644 +--- a/src/libaccountsservice/act-user-manager.c ++++ b/src/libaccountsservice/act-user-manager.c +@@ -27,60 +27,61 @@ + #include + #include + #include + #include + #include + + #ifdef HAVE_PATHS_H + #include + #endif /* HAVE_PATHS_H */ + + #include + #include + #include + #include + #include + #include + + #ifdef WITH_SYSTEMD + #include + + /* check if logind is running */ + #define LOGIND_RUNNING() (access("/run/systemd/seats/", F_OK) >= 0) + #endif + + #include "act-user-manager.h" + #include "act-user-private.h" + #include "accounts-generated.h" + #include "ck-manager-generated.h" + #include "ck-seat-generated.h" + #include "ck-session-generated.h" ++#include "com.redhat.AccountsServiceUser.System.h" + + /** + * SECTION:act-user-manager + * @title: ActUserManager + * @short_description: manages ActUser objects + * + * ActUserManager is a manager object that gives access to user + * creation, deletion, enumeration, etc. + * + * There is typically a singleton ActUserManager object, which + * can be obtained by act_user_manager_get_default(). + */ + + /** + * ActUserManager: + * + * A user manager object. + */ + + /** + * ACT_USER_MANAGER_ERROR: + * + * The GError domain for #ActUserManagerError errors + */ + + /** + * ActUserManagerError: + * @ACT_USER_MANAGER_ERROR_FAILED: Generic failure + * @ACT_USER_MANAGER_ERROR_USER_EXISTS: The user already exists + * @ACT_USER_MANAGER_ERROR_USER_DOES_NOT_EXIST: The user does not exist +@@ -167,60 +168,63 @@ typedef struct + ActUser *user; + ActUserManagerFetchUserRequestType type; + union { + char *username; + uid_t uid; + }; + char *object_path; + char *description; + } ActUserManagerFetchUserRequest; + + struct ActUserManagerPrivate + { + GHashTable *normal_users_by_name; + GHashTable *system_users_by_name; + GHashTable *users_by_object_path; + GHashTable *sessions; + GDBusConnection *connection; + AccountsAccounts *accounts_proxy; + ConsoleKitManager *ck_manager_proxy; + + ActUserManagerSeat seat; + + GSList *new_sessions; + GSList *new_users; + GSList *new_users_inhibiting_load; + GSList *fetch_user_requests; + + GSList *exclude_usernames; + GSList *include_usernames; + ++ char *os_id; ++ char *os_version_id; ++ + guint load_id; + + gboolean is_loaded; + gboolean has_multiple_users; + gboolean getting_sessions; + gboolean list_cached_users_done; + }; + + enum { + PROP_0, + PROP_INCLUDE_USERNAMES_LIST, + PROP_EXCLUDE_USERNAMES_LIST, + PROP_IS_LOADED, + PROP_HAS_MULTIPLE_USERS + }; + + enum { + USER_ADDED, + USER_REMOVED, + USER_IS_LOGGED_IN_CHANGED, + USER_CHANGED, + LAST_SIGNAL + }; + + static guint signals [LAST_SIGNAL] = { 0, }; + + static void act_user_manager_class_init (ActUserManagerClass *klass); + static void act_user_manager_init (ActUserManager *user_manager); + static void act_user_manager_finalize (GObject *object); + +@@ -2763,101 +2767,174 @@ ensure_accounts_proxy (ActUserManager *manager) + G_DBUS_PROXY_FLAGS_NONE, + ACCOUNTS_NAME, + ACCOUNTS_PATH, + NULL, + &error); + if (error != NULL) { + g_debug ("ActUserManager: getting account proxy failed: %s", error->message); + return FALSE; + } + + g_dbus_proxy_set_default_timeout (G_DBUS_PROXY (manager->priv->accounts_proxy), G_MAXINT); + + g_object_bind_property (G_OBJECT (manager->priv->accounts_proxy), + "has-multiple-users", + G_OBJECT (manager), + "has-multiple-users", + G_BINDING_SYNC_CREATE); + + g_signal_connect (manager->priv->accounts_proxy, + "user-added", + G_CALLBACK (on_new_user_in_accounts_service), + manager); + g_signal_connect (manager->priv->accounts_proxy, + "user-deleted", + G_CALLBACK (on_user_removed_in_accounts_service), + manager); + + return TRUE; + } + ++static inline gboolean ++is_valid_char (gchar c, ++ gboolean first) ++{ ++ return (!first && g_ascii_isdigit (c)) || ++ c == '_' || ++ g_ascii_isalpha (c); ++} ++ ++static void ++load_os_release (ActUserManager *manager) ++{ ++ ActUserManagerPrivate *priv = manager->priv; ++ g_autoptr(GFile) file = NULL; ++ g_autoptr(GError) error = NULL; ++ g_autofree char *contents = NULL; ++ g_auto(GStrv) lines = NULL; ++ size_t i; ++ ++ file = g_file_new_for_path ("/etc/os-release"); ++ ++ if (!g_file_load_contents (file, NULL, &contents, NULL, NULL, &error)) { ++ g_debug ("ActUserManager: couldn't load /etc/os-release: %s", error->message); ++ return; ++ } ++ ++ lines = g_strsplit (contents, "\n", -1); ++ for (i = 0; lines[i] != NULL; i++) { ++ char *p, *name, *name_end, *value, *value_end; ++ ++ p = lines[i]; ++ ++ while (g_ascii_isspace (*p)) ++ p++; ++ ++ if (*p == '#' || *p == '\0') ++ continue; ++ name = p; ++ while (is_valid_char (*p, p == name)) ++ p++; ++ name_end = p; ++ while (g_ascii_isspace (*p)) ++ p++; ++ if (name == name_end || *p != '=') { ++ continue; ++ } ++ *name_end = '\0'; ++ ++ p++; ++ ++ while (g_ascii_isspace (*p)) ++ p++; ++ ++ value = p; ++ value_end = value + strlen (value) - 1; ++ ++ if (value != value_end && *value == '"' && *value_end == '"') { ++ value++; ++ *value_end = '\0'; ++ } ++ ++ if (strcmp (name, "ID") == 0) { ++ g_debug ("ActUserManager: system OS is '%s'", value); ++ priv->os_id = g_strdup (value); ++ } else if (strcmp (name, "VERSION_ID") == 0) { ++ g_debug ("ActUserManager: system OS version is '%s'", value); ++ priv->os_version_id = g_strdup (value); ++ } ++ } ++} ++ + static void + act_user_manager_init (ActUserManager *manager) + { + g_autoptr(GError) error = NULL; + + manager->priv = ACT_USER_MANAGER_GET_PRIVATE (manager); + + act_user_manager_error_quark (); /* register dbus errors */ + + /* sessions */ + manager->priv->sessions = g_hash_table_new_full (g_str_hash, + g_str_equal, + g_free, + g_object_unref); + + /* users */ + manager->priv->normal_users_by_name = g_hash_table_new_full (g_str_hash, + g_str_equal, + g_free, + g_object_unref); + manager->priv->system_users_by_name = g_hash_table_new_full (g_str_hash, + g_str_equal, + g_free, + g_object_unref); + manager->priv->users_by_object_path = g_hash_table_new_full (g_str_hash, + g_str_equal, + NULL, + g_object_unref); + + manager->priv->connection = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, &error); + if (manager->priv->connection == NULL) { + if (error != NULL) { + g_warning ("Failed to connect to the D-Bus daemon: %s", error->message); + } else { + g_warning ("Failed to connect to the D-Bus daemon"); + } + return; + } + + ensure_accounts_proxy (manager); + ++ load_os_release (manager); ++ + manager->priv->seat.state = ACT_USER_MANAGER_SEAT_STATE_UNLOADED; + } + + static void + act_user_manager_finalize (GObject *object) + { + ActUserManager *manager; + GSList *node; + + g_debug ("ActUserManager: finalizing user manager"); + + g_return_if_fail (object != NULL); + g_return_if_fail (ACT_IS_USER_MANAGER (object)); + + manager = ACT_USER_MANAGER (object); + + g_return_if_fail (manager->priv != NULL); + + g_slist_foreach (manager->priv->new_sessions, + (GFunc) unload_new_session, NULL); + g_slist_free (manager->priv->new_sessions); + + g_slist_foreach (manager->priv->fetch_user_requests, + (GFunc) free_fetch_user_request, NULL); + g_slist_free (manager->priv->fetch_user_requests); + + g_slist_free (manager->priv->new_users_inhibiting_load); + + node = manager->priv->new_users; + while (node != NULL) { +@@ -2899,141 +2976,179 @@ act_user_manager_finalize (GObject *object) + + #ifdef WITH_SYSTEMD + if (manager->priv->seat.session_monitor != NULL) { + sd_login_monitor_unref (manager->priv->seat.session_monitor); + } + + if (manager->priv->seat.session_monitor_stream != NULL) { + g_object_unref (manager->priv->seat.session_monitor_stream); + } + + if (manager->priv->seat.session_monitor_source_id != 0) { + g_source_remove (manager->priv->seat.session_monitor_source_id); + } + #endif + + if (manager->priv->accounts_proxy != NULL) { + g_object_unref (manager->priv->accounts_proxy); + } + + if (manager->priv->load_id > 0) { + g_source_remove (manager->priv->load_id); + manager->priv->load_id = 0; + } + + g_hash_table_destroy (manager->priv->sessions); + + g_hash_table_destroy (manager->priv->normal_users_by_name); + g_hash_table_destroy (manager->priv->system_users_by_name); + g_hash_table_destroy (manager->priv->users_by_object_path); + ++ g_free (manager->priv->os_id); ++ g_free (manager->priv->os_version_id); ++ + G_OBJECT_CLASS (act_user_manager_parent_class)->finalize (object); + } + + /** + * act_user_manager_get_default: + * + * Returns the user manager singleton instance. Calling this function will + * automatically being loading the user list if it isn't loaded already. + * The #ActUserManager:is-loaded property will be set to %TRUE when the users + * are finished loading and then act_user_manager_list_users() can be called. + * + * Returns: (transfer none): user manager object + */ + ActUserManager * + act_user_manager_get_default (void) + { + if (user_manager_object == NULL) { + user_manager_object = g_object_new (ACT_TYPE_USER_MANAGER, NULL); + g_object_add_weak_pointer (user_manager_object, + (gpointer *) &user_manager_object); + act_user_manager_queue_load (user_manager_object); + } + + return ACT_USER_MANAGER (user_manager_object); + } + + /** + * act_user_manager_no_service: + * @manager: a #ActUserManager + * + * Check whether or not the accounts service is running. + * + * Returns: whether or not accounts service is running + */ + gboolean + act_user_manager_no_service (ActUserManager *manager) + { + return manager->priv->accounts_proxy == NULL; + } + ++static void ++save_system_info (ActUserManager *manager, ++ const char *user_path) ++{ ++ ActUserManagerPrivate *priv = manager->priv; ++ ActUserSystem *user_system_proxy = NULL; ++ g_autoptr(GError) error = NULL; ++ ++ if (priv->os_id == NULL && priv->os_version_id == NULL) ++ return; ++ ++ user_system_proxy = act_user_system_proxy_new_sync (priv->connection, ++ G_DBUS_PROXY_FLAGS_NONE, ++ ACCOUNTS_NAME, ++ user_path, ++ NULL, ++ &error); ++ if (user_system_proxy != NULL) { ++ if (priv->os_id != NULL) ++ act_user_system_set_id (user_system_proxy, priv->os_id); ++ ++ if (priv->os_version_id != NULL) ++ act_user_system_set_version_id (user_system_proxy, priv->os_version_id); ++ } else { ++ /* probably means accountsservice and lib are out of sync */ ++ g_debug ("ActUserManager: failed to create user system proxy: %s", ++ error? error->message: ""); ++ g_clear_error (&error); ++ } ++ ++ g_clear_object (&user_system_proxy); ++} ++ + /** + * act_user_manager_create_user: + * @manager: a #ActUserManager + * @username: a unix user name + * @fullname: a unix GECOS value + * @accounttype: a #ActUserAccountType + * @error: a #GError + * + * Creates a user account on the system. + * + * Returns: (transfer full): user object + */ + ActUser * + act_user_manager_create_user (ActUserManager *manager, + const char *username, + const char *fullname, + ActUserAccountType accounttype, + GError **error) + { + GError *local_error = NULL; + gboolean res; + g_autofree gchar *path = NULL; + ActUser *user; + + g_debug ("ActUserManager: Creating user '%s', '%s', %d", + username, fullname, accounttype); + + g_assert (manager->priv->accounts_proxy != NULL); + + res = accounts_accounts_call_create_user_sync (manager->priv->accounts_proxy, + username, + fullname, + accounttype, + &path, + NULL, + &local_error); + if (!res) { + g_propagate_error (error, local_error); + return NULL; + } + ++ save_system_info (manager, path); ++ + user = add_new_user_for_object_path (path, manager); + + return user; + } + + static void + act_user_manager_async_complete_handler (GObject *source, + GAsyncResult *result, + gpointer user_data) + { + GTask *task = user_data; + + g_task_return_pointer (task, g_object_ref (result), g_object_unref); + g_object_unref (task); + } + + /** + * act_user_manager_create_user_async: + * @manager: a #ActUserManager + * @username: a unix user name + * @fullname: a unix GECOS value + * @accounttype: a #ActUserAccountType + * @cancellable: (allow-none): optional #GCancellable object, + * %NULL to ignore + * @callback: (scope async): a #GAsyncReadyCallback to call + * when the request is satisfied + * @user_data: (closure): the data to pass to @callback + * + * Asynchronously creates a user account on the system. + * +@@ -3077,105 +3192,110 @@ act_user_manager_create_user_async (ActUserManager *manager, + * act_user_manager_create_user_finish: + * @manager: a #ActUserManager + * @result: a #GAsyncResult + * @error: a #GError + * + * Finishes an asynchronous user creation. + * + * See act_user_manager_create_user_async(). + * + * Returns: (transfer full): user object + * + * Since: 0.6.27 + */ + ActUser * + act_user_manager_create_user_finish (ActUserManager *manager, + GAsyncResult *result, + GError **error) + { + GAsyncResult *inner_result; + ActUser *user = NULL; + g_autofree gchar *path = NULL; + GError *remote_error = NULL; + + inner_result = g_task_propagate_pointer (G_TASK (result), error); + if (inner_result == NULL) { + return FALSE; + } + + if (accounts_accounts_call_create_user_finish (manager->priv->accounts_proxy, + &path, inner_result, &remote_error)) { ++ ++ save_system_info (manager, path); ++ + user = add_new_user_for_object_path (path, manager); + } + + if (remote_error) { + g_dbus_error_strip_remote_error (remote_error); + g_propagate_error (error, remote_error); + } + + return user; + } + + /** + * act_user_manager_cache_user: + * @manager: a #ActUserManager + * @username: a user name + * @error: a #GError + * + * Caches a user account so it shows up via act_user_manager_list_users(). + * + * Returns: (transfer full): user object + */ + ActUser * + act_user_manager_cache_user (ActUserManager *manager, + const char *username, + GError **error) + { + GError *local_error = NULL; + gboolean res; + g_autofree gchar *path = NULL; + + g_debug ("ActUserManager: Caching user '%s'", + username); + + g_assert (manager->priv->accounts_proxy != NULL); + + res = accounts_accounts_call_cache_user_sync (manager->priv->accounts_proxy, + username, + &path, + NULL, + &local_error); + if (!res) { + g_propagate_error (error, local_error); + return NULL; + } + ++ save_system_info (manager, path); ++ + return add_new_user_for_object_path (path, manager); + } + + + /** + * act_user_manager_cache_user_async: + * @manager: a #ActUserManager + * @username: a unix user name + * @cancellable: (allow-none): optional #GCancellable object, + * %NULL to ignore + * @callback: (scope async): a #GAsyncReadyCallback to call + * when the request is satisfied + * @user_data: (closure): the data to pass to @callback + * + * Asynchronously caches a user account so it shows up via + * act_user_manager_list_users(). + * + * For more details, see act_user_manager_cache_user(), which + * is the synchronous version of this call. + * + * Since: 0.6.27 + */ + void + act_user_manager_cache_user_async (ActUserManager *manager, + const char *username, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) + { + GTask *task; +@@ -3199,60 +3319,63 @@ act_user_manager_cache_user_async (ActUserManager *manager, + * act_user_manager_cache_user_finish: + * @manager: a #ActUserManager + * @result: a #GAsyncResult + * @error: a #GError + * + * Finishes an asynchronous user caching. + * + * See act_user_manager_cache_user_async(). + * + * Returns: (transfer full): user object + * + * Since: 0.6.27 + */ + ActUser * + act_user_manager_cache_user_finish (ActUserManager *manager, + GAsyncResult *result, + GError **error) + { + GAsyncResult *inner_result; + ActUser *user = NULL; + g_autofree gchar *path = NULL; + GError *remote_error = NULL; + + inner_result = g_task_propagate_pointer (G_TASK (result), error); + if (inner_result == NULL) { + return FALSE; + } + + if (accounts_accounts_call_cache_user_finish (manager->priv->accounts_proxy, + &path, inner_result, &remote_error)) { ++ ++ save_system_info (manager, path); ++ + user = add_new_user_for_object_path (path, manager); + } + + if (remote_error) { + g_dbus_error_strip_remote_error (remote_error); + g_propagate_error (error, remote_error); + } + + return user; + } + + /** + * act_user_manager_uncache_user: + * @manager: a #ActUserManager + * @username: a user name + * @error: a #GError + * + * Releases all metadata about a user account, including icon, + * language and session. If the user account is from a remote + * server and the user has never logged in before, then that + * account will no longer show up in ListCachedUsers() output. + * + * Returns: %TRUE if successful, otherwise %FALSE + */ + gboolean + act_user_manager_uncache_user (ActUserManager *manager, + const char *username, + GError **error) + { + GError *local_error = NULL; +-- +2.17.1 + diff --git a/SOURCES/0001-user-add-new-Session-SessionType-properties-to-repla.patch b/SOURCES/0001-user-add-new-Session-SessionType-properties-to-repla.patch new file mode 100644 index 0000000..c50b83f --- /dev/null +++ b/SOURCES/0001-user-add-new-Session-SessionType-properties-to-repla.patch @@ -0,0 +1,910 @@ +From c66cee942242a731082f0fac649f3f9569ae99a3 Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Wed, 8 Aug 2018 16:11:56 -0400 +Subject: [PATCH 1/2] user: add new Session/SessionType properties to replace + XSession + +Having a property called XSession in the API makes little +sense when wayland has taken the world by storm. + +This commit adds new "Session" property without the "X" in the name, +and an additional property "SessionType" that can be either +"wayland" or "x11". +--- + data/org.freedesktop.Accounts.User.xml | 103 +++++++++++++++++++++ + src/libaccountsservice/act-user.c | 93 +++++++++++++++++++ + src/libaccountsservice/act-user.h | 6 ++ + src/user.c | 121 +++++++++++++++++++++++++ + 4 files changed, 323 insertions(+) + +diff --git a/data/org.freedesktop.Accounts.User.xml b/data/org.freedesktop.Accounts.User.xml +index 4ab989a..7fc3c61 100644 +--- a/data/org.freedesktop.Accounts.User.xml ++++ b/data/org.freedesktop.Accounts.User.xml +@@ -141,60 +141,143 @@ + org.freedesktop.accounts.user-administration + To change the language of another user + + + + + if the caller lacks the appropriate PolicyKit authorization + if the operation failed + + + + + + + + + + + The new xsession to start (e.g. "gnome") + + + + + + + Sets the users x session. + + + The expectation is that display managers will log the user in to this + specified session, if available. ++ ++ Note this call is deprecated and has been superceded by SetSession since ++ not all graphical sessions use X as the display server. ++ ++ ++ ++ The caller needs one of the following PolicyKit authorizations: ++ ++ ++ org.freedesktop.accounts.change-own-user-data ++ To change his own language ++ ++ ++ org.freedesktop.accounts.user-administration ++ To change the language of another user ++ ++ ++ ++ ++ if the caller lacks the appropriate PolicyKit authorization ++ if the operation failed ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ The new session to start (e.g. "gnome-xorg") ++ ++ ++ ++ ++ ++ ++ Sets the users wayland or x session. ++ ++ ++ The expectation is that display managers will log the user in to this ++ specified session, if available. ++ ++ ++ ++ The caller needs one of the following PolicyKit authorizations: ++ ++ ++ org.freedesktop.accounts.change-own-user-data ++ To change his own language ++ ++ ++ org.freedesktop.accounts.user-administration ++ To change the language of another user ++ ++ ++ ++ ++ if the caller lacks the appropriate PolicyKit authorization ++ if the operation failed ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ The type of the new session to start (e.g. "wayland" or "x11") ++ ++ ++ ++ ++ ++ ++ Sets the session type of the users session. ++ ++ ++ Display managers may use this property to decide what type of display server to use when ++ loading the session + + + + The caller needs one of the following PolicyKit authorizations: + + + org.freedesktop.accounts.change-own-user-data + To change his own language + + + org.freedesktop.accounts.user-administration + To change the language of another user + + + + + if the caller lacks the appropriate PolicyKit authorization + if the operation failed + + + + + + + + + + The new location as a freeform string. + + +@@ -641,60 +724,80 @@ + + + + + The users shell. + + + + + + + + + + The email address. + + + + + + + + + + The users language, as a locale specification like "de_DE.UTF-8". + + + + + ++ ++ ++ ++ ++ The users Wayland or X session. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ The type of session the user should use (e.g. "wayland" or "x11") ++ ++ ++ ++ ++ + + + + + The users x session. + + + + + + + + + + The users location. + + + + + + + + + + How often the user has logged in. + + + + + +diff --git a/src/libaccountsservice/act-user.c b/src/libaccountsservice/act-user.c +index 7162221..dd8f81b 100644 +--- a/src/libaccountsservice/act-user.c ++++ b/src/libaccountsservice/act-user.c +@@ -999,60 +999,98 @@ act_user_get_icon_file (ActUser *user) + const char * + act_user_get_language (ActUser *user) + { + g_return_val_if_fail (ACT_IS_USER (user), NULL); + + if (user->accounts_proxy == NULL) + return NULL; + + return accounts_user_get_language (user->accounts_proxy); + } + + /** + * act_user_get_x_session: + * @user: a #ActUser + * + * Returns the path to the configured X session for @user. + * + * Returns: (transfer none): a path to an icon + */ + const char * + act_user_get_x_session (ActUser *user) + { + g_return_val_if_fail (ACT_IS_USER (user), NULL); + + if (user->accounts_proxy == NULL) + return NULL; + + return accounts_user_get_xsession (user->accounts_proxy); + } + ++/** ++ * act_user_get_session: ++ * @user: a #ActUser ++ * ++ * Returns the path to the configured session for @user. ++ * ++ * Returns: (transfer none): a path to an icon ++ */ ++const char * ++act_user_get_session (ActUser *user) ++{ ++ g_return_val_if_fail (ACT_IS_USER (user), NULL); ++ ++ if (user->accounts_proxy == NULL) ++ return NULL; ++ ++ return accounts_user_get_session (user->accounts_proxy); ++} ++ ++/** ++ * act_user_get_session_type: ++ * @user: a #ActUser ++ * ++ * Returns the type of the configured session for @user. ++ * ++ * Returns: (transfer none): a path to an icon ++ */ ++const char * ++act_user_get_session_type (ActUser *user) ++{ ++ g_return_val_if_fail (ACT_IS_USER (user), NULL); ++ ++ if (user->accounts_proxy == NULL) ++ return NULL; ++ ++ return accounts_user_get_session_type (user->accounts_proxy); ++} ++ + /** + * act_user_get_object_path: + * @user: a #ActUser + * + * Returns the user accounts service object path of @user, + * or %NULL if @user doesn't have an object path associated + * with it. + * + * Returns: (transfer none): the object path of the user + */ + const char * + act_user_get_object_path (ActUser *user) + { + g_return_val_if_fail (ACT_IS_USER (user), NULL); + + if (user->accounts_proxy == NULL) + return NULL; + + return g_dbus_proxy_get_object_path (G_DBUS_PROXY (user->accounts_proxy)); + } + + /** + * act_user_get_primary_session_id: + * @user: a #ActUser + * + * Returns the id of the primary session of @user, or %NULL if @user + * has no primary session. The primary session will always be + * graphical and will be chosen from the sessions on the same seat as + * the seat of the session of the calling process. + * +@@ -1310,60 +1348,115 @@ act_user_set_language (ActUser *user, + } + + /** + * act_user_set_x_session: + * @user: the user object to alter. + * @x_session: an x session (e.g. gnome) + * + * Assigns a new x session for @user. + * + * Note this function is synchronous and ignores errors. + **/ + void + act_user_set_x_session (ActUser *user, + const char *x_session) + { + g_autoptr(GError) error = NULL; + + g_return_if_fail (ACT_IS_USER (user)); + g_return_if_fail (x_session != NULL); + g_return_if_fail (ACCOUNTS_IS_USER (user->accounts_proxy)); + + if (!accounts_user_call_set_xsession_sync (user->accounts_proxy, + x_session, + NULL, + &error)) { + g_warning ("SetXSession call failed: %s", error->message); + return; + } + } + ++/** ++ * act_user_set_session: ++ * @user: the user object to alter. ++ * @session: a session (e.g. gnome) ++ * ++ * Assigns a new session for @user. ++ * ++ * Note this function is synchronous and ignores errors. ++ **/ ++void ++act_user_set_session (ActUser *user, ++ const char *session) ++{ ++ g_autoptr(GError) error = NULL; ++ ++ g_return_if_fail (ACT_IS_USER (user)); ++ g_return_if_fail (session != NULL); ++ g_return_if_fail (ACCOUNTS_IS_USER (user->accounts_proxy)); ++ ++ if (!accounts_user_call_set_session_sync (user->accounts_proxy, ++ session, ++ NULL, ++ &error)) { ++ g_warning ("SetSession call failed: %s", error->message); ++ return; ++ } ++} ++ ++/** ++ * act_user_set_session_type: ++ * @user: the user object to alter. ++ * @session_type: a type of session (e.g. "wayland" or "x11") ++ * ++ * Assigns a type to the session for @user. ++ * ++ * Note this function is synchronous and ignores errors. ++ **/ ++void ++act_user_set_session_type (ActUser *user, ++ const char *session_type) ++{ ++ g_autoptr(GError) error = NULL; ++ ++ g_return_if_fail (ACT_IS_USER (user)); ++ g_return_if_fail (session_type != NULL); ++ g_return_if_fail (ACCOUNTS_IS_USER (user->accounts_proxy)); ++ ++ if (!accounts_user_call_set_session_type_sync (user->accounts_proxy, ++ session_type, ++ NULL, ++ &error)) { ++ g_warning ("SetSessionType call failed: %s", error->message); ++ return; ++ } ++} + + /** + * act_user_set_location: + * @user: the user object to alter. + * @location: a location + * + * Assigns a new location for @user. + * + * Note this function is synchronous and ignores errors. + **/ + void + act_user_set_location (ActUser *user, + const char *location) + { + g_autoptr(GError) error = NULL; + + g_return_if_fail (ACT_IS_USER (user)); + g_return_if_fail (location != NULL); + g_return_if_fail (ACCOUNTS_IS_USER (user->accounts_proxy)); + + if (!accounts_user_call_set_location_sync (user->accounts_proxy, + location, + NULL, + &error)) { + g_warning ("SetLocation call failed: %s", error->message); + return; + } + } + + /** +diff --git a/src/libaccountsservice/act-user.h b/src/libaccountsservice/act-user.h +index c685fcc..2ef13b1 100644 +--- a/src/libaccountsservice/act-user.h ++++ b/src/libaccountsservice/act-user.h +@@ -51,79 +51,85 @@ typedef struct _ActUserClass ActUserClass; + + GType act_user_get_type (void) G_GNUC_CONST; + + const char *act_user_get_object_path (ActUser *user); + + uid_t act_user_get_uid (ActUser *user); + const char *act_user_get_user_name (ActUser *user); + const char *act_user_get_real_name (ActUser *user); + ActUserAccountType act_user_get_account_type (ActUser *user); + ActUserPasswordMode act_user_get_password_mode (ActUser *user); + const char *act_user_get_password_hint (ActUser *user); + const char *act_user_get_home_dir (ActUser *user); + const char *act_user_get_shell (ActUser *user); + const char *act_user_get_email (ActUser *user); + const char *act_user_get_location (ActUser *user); + guint act_user_get_num_sessions (ActUser *user); + guint act_user_get_num_sessions_anywhere (ActUser *user); + gboolean act_user_is_logged_in (ActUser *user); + gboolean act_user_is_logged_in_anywhere (ActUser *user); + int act_user_get_login_frequency (ActUser *user); + gint64 act_user_get_login_time (ActUser *user); + const GVariant*act_user_get_login_history (ActUser *user); + gboolean act_user_get_locked (ActUser *user); + gboolean act_user_get_automatic_login (ActUser *user); + gboolean act_user_is_system_account (ActUser *user); + gboolean act_user_is_local_account (ActUser *user); + gboolean act_user_is_nonexistent (ActUser *user); + const char *act_user_get_icon_file (ActUser *user); + const char *act_user_get_language (ActUser *user); + const char *act_user_get_x_session (ActUser *user); ++const char *act_user_get_session (ActUser *user); ++const char *act_user_get_session_type (ActUser *user); + const char *act_user_get_primary_session_id (ActUser *user); + + gint act_user_collate (ActUser *user1, + ActUser *user2); + gboolean act_user_is_loaded (ActUser *user); + + void act_user_get_password_expiration_policy (ActUser *user, + gint64 *expiration_time, + gint64 *last_change_time, + gint64 *min_days_between_changes, + gint64 *max_days_between_changes, + gint64 *days_to_warn, + gint64 *days_after_expiration_until_lock); + + void act_user_set_email (ActUser *user, + const char *email); + void act_user_set_language (ActUser *user, + const char *language); + void act_user_set_x_session (ActUser *user, + const char *x_session); ++void act_user_set_session (ActUser *user, ++ const char *session); ++void act_user_set_session_type (ActUser *user, ++ const char *session_type); + void act_user_set_location (ActUser *user, + const char *location); + void act_user_set_user_name (ActUser *user, + const char *user_name); + void act_user_set_real_name (ActUser *user, + const char *real_name); + void act_user_set_icon_file (ActUser *user, + const char *icon_file); + void act_user_set_account_type (ActUser *user, + ActUserAccountType account_type); + void act_user_set_password (ActUser *user, + const gchar *password, + const gchar *hint); + void act_user_set_password_hint (ActUser *user, + const gchar *hint); + void act_user_set_password_mode (ActUser *user, + ActUserPasswordMode password_mode); + void act_user_set_locked (ActUser *user, + gboolean locked); + void act_user_set_automatic_login (ActUser *user, + gboolean enabled); + + #if GLIB_CHECK_VERSION(2, 44, 0) + G_DEFINE_AUTOPTR_CLEANUP_FUNC (ActUser, g_object_unref) + #endif + + G_END_DECLS + + #endif /* __ACT_USER_H__ */ +diff --git a/src/user.c b/src/user.c +index 174530f..94c0244 100644 +--- a/src/user.c ++++ b/src/user.c +@@ -232,60 +232,75 @@ user_update_from_pwent (User *user, + user->account_expiration_policy_known = TRUE; + } + + accounts_user_set_password_mode (ACCOUNTS_USER (user), mode); + is_system_account = !user_classify_is_human (accounts_user_get_uid (ACCOUNTS_USER (user)), + accounts_user_get_user_name (ACCOUNTS_USER (user)), + accounts_user_get_shell (ACCOUNTS_USER (user)), + passwd); + accounts_user_set_system_account (ACCOUNTS_USER (user), is_system_account); + + g_object_thaw_notify (G_OBJECT (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) { + accounts_user_set_language (ACCOUNTS_USER (user), s); + g_clear_pointer (&s, g_free); + } + + s = g_key_file_get_string (keyfile, "User", "XSession", NULL); + if (s != NULL) { + accounts_user_set_xsession (ACCOUNTS_USER (user), s); ++ ++ /* for backward compat */ ++ accounts_user_set_session (ACCOUNTS_USER (user), s); ++ g_clear_pointer (&s, g_free); ++ } ++ ++ s = g_key_file_get_string (keyfile, "User", "Session", NULL); ++ if (s != NULL) { ++ accounts_user_set_session (ACCOUNTS_USER (user), s); ++ g_clear_pointer (&s, g_free); ++ } ++ ++ s = g_key_file_get_string (keyfile, "User", "SessionType", NULL); ++ if (s != NULL) { ++ accounts_user_set_session_type (ACCOUNTS_USER (user), s); + g_clear_pointer (&s, g_free); + } + + s = g_key_file_get_string (keyfile, "User", "Email", NULL); + if (s != NULL) { + accounts_user_set_email (ACCOUNTS_USER (user), s); + g_clear_pointer (&s, g_free); + } + + s = g_key_file_get_string (keyfile, "User", "Location", NULL); + if (s != NULL) { + accounts_user_set_location (ACCOUNTS_USER (user), s); + g_clear_pointer (&s, g_free); + } + + s = g_key_file_get_string (keyfile, "User", "PasswordHint", NULL); + if (s != NULL) { + accounts_user_set_password_hint (ACCOUNTS_USER (user), s); + g_clear_pointer (&s, g_free); + } + + s = g_key_file_get_string (keyfile, "User", "Icon", NULL); + if (s != NULL) { + accounts_user_set_icon_file (ACCOUNTS_USER (user), s); + g_clear_pointer (&s, g_free); + } + + if (g_key_file_has_key (keyfile, "User", "SystemAccount", NULL)) { + gboolean system_account; + +@@ -299,60 +314,66 @@ user_update_from_keyfile (User *user, + + g_object_thaw_notify (G_OBJECT (user)); + } + + void + user_update_local_account_property (User *user, + gboolean local) + { + accounts_user_set_local_account (ACCOUNTS_USER (user), local); + } + + void + user_update_system_account_property (User *user, + gboolean system) + { + accounts_user_set_system_account (ACCOUNTS_USER (user), system); + } + + static void + user_save_to_keyfile (User *user, + GKeyFile *keyfile) + { + g_key_file_remove_group (keyfile, "User", NULL); + + if (accounts_user_get_email (ACCOUNTS_USER (user))) + g_key_file_set_string (keyfile, "User", "Email", accounts_user_get_email (ACCOUNTS_USER (user))); + + if (accounts_user_get_language (ACCOUNTS_USER (user))) + g_key_file_set_string (keyfile, "User", "Language", accounts_user_get_language (ACCOUNTS_USER (user))); + ++ if (accounts_user_get_session (ACCOUNTS_USER (user))) ++ g_key_file_set_string (keyfile, "User", "Session", accounts_user_get_session (ACCOUNTS_USER (user))); ++ ++ if (accounts_user_get_session_type (ACCOUNTS_USER (user))) ++ g_key_file_set_string (keyfile, "User", "SessionType", accounts_user_get_session_type (ACCOUNTS_USER (user))); ++ + if (accounts_user_get_xsession (ACCOUNTS_USER (user))) + g_key_file_set_string (keyfile, "User", "XSession", accounts_user_get_xsession (ACCOUNTS_USER (user))); + + if (accounts_user_get_location (ACCOUNTS_USER (user))) + g_key_file_set_string (keyfile, "User", "Location", accounts_user_get_location (ACCOUNTS_USER (user))); + + if (accounts_user_get_password_hint (ACCOUNTS_USER (user))) + g_key_file_set_string (keyfile, "User", "PasswordHint", accounts_user_get_password_hint (ACCOUNTS_USER (user))); + + if (accounts_user_get_icon_file (ACCOUNTS_USER (user))) + g_key_file_set_string (keyfile, "User", "Icon", accounts_user_get_icon_file (ACCOUNTS_USER (user))); + + g_key_file_set_boolean (keyfile, "User", "SystemAccount", accounts_user_get_system_account (ACCOUNTS_USER (user))); + + user_set_cached (user, TRUE); + } + + static void + save_extra_data (User *user) + { + g_autofree gchar *data = NULL; + g_autofree gchar *filename = NULL; + g_autoptr(GError) error = NULL; + + user_save_to_keyfile (user, user->keyfile); + + data = g_key_file_to_data (user->keyfile, NULL, &error); + if (data == NULL) { + g_warning ("Saving data for user %s failed: %s", + accounts_user_get_user_name (ACCOUNTS_USER (user)), error->message); +@@ -996,60 +1017,158 @@ static gboolean + user_set_language (AccountsUser *auser, + GDBusMethodInvocation *context, + const gchar *language) + { + User *user = (User*)auser; + int uid; + const gchar *action_id; + + if (!get_caller_uid (context, &uid)) { + throw_error (context, ERROR_FAILED, "identifying caller failed"); + return FALSE; + } + + if (accounts_user_get_uid (ACCOUNTS_USER (user)) == (uid_t) uid) + action_id = "org.freedesktop.accounts.change-own-user-data"; + else + action_id = "org.freedesktop.accounts.user-administration"; + + daemon_local_check_auth (user->daemon, + user, + action_id, + TRUE, + user_change_language_authorized_cb, + context, + g_strdup (language), + (GDestroyNotify)g_free); + + return TRUE; + } + ++static void ++user_change_session_authorized_cb (Daemon *daemon, ++ User *user, ++ GDBusMethodInvocation *context, ++ gpointer user_data) ++ ++{ ++ const gchar *session = user_data; ++ ++ if (g_strcmp0 (accounts_user_get_session (ACCOUNTS_USER (user)), session) != 0) { ++ accounts_user_set_session (ACCOUNTS_USER (user), session); ++ ++ save_extra_data (user); ++ } ++ ++ accounts_user_complete_set_session (ACCOUNTS_USER (user), context); ++} ++ ++static gboolean ++user_set_session (AccountsUser *auser, ++ GDBusMethodInvocation *context, ++ const gchar *session) ++{ ++ User *user = (User*)auser; ++ int uid; ++ const gchar *action_id; ++ ++ if (!get_caller_uid (context, &uid)) { ++ throw_error (context, ERROR_FAILED, "identifying caller failed"); ++ return FALSE; ++ } ++ ++ if (accounts_user_get_uid (ACCOUNTS_USER (user)) == (uid_t) uid) ++ action_id = "org.freedesktop.accounts.change-own-user-data"; ++ else ++ action_id = "org.freedesktop.accounts.user-administration"; ++ ++ daemon_local_check_auth (user->daemon, ++ user, ++ action_id, ++ TRUE, ++ user_change_session_authorized_cb, ++ context, ++ g_strdup (session), ++ (GDestroyNotify) g_free); ++ ++ return TRUE; ++} ++ ++static void ++user_change_session_type_authorized_cb (Daemon *daemon, ++ User *user, ++ GDBusMethodInvocation *context, ++ gpointer user_data) ++ ++{ ++ const gchar *session_type = user_data; ++ ++ if (g_strcmp0 (accounts_user_get_session_type (ACCOUNTS_USER (user)), session_type) != 0) { ++ accounts_user_set_session_type (ACCOUNTS_USER (user), session_type); ++ ++ save_extra_data (user); ++ } ++ ++ accounts_user_complete_set_session_type (ACCOUNTS_USER (user), context); ++} ++ ++static gboolean ++user_set_session_type (AccountsUser *auser, ++ GDBusMethodInvocation *context, ++ const gchar *session_type) ++{ ++ User *user = (User*)auser; ++ int uid; ++ const gchar *action_id; ++ ++ if (!get_caller_uid (context, &uid)) { ++ throw_error (context, ERROR_FAILED, "identifying caller failed"); ++ return FALSE; ++ } ++ ++ if (accounts_user_get_uid (ACCOUNTS_USER (user)) == (uid_t) uid) ++ action_id = "org.freedesktop.accounts.change-own-user-data"; ++ else ++ action_id = "org.freedesktop.accounts.user-administration"; ++ ++ daemon_local_check_auth (user->daemon, ++ user, ++ action_id, ++ TRUE, ++ user_change_session_type_authorized_cb, ++ context, ++ g_strdup (session_type), ++ (GDestroyNotify) g_free); ++ ++ return TRUE; ++} ++ + static void + user_change_x_session_authorized_cb (Daemon *daemon, + User *user, + GDBusMethodInvocation *context, + gpointer data) + + { + gchar *x_session = data; + + if (g_strcmp0 (accounts_user_get_xsession (ACCOUNTS_USER (user)), x_session) != 0) { + accounts_user_set_xsession (ACCOUNTS_USER (user), x_session); + + save_extra_data (user); + } + + accounts_user_complete_set_xsession (ACCOUNTS_USER (user), context); + } + + static gboolean + user_set_x_session (AccountsUser *auser, + GDBusMethodInvocation *context, + const gchar *x_session) + { + User *user = (User*)auser; + int uid; + const gchar *action_id; + + if (!get_caller_uid (context, &uid)) { + throw_error (context, ERROR_FAILED, "identifying caller failed"); + return FALSE; +@@ -1966,41 +2085,43 @@ user_finalize (GObject *object) + } + + static void + user_class_init (UserClass *class) + { + GObjectClass *gobject_class; + + gobject_class = G_OBJECT_CLASS (class); + + gobject_class->finalize = user_finalize; + } + + static void + user_accounts_user_iface_init (AccountsUserIface *iface) + { + iface->handle_set_account_type = user_set_account_type; + iface->handle_set_automatic_login = user_set_automatic_login; + iface->handle_set_email = user_set_email; + iface->handle_set_home_directory = user_set_home_directory; + iface->handle_set_icon_file = user_set_icon_file; + iface->handle_set_language = user_set_language; + iface->handle_set_location = user_set_location; + iface->handle_set_locked = user_set_locked; + iface->handle_set_password = user_set_password; + iface->handle_set_password_mode = user_set_password_mode; + iface->handle_set_password_hint = user_set_password_hint; + iface->handle_set_real_name = user_set_real_name; + iface->handle_set_shell = user_set_shell; + iface->handle_set_user_name = user_set_user_name; + iface->handle_set_xsession = user_set_x_session; ++ iface->handle_set_session = user_set_session; ++ iface->handle_set_session_type = user_set_session_type; + iface->handle_get_password_expiration_policy = user_get_password_expiration_policy; + } + + static void + user_init (User *user) + { + user->system_bus_connection = NULL; + user->default_icon_file = NULL; + user->login_history = NULL; + user->keyfile = g_key_file_new (); + } +-- +2.17.1 + diff --git a/SOURCES/0002-user-export-new-Saved-property.patch b/SOURCES/0002-user-export-new-Saved-property.patch new file mode 100644 index 0000000..3b02996 --- /dev/null +++ b/SOURCES/0002-user-export-new-Saved-property.patch @@ -0,0 +1,629 @@ +From 14bb1237f71e38749558c74963032a0387eecec0 Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Wed, 15 Aug 2018 16:04:42 -0400 +Subject: [PATCH 2/2] user: export new Saved property + +accountsservice maintains a state file for some users, if those users +have selected a specific session or language. + +There's no good way, at the moment, for an application to check if a +specific user has saved state. + +This commit exports the Saved property on the User object. +--- + data/org.freedesktop.Accounts.User.xml | 10 ++++++++++ + src/daemon.c | 3 +++ + src/libaccountsservice/act-user.c | 19 +++++++++++++++++++ + src/libaccountsservice/act-user.h | 1 + + src/user.c | 10 ++++++++++ + src/user.h | 2 ++ + 6 files changed, 45 insertions(+) + +diff --git a/data/org.freedesktop.Accounts.User.xml b/data/org.freedesktop.Accounts.User.xml +index 7fc3c61..8d3fe1c 100644 +--- a/data/org.freedesktop.Accounts.User.xml ++++ b/data/org.freedesktop.Accounts.User.xml +@@ -811,60 +811,70 @@ + + + + + + + + The login history for this user. + Each entry in the array represents a login session. The first two + members are the login time and logout time, as timestamps (seconds since the epoch). If the session is still running, the logout time + is 0. + + + The a{sv} member is a dictionary containing additional information + about the session. Possible members include 'type' (with values like ':0', 'tty0', 'pts/0' etc). + + + + + + + + + + The filename of a png file containing the users icon. + + + + + ++ ++ ++ ++ ++ Whether the users account has retained state ++ ++ ++ ++ ++ + + + + + Whether the users account is locked. + + + + + + + + + + The password mode for the user account, encoded as an integer: + + + 0 + Regular password + + + 1 + Password must be set at next login + + + 2 + No password + + + +diff --git a/src/daemon.c b/src/daemon.c +index 2851ed6..2587b8a 100644 +--- a/src/daemon.c ++++ b/src/daemon.c +@@ -1187,60 +1187,61 @@ daemon_cache_user (AccountsAccounts *accounts, + context, + g_strdup (user_name), + g_free); + + return TRUE; + } + + static void + daemon_uncache_user_authorized_cb (Daemon *daemon, + User *dummy, + GDBusMethodInvocation *context, + gpointer data) + { + const gchar *user_name = data; + User *user; + + sys_log (context, "uncache user '%s'", user_name); + + user = daemon_local_find_user_by_name (daemon, user_name); + if (user == NULL) { + throw_error (context, ERROR_USER_DOES_NOT_EXIST, + "No user with the name %s found", user_name); + return; + } + + /* Always use the canonical user name looked up */ + user_name = user_get_user_name (user); + + remove_cache_files (user_name); + ++ user_set_saved (user, FALSE); + user_set_cached (user, FALSE); + + accounts_accounts_complete_uncache_user (NULL, context); + + queue_reload_users (daemon); + } + + static gboolean + daemon_uncache_user (AccountsAccounts *accounts, + GDBusMethodInvocation *context, + const gchar *user_name) + { + Daemon *daemon = (Daemon*)accounts; + + daemon_local_check_auth (daemon, + NULL, + "org.freedesktop.accounts.user-administration", + TRUE, + daemon_uncache_user_authorized_cb, + context, + g_strdup (user_name), + g_free); + + return TRUE; + } + + typedef struct { + uid_t uid; + gboolean remove_files; + } DeleteUserData; +@@ -1252,60 +1253,62 @@ daemon_delete_user_authorized_cb (Daemon *daemon, + gpointer data) + + { + DeleteUserData *ud = data; + g_autoptr(GError) error = NULL; + struct passwd *pwent; + const gchar *argv[6]; + User *user; + + pwent = getpwuid (ud->uid); + + if (pwent == NULL) { + throw_error (context, ERROR_USER_DOES_NOT_EXIST, "No user with uid %d found", ud->uid); + return; + } + + sys_log (context, "delete user '%s' (%d)", pwent->pw_name, ud->uid); + + user = daemon_local_find_user_by_id (daemon, ud->uid); + + if (user != NULL) { + user_set_cached (user, FALSE); + + if (daemon->priv->autologin == user) { + daemon_local_set_automatic_login (daemon, user, FALSE, NULL); + } + } + + remove_cache_files (pwent->pw_name); + ++ user_set_saved (user, FALSE); ++ + argv[0] = "/usr/sbin/userdel"; + if (ud->remove_files) { + argv[1] = "-f"; + argv[2] = "-r"; + argv[3] = "--"; + argv[4] = pwent->pw_name; + argv[5] = NULL; + } + else { + argv[1] = "-f"; + argv[2] = "--"; + argv[3] = pwent->pw_name; + argv[4] = NULL; + } + + if (!spawn_with_login_uid (context, argv, &error)) { + throw_error (context, ERROR_FAILED, "running '%s' failed: %s", argv[0], error->message); + return; + } + + accounts_accounts_complete_delete_user (NULL, context); + } + + + static gboolean + daemon_delete_user (AccountsAccounts *accounts, + GDBusMethodInvocation *context, + gint64 uid, + gboolean remove_files) + { +diff --git a/src/libaccountsservice/act-user.c b/src/libaccountsservice/act-user.c +index dd8f81b..540ffe7 100644 +--- a/src/libaccountsservice/act-user.c ++++ b/src/libaccountsservice/act-user.c +@@ -849,60 +849,79 @@ act_user_collate (ActUser *user1, + * + * Returns whether or not #ActUser is currently graphically logged in + * on the same seat as the seat of the session of the calling process. + * + * Returns: %TRUE or %FALSE + */ + gboolean + act_user_is_logged_in (ActUser *user) + { + return user->our_sessions != NULL; + } + + /** + * act_user_is_logged_in_anywhere: + * @user: a #ActUser + * + * Returns whether or not #ActUser is currently logged in in any way + * whatsoever. See also act_user_is_logged_in(). + * + * (Currently, this function is only implemented for systemd-logind. + * For ConsoleKit, it is equivalent to act_user_is_logged_in.) + * + * Returns: %TRUE or %FALSE + */ + gboolean + act_user_is_logged_in_anywhere (ActUser *user) + { + return user->our_sessions != NULL || user->other_sessions != NULL; + } + ++/** ++ * act_user_get_saved: ++ * @user: a #ActUser ++ * ++ * Returns whether or not the #ActUser account has retained state in accountsservice. ++ * ++ * Returns: %TRUE or %FALSE ++ */ ++gboolean ++act_user_get_saved (ActUser *user) ++{ ++ g_return_val_if_fail (ACT_IS_USER (user), TRUE); ++ ++ if (user->accounts_proxy == NULL) ++ return FALSE; ++ ++ return accounts_user_get_saved (user->accounts_proxy); ++} ++ + /** + * act_user_get_locked: + * @user: a #ActUser + * + * Returns whether or not the #ActUser account is locked. + * + * Returns: %TRUE or %FALSE + */ + gboolean + act_user_get_locked (ActUser *user) + { + g_return_val_if_fail (ACT_IS_USER (user), TRUE); + + if (user->accounts_proxy == NULL) + return TRUE; + + return accounts_user_get_locked (user->accounts_proxy); + } + + /** + * act_user_get_automatic_login: + * @user: a #ActUser + * + * Returns whether or not #ActUser is automatically logged in at boot time. + * + * Returns: %TRUE or %FALSE + */ + gboolean + act_user_get_automatic_login (ActUser *user) + { +diff --git a/src/libaccountsservice/act-user.h b/src/libaccountsservice/act-user.h +index 2ef13b1..34d7fe3 100644 +--- a/src/libaccountsservice/act-user.h ++++ b/src/libaccountsservice/act-user.h +@@ -43,60 +43,61 @@ typedef enum { + typedef enum { + ACT_USER_PASSWORD_MODE_REGULAR, + ACT_USER_PASSWORD_MODE_SET_AT_LOGIN, + ACT_USER_PASSWORD_MODE_NONE, + } ActUserPasswordMode; + + typedef struct _ActUser ActUser; + typedef struct _ActUserClass ActUserClass; + + GType act_user_get_type (void) G_GNUC_CONST; + + const char *act_user_get_object_path (ActUser *user); + + uid_t act_user_get_uid (ActUser *user); + const char *act_user_get_user_name (ActUser *user); + const char *act_user_get_real_name (ActUser *user); + ActUserAccountType act_user_get_account_type (ActUser *user); + ActUserPasswordMode act_user_get_password_mode (ActUser *user); + const char *act_user_get_password_hint (ActUser *user); + const char *act_user_get_home_dir (ActUser *user); + const char *act_user_get_shell (ActUser *user); + const char *act_user_get_email (ActUser *user); + const char *act_user_get_location (ActUser *user); + guint act_user_get_num_sessions (ActUser *user); + guint act_user_get_num_sessions_anywhere (ActUser *user); + gboolean act_user_is_logged_in (ActUser *user); + gboolean act_user_is_logged_in_anywhere (ActUser *user); + int act_user_get_login_frequency (ActUser *user); + gint64 act_user_get_login_time (ActUser *user); + const GVariant*act_user_get_login_history (ActUser *user); ++gboolean act_user_get_saved (ActUser *user); + gboolean act_user_get_locked (ActUser *user); + gboolean act_user_get_automatic_login (ActUser *user); + gboolean act_user_is_system_account (ActUser *user); + gboolean act_user_is_local_account (ActUser *user); + gboolean act_user_is_nonexistent (ActUser *user); + const char *act_user_get_icon_file (ActUser *user); + const char *act_user_get_language (ActUser *user); + const char *act_user_get_x_session (ActUser *user); + const char *act_user_get_session (ActUser *user); + const char *act_user_get_session_type (ActUser *user); + const char *act_user_get_primary_session_id (ActUser *user); + + gint act_user_collate (ActUser *user1, + ActUser *user2); + gboolean act_user_is_loaded (ActUser *user); + + void act_user_get_password_expiration_policy (ActUser *user, + gint64 *expiration_time, + gint64 *last_change_time, + gint64 *min_days_between_changes, + gint64 *max_days_between_changes, + gint64 *days_to_warn, + gint64 *days_after_expiration_until_lock); + + void act_user_set_email (ActUser *user, + const char *email); + void act_user_set_language (ActUser *user, + const char *language); + void act_user_set_x_session (ActUser *user, + const char *x_session); +diff --git a/src/user.c b/src/user.c +index 94c0244..93afadc 100644 +--- a/src/user.c ++++ b/src/user.c +@@ -284,60 +284,61 @@ user_update_from_keyfile (User *user, + } + + s = g_key_file_get_string (keyfile, "User", "Location", NULL); + if (s != NULL) { + accounts_user_set_location (ACCOUNTS_USER (user), s); + g_clear_pointer (&s, g_free); + } + + s = g_key_file_get_string (keyfile, "User", "PasswordHint", NULL); + if (s != NULL) { + accounts_user_set_password_hint (ACCOUNTS_USER (user), s); + g_clear_pointer (&s, g_free); + } + + s = g_key_file_get_string (keyfile, "User", "Icon", NULL); + if (s != NULL) { + accounts_user_set_icon_file (ACCOUNTS_USER (user), s); + g_clear_pointer (&s, g_free); + } + + if (g_key_file_has_key (keyfile, "User", "SystemAccount", NULL)) { + gboolean system_account; + + system_account = g_key_file_get_boolean (keyfile, "User", "SystemAccount", NULL); + accounts_user_set_system_account (ACCOUNTS_USER (user), system_account); + } + + g_clear_pointer (&user->keyfile, g_key_file_unref); + user->keyfile = g_key_file_ref (keyfile); + user_set_cached (user, TRUE); ++ user_set_saved (user, TRUE); + + g_object_thaw_notify (G_OBJECT (user)); + } + + void + user_update_local_account_property (User *user, + gboolean local) + { + accounts_user_set_local_account (ACCOUNTS_USER (user), local); + } + + void + user_update_system_account_property (User *user, + gboolean system) + { + accounts_user_set_system_account (ACCOUNTS_USER (user), system); + } + + static void + user_save_to_keyfile (User *user, + GKeyFile *keyfile) + { + g_key_file_remove_group (keyfile, "User", NULL); + + if (accounts_user_get_email (ACCOUNTS_USER (user))) + g_key_file_set_string (keyfile, "User", "Email", accounts_user_get_email (ACCOUNTS_USER (user))); + + if (accounts_user_get_language (ACCOUNTS_USER (user))) + g_key_file_set_string (keyfile, "User", "Language", accounts_user_get_language (ACCOUNTS_USER (user))); + +@@ -357,60 +358,62 @@ user_save_to_keyfile (User *user, + g_key_file_set_string (keyfile, "User", "PasswordHint", accounts_user_get_password_hint (ACCOUNTS_USER (user))); + + if (accounts_user_get_icon_file (ACCOUNTS_USER (user))) + g_key_file_set_string (keyfile, "User", "Icon", accounts_user_get_icon_file (ACCOUNTS_USER (user))); + + g_key_file_set_boolean (keyfile, "User", "SystemAccount", accounts_user_get_system_account (ACCOUNTS_USER (user))); + + user_set_cached (user, TRUE); + } + + static void + save_extra_data (User *user) + { + g_autofree gchar *data = NULL; + g_autofree gchar *filename = NULL; + g_autoptr(GError) error = NULL; + + user_save_to_keyfile (user, user->keyfile); + + data = g_key_file_to_data (user->keyfile, NULL, &error); + if (data == NULL) { + g_warning ("Saving data for user %s failed: %s", + accounts_user_get_user_name (ACCOUNTS_USER (user)), error->message); + return; + } + + filename = g_build_filename (USERDIR, + accounts_user_get_user_name (ACCOUNTS_USER (user)), + NULL); + g_file_set_contents (filename, data, -1, &error); ++ ++ user_set_saved (user, TRUE); + } + + static void + move_extra_data (const gchar *old_name, + const gchar *new_name) + { + g_autofree gchar *old_filename = NULL; + g_autofree gchar *new_filename = NULL; + + old_filename = g_build_filename (USERDIR, + old_name, NULL); + new_filename = g_build_filename (USERDIR, + new_name, NULL); + + g_rename (old_filename, new_filename); + } + + static GVariant * + user_extension_get_value (User *user, + GDBusInterfaceInfo *interface, + const GDBusPropertyInfo *property) + { + const GVariantType *type = G_VARIANT_TYPE (property->signature); + GVariant *value; + g_autofree gchar *printed = NULL; + gint i; + + /* First, try to get the value from the keyfile */ + printed = g_key_file_get_value (user->keyfile, interface->name, property->name, NULL); + if (printed) { +@@ -773,60 +776,67 @@ const gchar * + user_get_object_path (User *user) + { + return g_dbus_interface_skeleton_get_object_path (G_DBUS_INTERFACE_SKELETON (user)); + } + + uid_t + user_get_uid (User *user) + { + return accounts_user_get_uid (ACCOUNTS_USER (user)); + } + + const gchar * + user_get_shell(User *user) + { + return accounts_user_get_shell (ACCOUNTS_USER (user)); + } + + gboolean + user_get_cached (User *user) + { + return user->cached; + } + + void + user_set_cached (User *user, + gboolean cached) + { + user->cached = cached; + } + ++void ++user_set_saved (User *user, ++ gboolean saved) ++{ ++ accounts_user_set_saved (ACCOUNTS_USER (user), saved); ++} ++ + static void + throw_error (GDBusMethodInvocation *context, + gint error_code, + const gchar *format, + ...) + { + va_list args; + g_autofree gchar *message = NULL; + + 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); + } + + static void + user_change_real_name_authorized_cb (Daemon *daemon, + User *user, + GDBusMethodInvocation *context, + gpointer data) + + { + gchar *name = data; + g_autoptr(GError) error = NULL; + const gchar *argv[6]; + + if (g_strcmp0 (accounts_user_get_real_name (ACCOUNTS_USER (user)), name) != 0) { + sys_log (context, + "change real name of user '%s' (%d) to '%s'", +diff --git a/src/user.h b/src/user.h +index 39c6f13..b3b3380 100644 +--- a/src/user.h ++++ b/src/user.h +@@ -39,47 +39,49 @@ 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 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); + gboolean user_get_cached (User *user); + void user_set_cached (User *user, + gboolean cached); ++void user_set_saved (User *user, ++ gboolean saved); + + 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.17.1 + diff --git a/SPECS/accountsservice.spec b/SPECS/accountsservice.spec new file mode 100644 index 0000000..fcbb382 --- /dev/null +++ b/SPECS/accountsservice.spec @@ -0,0 +1,357 @@ +%global _hardened_build 1 + +Name: accountsservice +Version: 0.6.50 +Release: 7%{?dist} +Summary: D-Bus interfaces for querying and manipulating user account information +License: GPLv3+ +URL: https://www.freedesktop.org/wiki/Software/AccountsService/ + +#VCS: git:git://git.freedesktop.org/accountsservice +Source0: http://www.freedesktop.org/software/accountsservice/accountsservice-%{version}.tar.xz + +BuildRequires: glib2-devel +BuildRequires: polkit-devel +BuildRequires: libtool, automake, autoconf, gettext-devel, intltool +BuildRequires: systemd +BuildRequires: systemd-devel +BuildRequires: gobject-introspection-devel +BuildRequires: gtk-doc +BuildRequires: git + +Patch01: 0001-user-add-new-Session-SessionType-properties-to-repla.patch +Patch02: 0002-user-export-new-Saved-property.patch + +Patch10: 0001-daemon-Fix-warnings-about-type-punning.patch + +Patch20: 0001-lib-don-t-set-loaded-state-until-seat-is-fetched.patch + +Patch30: 0001-data-don-t-send-change-updates-for-login-history.patch + +Patch90: 0001-lib-save-os-when-creating-user.patch + +Requires: polkit +Requires: shadow-utils +%{?systemd_requires} + +%description +The accountsservice project provides a set of D-Bus interfaces for +querying and manipulating user account information and an implementation +of these interfaces, based on the useradd, usermod and userdel commands. + +%package libs +Summary: Client-side library to talk to accountsservice +Requires: %{name} = %{version}-%{release} + +%description libs +The accountsservice-libs package contains a library that can +be used by applications that want to interact with the accountsservice +daemon. + +%package devel +Summary: Development files for accountsservice-libs +Requires: %{name}-libs = %{version}-%{release} + +%description devel +The accountsservice-devel package contains headers and other +files needed to build applications that use accountsservice-libs. + + +%prep +%autosetup -S git +autoreconf -f -i + +%build +export CFLAGS="$RPM_OPT_FLAGS -fno-strict-aliasing" +%configure --enable-user-heuristics +make %{?_smp_mflags} + + +%install +make install DESTDIR=$RPM_BUILD_ROOT +rm $RPM_BUILD_ROOT%{_libdir}/*.la +rm $RPM_BUILD_ROOT%{_libdir}/*.a +%find_lang accounts-service + +%ldconfig_scriptlets libs + +%post +%systemd_post accounts-daemon.service + +%preun +%systemd_preun accounts-daemon.service + +%postun +%systemd_postun accounts-daemon.service + +%files -f accounts-service.lang +%license COPYING +%doc README AUTHORS +%{_sysconfdir}/dbus-1/system.d/org.freedesktop.Accounts.conf +%{_libexecdir}/accounts-daemon +%{_datadir}/dbus-1/interfaces/org.freedesktop.Accounts.xml +%{_datadir}/dbus-1/interfaces/org.freedesktop.Accounts.User.xml +%{_datadir}/dbus-1/system-services/org.freedesktop.Accounts.service +%{_datadir}/polkit-1/actions/org.freedesktop.accounts.policy +%{_datadir}/accountsservice/interfaces/com.redhat.AccountsServiceUser.System.xml +%{_datadir}/dbus-1/interfaces/com.redhat.AccountsServiceUser.System.xml +%dir %{_localstatedir}/lib/AccountsService/ +%dir %{_localstatedir}/lib/AccountsService/users +%dir %{_localstatedir}/lib/AccountsService/icons +%{_unitdir}/accounts-daemon.service + +%files libs +%{_libdir}/libaccountsservice.so.* +%{_libdir}/girepository-1.0/AccountsService-1.0.typelib + +%files devel +%{_includedir}/accountsservice-1.0 +%{_libdir}/libaccountsservice.so +%{_libdir}/pkgconfig/accountsservice.pc +%{_datadir}/gir-1.0/AccountsService-1.0.gir +%dir %{_datadir}/gtk-doc/html/libaccountsservice +%{_datadir}/gtk-doc/html/libaccountsservice/* + +%changelog +* Mon Jun 17 2019 Ray Strode - 0.6.50-7 +- Don't send change updates for login history changes + Resolves: #1713080 + +* Mon Nov 26 2018 Ray Strode - 0.6.50-6 +- Fix user switching before screen lock + Resolves: #1653263 + +* Mon Oct 15 2018 Ray Strode - 0.6.50-5 +- Turn off aliasing optimizations until glib codegen is fixed + Related: #1628060 1639428 + +* Fri Oct 12 2018 Ray Strode - 0.6.50-4 + Correct rpmdiff complaints + Related: #1628060 + +* Fri Oct 12 2018 Ray Strode - 0.6.50-3 +- Record OS in user data when creating new users + Related: #1628060 + +* Mon Aug 20 2018 Ray Strode - 0.6.50-2 +- add new api needed for handling upgrades from RHEL 7 + Related: #1612915 1595825 + +* Fri Jul 13 2018 Ray Strode - 0.6.50-1 +- Update to 0.6.50 + Related: #1597499 + +* Tue Apr 24 2018 Ray Strode - 0.6.47-1 +- Update to 0.6.47 + +* Sat Apr 21 2018 Peter Robinson 0.4.46-1 +- Update to 0.6.46 +- Spec cleanup, use %%license + +* Wed Feb 07 2018 Fedora Release Engineering - 0.6.42-9 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_28_Mass_Rebuild + +* Sun Feb 04 2018 Igor Gnatenko - 0.6.42-8 +- Switch to %%ldconfig_scriptlets + +* Thu Jan 25 2018 Igor Gnatenko - 0.6.42-7 +- Fix systemd executions/requirements + +* Wed Jan 24 2018 Ray Strode - 0.6.42-6 +- Fix crash introduced by glibc/libxcrypt change + https://fedoraproject.org/wiki/Changes/Replace_glibc_libcrypt_with_libxcrypt + Resolves: #1538181 + +* Sat Jan 20 2018 Björn Esser - 0.6.42-5 +- Rebuilt for switch to libxcrypt + +* Wed Aug 02 2017 Fedora Release Engineering - 0.6.42-4 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_27_Binutils_Mass_Rebuild + +* Wed Jul 26 2017 Fedora Release Engineering - 0.6.42-3 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_27_Mass_Rebuild + +* Fri Feb 10 2017 Fedora Release Engineering - 0.6.42-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_26_Mass_Rebuild + +* Thu Jun 09 2016 Ray Strode - 0.6.42-1 +- Update to 0.6.42 +- Fixes systemd incompatibility + +* Tue May 31 2016 Ray Strode - 0.6.40-4 +- Don't create /root/.cache at startup + Resolves: #1331926 + +* Wed Feb 03 2016 Fedora Release Engineering - 0.6.40-3 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_24_Mass_Rebuild + +* Tue Jun 16 2015 Fedora Release Engineering - 0.6.40-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_23_Mass_Rebuild + +* Fri Jan 23 2015 Ray Strode 0.6.40-1 +- Update to 0.6.40 + +* Fri Oct 17 2014 Ray Strode 0.6.39-2 +- More ListCachedUsers race fixes (this time with SSSD) + Related: #1147504 + +* Thu Oct 16 2014 Ray Strode 0.6.39-1 +- Update to 0.6.39 +- Fixes ListCachedUsers race at startup + +* Thu Sep 18 2014 Stef Walter - 0.6.38-1 +- Update to 0.6.38 +- Fixes polkit policy rhbz#1094138 +- Remove dbus-glib-devel dependency, accountsservice uses gdbus now + +* Fri Aug 15 2014 Fedora Release Engineering - 0.6.37-3 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_21_22_Mass_Rebuild + +* Tue Jul 22 2014 Kalev Lember - 0.6.37-2 +- Rebuilt for gobject-introspection 1.41.4 + +* Sat Jun 07 2014 Kalev Lember - 0.6.37-1 +- Update to 0.6.37, drop upstreamed patches + +* Sat Jun 07 2014 Fedora Release Engineering - 0.6.35-5 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_21_Mass_Rebuild + +* Fri Jan 10 2014 Matthias Clasen - 0.6.35-4 +- Consistently call userdel with -f + +* Wed Nov 20 2013 Ray Strode 0.6.35-3 +- Only treat users < 1000 as system users +- only use user heuristics on the range 500-1000 + +* Mon Nov 11 2013 Ray Strode 0.6.35-2 +- pass --enable-user-heuristics which fedora needs so users + with UIDs less than 1000 show up in the user list. + +* Mon Oct 28 2013 Ray Strode 0.6.35-1 +- Update to 0.6.35 + Related: #1013721 + +* Sat Aug 03 2013 Fedora Release Engineering - 0.6.34-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_20_Mass_Rebuild + +* Tue Jun 11 2013 Ray Strode 0.6.34-1 +- Update to 0.6.34 + +* Tue Jun 11 2013 Matthias Clasen - 0.6.33-1 +- Update to 0.6.33 + +* Tue May 14 2013 Matthias Clasen - 0.6.32-1 +- Update to 0.6.32 + +* Thu Apr 18 2013 Matthias Clasen - 0.6.31-2 +- Hardened build + +* Tue Apr 16 2013 Matthias Clasen - 0.6.31-1 +- Update to 0.6.31 + +* Wed Feb 13 2013 Fedora Release Engineering - 0.6.30-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_19_Mass_Rebuild + +* Wed Jan 16 2013 Richard Hughes - 0.6.30-1 +- Update to 0.6.30 + +* Fri Nov 16 2012 Matthias Clasen - 0.6.26-1 +- Update to 0.6.26 + +* Tue Oct 2 2012 Matthias Clasen - 0.6.25-2 +- Update to 0.6.25 +- Use systemd scriptlets (#856649) + +* Wed Jul 18 2012 Fedora Release Engineering - 0.6.22-3 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_18_Mass_Rebuild + +* Sat Jul 14 2012 Ville Skyttä - 0.6.22-2 +- Add ldconfig scriptlets to -libs. + +* Thu Jun 28 2012 Ray Strode 0.6.22-1 +- Update to 0.6.22. +- Fixes CVE-2012-2737 - local file disclosure + Related: #832532 + +* Thu May 30 2012 Matthias Clasen 0.6.21-1 +- Update to 0.6.21 + +* Fri May 04 2012 Ray Strode 0.6.20-1 +- Update to 0.6.20. Should fix user list. + Related: #814690 + +* Thu May 03 2012 Ray Strode 0.6.19-1 +- Update to 0.6.19 + Allows user deletion of logged in users + Related: #814690 + +* Wed Apr 11 2012 Matthias Clasen - 0.6.18-1 +- Update to 0.6.18 + +* Tue Mar 27 2012 Ray Strode 0.6.17-1 +- Update to latest release + +* Sun Mar 4 2012 Peter Robinson - 0.6.15-4 +- Fix unitdir with usrmove + +* Thu Jan 12 2012 Fedora Release Engineering - 0.6.15-3 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_17_Mass_Rebuild + +* Tue Nov 29 2011 Matthias Clasen 0.6.15-2 +- Make resetting user icons work +- Update to 0.6.15 +- Fixes session chooser at login screen when logged into vt + +* Wed Sep 21 2011 Ray Strode 0.6.14-2 +- Fix wtmp loading so users coming from the network are + remembered in the user list in subsequent boots + +* Wed Sep 21 2011 Ray Strode 0.6.14-1 +- Update to 0.6.14 + +* Sun Sep 4 2011 Matthias Clasen - 0.6.13-3 +- Fix fast user switching + +* Mon Aug 15 2011 Kalev Lember - 0.6.13-2 +- Rebuilt for rpm bug #728707 + +* Tue Jul 19 2011 Matthias Clasen - 0.6.13-1 +- Update to 0.6.13 +- Drop ConsoleKit dependency + +* Mon Jun 06 2011 Ray Strode 0.6.12-1 +- Update to latest release + +* Wed May 18 2011 Matthias Clasen 0.6.11-1 +- Update to 0.6.11 + +* Mon Feb 07 2011 Fedora Release Engineering - 0.6.3-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_15_Mass_Rebuild + +* Wed Feb 02 2011 Ray Strode 0.6.3-1 +- Update to 0.6.3 + +* Thu Jan 27 2011 Matthias Clasen 0.6.2-1 +- Update to 0.6.2 + +* Wed Jul 21 2010 Matthias Clasen 0.6.1-1 +- Update to 0.6.1 +- Install systemd unit file + +* Mon Apr 5 2010 Matthias Clasen 0.6-2 +- Always emit changed signal on icon change + +* Tue Mar 30 2010 Matthias Clasen 0.6-1 +- Update to 0.6 + +* Mon Mar 22 2010 Matthias Clasen 0.5-1 +- Update to 0.5 + +* Mon Feb 22 2010 Bastien Nocera 0.4-3 +- Fix directory ownership + +* Mon Feb 22 2010 Bastien Nocera 0.4-2 +- Add missing directories to the filelist + +* Fri Jan 29 2010 Matthias Clasen 0.4-1 +- Initial packaging, based on work by Richard Hughes