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