use std::fs;
use std::io::Write as _;
use std::os::unix::fs::PermissionsExt as _;
use std::path::{Path, PathBuf};
pub const SENTINEL: &str = "/run/arcbox-boot-done";
const BOOT_ID: &str = "/proc/sys/kernel/random/boot_id";
const SYSTEMD_UNIT: &str = "/etc/systemd/system/arcbox-boot-done.service";
const SYSTEMD_TARGET_DROP_IN: &str =
"/etc/systemd/system/multi-user.target.d/arcbox-boot-done.conf";
const OPENRC_SERVICE: &str = "/etc/init.d/arcbox-boot-done";
const OPENRC_RUNLEVEL: &str = "/etc/runlevels/default/arcbox-boot-done";
const SYSVINIT_INITTAB: &str = "/etc/inittab";
const SYSVINIT_RC: &str = "/etc/init.d/rc";
const SYSVINIT_ENTRY_ID: &str = "abd";
fn systemd_unit_body() -> String {
format!(
"[Unit]
Description=ArcBox boot-completion sentinel
After=multi-user.target
After=network-online.target
[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=/bin/sh -c 'cat {BOOT_ID} > {SENTINEL}'
"
)
}
const SYSTEMD_TARGET_DROP_IN_BODY: &str = "[Unit]\nWants=arcbox-boot-done.service\n";
fn openrc_service_body() -> String {
format!(
"#!/sbin/openrc-run
description=\"ArcBox boot-completion sentinel\"
depend() {{
after *
}}
start() {{
cat {BOOT_ID} > {SENTINEL}
}}
stop() {{
return 0
}}
"
)
}
fn sysvinit_entry() -> String {
format!("{SYSVINIT_ENTRY_ID}:2345:wait:/bin/sh -c 'cat {BOOT_ID} > {SENTINEL}'")
}
fn has_sysvinit_entry(inittab: &str) -> bool {
inittab
.lines()
.any(|line| line.split(':').next() == Some(SYSVINIT_ENTRY_ID))
}
struct Layout {
root: PathBuf,
}
impl Layout {
#[cfg(target_os = "linux")]
fn guest() -> Self {
Self {
root: PathBuf::from("/"),
}
}
fn path(&self, absolute: &str) -> PathBuf {
self.root.join(absolute.trim_start_matches('/'))
}
fn hook_installed(&self) -> bool {
self.path(SYSTEMD_UNIT).exists()
|| self.path(OPENRC_SERVICE).exists()
|| fs::read_to_string(self.path(SYSVINIT_INITTAB))
.is_ok_and(|inittab| has_sysvinit_entry(&inittab))
}
fn boot_complete(&self) -> bool {
let (Ok(sentinel), Ok(boot_id)) = (
fs::read_to_string(self.path(SENTINEL)),
fs::read_to_string(self.path(BOOT_ID)),
) else {
return false;
};
sentinel_matches(&sentinel, &boot_id)
}
}
#[cfg(target_os = "linux")]
#[must_use]
pub fn hook_installed() -> bool {
Layout::guest().hook_installed()
}
#[cfg(target_os = "linux")]
#[must_use]
pub fn boot_complete() -> bool {
Layout::guest().boot_complete()
}
fn sentinel_matches(sentinel: &str, boot_id: &str) -> bool {
let boot_id = boot_id.trim();
!boot_id.is_empty() && sentinel.trim() == boot_id
}
#[cfg(target_os = "linux")]
pub fn install() -> bool {
Layout::guest().install()
}
impl Layout {
fn install(&self) -> bool {
if self.path("/usr/lib/systemd/systemd").exists()
|| self.path("/lib/systemd/systemd").exists()
{
return self.install_systemd();
}
if self.path("/sbin/openrc").exists() || self.path("/usr/libexec/rc").is_dir() {
return self.install_openrc();
}
if self.path(SYSVINIT_INITTAB).is_file() && self.path(SYSVINIT_RC).is_file() {
return self.install_sysvinit();
}
tracing::info!(
"no recognized distro init; machine readiness will not wait for boot to settle"
);
false
}
fn install_systemd(&self) -> bool {
let unit = self.path(SYSTEMD_UNIT);
if let Err(e) = write_file(&unit, &systemd_unit_body(), 0o644) {
tracing::warn!(error = %e, "failed to write the systemd boot-done unit");
return false;
}
let drop_in = self.path(SYSTEMD_TARGET_DROP_IN);
if let Err(e) = write_file(&drop_in, SYSTEMD_TARGET_DROP_IN_BODY, 0o644) {
tracing::warn!(error = %e, "failed to wire the systemd boot-done unit into the boot");
let _ = fs::remove_file(&unit);
return false;
}
tracing::info!("installed the systemd boot-completion hook");
true
}
fn install_openrc(&self) -> bool {
let service = self.path(OPENRC_SERVICE);
if let Err(e) = write_file(&service, &openrc_service_body(), 0o755) {
tracing::warn!(error = %e, "failed to write the openrc boot-done service");
return false;
}
let runlevel = self.path(OPENRC_RUNLEVEL);
if let Some(parent) = runlevel.parent()
&& let Err(e) = fs::create_dir_all(parent)
{
tracing::warn!(error = %e, "failed to create the openrc default runlevel dir");
let _ = fs::remove_file(&service);
return false;
}
let _ = fs::remove_file(&runlevel);
if let Err(e) = std::os::unix::fs::symlink(OPENRC_SERVICE, &runlevel) {
tracing::warn!(error = %e, "failed to add the openrc boot-done service to the runlevel");
let _ = fs::remove_file(&service);
return false;
}
tracing::info!("installed the openrc boot-completion hook");
true
}
fn install_sysvinit(&self) -> bool {
let path = self.path(SYSVINIT_INITTAB);
let mut inittab = match fs::read_to_string(&path) {
Ok(inittab) => inittab,
Err(e) => {
tracing::warn!(error = %e, "failed to read /etc/inittab");
return false;
}
};
if !has_sysvinit_entry(&inittab) {
if !inittab.is_empty() && !inittab.ends_with('\n') {
inittab.push('\n');
}
inittab.push_str(&sysvinit_entry());
inittab.push('\n');
if let Err(e) = write_file(&path, &inittab, 0o644) {
tracing::warn!(error = %e, "failed to add the boot-done entry to /etc/inittab");
return false;
}
}
tracing::info!("installed the sysvinit boot-completion hook");
true
}
}
fn write_file(path: &Path, body: &str, mode: u32) -> std::io::Result<()> {
if let Some(parent) = path.parent() {
fs::create_dir_all(parent)?;
}
let staged = path.with_extension("arcbox-tmp");
let result = stage(&staged, body, mode).and_then(|()| fs::rename(&staged, path));
if result.is_err() {
let _ = fs::remove_file(&staged);
}
result
}
fn stage(staged: &Path, body: &str, mode: u32) -> std::io::Result<()> {
let mut file = fs::File::create(staged)?;
file.write_all(body.as_bytes())?;
file.sync_all()?;
fs::set_permissions(staged, fs::Permissions::from_mode(mode))
}
#[cfg(test)]
mod tests {
use super::*;
fn image() -> (tempfile::TempDir, Layout) {
let dir = tempfile::tempdir().expect("tempdir");
let layout = Layout {
root: dir.path().to_path_buf(),
};
(dir, layout)
}
fn touch(layout: &Layout, absolute: &str) {
let path = layout.path(absolute);
fs::create_dir_all(path.parent().expect("parent")).expect("mkdir");
fs::write(&path, "").expect("write");
}
#[test]
fn an_unrecognized_init_installs_nothing_and_promises_nothing() {
let (_dir, layout) = image();
assert!(!layout.install());
assert!(!layout.hook_installed());
}
#[test]
fn a_systemd_image_gets_a_unit_presets_cannot_disable() {
let (_dir, layout) = image();
touch(&layout, "/usr/lib/systemd/systemd");
assert!(layout.install());
assert!(layout.hook_installed());
let drop_in = fs::read_to_string(layout.path(SYSTEMD_TARGET_DROP_IN)).expect("drop-in");
assert!(
drop_in.contains("Wants=arcbox-boot-done.service"),
"{drop_in}"
);
let unit = fs::read_to_string(layout.path(SYSTEMD_UNIT)).expect("unit");
assert!(!unit.contains("[Install]"), "{unit}");
}
#[test]
fn an_openrc_image_gets_a_service_in_the_default_runlevel() {
let (_dir, layout) = image();
touch(&layout, "/sbin/openrc");
assert!(layout.install());
assert!(layout.hook_installed());
let runlevel = layout.path(OPENRC_RUNLEVEL);
assert_eq!(
fs::read_link(&runlevel).expect("symlink"),
Path::new(OPENRC_SERVICE)
);
let mode = fs::metadata(layout.path(OPENRC_SERVICE))
.expect("service")
.permissions()
.mode();
assert_eq!(mode & 0o777, 0o755, "openrc runs the service as a program");
}
#[test]
fn a_sysvinit_image_gets_one_inittab_entry_after_the_runlevels() {
let (_dir, layout) = image();
touch(&layout, SYSVINIT_RC);
let inittab = layout.path(SYSVINIT_INITTAB);
fs::write(
&inittab,
"id:2:initdefault:\nl2:2:wait:/etc/init.d/rc 2\nl3:3:wait:/etc/init.d/rc 3",
)
.expect("write");
assert!(layout.install());
assert!(layout.install());
assert!(layout.hook_installed());
let lines: Vec<String> = fs::read_to_string(&inittab)
.expect("inittab")
.lines()
.map(str::to_owned)
.collect();
assert_eq!(lines.len(), 4, "{lines:?}");
assert_eq!(lines[2], "l3:3:wait:/etc/init.d/rc 3");
assert_eq!(lines[3], sysvinit_entry());
}
#[test]
fn a_busybox_inittab_alone_is_not_sysvinit() {
let (_dir, layout) = image();
touch(&layout, SYSVINIT_INITTAB);
assert!(!layout.install());
assert!(!layout.hook_installed());
}
#[test]
fn a_failed_enable_rolls_the_unit_back() {
let (_dir, layout) = image();
touch(&layout, "/usr/lib/systemd/systemd");
touch(&layout, "/etc/systemd/system/multi-user.target.d");
assert!(!layout.install_systemd());
assert!(!layout.path(SYSTEMD_UNIT).exists());
assert!(!layout.hook_installed());
}
#[test]
fn a_failed_write_leaves_no_hook_behind() {
let (_dir, layout) = image();
touch(&layout, "/usr/lib/systemd/systemd");
let staged = layout.path(SYSTEMD_UNIT).with_extension("arcbox-tmp");
fs::create_dir_all(&staged).expect("mkdir");
assert!(!layout.install());
assert!(!layout.path(SYSTEMD_UNIT).exists());
assert!(!layout.hook_installed());
}
#[test]
fn boot_complete_reads_the_sentinel_against_this_boot() {
let (_dir, layout) = image();
fs::create_dir_all(layout.path(SENTINEL).parent().expect("parent")).expect("mkdir");
fs::create_dir_all(layout.path(BOOT_ID).parent().expect("parent")).expect("mkdir");
fs::write(layout.path(BOOT_ID), "boot-2\n").expect("write");
assert!(!layout.boot_complete(), "no sentinel yet");
fs::write(layout.path(SENTINEL), "boot-1\n").expect("write");
assert!(!layout.boot_complete(), "sentinel from the previous boot");
fs::write(layout.path(SENTINEL), "boot-2\n").expect("write");
assert!(layout.boot_complete());
}
#[test]
fn the_openrc_body_survives_format_escaping() {
let body = openrc_service_body();
assert!(body.contains("depend() {\n after *\n}"), "{body}");
assert!(body.contains("start() {\n"), "{body}");
assert!(!body.contains("{{") && !body.contains("}}"), "{body}");
}
#[test]
fn a_sentinel_from_another_boot_does_not_count() {
assert!(!sentinel_matches("stale-boot-id", "current-boot-id"));
}
#[test]
fn the_trailing_newline_the_hook_writes_is_tolerated() {
assert!(sentinel_matches("boot-id\n", "boot-id\n"));
}
#[test]
fn an_empty_boot_id_never_matches() {
assert!(!sentinel_matches("", ""));
assert!(!sentinel_matches("anything", ""));
}
}