init: Introduce dependency handling

This commit is contained in:
bjorn3
2026-02-25 22:43:11 +01:00
parent 6fcb07e7dd
commit bb2a00114f
16 changed files with 110 additions and 23 deletions
+1
View File
@@ -1,3 +1,4 @@
requires_weak 30_live
notify hwd
notify RSDP_ADDR=$ RSDP_SIZE=$ hwd
pcid-spawner --initfs
@@ -1,2 +1,3 @@
requires_weak 30_live
unset RSDP_ADDR RSDP_SIZE
notify bcm2835-sdhcid
+3 -1
View File
@@ -1,5 +1,7 @@
{
"unit": {},
"unit": {
"default_dependencies": false
},
"service": {
"cmd": "nulld",
"type": {
+3 -1
View File
@@ -1,5 +1,7 @@
{
"unit": {},
"unit": {
"default_dependencies": false
},
"service": {
"cmd": "randd",
"args": [
+2 -1
View File
@@ -1,6 +1,7 @@
{
"unit": {
"description": "Set time from realtime clock"
"description": "Set time from realtime clock",
"default_dependencies": false
},
"service": {
"cmd": "rtcd",
+12
View File
@@ -0,0 +1,12 @@
{
"unit": {
"description": "Various daemons that relibc needs to function.",
"default_dependencies": false,
"requires_weak": [
"00_nulld.service",
"00_randd.service",
"00_rtcd.service",
"00_zerod.service"
]
}
}
+3 -1
View File
@@ -1,5 +1,7 @@
{
"unit": {},
"unit": {
"default_dependencies": false
},
"service": {
"cmd": "zerod",
"type": {
+1
View File
@@ -1,3 +1,4 @@
requires_weak 00_runtime.target
scheme log logd
stdio /scheme/log
scheme logging ramfs logging
+1
View File
@@ -1,3 +1,4 @@
requires_weak 10_logging
scheme input inputd
notify FRAMEBUFFER_ADDR=$ FRAMEBUFFER_VIRT=$ FRAMEBUFFER_WIDTH=$ FRAMEBUFFER_HEIGHT=$ FRAMEBUFFER_STRIDE=$ vesad
scheme fbbootlog fbbootlogd
+1
View File
@@ -1,2 +1,3 @@
requires_weak 20_graphics
# Note: Needs to start before drivers to ensure it gets priority when redoxfs searches for disks
notify lived
+1
View File
@@ -1,3 +1,4 @@
requires_weak 30_live
notify ps2d
notify RSDP_ADDR=$ RSDP_SIZE=$ hwd
pcid-spawner --initfs
+1
View File
@@ -1 +1,2 @@
requires_weak 40_drivers
REDOXFS_PASSWORD_ADDR=$ REDOXFS_PASSWORD_SIZE=$ redoxfs --uuid $REDOXFS_UUID file $REDOXFS_BLOCK
+3 -1
View File
@@ -1,5 +1,7 @@
{
"unit": {},
"unit": {
"requires_weak": ["50_rootfs"]
},
"switchroot": {
"prefix": "/usr",
"etcdir": "/etc"
+37 -10
View File
@@ -1,8 +1,8 @@
use std::collections::BTreeMap;
use std::collections::{BTreeMap, VecDeque};
use std::env;
use std::ffi::OsString;
use std::io::Result;
use std::path::{Path, PathBuf};
use std::{env, mem};
use libredox::flag::{O_RDONLY, O_WRONLY};
use serde::Deserialize;
@@ -58,7 +58,7 @@ struct SwitchRoot {
impl SwitchRoot {
fn apply(
self,
pending_units: &mut Vec<UnitId>,
pending_units: &mut VecDeque<UnitId>,
unit_store: &mut UnitStore,
config: &mut InitConfig,
) {
@@ -85,7 +85,7 @@ impl SwitchRoot {
fn run(
unit: &UnitId,
pending_units: &mut Vec<UnitId>,
pending_units: &mut VecDeque<UnitId>,
unit_store: &mut UnitStore,
config: &mut InitConfig,
) -> Result<()> {
@@ -104,12 +104,25 @@ fn run(
if config.log_debug {
eprintln!(
"Starting {}",
unit.info.description.as_ref().unwrap_or(&unit.id.0)
unit.info.description.as_ref().unwrap_or(&unit.id.0),
);
}
service.spawn(&config.envs);
}
unit::UnitKind::Target {} => {
if config.log_debug {
eprintln!(
"Started target {}",
unit.info.description.as_ref().unwrap_or(&unit.id.0),
);
}
}
unit::UnitKind::SwitchRoot { switchroot } => {
eprintln!(
"init: switchroot to {} {}",
switchroot.prefix.display(),
switchroot.etcdir.display()
);
switchroot.clone().apply(pending_units, unit_store, config);
}
}
@@ -119,6 +132,7 @@ fn run(
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::Stdio(stdio) => {
@@ -144,7 +158,7 @@ fn run_command(cmd: Command, config: &mut InitConfig) {
fn main() {
let mut init_config = InitConfig::new();
let mut unit_store = UnitStore::new();
let mut pending_units = vec![];
let mut pending_units = VecDeque::new();
SwitchRoot {
prefix: Path::new("/scheme/initfs").to_owned(),
@@ -152,12 +166,25 @@ fn main() {
}
.apply(&mut pending_units, &mut unit_store, &mut init_config);
while !pending_units.is_empty() {
for unit in mem::take(&mut pending_units) {
if let Err(err) = run(&unit, &mut pending_units, &mut unit_store, &mut init_config) {
eprintln!("init: failed to run {}: {}", unit.0, err);
let runtime_target = UnitId("00_runtime.target".to_owned());
'a: while let Some(unit) = pending_units.pop_front() {
if unit_store.unit(&unit).info.default_dependencies {
if pending_units.contains(&runtime_target) {
pending_units.push_back(unit);
continue 'a;
}
}
for dep in &unit_store.unit(&unit).info.requires_weak {
if pending_units.contains(dep) {
pending_units.push_back(unit);
continue 'a;
}
}
if let Err(err) = run(&unit, &mut pending_units, &mut unit_store, &mut init_config) {
eprintln!("init: failed to run {}: {}", unit.0, err);
}
}
libredox::call::setrens(0, 0).expect("init: failed to enter null namespace");
+5 -1
View File
@@ -1,8 +1,8 @@
use std::collections::BTreeMap;
use std::path::PathBuf;
use std::{env, io, iter};
use crate::service::{Service, ServiceType};
use crate::unit::UnitId;
fn subst_env<'a>(arg: &str) -> String {
if arg.starts_with('$') {
@@ -39,6 +39,9 @@ impl Script {
#[derive(Clone, Debug)]
pub enum Command {
// Dependencies
RequiresWeak(Vec<UnitId>),
// Service
Service(Service),
@@ -57,6 +60,7 @@ impl Command {
};
match cmd.as_str() {
"requires_weak" => Ok(Command::RequiresWeak(args.map(UnitId).collect::<Vec<_>>())),
"echo" => Ok(Command::Echo(args.collect::<Vec<_>>().join(" "))),
"stdio" => {
let Some(stdio) = args.next() else {
+35 -7
View File
@@ -5,7 +5,7 @@ use std::{fs, io};
use serde::Deserialize;
use crate::SwitchRoot;
use crate::script::Script;
use crate::script::{Command, Script};
use crate::service::Service;
pub struct UnitStore {
@@ -56,12 +56,17 @@ impl UnitStore {
(loaded_units, errors)
}
pub fn unit(&self, unit: &UnitId) -> &Unit {
self.units.get(unit).unwrap()
}
pub fn unit_mut(&mut self, unit: &UnitId) -> &mut Unit {
self.units.get_mut(unit).unwrap()
}
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Deserialize)]
#[serde(transparent)]
pub struct UnitId(pub String);
pub struct Unit {
@@ -74,15 +79,20 @@ pub struct Unit {
#[derive(Deserialize)]
pub struct UnitInfo {
pub description: Option<String>,
#[serde(default = "true_bool")]
pub default_dependencies: bool,
#[serde(default)]
pub requires: Vec<String>,
#[serde(default)]
pub requires_weak: Vec<String>,
pub requires_weak: Vec<UnitId>,
}
fn true_bool() -> bool {
true
}
pub enum UnitKind {
LegacyScript { script: Script },
Service { service: Service },
Target {},
SwitchRoot { switchroot: SwitchRoot },
}
@@ -92,6 +102,11 @@ struct SerializedService {
service: Service,
}
#[derive(Deserialize)]
struct SerializedTarget {
unit: UnitInfo,
}
#[derive(Deserialize)]
struct SerializedSwitchRoot {
unit: UnitInfo,
@@ -114,11 +129,20 @@ impl Unit {
let (info, kind, errors) = match config_path.extension().map(|ext| ext.to_str().unwrap()) {
None => {
let (script, warnings) = Script::from_str(&config)?;
let mut requires_weak = vec![];
for command in &script.0 {
match command {
Command::RequiresWeak(deps) => {
requires_weak.extend(deps.into_iter().cloned())
}
_ => {}
}
}
(
UnitInfo {
description: None,
requires: vec![],
requires_weak: vec![],
default_dependencies: true,
requires_weak,
},
UnitKind::LegacyScript { script },
warnings,
@@ -134,6 +158,10 @@ impl Unit {
vec![],
)
}
Some("target") => {
let target: SerializedTarget = serde_json::from_str(&config)?;
(target.unit, UnitKind::Target {}, vec![])
}
Some("switchroot") => {
let switchroot: SerializedSwitchRoot = serde_json::from_str(&config)?;
(