Viewing: utmp.c
📄 utmp.c (Read Only) ⬅ To go back
// 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;
}