From 347287a3ed17a3d20943fae8fbd523965febf4d6 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Sat, 11 Apr 2026 12:28:24 +0200 Subject: [PATCH 1/6] init: Move run and run_command to the scheduler --- init/src/main.rs | 65 ++----------------------------------------- init/src/scheduler.rs | 62 ++++++++++++++++++++++++++++++++++++++--- 2 files changed, 61 insertions(+), 66 deletions(-) diff --git a/init/src/main.rs b/init/src/main.rs index 75940c4a5a..b917844863 100644 --- a/init/src/main.rs +++ b/init/src/main.rs @@ -1,21 +1,19 @@ use std::collections::BTreeMap; -use std::env; use std::ffi::OsString; -use std::io::Result; use std::path::Path; +use std::{env, io}; use libredox::flag::{O_RDONLY, O_WRONLY}; use crate::scheduler::Scheduler; -use crate::script::Command; -use crate::unit::{Unit, UnitId, UnitStore}; +use crate::unit::{UnitId, UnitStore}; mod scheduler; mod script; mod service; mod unit; -fn switch_stdio(stdio: &str) -> Result<()> { +fn switch_stdio(stdio: &str) -> io::Result<()> { let stdin = libredox::Fd::open(stdio, O_RDONLY, 0)?; let stdout = libredox::Fd::open(stdio, O_WRONLY, 0)?; let stderr = libredox::Fd::open(stdio, O_WRONLY, 0)?; @@ -68,63 +66,6 @@ fn switch_root(unit_store: &mut UnitStore, config: &mut InitConfig, prefix: &Pat unit_store.config_dirs = vec![prefix.join("lib").join("init.d"), etcdir.join("init.d")]; } -fn run(unit: &mut Unit, config: &mut InitConfig) -> Result<()> { - match &unit.kind { - unit::UnitKind::LegacyScript { script } => { - for cmd in script.0.clone() { - if config.log_debug { - eprintln!("init: running: {cmd:?}"); - } - run_command(cmd, config); - } - } - unit::UnitKind::Service { service } => { - if config.skip_cmd.contains(&service.cmd) { - eprintln!("Skipping '{} {}'", service.cmd, service.args.join(" ")); - return Ok(()); - } - if config.log_debug { - eprintln!( - "Starting {} ({})", - unit.info.description.as_ref().unwrap_or(&unit.id.0), - service.cmd, - ); - } - service.spawn(&config.envs); - } - unit::UnitKind::Target {} => { - if config.log_debug { - eprintln!( - "Reached target {}", - unit.info.description.as_ref().unwrap_or(&unit.id.0), - ); - } - } - } - - Ok(()) -} - -fn run_command(cmd: Command, config: &mut InitConfig) { - match cmd { - Command::RequiresWeak(_) => {} // handled by unit parsing code - Command::Nothing => {} - Command::Echo(text) => println!("{text}"), - Command::Service(service) => { - if config.skip_cmd.contains(&service.cmd) { - eprintln!( - "init: skipping '{} {}'", - service.cmd, - service.args.join(" ") - ); - return; - } - - service.spawn(&config.envs); - } - } -} - fn main() { let mut init_config = InitConfig::new(); let mut unit_store = UnitStore::new(); diff --git a/init/src/scheduler.rs b/init/src/scheduler.rs index 04918aff25..a5cc0ca6f0 100644 --- a/init/src/scheduler.rs +++ b/init/src/scheduler.rs @@ -1,7 +1,8 @@ use std::collections::VecDeque; use crate::InitConfig; -use crate::unit::{UnitId, UnitStore}; +use crate::script::Command; +use crate::unit::{Unit, UnitId, UnitKind, UnitStore}; pub struct Scheduler { pending: VecDeque, @@ -73,11 +74,64 @@ impl Scheduler { } } - if let Err(err) = crate::run(unit, init_config) { - eprintln!("init: failed to run {}: {}", job.unit.0, err); - } + run(unit, init_config); } } } } } + +fn run(unit: &mut Unit, config: &mut InitConfig) { + match &unit.kind { + UnitKind::LegacyScript { script } => { + for cmd in script.0.clone() { + if config.log_debug { + eprintln!("init: running: {cmd:?}"); + } + run_command(cmd, config); + } + } + UnitKind::Service { service } => { + if config.skip_cmd.contains(&service.cmd) { + eprintln!("Skipping '{} {}'", service.cmd, service.args.join(" ")); + return; + } + if config.log_debug { + eprintln!( + "Starting {} ({})", + unit.info.description.as_ref().unwrap_or(&unit.id.0), + service.cmd, + ); + } + service.spawn(&config.envs); + } + UnitKind::Target {} => { + if config.log_debug { + eprintln!( + "Reached target {}", + unit.info.description.as_ref().unwrap_or(&unit.id.0), + ); + } + } + } +} + +fn run_command(cmd: Command, config: &mut InitConfig) { + match cmd { + Command::RequiresWeak(_) => {} // handled by unit parsing code + Command::Nothing => {} + Command::Echo(text) => println!("{text}"), + Command::Service(service) => { + if config.skip_cmd.contains(&service.cmd) { + eprintln!( + "init: skipping '{} {}'", + service.cmd, + service.args.join(" ") + ); + return; + } + + service.spawn(&config.envs); + } + } +} From 44920aef59d6643d4593a02dcfb1ab9b24d05308 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Sat, 11 Apr 2026 15:23:28 +0200 Subject: [PATCH 2/6] init: Move audiod service definition into this repo And convert it into a scheme daemon. --- audiod/src/main.rs | 18 +++++------------- init.d/20_audiod.service | 9 +++++++++ 2 files changed, 14 insertions(+), 13 deletions(-) create mode 100644 init.d/20_audiod.service diff --git a/audiod/src/main.rs b/audiod/src/main.rs index 0fa913d69f..be7d5e1e2b 100644 --- a/audiod/src/main.rs +++ b/audiod/src/main.rs @@ -9,11 +9,10 @@ use ioslice::IoSlice; use libredox::flag; use libredox::{error::Result, Fd}; -use redox_scheme::scheme::register_sync_scheme; use redox_scheme::wrappers::ReadinessBased; use redox_scheme::Socket; -use daemon::Daemon; +use daemon::SchemeDaemon; use self::scheme::AudioScheme; @@ -35,7 +34,7 @@ fn thread(scheme: Arc>, pid: usize, hw_file: Fd) -> Result<() } } -fn daemon(daemon: Daemon) -> anyhow::Result<()> { +fn daemon(daemon: SchemeDaemon) -> anyhow::Result<()> { // Handle signals from the hw thread let new_sigaction = unsafe { @@ -55,14 +54,7 @@ fn daemon(daemon: Daemon) -> anyhow::Result<()> { let scheme = Arc::new(Mutex::new(AudioScheme::new())); - { - let mut scheme = scheme.lock().unwrap(); - register_sync_scheme(&socket, "audio", &mut *scheme) - .expect("audiod: failed to register scheme to namespace"); - } - - // The scheme is now ready to accept requests, notify the original process - daemon.ready(); + let _ = daemon.ready_sync_scheme(&socket, &mut *scheme.lock().unwrap()); // Enter a constrained namespace let ns = libredox::call::mkns(&[ @@ -93,10 +85,10 @@ fn daemon(daemon: Daemon) -> anyhow::Result<()> { } fn main() { - Daemon::new(inner); + SchemeDaemon::new(inner); } -fn inner(x: Daemon) -> ! { +fn inner(x: SchemeDaemon) -> ! { match daemon(x) { Ok(()) => { process::exit(0); diff --git a/init.d/20_audiod.service b/init.d/20_audiod.service new file mode 100644 index 0000000000..68332093d3 --- /dev/null +++ b/init.d/20_audiod.service @@ -0,0 +1,9 @@ +[unit] +description = "Audio multiplexer" +requires_weak = [ + "00_base.target", +] + +[service] +cmd = "audiod" +type = { scheme = "audio" } From 2e6cf9df6870e224785f7e6b5177c1339f456284 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Sat, 11 Apr 2026 15:03:09 +0200 Subject: [PATCH 3/6] init: Remove notify and scheme support from legacy init scripts --- init/src/script.rs | 26 -------------------------- init/src/service.rs | 2 +- 2 files changed, 1 insertion(+), 27 deletions(-) diff --git a/init/src/script.rs b/init/src/script.rs index 0f804f21a5..87e9f0523c 100644 --- a/init/src/script.rs +++ b/init/src/script.rs @@ -58,32 +58,6 @@ impl Command { match cmd.as_str() { "requires_weak" => Ok(Command::RequiresWeak(args.map(UnitId).collect::>())), "echo" => Ok(Command::Echo(args.collect::>().join(" "))), - "notify" => { - let process = Process::parse(args)?; - - Ok(Command::Service(Service { - cmd: process.cmd, - args: process.args, - envs: process.envs, - inherit_envs: BTreeSet::new(), - type_: ServiceType::Notify, - })) - } - "scheme" => { - let Some(scheme) = args.next() else { - return Err("init: failed to run scheme: no argument".to_owned()); - }; - - let process = Process::parse(args)?; - - Ok(Command::Service(Service { - cmd: process.cmd, - args: process.args, - envs: process.envs, - inherit_envs: BTreeSet::new(), - type_: ServiceType::Scheme(scheme), - })) - } "nowait" => { let process = Process::parse(args)?; diff --git a/init/src/service.rs b/init/src/service.rs index 9f53253ee6..ed0023e921 100644 --- a/init/src/service.rs +++ b/init/src/service.rs @@ -104,7 +104,7 @@ impl Service { match child.wait() { Ok(exit_status) => { if !exit_status.success() { - eprintln!("{command:?} failed with {exit_status}"); + eprintln!("init: {command:?} failed with {exit_status}"); } } Err(err) => { From baaddd7ccf3cb37d915820599bc3fb8aaf3a72d6 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Sat, 11 Apr 2026 15:39:23 +0200 Subject: [PATCH 4/6] init: Support reading env vars from /usr/lib/environment.d --- init/src/main.rs | 48 +++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 47 insertions(+), 1 deletion(-) diff --git a/init/src/main.rs b/init/src/main.rs index b917844863..5682cf4455 100644 --- a/init/src/main.rs +++ b/init/src/main.rs @@ -1,7 +1,7 @@ use std::collections::BTreeMap; use std::ffi::OsString; use std::path::Path; -use std::{env, io}; +use std::{env, fs, io}; use libredox::flag::{O_RDONLY, O_WRONLY}; @@ -64,6 +64,52 @@ fn switch_root(unit_store: &mut UnitStore, config: &mut InitConfig, prefix: &Pat ); unit_store.config_dirs = vec![prefix.join("lib").join("init.d"), etcdir.join("init.d")]; + + let env_dirs = &[ + prefix.join("lib").join("environment.d"), + etcdir.join("environment.d"), + ]; + match config::config_for_dirs(env_dirs) { + Ok(files) => { + for file in files { + match fs::read_to_string(&file) { + Ok(envs) => { + for env in envs.lines() { + if env.is_empty() || env.starts_with("#") { + continue; + } + let Some((key, value)) = env.split_once('=') else { + eprintln!( + "init: failed to parse env line from {}: {env:?}", + file.display(), + ); + continue; + }; + config + .envs + .insert(key.to_owned().into(), value.to_owned().into()); + } + } + Err(err) => { + eprintln!( + "init: failed to read environment from {}: {err}", + file.display(), + ); + } + } + } + } + Err(err) => { + eprintln!( + "init: failed to read environments from {}: {err}", + env_dirs + .iter() + .map(|dir| dir.display().to_string()) + .collect::>() + .join(", ") + ); + } + } } fn main() { From dd1c590131ca4500768dade3a558607f71d7ac18 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Sat, 11 Apr 2026 15:45:20 +0200 Subject: [PATCH 5/6] init: Remove echo special case from legacy init scripts The standalone echo command works fine too. --- init/src/scheduler.rs | 1 - init/src/script.rs | 2 -- 2 files changed, 3 deletions(-) diff --git a/init/src/scheduler.rs b/init/src/scheduler.rs index a5cc0ca6f0..133835d4f6 100644 --- a/init/src/scheduler.rs +++ b/init/src/scheduler.rs @@ -120,7 +120,6 @@ fn run_command(cmd: Command, config: &mut InitConfig) { match cmd { Command::RequiresWeak(_) => {} // handled by unit parsing code Command::Nothing => {} - Command::Echo(text) => println!("{text}"), Command::Service(service) => { if config.skip_cmd.contains(&service.cmd) { eprintln!( diff --git a/init/src/script.rs b/init/src/script.rs index 87e9f0523c..83b3518405 100644 --- a/init/src/script.rs +++ b/init/src/script.rs @@ -45,7 +45,6 @@ pub enum Command { Service(Service), // Misc - Echo(String), Nothing, } @@ -57,7 +56,6 @@ impl Command { match cmd.as_str() { "requires_weak" => Ok(Command::RequiresWeak(args.map(UnitId).collect::>())), - "echo" => Ok(Command::Echo(args.collect::>().join(" "))), "nowait" => { let process = Process::parse(args)?; From ee8109fe1d1822ca9a2f0ae6347efb663ecc11ac Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Sat, 11 Apr 2026 15:51:54 +0200 Subject: [PATCH 6/6] init: Move some legacy script complexity into Script::from_str --- init/src/scheduler.rs | 4 +--- init/src/script.rs | 37 +++++++++++++++++++------------------ init/src/unit.rs | 13 +++---------- 3 files changed, 23 insertions(+), 31 deletions(-) diff --git a/init/src/scheduler.rs b/init/src/scheduler.rs index 133835d4f6..670a552620 100644 --- a/init/src/scheduler.rs +++ b/init/src/scheduler.rs @@ -84,7 +84,7 @@ impl Scheduler { fn run(unit: &mut Unit, config: &mut InitConfig) { match &unit.kind { UnitKind::LegacyScript { script } => { - for cmd in script.0.clone() { + for cmd in script.clone() { if config.log_debug { eprintln!("init: running: {cmd:?}"); } @@ -118,8 +118,6 @@ fn run(unit: &mut Unit, config: &mut InitConfig) { fn run_command(cmd: Command, config: &mut InitConfig) { match cmd { - Command::RequiresWeak(_) => {} // handled by unit parsing code - Command::Nothing => {} Command::Service(service) => { if config.skip_cmd.contains(&service.cmd) { eprintln!( diff --git a/init/src/script.rs b/init/src/script.rs index 83b3518405..7db1f2ae38 100644 --- a/init/src/script.rs +++ b/init/src/script.rs @@ -12,11 +12,12 @@ pub fn subst_env<'a>(arg: &str) -> String { } } -pub struct Script(pub Vec); +pub struct Script(pub Vec, pub Vec); impl Script { pub fn from_str(config: &str, errors: &mut Vec) -> io::Result