From 24ff4f294ed1ace54f124dfc5eed74c364404032 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Fri, 10 Apr 2026 21:19:28 +0200 Subject: [PATCH 1/4] init: Start logd as a regular service --- init.d/00_logd.service | 7 +++++++ init.d/00_runtime.target | 1 + init/src/main.rs | 16 ++++++++-------- 3 files changed, 16 insertions(+), 8 deletions(-) create mode 100644 init.d/00_logd.service diff --git a/init.d/00_logd.service b/init.d/00_logd.service new file mode 100644 index 0000000000..b2293176c7 --- /dev/null +++ b/init.d/00_logd.service @@ -0,0 +1,7 @@ +[unit] +description = "Logger" +default_dependencies = false + +[service] +cmd = "logd" +type = { scheme = "log" } diff --git a/init.d/00_runtime.target b/init.d/00_runtime.target index 6a74498ec7..dee0f7217f 100644 --- a/init.d/00_runtime.target +++ b/init.d/00_runtime.target @@ -2,6 +2,7 @@ description = "Services that relibc needs to function" default_dependencies = false requires_weak = [ + "00_logd.service", "00_nulld.service", "00_randd.service", "00_rtcd.service", diff --git a/init/src/main.rs b/init/src/main.rs index 16c2b3a7ca..75940c4a5a 100644 --- a/init/src/main.rs +++ b/init/src/main.rs @@ -137,20 +137,20 @@ fn main() { Path::new("/scheme/initfs/etc"), ); + // Start logd first such that we can pass /scheme/log as stdio to all other services + scheduler + .schedule_start_and_report_errors(&mut unit_store, UnitId("00_logd.service".to_owned())); + scheduler.step(&mut unit_store, &mut init_config); + if let Err(err) = switch_stdio("/scheme/log") { + eprintln!("init: failed to switch stdio to '/scheme/log': {err}"); + } + let runtime_target = UnitId("00_runtime.target".to_owned()); scheduler.schedule_start_and_report_errors(&mut unit_store, runtime_target.clone()); unit_store.set_runtime_target(runtime_target); scheduler .schedule_start_and_report_errors(&mut unit_store, UnitId("90_initfs.target".to_owned())); - - let mut command = std::process::Command::new("logd"); - command.env_clear().envs(&init_config.envs); - daemon::SchemeDaemon::spawn(command, "log"); - if let Err(err) = switch_stdio("/scheme/log") { - eprintln!("init: failed to switch stdio to '/scheme/log': {err}"); - } - scheduler.step(&mut unit_store, &mut init_config); switch_root( From 8506ae5581539d6c4fdef70397a774332050c48a Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Fri, 10 Apr 2026 21:25:35 +0200 Subject: [PATCH 2/4] init: Move daemon spawning from daemon to init In preparation for concurrent daemon spawning. --- Cargo.lock | 4 +- daemon/src/lib.rs | 51 +-------------------- drivers/hwd/src/backend/acpi.rs | 1 + drivers/hwd/src/main.rs | 1 + drivers/pcid-spawner/src/main.rs | 1 + init/Cargo.toml | 4 +- init/src/service.rs | 79 ++++++++++++++++++++++++++++++-- 7 files changed, 87 insertions(+), 54 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 31330c563b..10e5fca241 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1085,8 +1085,10 @@ name = "init" version = "0.1.0" dependencies = [ "config", - "daemon", + "libc", "libredox", + "plain", + "redox_syscall 0.7.3", "serde", "serde_json", "toml", diff --git a/daemon/src/lib.rs b/daemon/src/lib.rs index 36376a05bc..9f50722141 100644 --- a/daemon/src/lib.rs +++ b/daemon/src/lib.rs @@ -55,6 +55,8 @@ impl Daemon { } /// Executes `Command` as a child process. + // FIXME remove once the service spawning of hwd and pcid-spawner is moved to init + #[deprecated] pub fn spawn(mut cmd: Command) { let (mut read_pipe, write_pipe) = io::pipe().unwrap(); @@ -124,53 +126,4 @@ impl SchemeDaemon { let cap_fd = socket.create_this_scheme_fd(0, cap_id, 0, 0)?; self.ready_with_fd(Fd::new(cap_fd)) } - - /// Executes `Command` as a child process registering it to the scheme namespace. - pub fn spawn(mut cmd: Command, scheme_name: &str) { - let (read_pipe, write_pipe) = io::pipe().unwrap(); - - unsafe { pass_fd(&mut cmd, "INIT_NOTIFY", write_pipe.into()) }; - - if let Err(err) = cmd.spawn() { - eprintln!("daemon: failed to execute {cmd:?}: {err}"); - return; - } - - let mut new_fd = usize::MAX; - let fd_bytes = unsafe { - core::slice::from_raw_parts_mut( - core::slice::from_mut(&mut new_fd).as_mut_ptr() as *mut u8, - core::mem::size_of::(), - ) - }; - loop { - match syscall::call_ro( - read_pipe.as_raw_fd() as usize, - fd_bytes, - syscall::CallFlags::FD | syscall::CallFlags::FD_UPPER, - &[], - ) { - Err(syscall::Error { - errno: syscall::EINTR, - }) => continue, - Ok(0) => { - eprintln!("daemon: {cmd:?} exited without notifying readiness"); - return; - } - Ok(1) => break, - Ok(n) => { - eprintln!("daemon: incorrect amount of fds {n} returned"); - return; - } - Err(err) => { - eprintln!("daemon: failed to wait for {cmd:?}: {err}"); - return; - } - } - } - - let current_namespace_fd = libredox::call::getns().expect("TODO"); - libredox::call::register_scheme_to_ns(current_namespace_fd, scheme_name, new_fd) - .expect("TODO"); - } } diff --git a/drivers/hwd/src/backend/acpi.rs b/drivers/hwd/src/backend/acpi.rs index e368b73124..3da41d6382 100644 --- a/drivers/hwd/src/backend/acpi.rs +++ b/drivers/hwd/src/backend/acpi.rs @@ -13,6 +13,7 @@ impl Backend for AcpiBackend { // Spawn acpid //TODO: pass rxsdt data to acpid? + #[allow(deprecated, reason = "we can't yet move this to init")] daemon::Daemon::spawn(Command::new("acpid")); Ok(Self { rxsdt }) diff --git a/drivers/hwd/src/main.rs b/drivers/hwd/src/main.rs index 43ef475d96..79360e34ee 100644 --- a/drivers/hwd/src/main.rs +++ b/drivers/hwd/src/main.rs @@ -37,6 +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 + #[allow(deprecated, reason = "we can't yet move this to init")] daemon::Daemon::spawn(process::Command::new("pcid")); daemon.ready(); diff --git a/drivers/pcid-spawner/src/main.rs b/drivers/pcid-spawner/src/main.rs index fca15b5865..a968f4d494 100644 --- a/drivers/pcid-spawner/src/main.rs +++ b/drivers/pcid-spawner/src/main.rs @@ -92,6 +92,7 @@ fn main() -> Result<()> { let channel_fd = handle.into_inner_fd(); command.env("PCID_CLIENT_CHANNEL", channel_fd.to_string()); + #[allow(deprecated, reason = "we can't yet move this to init")] daemon::Daemon::spawn(command); syscall::close(channel_fd as usize).unwrap(); } diff --git a/init/Cargo.toml b/init/Cargo.toml index 0a5bde7248..b0ae14a873 100644 --- a/init/Cargo.toml +++ b/init/Cargo.toml @@ -6,13 +6,15 @@ edition = "2024" license = "MIT" [dependencies] +libc.workspace = true libredox.workspace = true +plain.workspace = true +redox_syscall.workspace = true serde.workspace = true serde_json.workspace = true toml.workspace = true config = { path = "../config" } -daemon = { path = "../daemon" } [lints] workspace = true diff --git a/init/src/service.rs b/init/src/service.rs index e0b1e682db..e27f08b93c 100644 --- a/init/src/service.rs +++ b/init/src/service.rs @@ -1,7 +1,10 @@ use std::collections::{BTreeMap, BTreeSet}; -use std::env; use std::ffi::OsString; +use std::io::Read; +use std::os::fd::{AsRawFd, OwnedFd}; +use std::os::unix::process::CommandExt; use std::process::Command; +use std::{env, io}; use serde::Deserialize; @@ -45,10 +48,66 @@ impl Service { match &self.type_ { ServiceType::Notify => { - daemon::Daemon::spawn(command); + let (mut read_pipe, write_pipe) = io::pipe().unwrap(); + + unsafe { pass_fd(&mut command, "INIT_NOTIFY", write_pipe.into()) }; + + if let Err(err) = command.spawn() { + eprintln!("init: failed to execute {command:?}: {err}"); + return; + } + + let mut data = [0]; + match read_pipe.read_exact(&mut data) { + Ok(()) => {} + Err(err) if err.kind() == io::ErrorKind::UnexpectedEof => { + eprintln!("init: {command:?} exited without notifying readiness"); + } + Err(err) => { + eprintln!("init: failed to wait for {command:?}: {err}"); + } + } } ServiceType::Scheme(scheme) => { - daemon::SchemeDaemon::spawn(command, &scheme); + let (read_pipe, write_pipe) = io::pipe().unwrap(); + + unsafe { pass_fd(&mut command, "INIT_NOTIFY", write_pipe.into()) }; + + if let Err(err) = command.spawn() { + eprintln!("init: failed to execute {command:?}: {err}"); + return; + } + + let mut new_fd = usize::MAX; + loop { + match syscall::call_ro( + read_pipe.as_raw_fd() as usize, + unsafe { plain::as_mut_bytes(&mut new_fd) }, + syscall::CallFlags::FD | syscall::CallFlags::FD_UPPER, + &[], + ) { + Err(syscall::Error { + errno: syscall::EINTR, + }) => continue, + Ok(0) => { + eprintln!("init: {command:?} exited without notifying readiness"); + return; + } + Ok(1) => break, + Ok(n) => { + eprintln!("init: incorrect amount of fds {n} returned"); + return; + } + Err(err) => { + eprintln!("init: failed to wait for {command:?}: {err}"); + return; + } + } + } + + let current_namespace_fd = libredox::call::getns().expect("TODO"); + libredox::call::register_scheme_to_ns(current_namespace_fd, scheme, new_fd) + .expect("TODO"); } ServiceType::Oneshot => { let mut child = match command.spawn() { @@ -76,3 +135,17 @@ impl Service { } } } + +unsafe fn pass_fd(cmd: &mut Command, env: &str, fd: OwnedFd) { + cmd.env(env, format!("{}", fd.as_raw_fd())); + unsafe { + cmd.pre_exec(move || { + // Pass notify pipe to child + if libc::fcntl(fd.as_raw_fd(), libc::F_SETFD, 0) == -1 { + Err(io::Error::last_os_error()) + } else { + Ok(()) + } + }); + } +} From 02b074f2e6ff834cb89fa7733a9cca7b2983642b Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Fri, 10 Apr 2026 21:56:08 +0200 Subject: [PATCH 3/4] init: Unify child spawning calls --- init/src/service.rs | 59 +++++++++++++++------------------------------ 1 file changed, 20 insertions(+), 39 deletions(-) diff --git a/init/src/service.rs b/init/src/service.rs index e27f08b93c..9f53253ee6 100644 --- a/init/src/service.rs +++ b/init/src/service.rs @@ -46,38 +46,28 @@ impl Service { } command.envs(base_envs).envs(&self.envs); - match &self.type_ { - ServiceType::Notify => { - let (mut read_pipe, write_pipe) = io::pipe().unwrap(); + let (mut read_pipe, write_pipe) = io::pipe().unwrap(); + unsafe { pass_fd(&mut command, "INIT_NOTIFY", write_pipe.into()) }; - unsafe { pass_fd(&mut command, "INIT_NOTIFY", write_pipe.into()) }; - - if let Err(err) = command.spawn() { - eprintln!("init: failed to execute {command:?}: {err}"); - return; - } - - let mut data = [0]; - match read_pipe.read_exact(&mut data) { - Ok(()) => {} - Err(err) if err.kind() == io::ErrorKind::UnexpectedEof => { - eprintln!("init: {command:?} exited without notifying readiness"); - } - Err(err) => { - eprintln!("init: failed to wait for {command:?}: {err}"); - } - } + let mut child = match command.spawn() { + Ok(child) => child, + Err(err) => { + eprintln!("init: failed to execute {:?}: {}", command, err); + return; } - ServiceType::Scheme(scheme) => { - let (read_pipe, write_pipe) = io::pipe().unwrap(); + }; - unsafe { pass_fd(&mut command, "INIT_NOTIFY", write_pipe.into()) }; - - if let Err(err) = command.spawn() { - eprintln!("init: failed to execute {command:?}: {err}"); - return; + match &self.type_ { + ServiceType::Notify => match read_pipe.read_exact(&mut [0]) { + Ok(()) => {} + Err(err) if err.kind() == io::ErrorKind::UnexpectedEof => { + eprintln!("init: {command:?} exited without notifying readiness"); } - + Err(err) => { + eprintln!("init: failed to wait for {command:?}: {err}"); + } + }, + ServiceType::Scheme(scheme) => { let mut new_fd = usize::MAX; loop { match syscall::call_ro( @@ -110,13 +100,7 @@ impl Service { .expect("TODO"); } ServiceType::Oneshot => { - let mut child = match command.spawn() { - Ok(child) => child, - Err(err) => { - eprintln!("init: failed to execute {:?}: {}", command, err); - return; - } - }; + drop(read_pipe); match child.wait() { Ok(exit_status) => { if !exit_status.success() { @@ -128,10 +112,7 @@ impl Service { } } } - ServiceType::OneshotAsync => match command.spawn() { - Ok(_child) => {} - Err(err) => eprintln!("init: failed to execute '{:?}': {}", command, err), - }, + ServiceType::OneshotAsync => {} } } } From 853a26918f15ee9a5f93a8a485c20b1f5f7fe99a Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Fri, 10 Apr 2026 22:13:12 +0200 Subject: [PATCH 4/4] init: Move initfs configs to init.initfs.d This frees up init.d for init configs on the rootfs. --- {init.d => init.initfs.d}/00_logd.service | 0 {init.d => init.initfs.d}/00_nulld.service | 0 {init.d => init.initfs.d}/00_randd.service | 0 {init.d => init.initfs.d}/00_rtcd.service | 0 {init.d => init.initfs.d}/00_runtime.target | 0 {init.d => init.initfs.d}/00_zerod.service | 0 {init.d => init.initfs.d}/10_inputd.service | 0 {init.d => init.initfs.d}/10_lived.service | 0 {init.d => init.initfs.d}/20_fbbootlogd.service | 0 {init.d => init.initfs.d}/20_fbcond.service | 0 {init.d => init.initfs.d}/20_graphics.target | 0 {init.d => init.initfs.d}/20_vesad.service | 0 {init.d => init.initfs.d}/40_bcm2835-sdhcid.service | 0 {init.d => init.initfs.d}/40_drivers.target | 0 {init.d => init.initfs.d}/40_hwd.service | 0 {init.d => init.initfs.d}/40_pcid-spawner.service | 0 {init.d => init.initfs.d}/40_ps2d.service | 0 {init.d => init.initfs.d}/50_rootfs.service | 0 {init.d => init.initfs.d}/90_initfs.target | 0 {init.d => init.initfs.d}/ramfs@.service | 0 20 files changed, 0 insertions(+), 0 deletions(-) rename {init.d => init.initfs.d}/00_logd.service (100%) rename {init.d => init.initfs.d}/00_nulld.service (100%) rename {init.d => init.initfs.d}/00_randd.service (100%) rename {init.d => init.initfs.d}/00_rtcd.service (100%) rename {init.d => init.initfs.d}/00_runtime.target (100%) rename {init.d => init.initfs.d}/00_zerod.service (100%) rename {init.d => init.initfs.d}/10_inputd.service (100%) rename {init.d => init.initfs.d}/10_lived.service (100%) rename {init.d => init.initfs.d}/20_fbbootlogd.service (100%) rename {init.d => init.initfs.d}/20_fbcond.service (100%) rename {init.d => init.initfs.d}/20_graphics.target (100%) rename {init.d => init.initfs.d}/20_vesad.service (100%) rename {init.d => init.initfs.d}/40_bcm2835-sdhcid.service (100%) rename {init.d => init.initfs.d}/40_drivers.target (100%) rename {init.d => init.initfs.d}/40_hwd.service (100%) rename {init.d => init.initfs.d}/40_pcid-spawner.service (100%) rename {init.d => init.initfs.d}/40_ps2d.service (100%) rename {init.d => init.initfs.d}/50_rootfs.service (100%) rename {init.d => init.initfs.d}/90_initfs.target (100%) rename {init.d => init.initfs.d}/ramfs@.service (100%) diff --git a/init.d/00_logd.service b/init.initfs.d/00_logd.service similarity index 100% rename from init.d/00_logd.service rename to init.initfs.d/00_logd.service diff --git a/init.d/00_nulld.service b/init.initfs.d/00_nulld.service similarity index 100% rename from init.d/00_nulld.service rename to init.initfs.d/00_nulld.service diff --git a/init.d/00_randd.service b/init.initfs.d/00_randd.service similarity index 100% rename from init.d/00_randd.service rename to init.initfs.d/00_randd.service diff --git a/init.d/00_rtcd.service b/init.initfs.d/00_rtcd.service similarity index 100% rename from init.d/00_rtcd.service rename to init.initfs.d/00_rtcd.service diff --git a/init.d/00_runtime.target b/init.initfs.d/00_runtime.target similarity index 100% rename from init.d/00_runtime.target rename to init.initfs.d/00_runtime.target diff --git a/init.d/00_zerod.service b/init.initfs.d/00_zerod.service similarity index 100% rename from init.d/00_zerod.service rename to init.initfs.d/00_zerod.service diff --git a/init.d/10_inputd.service b/init.initfs.d/10_inputd.service similarity index 100% rename from init.d/10_inputd.service rename to init.initfs.d/10_inputd.service diff --git a/init.d/10_lived.service b/init.initfs.d/10_lived.service similarity index 100% rename from init.d/10_lived.service rename to init.initfs.d/10_lived.service diff --git a/init.d/20_fbbootlogd.service b/init.initfs.d/20_fbbootlogd.service similarity index 100% rename from init.d/20_fbbootlogd.service rename to init.initfs.d/20_fbbootlogd.service diff --git a/init.d/20_fbcond.service b/init.initfs.d/20_fbcond.service similarity index 100% rename from init.d/20_fbcond.service rename to init.initfs.d/20_fbcond.service diff --git a/init.d/20_graphics.target b/init.initfs.d/20_graphics.target similarity index 100% rename from init.d/20_graphics.target rename to init.initfs.d/20_graphics.target diff --git a/init.d/20_vesad.service b/init.initfs.d/20_vesad.service similarity index 100% rename from init.d/20_vesad.service rename to init.initfs.d/20_vesad.service diff --git a/init.d/40_bcm2835-sdhcid.service b/init.initfs.d/40_bcm2835-sdhcid.service similarity index 100% rename from init.d/40_bcm2835-sdhcid.service rename to init.initfs.d/40_bcm2835-sdhcid.service diff --git a/init.d/40_drivers.target b/init.initfs.d/40_drivers.target similarity index 100% rename from init.d/40_drivers.target rename to init.initfs.d/40_drivers.target diff --git a/init.d/40_hwd.service b/init.initfs.d/40_hwd.service similarity index 100% rename from init.d/40_hwd.service rename to init.initfs.d/40_hwd.service diff --git a/init.d/40_pcid-spawner.service b/init.initfs.d/40_pcid-spawner.service similarity index 100% rename from init.d/40_pcid-spawner.service rename to init.initfs.d/40_pcid-spawner.service diff --git a/init.d/40_ps2d.service b/init.initfs.d/40_ps2d.service similarity index 100% rename from init.d/40_ps2d.service rename to init.initfs.d/40_ps2d.service diff --git a/init.d/50_rootfs.service b/init.initfs.d/50_rootfs.service similarity index 100% rename from init.d/50_rootfs.service rename to init.initfs.d/50_rootfs.service diff --git a/init.d/90_initfs.target b/init.initfs.d/90_initfs.target similarity index 100% rename from init.d/90_initfs.target rename to init.initfs.d/90_initfs.target diff --git a/init.d/ramfs@.service b/init.initfs.d/ramfs@.service similarity index 100% rename from init.d/ramfs@.service rename to init.initfs.d/ramfs@.service