]> git.proxmox.com Git - proxmox-backup.git/blob - src/api2/node/syslog.rs
avoid some clippy warnings
[proxmox-backup.git] / src / api2 / node / syslog.rs
1 use failure::*;
2
3 use crate::api_schema::*;
4 use crate::api_schema::router::*;
5 use crate::api2::types::*;
6
7 use serde_json::{json, Value};
8
9 use std::sync::Arc;
10 use lazy_static::lazy_static;
11 use proxmox::tools::common_regex;
12 use std::process::{Command, Stdio};
13
14 fn 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
22 let mut args = vec!["-o", "short", "--no-pager"];
23
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]); }
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() {
42 match line {
43 Ok(line) => {
44 count += 1;
45 if count < start { continue };
46 if limit == 0 { continue };
47
48 lines.push(json!({ "n": count, "t": line }));
49
50 limit -= 1;
51 }
52 Err(err) => {
53 log::error!("reading journal failed: {}", err);
54 let _ = child.kill();
55 break;
56 }
57 }
58 }
59 }
60
61 let status = child.wait().unwrap();
62 if !status.success() {
63 log::error!("journalctl failed with {}", status);
64 }
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 }
75
76 fn get_syslog(
77 param: Value,
78 _info: &ApiMethod,
79 rpcenv: &mut dyn RpcEnvironment,
80 ) -> Result<Value, Error> {
81
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(),
87 param["service"].as_str())?;
88
89 rpcenv.set_result_attrib("total", Value::from(count));
90
91 Ok(json!(lines))
92 }
93
94 lazy_static! {
95 pub static ref SYSTEMD_DATETIME_FORMAT: Arc<ApiStringFormat> =
96 ApiStringFormat::Pattern(&common_regex::SYSTEMD_DATETIME_REGEX).into();
97 }
98
99 pub 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.")
106 .required("node", NODE_SCHEMA.clone())
107 .optional(
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."))
136 ).protected(true)
137 );
138
139 route
140 }