use std::io;
use std::ptr;
use std::mem;
use std::fs::File;
use std::ffi::CString;
use std::os::fd::{IntoRawFd, FromRawFd};
#[derive(Clone, Debug)]
pub struct Output {
pub status: i32,
pub stdout: String,
pub stderr: String
}
#[derive(Clone, Debug)]
pub struct Command<'a> {
pub command: &'a str
}
impl<'a> Command<'a> {
#[inline(always)]
pub fn new(command: &'a str) -> Self {
Self { command }
}
pub fn create_pipe() -> io::Result::<(File, File)> {
let mut fds = [0; 2];
if unsafe { libc::pipe(fds.as_mut_ptr()) } != 0 {
return Err(io::Error::last_os_error())
}
let r = unsafe { File::from_raw_fd(fds[0]) };
let w = unsafe { File::from_raw_fd(fds[1]) };
Ok((r, w))
}
pub fn execute(&self) -> io::Result::<Output> {
let Self { command } = self;
let (mut stdout_reader, stdout_writer) = Self::create_pipe()?;
let (mut stderr_reader, stderr_writer) = Self::create_pipe()?;
let cmd = CString::new(command.as_bytes())?;
let args = [c"/bin/sh".as_ptr(), c"-c".as_ptr(), cmd.as_ptr(), ptr::null()];
let stdout_writer_fd = stdout_writer.into_raw_fd();
let stderr_writer_fd = stderr_writer.into_raw_fd();
let mut file_actions = unsafe { mem::zeroed() };
unsafe {
libc::posix_spawn_file_actions_init(&mut file_actions);
libc::posix_spawn_file_actions_adddup2(&mut file_actions, stdout_writer_fd, libc::STDOUT_FILENO);
libc::posix_spawn_file_actions_adddup2(&mut file_actions, stderr_writer_fd, libc::STDERR_FILENO);
}
let mut attr = unsafe { mem::zeroed() };
unsafe {
libc::posix_spawnattr_init(&mut attr);
}
let env = [c"PATH=/usr/bin:/bin".as_ptr(), ptr::null()];
let mut pid = 0;
let ret = unsafe {
libc::posix_spawn(
&mut pid,
c"/bin/sh".as_ptr(),
&file_actions,
&attr,
args.as_ptr() as *const *mut _,
env.as_ptr() as *const *mut _
)
};
if ret != 0 {
return Err(io::Error::last_os_error())
}
unsafe {
libc::close(stdout_writer_fd);
libc::close(stderr_writer_fd);
}
let stdout = io::read_to_string(&mut stdout_reader)?;
let stderr = io::read_to_string(&mut stderr_reader)?;
let mut status = 0;
unsafe {
libc::waitpid(pid, &mut status, 0);
}
unsafe {
libc::posix_spawn_file_actions_destroy(&mut file_actions);
libc::posix_spawnattr_destroy(&mut attr);
}
Ok(Output {status, stdout, stderr})
}
}