use std::io::Write;
use std::mem::{forget, transmute};
use std::ptr::{null};
use std::ffi::CString;
use std::env::{current_exe, args_os, vars_os};
use nix;
use libc::{execve, c_char, pid_t, getpid, c_int};
use nix::sys::signal::{sigaction, SigAction, Signal, SigSet, SaFlags};
use nix::sys::signal::{pthread_sigmask, SigmaskHow, SigHandler};
use ffi::{ToCString};
static mut EXEC_COMMAND_LINE: *const ExecCommandLine =
0usize as *const ExecCommandLine;
#[allow(unused)]
struct ExecCommandLine {
program: CString,
args: Vec<CString>,
c_args: Vec<*const c_char>,
env: Vec<CString>,
c_env: Vec<*const c_char>,
pid: pid_t,
}
pub fn set_command_line<P, Ai, A, Ek, Ev, E>(program: P, args: A, environ: E)
where P: ToCString,
Ai: ToCString,
A: IntoIterator<Item=Ai>,
Ek: ToCString,
Ev: ToCString,
E: IntoIterator<Item=(Ek, Ev)>,
{
let args = args.into_iter().map(|x| x.to_cstring()).collect::<Vec<_>>();
let mut c_args = args.iter().map(|x| x.as_ptr()).collect::<Vec<_>>();
c_args.push(null());
let env = environ.into_iter().map(|(k, v)| {
let mut pair = Vec::new();
pair.write(k.as_bytes()).unwrap();
pair.push(b'=');
pair.write(v.as_bytes()).unwrap();
CString::new(pair).unwrap()
}).collect::<Vec<_>>();
let mut c_env = env.iter().map(|x| x.as_ptr()).collect::<Vec<_>>();
c_env.push(null());
unsafe {
if EXEC_COMMAND_LINE != null() {
transmute::<_, Box<ExecCommandLine>>(EXEC_COMMAND_LINE);
}
let new = Box::new(ExecCommandLine {
program: program.to_cstring(),
args: args,
c_args: c_args,
env: env,
c_env: c_env,
pid: getpid(),
});
EXEC_COMMAND_LINE = &*new;
forget(new);
}
}
extern "C" fn exec_handler(sig:c_int) {
unsafe {
if getpid() != (*EXEC_COMMAND_LINE).pid {
panic!("Early signal {:?} after fork", sig);
} else {
let err = execve((*EXEC_COMMAND_LINE).program.as_ptr(),
(*EXEC_COMMAND_LINE).c_args.as_ptr(),
(*EXEC_COMMAND_LINE).c_env.as_ptr());
panic!("Couldn't exec on signal {}, err code {}", sig, err);
}
}
}
pub fn set_handler(signals: &[Signal], avoid_race_condition: bool)
-> nix::Result<()>
{
unsafe {
if EXEC_COMMAND_LINE == null() {
set_command_line(current_exe().unwrap(), args_os(), vars_os());
}
let mut sigset = SigSet::empty();
if avoid_race_condition {
for &sig in signals {
sigset.add(sig);
}
}
let mut res = Ok(());
for &sig in signals {
res = res.and_then(|()| {
try!(sigaction(sig, &SigAction::new(
SigHandler::Handler(exec_handler),
SaFlags::empty(), sigset)));
Ok(())
});
}
if avoid_race_condition && res.is_ok() {
pthread_sigmask(SigmaskHow::SIG_UNBLOCK, Some(&sigset), None).unwrap();
}
res
}
}