Files
sausage/host/cred-script/vendor/llrt_utils_watts/src/signals.rs
T

150 lines
4.3 KiB
Rust

// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
use rquickjs::{prelude::Opt, Ctx, Exception, Result, Value};
use crate::result::ResultExt;
use std::io;
#[cfg(unix)]
macro_rules! generate_signal_from_str_fn {
($($signal:path),*) => {
pub fn signal_from_str(signal: &str) -> Option<i32> {
let signal = ["libc::", signal].concat();
match signal.as_str() {
$(stringify!($signal) => Some($signal),)*
_ => None,
}
}
pub fn signal_str_from_i32(signal: i32) -> Option<&'static str> {
$(if signal == $signal {
return Some(&stringify!($signal)[6..]);
})*
None
}
};
}
#[cfg(unix)]
generate_signal_from_str_fn!(
libc::SIGHUP,
libc::SIGINT,
libc::SIGQUIT,
libc::SIGILL,
libc::SIGABRT,
libc::SIGFPE,
libc::SIGKILL,
libc::SIGSEGV,
libc::SIGPIPE,
libc::SIGALRM,
libc::SIGTERM
);
#[cfg(not(unix))]
static WINDOWS_SIGTERM: i32 = -1;
pub fn parse_signal(signal: Option<Value<'_>>) -> Result<i32> {
let Some(val) = signal else {
#[cfg(unix)]
return Ok(libc::SIGTERM);
#[cfg(not(unix))]
return Ok(WINDOWS_SIGTERM);
};
if let Some(num) = val.as_number() {
let sig = num as i32;
#[cfg(unix)]
return Ok(sig);
// On Windows: 0 checks existence, anything else kills
#[cfg(not(unix))]
return Ok(if sig == 0 { 0 } else { WINDOWS_SIGTERM });
}
if let Some(str_val) = val.as_string() {
let s = str_val.to_string()?;
#[cfg(unix)]
let mapped_sig = signal_from_str(&s);
#[cfg(not(unix))]
let mapped_sig = match s.as_str() {
"SIGINT" | "SIGTERM" | "SIGKILL" | "SIGQUIT" | "SIGHUP" | "SIGUSR1" => {
Some(WINDOWS_SIGTERM)
},
_ => None,
};
return match mapped_sig {
Some(sig) => Ok(sig),
None => Err(Exception::throw_type(
val.ctx(),
&format!("Unknown signal: {}", s),
)),
};
}
Err(Exception::throw_type(val.ctx(), "Invalid signal type"))
}
#[cfg(unix)]
pub fn kill_process_raw(pid: u32, signal: i32) -> io::Result<()> {
// libc::kill returns 0 on success, -1 on error
// SAFETY: kill is a safe system call as long as the signal value is valid, which is ensured by parse_signal
if unsafe { libc::kill(pid as i32, signal) } == 0 {
Ok(())
} else {
Err(io::Error::last_os_error())
}
}
#[cfg(windows)]
pub fn kill_process_raw(pid: u32, signal: i32) -> io::Result<()> {
use windows_sys::Win32::Foundation::CloseHandle;
use windows_sys::Win32::System::Threading::{OpenProcess, TerminateProcess, PROCESS_TERMINATE};
// SAFETY: OpenProcess is safe to call with valid parameters, and PROCESS_TERMINATE is a valid access right
let handle = unsafe { OpenProcess(PROCESS_TERMINATE, 0, pid) };
if handle == std::ptr::null_mut() {
return Err(io::Error::last_os_error());
}
let result = if signal == 0 {
Ok(())
} else {
// SAFETY: TerminateProcess is safe to call with a valid process handle obtained from OpenProcess
if unsafe { TerminateProcess(handle, 1) } != 0 {
Ok(())
} else {
Err(io::Error::last_os_error())
}
};
// SAFETY: CloseHandle is safe to call with a valid handle obtained from OpenProcess
unsafe { CloseHandle(handle) };
result
}
pub fn kill(ctx: &Ctx<'_>, pid: u32, signal: Opt<Value<'_>>) -> Result<bool> {
let signal = parse_signal(signal.0)?;
kill_process_raw(pid, signal)
.map(|_| true)
.or_else(|e| {
// Handle "Process Not Found" / "Existence Check" logic
// If signal is 0 (check existence) and we hit a specific error, return Ok(false).
#[cfg(unix)]
let is_not_found = e.raw_os_error() == Some(libc::ESRCH); // Error 3
#[cfg(windows)]
let is_not_found = true; // On Windows, any OpenProcess failure during check implies "not found" (or not accessible)
if signal == 0 && is_not_found {
Ok(false)
} else {
Err(e)
}
})
.or_throw(ctx)
}