use std::{
error::Error,
fmt::{self, Display, Formatter},
io,
};
use crate::{ProcessGroupId, ProcessId};
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[repr(transparent)]
pub struct Signal(libc::c_int);
impl Signal {
pub const HUP: Self = Self(libc::SIGHUP);
pub const INT: Self = Self(libc::SIGINT);
pub const QUIT: Self = Self(libc::SIGQUIT);
pub const KILL: Self = Self(libc::SIGKILL);
pub const ALRM: Self = Self(libc::SIGALRM);
pub const TERM: Self = Self(libc::SIGTERM);
pub const STOP: Self = Self(libc::SIGSTOP);
pub const CONT: Self = Self(libc::SIGCONT);
pub const USR1: Self = Self(libc::SIGUSR1);
pub const USR2: Self = Self(libc::SIGUSR2);
pub const TTIN: Self = Self(libc::SIGTTIN);
pub const TTOU: Self = Self(libc::SIGTTOU);
pub const WINCH: Self = Self(libc::SIGWINCH);
#[must_use]
pub const fn new(raw: libc::c_int) -> Option<Self> {
if raw > 0 { Some(Self(raw)) } else { None }
}
#[must_use]
pub const fn get(self) -> libc::c_int {
self.0
}
}
impl Display for Signal {
fn fmt(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
Display::fmt(&self.0, formatter)
}
}
impl TryFrom<libc::c_int> for Signal {
type Error = InvalidSignal;
fn try_from(raw: libc::c_int) -> Result<Self, Self::Error> {
Self::new(raw).ok_or(InvalidSignal(raw))
}
}
impl From<Signal> for libc::c_int {
fn from(signal: Signal) -> Self {
signal.get()
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct InvalidSignal(libc::c_int);
impl InvalidSignal {
#[must_use]
pub const fn raw(self) -> libc::c_int {
self.0
}
}
impl Display for InvalidSignal {
fn fmt(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
write!(formatter, "signal must be positive, got {}", self.0)
}
}
impl Error for InvalidSignal {}
pub fn signal_process(process: ProcessId, signal: Signal) -> io::Result<()> {
kill_retry(process.get(), signal)
}
pub fn signal_process_group(group: ProcessGroupId, signal: Signal) -> io::Result<()> {
kill_retry(-group.get(), signal)
}
fn kill_retry(target: libc::pid_t, signal: Signal) -> io::Result<()> {
loop {
if unsafe { libc::kill(target, signal.get()) } == 0 {
return Ok(());
}
let error = io::Error::last_os_error();
if error.kind() != io::ErrorKind::Interrupted {
return Err(error);
}
}
}
#[cfg(test)]
mod tests {
use super::Signal;
#[test]
fn signals_reject_probe_and_negative_values() {
assert_eq!(Signal::new(0), None);
assert_eq!(Signal::new(-1), None);
}
#[test]
fn portable_constants_are_positive() {
for signal in [
Signal::HUP,
Signal::INT,
Signal::QUIT,
Signal::KILL,
Signal::ALRM,
Signal::TERM,
Signal::STOP,
Signal::CONT,
Signal::USR1,
Signal::USR2,
Signal::TTIN,
Signal::TTOU,
Signal::WINCH,
] {
assert!(signal.get() > 0);
}
}
}