]> git.proxmox.com Git - proxmox-backup.git/blame - src/api2/node/syslog.rs
avoid some clippy warnings
[proxmox-backup.git] / src / api2 / node / syslog.rs
CommitLineData
4f9a7268
DM
1use failure::*;
2
ef2f2efb 3use crate::api_schema::*;
dc9a007b 4use crate::api_schema::router::*;
4ebf0eab
DM
5use crate::api2::types::*;
6
4f9a7268
DM
7use serde_json::{json, Value};
8
9use std::sync::Arc;
10use lazy_static::lazy_static;
7f66c29e 11use proxmox::tools::common_regex;
04418868
DM
12use std::process::{Command, Stdio};
13
14fn dump_journal(
15 start: Option<u64>,
16 limit: Option<u64>,
17 since: Option<&str>,
18 until: Option<&str>,
19 service: Option<&str>,
20) -> Result<(u64, Vec<Value>), Error> {
21
d96d8273 22 let mut args = vec!["-o", "short", "--no-pager"];
04418868 23
e182ab4a
DM
24 if let Some(service) = service { args.extend(&["--unit", service]); }
25 if let Some(since) = since { args.extend(&["--since", since]); }
26 if let Some(until) = until { args.extend(&["--until", until]); }
04418868
DM
27
28 let mut lines: Vec<Value> = vec![];
29 let mut limit = limit.unwrap_or(50);
30 let start = start.unwrap_or(0);
31 let mut count: u64 = 0;
32
33 let mut child = Command::new("/bin/journalctl")
34 .args(&args)
35 .stdout(Stdio::piped())
36 .spawn()?;
37
38 use std::io::{BufRead,BufReader};
39
40 if let Some(ref mut stdout) = child.stdout {
41 for line in BufReader::new(stdout).lines() {
e182ab4a
DM
42 match line {
43 Ok(line) => {
44 count += 1;
45 if count < start { continue };
11377a47 46 if limit == 0 { continue };
e182ab4a
DM
47
48 lines.push(json!({ "n": count, "t": line }));
49
50 limit -= 1;
51 }
52 Err(err) => {
d96d8273 53 log::error!("reading journal failed: {}", err);
e182ab4a
DM
54 let _ = child.kill();
55 break;
56 }
57 }
04418868
DM
58 }
59 }
60
e182ab4a
DM
61 let status = child.wait().unwrap();
62 if !status.success() {
d96d8273 63 log::error!("journalctl failed with {}", status);
e182ab4a 64 }
04418868
DM
65
66 // HACK: ExtJS store.guaranteeRange() does not like empty array
67 // so we add a line
68 if count == 0 {
69 count += 1;
70 lines.push(json!({ "n": count, "t": "no content"}));
71 }
72
73 Ok((count, lines))
74}
4f9a7268 75
6049b71f
DM
76fn get_syslog(
77 param: Value,
78 _info: &ApiMethod,
dd5495d6 79 rpcenv: &mut dyn RpcEnvironment,
6049b71f 80) -> Result<Value, Error> {
4f9a7268 81
04418868
DM
82 let (count, lines) = dump_journal(
83 param["start"].as_u64(),
84 param["limit"].as_u64(),
85 param["since"].as_str(),
86 param["until"].as_str(),
e182ab4a 87 param["service"].as_str())?;
04418868 88
6049b71f 89 rpcenv.set_result_attrib("total", Value::from(count));
4f9a7268 90
04418868 91 Ok(json!(lines))
4f9a7268
DM
92}
93
94lazy_static! {
95 pub static ref SYSTEMD_DATETIME_FORMAT: Arc<ApiStringFormat> =
96 ApiStringFormat::Pattern(&common_regex::SYSTEMD_DATETIME_REGEX).into();
97}
98
99pub fn router() -> Router {
100
101 let route = Router::new()
102 .get(
103 ApiMethod::new(
104 get_syslog,
105 ObjectSchema::new("Read server time and time zone settings.")
4ebf0eab 106 .required("node", NODE_SCHEMA.clone())
d8d40dd0 107 .optional(
4f9a7268
DM
108 "start",
109 IntegerSchema::new("Start line number.")
110 .minimum(0)
111 )
112 .optional(
113 "limit",
114 IntegerSchema::new("Max. number of lines.")
115 .minimum(0)
116 )
117 .optional(
118 "since",
119 StringSchema::new("Display all log since this date-time string.")
120 .format(SYSTEMD_DATETIME_FORMAT.clone())
121 )
122 .optional(
123 "until",
124 StringSchema::new("Display all log until this date-time string.")
125 .format(SYSTEMD_DATETIME_FORMAT.clone())
126 )
127 .optional(
128 "service",
129 StringSchema::new("Service ID.")
130 .max_length(128)
131 )
132 ).returns(
133 ObjectSchema::new("Returns a list of syslog entries.")
134 .required("n", IntegerSchema::new("Line number."))
135 .required("t", StringSchema::new("Line text."))
a859fa95 136 ).protected(true)
4f9a7268
DM
137 );
138
139 route
140}