]>
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 | |
e78aca9d WB |
4 | //! state, and cannot rely on any of its reference life times, so we must be careful what kind of |
5 | //! data we continue to work with. | |
e420f6f9 | 6 | |
edf4f4b9 | 7 | use std::convert::TryInto; |
e420f6f9 WB |
8 | use std::io; |
9 | use std::os::raw::c_int; | |
10 | use std::os::unix::io::{FromRawFd, IntoRawFd}; | |
11 | use std::panic::UnwindSafe; | |
e420f6f9 | 12 | |
5bd0c562 WB |
13 | use tokio::io::AsyncReadExt; |
14 | ||
8dd26985 | 15 | use crate::io::pipe::{self, Pipe}; |
c95be5f6 | 16 | use crate::syscall::SyscallStatus; |
e420f6f9 WB |
17 | |
18 | pub async fn forking_syscall<F>(func: F) -> io::Result<SyscallStatus> | |
19 | where | |
275009ec | 20 | F: FnOnce() -> io::Result<SyscallStatus> + UnwindSafe, |
e420f6f9 WB |
21 | { |
22 | let mut fork = Fork::new(func)?; | |
61bfa355 | 23 | let result = fork.get_result().await?; |
e420f6f9 | 24 | fork.wait()?; |
61bfa355 | 25 | Ok(result) |
e420f6f9 WB |
26 | } |
27 | ||
28 | pub struct Fork { | |
29 | pid: Option<libc::pid_t>, | |
30 | // FIXME: abuse! tokio-fs is not updated to futures@0.3 yet, but a TcpStream does the same | |
31 | // thing as a file when it's already open anyway... | |
9aa2a15a | 32 | out: Pipe<pipe::Read>, |
e420f6f9 WB |
33 | } |
34 | ||
35 | impl Drop for Fork { | |
36 | fn drop(&mut self) { | |
37 | if self.pid.is_some() { | |
38 | let _ = self.wait(); | |
39 | } | |
40 | } | |
41 | } | |
42 | ||
61bfa355 WB |
43 | #[repr(C, packed)] |
44 | struct Data { | |
45 | val: i64, | |
46 | error: i32, | |
275009ec | 47 | failure: i32, |
61bfa355 WB |
48 | } |
49 | ||
e420f6f9 WB |
50 | impl Fork { |
51 | pub fn new<F>(func: F) -> io::Result<Self> | |
52 | where | |
275009ec | 53 | F: FnOnce() -> io::Result<SyscallStatus> + UnwindSafe, |
e420f6f9 | 54 | { |
edf4f4b9 | 55 | let (pipe_r, pipe_w) = pipe::pipe_fds()?; |
e420f6f9 | 56 | |
7ca1a14c | 57 | let pid = c_try!(unsafe { libc::fork() }); |
e420f6f9 | 58 | if pid == 0 { |
9aa2a15a | 59 | drop(pipe_r); |
caeb9675 | 60 | let pipe_w = pipe_w.into_fd(); |
e420f6f9 | 61 | let _ = std::panic::catch_unwind(move || { |
caeb9675 | 62 | crate::tools::set_fd_nonblocking(&pipe_w, false).unwrap(); |
1282264a | 63 | let mut pipe_w = unsafe { std::fs::File::from_raw_fd(pipe_w.into_raw_fd()) }; |
61bfa355 | 64 | let out = match func() { |
275009ec | 65 | Ok(SyscallStatus::Ok(val)) => Data { |
61bfa355 WB |
66 | val, |
67 | error: 0, | |
275009ec | 68 | failure: 0, |
e420f6f9 | 69 | }, |
275009ec | 70 | Ok(SyscallStatus::Err(error)) => Data { |
61bfa355 WB |
71 | val: -1, |
72 | error: error as _, | |
275009ec WB |
73 | failure: 0, |
74 | }, | |
75 | Err(err) => Data { | |
76 | val: -1, | |
77 | error: -1, | |
78 | failure: err.raw_os_error().unwrap_or(libc::EFAULT), | |
e420f6f9 | 79 | }, |
61bfa355 WB |
80 | }; |
81 | ||
82 | let slice = unsafe { | |
83 | std::slice::from_raw_parts( | |
84 | &out as *const Data as *const u8, | |
85 | std::mem::size_of::<Data>(), | |
86 | ) | |
87 | }; | |
e420f6f9 WB |
88 | |
89 | use std::io::Write; | |
61bfa355 | 90 | match pipe_w.write_all(slice) { |
e420f6f9 WB |
91 | Ok(()) => unsafe { libc::_exit(0) }, |
92 | Err(_) => unsafe { libc::_exit(1) }, | |
93 | } | |
94 | }); | |
95 | unsafe { | |
96 | libc::_exit(-1); | |
97 | } | |
98 | } | |
9aa2a15a | 99 | drop(pipe_w); |
61bfa355 | 100 | |
edf4f4b9 WB |
101 | let pipe_r = match pipe_r.try_into() { |
102 | Ok(p) => p, | |
103 | Err(err) => { | |
104 | unsafe { | |
105 | libc::kill(pid, 9); | |
106 | } | |
107 | return Err(err); | |
108 | } | |
109 | }; | |
110 | ||
e420f6f9 WB |
111 | Ok(Self { |
112 | pid: Some(pid), | |
113 | out: pipe_r, | |
114 | }) | |
115 | } | |
116 | ||
117 | pub fn wait(&mut self) -> io::Result<()> { | |
118 | let my_pid = self.pid.take().unwrap(); | |
61bfa355 | 119 | let mut status: c_int = -1; |
e420f6f9 WB |
120 | |
121 | loop { | |
a18b03f3 | 122 | match c_result!(unsafe { libc::waitpid(my_pid, &mut status, 0) }) { |
e420f6f9 WB |
123 | Ok(pid) if pid == my_pid => break, |
124 | Ok(_other) => continue, | |
125 | Err(ref err) if err.kind() == io::ErrorKind::Interrupted => continue, | |
126 | Err(other) => return Err(other), | |
127 | } | |
128 | } | |
129 | ||
61bfa355 | 130 | if status != 0 { |
275009ec WB |
131 | Err(io::Error::new( |
132 | io::ErrorKind::Other, | |
133 | "error in child process", | |
134 | )) | |
61bfa355 WB |
135 | } else { |
136 | Ok(()) | |
137 | } | |
e420f6f9 WB |
138 | } |
139 | ||
61bfa355 | 140 | pub async fn get_result(&mut self) -> io::Result<SyscallStatus> { |
61bfa355 | 141 | let mut data: Data = unsafe { std::mem::zeroed() }; |
ec68dd3e WB |
142 | // Compiler bug: we currently need to put the slice into a temporary variable... |
143 | let dataslice: &mut [u8] = unsafe { | |
61bfa355 WB |
144 | std::slice::from_raw_parts_mut( |
145 | &mut data as *mut Data as *mut u8, | |
146 | std::mem::size_of::<Data>(), | |
147 | ) | |
ec68dd3e | 148 | }; |
9aa2a15a | 149 | self.out.read_exact(dataslice).await?; |
ec68dd3e WB |
150 | //self.read_exact(unsafe { |
151 | // std::slice::from_raw_parts_mut( | |
152 | // &mut data as *mut Data as *mut u8, | |
153 | // std::mem::size_of::<Data>(), | |
154 | // ) | |
155 | //}) | |
156 | //.await?; | |
275009ec WB |
157 | if data.failure != 0 { |
158 | Err(io::Error::from_raw_os_error(data.failure)) | |
159 | } else if data.error == 0 { | |
61bfa355 WB |
160 | Ok(SyscallStatus::Ok(data.val)) |
161 | } else { | |
162 | Ok(SyscallStatus::Err(data.error)) | |
163 | } | |
e420f6f9 WB |
164 | } |
165 | } |