123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171 |
- //! 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<anyhow::Error>);
- 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<nix::sys::stat::Mode>,
- }
- 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::<Vec<_>>();
- // Iterate files in /bin.
- let read_dir = std::fs::read_dir("/bin").context("read_dir(/bin) failed")?;
- let errors = read_dir
- .map(|entry| {
- let entry = entry.context("read_dir(/bin) failed")?;
- let path = entry.path();
- let status = std::process::Command::new(&path)
- .args(&args)
- .env("RUST_LOG", "debug")
- .status()
- .with_context(|| format!("failed to execute {}", path.display()))?;
- if status.code() == Some(0) {
- Ok(())
- } else {
- Err(anyhow::anyhow!("{} failed: {status:?}", path.display()))
- }
- })
- .filter_map(|result| {
- // TODO(https://github.com/rust-lang/rust-clippy/issues/14112): Remove this allowance
- // when the lint behaves more sensibly.
- #[expect(clippy::manual_ok_err)]
- match result {
- Ok(()) => None,
- Err(err) => Some(err),
- }
- })
- .collect::<Vec<_>>();
- 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:?}"));
- }
|