summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorOndrej Holy <oholy@redhat.com>2012-11-02 16:04:56 +0100
committerRay Strode <rstrode@redhat.com>2012-11-12 16:10:48 -0500
commita30ac649fb83de8b1706d1b8bf4cee193c81b706 (patch)
treedd078c4255d19fefffd02881f6844a992c36f0c5
parenteb1c98bd9785cea849028b8be60d8d16c9abcab0 (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.xml17
-rw-r--r--src/daemon.c106
-rw-r--r--src/libaccountsservice/act-user.c39
-rw-r--r--src/libaccountsservice/act-user.h1
-rw-r--r--src/user.c16
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);
diff --git a/src/user.c b/src/user.c
index f9e09a6..8838872 100644
--- a/src/user.c
+++ b/src/user.c
@@ -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;
}