Merge branch 'init_rework11' into 'main'

Move audiod service definition into this repo and more init refactorings

See merge request redox-os/base!220
This commit is contained in:
Jeremy Soller
2026-04-11 08:11:48 -06:00
7 changed files with 134 additions and 129 deletions
+5 -13
View File
@@ -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<Mutex<AudioScheme>>, 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);
+9
View File
@@ -0,0 +1,9 @@
[unit]
description = "Audio multiplexer"
requires_weak = [
"00_base.target",
]
[service]
cmd = "audiod"
type = { scheme = "audio" }
+43 -56
View File
@@ -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, fs, 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)?;
@@ -66,61 +64,50 @@ 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:?}");
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(),
);
}
}
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);
Err(err) => {
eprintln!(
"init: failed to read environments from {}: {err}",
env_dirs
.iter()
.map(|dir| dir.display().to_string())
.collect::<Vec<_>>()
.join(", ")
);
}
}
}
+55 -4
View File
@@ -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<Job>,
@@ -73,11 +74,61 @@ 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.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::Service(service) => {
if config.skip_cmd.contains(&service.cmd) {
eprintln!(
"init: skipping '{} {}'",
service.cmd,
service.args.join(" ")
);
return;
}
service.spawn(&config.envs);
}
}
}
+18 -45
View File
@@ -12,11 +12,12 @@ pub fn subst_env<'a>(arg: &str) -> String {
}
}
pub struct Script(pub Vec<Command>);
pub struct Script(pub Vec<Command>, pub Vec<UnitId>);
impl Script {
pub fn from_str(config: &str, errors: &mut Vec<String>) -> io::Result<Script> {
let mut cmds = vec![];
let mut requires_weak = vec![];
for line_raw in config.lines() {
let line = line_raw.trim();
@@ -26,85 +27,57 @@ impl Script {
let args = line.split(' ').map(subst_env);
match Command::parse(args) {
Ok(cmd) => cmds.push(cmd),
match Command::parse(args, &mut requires_weak) {
Ok(None) => {}
Ok(Some(cmd)) => cmds.push(cmd),
Err(err) => errors.push(err),
}
}
Ok(Script(cmds))
Ok(Script(cmds, requires_weak))
}
}
#[derive(Clone, Debug)]
pub enum Command {
// Dependencies
RequiresWeak(Vec<UnitId>),
// Service
Service(Service),
// Misc
Echo(String),
Nothing,
}
impl Command {
fn parse(mut args: impl Iterator<Item = String>) -> Result<Command, String> {
fn parse(
mut args: impl Iterator<Item = String>,
requires_weak: &mut Vec<UnitId>,
) -> Result<Option<Command>, String> {
let Some(cmd) = args.next() else {
return Ok(Command::Nothing);
return Ok(None);
};
match cmd.as_str() {
"requires_weak" => Ok(Command::RequiresWeak(args.map(UnitId).collect::<Vec<_>>())),
"echo" => Ok(Command::Echo(args.collect::<Vec<_>>().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),
}))
"requires_weak" => {
requires_weak.extend(args.map(UnitId));
Ok(None)
}
"nowait" => {
let process = Process::parse(args)?;
Ok(Command::Service(Service {
Ok(Some(Command::Service(Service {
cmd: process.cmd,
args: process.args,
envs: process.envs,
inherit_envs: BTreeSet::new(),
type_: ServiceType::OneshotAsync,
}))
})))
}
_ => {
let process = Process::parse(iter::once(cmd).chain(args))?;
Ok(Command::Service(Service {
Ok(Some(Command::Service(Service {
cmd: process.cmd,
args: process.args,
envs: process.envs,
inherit_envs: BTreeSet::new(),
type_: ServiceType::Oneshot,
}))
})))
}
}
}
+1 -1
View File
@@ -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) => {
+3 -10
View File
@@ -132,7 +132,7 @@ fn true_bool() -> bool {
}
pub enum UnitKind {
LegacyScript { script: Script },
LegacyScript { script: Vec<Command> },
Service { service: Service },
Target {},
}
@@ -182,23 +182,16 @@ impl Unit {
let Some(ext) = config_path.extension().map(|ext| ext.to_str().unwrap()) else {
let script = Script::from_str(&config, errors)?;
let mut requires_weak = vec![];
for command in &script.0 {
match command {
Command::RequiresWeak(deps) => requires_weak.extend(deps.into_iter().cloned()),
_ => {}
}
}
return Ok(Unit {
id,
info: UnitInfo {
description: None,
default_dependencies: true,
requires_weak,
requires_weak: script.1,
condition_architecture: None,
condition_board: None,
},
kind: UnitKind::LegacyScript { script },
kind: UnitKind::LegacyScript { script: script.0 },
});
};