libc_tools/
pipe.rs

1use crate::Close;
2use libc::c_int;
3use std::fmt::Display;
4
5pub enum PipeSide {
6    Read,
7    Write,
8}
9
10// the other side of the pipe need to closed when you need to use one side, or one process will be suspend
11#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
12pub struct Pipe {
13    fd: (c_int, c_int),
14}
15
16impl Pipe {
17    pub fn new(&fds: &[libc::c_int; 2]) -> Pipe {
18        Pipe {
19            fd: (fds[0], fds[1]),
20        }
21    }
22    pub fn pipe() -> Option<Pipe> {
23        let mut end = [0; 2];
24        match unsafe { libc::pipe(end.as_mut_ptr()) } {
25            libc::INT_MIN..=-1 => None,
26            _ => Some(Pipe::new(&end)),
27        }
28    }
29
30    pub fn pipe2(flag: i32) -> Option<Pipe> {
31        let mut end = [0; 2];
32        match unsafe { libc::pipe2(end.as_mut_ptr(), flag) } {
33            libc::INT_MIN..=-1 => None,
34            _ => Some(Pipe::new(&end)),
35        }
36    }
37
38    pub fn get(&self, side: PipeSide) -> c_int {
39        match side {
40            PipeSide::Read => self.fd.1,
41            PipeSide::Write => self.fd.0,
42        }
43    }
44}
45
46impl Drop for Pipe {
47    fn drop(&mut self) {
48        match Close::close(&[self.fd.0, self.fd.1]) {
49            Ok(_) => (),
50            Err(v) => eprintln!("{}", v),
51        }
52    }
53}
54
55impl Display for Pipe {
56    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
57        f.write_str(
58            std::format!(
59                "pipe: | for_read(fd[0]) {} <<= for_write(fd[1]) {} |",
60                self.fd.0,
61                self.fd.1,
62            )
63            .as_str(),
64        )
65    }
66}
67#[macro_export]
68macro_rules! create_pipe {
69    (1) => {{
70        let mut pipe = [libc::c_int; 2];
71        match unsafe {
72            pipe(pipe.as_mut_ptr())
73        } {
74            -1 => None,
75            _  => Some(pipe)
76        }
77    }};
78    ($n: expr) => {{
79        if $n <= 0 {
80            None
81        } else {
82            let mut pipes: [[libc::c_int; 2]; $n] = [[0; 2]; $n];
83            let mut success = true;
84            for i in 0..$n as usize {
85                unsafe {
86                    match libc::pipe(pipes[i].as_mut_ptr()) {
87                        0 => {}
88                        _ => {
89                            success = false;
90                            break;
91                        }
92                    }
93                };
94            }
95            if success {
96                Some(pipes)
97            } else {
98                None
99            }
100        }
101    }};
102}
103
104#[macro_export]
105macro_rules! create_pipe2 {
106    ($n: expr, $modes: expr) => {{
107        if $n != $modes.len() || $n == 0 {
108            None
109        } else {
110            let mut pipes: [[libc::c_int; 2]; $n] = [[0; 2]; $n];
111            let mut success = true;
112            for i in 0..$n as usize {
113                unsafe {
114                    match libc::pipe2(pipes[i].as_mut_ptr(), $modes[i]) {
115                        0 => {}
116                        _ => {
117                            success = false;
118                            break;
119                        }
120                    }
121                };
122            }
123            if success {
124                Some(pipes)
125            } else {
126                None
127            }
128        }
129    }};
130}
131
132#[cfg(test)]
133mod pipe {
134    use std::{error::Error, ffi::CString};
135
136    use libc::execl;
137
138    use crate::Close;
139
140    #[test]
141    fn test_execl() -> Result<(), Box<dyn Error>> {
142        let path = CString::new("/bin/sh")?;
143        let sh = CString::new("sh")?;
144        let exec = CString::new("-c")?;
145        let arg = CString::new("ls")?;
146        // unsafe { execl(path.as_ptr(), sh.as_ptr(), exec.as_ptr(), arg.as_ptr(), 0) };
147        Ok(())
148    }
149
150    #[test]
151    fn test_create_pipe_macro() {
152        let pipes1 = create_pipe!(1 + 1).unwrap();
153        let pipes2 = create_pipe!(1 & 1).unwrap();
154        assert!(pipes1.len() == 2);
155        assert!(pipes2.len() == 1);
156        let s = &pipes1.iter().flatten().map(|x| *x).collect::<Vec<i32>>()[..];
157        Close::close(s).unwrap();
158        let s1 = &pipes2.iter().flatten().map(|x| *x).collect::<Vec<i32>>()[..];
159        Close::close(s1).unwrap();
160    }
161}