mirror of
https://git.proxmox.com/git/pmg-log-tracker
synced 2025-08-11 15:01:14 +00:00
drop 'time' dependency
We're using a very old version of it and the functions we actually use are available in glibc anyway. The only difference I found was that the result of glibc's `strptime()`'s `%s` does *not* want an additional `.to_local()` call anymore... ... which was weird anyway. As a minor optimization I pass the format string as `&CStr` to avoid the memcpy. (The CString::new() call in `strptime()` only happens during parameter parsing) Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com> Reviewed-By: Stoiko Ivanov <s.ivanov@proxmox.com> Tested-By: Stoiko Ivanov <s.ivanov@proxmox.com> [dropped left-over and fixed FIXME] Signed-off-by: Stoiko Ivanov <s.ivanov@proxmox.com>
This commit is contained in:
parent
a4d769405c
commit
8f1719eebe
@ -14,4 +14,3 @@ anyhow = "1"
|
|||||||
clap = "2.32"
|
clap = "2.32"
|
||||||
flate2 = "1.0"
|
flate2 = "1.0"
|
||||||
libc = "0.2.48"
|
libc = "0.2.48"
|
||||||
time = "0.1.42"
|
|
||||||
|
74
src/main.rs
74
src/main.rs
@ -15,6 +15,9 @@ use libc::time_t;
|
|||||||
|
|
||||||
use clap::{App, Arg};
|
use clap::{App, Arg};
|
||||||
|
|
||||||
|
mod time;
|
||||||
|
use time::{Tm, CAL_MTOD};
|
||||||
|
|
||||||
fn main() -> Result<(), Error> {
|
fn main() -> Result<(), Error> {
|
||||||
let matches = App::new(clap::crate_name!())
|
let matches = App::new(clap::crate_name!())
|
||||||
.version(clap::crate_version!())
|
.version(clap::crate_version!())
|
||||||
@ -114,7 +117,7 @@ fn main() -> Result<(), Error> {
|
|||||||
)
|
)
|
||||||
.get_matches();
|
.get_matches();
|
||||||
|
|
||||||
let mut parser = Parser::new();
|
let mut parser = Parser::new()?;
|
||||||
parser.handle_args(matches)?;
|
parser.handle_args(matches)?;
|
||||||
|
|
||||||
println!("# LogReader: {}", std::process::id());
|
println!("# LogReader: {}", std::process::id());
|
||||||
@ -144,12 +147,12 @@ fn main() -> Result<(), Error> {
|
|||||||
|
|
||||||
println!(
|
println!(
|
||||||
"# Start: {} ({})",
|
"# Start: {} ({})",
|
||||||
time::strftime("%F %T", &parser.start_tm)?,
|
time::strftime(c_str!("%F %T"), &parser.start_tm)?,
|
||||||
parser.options.start
|
parser.options.start
|
||||||
);
|
);
|
||||||
println!(
|
println!(
|
||||||
"# End: {} ({})",
|
"# End: {} ({})",
|
||||||
time::strftime("%F %T", &parser.end_tm)?,
|
time::strftime(c_str!("%F %T"), &parser.end_tm)?,
|
||||||
parser.options.end
|
parser.options.end
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -1743,10 +1746,10 @@ struct Parser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Parser {
|
impl Parser {
|
||||||
fn new() -> Self {
|
fn new() -> Result<Self, Error> {
|
||||||
let ltime = time::now();
|
let ltime = Tm::now_local()?;
|
||||||
|
|
||||||
Self {
|
Ok(Self {
|
||||||
sentries: HashMap::new(),
|
sentries: HashMap::new(),
|
||||||
fentries: HashMap::new(),
|
fentries: HashMap::new(),
|
||||||
qentries: HashMap::new(),
|
qentries: HashMap::new(),
|
||||||
@ -1760,12 +1763,12 @@ impl Parser {
|
|||||||
count: 0,
|
count: 0,
|
||||||
buffered_stdout: BufWriter::with_capacity(4 * 1024 * 1024, std::io::stdout()),
|
buffered_stdout: BufWriter::with_capacity(4 * 1024 * 1024, std::io::stdout()),
|
||||||
options: Options::default(),
|
options: Options::default(),
|
||||||
start_tm: time::empty_tm(),
|
start_tm: Tm::zero(),
|
||||||
end_tm: time::empty_tm(),
|
end_tm: Tm::zero(),
|
||||||
ctime: 0,
|
ctime: 0,
|
||||||
string_match: false,
|
string_match: false,
|
||||||
lines: 0,
|
lines: 0,
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn free_sentry(&mut self, sentry_pid: u64) {
|
fn free_sentry(&mut self, sentry_pid: u64) {
|
||||||
@ -1974,40 +1977,37 @@ impl Parser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if let Some(start) = args.value_of("start") {
|
if let Some(start) = args.value_of("start") {
|
||||||
if let Ok(res) = time::strptime(start, "%F %T") {
|
if let Ok(res) = time::strptime(start, c_str!("%F %T")) {
|
||||||
self.options.start = mkgmtime(&res);
|
self.options.start = res.as_utc_to_epoch();
|
||||||
self.start_tm = res;
|
self.start_tm = res;
|
||||||
} else if let Ok(res) = time::strptime(start, "%s") {
|
} else if let Ok(res) = time::strptime(start, c_str!("%s")) {
|
||||||
let res = res.to_local();
|
self.options.start = res.as_utc_to_epoch();
|
||||||
self.options.start = mkgmtime(&res);
|
|
||||||
self.start_tm = res;
|
self.start_tm = res;
|
||||||
} else {
|
} else {
|
||||||
bail!("failed to parse start time");
|
bail!("failed to parse start time");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let mut ltime = time::now();
|
let mut ltime = Tm::now_local()?;
|
||||||
ltime.tm_sec = 0;
|
ltime.tm_sec = 0;
|
||||||
ltime.tm_min = 0;
|
ltime.tm_min = 0;
|
||||||
ltime.tm_hour = 0;
|
ltime.tm_hour = 0;
|
||||||
self.options.start = mkgmtime(<ime);
|
self.options.start = ltime.as_utc_to_epoch();
|
||||||
self.start_tm = ltime;
|
self.start_tm = ltime;
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(end) = args.value_of("end") {
|
if let Some(end) = args.value_of("end") {
|
||||||
if let Ok(res) = time::strptime(end, "%F %T") {
|
if let Ok(res) = time::strptime(end, c_str!("%F %T")) {
|
||||||
self.options.end = mkgmtime(&res);
|
self.options.end = res.as_utc_to_epoch();
|
||||||
self.end_tm = res;
|
self.end_tm = res;
|
||||||
} else if let Ok(res) = time::strptime(end, "%s") {
|
} else if let Ok(res) = time::strptime(end, c_str!("%s")) {
|
||||||
let res = res.to_local();
|
self.options.end = res.as_utc_to_epoch();
|
||||||
self.options.end = mkgmtime(&res);
|
|
||||||
self.end_tm = res;
|
self.end_tm = res;
|
||||||
} else {
|
} else {
|
||||||
bail!("failed to parse end time");
|
bail!("failed to parse end time");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let ltime = time::now();
|
self.options.end = unsafe { libc::time(std::ptr::null_mut()) };
|
||||||
self.options.end = mkgmtime(<ime);
|
self.end_tm = Tm::at_local(self.options.end)?;
|
||||||
self.end_tm = ltime;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.options.end < self.options.start {
|
if self.options.end < self.options.start {
|
||||||
@ -2169,30 +2169,6 @@ fn get_or_create_fentry(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn mkgmtime(tm: &time::Tm) -> time_t {
|
|
||||||
let mut res: time_t;
|
|
||||||
|
|
||||||
let mut year = tm.tm_year as i64 + 1900;
|
|
||||||
let mon = tm.tm_mon;
|
|
||||||
|
|
||||||
res = (year - 1970) * 365 + CAL_MTOD[mon as usize];
|
|
||||||
|
|
||||||
if mon <= 1 {
|
|
||||||
year -= 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
res += (year - 1968) / 4;
|
|
||||||
res -= (year - 1900) / 100;
|
|
||||||
res += (year - 1600) / 400;
|
|
||||||
|
|
||||||
res += (tm.tm_mday - 1) as i64;
|
|
||||||
res = res * 24 + tm.tm_hour as i64;
|
|
||||||
res = res * 60 + tm.tm_min as i64;
|
|
||||||
res = res * 60 + tm.tm_sec as i64;
|
|
||||||
|
|
||||||
res
|
|
||||||
}
|
|
||||||
|
|
||||||
const LOGFILES: [&str; 32] = [
|
const LOGFILES: [&str; 32] = [
|
||||||
"/var/log/syslog",
|
"/var/log/syslog",
|
||||||
"/var/log/syslog.1",
|
"/var/log/syslog.1",
|
||||||
@ -2395,8 +2371,6 @@ fn parse_time<'a>(
|
|||||||
Some((ltime, data))
|
Some((ltime, data))
|
||||||
}
|
}
|
||||||
|
|
||||||
const CAL_MTOD: [i64; 12] = [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334];
|
|
||||||
|
|
||||||
type ByteSlice<'a> = &'a [u8];
|
type ByteSlice<'a> = &'a [u8];
|
||||||
/// Parse Host, Service and PID at the beginning of data. Returns a tuple of (host, service, pid, remaining_text).
|
/// Parse Host, Service and PID at the beginning of data. Returns a tuple of (host, service, pid, remaining_text).
|
||||||
fn parse_host_service_pid(data: &[u8]) -> Option<(ByteSlice, ByteSlice, u64, ByteSlice)> {
|
fn parse_host_service_pid(data: &[u8]) -> Option<(ByteSlice, ByteSlice, u64, ByteSlice)> {
|
||||||
|
169
src/time.rs
Normal file
169
src/time.rs
Normal file
@ -0,0 +1,169 @@
|
|||||||
|
//! Time support library.
|
||||||
|
//!
|
||||||
|
//! Contains wrappers for `strftime`, `strptime`, `libc::localtime_r`.
|
||||||
|
|
||||||
|
use std::ffi::{CStr, CString};
|
||||||
|
use std::fmt;
|
||||||
|
use std::mem::MaybeUninit;
|
||||||
|
|
||||||
|
use anyhow::{bail, format_err, Error};
|
||||||
|
|
||||||
|
/// Calender month index to *non-leap-year* day-of-the-year.
|
||||||
|
pub const CAL_MTOD: [i64; 12] = [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334];
|
||||||
|
|
||||||
|
/// Shortcut for generating an `&'static CStr`.
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! c_str {
|
||||||
|
($data:expr) => {{
|
||||||
|
let bytes = concat!($data, "\0");
|
||||||
|
unsafe { ::std::ffi::CStr::from_bytes_with_nul_unchecked(bytes.as_bytes()) }
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wrapper around `libc::tm` providing `Debug` and some helper methods.
|
||||||
|
pub struct Tm(libc::tm);
|
||||||
|
|
||||||
|
impl std::ops::Deref for Tm {
|
||||||
|
type Target = libc::tm;
|
||||||
|
|
||||||
|
fn deref(&self) -> &libc::tm {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::ops::DerefMut for Tm {
|
||||||
|
fn deref_mut(&mut self) -> &mut libc::tm {
|
||||||
|
&mut self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// (or add libc feature 'extra-traits' but that's the only struct we need it for...)
|
||||||
|
impl fmt::Debug for Tm {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
f.debug_struct("Tm")
|
||||||
|
.field("tm_sec", &self.tm_sec)
|
||||||
|
.field("tm_min", &self.tm_min)
|
||||||
|
.field("tm_hour", &self.tm_hour)
|
||||||
|
.field("tm_mday", &self.tm_mday)
|
||||||
|
.field("tm_mon", &self.tm_mon)
|
||||||
|
.field("tm_year", &self.tm_year)
|
||||||
|
.field("tm_wday", &self.tm_wday)
|
||||||
|
.field("tm_yday", &self.tm_yday)
|
||||||
|
.field("tm_isdst", &self.tm_isdst)
|
||||||
|
.field("tm_gmtoff", &self.tm_gmtoff)
|
||||||
|
.field("tm_zone", &self.tm_zone)
|
||||||
|
.finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// These are now exposed via `libc`, but they're part of glibc.
|
||||||
|
mod imports {
|
||||||
|
extern "C" {
|
||||||
|
pub fn strftime(
|
||||||
|
s: *mut libc::c_char,
|
||||||
|
max: libc::size_t,
|
||||||
|
format: *const libc::c_char,
|
||||||
|
tm: *const libc::tm,
|
||||||
|
) -> libc::size_t;
|
||||||
|
|
||||||
|
pub fn strptime(
|
||||||
|
s: *const libc::c_char,
|
||||||
|
format: *const libc::c_char,
|
||||||
|
tm: *mut libc::tm,
|
||||||
|
) -> *const libc::c_char;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Tm {
|
||||||
|
/// A zero-initialized time struct.
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn zero() -> Self {
|
||||||
|
unsafe { std::mem::zeroed() }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the current time in the local timezone.
|
||||||
|
pub fn now_local() -> Result<Self, Error> {
|
||||||
|
Self::at_local(unsafe { libc::time(std::ptr::null_mut()) })
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get a local time from a unix time stamp.
|
||||||
|
pub fn at_local(time: libc::time_t) -> Result<Self, Error> {
|
||||||
|
let mut out = MaybeUninit::<libc::tm>::uninit();
|
||||||
|
Ok(Self(unsafe {
|
||||||
|
if libc::localtime_r(&time, out.as_mut_ptr()).is_null() {
|
||||||
|
bail!("failed to convert timestamp to local time");
|
||||||
|
}
|
||||||
|
out.assume_init()
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Assume this is an UTC time and convert it to a unix time stamp.
|
||||||
|
///
|
||||||
|
/// Equivalent to `timegm(3)`, the gmt equivalent of `mktime(3)`.
|
||||||
|
pub fn as_utc_to_epoch(&self) -> libc::time_t {
|
||||||
|
let mut year = self.0.tm_year as i64 + 1900;
|
||||||
|
let mon = self.0.tm_mon;
|
||||||
|
|
||||||
|
let mut res: libc::time_t = (year - 1970) * 365 + CAL_MTOD[mon as usize];
|
||||||
|
|
||||||
|
if mon <= 1 {
|
||||||
|
year -= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
res += (year - 1968) / 4;
|
||||||
|
res -= (year - 1900) / 100;
|
||||||
|
res += (year - 1600) / 400;
|
||||||
|
|
||||||
|
res += (self.0.tm_mday - 1) as i64;
|
||||||
|
res = res * 24 + self.0.tm_hour as i64;
|
||||||
|
res = res * 60 + self.0.tm_min as i64;
|
||||||
|
res = res * 60 + self.0.tm_sec as i64;
|
||||||
|
|
||||||
|
res
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Wrapper around `strftime(3)` to format time strings.
|
||||||
|
pub fn strftime(format: &CStr, tm: &Tm) -> Result<String, Error> {
|
||||||
|
let mut buf = MaybeUninit::<[u8; 64]>::uninit();
|
||||||
|
|
||||||
|
let size = unsafe {
|
||||||
|
imports::strftime(
|
||||||
|
buf.as_mut_ptr() as *mut libc::c_char,
|
||||||
|
64,
|
||||||
|
format.as_ptr(),
|
||||||
|
&tm.0,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
if size == 0 {
|
||||||
|
bail!("failed to format time");
|
||||||
|
}
|
||||||
|
let size = size as usize;
|
||||||
|
|
||||||
|
let buf = unsafe { buf.assume_init() };
|
||||||
|
|
||||||
|
std::str::from_utf8(&buf[..size])
|
||||||
|
.map_err(|_| format_err!("formatted time was not valid utf-8"))
|
||||||
|
.map(str::to_string)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Wrapper around `strptime(3)` to parse time strings.
|
||||||
|
pub fn strptime(time: &str, format: &CStr) -> Result<Tm, Error> {
|
||||||
|
let mut out = MaybeUninit::<libc::tm>::uninit();
|
||||||
|
|
||||||
|
let time = CString::new(time).map_err(|_| format_err!("time string contains nul bytes"))?;
|
||||||
|
|
||||||
|
let end = unsafe {
|
||||||
|
imports::strptime(
|
||||||
|
time.as_ptr() as *const libc::c_char,
|
||||||
|
format.as_ptr(),
|
||||||
|
out.as_mut_ptr(),
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
if end.is_null() {
|
||||||
|
bail!("failed to parse time string {:?}", time);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Tm(unsafe { out.assume_init() }))
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user