//
// Syd: rock-solid application kernel
// src/utils/syd-aux.rs: Print auxiliary vector information.
//
// Copyright (c) 2024, 2025, 2026 Ali Polatel <alip@chesswob.org>
//
// SPDX-License-Identifier: GPL-3.0

use std::{ffi::CStr, process::ExitCode};

use data_encoding::HEXLOWER;
use nix::{
    errno::Errno,
    libc::{
        getauxval, AT_BASE, AT_CLKTCK, AT_EGID, AT_ENTRY, AT_EUID, AT_FLAGS, AT_GID, AT_PAGESZ,
        AT_PHDR, AT_PHENT, AT_PHNUM, AT_PLATFORM, AT_RANDOM, AT_SECURE, AT_UID,
    },
};
use serde_json::json;

// Set global allocator to GrapheneOS allocator.
#[cfg(all(
    not(coverage),
    not(feature = "prof"),
    not(target_os = "android"),
    not(target_arch = "riscv64"),
    target_page_size_4k,
    target_pointer_width = "64"
))]
#[global_allocator]
static GLOBAL: hardened_malloc::HardenedMalloc = hardened_malloc::HardenedMalloc;

// Set global allocator to tcmalloc if profiling is enabled.
#[cfg(feature = "prof")]
#[global_allocator]
static GLOBAL: tcmalloc::TCMalloc = tcmalloc::TCMalloc;

syd::main! {
    use lexopt::prelude::*;

    syd::set_sigpipe_dfl()?;

    // Parse CLI options.
    let mut opt_secure = false;
    let mut opt_random = false;

    let mut parser = lexopt::Parser::from_env();
    while let Some(arg) = parser.next()? {
        match arg {
            Short('h') => {
                help();
                return Ok(ExitCode::SUCCESS);
            }
            Short('r') => opt_random = true,
            Short('s') => opt_secure = true,
            // We ignore non-option arguments,
            // to make set_at_secure_max test work.
            Value(_) => {}
            _ => return Err(arg.unexpected().into()),
        }
    }

    if opt_secure && opt_random {
        eprintln!("syd-aux: At most one of -r, -s must be given!");
        return Err(Errno::EINVAL.into());
    }

    let at_secure = unsafe { getauxval(AT_SECURE) } != 0;
    if opt_secure {
        return Ok(if at_secure {
            ExitCode::SUCCESS
        } else {
            ExitCode::FAILURE
        });
    }

    let at_random = unsafe { getauxval(AT_RANDOM) };
    let at_random =
        HEXLOWER.encode(unsafe { std::slice::from_raw_parts(at_random as *const u8, 16) });
    if opt_random {
        println!("{at_random}");
        return Ok(ExitCode::SUCCESS);
    }

    let at_uid = unsafe { getauxval(AT_UID) };
    let at_euid = unsafe { getauxval(AT_EUID) };
    let at_gid = unsafe { getauxval(AT_GID) };
    let at_egid = unsafe { getauxval(AT_EGID) };

    let at_base = unsafe { getauxval(AT_BASE) };
    let at_clktck = unsafe { getauxval(AT_CLKTCK) };
    let at_entry = unsafe { getauxval(AT_ENTRY) };
    let at_flags = unsafe { getauxval(AT_FLAGS) };
    let at_pagesz = unsafe { getauxval(AT_PAGESZ) };
    let at_phdr = unsafe { getauxval(AT_PHDR) };
    let at_phent = unsafe { getauxval(AT_PHENT) };
    let at_phnum = unsafe { getauxval(AT_PHNUM) };

    let at_platform = unsafe { getauxval(AT_PLATFORM) };
    let at_platform = unsafe { CStr::from_ptr(at_platform as *const nix::libc::c_char) }
        .to_string_lossy()
        .into_owned();

    #[expect(clippy::disallowed_methods)]
    let aux = json!({
        "uid": at_uid,
        "euid": at_euid,
        "gid": at_gid,
        "egid": at_egid,
        "base": at_base,
        "clktck": at_clktck,
        "entry": at_entry,
        "flags": at_flags,
        "pagesz": at_pagesz,
        "phdr": at_phdr,
        "phent": at_phent,
        "phnum": at_phnum,
        "secure": at_secure,
        "random": at_random,
        "platform": at_platform,
    });

    #[expect(clippy::disallowed_methods)]
    let aux = serde_json::to_string_pretty(&aux).unwrap();
    println!("{aux}");

    Ok(ExitCode::SUCCESS)
}

fn help() {
    println!("Usage: syd-aux [-hrs]");
    println!("Print auxiliary vector information.");
    println!("If -r is given print hexadecimal-encoded AT_RANDOM cookie.");
    println!("If -s is given exit with success if AT_SECURE is set.");
}
