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}