proxmox-backup/src/api2/node/syslog.rs

152 lines
3.9 KiB
Rust
Raw Normal View History

use std::process::{Command, Stdio};
use anyhow::{Error};
use serde_json::{json, Value};
use proxmox::api::{api, ApiMethod, Router, RpcEnvironment, Permission};
use crate::api2::types::*;
use crate::config::acl::PRIV_SYS_AUDIT;
fn dump_journal(
start: Option<u64>,
limit: Option<u64>,
since: Option<&str>,
until: Option<&str>,
service: Option<&str>,
) -> Result<(u64, Vec<Value>), Error> {
2019-01-26 09:56:11 +00:00
let mut args = vec!["-o", "short", "--no-pager"];
if let Some(service) = service { args.extend(&["--unit", service]); }
if let Some(since) = since { args.extend(&["--since", since]); }
if let Some(until) = until { args.extend(&["--until", until]); }
let mut lines: Vec<Value> = vec![];
let mut limit = limit.unwrap_or(50);
let start = start.unwrap_or(0);
let mut count: u64 = 0;
let mut child = Command::new("journalctl")
.args(&args)
.stdout(Stdio::piped())
.spawn()?;
use std::io::{BufRead,BufReader};
if let Some(ref mut stdout) = child.stdout {
for line in BufReader::new(stdout).lines() {
match line {
Ok(line) => {
count += 1;
if count < start { continue };
2019-10-25 16:04:37 +00:00
if limit == 0 { continue };
lines.push(json!({ "n": count, "t": line }));
limit -= 1;
}
Err(err) => {
2019-01-26 09:56:11 +00:00
log::error!("reading journal failed: {}", err);
let _ = child.kill();
break;
}
}
}
}
let status = child.wait().unwrap();
if !status.success() {
2019-01-26 09:56:11 +00:00
log::error!("journalctl failed with {}", status);
}
// HACK: ExtJS store.guaranteeRange() does not like empty array
// so we add a line
if count == 0 {
count += 1;
lines.push(json!({ "n": count, "t": "no content"}));
}
Ok((count, lines))
}
2019-12-17 12:26:14 +00:00
#[api(
protected: true,
input: {
properties: {
node: {
schema: NODE_SCHEMA,
},
start: {
type: Integer,
description: "Start line number.",
minimum: 0,
optional: true,
},
limit: {
type: Integer,
description: "Max. number of lines.",
optional: true,
minimum: 0,
},
since: {
type: String,
optional: true,
description: "Display all log since this date-time string.",
format: &SYSTEMD_DATETIME_FORMAT,
},
until: {
type: String,
optional: true,
description: "Display all log until this date-time string.",
format: &SYSTEMD_DATETIME_FORMAT,
},
service: {
type: String,
optional: true,
description: "Service ID.",
max_length: 128,
},
},
},
returns: {
type: Object,
description: "Returns a list of syslog entries.",
properties: {
n: {
type: Integer,
description: "Line number.",
},
t: {
type: String,
description: "Line text.",
}
},
},
access: {
2020-04-30 07:30:00 +00:00
permission: &Permission::Privilege(&["system", "log"], PRIV_SYS_AUDIT, false),
},
2019-12-17 12:26:14 +00:00
)]
/// Read syslog entries.
fn get_syslog(
param: Value,
_info: &ApiMethod,
mut rpcenv: &mut dyn RpcEnvironment,
) -> Result<Value, Error> {
let (count, lines) = dump_journal(
param["start"].as_u64(),
param["limit"].as_u64(),
param["since"].as_str(),
param["until"].as_str(),
param["service"].as_str())?;
rpcenv["total"] = Value::from(count);
Ok(json!(lines))
}
2019-11-21 08:36:41 +00:00
pub const ROUTER: Router = Router::new()
2019-12-17 12:26:14 +00:00
.get(&API_METHOD_GET_SYSLOG);