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