init: Introduce Service type

A future commit will use it to define services in toml format.
This commit is contained in:
bjorn3
2026-02-19 22:18:29 +01:00
parent c9d691fa29
commit 4e772728ad
4 changed files with 115 additions and 78 deletions
+9 -55
View File
@@ -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);
}
}
}
+45 -22
View File
@@ -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<String>,
pub envs: Vec<(String, String)>,
pub envs: BTreeMap<String, String>,
}
impl Process {
fn parse(parts: impl Iterator<Item = String>) -> Result<Process, String> {
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<String, OsString>) -> process::Command {
let mut command = process::Command::new(self.cmd);
command.args(self.args);
command.env_clear().envs(base_envs).envs(self.envs);
command
}
}
+60
View File
@@ -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<String>,
pub envs: BTreeMap<String, String>,
pub type_: ServiceType,
}
#[derive(Debug, Default)]
pub enum ServiceType {
#[default]
Notify,
Scheme(String),
Oneshot,
OneshotAsync,
}
impl Service {
pub fn spawn(self, base_envs: &BTreeMap<String, OsString>) {
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),
},
}
}
}
+1 -1
View File
@@ -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,