wasmer_wasix/syscalls/wasix/
proc_spawn.rs1use virtual_fs::Pipe;
2use wasmer_wasix_types::wasi::ProcessHandles;
3
4use super::*;
5use crate::syscalls::*;
6
7#[instrument(level = "trace", skip_all, fields(name = field::Empty, working_dir = field::Empty), ret)]
27pub fn proc_spawn<M: MemorySize>(
28 mut ctx: FunctionEnvMut<'_, WasiEnv>,
29 name: WasmPtr<u8, M>,
30 name_len: M::Offset,
31 chroot: Bool,
32 args: WasmPtr<u8, M>,
33 args_len: M::Offset,
34 preopen: WasmPtr<u8, M>,
35 preopen_len: M::Offset,
36 stdin: WasiStdioMode,
37 stdout: WasiStdioMode,
38 stderr: WasiStdioMode,
39 working_dir: WasmPtr<u8, M>,
40 working_dir_len: M::Offset,
41 ret_handles: WasmPtr<ProcessHandles, M>,
42) -> Result<Errno, WasiError> {
43 WasiEnv::do_pending_operations(&mut ctx)?;
44
45 let env = ctx.data();
46 let control_plane = &env.control_plane;
47 let memory = unsafe { env.memory_view(&ctx) };
48 let name = unsafe { get_input_str_ok!(&memory, name, name_len) };
49 let args = unsafe { get_input_str_ok!(&memory, args, args_len) };
50 let preopen = unsafe { get_input_str_ok!(&memory, preopen, preopen_len) };
51 let working_dir = unsafe { get_input_str_ok!(&memory, working_dir, working_dir_len) };
52
53 Span::current()
54 .record("name", name.as_str())
55 .record("working_dir", working_dir.as_str());
56
57 if chroot == Bool::True {
58 warn!("chroot is not currently supported");
59 return Ok(Errno::Notsup);
60 }
61
62 let args: Vec<_> = args
63 .split(&['\n', '\r'])
64 .map(|a| a.to_string())
65 .filter(|a| !a.is_empty())
66 .collect();
67
68 let preopen: Vec<_> = preopen
69 .split(&['\n', '\r'])
70 .map(|a| a.to_string())
71 .filter(|a| !a.is_empty())
72 .collect();
73
74 let (handles, ctx) = match proc_spawn_internal(
75 ctx,
76 name,
77 Some(args),
78 Some(preopen),
79 Some(working_dir),
80 stdin,
81 stdout,
82 stderr,
83 )? {
84 Ok(a) => a,
85 Err(err) => {
86 return Ok(err);
87 }
88 };
89
90 let env = ctx.data();
91 let memory = unsafe { env.memory_view(&ctx) };
92 wasi_try_mem_ok!(ret_handles.write(&memory, handles));
93 Ok(Errno::Success)
94}
95
96pub fn proc_spawn_internal(
97 mut ctx: FunctionEnvMut<'_, WasiEnv>,
98 name: String,
99 args: Option<Vec<String>>,
100 preopen: Option<Vec<String>>,
101 working_dir: Option<String>,
102 stdin: WasiStdioMode,
103 stdout: WasiStdioMode,
104 stderr: WasiStdioMode,
105) -> WasiResult<(ProcessHandles, FunctionEnvMut<'_, WasiEnv>)> {
106 let env = ctx.data();
107
108 let (mut child_env, handle) = match ctx.data().fork() {
110 Ok(x) => x,
111 Err(err) => {
112 return Ok(Err(Errno::Access));
114 }
115 };
116 let child_process = child_env.process.clone();
117 if let Some(args) = args {
118 let mut child_state = env.state.fork();
119 child_state.args = std::sync::Mutex::new(args);
120 child_env.state = Arc::new(child_state);
121 }
122
123 ctx.data_mut().owned_handles.push(handle);
125 let env = ctx.data();
126
127 if let Some(preopen) = preopen {
129 if !preopen.is_empty() {
130 for preopen in preopen {
131 warn!(
132 "preopens are not yet supported for spawned processes [{}]",
133 preopen
134 );
135 }
136 return Ok(Err(Errno::Notsup));
137 }
138 }
139
140 if let Some(working_dir) = working_dir {
142 child_env.state.fs.set_current_dir(working_dir.as_str());
143 }
144
145 let (stdin, stdout, stderr) = {
147 let (child_state, child_inodes) = child_env.get_wasi_state_and_inodes();
148 let mut conv_stdio_mode = |mode: WasiStdioMode,
149 fd: WasiFd,
150 pipe_towards_child: bool|
151 -> Result<OptionFd, Errno> {
152 match mode {
153 WasiStdioMode::Piped => {
154 let (tx, rx) = Pipe::new().split();
155 let read_inode = child_state.fs.create_inode_with_default_stat(
156 child_inodes,
157 Kind::PipeRx { rx },
158 false,
159 "pipe".into(),
160 );
161 let write_inode = child_state.fs.create_inode_with_default_stat(
162 child_inodes,
163 Kind::PipeTx { tx },
164 false,
165 "pipe".into(),
166 );
167
168 let (parent_end, child_end) = if pipe_towards_child {
169 (write_inode, read_inode)
170 } else {
171 (read_inode, write_inode)
172 };
173
174 let rights = crate::net::socket::all_socket_rights();
175 let pipe = ctx.data().state.fs.create_fd(
176 rights,
177 rights,
178 Fdflags::empty(),
179 Fdflagsext::empty(),
180 0,
181 parent_end,
182 )?;
183 child_state.fs.create_fd_ext(
184 rights,
185 rights,
186 Fdflags::empty(),
187 Fdflagsext::empty(),
188 0,
189 child_end,
190 Some(fd),
191 false,
192 )?;
193
194 trace!("fd_pipe (fd1={}, fd2={})", pipe, fd);
195 Ok(OptionFd {
196 tag: OptionTag::Some,
197 fd: pipe,
198 })
199 }
200 WasiStdioMode::Inherit => Ok(OptionFd {
201 tag: OptionTag::None,
202 fd: u32::MAX,
203 }),
204 _ => {
205 child_state.fs.close_fd(fd);
206 Ok(OptionFd {
207 tag: OptionTag::None,
208 fd: u32::MAX,
209 })
210 }
211 }
212 };
213 let stdin = match conv_stdio_mode(stdin, 0, true) {
216 Ok(a) => a,
217 Err(err) => return Ok(Err(err)),
218 };
219 let stdout = match conv_stdio_mode(stdout, 1, false) {
220 Ok(a) => a,
221 Err(err) => return Ok(Err(err)),
222 };
223 let stderr = match conv_stdio_mode(stderr, 2, false) {
224 Ok(a) => a,
225 Err(err) => return Ok(Err(err)),
226 };
227 (stdin, stdout, stderr)
228 };
229
230 let bin_factory = Box::new(ctx.data().bin_factory.clone());
232 let child_pid = child_env.pid();
233
234 let mut builder = Some(child_env);
235
236 let mut process = match bin_factory.try_built_in(name.clone(), Some(&ctx), &mut builder) {
238 Ok(a) => a,
239 Err(err) => {
240 if !err.is_not_found() {
241 error!("builtin failed - {}", err);
242 }
243 let child_work = bin_factory.spawn(name, builder.take().unwrap());
245
246 match __asyncify(&mut ctx, None, async move { Ok(child_work.await) })?
247 .map_err(|err| Errno::Unknown)
248 {
249 Ok(Ok(a)) => a,
250 Ok(Err(err)) => return Ok(Err(conv_spawn_err_to_errno(&err))),
251 Err(err) => return Ok(Err(err)),
252 }
253 }
254 };
255
256 {
258 let mut inner = ctx.data().process.lock();
259 inner.children.push(child_process);
260 }
261 let env = ctx.data();
262 let memory = unsafe { env.memory_view(&ctx) };
263
264 let handles = ProcessHandles {
265 pid: child_pid.raw(),
266 stdin,
267 stdout,
268 stderr,
269 };
270 Ok(Ok((handles, ctx)))
271}