|
|
|
@ -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));
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
}
|