From: Tulio A M Mendes Date: Thu, 11 Jun 2026 02:45:47 +0000 (-0300) Subject: utmp: add kernel utmp database infrastructure X-Git-Url: https://projects.tadryanom.me/?a=commitdiff_plain;h=89a739758e945df69d28f40e518a84d0655b16a6;p=AdrOS.git utmp: add kernel utmp database infrastructure 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. --- diff --git a/include/utmp.h b/include/utmp.h new file mode 100644 index 00000000..0af7cbf7 --- /dev/null +++ b/include/utmp.h @@ -0,0 +1,54 @@ +// SPDX-License-Identifier: BSD-3-Clause +/* + * Copyright (c) 2018, Tulio A M Mendes + * All rights reserved. + * See LICENSE for details. + * + * Source: https://github.com/tadryanom/AdrOS + */ + +#ifndef UTMP_H +#define UTMP_H + +#include + +/* 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 index 00000000..509271e8 --- /dev/null +++ b/src/kernel/utmp.c @@ -0,0 +1,136 @@ +// SPDX-License-Identifier: BSD-3-Clause +/* + * Copyright (c) 2018, Tulio A M Mendes + * 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 + +#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; +}