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};
use libc::{getpid, pid_t};
use nix::sys::signal::{sigaction, SigAction, SigNum, SigSet, SaFlags};
use nix::sys::signal::{pthread_sigmask, SIG_UNBLOCK, SigHandler, raise};
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: SigNum) {
unsafe {
if getpid() != (*exec_command_line).pid {
raise(sig).expect("reraising signal");
}
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: &[SigNum], 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).unwrap();
}
}
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(SIG_UNBLOCK, Some(&sigset), None).unwrap();
}
res
}
}