$OpenBSD: patch-src_wtmp-helper_c,v 1.3 2021/08/30 19:10:43 ajacoutot Exp $

Index: src/wtmp-helper.c
--- src/wtmp-helper.c.orig
+++ src/wtmp-helper.c
@@ -25,10 +25,6 @@
 #include "wtmp-helper.h"
 #include "user.h"
 
-#ifdef HAVE_UTMPX_H
-
-#include <utmpx.h>
-
 typedef struct {
         guint64 frequency;
         gint64 time;
@@ -48,6 +44,10 @@ user_previous_login_free (UserPreviousLogin *previous_
         g_free (previous_login);
 }
 
+#ifdef HAVE_UTMPX_H
+
+#include <utmpx.h>
+
 static gboolean
 wtmp_helper_start (void)
 {
@@ -213,10 +213,136 @@ wtmp_helper_get_path_for_monitor (void)
 
 #else /* HAVE_UTMPX_H */
 
+#include <stdio.h>
+#include <utmp.h>
+
+void
+wtmp_helper_update_login_frequencies (GHashTable *users)
+{
+        GHashTable *login_hash, *logout_hash;
+        struct utmp wtmp_entry;
+        GHashTableIter iter;
+        gpointer key, value;
+        User *user;
+        GVariantBuilder *builder, *builder2;
+        GList *l;
+        FILE *fp;
+
+        login_hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
+        logout_hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
+
+        fp = fopen(_PATH_WTMP, "r");
+        if (fp == NULL)
+            return;
+
+        while (fread(&wtmp_entry, sizeof(wtmp_entry), 1, fp) == 1) {
+                UserAccounting    *accounting;
+                UserPreviousLogin *previous_login;
+                gboolean shutdown_or_reboot = FALSE;
+
+                if (g_str_equal (wtmp_entry.ut_line, "~")) {
+                        if (g_str_equal (wtmp_entry.ut_name, "shutdown") ||
+                            g_str_equal (wtmp_entry.ut_name, "reboot")) {
+                                shutdown_or_reboot = TRUE;
+                        }
+                }
+
+                if (shutdown_or_reboot) {
+                        /* Set shutdown, reboot, or 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_time;
+                                }
+                        }
+                        g_hash_table_remove_all (logout_hash);
+                }
+
+                if (wtmp_entry.ut_name[0] == 0) {
+                        continue;
+                }
+
+                if (!g_hash_table_lookup_extended (login_hash,
+                                                   wtmp_entry.ut_name,
+                                                   &key, &value)) {
+                        accounting = g_new (UserAccounting, 1);
+                        accounting->frequency = 0;
+                        accounting->previous_logins = NULL;
+
+                        g_hash_table_insert (login_hash, g_strdup (wtmp_entry.ut_name), accounting);
+                } else {
+                        accounting = value;
+                }
+
+                accounting->frequency++;
+                accounting->time = wtmp_entry.ut_time;
+
+                /* 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_time;
+                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);
+        }
+
+        g_hash_table_iter_init (&iter, login_hash);
+        while (g_hash_table_iter_next (&iter, &key, &value)) {
+                UserAccounting    *accounting = (UserAccounting *) value;
+                UserPreviousLogin *previous_login;
+                gboolean           changed = FALSE;
+                guint64            old_login_frequency;
+                guint64            old_login_time;
+
+                user = g_hash_table_lookup (users, key);
+                if (user == NULL) {
+                        g_list_free_full (accounting->previous_logins, (GDestroyNotify) user_previous_login_free);
+                        continue;
+                }
+
+                g_object_get (user,
+                              "login-frequency", &old_login_frequency,
+                              "login-time", &old_login_time,
+                              NULL);
+
+                if (old_login_frequency != accounting->frequency) {
+                        g_object_set (user, "login-frequency", accounting->frequency, NULL);
+                        changed = TRUE;
+                }
+
+                if (old_login_time != accounting->time) {
+                        g_object_set (user, "login-time", accounting->time, NULL);
+                        changed = TRUE;
+                }
+
+                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_object_set (user, "login-history", g_variant_new ("a(xxa{sv})", builder), NULL);
+                g_variant_builder_unref (builder);
+                g_list_free_full (accounting->previous_logins, (GDestroyNotify) user_previous_login_free);
+
+                if (changed)
+                        user_changed (user);
+        }
+
+        g_hash_table_unref (login_hash);
+        g_hash_table_unref (logout_hash);
+}
+
 const gchar *
 wtmp_helper_get_path_for_monitor (void)
 {
-        return NULL;
+        return _PATH_WTMP;
 }
 
 #endif /* HAVE_UTMPX_H */
