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