//! init is the first process started by the kernel. //! //! This implementation creates the minimal mounts required to run BPF programs, runs all binaries //! in /bin, prints a final message ("init: success|failure"), and powers off the machine. use anyhow::Context as _; #[derive(Debug)] struct Errors(Vec); impl std::fmt::Display for Errors { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let Self(errors) = self; for (i, error) in errors.iter().enumerate() { if i != 0 { writeln!(f)?; } write!(f, "{:?}", error)?; } Ok(()) } } impl std::error::Error for Errors {} fn run() -> anyhow::Result<()> { const RXRXRX: nix::sys::stat::Mode = nix::sys::stat::Mode::empty() .union(nix::sys::stat::Mode::S_IRUSR) .union(nix::sys::stat::Mode::S_IXUSR) .union(nix::sys::stat::Mode::S_IRGRP) .union(nix::sys::stat::Mode::S_IXGRP) .union(nix::sys::stat::Mode::S_IROTH) .union(nix::sys::stat::Mode::S_IXOTH); struct Mount { source: &'static str, target: &'static str, fstype: &'static str, flags: nix::mount::MsFlags, data: Option<&'static str>, target_mode: Option, } for Mount { source, target, fstype, flags, data, target_mode, } in [ Mount { source: "proc", target: "/proc", fstype: "proc", flags: nix::mount::MsFlags::empty(), data: None, target_mode: Some(RXRXRX), }, Mount { source: "sysfs", target: "/sys", fstype: "sysfs", flags: nix::mount::MsFlags::empty(), data: None, target_mode: Some(RXRXRX), }, Mount { source: "debugfs", target: "/sys/kernel/debug", fstype: "debugfs", flags: nix::mount::MsFlags::empty(), data: None, target_mode: None, }, Mount { source: "bpffs", target: "/sys/fs/bpf", fstype: "bpf", flags: nix::mount::MsFlags::empty(), data: None, target_mode: None, }, ] { match target_mode { None => { // Must exist. let nix::sys::stat::FileStat { st_mode, .. } = nix::sys::stat::stat(target) .with_context(|| format!("stat({target}) failed"))?; let s_flag = nix::sys::stat::SFlag::from_bits_truncate(st_mode); if !s_flag.contains(nix::sys::stat::SFlag::S_IFDIR) { anyhow::bail!("{target} is not a directory"); } } Some(target_mode) => { // Must not exist. nix::unistd::mkdir(target, target_mode) .with_context(|| format!("mkdir({target}) failed"))?; } } nix::mount::mount(Some(source), target, Some(fstype), flags, data).with_context(|| { format!("mount({source}, {target}, {fstype}, {flags:?}, {data:?}) failed") })?; } // By contract we run everything in /bin and assume they're rust test binaries. // // If the user requested command line arguments, they're named init.arg={}. // Read kernel parameters from /proc/cmdline. They're space separated on a single line. let cmdline = std::fs::read_to_string("/proc/cmdline") .with_context(|| "read_to_string(/proc/cmdline) failed")?; let args = cmdline .split_whitespace() .filter_map(|parameter| { parameter .strip_prefix("init.arg=") .map(std::ffi::OsString::from) }) .collect::>(); // Iterate files in /bin. let read_dir = std::fs::read_dir("/bin").context("read_dir(/bin) failed")?; let errors = read_dir .filter_map(|entry| { match (|| { let entry = entry.context("read_dir(/bin) failed")?; let path = entry.path(); let status = std::process::Command::new(&path) .args(&args) .status() .with_context(|| format!("failed to execute {}", path.display()))?; if status.code() == Some(0) { Ok(()) } else { Err(anyhow::anyhow!("{} failed: {status:?}", path.display())) } })() { Ok(()) => None, Err(err) => Some(err), } }) .collect::>(); if errors.is_empty() { Ok(()) } else { Err(Errors(errors).into()) } } fn main() { match run() { Ok(()) => { println!("init: success"); } Err(err) => { println!("{err:?}"); println!("init: failure"); } } let how = nix::sys::reboot::RebootMode::RB_POWER_OFF; let _: std::convert::Infallible = nix::sys::reboot::reboot(how) .unwrap_or_else(|err| panic!("reboot({how:?}) failed: {err:?}")); }