add systemd configuration file parser/writer, start job configuration
This commit is contained in:
2
src/tools/systemd.rs
Normal file
2
src/tools/systemd.rs
Normal file
@ -0,0 +1,2 @@
|
||||
pub mod types;
|
||||
pub mod parser;
|
81
src/tools/systemd/parser.rs
Normal file
81
src/tools/systemd/parser.rs
Normal file
@ -0,0 +1,81 @@
|
||||
use anyhow::Error;
|
||||
use lazy_static::lazy_static;
|
||||
|
||||
use super::types::*;
|
||||
|
||||
use proxmox::api::{
|
||||
schema::*,
|
||||
section_config::{
|
||||
SectionConfig,
|
||||
SectionConfigData,
|
||||
SectionConfigPlugin,
|
||||
}
|
||||
};
|
||||
|
||||
use proxmox::tools::{fs::replace_file, fs::CreateOptions};
|
||||
|
||||
|
||||
lazy_static! {
|
||||
pub static ref CONFIG: SectionConfig = init();
|
||||
}
|
||||
|
||||
fn init() -> SectionConfig {
|
||||
|
||||
let mut config = SectionConfig::with_systemd_syntax(&SYSTEMD_SECTION_NAME_SCHEMA);
|
||||
|
||||
match SystemdUnitSection::API_SCHEMA {
|
||||
Schema::Object(ref obj_schema) => {
|
||||
let plugin = SectionConfigPlugin::new("Unit".to_string(), obj_schema);
|
||||
config.register_plugin(plugin);
|
||||
}
|
||||
_ => unreachable!(),
|
||||
};
|
||||
match SystemdInstallSection::API_SCHEMA {
|
||||
Schema::Object(ref obj_schema) => {
|
||||
let plugin = SectionConfigPlugin::new("Install".to_string(), obj_schema);
|
||||
config.register_plugin(plugin);
|
||||
}
|
||||
_ => unreachable!(),
|
||||
};
|
||||
match SystemdServiceSection::API_SCHEMA {
|
||||
Schema::Object(ref obj_schema) => {
|
||||
let plugin = SectionConfigPlugin::new("Service".to_string(), obj_schema);
|
||||
config.register_plugin(plugin);
|
||||
}
|
||||
_ => unreachable!(),
|
||||
};
|
||||
match SystemdTimerSection::API_SCHEMA {
|
||||
Schema::Object(ref obj_schema) => {
|
||||
let plugin = SectionConfigPlugin::new("Timer".to_string(), obj_schema);
|
||||
config.register_plugin(plugin);
|
||||
}
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
config
|
||||
}
|
||||
|
||||
pub fn parse_systemd_config(filename: &str) -> Result<SectionConfigData, Error> {
|
||||
|
||||
let raw = proxmox::tools::fs::file_get_contents(filename)?;
|
||||
let input = String::from_utf8(raw)?;
|
||||
|
||||
let data = CONFIG.parse(filename, &input)?;
|
||||
|
||||
Ok(data)
|
||||
}
|
||||
|
||||
|
||||
pub fn save_systemd_config(filename: &str, config: &SectionConfigData) -> Result<(), Error> {
|
||||
let raw = CONFIG.write(filename, &config)?;
|
||||
|
||||
let mode = nix::sys::stat::Mode::from_bits_truncate(0o0644);
|
||||
// set the correct owner/group/permissions while saving file, owner(rw) = root
|
||||
let options = CreateOptions::new()
|
||||
.perm(mode)
|
||||
.owner(nix::unistd::ROOT);
|
||||
|
||||
replace_file(filename, raw.as_bytes(), options)?;
|
||||
|
||||
Ok(())
|
||||
}
|
140
src/tools/systemd/types.rs
Normal file
140
src/tools/systemd/types.rs
Normal file
@ -0,0 +1,140 @@
|
||||
use serde::{Serialize, Deserialize};
|
||||
|
||||
use proxmox::api::{ api, schema::* };
|
||||
use crate::api2::types::SINGLE_LINE_COMMENT_FORMAT;
|
||||
|
||||
pub const SYSTEMD_SECTION_NAME_SCHEMA: Schema = StringSchema::new(
|
||||
"Section name")
|
||||
.format(&ApiStringFormat::Enum(&[
|
||||
EnumEntry::new("Unit", "Unit"),
|
||||
EnumEntry::new("Timer", "Timer"),
|
||||
EnumEntry::new("Install", "Install"),
|
||||
EnumEntry::new("Service", "Service")]))
|
||||
.schema();
|
||||
|
||||
pub const SYSTEMD_STRING_SCHEMA: Schema =
|
||||
StringSchema::new("Systemd configuration value.")
|
||||
.format(&SINGLE_LINE_COMMENT_FORMAT)
|
||||
.schema();
|
||||
|
||||
pub const SYSTEMD_STRING_ARRAY_SCHEMA: Schema = ArraySchema::new(
|
||||
"Array of Strings", &SYSTEMD_STRING_SCHEMA)
|
||||
.schema();
|
||||
|
||||
#[api(
|
||||
properties: {
|
||||
"OnCalendar": {
|
||||
type: Array,
|
||||
optional: true,
|
||||
items: {
|
||||
description: "Calendar event expression.",
|
||||
type: String,
|
||||
},
|
||||
},
|
||||
},
|
||||
)]
|
||||
#[derive(Serialize, Deserialize, Default)]
|
||||
#[allow(non_snake_case)]
|
||||
/// Systemd Timer Section
|
||||
pub struct SystemdTimerSection {
|
||||
/// Calender event list.
|
||||
#[serde(skip_serializing_if="Option::is_none")]
|
||||
pub OnCalendar: Option<Vec<String>>,
|
||||
/// If true, the time when the service unit was last triggered is stored on disk.
|
||||
#[serde(skip_serializing_if="Option::is_none")]
|
||||
pub Persistent: Option<bool>,
|
||||
}
|
||||
|
||||
#[api(
|
||||
properties: {
|
||||
"Type": {
|
||||
type: ServiceStartup,
|
||||
optional: true,
|
||||
},
|
||||
"ExecStart": {
|
||||
schema: SYSTEMD_STRING_ARRAY_SCHEMA,
|
||||
optional: true,
|
||||
},
|
||||
}
|
||||
)]
|
||||
#[derive(Serialize, Deserialize, Default)]
|
||||
#[allow(non_snake_case)]
|
||||
/// Systemd Service Section
|
||||
pub struct SystemdServiceSection {
|
||||
/// The process start-up type for this service unit.
|
||||
#[serde(skip_serializing_if="Option::is_none")]
|
||||
pub Type: Option<ServiceStartup>,
|
||||
#[serde(skip_serializing_if="Option::is_none")]
|
||||
pub ExecStart: Option<Vec<String>>,
|
||||
}
|
||||
|
||||
#[api()]
|
||||
#[derive(Serialize, Deserialize, Default)]
|
||||
#[allow(non_snake_case)]
|
||||
/// Systemd Unit Section
|
||||
pub struct SystemdUnitSection {
|
||||
/// A human readable name for the unit.
|
||||
pub Description: String,
|
||||
/// Check whether the system has AC power.
|
||||
#[serde(skip_serializing_if="Option::is_none")]
|
||||
pub ConditionACPower: Option<bool>,
|
||||
}
|
||||
|
||||
#[api(
|
||||
properties: {
|
||||
"Alias": {
|
||||
schema: SYSTEMD_STRING_ARRAY_SCHEMA,
|
||||
optional: true,
|
||||
},
|
||||
"Also": {
|
||||
schema: SYSTEMD_STRING_ARRAY_SCHEMA,
|
||||
optional: true,
|
||||
},
|
||||
"DefaultInstance": {
|
||||
optional: true,
|
||||
},
|
||||
"WantedBy": {
|
||||
schema: SYSTEMD_STRING_ARRAY_SCHEMA,
|
||||
optional: true,
|
||||
},
|
||||
"RequiredBy": {
|
||||
schema: SYSTEMD_STRING_ARRAY_SCHEMA,
|
||||
optional: true,
|
||||
},
|
||||
},
|
||||
)]
|
||||
#[derive(Serialize, Deserialize, Default)]
|
||||
#[allow(non_snake_case)]
|
||||
/// Systemd Install Section
|
||||
pub struct SystemdInstallSection {
|
||||
#[serde(skip_serializing_if="Option::is_none")]
|
||||
pub Alias: Option<Vec<String>>,
|
||||
#[serde(skip_serializing_if="Option::is_none")]
|
||||
pub Also: Option<Vec<String>>,
|
||||
/// DefaultInstance for template unit.
|
||||
#[serde(skip_serializing_if="Option::is_none")]
|
||||
pub DefaultInstance: Option<String>,
|
||||
#[serde(skip_serializing_if="Option::is_none")]
|
||||
pub WantedBy: Option<Vec<String>>,
|
||||
#[serde(skip_serializing_if="Option::is_none")]
|
||||
pub RequiredBy: Option<Vec<String>>,
|
||||
}
|
||||
|
||||
#[api()]
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
/// Node Power command type.
|
||||
pub enum ServiceStartup {
|
||||
/// Simple fork
|
||||
Simple,
|
||||
/// Like fork, but wait until exec succeeds
|
||||
Exec,
|
||||
/// Fork daemon
|
||||
Forking,
|
||||
/// Like 'simple', but consider the unit up after the process exits.
|
||||
Oneshot,
|
||||
/// Like 'simple', but use DBUS to synchronize startup.
|
||||
Dbus,
|
||||
/// Like 'simple', but use sd_notify to synchronize startup.
|
||||
Notify,
|
||||
}
|
Reference in New Issue
Block a user