fast_command/
command.rs

1use std::io;
2use std::ptr;
3use std::mem;
4use std::fs::File;
5use std::ffi::CString;
6use std::os::fd::{IntoRawFd, FromRawFd};
7
8#[derive(Clone, Debug)]
9pub struct Output {
10    pub status: i32,
11    pub stdout: String,
12    pub stderr: String
13}
14
15#[derive(Clone, Debug)]
16pub struct Command<'a> {
17    pub command: &'a str
18}
19
20impl<'a> Command<'a> {
21    #[inline(always)]
22    pub fn new(command: &'a str) -> Self {
23        Self { command }
24    }
25
26    pub fn create_pipe() -> io::Result::<(File, File)> {
27        let mut fds = [0; 2];
28        if unsafe { libc::pipe(fds.as_mut_ptr()) } != 0 {
29            return Err(io::Error::last_os_error())
30        }
31        let r = unsafe { File::from_raw_fd(fds[0]) };
32        let w = unsafe { File::from_raw_fd(fds[1]) };
33        Ok((r, w))
34    }
35
36    pub fn execute(&self) -> io::Result::<Output> {
37        let Self { command } = self;
38
39        let (mut stdout_reader, stdout_writer) = Self::create_pipe()?;
40        let (mut stderr_reader, stderr_writer) = Self::create_pipe()?;
41
42        let cmd = CString::new(command.as_bytes())?;
43        let args = [c"/bin/sh".as_ptr(), c"-c".as_ptr(), cmd.as_ptr(), ptr::null()];
44
45        let stdout_writer_fd = stdout_writer.into_raw_fd();
46        let stderr_writer_fd = stderr_writer.into_raw_fd();
47
48        let mut file_actions = unsafe { mem::zeroed() };
49        unsafe {
50            libc::posix_spawn_file_actions_init(&mut file_actions);
51            libc::posix_spawn_file_actions_adddup2(&mut file_actions, stdout_writer_fd, libc::STDOUT_FILENO);
52            libc::posix_spawn_file_actions_adddup2(&mut file_actions, stderr_writer_fd, libc::STDERR_FILENO);
53        }
54
55        let mut attr = unsafe { mem::zeroed() };
56        unsafe {
57            libc::posix_spawnattr_init(&mut attr);
58        }
59
60        let env = [c"PATH=/usr/bin:/bin".as_ptr(), ptr::null()];
61
62        let mut pid = 0;
63        let ret = unsafe {
64            libc::posix_spawn(
65                &mut pid,
66                c"/bin/sh".as_ptr(),
67                &file_actions,
68                &attr,
69                args.as_ptr() as *const *mut _,
70                env.as_ptr() as *const *mut _
71            )
72        };
73
74        if ret != 0 {
75            return Err(io::Error::last_os_error())
76        }
77
78        unsafe {
79            libc::close(stdout_writer_fd);
80            libc::close(stderr_writer_fd);
81        }
82
83        let stdout = io::read_to_string(&mut stdout_reader)?;
84        let stderr = io::read_to_string(&mut stderr_reader)?;
85
86        let mut status = 0;
87        unsafe {
88            libc::waitpid(pid, &mut status, 0);
89        }
90
91        unsafe {
92            libc::posix_spawn_file_actions_destroy(&mut file_actions);
93            libc::posix_spawnattr_destroy(&mut attr);
94        }
95
96        Ok(Output {status, stdout, stderr})
97    }
98}