init: Change from push to a pull for loading units
Rather than starting all services in the config dir, we only start those which are a dependency of the target we want to reach. This is necessary to handle unit templating, service start stop and an alternative target for recovery or graceful shutdown.
This commit is contained in:
+41
-4
@@ -54,6 +54,7 @@ impl InitConfig {
|
||||
struct SwitchRoot {
|
||||
prefix: PathBuf,
|
||||
etcdir: PathBuf,
|
||||
target: Option<UnitId>,
|
||||
}
|
||||
|
||||
impl SwitchRoot {
|
||||
@@ -82,11 +83,44 @@ impl SwitchRoot {
|
||||
self.etcdir.join("init.d"),
|
||||
];
|
||||
|
||||
let (loaded_units, errors) = unit_store.load_units();
|
||||
let (loaded_units, errors) = unit_store.load_units(UnitId("00_runtime.target".to_owned()));
|
||||
for error in errors {
|
||||
eprintln!("init: {error}");
|
||||
}
|
||||
pending_units.extend(loaded_units);
|
||||
|
||||
if let Some(target) = self.target {
|
||||
let (loaded_units, errors) = unit_store.load_units(target);
|
||||
for error in errors {
|
||||
eprintln!("init: {error}");
|
||||
}
|
||||
pending_units.extend(loaded_units);
|
||||
} else {
|
||||
let entries = match config::config_for_dirs(&unit_store.config_dirs) {
|
||||
Ok(entries) => entries,
|
||||
Err(err) => {
|
||||
eprintln!(
|
||||
"init: failed to read configs from {}: {err}",
|
||||
unit_store
|
||||
.config_dirs
|
||||
.iter()
|
||||
.map(|dir| dir.display().to_string())
|
||||
.collect::<Vec<_>>()
|
||||
.join(", ")
|
||||
);
|
||||
return;
|
||||
}
|
||||
};
|
||||
for entry in entries {
|
||||
let (loaded_units, errors) = unit_store.load_units(UnitId(
|
||||
entry.file_name().unwrap().to_str().unwrap().to_owned(),
|
||||
));
|
||||
for error in errors {
|
||||
eprintln!("init: {error}");
|
||||
}
|
||||
pending_units.extend(loaded_units);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -149,9 +183,13 @@ fn main() {
|
||||
let mut unit_store = UnitStore::new();
|
||||
let mut pending_units = VecDeque::new();
|
||||
|
||||
let runtime_target = UnitId("00_runtime.target".to_owned());
|
||||
let initfs_target = UnitId("90_initfs.target".to_owned());
|
||||
|
||||
SwitchRoot {
|
||||
prefix: Path::new("/scheme/initfs").to_owned(),
|
||||
etcdir: Path::new("/scheme/initfs/etc").to_owned(),
|
||||
target: Some(initfs_target.clone()),
|
||||
}
|
||||
.apply(&mut pending_units, &mut unit_store, &mut init_config);
|
||||
|
||||
@@ -162,9 +200,6 @@ fn main() {
|
||||
eprintln!("init: failed to switch stdio to '/scheme/log': {err}");
|
||||
}
|
||||
|
||||
let runtime_target = UnitId("00_runtime.target".to_owned());
|
||||
let initfs_target = UnitId("90_initfs.target".to_owned());
|
||||
|
||||
'a: while let Some(unit) = pending_units.pop_front() {
|
||||
if let Some(condition_architecture) = &unit_store.unit(&unit).info.condition_architecture {
|
||||
if !condition_architecture
|
||||
@@ -203,6 +238,8 @@ fn main() {
|
||||
SwitchRoot {
|
||||
prefix: PathBuf::from("/usr"),
|
||||
etcdir: PathBuf::from("/etc"),
|
||||
// FIXME make target non-optional once there is a multi-user.target unit
|
||||
target: None, //UnitId("multi-user.target".to_owned()),
|
||||
}
|
||||
.apply(&mut pending_units, &mut unit_store, &mut init_config);
|
||||
}
|
||||
|
||||
+37
-27
@@ -20,36 +20,46 @@ impl UnitStore {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn load_units(&mut self) -> (Vec<UnitId>, Vec<String>) {
|
||||
let mut loaded_units = vec![];
|
||||
let mut errors = vec![];
|
||||
|
||||
let entries = match config::config_for_dirs(&self.config_dirs) {
|
||||
Ok(entries) => entries,
|
||||
Err(err) => {
|
||||
errors.push(format!(
|
||||
"failed to read configs from {}: {err}",
|
||||
self.config_dirs
|
||||
.iter()
|
||||
.map(|dir| dir.display().to_string())
|
||||
.collect::<Vec<_>>()
|
||||
.join(", ")
|
||||
));
|
||||
return (loaded_units, errors);
|
||||
}
|
||||
fn load_single_unit(&mut self, unit_id: UnitId) -> (Option<UnitId>, Vec<String>) {
|
||||
let Some(path) = self
|
||||
.config_dirs
|
||||
.iter()
|
||||
.rev()
|
||||
.map(|dir| dir.join(&unit_id.0))
|
||||
.find(|path| path.exists())
|
||||
else {
|
||||
return (None, vec![format!("unit {} not found", unit_id.0)]);
|
||||
};
|
||||
|
||||
for entry in entries {
|
||||
let (unit, new_errors) = match Unit::from_file(&entry) {
|
||||
Ok(unit) => unit,
|
||||
Err(err) => {
|
||||
errors.push(format!("{}: {err}", entry.display()));
|
||||
continue;
|
||||
}
|
||||
};
|
||||
let (unit, errors) = match Unit::from_file(&path) {
|
||||
Ok(unit) => unit,
|
||||
Err(err) => {
|
||||
return (None, vec![format!("{}: {err}", path.display())]);
|
||||
}
|
||||
};
|
||||
assert_eq!(unit_id, unit.id);
|
||||
self.units.insert(unit_id.clone(), unit);
|
||||
|
||||
(Some(unit_id), errors)
|
||||
}
|
||||
|
||||
pub fn load_units(&mut self, root_unit: UnitId) -> (Vec<UnitId>, Vec<String>) {
|
||||
let mut loaded_units = vec![];
|
||||
let mut pending_units = vec![root_unit];
|
||||
let mut errors = vec![];
|
||||
|
||||
while let Some(unit_id) = pending_units.pop() {
|
||||
if self.units.contains_key(&unit_id) {
|
||||
continue;
|
||||
}
|
||||
let (unit, new_errors) = self.load_single_unit(unit_id);
|
||||
errors.extend(new_errors);
|
||||
loaded_units.push(unit.id.clone());
|
||||
self.units.insert(unit.id.clone(), unit);
|
||||
if let Some(unit) = unit {
|
||||
loaded_units.push(unit.clone());
|
||||
for dep in &self.unit(&unit).info.requires_weak {
|
||||
pending_units.push(dep.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
(loaded_units, errors)
|
||||
|
||||
Reference in New Issue
Block a user