1use super::{command_mechanism, ActionSpec};
2use sysknife_types::RiskLevel;
3
4pub fn specs() -> Vec<ActionSpec> {
5 vec![
6 list_services(),
7 start_service("NetworkManager.service"),
8 stop_service("NetworkManager.service"),
9 restart_service("NetworkManager.service"),
10 set_service_enabled("sshd.service", true),
11 mask_service("cups.service"),
12 unmask_service("cups.service"),
13 get_service_logs("NetworkManager.service"),
14 get_service_status("nginx.service"),
15 reload_service("nginx.service"),
16 list_timers(),
17 reload_daemon(),
18 create_scheduled_job("sysknife-example", "/usr/bin/true", "*-*-* 02:00:00"),
19 get_service_resource_limits("nginx.service"),
20 set_service_resource_limits(
21 "nginx.service",
22 &["MemoryMax=500M".to_string(), "CPUQuota=50%".to_string()],
23 ),
24 ]
25}
26
27pub fn get_service_resource_limits(unit: &str) -> ActionSpec {
32 ActionSpec {
33 action_name: "GetServiceResourceLimits",
34 mechanism: command_mechanism(
35 "systemctl",
36 [
37 "show",
38 unit,
39 "--property=MemoryMax,MemoryHigh,CPUQuotaPerSecUSec,TasksMax",
40 ],
41 ),
42 risk_level: RiskLevel::Low,
43 reboot_required: false,
44 rollback_available: false,
45 }
46}
47
48pub fn set_service_resource_limits(unit: &str, assignments: &[String]) -> ActionSpec {
58 let mut args = vec![
59 "systemctl".to_string(),
60 "set-property".to_string(),
61 unit.to_string(),
62 ];
63 args.extend(assignments.iter().cloned());
64 ActionSpec {
65 action_name: "SetServiceResourceLimits",
66 mechanism: command_mechanism("sudo", args),
67 risk_level: RiskLevel::Medium,
68 reboot_required: false,
69 rollback_available: false,
70 }
71}
72
73const SCHEDULED_JOB_HELPER: &str = "/usr/lib/sysknife/scheduled-job-edit";
77
78pub fn create_scheduled_job(name: &str, command: &str, schedule: &str) -> ActionSpec {
87 ActionSpec {
88 action_name: "CreateScheduledJob",
89 mechanism: command_mechanism(
90 "sudo",
91 [
92 SCHEDULED_JOB_HELPER,
93 "--name",
94 name,
95 "--command",
96 command,
97 "--schedule",
98 schedule,
99 ],
100 ),
101 risk_level: RiskLevel::High,
102 reboot_required: false,
103 rollback_available: false,
104 }
105}
106
107pub fn list_services() -> ActionSpec {
108 ActionSpec {
109 action_name: "ListServices",
110 mechanism: command_mechanism(
111 "systemctl",
112 [
113 "list-units",
114 "--type=service",
115 "--all",
116 "--no-legend",
117 "--no-pager",
118 ],
119 ),
120 risk_level: RiskLevel::Low,
121 reboot_required: false,
122 rollback_available: false,
123 }
124}
125
126pub fn start_service(unit: &str) -> ActionSpec {
127 ActionSpec {
128 action_name: "StartService",
129 mechanism: command_mechanism("sudo", ["systemctl", "start", unit]),
130 risk_level: RiskLevel::Medium,
131 reboot_required: false,
132 rollback_available: false,
133 }
134}
135
136pub fn stop_service(unit: &str) -> ActionSpec {
137 ActionSpec {
138 action_name: "StopService",
139 mechanism: command_mechanism("sudo", ["systemctl", "stop", unit]),
140 risk_level: RiskLevel::Medium,
141 reboot_required: false,
142 rollback_available: false,
143 }
144}
145
146pub fn restart_service(unit: &str) -> ActionSpec {
147 ActionSpec {
148 action_name: "RestartService",
149 mechanism: command_mechanism("sudo", ["systemctl", "restart", unit]),
150 risk_level: RiskLevel::Medium,
151 reboot_required: false,
152 rollback_available: false,
153 }
154}
155
156pub fn set_service_enabled(unit: &str, enabled: bool) -> ActionSpec {
157 let verb = if enabled { "enable" } else { "disable" };
158
159 ActionSpec {
160 action_name: "SetServiceEnabled",
161 mechanism: command_mechanism("sudo", ["systemctl", verb, unit]),
162 risk_level: RiskLevel::Medium,
163 reboot_required: false,
164 rollback_available: false,
165 }
166}
167
168pub fn mask_service(unit: &str) -> ActionSpec {
169 ActionSpec {
170 action_name: "MaskService",
171 mechanism: command_mechanism("sudo", ["systemctl", "mask", unit]),
172 risk_level: RiskLevel::High,
173 reboot_required: false,
174 rollback_available: false,
175 }
176}
177
178pub fn unmask_service(unit: &str) -> ActionSpec {
179 ActionSpec {
180 action_name: "UnmaskService",
181 mechanism: command_mechanism("sudo", ["systemctl", "unmask", unit]),
182 risk_level: RiskLevel::Medium,
183 reboot_required: false,
184 rollback_available: false,
185 }
186}
187
188pub fn get_service_status(unit: &str) -> ActionSpec {
189 ActionSpec {
193 action_name: "GetServiceStatus",
194 mechanism: command_mechanism("systemctl", ["status", unit, "--no-pager"]),
195 risk_level: RiskLevel::Low,
196 reboot_required: false,
197 rollback_available: false,
198 }
199}
200
201pub fn reload_service(unit: &str) -> ActionSpec {
202 ActionSpec {
208 action_name: "ReloadService",
209 mechanism: command_mechanism("sudo", ["systemctl", "reload", unit]),
210 risk_level: RiskLevel::Medium,
211 reboot_required: false,
212 rollback_available: false,
213 }
214}
215
216pub fn list_timers() -> ActionSpec {
217 ActionSpec {
221 action_name: "ListTimers",
222 mechanism: command_mechanism(
223 "systemctl",
224 ["list-timers", "--all", "--no-legend", "--no-pager"],
225 ),
226 risk_level: RiskLevel::Low,
227 reboot_required: false,
228 rollback_available: false,
229 }
230}
231
232pub fn reload_daemon() -> ActionSpec {
233 ActionSpec {
238 action_name: "ReloadDaemon",
239 mechanism: command_mechanism("sudo", ["systemctl", "daemon-reload"]),
240 risk_level: RiskLevel::Medium,
241 reboot_required: false,
242 rollback_available: false,
243 }
244}
245
246pub fn get_service_logs(unit: &str) -> ActionSpec {
247 ActionSpec {
248 action_name: "GetServiceLogs",
249 mechanism: command_mechanism("journalctl", ["-u", unit, "-n", "200", "--no-pager"]),
250 risk_level: RiskLevel::Low,
251 reboot_required: false,
252 rollback_available: false,
253 }
254}
255
256#[cfg(test)]
257mod tests {
258 use super::*;
259 use crate::actions::ActionMechanism;
260
261 fn args_of(spec: &ActionSpec) -> (&'static str, Vec<String>) {
262 match &spec.mechanism {
263 ActionMechanism::Command { program, args } => (program, args.clone()),
264 other => panic!("expected Command, got {other:?}"),
265 }
266 }
267
268 #[test]
269 fn get_resource_limits_is_read_only_show() {
270 let spec = get_service_resource_limits("nginx.service");
271 let (program, args) = args_of(&spec);
272 assert_eq!(program, "systemctl");
273 assert_eq!(args[0], "show");
274 assert_eq!(args[1], "nginx.service");
275 assert!(args[2].contains("MemoryMax") && args[2].contains("CPUQuotaPerSecUSec"));
276 assert_eq!(spec.risk_level, RiskLevel::Low);
277 }
278
279 #[test]
280 fn set_resource_limits_builds_set_property() {
281 let spec = set_service_resource_limits(
282 "nginx.service",
283 &["MemoryMax=500M".to_string(), "CPUQuota=50%".to_string()],
284 );
285 let (program, args) = args_of(&spec);
286 assert_eq!(program, "sudo");
287 assert_eq!(
288 args,
289 vec![
290 "systemctl",
291 "set-property",
292 "nginx.service",
293 "MemoryMax=500M",
294 "CPUQuota=50%"
295 ]
296 );
297 assert_eq!(spec.risk_level, RiskLevel::Medium);
298 }
299}