]>
Commit | Line | Data |
---|---|---|
e420f6f9 WB |
1 | //! Fork helper. |
2 | //! | |
3 | //! Note that forking in rust can be dangerous. A fork must consider all mutexes to be in a broken | |
4 | //! state, and cannot rely on any of its reference life times, so we be careful what kind of data | |
5 | //! we continue to work with. | |
6 | ||
7 | use std::io; | |
8 | use std::os::raw::c_int; | |
9 | use std::os::unix::io::{FromRawFd, IntoRawFd}; | |
10 | use std::panic::UnwindSafe; | |
11 | use std::pin::Pin; | |
12 | use std::task::{Context, Poll}; | |
13 | ||
e420f6f9 | 14 | use futures::io::AsyncRead; |
61bfa355 | 15 | use nix::errno::Errno; |
e420f6f9 | 16 | |
c95be5f6 | 17 | use crate::syscall::SyscallStatus; |
e420f6f9 | 18 | use crate::tools::Fd; |
e420f6f9 WB |
19 | use crate::{libc_try, libc_wrap}; |
20 | ||
21 | pub async fn forking_syscall<F>(func: F) -> io::Result<SyscallStatus> | |
22 | where | |
275009ec | 23 | F: FnOnce() -> io::Result<SyscallStatus> + UnwindSafe, |
e420f6f9 WB |
24 | { |
25 | let mut fork = Fork::new(func)?; | |
61bfa355 | 26 | let result = fork.get_result().await?; |
e420f6f9 | 27 | fork.wait()?; |
61bfa355 | 28 | Ok(result) |
e420f6f9 WB |
29 | } |
30 | ||
31 | pub struct Fork { | |
32 | pid: Option<libc::pid_t>, | |
33 | // FIXME: abuse! tokio-fs is not updated to futures@0.3 yet, but a TcpStream does the same | |
34 | // thing as a file when it's already open anyway... | |
35 | out: crate::tools::GenericStream, | |
36 | } | |
37 | ||
38 | impl Drop for Fork { | |
39 | fn drop(&mut self) { | |
40 | if self.pid.is_some() { | |
41 | let _ = self.wait(); | |
42 | } | |
43 | } | |
44 | } | |
45 | ||
61bfa355 WB |
46 | #[repr(C, packed)] |
47 | struct Data { | |
48 | val: i64, | |
49 | error: i32, | |
275009ec | 50 | failure: i32, |
61bfa355 WB |
51 | } |
52 | ||
e420f6f9 WB |
53 | impl Fork { |
54 | pub fn new<F>(func: F) -> io::Result<Self> | |
55 | where | |
275009ec | 56 | F: FnOnce() -> io::Result<SyscallStatus> + UnwindSafe, |
e420f6f9 WB |
57 | { |
58 | let mut pipe: [c_int; 2] = [0, 0]; | |
59 | libc_try!(unsafe { libc::pipe2(pipe.as_mut_ptr(), libc::O_CLOEXEC | libc::O_NONBLOCK) }); | |
60 | let (pipe_r, pipe_w) = (Fd(pipe[0]), Fd(pipe[1])); | |
61 | ||
e420f6f9 WB |
62 | let pid = libc_try!(unsafe { libc::fork() }); |
63 | if pid == 0 { | |
64 | std::mem::drop(pipe_r); | |
65 | let mut pipe_w = unsafe { std::fs::File::from_raw_fd(pipe_w.into_raw_fd()) }; | |
66 | ||
67 | let _ = std::panic::catch_unwind(move || { | |
61bfa355 | 68 | let out = match func() { |
275009ec | 69 | Ok(SyscallStatus::Ok(val)) => Data { |
61bfa355 WB |
70 | val, |
71 | error: 0, | |
275009ec | 72 | failure: 0, |
e420f6f9 | 73 | }, |
275009ec | 74 | Ok(SyscallStatus::Err(error)) => Data { |
61bfa355 WB |
75 | val: -1, |
76 | error: error as _, | |
275009ec WB |
77 | failure: 0, |
78 | }, | |
79 | Err(err) => Data { | |
80 | val: -1, | |
81 | error: -1, | |
82 | failure: err.raw_os_error().unwrap_or(libc::EFAULT), | |
e420f6f9 | 83 | }, |
61bfa355 WB |
84 | }; |
85 | ||
86 | let slice = unsafe { | |
87 | std::slice::from_raw_parts( | |
88 | &out as *const Data as *const u8, | |
89 | std::mem::size_of::<Data>(), | |
90 | ) | |
91 | }; | |
e420f6f9 WB |
92 | |
93 | use std::io::Write; | |
61bfa355 | 94 | match pipe_w.write_all(slice) { |
e420f6f9 WB |
95 | Ok(()) => unsafe { libc::_exit(0) }, |
96 | Err(_) => unsafe { libc::_exit(1) }, | |
97 | } | |
98 | }); | |
99 | unsafe { | |
100 | libc::_exit(-1); | |
101 | } | |
102 | } | |
103 | ||
61bfa355 WB |
104 | let pipe_r = match crate::tools::GenericStream::from_fd(pipe_r) { |
105 | Ok(o) => o, | |
106 | Err(err) => return Err(io::Error::new(io::ErrorKind::Other, err.to_string())), | |
107 | }; | |
108 | ||
e420f6f9 WB |
109 | Ok(Self { |
110 | pid: Some(pid), | |
111 | out: pipe_r, | |
112 | }) | |
113 | } | |
114 | ||
115 | pub fn wait(&mut self) -> io::Result<()> { | |
116 | let my_pid = self.pid.take().unwrap(); | |
61bfa355 | 117 | let mut status: c_int = -1; |
e420f6f9 WB |
118 | |
119 | loop { | |
e420f6f9 WB |
120 | match libc_wrap!(unsafe { libc::waitpid(my_pid, &mut status, 0) }) { |
121 | Ok(pid) if pid == my_pid => break, | |
122 | Ok(_other) => continue, | |
123 | Err(ref err) if err.kind() == io::ErrorKind::Interrupted => continue, | |
124 | Err(other) => return Err(other), | |
125 | } | |
126 | } | |
127 | ||
61bfa355 | 128 | if status != 0 { |
275009ec WB |
129 | Err(io::Error::new( |
130 | io::ErrorKind::Other, | |
131 | "error in child process", | |
132 | )) | |
61bfa355 WB |
133 | } else { |
134 | Ok(()) | |
135 | } | |
e420f6f9 WB |
136 | } |
137 | ||
61bfa355 WB |
138 | pub async fn get_result(&mut self) -> io::Result<SyscallStatus> { |
139 | use futures::io::AsyncReadExt; | |
140 | ||
141 | let mut data: Data = unsafe { std::mem::zeroed() }; | |
142 | self.read_exact(unsafe { | |
143 | std::slice::from_raw_parts_mut( | |
144 | &mut data as *mut Data as *mut u8, | |
145 | std::mem::size_of::<Data>(), | |
146 | ) | |
275009ec WB |
147 | }) |
148 | .await?; | |
149 | if data.failure != 0 { | |
150 | Err(io::Error::from_raw_os_error(data.failure)) | |
151 | } else if data.error == 0 { | |
61bfa355 WB |
152 | Ok(SyscallStatus::Ok(data.val)) |
153 | } else { | |
154 | Ok(SyscallStatus::Err(data.error)) | |
155 | } | |
e420f6f9 WB |
156 | } |
157 | } | |
158 | ||
159 | // default impl will work | |
160 | impl AsyncRead for Fork { | |
161 | fn poll_read( | |
162 | self: Pin<&mut Self>, | |
163 | cx: &mut Context, | |
164 | buf: &mut [u8], | |
165 | ) -> Poll<io::Result<usize>> { | |
166 | unsafe { self.map_unchecked_mut(|this| &mut this.out) }.poll_read(cx, buf) | |
167 | } | |
168 | ||
169 | unsafe fn initializer(&self) -> futures::io::Initializer { | |
170 | self.out.initializer() | |
171 | } | |
172 | ||
173 | fn poll_read_vectored( | |
174 | self: Pin<&mut Self>, | |
175 | cx: &mut Context, | |
176 | bufs: &mut [futures::io::IoSliceMut], | |
177 | ) -> Poll<io::Result<usize>> { | |
178 | unsafe { self.map_unchecked_mut(|this| &mut this.out) }.poll_read_vectored(cx, bufs) | |
179 | } | |
180 | } |