1use thiserror::Error;
4use tokio::signal::unix;
5use tracing::{info, warn};
6
7use crate::errors::IoError;
8
9#[derive(Debug, Error)]
11#[non_exhaustive]
12pub enum SignalError {
13 #[error("Unable to register signal handler: {0}")]
15 Register(IoError),
16}
17
18fn register(kind: unix::SignalKind) -> Result<unix::Signal, SignalError> {
24 unix::signal(kind).map_err(|err| SignalError::Register(err.into()))
25}
26
27pub struct SignalStream {
29 sig_term: unix::Signal,
31 sig_int: unix::Signal,
33 sig_quit: unix::Signal,
35 sig_hup: unix::Signal,
37 sig_usr1: unix::Signal,
39 sig_usr2: unix::Signal,
41}
42
43#[derive(Clone, Copy, Debug, PartialEq, Eq)]
45#[non_exhaustive]
46pub enum Signal {
47 Terminate,
49 Interrupt,
51 Quit,
53 HangUp,
55 UserDefined1,
57 UserDefined2,
59}
60
61impl Signal {
62 #[must_use]
64 pub fn name(&self) -> &'static str {
65 match self {
66 Self::Terminate => "SIGTERM",
67 Self::Interrupt => "SIGINT",
68 Self::Quit => "SIGQUIT",
69 Self::HangUp => "SIGHUP",
70 Self::UserDefined1 => "SIGUSR1",
71 Self::UserDefined2 => "SIGUSR2",
72 }
73 }
74
75 #[must_use]
77 pub fn is_shutdown(&self) -> bool {
78 matches!(self, Self::Terminate | Self::Interrupt | Self::Quit)
79 }
80}
81
82impl SignalStream {
83 pub fn new() -> Result<Self, SignalError> {
91 let sig_term = register(unix::SignalKind::terminate())?;
93 let sig_int = register(unix::SignalKind::interrupt())?;
94 let sig_quit = register(unix::SignalKind::quit())?;
95 let sig_hup = register(unix::SignalKind::hangup())?;
96 let sig_usr1 = register(unix::SignalKind::user_defined1())?;
97 let sig_usr2 = register(unix::SignalKind::user_defined2())?;
98
99 Ok(Self {
100 sig_term,
101 sig_int,
102 sig_quit,
103 sig_hup,
104 sig_usr1,
105 sig_usr2,
106 })
107 }
108
109 pub async fn next(&mut self) -> Result<Signal, SignalError> {
115 macro_rules! sig_recv {
116 ($name:literal, $value:ident) => {
117 info!(kind = $name, "received signal");
118 return Ok(Signal::$value);
119 };
120 }
121 macro_rules! sig_restart {
122 ($name:literal, $sig:ident, $create:ident) => {
123 warn!(kind = $name, "signal handler exited, restarting");
124 self.$sig = register(unix::SignalKind::$create())?;
125 continue;
126 };
127 }
128 loop {
129 tokio::select! {
130 ret = self.sig_term.recv() => match ret {
131 Some(_) => { sig_recv!("SIGTERM", Terminate); }
132 None => { sig_restart!("SIGTERM", sig_term, terminate); }
133 },
134 ret = self.sig_int.recv() => match ret {
135 Some(_) => { sig_recv!("SIGINT", Interrupt); }
136 None => { sig_restart!("SIGINT", sig_int, interrupt); }
137 },
138 ret = self.sig_quit.recv() => match ret {
139 Some(_) => { sig_recv!("SIGQUIT", Quit); }
140 None => { sig_restart!("SIGQUIT", sig_quit, quit); }
141 },
142 ret = self.sig_hup.recv() => match ret {
143 Some(_) => { sig_recv!("SIGHUP", HangUp); }
144 None => { sig_restart!("SIGHUP", sig_hup, hangup); }
145 },
146 ret = self.sig_usr1.recv() => match ret {
147 Some(_) => { sig_recv!("SIGUSR1", UserDefined1); }
148 None => { sig_restart!("SIGUSR1", sig_usr1, user_defined1); }
149 },
150 ret = self.sig_usr2.recv() => match ret {
151 Some(_) => { sig_recv!("SIGUSR2", UserDefined2); }
152 None => { sig_restart!("SIGUSR2", sig_usr2, user_defined2); }
153 },
154 }
155 }
156 }
157}