use alloc::borrow::ToOwned; use alloc::vec::Vec; use syscall::{Error, EINTR}; use syscall::flag::{O_CLOEXEC, O_RDONLY}; use redox_exec::*; pub fn main() -> ! { let initfs_length; let envs = { let mut env = [0_u8; 4096]; let fd = FdGuard::new(syscall::open("sys:env", O_RDONLY).expect("bootstrap: failed to open env")); let bytes_read = syscall::read(*fd, &mut env).expect("bootstrap: failed to read env"); if bytes_read >= env.len() { // TODO: Handle this, we can allocate as much as we want in theory. panic!("env is too large"); } let env = &mut env[..bytes_read]; let raw_iter = || env.split(|c| *c == b'\n').filter(|var| !var.is_empty()); let mut initfs_length_opt = None; for var in raw_iter() { let equal_sign = var.iter().position(|c| *c == b'=').expect("malformed environment variable"); let name = &var[..equal_sign]; let value = &var[equal_sign + 1..]; match name { b"INITFS_LENGTH" => initfs_length_opt = core::str::from_utf8(value).ok().and_then(|s| usize::from_str_radix(s, 16).ok()), _ => continue, } } initfs_length = initfs_length_opt.expect("missing INITFS_LENGTH"); let iter = || raw_iter().filter(|var| !var.starts_with(b"INITFS_")); iter().map(|var| var.to_owned()).collect::>() }; extern { // The linker script will define this as the location of the initfs header. static __initfs_header: u8; } unsafe { // Creating a reference to NULL is UB. Mask the UB for now using black_box. // FIXME use a raw pointer and inline asm for reading instead for the initfs header. spawn_initfs(core::ptr::addr_of!(__initfs_header), initfs_length); } const CWD: &[u8] = b"/scheme/initfs"; let extrainfo = redox_exec::ExtraInfo { cwd: Some(CWD), }; let path = "/scheme/initfs/bin/init"; let total_args_envs_auxvpointee_size = path.len() + 1 + envs.len() + envs.iter().map(|v| v.len()).sum::() + CWD.len() + 1; let image_file = FdGuard::new(syscall::open(path, O_RDONLY).expect("failed to open init")); let open_via_dup = FdGuard::new(syscall::open("thisproc:current/open_via_dup", 0).expect("failed to open open_via_dup")); let memory = FdGuard::new(syscall::open("memory:", 0).expect("failed to open memory")); fexec_impl(image_file, open_via_dup, &memory, path.as_bytes(), [path], envs.iter(), total_args_envs_auxvpointee_size, &extrainfo, None).expect("failed to execute init"); unreachable!() } unsafe fn spawn_initfs(initfs_start: *const u8, initfs_length: usize) { let read = syscall::open("pipe:", O_CLOEXEC).expect("failed to open sync read pipe"); // The write pipe will not inherit O_CLOEXEC, but is closed by the daemon later. let write = syscall::dup(read, b"write").expect("failed to open sync write pipe"); match redox_exec::fork_impl() { Err(err) => { panic!("Failed to fork in order to start initfs daemon: {}", err); } // Continue serving the scheme as the child. Ok(0) => { let _ = syscall::close(read); } // Return in order to execute init, as the parent. Ok(_) => { let _ = syscall::close(write); loop { match syscall::read(read, &mut [0]) { Err(Error { errno: EINTR }) => continue, _ => break, } } return; } } crate::initfs::run(core::slice::from_raw_parts(initfs_start, initfs_length), write); }