atagen

monolith

public
Star0
Watch0

Commit 0f0db6ab8e50

tarball

atagen <boss@atagen.co>2026-10-19 10:44 UTC

1 file changed+16-6
signals: keep errno, handle SIGHUP and exit on a repeated stop

The handler's write could clobber the errno of the code it interrupted. SIGHUP killed the process without running cleanup, and a second SIGTERM during a hung shutdown wrote a byte nobody read, so only SIGKILL could end it.
Modified file: crates/monolith/src/signals.rs+16 additions−6 deletions
@@ -1,5 +1,6 @@
1-//! SIGINT and SIGTERM stop the event loop like `msg quit`, so the socket and
2-//! the X11 lock file are cleaned up when a session manager logs us out.
1+//! SIGINT, SIGTERM and SIGHUP stop the event loop like `msg quit`, so the
2+//! socket and the X11 lock file are cleaned up when a session manager logs us
3+//! out. A second one exits at once, for a shutdown that hangs.
34
45 use crate::state::State;
56 use smithay::reexports::calloop::generic::Generic;
@@ -7,16 +8,25 @@ use smithay::reexports::calloop::{EventLoop, Interest, Mode, PostAction};
78 use std::io::{ErrorKind, Read};
89 use std::os::fd::IntoRawFd;
910 use std::os::unix::net::UnixStream;
10-use std::sync::atomic::{AtomicI32, Ordering};
11+use std::sync::atomic::{AtomicI32, AtomicU32, Ordering};
1112
12-const STOPPING: [libc::c_int; 2] = [libc::SIGINT, libc::SIGTERM];
13+const STOPPING: [libc::c_int; 3] = [libc::SIGINT, libc::SIGTERM, libc::SIGHUP];
1314
1415 static WAKE: AtomicI32 = AtomicI32::new(-1);
16+static RECEIVED: AtomicU32 = AtomicU32::new(0);
1517
1618 extern "C" fn on_signal(sig: libc::c_int) {
1719 let byte = sig as u8;
18- // SAFETY: write is async-signal-safe and the buffer is a live local.
19- unsafe { libc::write(WAKE.load(Ordering::Relaxed), (&raw const byte).cast(), 1) };
20+ // SAFETY: _exit and write are async-signal-safe and the buffer is a live
21+ // local. The interrupted code may be reading errno, so it is put back.
22+ unsafe {
23+ if RECEIVED.fetch_add(1, Ordering::Relaxed) > 0 {
24+ libc::_exit(128 + sig);
25+ }
26+ let errno = *libc::__errno_location();
27+ libc::write(WAKE.load(Ordering::Relaxed), (&raw const byte).cast(), 1);
28+ *libc::__errno_location() = errno;
29+ }
2030 }
2131
2232 pub fn init(event_loop: &EventLoop<'static, State>) -> std::io::Result<()> {