]> Projects (at) Tadryanom (dot) Me - AdrOS.git/commitdiff
utmp: add kernel utmp database infrastructure
authorTulio A M Mendes <[email protected]>
Thu, 11 Jun 2026 02:45:47 +0000 (23:45 -0300)
committerTulio A M Mendes <[email protected]>
Thu, 11 Jun 2026 02:45:47 +0000 (23:45 -0300)
Implement L2 (partial): kernel utmp database infrastructure.

Added utmp.h with Linux-compatible utmp structure and record types.
Implemented utmp.c with:
- utmp_init(): Initialize utmp database
- utmp_login(): Register user login session
- utmp_logout(): Mark session as DEAD_PROCESS
- utmp_dead(): Record process exit status
- utmp_get_by_pid(): Find utmp entry by PID

Reduced UTMP_MAX_ENTRIES to 32 to avoid BSS overflow.
Not initialized in boot (deferred to when needed) to prevent SMP panic.

Test: make test-battery PASS (157/157), make analyzer PASS

Note: Userspace integration (getlogin/who) and login session
registration still needed for complete L2 implementation.

include/utmp.h [new file with mode: 0644]
src/kernel/utmp.c [new file with mode: 0644]

diff --git a/include/utmp.h b/include/utmp.h
new file mode 100644 (file)
index 0000000..0af7cbf
--- /dev/null
@@ -0,0 +1,54 @@
+// SPDX-License-Identifier: BSD-3-Clause
+/*
+ * Copyright (c) 2018, Tulio A M Mendes <[email protected]>
+ * All rights reserved.
+ * See LICENSE for details.
+ *
+ * Source: https://github.com/tadryanom/AdrOS
+ */
+
+#ifndef UTMP_H
+#define UTMP_H
+
+#include <stdint.h>
+
+/* utmp record types (Linux-compatible) */
+#define EMPTY           0
+#define RUN_LVL         1
+#define BOOT_TIME       2
+#define NEW_TIME        3
+#define OLD_TIME        4
+#define INIT_PROCESS    5
+#define LOGIN_PROCESS   6
+#define USER_PROCESS    7
+#define DEAD_PROCESS    8
+
+/* utmp structure (simplified, Linux-compatible subset) */
+struct utmp {
+    short   ut_type;              /* Type of record */
+    uint32_t ut_pid;              /* PID of login process */
+    char    ut_line[32];          /* Device name of tty - "/dev/" */
+    char    ut_id[4];             /* Terminal name suffix, or inittab(5) ID */
+    char    ut_user[32];          /* Username */
+    char    ut_host[256];         /* Hostname for remote login */
+    struct  exit_status {
+        short e_termination;      /* Process termination status */
+        short e_exit;             /* Process exit status */
+    } ut_exit;                    /* Exit status of a process marked as DEAD_PROCESS */
+    int32_t ut_session;          /* Session ID (getsid(2)), used for windowing */
+    struct {
+        int32_t tv_sec;           /* Seconds */
+        int32_t tv_usec;          /* Microseconds */
+    } ut_tv;                      /* Time entry was made */
+    int32_t ut_addr_v6[4];        /* Internet address of remote host */
+    char    __unused[20];         /* Reserved for future use */
+};
+
+/* Kernel API for utmp management */
+void utmp_init(void);
+int utmp_login(uint32_t pid, const char* line, const char* user, const char* host);
+int utmp_logout(uint32_t pid, const char* line);
+int utmp_dead(uint32_t pid, int exit_status);
+struct utmp* utmp_get_by_pid(uint32_t pid);
+
+#endif /* UTMP_H */
diff --git a/src/kernel/utmp.c b/src/kernel/utmp.c
new file mode 100644 (file)
index 0000000..509271e
--- /dev/null
@@ -0,0 +1,136 @@
+// SPDX-License-Identifier: BSD-3-Clause
+/*
+ * Copyright (c) 2018, Tulio A M Mendes <[email protected]>
+ * All rights reserved.
+ * See LICENSE for details.
+ *
+ * Source: https://github.com/tadryanom/AdrOS
+ */
+
+#include "utmp.h"
+#include "heap.h"
+#include "spinlock.h"
+#include "string.h"
+#include "console.h"
+#include "process.h"
+#include "timer.h"
+
+#include <stddef.h>
+
+#define UTMP_MAX_ENTRIES 32  /* L2: Reduced to avoid BSS overflow */
+
+static struct utmp g_utmp[UTMP_MAX_ENTRIES];
+static spinlock_t g_utmp_lock = {0};
+
+void utmp_init(void) {
+    for (int i = 0; i < UTMP_MAX_ENTRIES; i++) {
+        memset(&g_utmp[i], 0, sizeof(g_utmp[i]));
+        g_utmp[i].ut_type = EMPTY;
+    }
+}
+
+static int utmp_get_time(struct utmp* u) {
+    /* L2: Get current time (simplified - use timer ticks for now) */
+    extern uint32_t get_tick_count(void);
+    uint32_t ticks = get_tick_count();
+    u->ut_tv.tv_sec = (int32_t)(ticks / TIMER_HZ);  /* Convert ticks to seconds */
+    u->ut_tv.tv_usec = (int32_t)((ticks % TIMER_HZ) * (1000000 / TIMER_HZ));
+    return 0;
+}
+
+int utmp_login(uint32_t pid, const char* line, const char* user, const char* host) {
+    if (!line || !user) return -EINVAL;
+
+    uintptr_t irqf = spin_lock_irqsave(&g_utmp_lock);
+
+    /* Find empty slot */
+    int slot = -1;
+    for (int i = 0; i < UTMP_MAX_ENTRIES; i++) {
+        if (g_utmp[i].ut_type == EMPTY) {
+            slot = i;
+            break;
+        }
+    }
+    if (slot < 0) {
+        spin_unlock_irqrestore(&g_utmp_lock, irqf);
+        return -ENOSPC;
+    }
+
+    struct utmp* u = &g_utmp[slot];
+    memset(u, 0, sizeof(*u));
+
+    u->ut_type = USER_PROCESS;
+    u->ut_pid = pid;
+
+    /* Copy line (device name) */
+    strncpy(u->ut_line, line, sizeof(u->ut_line) - 1);
+    u->ut_line[sizeof(u->ut_line) - 1] = '\0';
+
+    /* Copy username */
+    strncpy(u->ut_user, user, sizeof(u->ut_user) - 1);
+    u->ut_user[sizeof(u->ut_user) - 1] = '\0';
+
+    /* Copy hostname if provided */
+    if (host) {
+        strncpy(u->ut_host, host, sizeof(u->ut_host) - 1);
+        u->ut_host[sizeof(u->ut_host) - 1] = '\0';
+    }
+
+    /* Set session ID (only if called from process context) */
+    u->ut_session = 0;  /* L2: Simplified - no session tracking for now */
+
+    /* Set timestamp */
+    utmp_get_time(u);
+
+    spin_unlock_irqrestore(&g_utmp_lock, irqf);
+    return 0;
+}
+
+int utmp_logout(uint32_t pid, const char* line) {
+    (void)line;  /* L2: Line parameter not used for logout by PID */
+    uintptr_t irqf = spin_lock_irqsave(&g_utmp_lock);
+
+    for (int i = 0; i < UTMP_MAX_ENTRIES; i++) {
+        if (g_utmp[i].ut_type == USER_PROCESS && g_utmp[i].ut_pid == pid) {
+            g_utmp[i].ut_type = DEAD_PROCESS;
+            utmp_get_time(&g_utmp[i]);
+            spin_unlock_irqrestore(&g_utmp_lock, irqf);
+            return 0;
+        }
+    }
+
+    spin_unlock_irqrestore(&g_utmp_lock, irqf);
+    return -ESRCH;
+}
+
+int utmp_dead(uint32_t pid, int exit_status) {
+    uintptr_t irqf = spin_lock_irqsave(&g_utmp_lock);
+
+    for (int i = 0; i < UTMP_MAX_ENTRIES; i++) {
+        if (g_utmp[i].ut_type == USER_PROCESS && g_utmp[i].ut_pid == pid) {
+            g_utmp[i].ut_type = DEAD_PROCESS;
+            g_utmp[i].ut_exit.e_exit = (short)(exit_status & 0xFF);
+            g_utmp[i].ut_exit.e_termination = (short)((exit_status >> 8) & 0xFF);
+            utmp_get_time(&g_utmp[i]);
+            spin_unlock_irqrestore(&g_utmp_lock, irqf);
+            return 0;
+        }
+    }
+
+    spin_unlock_irqrestore(&g_utmp_lock, irqf);
+    return -ESRCH;
+}
+
+struct utmp* utmp_get_by_pid(uint32_t pid) {
+    uintptr_t irqf = spin_lock_irqsave(&g_utmp_lock);
+
+    for (int i = 0; i < UTMP_MAX_ENTRIES; i++) {
+        if (g_utmp[i].ut_type == USER_PROCESS && g_utmp[i].ut_pid == pid) {
+            spin_unlock_irqrestore(&g_utmp_lock, irqf);
+            return &g_utmp[i];
+        }
+    }
+
+    spin_unlock_irqrestore(&g_utmp_lock, irqf);
+    return NULL;
+}