// This module defines unit settings parsing logig // // NOTE: ON UNIT PARSING LOGIC // when parsing a unit in /etc/vigil/units/ we shoul ignore // ANY broken/bad constructed toml unit configuration file // without "crashing" logic. // // NOTE: ON GENERAL SERVICE LOGGING // when vigil starts service, it should send out logs to vigil? // or logs in such init systems are just taking the stdout+stderr // of service and showing its output? idk 4 now, look into how its supposed to be use crate::{log_warning, RUNLEVEL_STATE}; use serde::Deserialize; use std::fs::{read_dir, read_to_string}; /* [info] name = "name" description = "description" version = "version" [config] exec = "path to exec" runlevel = restart=always|never */ #[derive(Deserialize)] enum Restart { #[serde(alias = "always")] Always, #[serde(alias = "never")] Never, } #[derive(Deserialize, PartialEq, Eq)] pub enum Runlevel { /// The system is shutting down, runlevel int: 0 Shutdown, /// One-user system debug-mode, runlevel int: 1 OneUser, /// Multi-user CLI (TTY) with no network, runlevel int: 2 MultiNoNetwork, /// Multi-user CLI with network, runlevel int: 3 MultiNetwork, /// Multi-user mode with GUI, runlevel int: 5 MultiUserGUINetwork, /// Runlevel is not critical for running the service, runlevel int: 4 Undefined, /// System going too reboot, runlevel int: 6 Reboot, } #[allow(dead_code)] #[derive(Deserialize)] pub struct ServiceInfo { /// Service name name: String, /// Service description description: String, /// Service version version: String, } #[allow(dead_code)] #[derive(Deserialize)] pub struct ServiceConfig { /// Execution command, like ExecStart in sysd exec: String, /// Runlevel, like after=*.target runlevel: Runlevel, /// Restart service: Always|Never restart: Restart, } /// Main Unit-file struct #[allow(dead_code)] #[derive(Deserialize)] pub struct Unit { info: ServiceInfo, config: ServiceConfig, } #[allow(dead_code)] impl Restart { pub fn as_str(&self) -> &'static str { match self { Restart::Always => "always", Restart::Never => "never", } } } /// Function which starting services declared in `/etc/vigil/units/` /// Based on the global runlevel variable, should be runned as second thread pub fn execute_services() -> Result<(), Box> { let unit_list = parse_all_units()?; loop { match *RUNLEVEL_STATE.try_lock()? { Runlevel::Undefined => { for unit in &unit_list { if unit.config.runlevel == Runlevel::Undefined { std::process::Command::new(unit.config.exec.clone()); } else { continue; } } } Runlevel::Reboot => { todo!(); } Runlevel::Shutdown => { todo!(); } Runlevel::OneUser => { todo!(); } Runlevel::MultiNoNetwork => { todo!(); } Runlevel::MultiNetwork => { todo!(); } Runlevel::MultiUserGUINetwork => { todo!(); } } } } #[allow(dead_code)] fn parse_all_units() -> Result, Box> { let mut units: Vec = Vec::new(); for unit in read_dir("/etc/vigil/units")? { let unit_path = unit?.path(); let unit_str: String = match read_to_string(unit_path) { Ok(content) => content, Err(e) => { log_warning(&format!("Error while reading unit: {}", e)); continue; } }; let deserialized: Result = toml::from_str(&unit_str); match deserialized { Ok(unit) => { units.push(unit); } Err(e) => { log_warning(&format!("Error while parsing unit: {}", e)); continue; } }; } Ok(units) }