use rquickjs::{prelude::Opt, Ctx, Exception, Result, Value};
use crate::utils::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);
#[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<()> {
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};
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 {
if unsafe { TerminateProcess(handle, 1) } != 0 {
Ok(())
} else {
Err(io::Error::last_os_error())
}
};
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| {
#[cfg(unix)]
let is_not_found = e.raw_os_error() == Some(libc::ESRCH);
#[cfg(windows)]
let is_not_found = true;
if signal == 0 && is_not_found {
Ok(false)
} else {
Err(e)
}
})
.or_throw(ctx)
}