150 lines
4.3 KiB
Rust
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)
|
|
}
|