//
// Syd: rock-solid application kernel
// src/utils/syd-env.rs: Run a command with the environment of the process with the given PID.
//
// Copyright (c) 2024, 2025, 2026 Ali Polatel <alip@chesswob.org>
//
// SPDX-License-Identifier: GPL-3.0

use std::{
    collections::VecDeque,
    os::unix::process::CommandExt,
    process::{Command, ExitCode, Stdio},
};

use nix::{
    fcntl::{open, OFlag},
    libc::pid_t,
    sys::stat::Mode,
    unistd::Pid,
};
use syd::{path::XPathBuf, proc::proc_environ_read, syslog::LogLevel};

// 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! {
    syd::set_sigpipe_dfl()?;

    // Initialize logging.
    syd::log::log_init_simple(LogLevel::Warn)?;

    let mut args: VecDeque<_> = std::env::args().skip(1).collect();
    let pid  = match args.pop_front().as_deref() {
        None | Some("-h") => {
            help();
            return Ok(ExitCode::SUCCESS);
        }
        Some("-e") => {
            let _var = if let Some(var) = args.pop_front() {
                var
            } else {
                eprintln!("Error: -e requires an argument!");
                return Ok(ExitCode::FAILURE);
            };

            #[cfg(target_os = "android")]
            {
                eprintln!("Error: -e is not supported on Android!");
                return Ok(ExitCode::FAILURE);
            }

            #[cfg(not(target_os = "android"))]
            match syd::wordexp::WordExp::expand(&_var, true, dur::Duration::from_secs(3)) {
                Ok(val) => {
                    print!("{val}");
                    return Ok(ExitCode::SUCCESS);
                }
                Err(err) => {
                    eprintln!("Error: {err}");
                    std::process::exit(err.into());
                }
            };
        }
        Some(pid) => match pid.parse::<pid_t>().map(Pid::from_raw) {
            Ok(pid) => pid,
            Err(error) => {
                eprintln!("Invalid PID: {error}");
                return Ok(ExitCode::FAILURE);
            }
        },
    };

    let mut pfd = XPathBuf::from("/proc");
    pfd.push_pid(pid);
    pfd.push(b"environ");
    #[expect(clippy::disallowed_methods)]
    let pfd = open(&pfd, OFlag::O_RDONLY, Mode::empty())?;

    let environ = match proc_environ_read(pfd) {
        Ok(environ) => environ,
        Err(error) => {
            eprintln!("syd-env: {error}");
            return Ok(ExitCode::FAILURE);
        }
    };

    let error = Command::new("env")
        .args(args)
        .env_clear()
        .envs(&environ)
        .stdin(Stdio::inherit())
        .stdout(Stdio::inherit())
        .stderr(Stdio::inherit())
        .exec();
    eprintln!("syd-env: {error}");
    Ok(ExitCode::FAILURE)
}

fn help() {
    println!("Usage: syd-env pid [-i] [name=value]... {{command [arg...]}}");
    println!("Run a command with the environment of the process with the given PID.");
}
