@@ -98,6 +98,40 @@ extern "C" fn reload_handler(_sig: libc::c_int) {
9898 }
9999}
100100
101+ /// Build a `sigaction` portably. The struct's fields differ across Unix targets
102+ /// (Linux carries the obsolete `sa_restorer`, macOS/BSD do not), so we zero the
103+ /// struct and set only the fields common to all of them — never naming
104+ /// `sa_restorer`, which would not compile on macOS.
105+ #[ cfg( unix) ]
106+ unsafe fn make_sigaction ( handler : libc:: sighandler_t , flags : libc:: c_int ) -> libc:: sigaction {
107+ let mut sa: libc:: sigaction = unsafe { std:: mem:: zeroed ( ) } ;
108+ sa. sa_sigaction = handler;
109+ sa. sa_flags = flags;
110+ sa
111+ }
112+
113+ /// Create a pipe with both ends non-blocking + close-on-exec on platforms that
114+ /// lack Linux's atomic `pipe2(2)` (e.g. macOS). Returns 0 on success, -1 on
115+ /// failure (errno set by the failing call).
116+ #[ cfg( all( unix, not( target_os = "linux" ) ) ) ]
117+ unsafe fn make_nonblock_cloexec_pipe ( fds : * mut libc:: c_int ) -> libc:: c_int {
118+ if unsafe { libc:: pipe ( fds) } != 0 {
119+ return -1 ;
120+ }
121+ for i in 0 ..2 {
122+ let fd = unsafe { * fds. add ( i) } ;
123+ let fl = unsafe { libc:: fcntl ( fd, libc:: F_GETFL ) } ;
124+ if fl >= 0 {
125+ unsafe { libc:: fcntl ( fd, libc:: F_SETFL , fl | libc:: O_NONBLOCK ) } ;
126+ }
127+ let fdfl = unsafe { libc:: fcntl ( fd, libc:: F_GETFD ) } ;
128+ if fdfl >= 0 {
129+ unsafe { libc:: fcntl ( fd, libc:: F_SETFD , fdfl | libc:: FD_CLOEXEC ) } ;
130+ }
131+ }
132+ 0
133+ }
134+
101135// ─── Windows: SetConsoleCtrlHandler ──────────────────────────────────────────
102136
103137/// Ctrl event handler — Ctrl+C, Ctrl+Break, console close, system shutdown.
@@ -163,7 +197,10 @@ impl ShutdownHandle {
163197 #[ cfg( unix) ]
164198 {
165199 let mut fds = [ 0i32 ; 2 ] ;
200+ #[ cfg( target_os = "linux" ) ]
166201 let rc = unsafe { libc:: pipe2 ( fds. as_mut_ptr ( ) , libc:: O_NONBLOCK | libc:: O_CLOEXEC ) } ;
202+ #[ cfg( not( target_os = "linux" ) ) ]
203+ let rc = unsafe { make_nonblock_cloexec_pipe ( fds. as_mut_ptr ( ) ) } ;
167204 if rc != 0 {
168205 return Err ( anyhow:: anyhow!(
169206 "Failed to create shutdown pipe: {}" ,
@@ -177,17 +214,17 @@ impl ShutdownHandle {
177214 // SA_RESTART: slow syscalls (read/write) are restarted automatically.
178215 // poll(2) is NOT restarted on Linux and returns EINTR instead —
179216 // handled explicitly in the poll loop.
180- let sa_shutdown = libc :: sigaction {
181- sa_sigaction : shutdown_handler as * const ( ) as libc :: sighandler_t ,
182- sa_mask : unsafe { std :: mem :: zeroed ( ) } ,
183- sa_flags : libc:: SA_RESTART ,
184- sa_restorer : None ,
217+ let sa_shutdown = unsafe {
218+ make_sigaction (
219+ shutdown_handler as * const ( ) as libc :: sighandler_t ,
220+ libc:: SA_RESTART ,
221+ )
185222 } ;
186- let sa_reload = libc :: sigaction {
187- sa_sigaction : reload_handler as * const ( ) as libc :: sighandler_t ,
188- sa_mask : unsafe { std :: mem :: zeroed ( ) } ,
189- sa_flags : libc:: SA_RESTART ,
190- sa_restorer : None ,
223+ let sa_reload = unsafe {
224+ make_sigaction (
225+ reload_handler as * const ( ) as libc :: sighandler_t ,
226+ libc:: SA_RESTART ,
227+ )
191228 } ;
192229
193230 let registrations: & [ ( libc:: c_int , & libc:: sigaction ) ] = & [
@@ -321,12 +358,7 @@ impl Drop for ShutdownHandle {
321358 // Restore default signal dispositions so the process behaves normally
322359 // if it continues running after this handle is dropped.
323360 for & sig in & [ libc:: SIGTERM , libc:: SIGINT , libc:: SIGHUP ] {
324- let sa_dfl = libc:: sigaction {
325- sa_sigaction : libc:: SIG_DFL ,
326- sa_mask : unsafe { std:: mem:: zeroed ( ) } ,
327- sa_flags : 0 ,
328- sa_restorer : None ,
329- } ;
361+ let sa_dfl = unsafe { make_sigaction ( libc:: SIG_DFL , 0 ) } ;
330362 unsafe { libc:: sigaction ( sig, & sa_dfl, std:: ptr:: null_mut ( ) ) } ;
331363 }
332364
@@ -465,7 +497,21 @@ pub fn sd_notify(msg: &str) {
465497 } ;
466498
467499 unsafe {
500+ // SOCK_CLOEXEC is Linux-only as a socket-type flag; on macOS create the
501+ // socket then set FD_CLOEXEC via fcntl.
502+ #[ cfg( target_os = "linux" ) ]
468503 let sock = libc:: socket ( libc:: AF_UNIX , libc:: SOCK_DGRAM | libc:: SOCK_CLOEXEC , 0 ) ;
504+ #[ cfg( not( target_os = "linux" ) ) ]
505+ let sock = {
506+ let s = libc:: socket ( libc:: AF_UNIX , libc:: SOCK_DGRAM , 0 ) ;
507+ if s >= 0 {
508+ let fl = libc:: fcntl ( s, libc:: F_GETFD ) ;
509+ if fl >= 0 {
510+ libc:: fcntl ( s, libc:: F_SETFD , fl | libc:: FD_CLOEXEC ) ;
511+ }
512+ }
513+ s
514+ } ;
469515 if sock < 0 {
470516 return ;
471517 }
@@ -496,11 +542,18 @@ pub fn sd_notify(msg: &str) {
496542 + path_bytes. len ( )
497543 + if is_abstract { 0 } else { 1 } ) as libc:: socklen_t ;
498544
545+ // MSG_NOSIGNAL suppresses SIGPIPE on Linux; macOS has no such flag (it
546+ // uses the SO_NOSIGPIPE sockopt). For this best-effort sd_notify on a
547+ // DGRAM socket, sending with no flag is fine.
548+ #[ cfg( target_os = "linux" ) ]
549+ let send_flags: libc:: c_int = libc:: MSG_NOSIGNAL ;
550+ #[ cfg( not( target_os = "linux" ) ) ]
551+ let send_flags: libc:: c_int = 0 ;
499552 libc:: sendto (
500553 sock,
501554 msg. as_ptr ( ) as * const libc:: c_void ,
502555 msg. len ( ) ,
503- libc :: MSG_NOSIGNAL ,
556+ send_flags ,
504557 & addr as * const libc:: sockaddr_un as * const libc:: sockaddr ,
505558 addr_len,
506559 ) ;
0 commit comments