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