fully interactive shell
* Background task implementation * Foreground an existing task implementation * Documentation for both * Refactors to signal handling, process pre-exec
This commit is contained in:
parent
69216db72a
commit
c127118465
8 changed files with 535 additions and 107 deletions
|
|
@ -80,7 +80,7 @@
|
|||
'returns true or false depending on if current dir is a dirty git repo'
|
||||
() (not (eq? (load-to-string git diff '--stat') "")))
|
||||
|
||||
(def git-status 'returns "(git:<branch>(!))" if dir is in a git repository'
|
||||
(def git-status 'returns "(git:<branch>{!,})" if dir is in a git repository'
|
||||
()
|
||||
(if (in-a-git-repo?)
|
||||
(concat
|
||||
|
|
|
|||
81
snippets/shell-sigttou-minimal-reproduction.rs
Normal file
81
snippets/shell-sigttou-minimal-reproduction.rs
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
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!");
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue