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 c_int
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 pub const SIGIO: c_int = sig!(Io, 29);
138 pub const SIGSYS: c_int = sig!(Sys, 31);
139}
140
141macro_rules! define_signal_enum {
142 (
143 $(#[$outer:meta])*
144 pub enum Signal {
145 $(
146 $(#[$inner:meta])*
147 $name:ident = $value:ident,
148 )*
149 }
150 ) => {
151 $(#[$outer])*
152 #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Hash)]
153 #[repr(i32)]
154 pub enum Signal {
155 $(
156 $(#[$inner])*
157 $name = signum::$value,
158 )*
159 }
160
161 impl Signal {
162 fn number(self) -> std::os::raw::c_int {
164 match self {
165 $(
166 Signal::$name => signum::$value,
167 )*
168 }
169 }
170
171 #[cfg(unix)]
173 fn from_number(number: std::os::raw::c_int) -> Option<Self> {
174 match number {
175 $(
176 signum::$value => Some(Signal::$name),
177 )*
178 _ => None,
179 }
180 }
181 }
182 }
183}
184
185define_signal_enum! {
186 pub enum Signal {
190 Hup = SIGHUP,
192 Int = SIGINT,
194 Quit = SIGQUIT,
196 Ill = SIGILL,
198 Trap = SIGTRAP,
200 #[doc(alias = "Iot")]
202 #[doc(alias = "Abrt")]
203 Abort = SIGABRT,
204 Bus = SIGBUS,
206 Fpe = SIGFPE,
208 Kill = SIGKILL,
210 Usr1 = SIGUSR1,
212 Segv = SIGSEGV,
214 Usr2 = SIGUSR2,
216 Pipe = SIGPIPE,
218 #[doc(alias = "Alrm")]
220 Alarm = SIGALRM,
221 Term = SIGTERM,
223 #[doc(alias = "Chld")]
225 Child = SIGCHLD,
226 Cont = SIGCONT,
228 Stop = SIGSTOP,
230 Tstp = SIGTSTP,
232 Ttin = SIGTTIN,
234 Ttou = SIGTTOU,
236 Urg = SIGURG,
238 Xcpu = SIGXCPU,
240 Xfsz = SIGXFSZ,
242 #[doc(alias = "Vtalrm")]
244 Vtalarm = SIGVTALRM,
245 Prof = SIGPROF,
247 Winch = SIGWINCH,
249 #[doc(alias = "Poll")]
251 Io = SIGIO,
252 #[doc(alias = "Unused")]
254 Sys = SIGSYS,
255 }
256}
257
258pub struct Signals {
262 notifier: sys::Notifier,
264
265 signal_ids: HashMap<Signal, SigId>,
267}
268
269impl Drop for Signals {
270 fn drop(&mut self) {
271 for signal in self.signal_ids.values() {
272 registry::unregister(*signal);
273 }
274 }
275}
276
277impl fmt::Debug for Signals {
278 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
279 struct RegisteredSignals<'a>(&'a HashMap<Signal, SigId>);
280
281 impl fmt::Debug for RegisteredSignals<'_> {
282 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
283 f.debug_set().entries(self.0.keys()).finish()
284 }
285 }
286
287 f.debug_struct("Signals")
288 .field("notifier", &self.notifier)
289 .field("signal_ids", &RegisteredSignals(&self.signal_ids))
290 .finish()
291 }
292}
293
294impl Signals {
295 pub fn new<B>(signals: impl IntoIterator<Item = B>) -> io::Result<Self>
297 where
298 B: Borrow<Signal>,
299 {
300 let mut this = Self {
301 notifier: sys::Notifier::new()?,
302 signal_ids: HashMap::new(),
303 };
304
305 this.add_signals(signals)?;
307
308 Ok(this)
309 }
310
311 pub fn add_signals<B>(&mut self, signals: impl IntoIterator<Item = B>) -> io::Result<()>
323 where
324 B: Borrow<Signal>,
325 {
326 for signal in signals {
327 let signal = signal.borrow();
328
329 if self.signal_ids.contains_key(signal) {
331 continue;
332 }
333
334 let closure = self.notifier.add_signal(*signal)?;
336
337 let id = unsafe {
338 registry::register(signal.number(), closure)?
340 };
341
342 self.signal_ids.insert(*signal, id);
344 }
345
346 Ok(())
347 }
348
349 pub fn remove_signals<B>(&mut self, signals: impl IntoIterator<Item = B>) -> io::Result<()>
355 where
356 B: Borrow<Signal>,
357 {
358 for signal in signals {
359 let signal = signal.borrow();
360
361 let id = match self.signal_ids.remove(signal) {
363 Some(id) => id,
364 None => continue,
365 };
366
367 self.notifier.remove_signal(*signal)?;
369
370 registry::unregister(id);
372 }
373
374 Ok(())
375 }
376}
377
378#[cfg(unix)]
379impl AsRawFd for Signals {
380 fn as_raw_fd(&self) -> RawFd {
381 self.notifier.as_raw_fd()
382 }
383}
384
385#[cfg(unix)]
386impl AsFd for Signals {
387 fn as_fd(&self) -> BorrowedFd<'_> {
388 self.notifier.as_fd()
389 }
390}
391
392impl Unpin for Signals {}
393
394impl Stream for Signals {
395 type Item = io::Result<Signal>;
396
397 #[inline]
398 fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
399 Pin::new(&mut &*self).poll_next(cx)
400 }
401
402 #[inline]
403 fn size_hint(&self) -> (usize, Option<usize>) {
404 (usize::MAX, None)
406 }
407}
408
409impl Stream for &Signals {
410 type Item = io::Result<Signal>;
411
412 fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
413 let signal = ready!(self.notifier.poll_next(cx))?;
414 Poll::Ready(Some(Ok(signal)))
415 }
416
417 #[inline]
418 fn size_hint(&self) -> (usize, Option<usize>) {
419 (usize::MAX, None)
421 }
422}