From 4e772728ad74ec79fdd145ffc19b871fcc3513b0 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Thu, 19 Feb 2026 22:18:29 +0100 Subject: [PATCH] init: Introduce Service type A future commit will use it to define services in toml format. --- init/src/main.rs | 64 ++++++------------------------------------- init/src/script.rs | 67 ++++++++++++++++++++++++++++++--------------- init/src/service.rs | 60 ++++++++++++++++++++++++++++++++++++++++ zerod/src/scheme.rs | 2 +- 4 files changed, 115 insertions(+), 78 deletions(-) create mode 100644 init/src/service.rs diff --git a/init/src/main.rs b/init/src/main.rs index cfdca096da..ea16bf226d 100644 --- a/init/src/main.rs +++ b/init/src/main.rs @@ -9,6 +9,7 @@ use libredox::flag::{O_RDONLY, O_WRONLY}; use crate::script::Command; mod script; +mod service; fn switch_stdio(stdio: &str) -> Result<()> { let stdin = libredox::Fd::open(stdio, O_RDONLY, 0)?; @@ -101,64 +102,17 @@ fn run_command(cmd: Command, config: &mut InitConfig) { eprintln!("init: failed to switch stdio to '{}': {}", stdio, err); } } - Command::Nowait(cmd) => { - if config.skip_cmd.contains(&cmd.cmd) { - eprintln!("init: skipping '{} {}'", cmd.cmd, cmd.args.join(" ")); + Command::Service(service) => { + if config.skip_cmd.contains(&service.cmd) { + eprintln!( + "init: skipping '{} {}'", + service.cmd, + service.args.join(" ") + ); return; } - let mut command = cmd.into_command(&config.envs); - - match command.spawn() { - Ok(_child) => {} - Err(err) => eprintln!("init: failed to execute '{:?}': {}", command, err), - } - } - Command::Notify(cmd) => { - if config.skip_cmd.contains(&cmd.cmd) { - eprintln!("init: skipping '{} {}'", cmd.cmd, cmd.args.join(" ")); - return; - } - - let command = cmd.into_command(&config.envs); - - daemon::Daemon::spawn(command); - } - Command::Scheme(scheme, cmd) => { - if config.skip_cmd.contains(&cmd.cmd) { - eprintln!("init: skipping '{} {}'", cmd.cmd, cmd.args.join(" ")); - return; - } - - let command = cmd.into_command(&config.envs); - - daemon::SchemeDaemon::spawn(command, &scheme); - } - Command::Regular(cmd) => { - if config.skip_cmd.contains(&cmd.cmd) { - eprintln!("init: skipping '{} {}'", cmd.cmd, cmd.args.join(" ")); - return; - } - - let mut command = cmd.into_command(&config.envs); - - let mut child = match command.spawn() { - Ok(child) => child, - Err(err) => { - eprintln!("init: failed to execute {:?}: {}", command, err); - return; - } - }; - match child.wait() { - Ok(exit_status) => { - if !exit_status.success() { - eprintln!("{command:?} failed with {exit_status}"); - } - } - Err(err) => { - eprintln!("init: failed to wait for {:?}: {}", command, err) - } - } + service.spawn(&config.envs); } } } diff --git a/init/src/script.rs b/init/src/script.rs index 10ac3c6a7e..bd0de5161c 100644 --- a/init/src/script.rs +++ b/init/src/script.rs @@ -1,7 +1,8 @@ use std::collections::BTreeMap; -use std::ffi::OsString; use std::path::{Path, PathBuf}; -use std::{env, fs, io, iter, process}; +use std::{env, fs, io, iter}; + +use crate::service::{Service, ServiceType}; fn subst_env<'a>(arg: &str) -> String { if arg.starts_with('$') { @@ -39,10 +40,7 @@ impl Script { #[derive(Debug)] pub enum Command { // Service - Nowait(Process), - Notify(Process), - Scheme(String, Process), - Regular(Process), + Service(Service), // Modify env Stdio(String), @@ -79,18 +77,50 @@ impl Command { }; Ok(Command::Stdio(stdio)) } - "nowait" => Ok(Command::Nowait(Process::parse(args)?)), - "notify" => Ok(Command::Notify(Process::parse(args)?)), + "notify" => { + let process = Process::parse(args)?; + + Ok(Command::Service(Service { + cmd: process.cmd, + args: process.args, + envs: process.envs, + type_: ServiceType::Notify, + })) + } "scheme" => { let Some(scheme) = args.next() else { return Err("init: failed to run scheme: no argument".to_owned()); }; - Ok(Command::Scheme(scheme, Process::parse(args)?)) + let process = Process::parse(args)?; + + Ok(Command::Service(Service { + cmd: process.cmd, + args: process.args, + envs: process.envs, + type_: ServiceType::Scheme(scheme), + })) + } + "nowait" => { + let process = Process::parse(args)?; + + Ok(Command::Service(Service { + cmd: process.cmd, + args: process.args, + envs: process.envs, + type_: ServiceType::OneshotAsync, + })) + } + _ => { + let process = Process::parse(iter::once(cmd).chain(args))?; + + Ok(Command::Service(Service { + cmd: process.cmd, + args: process.args, + envs: process.envs, + type_: ServiceType::Oneshot, + })) } - _ => Ok(Command::Regular(Process::parse( - iter::once(cmd).chain(args), - )?)), } } } @@ -99,14 +129,14 @@ impl Command { pub struct Process { pub cmd: String, pub args: Vec, - pub envs: Vec<(String, String)>, + pub envs: BTreeMap, } impl Process { fn parse(parts: impl Iterator) -> Result { let mut cmd = None; let mut args = vec![]; - let mut envs = vec![]; + let mut envs = BTreeMap::new(); for arg in parts { if cmd.is_none() { @@ -117,7 +147,7 @@ impl Process { subst_env(value) }; if !value.is_empty() { - envs.push((env.to_owned(), value)); + envs.insert(env.to_owned(), value); } } else { cmd = Some(arg); @@ -133,11 +163,4 @@ impl Process { Err("no command given".to_owned()) } } - - pub fn into_command(self, base_envs: &BTreeMap) -> process::Command { - let mut command = process::Command::new(self.cmd); - command.args(self.args); - command.env_clear().envs(base_envs).envs(self.envs); - command - } } diff --git a/init/src/service.rs b/init/src/service.rs new file mode 100644 index 0000000000..e7a433e510 --- /dev/null +++ b/init/src/service.rs @@ -0,0 +1,60 @@ +use std::collections::BTreeMap; +use std::ffi::OsString; +use std::process::Command; + +#[derive(Debug)] +pub struct Service { + pub cmd: String, + pub args: Vec, + pub envs: BTreeMap, + pub type_: ServiceType, +} + +#[derive(Debug, Default)] +pub enum ServiceType { + #[default] + Notify, + Scheme(String), + Oneshot, + OneshotAsync, +} + +impl Service { + pub fn spawn(self, base_envs: &BTreeMap) { + let mut command = Command::new(self.cmd); + command.args(self.args); + command.env_clear().envs(base_envs).envs(self.envs); + + match self.type_ { + ServiceType::Notify => { + daemon::Daemon::spawn(command); + } + ServiceType::Scheme(scheme) => { + daemon::SchemeDaemon::spawn(command, &scheme); + } + ServiceType::Oneshot => { + let mut child = match command.spawn() { + Ok(child) => child, + Err(err) => { + eprintln!("init: failed to execute {:?}: {}", command, err); + return; + } + }; + match child.wait() { + Ok(exit_status) => { + if !exit_status.success() { + eprintln!("{command:?} failed with {exit_status}"); + } + } + Err(err) => { + eprintln!("init: failed to wait for {:?}: {}", command, err) + } + } + } + ServiceType::OneshotAsync => match command.spawn() { + Ok(_child) => {} + Err(err) => eprintln!("init: failed to execute '{:?}': {}", command, err), + }, + } + } +} diff --git a/zerod/src/scheme.rs b/zerod/src/scheme.rs index d39772f026..a180d6377f 100644 --- a/zerod/src/scheme.rs +++ b/zerod/src/scheme.rs @@ -15,7 +15,7 @@ impl SchemeSync for ZeroScheme { fn openat( &mut self, dirfd: usize, - path: &str, + _path: &str, _flags: usize, _fcntl_flags: u32, _ctx: &CallerCtx,