diff --git a/daemon/src/config.rs b/daemon/src/config.rs index a5a7c89..94649ba 100644 --- a/daemon/src/config.rs +++ b/daemon/src/config.rs @@ -1,6 +1,7 @@ use anyhow::Context; use bevy_ecs::prelude::*; use serde::Deserialize; +use std::collections::BTreeMap; use std::{path, sync}; #[derive(Deserialize, Debug)] @@ -9,9 +10,12 @@ pub struct Service { pub command: String, #[serde(default)] pub args: Vec, + #[serde(default)] pub conditions: Vec, pub ready: Condition, pub running: Condition, + #[serde(default)] + pub env: BTreeMap, } #[derive(Deserialize, Debug)] diff --git a/daemon/src/main.rs b/daemon/src/main.rs index 0d159ec..d90a45e 100644 --- a/daemon/src/main.rs +++ b/daemon/src/main.rs @@ -16,7 +16,7 @@ use calloop::generic; use clap::{Parser, ValueEnum}; use hyprwire::server; use serviced_v1::{serviced_manager_v1, serviced_reload_v1}; -use std::collections::HashSet; +use std::collections::{BTreeMap, HashSet}; use std::os::fd::AsRawFd; use std::{env, fs, path, process, sync}; @@ -66,9 +66,11 @@ fn spawn_process( service: Entity, command: &str, args: &[String], + environment: &BTreeMap, ) -> anyhow::Result { let mut child = process::Command::new(command) .args(args) + .envs(environment) .spawn() .with_context(|| format!("failed to spawn {command}"))?; let id = ecs::ProcessId(child.id()); @@ -155,6 +157,7 @@ fn handle_reload( mut commands: Commands, service_commands: Query<&service::Command>, service_args: Query<&service::Args>, + service_environments: Query<&service::Environment>, service_conditions: Query<&service::Conditions>, service_ready: Query<&service::Ready>, service_running: Query<&service::Running>, @@ -203,6 +206,14 @@ fn handle_reload( { commands.entity(*entity).insert(service::Args(service.args)); } + if service_environments + .get(*entity) + .is_ok_and(|current| current.0 != service.env) + { + commands + .entity(*entity) + .insert(service::Environment(service.env)); + } if service_conditions .get(*entity) .is_ok_and(|current| current.0 != resolved_conditions) @@ -233,6 +244,7 @@ fn handle_reload( resolved_conditions, ready, running, + service.env, ); } } @@ -262,10 +274,12 @@ fn service_modified( Option<&mut service::Process>, &service::Ready, &service::Running, + &service::Environment, ), Or<( Changed, Changed, + Changed, Changed, Changed, Changed, @@ -274,7 +288,7 @@ fn service_modified( fulfilled: Query<(), With>, loop_handle: NonSend, ) { - for (entity, name, command, args, conditions, service, process, ready, running) in + for (entity, name, command, args, conditions, service, process, ready, running, environment) in services.iter_mut() { if service.is_added() { @@ -310,7 +324,7 @@ fn service_modified( .iter() .all(|condition| fulfilled.contains(*condition)) { - match spawn_process(&loop_handle, entity, &command.0, &args.0) { + match spawn_process(&loop_handle, entity, &command.0, &args.0, &environment.0) { Ok(process) => { commands.entity(entity).insert(process); @@ -339,6 +353,7 @@ fn start_services( &service::Conditions, &service::Ready, &service::Running, + &service::Environment, ), (With, Without), >, @@ -352,7 +367,7 @@ fn start_services( let any_started = services .iter() - .filter(|(_, _, _, _, conditions, _, _)| { + .filter(|(_, _, _, _, conditions, _, _, _)| { conditions .0 .iter() @@ -360,7 +375,7 @@ fn start_services( }) .fold( false, - |any_started, (entity, name, command, args, _, ready, running)| { + |any_started, (entity, name, command, args, _, ready, running, environment)| { log::info!( "starting service {entity:?}: {} ({} {:?})", name.0, @@ -368,7 +383,7 @@ fn start_services( args.0 ); - (match spawn_process(&loop_handle, entity, &command.0, &args.0) { + (match spawn_process(&loop_handle, entity, &command.0, &args.0, &environment.0) { Ok(process) => { commands.entity(entity).insert(process); commands.entity(ready.0).insert(condition::Fulfilled); diff --git a/daemon/src/service.rs b/daemon/src/service.rs index 3147c3b..973ad82 100644 --- a/daemon/src/service.rs +++ b/daemon/src/service.rs @@ -1,5 +1,6 @@ use crate::ecs; use bevy_ecs::prelude::*; +use std::collections::BTreeMap; use std::{process, sync}; #[derive(Component, Debug, Clone, Copy, PartialEq, Eq)] @@ -23,6 +24,9 @@ pub struct Ready(pub Entity); #[derive(Component)] pub struct Running(pub Entity); +#[derive(Component)] +pub struct Environment(pub BTreeMap); + #[derive(Bundle)] pub struct ServiceBundle { pub marker: Service, @@ -32,6 +36,7 @@ pub struct ServiceBundle { pub conditions: Conditions, pub ready: Ready, pub running: Running, + pub environment: Environment, } #[derive(Component)] @@ -50,6 +55,7 @@ pub fn spawn( conditions: Vec, ready: Entity, running: Entity, + environment: BTreeMap, ) -> Entity { let entity = commands .spawn(ServiceBundle { @@ -60,6 +66,7 @@ pub fn spawn( conditions: Conditions(conditions), ready: Ready(ready), running: Running(running), + environment: Environment(environment), }) .id(); services.0.insert(name, entity); diff --git a/dhall/Serviced.dhall b/dhall/Serviced.dhall index c3f13a4..ad4613f 100644 --- a/dhall/Serviced.dhall +++ b/dhall/Serviced.dhall @@ -1,5 +1,7 @@ let Condition = { name : Text } +let Environment = List { mapKey : Text, mapValue : Text } + let ServiceOptions = { name : Text , command : Text @@ -14,16 +16,25 @@ let Service = , conditions : List Condition , ready : Condition , running : Condition + , env : Environment } let mkService = \(options : ServiceOptions) -> - { name = options.name - , command = options.command - , args = options.args - , conditions = options.conditions - , ready = { name = "${options.name}/ready" } - , running = { name = "${options.name}/running" } - } : Service + { name = options.name + , command = options.command + , args = options.args + , conditions = options.conditions + , ready.name = "${options.name}/ready" + , running.name = "${options.name}/running" + , env = [] : Environment + } + : Service -in { Condition, ServiceOptions, Service, mkService, Config = List Service } +in { Condition + , Environment + , ServiceOptions + , Service + , mkService + , Config = List Service + } diff --git a/flake.nix b/flake.nix index db8aba2..b5ae4b9 100644 --- a/flake.nix +++ b/flake.nix @@ -95,7 +95,7 @@ hjemModules = { sessiond = import ./nix/hjem.nix; - default = self.finixModules.sessiond; + default = self.hjemModules.sessiond; }; finixConfigurations = diff --git a/nix/finix.nix b/nix/finix.nix index 4e566bb..1f885fd 100644 --- a/nix/finix.nix +++ b/nix/finix.nix @@ -11,14 +11,17 @@ let toDhall = lib.generators.toDhall { }; renderService = name: service: '' - Serviced.mkService - { name = ${toDhall name} - , command = ${toDhall service.command} - , args = ${toDhall service.args} : List Text - , conditions = ${ - toDhall (map (name: { inherit name; }) service.conditions) - } : List Serviced.Condition - } + ( Serviced.mkService + { name = ${toDhall name} + , command = ${toDhall service.command} + , args = ${toDhall service.args} : List Text + , conditions = ${ + toDhall (map (name: { inherit name; }) service.conditions) + } : List Serviced.Condition + } + ) with env = ( ${ + toDhall (lib.mapAttrsToList (mapKey: mapValue: { inherit mapKey mapValue; }) service.env) + } : Serviced.Environment ) ''; enabledServices = lib.filterAttrs (_: service: service.enable) cfg.services; @@ -57,6 +60,11 @@ in type = types.listOf types.nonEmptyStr; default = [ ]; }; + + env = lib.mkOption { + type = types.attrsOf types.str; + default = { }; + }; }; } ); diff --git a/nix/hjem.nix b/nix/hjem.nix index 008d458..60734ec 100644 --- a/nix/hjem.nix +++ b/nix/hjem.nix @@ -11,14 +11,17 @@ let toDhall = lib.generators.toDhall { }; renderService = name: service: '' - Serviced.mkService - { name = ${toDhall name} - , command = ${toDhall service.command} - , args = ${toDhall service.args} : List Text - , conditions = ${ - toDhall (map (name: { inherit name; }) service.conditions) - } : List Serviced.Condition - } + ( Serviced.mkService + { name = ${toDhall name} + , command = ${toDhall service.command} + , args = ${toDhall service.args} : List Text + , conditions = ${ + toDhall (map (name: { inherit name; }) service.conditions) + } : List Serviced.Condition + } + ) with env = ( ${ + toDhall (lib.mapAttrsToList (mapKey: mapValue: { inherit mapKey mapValue; }) service.env) + } : Serviced.Environment ) ''; enabledServices = lib.filterAttrs (_: service: service.enable) cfg.services; @@ -57,6 +60,11 @@ in type = types.listOf types.nonEmptyStr; default = [ ]; }; + + env = lib.mkOption { + type = types.attrsOf types.str; + default = { }; + }; }; } );