1#![doc(
53 html_favicon_url = "https://raw.githubusercontent.com/smol-rs/smol/master/assets/images/logo_fullsize_transparent.png"
54)]
55#![doc(
56 html_logo_url = "https://raw.githubusercontent.com/smol-rs/smol/master/assets/images/logo_fullsize_transparent.png"
57)]
58
59cfg_if::cfg_if! {
60 if #[cfg(windows)] {
61 mod channel;
62 use channel as sys;
63 } else {
64 mod pipe;
65 use pipe as sys;
66 }
67}
68
69cfg_if::cfg_if! {
70 if #[cfg(unix)] {
71 use signal_hook_registry as registry;
72 } else if #[cfg(windows)] {
73 mod windows_registry;
74 use windows_registry as registry;
75 }
76}
77
78use futures_core::ready;
79use futures_core::stream::Stream;
80use registry::SigId;
81
82use std::borrow::Borrow;
83use std::collections::HashMap;
84use std::fmt;
85use std::io;
86use std::pin::Pin;
87use std::task::{Context, Poll};
88
89#[cfg(unix)]
90use std::os::unix::io::{AsFd, AsRawFd, BorrowedFd, RawFd};
91
92mod signum {
93 pub(crate) use std::os::raw::c_int;
94
95 macro_rules! sig {
96 ($rustix_name:ident, $raw_value:literal) => {{
97 #[cfg(unix)]
98 {
99 rustix::process::Signal::$rustix_name.as_raw()
100 }
101
102 #[cfg(windows)]
103 {
104 $raw_value
105 }
106 }};
107 }
108
109 pub const SIGHUP: c_int = sig!(HUP, 1);
111 pub const SIGINT: c_int = sig!(INT, 2);
112 pub const SIGQUIT: c_int = sig!(QUIT, 3);
113 pub const SIGILL: c_int = sig!(ILL, 4);
114 pub const SIGTRAP: c_int = sig!(TRAP, 5);
115 pub const SIGABRT: c_int = sig!(ABORT, 6);
116 pub const SIGFPE: c_int = sig!(FPE, 8);
117 pub const SIGKILL: c_int = sig!(KILL, 9);
118 pub const SIGSEGV: c_int = sig!(SEGV, 11);
119 pub const SIGPIPE: c_int = sig!(PIPE, 13);
120 pub const SIGALRM: c_int = sig!(ALARM, 14);
121 pub const SIGTERM: c_int = sig!(TERM, 15);
122 pub const SIGTTIN: c_int = sig!(TTIN, 21);
123 pub const SIGTTOU: c_int = sig!(TTOU, 22);
124 pub const SIGXCPU: c_int = sig!(XCPU, 24);
125 pub const SIGXFSZ: c_int = sig!(XFSZ, 25);
126 pub const SIGVTALRM: c_int = sig!(VTALARM, 26);
127 pub const SIGPROF: c_int = sig!(PROF, 27);
128 pub const SIGWINCH: c_int = sig!(WINCH, 28);
129 pub const SIGCHLD: c_int = sig!(CHILD, 17);
130 pub const SIGBUS: c_int = sig!(BUS, 7);
131 pub const SIGUSR1: c_int = sig!(USR1, 10);
132 pub const SIGUSR2: c_int = sig!(USR2, 12);
133 pub const SIGCONT: c_int = sig!(CONT, 18);
134 pub const SIGSTOP: c_int = sig!(STOP, 19);
135 pub const SIGTSTP: c_int = sig!(TSTP, 20);
136 pub const SIGURG: c_int = sig!(URG, 23);
137 #[cfg(not(target_os = "haiku"))]
138 pub const SIGIO: c_int = sig!(IO, 29);
139 pub const SIGSYS: c_int = sig!(SYS, 31);
140}
141
142macro_rules! define_signal_enum {
143 (
144 $(#[$outer:meta])*
145 pub enum Signal {
146 $(
147 $(#[cfg($($inner_cfg:tt)*)])?
148 $(#[doc $($inner_doc:tt)*])*
149 $name:ident = $value:ident,
150 )*
151 }
152 ) => {
153 $(#[$outer])*
154 #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Hash)]
155 #[repr(i32)]
156 pub enum Signal {
157 $(
158 $(#[cfg($($inner_cfg)*)])?
159 $(#[doc $($inner_doc)*])*
160 $name = signum::$value,
161 )*
162 }
163
164 impl Signal {
165 fn number(self) -> std::os::raw::c_int {
167 match self {
168 $(
169 $(#[cfg($($inner_cfg)*)])?
170 Signal::$name => signum::$value,
171 )*
172 }
173 }
174
175 #[cfg(unix)]
177 fn from_number(number: std::os::raw::c_int) -> Option<Self> {
178 match number {
179 $(
180 $(#[cfg($($inner_cfg)*)])?
181 signum::$value => Some(Signal::$name),
182 )*
183 _ => None,
184 }
185 }
186 }
187 }
188}
189
190define_signal_enum! {
191 pub enum Signal {
195 Hup = SIGHUP,
197 Int = SIGINT,
199 Quit = SIGQUIT,
201 Ill = SIGILL,
203 Trap = SIGTRAP,
205 #[doc(alias = "Iot")]
207 #[doc(alias = "Abrt")]
208 Abort = SIGABRT,
209 Bus = SIGBUS,
211 Fpe = SIGFPE,
213 Kill = SIGKILL,
215 Usr1 = SIGUSR1,
217 Segv = SIGSEGV,
219 Usr2 = SIGUSR2,
221 Pipe = SIGPIPE,
223 #[doc(alias = "Alrm")]
225 Alarm = SIGALRM,
226 Term = SIGTERM,
228 #[doc(alias = "Chld")]
230 Child = SIGCHLD,
231 Cont = SIGCONT,
233 Stop = SIGSTOP,
235 Tstp = SIGTSTP,
237 Ttin = SIGTTIN,
239 Ttou = SIGTTOU,
241 Urg = SIGURG,
243 Xcpu = SIGXCPU,
245 Xfsz = SIGXFSZ,
247 #[doc(alias = "Vtalrm")]
249 Vtalarm = SIGVTALRM,
250 Prof = SIGPROF,
252 Winch = SIGWINCH,
254 #[cfg(not(target_os = "haiku"))]
255 #[doc(alias = "Poll")]
257 Io = SIGIO,
258 #[doc(alias = "Unused")]
260 Sys = SIGSYS,
261 }
262}
263
264pub struct Signals {
268 notifier: sys::Notifier,
270
271 signal_ids: HashMap<Signal, SigId>,
273}
274
275impl Drop for Signals {
276 fn drop(&mut self) {
277 for signal in self.signal_ids.values() {
278 registry::unregister(*signal);
279 }
280 }
281}
282
283impl fmt::Debug for Signals {
284 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
285 struct RegisteredSignals<'a>(&'a HashMap<Signal, SigId>);
286
287 impl fmt::Debug for RegisteredSignals<'_> {
288 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
289 f.debug_set().entries(self.0.keys()).finish()
290 }
291 }
292
293 f.debug_struct("Signals")
294 .field("notifier", &self.notifier)
295 .field("signal_ids", &RegisteredSignals(&self.signal_ids))
296 .finish()
297 }
298}
299
300impl Signals {
301 pub fn new<B>(signals: impl IntoIterator<Item = B>) -> io::Result<Self>
303 where
304 B: Borrow<Signal>,
305 {
306 let mut this = Self {
307 notifier: sys::Notifier::new()?,
308 signal_ids: HashMap::new(),
309 };
310
311 this.add_signals(signals)?;
313
314 Ok(this)
315 }
316
317 pub fn add_signals<B>(&mut self, signals: impl IntoIterator<Item = B>) -> io::Result<()>
329 where
330 B: Borrow<Signal>,
331 {
332 for signal in signals {
333 let signal = signal.borrow();
334
335 if self.signal_ids.contains_key(signal) {
337 continue;
338 }
339
340 let closure = self.notifier.add_signal(*signal)?;
342
343 let id = unsafe {
344 registry::register(signal.number(), closure)?
346 };
347
348 self.signal_ids.insert(*signal, id);
350 }
351
352 Ok(())
353 }
354
355 pub fn remove_signals<B>(&mut self, signals: impl IntoIterator<Item = B>) -> io::Result<()>
361 where
362 B: Borrow<Signal>,
363 {
364 for signal in signals {
365 let signal = signal.borrow();
366
367 let id = match self.signal_ids.remove(signal) {
369 Some(id) => id,
370 None => continue,
371 };
372
373 self.notifier.remove_signal(*signal)?;
375
376 registry::unregister(id);
378 }
379
380 Ok(())
381 }
382}
383
384#[cfg(unix)]
385impl AsRawFd for Signals {
386 fn as_raw_fd(&self) -> RawFd {
387 self.notifier.as_raw_fd()
388 }
389}
390
391#[cfg(unix)]
392impl AsFd for Signals {
393 fn as_fd(&self) -> BorrowedFd<'_> {
394 self.notifier.as_fd()
395 }
396}
397
398impl Unpin for Signals {}
399
400impl Stream for Signals {
401 type Item = io::Result<Signal>;
402
403 #[inline]
404 fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
405 Pin::new(&mut &*self).poll_next(cx)
406 }
407
408 #[inline]
409 fn size_hint(&self) -> (usize, Option<usize>) {
410 (usize::MAX, None)
412 }
413}
414
415impl Stream for &Signals {
416 type Item = io::Result<Signal>;
417
418 fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
419 let signal = ready!(self.notifier.poll_next(cx))?;
420 Poll::Ready(Some(Ok(signal)))
421 }
422
423 #[inline]
424 fn size_hint(&self) -> (usize, Option<usize>) {
425 (usize::MAX, None)
427 }
428}