use super::{command_mechanism, ActionSpec};
use sysknife_types::RiskLevel;
pub fn specs() -> Vec<ActionSpec> {
vec![
list_services(),
start_service("NetworkManager.service"),
stop_service("NetworkManager.service"),
restart_service("NetworkManager.service"),
set_service_enabled("sshd.service", true),
mask_service("cups.service"),
unmask_service("cups.service"),
get_service_logs("NetworkManager.service"),
get_service_status("nginx.service"),
reload_service("nginx.service"),
list_timers(),
reload_daemon(),
create_scheduled_job("sysknife-example", "/usr/bin/true", "*-*-* 02:00:00"),
get_service_resource_limits("nginx.service"),
set_service_resource_limits(
"nginx.service",
&["MemoryMax=500M".to_string(), "CPUQuota=50%".to_string()],
),
]
}
pub fn get_service_resource_limits(unit: &str) -> ActionSpec {
ActionSpec {
action_name: "GetServiceResourceLimits",
mechanism: command_mechanism(
"systemctl",
[
"show",
unit,
"--property=MemoryMax,MemoryHigh,CPUQuotaPerSecUSec,TasksMax",
],
),
risk_level: RiskLevel::Low,
reboot_required: false,
rollback_available: false,
}
}
pub fn set_service_resource_limits(unit: &str, assignments: &[String]) -> ActionSpec {
let mut args = vec![
"systemctl".to_string(),
"set-property".to_string(),
unit.to_string(),
];
args.extend(assignments.iter().cloned());
ActionSpec {
action_name: "SetServiceResourceLimits",
mechanism: command_mechanism("sudo", args),
risk_level: RiskLevel::Medium,
reboot_required: false,
rollback_available: false,
}
}
const SCHEDULED_JOB_HELPER: &str = "/usr/lib/sysknife/scheduled-job-edit";
const ACTION_STEPS: &str = "/usr/lib/sysknife/action-steps";
pub fn create_scheduled_job(name: &str, command: &str, schedule: &str) -> ActionSpec {
ActionSpec {
action_name: "CreateScheduledJob",
mechanism: command_mechanism(
"sudo",
[
SCHEDULED_JOB_HELPER,
"--name",
name,
"--command",
command,
"--schedule",
schedule,
],
),
risk_level: RiskLevel::High,
reboot_required: false,
rollback_available: false,
}
}
pub fn list_services() -> ActionSpec {
ActionSpec {
action_name: "ListServices",
mechanism: command_mechanism(
"systemctl",
[
"list-units",
"--type=service",
"--all",
"--no-legend",
"--no-pager",
],
),
risk_level: RiskLevel::Low,
reboot_required: false,
rollback_available: false,
}
}
pub fn start_service(unit: &str) -> ActionSpec {
ActionSpec {
action_name: "StartService",
mechanism: command_mechanism("sudo", [ACTION_STEPS, "unit", "start", unit]),
risk_level: RiskLevel::Medium,
reboot_required: false,
rollback_available: false,
}
}
pub fn stop_service(unit: &str) -> ActionSpec {
ActionSpec {
action_name: "StopService",
mechanism: command_mechanism("sudo", [ACTION_STEPS, "unit", "stop", unit]),
risk_level: RiskLevel::Medium,
reboot_required: false,
rollback_available: false,
}
}
pub fn restart_service(unit: &str) -> ActionSpec {
ActionSpec {
action_name: "RestartService",
mechanism: command_mechanism("sudo", [ACTION_STEPS, "unit", "restart", unit]),
risk_level: RiskLevel::Medium,
reboot_required: false,
rollback_available: false,
}
}
pub fn set_service_enabled(unit: &str, enabled: bool) -> ActionSpec {
let verb = if enabled { "enable" } else { "disable" };
ActionSpec {
action_name: "SetServiceEnabled",
mechanism: command_mechanism("sudo", [ACTION_STEPS, "unit", verb, unit]),
risk_level: RiskLevel::Medium,
reboot_required: false,
rollback_available: false,
}
}
pub fn mask_service(unit: &str) -> ActionSpec {
ActionSpec {
action_name: "MaskService",
mechanism: command_mechanism("sudo", [ACTION_STEPS, "unit", "mask", unit]),
risk_level: RiskLevel::High,
reboot_required: false,
rollback_available: false,
}
}
pub fn unmask_service(unit: &str) -> ActionSpec {
ActionSpec {
action_name: "UnmaskService",
mechanism: command_mechanism("sudo", [ACTION_STEPS, "unit", "unmask", unit]),
risk_level: RiskLevel::Medium,
reboot_required: false,
rollback_available: false,
}
}
pub fn get_service_status(unit: &str) -> ActionSpec {
ActionSpec {
action_name: "GetServiceStatus",
mechanism: command_mechanism("systemctl", ["status", unit, "--no-pager"]),
risk_level: RiskLevel::Low,
reboot_required: false,
rollback_available: false,
}
}
pub fn reload_service(unit: &str) -> ActionSpec {
ActionSpec {
action_name: "ReloadService",
mechanism: command_mechanism("sudo", [ACTION_STEPS, "unit", "reload", unit]),
risk_level: RiskLevel::Medium,
reboot_required: false,
rollback_available: false,
}
}
pub fn list_timers() -> ActionSpec {
ActionSpec {
action_name: "ListTimers",
mechanism: command_mechanism(
"systemctl",
["list-timers", "--all", "--no-legend", "--no-pager"],
),
risk_level: RiskLevel::Low,
reboot_required: false,
rollback_available: false,
}
}
pub fn reload_daemon() -> ActionSpec {
ActionSpec {
action_name: "ReloadDaemon",
mechanism: command_mechanism("sudo", ["systemctl", "daemon-reload"]),
risk_level: RiskLevel::Medium,
reboot_required: false,
rollback_available: false,
}
}
pub fn get_service_logs(unit: &str) -> ActionSpec {
ActionSpec {
action_name: "GetServiceLogs",
mechanism: command_mechanism("journalctl", ["-u", unit, "-n", "200", "--no-pager"]),
risk_level: RiskLevel::Low,
reboot_required: false,
rollback_available: false,
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::actions::ActionMechanism;
fn args_of(spec: &ActionSpec) -> (&'static str, Vec<String>) {
match &spec.mechanism {
ActionMechanism::Command { program, args } => (program, args.clone()),
other => panic!("expected Command, got {other:?}"),
}
}
#[test]
fn get_resource_limits_is_read_only_show() {
let spec = get_service_resource_limits("nginx.service");
let (program, args) = args_of(&spec);
assert_eq!(program, "systemctl");
assert_eq!(args[0], "show");
assert_eq!(args[1], "nginx.service");
assert!(args[2].contains("MemoryMax") && args[2].contains("CPUQuotaPerSecUSec"));
assert_eq!(spec.risk_level, RiskLevel::Low);
}
#[test]
fn set_resource_limits_builds_set_property() {
let spec = set_service_resource_limits(
"nginx.service",
&["MemoryMax=500M".to_string(), "CPUQuota=50%".to_string()],
);
let (program, args) = args_of(&spec);
assert_eq!(program, "sudo");
assert_eq!(
args,
vec![
"systemctl",
"set-property",
"nginx.service",
"MemoryMax=500M",
"CPUQuota=50%"
]
);
assert_eq!(spec.risk_level, RiskLevel::Medium);
}
}