mirror of https://github.com/aya-rs/aya
You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
166 lines
5.1 KiB
Rust
166 lines
5.1 KiB
Rust
//! 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)
|
|
.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| 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:?}"));
|
|
}
|