From 6096fffe5da392a56a94a479b90141a9c292fec5 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Mon, 26 Jan 2026 21:51:14 +0100 Subject: [PATCH] Let init handle daemonization This way init can track the lifecycle of services in the future. It may also help with parallel booting in the future. --- Cargo.lock | 2 + aarch64-unknown-redox/init_drivers.rc | 2 +- .../init_drivers.rc.raspi3bp | 2 +- daemon/src/lib.rs | 69 +++++++++++-------- drivers/hwd/src/backend/acpi.rs | 2 +- drivers/hwd/src/main.rs | 16 +---- drivers/pcid-spawner/Cargo.toml | 1 + drivers/pcid-spawner/src/main.rs | 8 +-- init.rc | 20 +++--- init/Cargo.toml | 2 + init/src/main.rs | 36 ++++++++-- init_drivers.rc | 4 +- 12 files changed, 92 insertions(+), 72 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 74fc86b306..e98134bff1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1109,6 +1109,7 @@ dependencies = [ name = "init" version = "0.1.0" dependencies = [ + "daemon", "libredox", ] @@ -1490,6 +1491,7 @@ version = "0.1.0" dependencies = [ "anyhow", "common", + "daemon", "log", "pcid", "pico-args", diff --git a/aarch64-unknown-redox/init_drivers.rc b/aarch64-unknown-redox/init_drivers.rc index 81a8d14c22..eb8c0f1af5 100644 --- a/aarch64-unknown-redox/init_drivers.rc +++ b/aarch64-unknown-redox/init_drivers.rc @@ -1,2 +1,2 @@ -hwd +notify hwd pcid-spawner /etc/pcid/initfs.toml diff --git a/aarch64-unknown-redox/init_drivers.rc.raspi3bp b/aarch64-unknown-redox/init_drivers.rc.raspi3bp index ae9b17a456..2534ef0727 100644 --- a/aarch64-unknown-redox/init_drivers.rc.raspi3bp +++ b/aarch64-unknown-redox/init_drivers.rc.raspi3bp @@ -1 +1 @@ -bcm2835-sdhcid +notify bcm2835-sdhcid diff --git a/daemon/src/lib.rs b/daemon/src/lib.rs index 35cdb0ce2d..8967469d93 100644 --- a/daemon/src/lib.rs +++ b/daemon/src/lib.rs @@ -1,46 +1,59 @@ #![feature(never_type)] use std::io::{self, PipeWriter, Read, Write}; +use std::os::fd::{AsRawFd, FromRawFd}; +use std::process::Command; #[must_use = "Daemon::ready must be called"] pub struct Daemon { write_pipe: PipeWriter, } -fn errno() -> io::Error { - io::Error::last_os_error() -} - impl Daemon { pub fn new !>(f: F) -> ! { - let (mut read_pipe, write_pipe) = std::io::pipe().unwrap(); + let write_pipe = unsafe { + io::PipeWriter::from_raw_fd(std::env::var("INIT_NOTIFY").unwrap().parse().unwrap()) + }; - match unsafe { libc::fork() } { - 0 => { - drop(read_pipe); - - f(Daemon { write_pipe }) - } - -1 => return Err(errno()).unwrap(), - _pid => { - drop(write_pipe); - - let mut data = [0]; - - match read_pipe.read_exact(&mut data) { - Ok(()) => {} - Err(err) if err.kind() == io::ErrorKind::UnexpectedEof => { - unsafe { libc::_exit(101) }; - } - Err(err) => return Err(err).unwrap(), - }; - - unsafe { libc::_exit(data[0].into()) }; - } - } + f(Daemon { write_pipe }) } pub fn ready(mut self) { self.write_pipe.write_all(&[0]).unwrap(); } + + pub fn spawn(mut cmd: Command) { + let (mut read_pipe, write_pipe) = io::pipe().unwrap(); + + // Pass pipe to child + if unsafe { libc::fcntl(write_pipe.as_raw_fd(), libc::F_SETFD, 0) } == -1 { + eprintln!( + "daemon: failed to unset CLOEXEC flag for pipe: {}", + io::Error::last_os_error() + ); + return; + } + cmd.env("INIT_NOTIFY", format!("{}", write_pipe.as_raw_fd())); + + if let Err(err) = cmd.spawn() { + eprintln!("daemon: failed to execute {cmd:?}: {err}"); + return; + } + drop(write_pipe); + + let mut data = [0]; + match read_pipe.read_exact(&mut data) { + Ok(()) => { + if data[0] != 0 { + eprintln!("daemon: {cmd:?} failed with {}", data[0]); + } + } + Err(err) if err.kind() == io::ErrorKind::UnexpectedEof => { + eprintln!("daemon: {cmd:?} exited without notifying readiness"); + } + Err(err) => { + eprintln!("daemon: failed to wait for {cmd:?}: {err}"); + } + } + } } diff --git a/drivers/hwd/src/backend/acpi.rs b/drivers/hwd/src/backend/acpi.rs index 1d24bb6f83..e368b73124 100644 --- a/drivers/hwd/src/backend/acpi.rs +++ b/drivers/hwd/src/backend/acpi.rs @@ -13,7 +13,7 @@ impl Backend for AcpiBackend { // Spawn acpid //TODO: pass rxsdt data to acpid? - Command::new("acpid").spawn()?.wait()?; + daemon::Daemon::spawn(Command::new("acpid")); Ok(Self { rxsdt }) } diff --git a/drivers/hwd/src/main.rs b/drivers/hwd/src/main.rs index ab07522ccb..43ef475d96 100644 --- a/drivers/hwd/src/main.rs +++ b/drivers/hwd/src/main.rs @@ -37,21 +37,7 @@ fn daemon(daemon: daemon::Daemon) -> ! { //TODO: launch pcid based on backend information? // Must launch after acpid but before probe calls /scheme/acpi/symbols - match process::Command::new("pcid").spawn() { - Ok(mut child) => match child.wait() { - Ok(status) => { - if !status.success() { - log::error!("pcid exited with status {}", status); - } - } - Err(err) => { - log::error!("failed to wait for pcid: {}", err); - } - }, - Err(err) => { - log::error!("failed to spawn pcid: {}", err); - } - } + daemon::Daemon::spawn(process::Command::new("pcid")); daemon.ready(); diff --git a/drivers/pcid-spawner/Cargo.toml b/drivers/pcid-spawner/Cargo.toml index a180426a7f..fe9d4907bd 100644 --- a/drivers/pcid-spawner/Cargo.toml +++ b/drivers/pcid-spawner/Cargo.toml @@ -14,4 +14,5 @@ serde = { version = "1", features = ["derive"] } toml = "0.5" common = { path = "../common" } +daemon = { path = "../../daemon" } pcid = { path = "../pcid" } diff --git a/drivers/pcid-spawner/src/main.rs b/drivers/pcid-spawner/src/main.rs index f57cb50f92..9a22d72ef4 100644 --- a/drivers/pcid-spawner/src/main.rs +++ b/drivers/pcid-spawner/src/main.rs @@ -88,13 +88,7 @@ fn main() -> Result<()> { let channel_fd = handle.into_inner_fd(); command.env("PCID_CLIENT_CHANNEL", channel_fd.to_string()); - match command.status() { - Ok(status) if !status.success() => { - log::error!("pcid-spawner: driver {command:?} failed with {status}") - } - Ok(_) => {} - Err(err) => log::error!("pcid-spawner: failed to execute {command:?}: {err}"), - } + daemon::Daemon::spawn(command); syscall::close(channel_fd as usize).unwrap(); } diff --git a/init.rc b/init.rc index 6e59732df0..598c613450 100644 --- a/init.rc +++ b/init.rc @@ -3,26 +3,26 @@ export PATH /scheme/initfs/bin export RUST_BACKTRACE 1 rtcd -nulld -zerod -randd +notify nulld +notify zerod +notify randd # Logging -logd +notify logd stdio /scheme/log -ramfs logging +notify ramfs logging # Graphics infrastructure -inputd -vesad +notify inputd +notify vesad unset FRAMEBUFFER_ADDR FRAMEBUFFER_VIRT FRAMEBUFFER_WIDTH FRAMEBUFFER_HEIGHT FRAMEBUFFER_STRIDE #TODO: unset FRAMEBUFFER1 and beyond? -fbbootlogd -fbcond 2 +notify fbbootlogd +notify fbcond 2 # Live disk # Note: Needs to start before drivers to ensure it gets priority when redoxfs searches for disks -lived +notify lived # Drivers run /scheme/initfs/etc/init_drivers.rc diff --git a/init/Cargo.toml b/init/Cargo.toml index e053e8dae4..d82425bc60 100644 --- a/init/Cargo.toml +++ b/init/Cargo.toml @@ -6,3 +6,5 @@ license = "MIT" [dependencies] libredox.workspace = true + +daemon = { path = "../daemon" } diff --git a/init/src/main.rs b/init/src/main.rs index e963a882ea..dca3ba9382 100644 --- a/init/src/main.rs +++ b/init/src/main.rs @@ -20,12 +20,12 @@ fn switch_stdio(stdio: &str) -> Result<()> { } struct InitConfig { - pub log_debug: bool, - pub skip_cmd: Vec, + log_debug: bool, + skip_cmd: Vec, } impl InitConfig { - pub fn new() -> Self { + fn new() -> Self { let log_level = env::var("INIT_LOG_LEVEL").unwrap_or("INFO".into()); let log_debug = matches!(log_level.as_str(), "DEBUG" | "TRACE"); let skip_cmd: Vec = match env::var("INIT_SKIP") { @@ -40,7 +40,7 @@ impl InitConfig { } } -pub fn run(file: &Path, config: &InitConfig) -> Result<()> { +fn run(file: &Path, config: &InitConfig) -> Result<()> { for line_raw in fs::read_to_string(file)?.lines() { let line = line_raw.trim(); if line.is_empty() || line.starts_with('#') { @@ -167,6 +167,11 @@ fn run_command(line: &str, config: &InitConfig) { eprintln!("init: failed to run nowait: no argument"); return; }; + if config.skip_cmd.contains(&cmd) { + eprintln!("init: skipping '{}'", line); + return; + } + let mut command = Command::new(cmd); for arg in args { @@ -178,17 +183,34 @@ fn run_command(line: &str, config: &InitConfig) { Err(err) => eprintln!("init: failed to execute '{}': {}", line, err), } } - _ => { - let mut command = Command::new(cmd.clone()); + "notify" => { + let Some(cmd) = args.next() else { + eprintln!("init: failed to run nowait: no argument"); + return; + }; + if config.skip_cmd.contains(&cmd) { + eprintln!("init: skipping '{}'", line); + return; + } + + let mut command = Command::new(&cmd); for arg in args { command.arg(arg); } + daemon::Daemon::spawn(command); + } + _ => { if config.skip_cmd.contains(&cmd) { eprintln!("init: skipping '{}'", line); return; } + let mut command = Command::new(cmd.clone()); + for arg in args { + command.arg(arg); + } + let mut child = match command.spawn() { Ok(child) => child, Err(err) => { @@ -211,7 +233,7 @@ fn run_command(line: &str, config: &InitConfig) { } } -pub fn main() { +fn main() { let init_path = Path::new("/scheme/initfs/etc/init.rc"); let init_config = InitConfig::new(); if let Err(err) = run(&init_path, &init_config) { diff --git a/init_drivers.rc b/init_drivers.rc index 4ccdf61475..291cf82fd3 100644 --- a/init_drivers.rc +++ b/init_drivers.rc @@ -1,3 +1,3 @@ -ps2d us -hwd +notify ps2d us +notify hwd pcid-spawner /scheme/initfs/etc/pcid/initfs.toml