1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212
//! Types for cross-platform and cross-purpose handling of subprocess signals.
use std::str::FromStr;
#[cfg(unix)]
use command_group::Signal as NixSignal;
use crate::error::SignalParseError;
use super::source::MainSignal;
/// A notification sent to a subprocess.
///
/// On Windows, only some signals are supported, as described. Others will be ignored.
///
/// On Unix, there are several "first-class" signals which have their own variants, and a generic
/// [`Custom`][SubSignal::Custom] variant which can be used to send arbitrary signals.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum SubSignal {
/// Indicate that the terminal is disconnected.
///
/// On Unix, this is `SIGHUP`. On Windows, this is ignored for now but may be supported in the
/// future (see [#219](https://github.com/watchexec/watchexec/issues/219)).
///
/// Despite its nominal purpose, on Unix this signal is often used to reload configuration files.
Hangup,
/// Indicate to the kernel that the process should stop.
///
/// On Unix, this is `SIGKILL`. On Windows, this is `TerminateProcess`.
///
/// This signal is not handled by the process, but directly by the kernel, and thus cannot be
/// intercepted. Subprocesses may exit in inconsistent states.
ForceStop,
/// Indicate that the process should stop.
///
/// On Unix, this is `SIGINT`. On Windows, this is ignored for now but may be supported in the
/// future (see [#219](https://github.com/watchexec/watchexec/issues/219)).
///
/// This signal generally indicates an action taken by the user, so it may be handled
/// differently than a termination.
Interrupt,
/// Indicate that the process is to stop, the kernel will then dump its core.
///
/// On Unix, this is `SIGQUIT`. On Windows, it is ignored.
///
/// This is rarely used.
Quit,
/// Indicate that the process should stop.
///
/// On Unix, this is `SIGTERM`. On Windows, this is ignored for now but may be supported in the
/// future (see [#219](https://github.com/watchexec/watchexec/issues/219)).
///
/// On Unix, this signal generally indicates an action taken by the system, so it may be handled
/// differently than an interruption.
Terminate,
/// Indicate an application-defined behaviour should happen.
///
/// On Unix, this is `SIGUSR1`. On Windows, it is ignored.
///
/// This signal is generally used to start debugging.
User1,
/// Indicate an application-defined behaviour should happen.
///
/// On Unix, this is `SIGUSR2`. On Windows, it is ignored.
///
/// This signal is generally used to reload configuration.
User2,
/// Indicate using a custom signal.
///
/// Internally, this is converted to a [`nix::Signal`](https://docs.rs/nix/*/nix/sys/signal/enum.Signal.html)
/// but for portability this variant is a raw `i32`.
///
/// Invalid signals on the current platform will be ignored. Does nothing on Windows.
///
/// # Examples
///
/// ```
/// # // we don't have a direct nix dependency, so we fake it... rather horribly
/// # mod nix { pub mod sys { pub mod signal {
/// # #[cfg(unix)] pub use command_group::Signal;
/// # #[cfg(not(unix))] #[repr(i32)] pub enum Signal { SIGABRT = 6 }
/// # } } }
/// use watchexec::signal::process::SubSignal;
/// use nix::sys::signal::Signal;
/// assert_eq!(SubSignal::Custom(6), SubSignal::from(Signal::SIGABRT as i32));
/// ```
///
/// On Unix the [`from_nix`][SubSignal::from_nix] method should be preferred if converting from
/// Nix's `Signal` type:
///
/// ```
/// # #[cfg(unix)]
/// # {
/// # // we don't have a direct nix dependency, so we fake it... rather horribly
/// # mod nix { pub mod sys { pub mod signal { pub use command_group::Signal; } } }
/// use watchexec::signal::process::SubSignal;
/// use nix::sys::signal::Signal;
/// assert_eq!(SubSignal::Custom(6), SubSignal::from_nix(Signal::SIGABRT));
/// # }
/// ```
Custom(i32),
}
impl SubSignal {
/// Converts to a [`nix::Signal`][command_group::Signal] if possible.
///
/// This will return `None` if the signal is not supported on the current platform (only for
/// [`Custom`][SubSignal::Custom], as the first-class ones are always supported).
#[cfg(unix)]
pub fn to_nix(self) -> Option<NixSignal> {
match self {
Self::Hangup => Some(NixSignal::SIGHUP),
Self::ForceStop => Some(NixSignal::SIGKILL),
Self::Interrupt => Some(NixSignal::SIGINT),
Self::Quit => Some(NixSignal::SIGQUIT),
Self::Terminate => Some(NixSignal::SIGTERM),
Self::User1 => Some(NixSignal::SIGUSR1),
Self::User2 => Some(NixSignal::SIGUSR2),
Self::Custom(sig) => NixSignal::try_from(sig).ok(),
}
}
/// Converts from a [`nix::Signal`][command_group::Signal].
#[cfg(unix)]
pub fn from_nix(sig: NixSignal) -> Self {
match sig {
NixSignal::SIGHUP => Self::Hangup,
NixSignal::SIGKILL => Self::ForceStop,
NixSignal::SIGINT => Self::Interrupt,
NixSignal::SIGQUIT => Self::Quit,
NixSignal::SIGTERM => Self::Terminate,
NixSignal::SIGUSR1 => Self::User1,
NixSignal::SIGUSR2 => Self::User2,
sig => Self::Custom(sig as _),
}
}
}
impl From<MainSignal> for SubSignal {
fn from(main: MainSignal) -> Self {
match main {
MainSignal::Hangup => Self::Hangup,
MainSignal::Interrupt => Self::Interrupt,
MainSignal::Quit => Self::Quit,
MainSignal::Terminate => Self::Terminate,
MainSignal::User1 => Self::User1,
MainSignal::User2 => Self::User2,
}
}
}
impl From<i32> for SubSignal {
/// Converts from a raw signal number.
///
/// This uses hardcoded numbers for the first-class signals.
fn from(raw: i32) -> Self {
match raw {
1 => Self::Hangup,
2 => Self::Interrupt,
3 => Self::Quit,
9 => Self::ForceStop,
10 => Self::User1,
12 => Self::User2,
15 => Self::Terminate,
_ => Self::Custom(raw),
}
}
}
impl FromStr for SubSignal {
type Err = SignalParseError;
#[cfg(unix)]
fn from_str(s: &str) -> Result<Self, Self::Err> {
if let Ok(sig) = i32::from_str(s) {
if let Ok(sig) = NixSignal::try_from(sig) {
return Ok(Self::from_nix(sig));
}
}
if let Ok(sig) = NixSignal::from_str(&s.to_ascii_uppercase())
.or_else(|_| NixSignal::from_str(&format!("SIG{}", s.to_ascii_uppercase())))
{
return Ok(Self::from_nix(sig));
}
Err(SignalParseError::new(s, "unsupported signal"))
}
#[cfg(windows)]
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s.to_ascii_uppercase().as_str() {
"CTRL-CLOSE" | "CTRL+CLOSE" | "CLOSE" => Ok(Self::Hangup),
"CTRL-BREAK" | "CTRL+BREAK" | "BREAK" => Ok(Self::Terminate),
"CTRL-C" | "CTRL+C" | "C" => Ok(Self::Interrupt),
"KILL" | "SIGKILL" | "FORCE-STOP" | "STOP" => Ok(Self::ForceStop),
_ => Err(SignalParseError::new(s, "unknown control name")),
}
}
#[cfg(not(any(unix, windows)))]
fn from_str(s: &str) -> Result<Self, Self::Err> {
Err(SignalParseError::new(s, "no signals supported"))
}
}