1#[cfg(feature = "rustc-dep-of-std")]
4use alloc;
5
6#[cfg(not(feature = "rustc-dep-of-std"))]
7extern crate alloc;
8
9use crate::ErrorCode;
10use crate::Result;
11use crate::RtFd;
12use crate::RtVdsoVtable;
13use crate::into_result;
14use alloc::string::String;
15use alloc::vec::Vec;
16use core::sync::atomic::Ordering;
17
18pub const MAX_ENV_KEY_LEN: usize = 256;
20pub const MAX_ENV_VAL_LEN: usize = 4092;
22
23pub const STDIO_INHERIT: RtFd = -((ErrorCode::MAX as RtFd) + 1);
24pub const STDIO_NULL: RtFd = -((ErrorCode::MAX as RtFd) + 2);
25pub const STDIO_MAKE_PIPE: RtFd = -((ErrorCode::MAX as RtFd) + 3);
26
27pub const STDIO_IS_TERMINAL_ENV_KEY: &str = "MOTURUS_STDIO_IS_TERMINAL";
31
32pub fn args() -> alloc::vec::Vec<String> {
34 let vdso_args: extern "C" fn() -> u64 = unsafe {
35 core::mem::transmute(
36 RtVdsoVtable::get().proc_args.load(Ordering::Relaxed) as usize as *const (),
37 )
38 };
39
40 let args_addr = vdso_args();
41 if args_addr == 0 {
42 return alloc::vec::Vec::new();
43 }
44 let raw_vec = unsafe { deserialize_vec(args_addr) };
45
46 let mut result = Vec::new();
47 for entry in &raw_vec {
48 let arg = entry.to_vec();
49 result.push(unsafe { String::from_utf8_unchecked(arg) });
50 }
51
52 crate::alloc::raw_dealloc(args_addr);
53 result
54}
55
56pub fn env() -> alloc::vec::Vec<(String, String)> {
58 let vdso_get_full_env: extern "C" fn() -> u64 = unsafe {
59 core::mem::transmute(
60 RtVdsoVtable::get()
61 .proc_get_full_env
62 .load(Ordering::Relaxed) as usize as *const (),
63 )
64 };
65
66 let env_addr = vdso_get_full_env();
67 if env_addr == 0 {
68 return alloc::vec::Vec::new();
69 }
70 let raw_vec = unsafe { deserialize_vec(env_addr) };
71 assert_eq!(0, raw_vec.len() & 1);
72
73 let mut result = Vec::new();
74 let num_keys = raw_vec.len() >> 1;
75 for idx in 0..num_keys {
76 let key = raw_vec[idx].to_vec();
77 let val = raw_vec[idx + num_keys].to_vec();
78 result.push(unsafe {
79 (
80 String::from_utf8_unchecked(key),
81 String::from_utf8_unchecked(val),
82 )
83 });
84 }
85
86 crate::alloc::raw_dealloc(env_addr);
87 result
88}
89
90pub fn getenv(key: &str) -> Option<String> {
92 let vdso_get: extern "C" fn(*const u8, usize) -> u64 = unsafe {
93 core::mem::transmute(
94 RtVdsoVtable::get().proc_getenv.load(Ordering::Relaxed) as usize as *const (),
95 )
96 };
97
98 let key = key.as_bytes();
99 assert!(key.len() <= MAX_ENV_KEY_LEN); let val_addr = vdso_get(key.as_ptr(), key.len());
102 if val_addr == u64::MAX {
103 return None;
104 }
105 if val_addr == 0 {
106 return Some(String::new());
107 }
108
109 let val_len: *const u32 = val_addr as usize as *const u32;
110 let val_bytes: *const u8 = (val_addr + 4) as usize as *const u8;
111
112 let val: &[u8] = unsafe { core::slice::from_raw_parts(val_bytes, (*val_len) as usize) };
113 let result = Some(alloc::string::ToString::to_string(
114 core::str::from_utf8(val).unwrap(),
115 ));
116
117 crate::alloc::raw_dealloc(val_addr);
118 result
119}
120
121pub fn setenv(key: &str, val: &str) {
123 let vdso_set: extern "C" fn(*const u8, usize, usize, usize) = unsafe {
124 core::mem::transmute(
125 RtVdsoVtable::get().proc_setenv.load(Ordering::Relaxed) as usize as *const (),
126 )
127 };
128
129 let key = key.as_bytes();
130 assert!(key.len() <= MAX_ENV_KEY_LEN); let val = val.as_bytes();
132 assert!(val.len() <= MAX_ENV_VAL_LEN); vdso_set(key.as_ptr(), key.len(), val.as_ptr() as usize, val.len());
134}
135
136pub fn unsetenv(key: &str) {
138 let vdso_set: extern "C" fn(*const u8, usize, usize, usize) = unsafe {
139 core::mem::transmute(
140 RtVdsoVtable::get().proc_setenv.load(Ordering::Relaxed) as usize as *const (),
141 )
142 };
143
144 let key = key.as_bytes();
145 assert!(key.len() <= MAX_ENV_KEY_LEN); vdso_set(key.as_ptr(), key.len(), 0, usize::MAX);
147}
148
149unsafe fn deserialize_vec(addr: u64) -> Vec<&'static [u8]> {
150 assert_ne!(addr, 0);
151 let mut pos = addr as usize;
155 assert_eq!(pos & 3, 0);
156
157 let num_args = unsafe { *((pos as *const u32).as_ref().unwrap()) };
158 pos += 4;
159
160 let mut result = Vec::new();
161 for _i in 0..num_args {
162 let len = unsafe { *((pos as *const u32).as_ref().unwrap()) };
163 pos += 4;
164 let bytes: &[u8] = unsafe { core::slice::from_raw_parts(pos as *const u8, len as usize) };
165 result.push(bytes);
166 pos += len as usize;
167 pos = (pos + 3) & !3; }
169
170 result
171}
172
173#[allow(unused)]
174#[derive(Default)]
175pub struct SpawnArgs {
176 pub program: String,
177 pub args: Vec<String>,
178 pub env: Vec<(String, String)>,
179 pub cwd: Option<String>,
180 pub stdin: RtFd,
181 pub stdout: RtFd,
182 pub stderr: RtFd,
183}
184
185#[repr(C)]
186pub struct SpawnArgsRt {
187 pub prog_name_addr: u64,
188 pub prog_name_size: u64,
189 pub args: u64, pub env: u64, pub stdin: RtFd,
192 pub stdout: RtFd,
193 pub stderr: RtFd,
194 pub _reserved: i32,
195}
196
197#[derive(Default)]
198#[repr(C)]
199pub struct SpawnResult {
200 pub handle: u64,
201 pub stdin: RtFd,
202 pub stdout: RtFd,
203 pub stderr: RtFd,
204 pub _reserved: i32,
205}
206
207pub fn spawn(args: SpawnArgs) -> Result<(u64, RtFd, RtFd, RtFd)> {
208 use alloc::borrow::ToOwned;
209 let vdso_spawn: extern "C" fn(*const SpawnArgsRt, *mut SpawnResult) -> crate::ErrorCode = unsafe {
210 core::mem::transmute(
211 RtVdsoVtable::get().proc_spawn.load(Ordering::Relaxed) as usize as *const (),
212 )
213 };
214
215 let mut keys = alloc::vec![];
216 let mut vals = alloc::vec![];
217
218 let mut have_is_terminal = false;
219
220 for (k, v) in &args.env {
221 if k == "PWD" {
222 continue;
224 } else if k == STDIO_IS_TERMINAL_ENV_KEY {
225 have_is_terminal = true;
226 }
227 keys.push(k.clone());
228 vals.push(v.clone());
229 }
230
231 let pwd = match args.cwd.as_ref() {
233 Some(cwd) => cwd.clone(),
234 None => {
235 if let Ok(cwd) = super::fs::getcwd() {
236 cwd.clone()
237 } else {
238 "".to_owned()
239 }
240 }
241 };
242 keys.push("PWD".to_owned());
243 vals.push(pwd);
244
245 if !have_is_terminal && args.stdin == STDIO_INHERIT && args.stdout == STDIO_INHERIT {
246 keys.push(STDIO_IS_TERMINAL_ENV_KEY.to_owned());
247 vals.push("true".to_owned());
248 }
249
250 let (rt_args, args_layout) = encode_args(&args.args);
251 let (env, env_layout) = encode_env(keys, vals);
252
253 let args_rt = SpawnArgsRt {
254 prog_name_addr: args.program.as_str().as_ptr() as usize as u64,
255 prog_name_size: args.program.as_str().len() as u64,
256 args: rt_args,
257 env,
258 stdin: args.stdin,
259 stdout: args.stdout,
260 stderr: args.stderr,
261 _reserved: 0,
262 };
263
264 let mut result_rt = SpawnResult::default();
265 let res = vdso_spawn(&args_rt, &mut result_rt);
266 if let Some(layout) = args_layout {
267 unsafe { crate::alloc::dealloc(args_rt.args as usize as *mut u8, layout) };
268 }
269 if let Some(layout) = env_layout {
270 unsafe { crate::alloc::dealloc(args_rt.env as usize as *mut u8, layout) };
271 }
272
273 into_result(res)?;
274 Ok((
275 result_rt.handle,
276 result_rt.stdin,
277 result_rt.stdout,
278 result_rt.stderr,
279 ))
280}
281
282pub fn kill(handle: u64) -> Result<()> {
283 let vdso_kill: extern "C" fn(u64) -> crate::ErrorCode = unsafe {
284 core::mem::transmute(
285 RtVdsoVtable::get().proc_kill.load(Ordering::Relaxed) as usize as *const (),
286 )
287 };
288
289 into_result(vdso_kill(handle))
290}
291
292pub fn wait(handle: u64) -> Result<i32> {
293 let vdso_wait: extern "C" fn(u64) -> crate::ErrorCode = unsafe {
294 core::mem::transmute(
295 RtVdsoVtable::get().proc_wait.load(Ordering::Relaxed) as usize as *const (),
296 )
297 };
298
299 into_result(vdso_wait(handle))?;
300 try_wait(handle)
301}
302
303fn convert_exit_status(exit_status: u64) -> i32 {
304 if exit_status & 0xffff_ffff_0000_0000 == 0 {
305 let status_u32: u32 = exit_status as u32;
307 u32::cast_signed(status_u32)
308 } else {
309 -1
313 }
314}
315
316pub fn try_wait(handle: u64) -> Result<i32> {
317 let vdso_status: extern "C" fn(u64, *mut u64) -> crate::ErrorCode = unsafe {
318 core::mem::transmute(
319 RtVdsoVtable::get().proc_status.load(Ordering::Relaxed) as usize as *const (),
320 )
321 };
322
323 let mut status = 0_u64;
324 into_result(vdso_status(handle, &mut status))?;
325 Ok(convert_exit_status(status))
326}
327
328pub fn exit(code: i32) -> ! {
329 let vdso_exit: extern "C" fn(i32) -> ! = unsafe {
330 core::mem::transmute(
331 RtVdsoVtable::get().proc_exit.load(Ordering::Relaxed) as usize as *const (),
332 )
333 };
334
335 vdso_exit(code)
336}
337
338fn encode_env(keys: Vec<String>, vals: Vec<String>) -> (u64, Option<core::alloc::Layout>) {
339 assert_eq!(keys.len(), vals.len());
340
341 let mut needed_len: u32 = 4; let mut num_args = 0_u32;
343
344 let mut calc_lengths = |arg: &str| {
345 needed_len += 4;
346 needed_len += ((arg.len() as u32) + 3) & !3_u32;
347 num_args += 1;
348 };
349
350 for arg in &keys {
351 calc_lengths(arg.as_str());
352 }
353
354 for arg in &vals {
355 calc_lengths(arg.as_str());
356 }
357
358 if num_args == 0 {
359 return (0, None);
360 }
361
362 let layout = core::alloc::Layout::from_size_align(needed_len as usize, 8).unwrap();
363 let result_addr = crate::alloc::alloc(layout) as usize;
364 assert_ne!(result_addr, 0);
365
366 unsafe {
367 let mut pos = result_addr as usize;
368 *((pos as *mut u32).as_mut().unwrap()) = num_args;
369 pos += 4;
370
371 let mut write_arg = |arg: &str| {
372 *((pos as *mut u32).as_mut().unwrap()) = arg.len() as u32;
373 pos += 4;
374
375 let bytes = arg.as_bytes();
376 core::ptr::copy_nonoverlapping(bytes.as_ptr(), pos as *mut u8, bytes.len());
377 pos += (bytes.len() + 3) & !3_usize;
378 };
379
380 for arg in keys {
381 write_arg(arg.as_str());
382 }
383
384 for arg in vals {
385 write_arg(arg.as_str());
386 }
387 }
388
389 (result_addr as u64, Some(layout))
390}
391
392fn encode_args(args: &Vec<String>) -> (u64, Option<core::alloc::Layout>) {
393 let mut needed_len: u32 = 4; let mut num_args = 0_u32;
395
396 let mut calc_lengths = |arg: &str| {
397 needed_len += 4;
398 needed_len += ((arg.len() as u32) + 3) & !3_u32;
399 num_args += 1;
400 };
401
402 for arg in args {
403 if arg.is_empty() {
404 continue;
405 }
406 calc_lengths(arg.as_str());
407 }
408
409 if num_args == 0 {
410 return (0, None);
411 }
412
413 let layout = core::alloc::Layout::from_size_align(needed_len as usize, 8).unwrap();
414 let result_addr = crate::alloc::alloc(layout) as usize;
415 assert_ne!(result_addr, 0);
416
417 unsafe {
418 let mut pos = result_addr;
419 *((pos as *mut u32).as_mut().unwrap()) = num_args;
420 pos += 4;
421
422 let mut write_arg = |arg: &str| {
423 *((pos as *mut u32).as_mut().unwrap()) = arg.len() as u32;
424 pos += 4;
425
426 let bytes = arg.as_bytes();
427 core::ptr::copy_nonoverlapping(bytes.as_ptr(), pos as *mut u8, bytes.len());
428 pos += (bytes.len() + 3) & !3_usize;
429 };
430
431 for arg in args {
432 if arg.is_empty() {
433 continue;
434 }
435 write_arg(arg.as_str());
436 }
437 }
438
439 (result_addr as u64, Some(layout))
440}
441
442pub fn current_exe() -> Result<alloc::string::String> {
443 let vdso_current_exe: extern "C" fn(*mut u8, *mut usize) -> crate::ErrorCode = unsafe {
444 core::mem::transmute(
445 RtVdsoVtable::get().current_exe.load(Ordering::Relaxed) as usize as *const (),
446 )
447 };
448
449 let mut bytes = [0_u8; crate::fs::MAX_PATH_LEN];
450 let mut len = 0_usize;
451
452 use alloc::borrow::ToOwned;
453 into_result(vdso_current_exe(bytes.as_mut_ptr(), &mut len))?;
454 Ok(core::str::from_utf8(&bytes[..len]).unwrap().to_owned())
455}