From 51d5c9e9baec33aa74a60a4ac11f1de8f71acb2a 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 6cf5e30..648774c 100644 --- a/data/Makefile.am +++ b/data/Makefile.am @@ -1,45 +1,57 @@ 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) \ org.freedesktop.realmd.xml \ 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 e7e26b1..9f3d6e5 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.21.0