diff --git a/rust/chg/src/main.rs b/rust/chg/src/main.rs --- a/rust/chg/src/main.rs +++ b/rust/chg/src/main.rs @@ -10,6 +10,7 @@ extern crate tokio_hglib; use chg::{ChgClientExt, ChgUiHandler}; use chg::locator; +use chg::procutil; use futures::sync::oneshot; use std::env; use std::io; @@ -38,9 +39,16 @@ fn run() -> io::Result { client.attach_io(io::stdin(), io::stdout(), io::stderr()) }) .and_then(|client| { + let pid = client.server_spec().process_id.unwrap(); + let pgid = client.server_spec().process_group_id; + procutil::setup_signal_handler_once(pid, pgid)?; + Ok(client) + }) + .and_then(|client| { client.run_command_chg(handler, env::args_os().skip(1)) }) .map(|(_client, _handler, code)| { + procutil::restore_signal_handler_once()?; Ok(code) }) .or_else(|err| Ok(Err(err))) // pass back error to caller diff --git a/rust/chg/src/procutil.rs b/rust/chg/src/procutil.rs --- a/rust/chg/src/procutil.rs +++ b/rust/chg/src/procutil.rs @@ -5,14 +5,19 @@ //! Low-level utility for signal and process handling. -use libc::{self, c_int, size_t, ssize_t}; +use libc::{self, c_int, pid_t, size_t, ssize_t}; use std::io; use std::os::unix::io::RawFd; +use std::sync; #[link(name = "procutil", kind = "static")] extern "C" { // sendfds.c fn sendfds(sockfd: c_int, fds: *const c_int, fdlen: size_t) -> ssize_t; + + // sighandlers.c + fn setupsignalhandler(pid: pid_t, pgid: pid_t) -> c_int; + fn restoresignalhandler() -> c_int; } /// Returns the effective uid of the current process. @@ -41,3 +46,42 @@ pub fn send_raw_fds(sock_fd: RawFd, fds: } Ok(()) } + +static SETUP_SIGNAL_HANDLER: sync::Once = sync::Once::new(); +static RESTORE_SIGNAL_HANDLER: sync::Once = sync::Once::new(); + +/// Installs signal handlers to forward signals to the server. +/// +/// # Safety +/// +/// This touches global states, and thus synchronized as a one-time +/// initialization function. +pub fn setup_signal_handler_once(pid: u32, pgid: Option) -> io::Result<()> { + let pid_signed = pid as i32; + let pgid_signed = pgid.map(|n| n as i32).unwrap_or(0); + let mut r = 0; + SETUP_SIGNAL_HANDLER.call_once(|| { + r = unsafe { setupsignalhandler(pid_signed, pgid_signed) }; + }); + if r < 0 { + return Err(io::Error::last_os_error()); + } + Ok(()) +} + +/// Restores the original signal handlers. +/// +/// # Safety +/// +/// This touches global states, and thus synchronized as a one-time +/// initialization function. +pub fn restore_signal_handler_once() -> io::Result<()> { + let mut r = 0; + RESTORE_SIGNAL_HANDLER.call_once(|| { + r = unsafe { restoresignalhandler() }; + }); + if r < 0 { + return Err(io::Error::last_os_error()); + } + Ok(()) +}