]>
Commit | Line | Data |
---|---|---|
4f9a7268 DM |
1 | use failure::*; |
2 | ||
ef2f2efb | 3 | use crate::api_schema::*; |
dc9a007b | 4 | use crate::api_schema::router::*; |
4ebf0eab DM |
5 | use crate::api2::types::*; |
6 | ||
4f9a7268 DM |
7 | use serde_json::{json, Value}; |
8 | ||
9 | use std::sync::Arc; | |
10 | use lazy_static::lazy_static; | |
7f66c29e | 11 | use proxmox::tools::common_regex; |
04418868 DM |
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 | ||
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 |
76 | fn 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 | ||
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.") | |
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 | } |