//
// Syd: rock-solid application kernel
// src/t/debugoff.rs: ptracer detection
//
// Copyright (c) 2025 Ali Polatel <alip@chesswob.org>
// Based in part upon DebugOff crate which is
//   Copyright (C) 2022 0xor0ne
//
// SPDX-License-Identifier: GPL-3.0

use nix::sys::ptrace::traceme;
use std::sync::atomic::{AtomicBool, Ordering};

thread_local!(pub static TRACEME_DONE: AtomicBool = const { AtomicBool::new(false) });

/// Call `ptrace(PTRACE_TRACEME, ...)` one time to detect the presence of a debugger.
///
/// This function can be called multiple times.
///
/// At the first invocation, the function expects a return value of 0 from `ptrace(PTRACE_TRACEME, ...)`.
/// In subsequent calls, `ptrace(PTRACE_TRACEME, ...)` should return -1.
///
/// If the above is not satisfied, the function calls `exit_group(0)`.
///
/// To be more effective, the function should be called at least once for each thread.
#[inline(always)]
pub fn ptraceme_or_die() {
    let res = traceme();

    TRACEME_DONE.with(|traceme_done| {
        // The first time this function is called, res should be Ok(_). Subsequent calls should
        // return Err(_)
        if !traceme_done.load(Ordering::SeqCst) {
            match res {
                Ok(_) => traceme_done.store(true, Ordering::SeqCst),
                Err(_) => the_end(),
            }
        } else if res.is_ok() {
            the_end()
        }
    });
}

#[inline(always)]
fn the_end() {
    panic!("ptracer detected!");
}
