diff options
author | Ondrej Holy <oholy@redhat.com> | 2012-11-02 16:04:56 +0100 |
---|---|---|
committer | Ray Strode <rstrode@redhat.com> | 2012-11-12 16:10:48 -0500 |
commit | a30ac649fb83de8b1706d1b8bf4cee193c81b706 (patch) | |
tree | dd078c4255d19fefffd02881f6844a992c36f0c5 | |
parent | eb1c98bd9785cea849028b8be60d8d16c9abcab0 (diff) |
accountsservice: Add User.LoginHistory property
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.
https://bugs.freedesktop.org/show_bug.cgi?id=55915
-rw-r--r-- | data/org.freedesktop.Accounts.User.xml | 17 | ||||
-rw-r--r-- | src/daemon.c | 106 | ||||
-rw-r--r-- | src/libaccountsservice/act-user.c | 39 | ||||
-rw-r--r-- | src/libaccountsservice/act-user.h | 1 | ||||
-rw-r--r-- | src/user.c | 16 |
5 files changed, 161 insertions, 18 deletions
diff --git a/data/org.freedesktop.Accounts.User.xml b/data/org.freedesktop.Accounts.User.xml index 11884d6..88198d8 100644 --- a/data/org.freedesktop.Accounts.User.xml +++ b/data/org.freedesktop.Accounts.User.xml @@ -663,6 +663,23 @@ </doc:doc> </property> + <property name="LoginHistory" type="a(xxa{sv})" access="read"> + <doc:doc> + <doc:description> + <doc:para> + 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. + </doc:para> + <doc:para> + 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). + </doc:para> + </doc:description> + </doc:doc> + </property> + <property name="IconFile" type="s" access="read"> <doc:doc> <doc:description> diff --git a/src/daemon.c b/src/daemon.c index 8000192..9c6fe28 100644 --- a/src/daemon.c +++ b/src/daemon.c @@ -209,18 +209,33 @@ daemon_local_user_is_excluded (Daemon *daemon, const gchar *username, const gcha typedef struct { int frequency; gint64 time; + GList *previous_logins; } UserAccounting; +typedef struct { + gchar *id; + gint64 login_time; + gint64 logout_time; +} UserPreviousLogin; + +typedef struct { + GHashTable *login_hash; + GHashTable *logout_hash; +} WTmpGeneratorState; + static struct passwd * entry_generator_wtmp (GHashTable *users, gpointer *state) { - GHashTable *login_frequency_hash; + GHashTable *login_hash, *logout_hash; struct utmpx *wtmp_entry; GHashTableIter iter; gpointer key, value; struct passwd *pwent; User *user; + WTmpGeneratorState *state_data; + GVariantBuilder *builder, *builder2; + GList *l; if (*state == NULL) { /* First iteration */ @@ -232,14 +247,40 @@ entry_generator_wtmp (GHashTable *users, utmpxname (_PATH_WTMPX); setutxent (); #endif - *state = g_hash_table_new (g_str_hash, g_str_equal); + *state = g_new (WTmpGeneratorState, 1); + state_data = *state; + state_data->login_hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); + state_data->logout_hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); } /* Every iteration */ - login_frequency_hash = *state; + state_data = *state; + login_hash = state_data->login_hash; + logout_hash = state_data->logout_hash; while ((wtmp_entry = getutxent ())) { - - UserAccounting *accounting; + UserAccounting *accounting; + UserPreviousLogin *previous_login; + + if (wtmp_entry->ut_type == BOOT_TIME) { + /* Set boot time for missing logout records */ + g_hash_table_iter_init (&iter, logout_hash); + while (g_hash_table_iter_next (&iter, &key, &value)) { + previous_login = (UserPreviousLogin *) value; + + if (previous_login->logout_time == 0) { + previous_login->logout_time = wtmp_entry->ut_tv.tv_sec; + } + } + g_hash_table_remove_all (logout_hash); + } else if (wtmp_entry->ut_type == DEAD_PROCESS) { + /* Save corresponding logout time */ + if (g_hash_table_lookup_extended (logout_hash, wtmp_entry->ut_line, &key, &value)) { + previous_login = (UserPreviousLogin *) value; + previous_login->logout_time = wtmp_entry->ut_tv.tv_sec; + + g_hash_table_remove (logout_hash, previous_login->id); + } + } if (wtmp_entry->ut_type != USER_PROCESS) { continue; @@ -254,43 +295,72 @@ entry_generator_wtmp (GHashTable *users, continue; } - if (!g_hash_table_lookup_extended (login_frequency_hash, + if (!g_hash_table_lookup_extended (login_hash, wtmp_entry->ut_user, &key, &value)) { accounting = g_new (UserAccounting, 1); - accounting->frequency = 1; - accounting->time = wtmp_entry->ut_tv.tv_sec; - g_hash_table_insert (login_frequency_hash, - g_strdup (wtmp_entry->ut_user), - accounting); + accounting->frequency = 0; + accounting->previous_logins = NULL; + + g_hash_table_insert (login_hash, g_strdup (wtmp_entry->ut_user), accounting); } else { accounting = value; - accounting->frequency++; - accounting->time = wtmp_entry->ut_tv.tv_sec; } + accounting->frequency++; + accounting->time = wtmp_entry->ut_tv.tv_sec; + + /* Add zero logout time to change it later on logout record */ + previous_login = g_new (UserPreviousLogin, 1); + previous_login->id = g_strdup (wtmp_entry->ut_line); + previous_login->login_time = wtmp_entry->ut_tv.tv_sec; + previous_login->logout_time = 0; + accounting->previous_logins = g_list_prepend (accounting->previous_logins, previous_login); + + g_hash_table_insert (logout_hash, g_strdup (wtmp_entry->ut_line), previous_login); + return pwent; } /* Last iteration */ endutxent (); - g_hash_table_iter_init (&iter, login_frequency_hash); + g_hash_table_iter_init (&iter, login_hash); while (g_hash_table_iter_next (&iter, &key, &value)) { - User *user; - UserAccounting *accounting = (UserAccounting *) value; + UserAccounting *accounting = (UserAccounting *) value; + UserPreviousLogin *previous_login; user = g_hash_table_lookup (users, key); if (user == NULL) { + for (l = accounting->previous_logins; l != NULL; l = l->next) { + previous_login = l->data; + g_free (previous_login->id); + } + g_list_free (accounting->previous_logins); continue; } g_object_set (user, "login-frequency", accounting->frequency, NULL); g_object_set (user, "login-time", accounting->time, NULL); + + builder = g_variant_builder_new (G_VARIANT_TYPE ("a(xxa{sv})")); + for (l = g_list_last (accounting->previous_logins); l != NULL; l = l->prev) { + previous_login = l->data; + + builder2 = g_variant_builder_new (G_VARIANT_TYPE ("a{sv}")); + g_variant_builder_add (builder2, "{sv}", "type", g_variant_new_string (previous_login->id)); + g_variant_builder_add (builder, "(xxa{sv})", previous_login->login_time, previous_login->logout_time, builder2); + g_variant_builder_unref (builder2); + g_free (previous_login->id); + } + g_object_set (user, "login-previous_login", g_variant_new ("a(xxa{sv})", builder), NULL); + g_variant_builder_unref (builder); + g_list_free (accounting->previous_logins); } - g_hash_table_foreach (login_frequency_hash, (GHFunc) g_free, NULL); - g_hash_table_unref (login_frequency_hash); + g_hash_table_unref (login_hash); + g_hash_table_unref (logout_hash); + g_free (state_data); *state = NULL; return NULL; } diff --git a/src/libaccountsservice/act-user.c b/src/libaccountsservice/act-user.c index 33d15a8..9f80e71 100644 --- a/src/libaccountsservice/act-user.c +++ b/src/libaccountsservice/act-user.c @@ -58,6 +58,7 @@ enum { PROP_LOCAL_ACCOUNT, PROP_LOGIN_FREQUENCY, PROP_LOGIN_TIME, + PROP_LOGIN_HISTORY, PROP_ICON_FILE, PROP_LANGUAGE, PROP_X_SESSION, @@ -93,6 +94,7 @@ struct _ActUser { GList *sessions; int login_frequency; gint64 login_time; + GVariant *login_history; ActUserAccountType account_type; ActUserPasswordMode password_mode; @@ -211,6 +213,9 @@ act_user_get_property (GObject *object, case PROP_LOGIN_TIME: g_value_set_int64 (value, user->login_time); break; + case PROP_LOGIN_HISTORY: + g_value_set_variant (value, user->login_history); + break; case PROP_SHELL: g_value_set_string (value, user->shell); break; @@ -357,6 +362,14 @@ act_user_class_init (ActUserClass *class) 0, G_PARAM_READABLE)); g_object_class_install_property (gobject_class, + PROP_LOGIN_HISTORY, + g_param_spec_variant ("login-history", + "Login history", + "The login history for this user.", + G_VARIANT_TYPE ("a(xxa{sv})"), + NULL, + G_PARAM_READABLE)); + g_object_class_install_property (gobject_class, PROP_ICON_FILE, g_param_spec_string ("icon-file", "Icon File", @@ -443,6 +456,7 @@ act_user_init (ActUser *user) user->user_name = NULL; user->real_name = NULL; user->sessions = NULL; + user->login_history = NULL; user->connection = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, &error); if (user->connection == NULL) { @@ -468,6 +482,7 @@ act_user_finalize (GObject *object) g_free (user->shell); g_free (user->email); g_free (user->location); + g_variant_unref (user->login_history); if (user->accounts_proxy != NULL) { g_object_unref (user->accounts_proxy); @@ -705,6 +720,22 @@ act_user_get_login_time (ActUser *user) { return user->login_time; } +/** + * act_user_get_login_history: + * @user: a #ActUser + * + * Returns the login history for @user. + * + * Returns: (transfer none): a pointer to GVariant of type "a(xxa{sv})" + * which must not be modified or freed, or %NULL. + */ +const GVariant * +act_user_get_login_history (ActUser *user) { + g_return_val_if_fail (ACT_IS_USER (user), NULL); + + return user->login_history; +} + int act_user_collate (ActUser *user1, ActUser *user2) @@ -1072,6 +1103,14 @@ collect_props (const gchar *key, user->login_time = new_login_time; g_object_notify (G_OBJECT (user), "login-time"); } + } else if (strcmp (key, "LoginHistory") == 0) { + GVariant *new_login_history = value; + + if (!g_variant_compare (user->login_history, new_login_history)) { + g_variant_unref (user->login_history); + user->login_history = g_variant_ref (new_login_history); + g_object_notify (G_OBJECT (user), "login-history"); + } } else if (strcmp (key, "IconFile") == 0) { const char *new_icon_file; diff --git a/src/libaccountsservice/act-user.h b/src/libaccountsservice/act-user.h index 70a97c1..31b2cc6 100644 --- a/src/libaccountsservice/act-user.h +++ b/src/libaccountsservice/act-user.h @@ -67,6 +67,7 @@ guint act_user_get_num_sessions (ActUser *user); gboolean act_user_is_logged_in (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); @@ -60,6 +60,7 @@ enum { PROP_LOCATION, PROP_LOGIN_FREQUENCY, PROP_LOGIN_TIME, + PROP_LOGIN_HISTORY, PROP_ICON_FILE, PROP_LOCKED, PROP_PASSWORD_MODE, @@ -92,6 +93,7 @@ struct User { gchar *location; guint64 login_frequency; gint64 login_time; + GVariant *login_history; gchar *icon_file; gchar *default_icon_file; gboolean locked; @@ -1691,6 +1693,12 @@ user_real_get_login_time (AccountsUser *user) return USER (user)->login_time; } +static const GVariant * +user_real_get_login_history (AccountsUser *user) +{ + return USER (user)->login_history; +} + static const gchar * user_real_get_icon_file (AccountsUser *user) { @@ -1781,6 +1789,9 @@ user_set_property (GObject *object, case PROP_LOGIN_TIME: user->login_time = g_value_get_int64 (value); break; + case PROP_LOGIN_HISTORY: + user->login_history = g_variant_ref (g_value_get_variant (value)); + break; case PROP_AUTOMATIC_LOGIN: user->automatic_login = g_value_get_boolean (value); break; @@ -1854,6 +1865,9 @@ user_get_property (GObject *object, case PROP_LOGIN_TIME: g_value_set_int64 (value, user->login_time); break; + case PROP_LOGIN_HISTORY: + g_value_set_variant (value, user->login_history); + break; case PROP_LOCKED: g_value_set_boolean (value, user->locked); break; @@ -1915,6 +1929,7 @@ user_accounts_user_iface_init (AccountsUserIface *iface) iface->get_location = user_real_get_location; iface->get_login_frequency = user_real_get_login_frequency; iface->get_login_time = user_real_get_login_time; + iface->get_login_history = user_real_get_login_history; iface->get_icon_file = user_real_get_icon_file; iface->get_locked = user_real_get_locked; iface->get_password_mode = user_real_get_password_mode; @@ -1944,4 +1959,5 @@ user_init (User *user) user->locked = FALSE; user->automatic_login = FALSE; user->system_account = FALSE; + user->login_history = NULL; } |