This commit is contained in:
parent
c93ad87d61
commit
fa1e398f0a
5 changed files with 328 additions and 14 deletions
|
@ -1,6 +1,9 @@
|
|||
extern crate sysf;
|
||||
|
||||
use sysf::unit::Unit;
|
||||
|
||||
|
||||
fn main() {
|
||||
|
||||
let x = Unit::new("hello");
|
||||
println!("{:?}", x);
|
||||
}
|
||||
|
|
|
@ -2,5 +2,4 @@
|
|||
name = "sysf"
|
||||
version = "0.1.0"
|
||||
authors = ["eater <=@eater.me>"]
|
||||
edition = "2018"
|
||||
crate-type = "dylib"
|
||||
edition = "2018"
|
|
@ -1,6 +1,7 @@
|
|||
use std::collections::HashMap;
|
||||
use crate::config::parser::{ParserError, parse_config};
|
||||
use crate::time::{parse_finite_time_span, FiniteTimeSpan, TimeSpan, parse_time_span};
|
||||
use crate::unit::Unit;
|
||||
|
||||
pub mod parser;
|
||||
|
||||
|
@ -21,8 +22,16 @@ impl Config {
|
|||
id
|
||||
}
|
||||
|
||||
pub fn load_file(&mut self, file_name: String, file_contents: String) -> Result<(), ParserError> {
|
||||
parse_config(self, file_name, file_contents)
|
||||
pub fn section(&self, name: &str) -> Option<&Section> {
|
||||
self.sections.get(name)
|
||||
}
|
||||
|
||||
pub fn load_file(&mut self, file_name: &str, file_contents: &str) -> Result<(), ParserError> {
|
||||
parse_config(self, file_name.to_string(), file_contents.to_string())
|
||||
}
|
||||
|
||||
pub fn get_unit(&self) -> Option<Unit> {
|
||||
Unit::load_from_config(&self.name, self)
|
||||
}
|
||||
|
||||
pub fn new(name: &str) -> Config {
|
||||
|
@ -73,6 +82,26 @@ impl Section {
|
|||
pub fn get_time_span(&self, name: &str) -> Option<TimeSpan> {
|
||||
self.entries.get(name).and_then(|x| x.get_time_span())
|
||||
}
|
||||
|
||||
pub fn get_space_separated_list(&self, name: &str) -> Vec<&str> {
|
||||
self
|
||||
.get_list(name)
|
||||
.unwrap_or(vec![])
|
||||
.iter()
|
||||
.flat_map(|x| x.split(" "))
|
||||
.filter(|x| x.len() > 0)
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn get_space_separated_string(&self, name: &str) -> Vec<&str> {
|
||||
self
|
||||
.get_string(name)
|
||||
.unwrap_or("")
|
||||
.trim()
|
||||
.split(" ")
|
||||
.filter(|x| x.len() > 0)
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default)]
|
||||
|
|
|
@ -3,7 +3,7 @@ use crate::time::TimeSpan::{Infinite, Finite};
|
|||
#[derive(Debug, Copy, Clone, Hash, Ord, PartialOrd, Eq, PartialEq)]
|
||||
pub enum TimeSpan {
|
||||
Infinite,
|
||||
Finite(FiniteTimeSpan)
|
||||
Finite(FiniteTimeSpan),
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Copy, Clone, Hash, Ord, PartialOrd, Eq, PartialEq)]
|
||||
|
|
299
sysf/src/unit.rs
299
sysf/src/unit.rs
|
@ -1,9 +1,15 @@
|
|||
use crate::unit::JobMode::Replace;
|
||||
use crate::time::{TimeSpan, FiniteTimeSpan};
|
||||
use crate::time::TimeSpan::Infinite;
|
||||
use crate::unit::UnitState::Stopped;
|
||||
use crate::config::Config;
|
||||
use crate::unit::JobMode::{Fail, ReplaceIrreversibly, Isolate, Flush, IgnoreDependencies, IgnoreRequirements};
|
||||
use crate::unit::CollectMode::{Inactive, InactiveOrFailed};
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[derive(Ord, PartialOrd, Eq, PartialEq, Debug, Clone)]
|
||||
pub struct Unit {
|
||||
name: String,
|
||||
state: UnitState,
|
||||
description: String,
|
||||
|
||||
// Space only
|
||||
|
@ -43,8 +49,8 @@ pub struct Unit {
|
|||
collect_mode: CollectMode,
|
||||
failure_action: UnitAction,
|
||||
success_action: UnitAction,
|
||||
failure_action_exit_status: u8,
|
||||
success_action_exit_status: u8,
|
||||
failure_action_exit_status: Option<u8>,
|
||||
success_action_exit_status: Option<u8>,
|
||||
job_timeout_sec: TimeSpan,
|
||||
job_running_timeout_sec: TimeSpan,
|
||||
job_timeout_action: UnitAction,
|
||||
|
@ -56,9 +62,192 @@ pub struct Unit {
|
|||
source_path: Option<String>,
|
||||
}
|
||||
|
||||
impl Unit {
|
||||
pub fn new(name: &str) -> Unit {
|
||||
let mut unit = Unit::default();
|
||||
unit.name = name.to_string();
|
||||
|
||||
unit
|
||||
}
|
||||
|
||||
// RsBorrowCheck says unit is moved, even though it isn't
|
||||
//noinspection RsBorrowChecker
|
||||
pub(crate) fn load_from_config(name: &str, config: &Config) -> Option<Unit> {
|
||||
let section = config.section("Unit")?;
|
||||
let mut unit = Unit::new(name);
|
||||
unit.description = section
|
||||
.get_string("Description")
|
||||
.map(str::to_string)
|
||||
.unwrap_or(unit.description);
|
||||
|
||||
unit.documentation = section
|
||||
.get_space_separated_string("Documentation")
|
||||
.to_string();
|
||||
|
||||
unit.wants = section
|
||||
.get_space_separated_list("Wants")
|
||||
.to_string();
|
||||
|
||||
unit.requires = section
|
||||
.get_space_separated_list("Requirers")
|
||||
.to_string();
|
||||
|
||||
unit.requisite = section
|
||||
.get_space_separated_list("Requisite")
|
||||
.to_string();
|
||||
|
||||
unit.binds_to = section
|
||||
.get_space_separated_list("BindsTo")
|
||||
.to_string();
|
||||
|
||||
unit.part_of = section
|
||||
.get_space_separated_list("PartOf")
|
||||
.to_string();
|
||||
|
||||
unit.conflicts = section
|
||||
.get_space_separated_string("Conflicts")
|
||||
.to_string();
|
||||
|
||||
unit.before = section
|
||||
.get_space_separated_list("Before")
|
||||
.to_string();
|
||||
|
||||
unit.after = section
|
||||
.get_space_separated_list("After")
|
||||
.to_string();
|
||||
|
||||
unit.on_failure = section
|
||||
.get_space_separated_string("OnFailure")
|
||||
.to_string();
|
||||
|
||||
unit.propagates_reload_to = section
|
||||
.get_space_separated_list("PropagatesReloadTo")
|
||||
.to_string();
|
||||
|
||||
unit.propagates_reload_from = section
|
||||
.get_space_separated_list("PropagatesReloadFrom")
|
||||
.to_string();
|
||||
|
||||
unit.joins_namespace_of = section
|
||||
.get_space_separated_list("JoinsNamespaceOf")
|
||||
.to_string();
|
||||
|
||||
unit.requires_mounts_for = section
|
||||
.get_space_separated_list("RequiresMountsFor")
|
||||
.to_string();
|
||||
|
||||
unit.on_failure_job_mode = section
|
||||
.get_string("OnFailureJobMode")
|
||||
.and_then(JobMode::from_str)
|
||||
.unwrap_or(unit.on_failure_job_mode);
|
||||
|
||||
unit.ignore_on_isolate = section
|
||||
.get_boolean("IgnoreOnIsolate")
|
||||
.unwrap_or(unit.ignore_on_isolate);
|
||||
|
||||
unit.stop_when_unneeded = section
|
||||
.get_boolean("StopWhenUnneeded")
|
||||
.unwrap_or(unit.stop_when_unneeded);
|
||||
|
||||
unit.refuse_manual_start = section
|
||||
.get_boolean("RefuseManualStart")
|
||||
.unwrap_or(unit.refuse_manual_start);
|
||||
|
||||
unit.refuse_manual_stop = section
|
||||
.get_boolean("RefuseManualStop")
|
||||
.unwrap_or(unit.refuse_manual_stop);
|
||||
|
||||
unit.allow_isolate = section
|
||||
.get_boolean("AllowIsolate")
|
||||
.unwrap_or(unit.allow_isolate);
|
||||
|
||||
unit.default_dependencies = section
|
||||
.get_boolean("DefaultDependencies")
|
||||
.unwrap_or(unit.default_dependencies);
|
||||
|
||||
unit.collect_mode = section
|
||||
.get_string("CollectMode")
|
||||
.and_then(CollectMode::from_str)
|
||||
.unwrap_or(unit.collect_mode);
|
||||
|
||||
unit.failure_action = section
|
||||
.get_string("FailureAction")
|
||||
.and_then(UnitAction::from_str)
|
||||
.unwrap_or(unit.failure_action);
|
||||
|
||||
unit.success_action = section
|
||||
.get_string("SuccessAction")
|
||||
.and_then(UnitAction::from_str)
|
||||
.unwrap_or(unit.success_action);
|
||||
|
||||
unit.failure_action_exit_status = section
|
||||
.get_string("FailureActionExitStatus")
|
||||
.and_then(|x| x.parse::<u8>().ok())
|
||||
.or(unit.failure_action_exit_status);
|
||||
|
||||
unit.success_action_exit_status = section
|
||||
.get_string("SuccessActionExitStatus")
|
||||
.and_then(|x| x.parse::<u8>().ok())
|
||||
.or(unit.success_action_exit_status);
|
||||
|
||||
unit.job_timeout_sec = section
|
||||
.get_time_span("JobTimeoutSec")
|
||||
.unwrap_or(unit.job_timeout_sec);
|
||||
|
||||
unit.job_running_timeout_sec = section
|
||||
.get_time_span("JobRunningTimeoutSec")
|
||||
.unwrap_or(unit.job_running_timeout_sec);
|
||||
|
||||
unit.job_timeout_action = section
|
||||
.get_string("JobTimeoutAction")
|
||||
.and_then(UnitAction::from_str)
|
||||
.unwrap_or(unit.job_timeout_action);
|
||||
|
||||
unit.job_timeout_reboot_argument = section
|
||||
.get_string("JobTimeoutRebootArgument")
|
||||
.map(str::to_string);
|
||||
|
||||
unit.start_limit_interval_sec = section
|
||||
.get_finite_time_span("StartLimitIntervalSec")
|
||||
.unwrap_or(unit.start_limit_interval_sec);
|
||||
|
||||
unit.start_limit_burst = section
|
||||
.get_string("StartLimitBurst")
|
||||
.and_then(|x| x.parse::<usize>().ok())
|
||||
.unwrap_or(unit.start_limit_burst);
|
||||
|
||||
unit.start_limit_action = section
|
||||
.get_string("StartLimitAction")
|
||||
.and_then(UnitAction::from_str)
|
||||
.unwrap_or(unit.start_limit_action);
|
||||
|
||||
unit.reboot_argument = section
|
||||
.get_string("RebootArgument")
|
||||
.map(str::to_string);
|
||||
|
||||
unit.source_path = section
|
||||
.get_string("SourcePath")
|
||||
.map(str::to_string);
|
||||
|
||||
Some(unit)
|
||||
}
|
||||
}
|
||||
|
||||
trait StringFromStr {
|
||||
fn to_string(&self) -> Vec<String>;
|
||||
}
|
||||
|
||||
impl StringFromStr for Vec<&str> {
|
||||
fn to_string(&self) -> Vec<String> {
|
||||
self.iter().map(|x| x.to_string()).collect()
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Unit {
|
||||
fn default() -> Self {
|
||||
Unit {
|
||||
name: "".to_string(),
|
||||
state: Stopped,
|
||||
description: "".to_string(),
|
||||
documentation: vec![],
|
||||
wants: vec![],
|
||||
|
@ -84,8 +273,8 @@ impl Default for Unit {
|
|||
collect_mode: CollectMode::Inactive,
|
||||
failure_action: UnitAction::None,
|
||||
success_action: UnitAction::None,
|
||||
failure_action_exit_status: 0,
|
||||
success_action_exit_status: 0,
|
||||
failure_action_exit_status: None,
|
||||
success_action_exit_status: None,
|
||||
job_timeout_sec: Infinite,
|
||||
job_running_timeout_sec: Infinite,
|
||||
job_timeout_action: UnitAction::None,
|
||||
|
@ -99,7 +288,15 @@ impl Default for Unit {
|
|||
}
|
||||
}
|
||||
|
||||
enum JobMode {
|
||||
#[derive(Ord, PartialOrd, Eq, PartialEq, Debug, Copy, Clone)]
|
||||
pub enum UnitState {
|
||||
Stopped,
|
||||
Failed,
|
||||
Running,
|
||||
}
|
||||
|
||||
#[derive(Ord, PartialOrd, Eq, PartialEq, Debug, Copy, Clone)]
|
||||
pub enum JobMode {
|
||||
Fail,
|
||||
Replace,
|
||||
ReplaceIrreversibly,
|
||||
|
@ -109,11 +306,38 @@ enum JobMode {
|
|||
IgnoreRequirements,
|
||||
}
|
||||
|
||||
enum CollectMode {
|
||||
impl JobMode {
|
||||
fn from_str(value: &str) -> Option<JobMode> {
|
||||
Some(match value.trim().to_ascii_lowercase().as_str() {
|
||||
"fail" => Fail,
|
||||
"replace" => Replace,
|
||||
"replace-irreversibly" => ReplaceIrreversibly,
|
||||
"isolate" => Isolate,
|
||||
"flush" => Flush,
|
||||
"ignore-dependencies" => IgnoreDependencies,
|
||||
"ignore-requirements" => IgnoreRequirements,
|
||||
_ => return None
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Ord, PartialOrd, Eq, PartialEq, Debug, Copy, Clone)]
|
||||
pub enum CollectMode {
|
||||
Inactive,
|
||||
InactiveOrFailed,
|
||||
}
|
||||
|
||||
impl CollectMode {
|
||||
pub fn from_str(value: &str) -> Option<CollectMode> {
|
||||
Some(match value.trim().to_ascii_lowercase().as_str() {
|
||||
"inactive" => Inactive,
|
||||
"inactive-or-failed" => InactiveOrFailed,
|
||||
_ => return None
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Ord, PartialOrd, Eq, PartialEq, Debug, Copy, Clone)]
|
||||
enum UnitAction {
|
||||
None,
|
||||
Reboot(UnitActionSeverity),
|
||||
|
@ -121,8 +345,67 @@ enum UnitAction {
|
|||
Exit(UnitActionSeverity),
|
||||
}
|
||||
|
||||
enum UnitActionSeverity {
|
||||
impl UnitAction {
|
||||
pub fn from_str(value: &str) -> Option<UnitAction> {
|
||||
Some(match value.trim().to_ascii_lowercase().as_str() {
|
||||
"none" => UnitAction::None,
|
||||
"reboot" => UnitAction::Reboot(UnitActionSeverity::None),
|
||||
"reboot-force" => UnitAction::Reboot(UnitActionSeverity::Force),
|
||||
"reboot-immediate" => UnitAction::Reboot(UnitActionSeverity::Immediate),
|
||||
"poweroff" => UnitAction::PowerOff(UnitActionSeverity::None),
|
||||
"poweroff-force" => UnitAction::PowerOff(UnitActionSeverity::Force),
|
||||
"poweroff-immediate" => UnitAction::PowerOff(UnitActionSeverity::Immediate),
|
||||
"exit" => UnitAction::Exit(UnitActionSeverity::None),
|
||||
"exit-force" => UnitAction::Exit(UnitActionSeverity::Force),
|
||||
_ => return None
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Ord, PartialOrd, Eq, PartialEq, Debug, Copy, Clone)]
|
||||
pub enum UnitActionSeverity {
|
||||
None,
|
||||
Force,
|
||||
Immediate,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_simple_unit_parsing() {
|
||||
let mut config = Config::new("test.unit");
|
||||
let res = config.load_file("/test.unit", r"
|
||||
[Unit]
|
||||
Description=It's just a test.
|
||||
Documentation=none :)
|
||||
Documentation=man:test https://example.com
|
||||
Before=sysinit.target multi-user.target
|
||||
Before=ye.target
|
||||
Before=
|
||||
Before=ohno.target
|
||||
After=time.target
|
||||
After=myself.target you.target
|
||||
FailureAction=reboot-immediate
|
||||
CollectMode=inactive-or-failed
|
||||
FailureActionExitStatus=1
|
||||
SuccessActionExitStatus=256
|
||||
");
|
||||
|
||||
assert!(res.is_ok());
|
||||
|
||||
let unit = config.get_unit();
|
||||
assert!(unit.is_some());
|
||||
let unit = unit.unwrap();
|
||||
assert_eq!(unit.description, "It's just a test.");
|
||||
assert_eq!(unit.documentation, vec!["man:test", "https://example.com"]);
|
||||
assert_eq!(unit.before, vec!["ohno.target"]);
|
||||
assert_eq!(unit.after, vec!["time.target", "myself.target", "you.target"]);
|
||||
assert_eq!(unit.failure_action, UnitAction::Reboot(UnitActionSeverity::Immediate));
|
||||
assert_eq!(unit.collect_mode, CollectMode::InactiveOrFailed);
|
||||
assert_eq!(unit.success_action_exit_status, None);
|
||||
assert_eq!(unit.failure_action_exit_status, Some(1));
|
||||
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue