init: ConditionPathExists service gate (systemd-style, with ! negation)

init's Service parser used deny_unknown_fields, so the C0
driver-manager service files (which use ConditionPathExists) were
rejected wholesale — driver-manager stayed dormant by parser accident
instead of by design.

- service.rs: new optional ConditionPathExists field + condition_met()
  gate. A leading '!' negates: the service starts only when the path
  does NOT exist.
- scheduler.rs: skip services whose condition is not met (status_skip,
  same pattern as skip_cmd).
- 00_driver-manager.service: fix inverted polarity — the intent is
  'run unless /etc/driver-manager.d/disabled exists', i.e.
  ConditionPathExists = "!/etc/driver-manager.d/disabled".
- Drop two pre-existing unused imports (Write in service.rs,
  status_fail in main.rs).
This commit is contained in:
Red Bear OS
2026-07-23 16:55:42 +09:00
parent c836fffb66
commit e30f24997c
4 changed files with 27 additions and 3 deletions
+1 -1
View File
@@ -23,4 +23,4 @@ args = ["--rootfs"]
type = "oneshot_async" type = "oneshot_async"
# Operator must explicitly enable this service: # Operator must explicitly enable this service:
# touch /etc/driver-manager.d/disabled # force fallback to pcid-spawner # touch /etc/driver-manager.d/disabled # force fallback to pcid-spawner
ConditionPathExists = "/etc/driver-manager.d/disabled" ConditionPathExists = "!/etc/driver-manager.d/disabled"
+1 -1
View File
@@ -14,7 +14,7 @@ mod script;
mod service; mod service;
mod unit; mod unit;
use crate::color::{init_error, init_warn, status_fail, status_ok}; use crate::color::{init_error, init_warn, status_ok};
fn switch_stdio(stdio: &str) -> io::Result<()> { fn switch_stdio(stdio: &str) -> io::Result<()> {
let stdin = libredox::Fd::open(stdio, O_RDONLY, 0)?; let stdin = libredox::Fd::open(stdio, O_RDONLY, 0)?;
+7
View File
@@ -109,6 +109,13 @@ fn run(unit: &mut Unit, config: &mut InitConfig) {
status_skip(&format!("Skipping {} ({})", desc, service.cmd)); status_skip(&format!("Skipping {} ({})", desc, service.cmd));
return; return;
} }
if !service.condition_met() {
status_skip(&format!(
"Skipping {} (ConditionPathExists not met)",
desc
));
return;
}
if config.log_debug { if config.log_debug {
init_info(&format!("Starting {} ({})", desc, service.cmd)); init_info(&format!("Starting {} ({})", desc, service.cmd));
} else { } else {
+18 -1
View File
@@ -1,7 +1,7 @@
use std::collections::{BTreeMap, BTreeSet}; use std::collections::{BTreeMap, BTreeSet};
use std::ffi::OsString; use std::ffi::OsString;
use std::fs::File; use std::fs::File;
use std::io::{self, Read, Write}; use std::io::{self, Read};
use std::os::fd::{AsRawFd, FromRawFd, OwnedFd}; use std::os::fd::{AsRawFd, FromRawFd, OwnedFd};
use std::os::unix::process::CommandExt; use std::os::unix::process::CommandExt;
use std::process::Command; use std::process::Command;
@@ -24,6 +24,8 @@ pub struct Service {
pub inherit_envs: BTreeSet<String>, pub inherit_envs: BTreeSet<String>,
#[serde(rename = "type")] #[serde(rename = "type")]
pub type_: ServiceType, pub type_: ServiceType,
#[serde(default, rename = "ConditionPathExists")]
pub condition_path_exists: Option<String>,
} }
#[derive(Clone, Debug, Default, Deserialize)] #[derive(Clone, Debug, Default, Deserialize)]
@@ -36,6 +38,21 @@ pub enum ServiceType {
OneshotAsync, OneshotAsync,
} }
impl Service {
/// systemd-style ConditionPathExists gate. A leading `!` negates the
/// test: the service starts only when the path does NOT exist.
pub fn condition_met(&self) -> bool {
let Some(cond) = &self.condition_path_exists else {
return true;
};
let (negated, path) = match cond.strip_prefix('!') {
Some(rest) => (true, rest),
None => (false, cond.as_str()),
};
std::path::Path::new(path).exists() != negated
}
}
fn create_pipe() -> io::Result<(File, OwnedFd)> { fn create_pipe() -> io::Result<(File, OwnedFd)> {
let mut fds = [0i32; 2]; let mut fds = [0i32; 2];
// Use libc::pipe2() which goes through relibc's FILETABLE-managed path // Use libc::pipe2() which goes through relibc's FILETABLE-managed path