pub const HELPER_ARG: &str = "--internal-jobctl-helper";
pub const STOPPED: i32 = libc::SIGUSR1;
pub const CONTINUE: i32 = libc::SIGUSR2;
pub fn helper_main(args: &[String]) -> ! {
use std::ffi::CString;
let Some(program) = args.first() else {
eprintln!("peekme: {HELPER_ARG} needs a command");
unsafe { libc::_exit(2) };
};
let c_args: Vec<CString> = args
.iter()
.map(|a| CString::new(a.as_str()).unwrap_or_default())
.collect();
let c_program = c_args[0].clone();
let mut argv: Vec<*const libc::c_char> = c_args.iter().map(|a| a.as_ptr()).collect();
argv.push(std::ptr::null());
unsafe {
libc::signal(libc::SIGTTOU, libc::SIG_IGN);
let mut set: libc::sigset_t = std::mem::zeroed();
libc::sigemptyset(&mut set);
libc::sigaddset(&mut set, CONTINUE);
libc::sigprocmask(libc::SIG_BLOCK, &set, std::ptr::null_mut());
let child = libc::fork();
if child < 0 {
eprintln!("peekme: fork failed");
libc::_exit(127);
}
if child == 0 {
libc::setpgid(0, 0);
libc::tcsetpgrp(0, libc::getpid());
libc::signal(libc::SIGTTOU, libc::SIG_DFL);
libc::sigprocmask(libc::SIG_UNBLOCK, &set, std::ptr::null_mut());
libc::execvp(c_program.as_ptr(), argv.as_ptr());
let err = std::io::Error::last_os_error();
eprintln!("peekme: could not start `{program}`: {err}");
libc::_exit(127);
}
libc::setpgid(child, child);
libc::tcsetpgrp(0, child);
loop {
let mut status = 0;
let r = libc::waitpid(child, &mut status, libc::WUNTRACED);
if r < 0 {
if std::io::Error::last_os_error().raw_os_error() == Some(libc::EINTR) {
continue;
}
libc::_exit(1);
}
if libc::WIFSTOPPED(status) {
libc::kill(libc::getppid(), STOPPED);
let mut sig = 0;
libc::sigwait(&set, &mut sig);
libc::tcsetpgrp(0, child);
libc::kill(-child, libc::SIGCONT);
continue;
}
if libc::WIFEXITED(status) {
libc::_exit(libc::WEXITSTATUS(status));
}
if libc::WIFSIGNALED(status) {
let sig = libc::WTERMSIG(status);
libc::signal(sig, libc::SIG_DFL);
libc::kill(libc::getpid(), sig);
libc::_exit(128 + sig);
}
}
}
}
pub fn suspend_self() {
let _ = crossterm::terminal::disable_raw_mode();
unsafe {
libc::raise(libc::SIGTSTP);
}
let _ = crossterm::terminal::enable_raw_mode();
}
pub fn continue_child(helper_pid: u32) {
unsafe {
libc::kill(helper_pid as libc::pid_t, CONTINUE);
}
}