1 use std
::process
::{Command, Stdio}
;
4 use serde_json
::{json, Value}
;
6 use proxmox
::{sortable, identity, list_subdirs_api_method}
;
7 use proxmox
::api
::{api, Router, Permission}
;
8 use proxmox
::api
::router
::SubdirMap
;
9 use proxmox
::api
::schema
::*;
11 use crate::api2
::types
::*;
12 use crate::config
::acl
::{PRIV_SYS_AUDIT, PRIV_SYS_MODIFY}
;
14 static SERVICE_NAME_LIST
: [&str; 7] = [
16 "proxmox-backup-proxy",
24 fn real_service_name(service
: &str) -> &str {
26 // since postfix package 3.1.0-3.1 the postfix unit is only here
27 // to manage subinstances, of which the default is called "-".
28 // This is where we look for the daemon status
30 if service
== "postfix" {
37 fn get_full_service_state(service
: &str) -> Result
<Value
, Error
> {
39 let real_service_name
= real_service_name(service
);
41 let mut child
= Command
::new("/bin/systemctl")
42 .args(&["show", real_service_name
])
43 .stdout(Stdio
::piped())
46 use std
::io
::{BufRead,BufReader}
;
48 let mut result
= json
!({}
);
50 if let Some(ref mut stdout
) = child
.stdout
{
51 for line
in BufReader
::new(stdout
).lines() {
54 let mut iter
= line
.splitn(2, '
='
);
55 let key
= iter
.next();
56 let value
= iter
.next();
57 if let (Some(key
), Some(value
)) = (key
, value
) {
58 result
[key
] = Value
::from(value
);
62 log
::error
!("reading service config failed: {}", err
);
70 let status
= child
.wait().unwrap();
71 if !status
.success() {
72 bail
!("systemctl show failed with {}", status
);
78 fn json_service_state(service
: &str, status
: Value
) -> Value
{
80 if let Some(desc
) = status
["Description"].as_str() {
81 let name
= status
["Name"].as_str().unwrap_or(service
);
82 let state
= status
["SubState"].as_str().unwrap_or("unknown");
103 description
: "Returns a list of systemd services.",
106 description
: "Service details.",
109 schema
: SERVICE_ID_SCHEMA
,
113 description
: "systemd service name.",
117 description
: "systemd service description.",
121 description
: "systemd service 'SubState'.",
127 permission
: &Permission
::Privilege(&[], PRIV_SYS_AUDIT
, false),
133 ) -> Result
<Value
, Error
> {
135 let mut list
= vec
![];
137 for service
in &SERVICE_NAME_LIST
{
138 match get_full_service_state(service
) {
140 let state
= json_service_state(service
, status
);
141 if state
!= Value
::Null
{
145 Err(err
) => log
::error
!("{}", err
),
149 Ok(Value
::from(list
))
159 schema
: SERVICE_ID_SCHEMA
,
164 permission
: &Permission
::Privilege(&[], PRIV_SYS_AUDIT
, false),
167 /// Read service properties.
168 fn get_service_state(
171 ) -> Result
<Value
, Error
> {
173 let service
= service
.as_str();
175 if !SERVICE_NAME_LIST
.contains(&service
) {
176 bail
!("unknown service name '{}'", service
);
179 let status
= get_full_service_state(&service
)?
;
181 Ok(json_service_state(&service
, status
))
184 fn run_service_command(service
: &str, cmd
: &str) -> Result
<Value
, Error
> {
186 // fixme: run background worker (fork_worker) ???
189 "start"|"stop"|"restart"|"reload" => {}
,
190 _
=> bail
!("unknown service command '{}'", cmd
),
193 if service
== "proxmox-backup" && cmd
!= "restart" {
194 bail
!("invalid service cmd '{} {}'", service
, cmd
);
197 let real_service_name
= real_service_name(service
);
199 let status
= Command
::new("/bin/systemctl")
200 .args(&[cmd
, real_service_name
])
203 if !status
.success() {
204 bail
!("systemctl {} failed with {}", cmd
, status
);
218 schema
: SERVICE_ID_SCHEMA
,
223 permission
: &Permission
::Privilege(&[], PRIV_SYS_MODIFY
, false),
230 ) -> Result
<Value
, Error
> {
232 log
::info
!("starting service {}", service
);
234 run_service_command(&service
, "start")
245 schema
: SERVICE_ID_SCHEMA
,
250 permission
: &Permission
::Privilege(&[], PRIV_SYS_MODIFY
, false),
257 ) -> Result
<Value
, Error
> {
259 log
::info
!("stoping service {}", service
);
261 run_service_command(&service
, "stop")
272 schema
: SERVICE_ID_SCHEMA
,
277 permission
: &Permission
::Privilege(&[], PRIV_SYS_MODIFY
, false),
284 ) -> Result
<Value
, Error
> {
286 log
::info
!("re-starting service {}", service
);
288 if &service
== "proxmox-backup-proxy" {
289 // special case, avoid aborting running tasks
290 run_service_command(&service
, "reload")
292 run_service_command(&service
, "restart")
304 schema
: SERVICE_ID_SCHEMA
,
309 permission
: &Permission
::Privilege(&[], PRIV_SYS_MODIFY
, false),
316 ) -> Result
<Value
, Error
> {
318 log
::info
!("reloading service {}", service
);
320 run_service_command(&service
, "reload")
324 const SERVICE_ID_SCHEMA
: Schema
= StringSchema
::new("Service ID.")
329 const SERVICE_SUBDIRS
: SubdirMap
= &sorted
!([
331 "reload", &Router
::new()
332 .post(&API_METHOD_RELOAD_SERVICE
)
335 "restart", &Router
::new()
336 .post(&API_METHOD_RESTART_SERVICE
)
339 "start", &Router
::new()
340 .post(&API_METHOD_START_SERVICE
)
343 "state", &Router
::new()
344 .get(&API_METHOD_GET_SERVICE_STATE
)
347 "stop", &Router
::new()
348 .post(&API_METHOD_STOP_SERVICE
)
352 const SERVICE_ROUTER
: Router
= Router
::new()
353 .get(&list_subdirs_api_method
!(SERVICE_SUBDIRS
))
354 .subdirs(SERVICE_SUBDIRS
);
356 pub const ROUTER
: Router
= Router
::new()
357 .get(&API_METHOD_LIST_SERVICES
)
358 .match_all("service", &SERVICE_ROUTER
);