1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
|
use std::{
ffi::{CString, OsStr},
io::{Error, ErrorKind, Result},
iter,
os::unix::{ffi::*, io::RawFd},
ptr,
};
use libc::{c_char, c_void};
use nix::{fcntl::OFlag, sys::wait, unistd};
use crate::util::ToIOResult;
#[derive(Clone, Debug)]
pub struct PreparedCommandBuilder {
program: CString,
args: Vec<CString>,
}
#[derive(Debug)]
pub struct PreparedCommand {
child: unistd::Pid,
pipew: RawFd,
}
impl Drop for PreparedCommand {
fn drop(&mut self) {
assert!(unistd::close(self.pipew).is_ok());
}
}
fn os2c<S: AsRef<OsStr>>(s: S) -> CString {
CString::new(s.as_ref().as_bytes()).unwrap()
}
impl PreparedCommandBuilder {
pub fn arg<S: AsRef<OsStr>>(&mut self, arg: S) -> &mut Self {
self.args.push(os2c(arg));
self
}
pub fn prepare(&mut self) -> Result<PreparedCommand> {
let exe_p = self.program.as_ptr();
let argv: Vec<*const c_char> = iter::once(exe_p)
.chain(self.args.iter().map(|arg| arg.as_ptr()))
.chain(iter::once(ptr::null()))
.collect();
let argv_p = argv.as_ptr();
let (piper, pipew) = unistd::pipe2(OFlag::O_CLOEXEC).to_io_result()?;
unsafe {
match unistd::fork().to_io_result()? {
unistd::ForkResult::Parent { child } => {
let cmd = PreparedCommand { child, pipew };
assert!(unistd::close(piper).is_ok());
return Ok(cmd);
}
unistd::ForkResult::Child => {}
}
// Child process - only async-signal-safe calls allowed
if libc::close(pipew) != 0 {
libc::_exit(126);
}
// Wait for run trigger
let mut buf = [0u8; 1];
if libc::read(piper, buf.as_mut_ptr() as *mut c_void, buf.len()) != 1 {
// PreparedCommand was dropped, or controlling process exited
libc::_exit(126);
}
libc::execvp(exe_p, argv_p);
// exec failed
libc::_exit(127);
}
}
}
impl PreparedCommand {
pub fn new<S: AsRef<OsStr>>(program: S) -> PreparedCommandBuilder {
PreparedCommandBuilder {
program: os2c(program),
args: Vec::new(),
}
}
pub fn run(self) -> Result<wait::WaitStatus> {
if unistd::write(self.pipew, &[0]).to_io_result()? != 1 {
return Err(Error::new(ErrorKind::Other, "command trigger write failed"));
}
wait::waitpid(Some(self.child), None).to_io_result()
}
}
|