extern crate signal_hook; extern crate nix; use std::process::{Command, Stdio}; use signal_hook::{consts, iterator::Signals}; use std::os::unix::process::CommandExt; use std::thread::spawn; use nix::{unistd, unistd::Pid, sys::termios::{tcgetattr, tcsetattr, SetArg}}; fn main() { // setup signal handlers let signals = Signals::new([ /* glibc says to block SIGCHLD but since I already have a backgroun thread * it seems like a perfect way to know when background jobs have stopped... */ consts::SIGINT, consts::SIGQUIT, //consts::SIGCHLD, consts::SIGTSTP, consts::SIGTTOU, consts::SIGTTIN, ]); if let Err(e) = signals { println!("couldn't spawn signal handler: {}", e); return } spawn(move || { for sig in signals.unwrap().forever() { println!("Received signal {:?}", sig); } }); let parent_pid = unistd::getpid(); let attr = tcgetattr(0).unwrap(); let term_owner = unistd::tcgetpgrp(0).unwrap(); let parent_pgid = unistd::getpgid(Some(parent_pid)).unwrap(); if parent_pgid != term_owner { nix::sys::signal::kill( term_owner, nix::sys::signal::Signal::SIGTTIN, ).expect("cant seize terminal"); } if parent_pid != parent_pgid { unistd::setpgid(parent_pid, parent_pid).unwrap() } unistd::tcsetpgrp(0, parent_pid).unwrap(); // kick off child process in its own PG let mut chldproc = Command::new("/bin/bash") .stdin(Stdio::inherit()) .stdout(Stdio::inherit()) .stderr(Stdio::inherit()) .process_group(0) .spawn() .unwrap(); let pid = chldproc.id(); unistd::setpgid( Pid::from_raw(pid as i32), Pid::from_raw(pid as i32), ).unwrap(); // give child terminal unistd::tcsetpgrp( 0, Pid::from_raw((chldproc.id() as i32).into()), ).unwrap(); // cant use i.wait() because it closes stdin loop { if let Ok(maybe) = chldproc.try_wait() { if let Some(_) = maybe { unistd::tcsetpgrp(0, parent_pid).unwrap(); tcsetattr(0, SetArg::TCSADRAIN, &attr).ok(); break; } } else { panic!() } } println!("finished successfully!"); }