]> git.proxmox.com Git - pve-lxc-syscalld.git/blame - src/fork.rs
Some CStr related changes
[pve-lxc-syscalld.git] / src / fork.rs
CommitLineData
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
7use std::io;
8use std::os::raw::c_int;
9use std::os::unix::io::{FromRawFd, IntoRawFd};
10use std::panic::UnwindSafe;
11use std::pin::Pin;
12use std::task::{Context, Poll};
13
e420f6f9 14use futures::io::AsyncRead;
61bfa355 15use nix::errno::Errno;
e420f6f9 16
c95be5f6 17use crate::syscall::SyscallStatus;
e420f6f9 18use crate::tools::Fd;
e420f6f9
WB
19use crate::{libc_try, libc_wrap};
20
21pub async fn forking_syscall<F>(func: F) -> io::Result<SyscallStatus>
22where
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
31pub 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
38impl 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)]
47struct Data {
48 val: i64,
49 error: i32,
275009ec 50 failure: i32,
61bfa355
WB
51}
52
e420f6f9
WB
53impl 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
160impl 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}