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 let child_finished = child_process.finished.clone();
122 let tasks = child_env.tasks().clone();
123 if let Some(args) = args {
124 let mut child_state = env.state.fork();
125 child_state.args = std::sync::Mutex::new(args);
126 child_env.state = Arc::new(child_state);
127 }
128
129 ctx.data_mut().owned_handles.push(handle);
131 let env = ctx.data();
132
133 if let Some(preopen) = preopen
135 && !preopen.is_empty()
136 {
137 for preopen in preopen {
138 warn!(
139 "preopens are not yet supported for spawned processes [{}]",
140 preopen
141 );
142 }
143 return Ok(Err(Errno::Notsup));
144 }
145
146 if let Some(working_dir) = working_dir {
148 child_env.state.fs.set_current_dir(working_dir.as_str());
149 }
150
151 let (stdin, stdout, stderr) = {
153 let (child_state, child_inodes) = child_env.get_wasi_state_and_inodes();
154 let mut conv_stdio_mode = |mode: WasiStdioMode,
155 fd: WasiFd,
156 pipe_towards_child: bool|
157 -> Result<OptionFd, Errno> {
158 match mode {
159 WasiStdioMode::Piped => {
160 let (tx, rx) = Pipe::new().split();
161 let read_inode = child_state.fs.create_inode_with_default_stat(
162 child_inodes,
163 Kind::PipeRx { rx },
164 false,
165 "pipe".into(),
166 );
167 let write_inode = child_state.fs.create_inode_with_default_stat(
168 child_inodes,
169 Kind::PipeTx { tx },
170 false,
171 "pipe".into(),
172 );
173
174 let (parent_end, child_end) = if pipe_towards_child {
175 (write_inode, read_inode)
176 } else {
177 (read_inode, write_inode)
178 };
179
180 let rights = crate::net::socket::all_socket_rights();
181 let pipe = ctx.data().state.fs.create_fd(
182 rights,
183 rights,
184 Fdflags::empty(),
185 Fdflagsext::empty(),
186 0,
187 parent_end,
188 )?;
189 child_state.fs.create_fd_ext(
190 rights,
191 rights,
192 Fdflags::empty(),
193 Fdflagsext::empty(),
194 0,
195 child_end,
196 Some(fd),
197 false,
198 )?;
199
200 trace!("fd_pipe (fd1={}, fd2={})", pipe, fd);
201 Ok(OptionFd {
202 tag: OptionTag::Some,
203 fd: pipe,
204 })
205 }
206 WasiStdioMode::Inherit => Ok(OptionFd {
207 tag: OptionTag::None,
208 fd: u32::MAX,
209 }),
210 _ => {
211 child_state.fs.close_fd(fd);
212 Ok(OptionFd {
213 tag: OptionTag::None,
214 fd: u32::MAX,
215 })
216 }
217 }
218 };
219 let stdin = match conv_stdio_mode(stdin, 0, true) {
222 Ok(a) => a,
223 Err(err) => return Ok(Err(err)),
224 };
225 let stdout = match conv_stdio_mode(stdout, 1, false) {
226 Ok(a) => a,
227 Err(err) => return Ok(Err(err)),
228 };
229 let stderr = match conv_stdio_mode(stderr, 2, false) {
230 Ok(a) => a,
231 Err(err) => return Ok(Err(err)),
232 };
233 (stdin, stdout, stderr)
234 };
235
236 let bin_factory = Box::new(ctx.data().bin_factory.clone());
238 let child_pid = child_env.pid();
239
240 let mut builder = Some(child_env);
241
242 match bin_factory.try_built_in(name.clone(), Some(&ctx), &mut builder) {
244 Ok(task) => {
245 if let Err(err) = propagate_virtual_task_completion(&tasks, task, child_finished) {
246 return Ok(Err(err.into()));
247 }
248 }
249 Err(err) => {
250 if !err.is_not_found() {
251 error!("builtin failed - {}", err);
252 }
253 let child_work = bin_factory.spawn(name, builder.take().unwrap());
255
256 match __asyncify(&mut ctx, None, async move { Ok(child_work.await) })?
257 .map_err(|err| Errno::Unknown)
258 {
259 Ok(Ok(_)) => {}
260 Ok(Err(err)) => return Ok(Err(conv_spawn_err_to_errno(&err))),
261 Err(err) => return Ok(Err(err)),
262 }
263 }
264 }
265
266 {
268 let mut inner = ctx.data().process.lock();
269 inner.children.push(child_process);
270 }
271 let env = ctx.data();
272 let memory = unsafe { env.memory_view(&ctx) };
273
274 let handles = ProcessHandles {
275 pid: child_pid.raw(),
276 stdin,
277 stdout,
278 stderr,
279 };
280 Ok(Ok((handles, ctx)))
281}