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