moto_rt/
process.rs

1//! Per-process data such as command line arguments and environment variables.
2
3#[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
18/// An arbitrarily defined maximum lenth of an environment variable key.
19pub const MAX_ENV_KEY_LEN: usize = 256;
20/// An arbitrarily defined maximum lenth of an environment variable value.
21pub 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
27/// If this ENV var is present, and its value is "true" or "TRUE",
28/// the stdio of the process will return true on is_terminal(), otherwise
29/// false.
30pub const STDIO_IS_TERMINAL_ENV_KEY: &str = "MOTURUS_STDIO_IS_TERMINAL";
31
32/// Get all commandline args for the current process.
33pub 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
56/// Get all environment variables for the current process.
57pub 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
90/// Get a specific environment variable, if set.
91pub 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); // Better to panic than silently do something different.
100
101    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
121/// Set an environment variable.
122pub 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); // Better to panic than silently do something different.
131    let val = val.as_bytes();
132    assert!(val.len() <= MAX_ENV_VAL_LEN); // Better to panic than silently do something different.
133    vdso_set(key.as_ptr(), key.len(), val.as_ptr() as usize, val.len());
134}
135
136/// Unset an environment variable.
137pub 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); // Better to panic than silently do something different.
146    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    // first four bytes: the number of arguments;
152    // then arguments, aligned at four bytes: size (four bytes), bytes.
153
154    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; // Align up to 4 bytes.
168    }
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, // Encoded.
190    pub env: u64,  // Encoded.
191    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            // Ignore the env var.
223            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    // Set $PWD.
232    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        // Map u64 to i32.
306        let status_u32: u32 = exit_status as u32;
307        u32::cast_signed(status_u32)
308    } else {
309        // The process exited not via Rust's std::process::exit, but
310        // via a lower-level syscall. Don't try to second-guess what
311        // it wanted to say, just return a -1.
312        -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; // Total num strings.
342    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; // Args num.
394    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}