* Background task implementation * Foreground an existing task implementation * Documentation for both * Refactors to signal handling, process pre-exec
81 lines
2.3 KiB
Rust
81 lines
2.3 KiB
Rust
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!");
|
|
}
|