Merge pull request #160 from fuad1502/dmesg-time-format
Add`—time-format` option support for `dmesg`
This commit is contained in:
170
Cargo.lock
generated
170
Cargo.lock
generated
@@ -11,6 +11,21 @@ dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "android-tzdata"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0"
|
||||
|
||||
[[package]]
|
||||
name = "android_system_properties"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstream"
|
||||
version = "0.6.15"
|
||||
@@ -60,6 +75,12 @@ dependencies = [
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "1.3.2"
|
||||
@@ -72,6 +93,12 @@ version = "2.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de"
|
||||
|
||||
[[package]]
|
||||
name = "bumpalo"
|
||||
version = "3.16.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c"
|
||||
|
||||
[[package]]
|
||||
name = "bytecount"
|
||||
version = "0.6.8"
|
||||
@@ -84,6 +111,15 @@ version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fd9de9f2205d5ef3fd67e685b0df337994ddd4495e2a28d185500d0e1edfea47"
|
||||
dependencies = [
|
||||
"shlex",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.0"
|
||||
@@ -96,6 +132,20 @@ version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"
|
||||
|
||||
[[package]]
|
||||
name = "chrono"
|
||||
version = "0.4.38"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401"
|
||||
dependencies = [
|
||||
"android-tzdata",
|
||||
"iana-time-zone",
|
||||
"js-sys",
|
||||
"num-traits",
|
||||
"wasm-bindgen",
|
||||
"windows-targets 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.5.21"
|
||||
@@ -270,6 +320,29 @@ version = "0.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
|
||||
|
||||
[[package]]
|
||||
name = "iana-time-zone"
|
||||
version = "0.1.61"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "235e081f3925a06703c2d0117ea8b91f042756fd6e7a6e5d901e8ca1a996b220"
|
||||
dependencies = [
|
||||
"android_system_properties",
|
||||
"core-foundation-sys",
|
||||
"iana-time-zone-haiku",
|
||||
"js-sys",
|
||||
"wasm-bindgen",
|
||||
"windows-core 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "iana-time-zone-haiku"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f"
|
||||
dependencies = [
|
||||
"cc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "io-lifetimes"
|
||||
version = "1.0.11"
|
||||
@@ -293,6 +366,15 @@ version = "1.0.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b"
|
||||
|
||||
[[package]]
|
||||
name = "js-sys"
|
||||
version = "0.3.72"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6a88f1bda2bd75b0452a14784937d796722fdebfe50df998aeb3f0b7603019a9"
|
||||
dependencies = [
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.167"
|
||||
@@ -311,6 +393,12 @@ version = "0.4.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89"
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.22"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.7.4"
|
||||
@@ -344,6 +432,15 @@ version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9"
|
||||
|
||||
[[package]]
|
||||
name = "num-traits"
|
||||
version = "0.2.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num_threads"
|
||||
version = "0.1.7"
|
||||
@@ -671,6 +768,12 @@ dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "shlex"
|
||||
version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
|
||||
|
||||
[[package]]
|
||||
name = "siphasher"
|
||||
version = "0.3.11"
|
||||
@@ -903,6 +1006,7 @@ dependencies = [
|
||||
name = "uu_dmesg"
|
||||
version = "0.0.1"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"clap",
|
||||
"regex",
|
||||
"serde",
|
||||
@@ -1013,6 +1117,61 @@ version = "0.11.0+wasi-snapshot-preview1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen"
|
||||
version = "0.2.95"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "128d1e363af62632b8eb57219c8fd7877144af57558fb2ef0368d0087bddeb2e"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"once_cell",
|
||||
"wasm-bindgen-macro",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-backend"
|
||||
version = "0.2.95"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cb6dd4d3ca0ddffd1dd1c9c04f94b868c37ff5fac97c30b97cff2d74fce3a358"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"log",
|
||||
"once_cell",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.82",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro"
|
||||
version = "0.2.95"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e79384be7f8f5a9dd5d7167216f022090cf1f9ec128e6e6a482a2cb5c5422c56"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"wasm-bindgen-macro-support",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro-support"
|
||||
version = "0.2.95"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "26c6ab57572f7a24a4985830b120de1594465e5d500f24afe89e16b4e833ef68"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.82",
|
||||
"wasm-bindgen-backend",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-shared"
|
||||
version = "0.2.95"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "65fc09f10666a9f147042251e0dda9c18f166ff7de300607007e96bdebc1068d"
|
||||
|
||||
[[package]]
|
||||
name = "wild"
|
||||
version = "2.2.1"
|
||||
@@ -1050,7 +1209,16 @@ version = "0.57.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "12342cb4d8e3b046f3d80effd474a7a02447231330ef77d71daa6fbc40681143"
|
||||
dependencies = [
|
||||
"windows-core",
|
||||
"windows-core 0.57.0",
|
||||
"windows-targets 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-core"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9"
|
||||
dependencies = [
|
||||
"windows-targets 0.52.6",
|
||||
]
|
||||
|
||||
|
@@ -82,6 +82,8 @@ tempfile = { workspace = true }
|
||||
libc = { workspace = true }
|
||||
rand = { workspace = true }
|
||||
uucore = { workspace = true, features = ["entries", "process", "signals"] }
|
||||
# dmesg test require fixed-boot-time feature turned on.
|
||||
dmesg = { version = "0.0.1", package = "uu_dmesg", path = "src/uu/dmesg", features = ["fixed-boot-time"] }
|
||||
|
||||
[target.'cfg(unix)'.dev-dependencies]
|
||||
xattr = { workspace = true }
|
||||
|
@@ -16,3 +16,7 @@ uucore = { workspace = true }
|
||||
regex = { workspace = true }
|
||||
serde_json = { workspace = true }
|
||||
serde = { workspace = true }
|
||||
chrono = "0.4.38"
|
||||
|
||||
[features]
|
||||
fixed-boot-time = []
|
||||
|
@@ -12,6 +12,7 @@ use uucore::{
|
||||
};
|
||||
|
||||
mod json;
|
||||
mod time_formatter;
|
||||
|
||||
const ABOUT: &str = help_about!("dmesg.md");
|
||||
const USAGE: &str = help_usage!("dmesg.md");
|
||||
@@ -26,6 +27,22 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
||||
if matches.get_flag(options::JSON) {
|
||||
dmesg.output_format = OutputFormat::Json;
|
||||
}
|
||||
if let Some(time_format) = matches.get_one::<String>(options::TIME_FORMAT) {
|
||||
dmesg.time_format = match &time_format[..] {
|
||||
"delta" => TimeFormat::Delta,
|
||||
"reltime" => TimeFormat::Reltime,
|
||||
"ctime" => TimeFormat::Ctime,
|
||||
"notime" => TimeFormat::Notime,
|
||||
"iso" => TimeFormat::Iso,
|
||||
"raw" => TimeFormat::Raw,
|
||||
_ => {
|
||||
return Err(USimpleError::new(
|
||||
1,
|
||||
format!("unknown time format: {time_format}"),
|
||||
))
|
||||
}
|
||||
};
|
||||
}
|
||||
dmesg.parse()?.print();
|
||||
Ok(())
|
||||
}
|
||||
@@ -49,16 +66,27 @@ pub fn uu_app() -> Command {
|
||||
.help("use JSON output format")
|
||||
.action(ArgAction::SetTrue),
|
||||
)
|
||||
.arg(
|
||||
Arg::new(options::TIME_FORMAT)
|
||||
.long("time-format")
|
||||
.help(
|
||||
"show timestamp using the given format:\n".to_string()
|
||||
+ " [delta|reltime|ctime|notime|iso|raw]",
|
||||
)
|
||||
.action(ArgAction::Set),
|
||||
)
|
||||
}
|
||||
|
||||
mod options {
|
||||
pub const KMSG_FILE: &str = "kmsg-file";
|
||||
pub const JSON: &str = "json";
|
||||
pub const TIME_FORMAT: &str = "time-format";
|
||||
}
|
||||
|
||||
struct Dmesg<'a> {
|
||||
kmsg_file: &'a str,
|
||||
output_format: OutputFormat,
|
||||
time_format: TimeFormat,
|
||||
records: Option<Vec<Record>>,
|
||||
}
|
||||
|
||||
@@ -67,6 +95,7 @@ impl Dmesg<'_> {
|
||||
Dmesg {
|
||||
kmsg_file: "/dev/kmsg",
|
||||
output_format: OutputFormat::Normal,
|
||||
time_format: TimeFormat::Raw,
|
||||
records: None,
|
||||
}
|
||||
}
|
||||
@@ -112,7 +141,7 @@ impl Dmesg<'_> {
|
||||
fn print(&self) {
|
||||
match self.output_format {
|
||||
OutputFormat::Json => self.print_json(),
|
||||
OutputFormat::Normal => unimplemented!(),
|
||||
OutputFormat::Normal => self.print_normal(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -121,6 +150,34 @@ impl Dmesg<'_> {
|
||||
println!("{}", json::serialize_records(records));
|
||||
}
|
||||
}
|
||||
|
||||
fn print_normal(&self) {
|
||||
if let Some(records) = &self.records {
|
||||
let mut reltime_formatter = time_formatter::ReltimeFormatter::new();
|
||||
let mut delta_formatter = time_formatter::DeltaFormatter::new();
|
||||
for record in records {
|
||||
match self.time_format {
|
||||
TimeFormat::Delta => {
|
||||
print!("[{}] ", delta_formatter.format(record.timestamp_us))
|
||||
}
|
||||
TimeFormat::Reltime => {
|
||||
print!("[{}] ", reltime_formatter.format(record.timestamp_us))
|
||||
}
|
||||
TimeFormat::Ctime => {
|
||||
print!("[{}] ", time_formatter::ctime(record.timestamp_us))
|
||||
}
|
||||
TimeFormat::Iso => {
|
||||
print!("{} ", time_formatter::iso(record.timestamp_us))
|
||||
}
|
||||
TimeFormat::Raw => {
|
||||
print!("[{}] ", time_formatter::raw(record.timestamp_us))
|
||||
}
|
||||
TimeFormat::Notime => (),
|
||||
}
|
||||
println!("{}", record.message);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum OutputFormat {
|
||||
@@ -128,10 +185,19 @@ enum OutputFormat {
|
||||
Json,
|
||||
}
|
||||
|
||||
enum TimeFormat {
|
||||
Delta,
|
||||
Reltime,
|
||||
Ctime,
|
||||
Notime,
|
||||
Iso,
|
||||
Raw,
|
||||
}
|
||||
|
||||
struct Record {
|
||||
priority_facility: u32,
|
||||
_sequence: u64,
|
||||
timestamp_us: u64,
|
||||
timestamp_us: i64,
|
||||
message: String,
|
||||
}
|
||||
|
||||
|
@@ -23,7 +23,7 @@ struct Dmesg<'a> {
|
||||
#[derive(serde::Serialize)]
|
||||
struct Record<'a> {
|
||||
pri: u32,
|
||||
time: u64,
|
||||
time: i64,
|
||||
msg: &'a str,
|
||||
}
|
||||
|
||||
@@ -129,11 +129,11 @@ impl serde_json::ser::Formatter for DmesgFormatter {
|
||||
}
|
||||
}
|
||||
|
||||
fn write_u64<W>(&mut self, writer: &mut W, value: u64) -> io::Result<()>
|
||||
fn write_i64<W>(&mut self, writer: &mut W, value: i64) -> io::Result<()>
|
||||
where
|
||||
W: ?Sized + io::Write,
|
||||
{
|
||||
// The only u64 field in Dmesg is time, which requires a specific format
|
||||
// The only i64 field in Dmesg is time, which requires a specific format
|
||||
let seconds = value / 1000000;
|
||||
let sub_seconds = value % 1000000;
|
||||
let repr = format!("{:>5}.{:0>6}", seconds, sub_seconds);
|
||||
|
170
src/uu/dmesg/src/time_formatter.rs
Normal file
170
src/uu/dmesg/src/time_formatter.rs
Normal file
@@ -0,0 +1,170 @@
|
||||
// This file is part of the uutils util-linux package.
|
||||
//
|
||||
// For the full copyright and license information, please view the LICENSE
|
||||
// file that was distributed with this source code.
|
||||
|
||||
use chrono::{DateTime, FixedOffset, TimeDelta};
|
||||
#[cfg(feature = "fixed-boot-time")]
|
||||
use chrono::{NaiveDate, NaiveTime};
|
||||
use std::sync::OnceLock;
|
||||
|
||||
pub fn raw(timestamp_us: i64) -> String {
|
||||
let seconds = timestamp_us / 1000000;
|
||||
let sub_seconds = timestamp_us % 1000000;
|
||||
format!("{:>5}.{:0>6}", seconds, sub_seconds)
|
||||
}
|
||||
|
||||
pub fn ctime(timestamp_us: i64) -> String {
|
||||
let date_time = boot_time()
|
||||
.checked_add_signed(TimeDelta::microseconds(timestamp_us))
|
||||
.unwrap();
|
||||
date_time.format("%a %b %d %H:%M:%S %Y").to_string()
|
||||
}
|
||||
|
||||
pub fn iso(timestamp_us: i64) -> String {
|
||||
let date_time = boot_time()
|
||||
.checked_add_signed(TimeDelta::microseconds(timestamp_us))
|
||||
.unwrap();
|
||||
date_time.format("%Y-%m-%dT%H:%M:%S,%6f%:z").to_string()
|
||||
}
|
||||
|
||||
pub struct ReltimeFormatter {
|
||||
state: State,
|
||||
prev_timestamp_us: i64,
|
||||
previous_unix_timestamp: i64,
|
||||
}
|
||||
|
||||
pub struct DeltaFormatter {
|
||||
state: State,
|
||||
prev_timestamp_us: i64,
|
||||
}
|
||||
|
||||
pub enum State {
|
||||
Initial,
|
||||
AfterBoot,
|
||||
Delta,
|
||||
}
|
||||
|
||||
impl ReltimeFormatter {
|
||||
pub fn new() -> Self {
|
||||
ReltimeFormatter {
|
||||
state: State::Initial,
|
||||
prev_timestamp_us: 0,
|
||||
previous_unix_timestamp: 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn format(&mut self, timestamp_us: i64) -> String {
|
||||
let date_time = boot_time()
|
||||
.checked_add_signed(TimeDelta::microseconds(timestamp_us))
|
||||
.unwrap();
|
||||
let unix_timestamp = date_time.timestamp();
|
||||
let minute_changes = (unix_timestamp / 60) != (self.previous_unix_timestamp / 60);
|
||||
let format_res = match self.state {
|
||||
State::Initial => date_time.format("%b%d %H:%M").to_string(),
|
||||
_ if minute_changes => date_time.format("%b%d %H:%M").to_string(),
|
||||
State::AfterBoot => Self::delta(0),
|
||||
State::Delta => Self::delta(timestamp_us - self.prev_timestamp_us),
|
||||
};
|
||||
self.prev_timestamp_us = timestamp_us;
|
||||
self.previous_unix_timestamp = unix_timestamp;
|
||||
self.state = match self.state {
|
||||
State::Initial if timestamp_us == 0 => State::AfterBoot,
|
||||
_ => State::Delta,
|
||||
};
|
||||
format_res
|
||||
}
|
||||
|
||||
fn delta(delta_us: i64) -> String {
|
||||
let seconds = i64::abs(delta_us / 1000000);
|
||||
let sub_seconds = i64::abs(delta_us % 1000000);
|
||||
let sign = if delta_us >= 0 { '+' } else { '-' };
|
||||
let res = format!("{}{}.{:0>6}", sign, seconds, sub_seconds);
|
||||
format!("{:>11}", res)
|
||||
}
|
||||
}
|
||||
|
||||
impl DeltaFormatter {
|
||||
pub fn new() -> Self {
|
||||
DeltaFormatter {
|
||||
state: State::Initial,
|
||||
prev_timestamp_us: 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn format(&mut self, timestamp_us: i64) -> String {
|
||||
let format_res = match self.state {
|
||||
State::Delta => Self::delta(timestamp_us - self.prev_timestamp_us),
|
||||
_ => Self::delta(0),
|
||||
};
|
||||
self.prev_timestamp_us = timestamp_us;
|
||||
self.state = match self.state {
|
||||
State::Initial if timestamp_us == 0 => State::AfterBoot,
|
||||
_ => State::Delta,
|
||||
};
|
||||
format_res
|
||||
}
|
||||
|
||||
fn delta(delta_us: i64) -> String {
|
||||
let seconds = i64::abs(delta_us / 1000000);
|
||||
let sub_seconds = i64::abs(delta_us % 1000000);
|
||||
let mut res = format!("{}.{:0>6}", seconds, sub_seconds);
|
||||
if delta_us < 0 {
|
||||
res.insert(0, '-');
|
||||
}
|
||||
format!("<{:>12}>", res)
|
||||
}
|
||||
}
|
||||
|
||||
static BOOT_TIME: OnceLock<DateTime<FixedOffset>> = OnceLock::new();
|
||||
|
||||
#[cfg(feature = "fixed-boot-time")]
|
||||
fn boot_time() -> DateTime<FixedOffset> {
|
||||
*BOOT_TIME.get_or_init(|| {
|
||||
let date = NaiveDate::from_ymd_opt(2024, 11, 18).unwrap();
|
||||
let time = NaiveTime::from_hms_micro_opt(19, 34, 12, 866807).unwrap();
|
||||
let tz = FixedOffset::east_opt(7 * 3600).unwrap();
|
||||
chrono::NaiveDateTime::new(date, time)
|
||||
.and_local_timezone(tz)
|
||||
.unwrap()
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "fixed-boot-time"))]
|
||||
#[cfg(unix)]
|
||||
#[cfg(not(target_os = "openbsd"))]
|
||||
fn boot_time() -> DateTime<FixedOffset> {
|
||||
*BOOT_TIME.get_or_init(|| boot_time_from_utmpx().unwrap())
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "fixed-boot-time"))]
|
||||
#[cfg(windows)]
|
||||
fn boot_time() -> DateTime<FixedOffset> {
|
||||
// TODO: get windows boot time
|
||||
*BOOT_TIME.get_or_init(|| chrono::DateTime::from_timestamp(0, 0).unwrap().into())
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "fixed-boot-time"))]
|
||||
#[cfg(target_os = "openbsd")]
|
||||
fn boot_time() -> DateTime<FixedOffset> {
|
||||
// TODO: get openbsd boot time
|
||||
*BOOT_TIME.get_or_init(|| chrono::DateTime::from_timestamp(0, 0).unwrap().into())
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "fixed-boot-time"))]
|
||||
#[cfg(unix)]
|
||||
#[cfg(not(target_os = "openbsd"))]
|
||||
fn boot_time_from_utmpx() -> Option<DateTime<FixedOffset>> {
|
||||
for record in uucore::utmpx::Utmpx::iter_all_records() {
|
||||
if record.record_type() == uucore::utmpx::BOOT_TIME {
|
||||
let t = record.login_time();
|
||||
return Some(
|
||||
chrono::DateTime::from_timestamp(t.unix_timestamp(), t.nanosecond())
|
||||
.unwrap()
|
||||
.with_timezone(&chrono::Local)
|
||||
.into(),
|
||||
);
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
@@ -30,3 +30,54 @@ fn test_kmsg_json() {
|
||||
.no_stderr()
|
||||
.stdout_is_templated_fixture("test_kmsg_json.expected", &[("\r\n", "\n")]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_kmsg_time_format_delta() {
|
||||
test_kmsg_time_format("delta");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_kmsg_time_format_reltime() {
|
||||
test_kmsg_time_format("reltime");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_kmsg_time_format_ctime() {
|
||||
test_kmsg_time_format("ctime");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_kmsg_time_format_notime() {
|
||||
test_kmsg_time_format("notime");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_kmsg_time_format_iso() {
|
||||
test_kmsg_time_format("iso");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_kmsg_time_format_raw() {
|
||||
test_kmsg_time_format("raw");
|
||||
}
|
||||
|
||||
fn test_kmsg_time_format(format: &str) {
|
||||
let time_format_arg = format!("--time-format={format}");
|
||||
let expected_output = format!("test_kmsg_time_format_{format}.expected");
|
||||
new_ucmd!()
|
||||
.arg("--kmsg-file")
|
||||
.arg("kmsg.input.1")
|
||||
.arg(time_format_arg)
|
||||
.succeeds()
|
||||
.no_stderr()
|
||||
.stdout_is_templated_fixture(expected_output, &[("\r\n", "\n")]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_invalid_time_format() {
|
||||
new_ucmd!()
|
||||
.arg("--time-format=definitely-invalid")
|
||||
.fails()
|
||||
.code_is(1)
|
||||
.stderr_only("dmesg: unknown time format: definitely-invalid\n");
|
||||
}
|
||||
|
BIN
tests/fixtures/dmesg/kmsg.input.1
vendored
Normal file
BIN
tests/fixtures/dmesg/kmsg.input.1
vendored
Normal file
Binary file not shown.
5
tests/fixtures/dmesg/test_kmsg_time_format_ctime.expected
vendored
Normal file
5
tests/fixtures/dmesg/test_kmsg_time_format_ctime.expected
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
[Mon Nov 18 19:34:12 2024] LOG_EMERG LOG_AUTH
|
||||
[Mon Nov 18 19:34:13 2024] LOG_EMERG LOG_AUTHPRIV
|
||||
[Mon Nov 18 19:34:13 2024] LOG_EMERG LOG_CRON
|
||||
[Mon Nov 18 19:34:13 2024] LOG_EMERG LOG_DAEMON
|
||||
[Mon Nov 18 19:35:00 2024] LOG_EMERG LOG_FTP
|
5
tests/fixtures/dmesg/test_kmsg_time_format_delta.expected
vendored
Normal file
5
tests/fixtures/dmesg/test_kmsg_time_format_delta.expected
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
[< 0.000000>] LOG_EMERG LOG_AUTH
|
||||
[< 0.000000>] LOG_EMERG LOG_AUTHPRIV
|
||||
[< -0.166667>] LOG_EMERG LOG_CRON
|
||||
[< 0.666667>] LOG_EMERG LOG_DAEMON
|
||||
[< 47.000000>] LOG_EMERG LOG_FTP
|
5
tests/fixtures/dmesg/test_kmsg_time_format_iso.expected
vendored
Normal file
5
tests/fixtures/dmesg/test_kmsg_time_format_iso.expected
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
2024-11-18T19:34:12,866807+07:00 LOG_EMERG LOG_AUTH
|
||||
2024-11-18T19:34:13,366807+07:00 LOG_EMERG LOG_AUTHPRIV
|
||||
2024-11-18T19:34:13,200140+07:00 LOG_EMERG LOG_CRON
|
||||
2024-11-18T19:34:13,866807+07:00 LOG_EMERG LOG_DAEMON
|
||||
2024-11-18T19:35:00,866807+07:00 LOG_EMERG LOG_FTP
|
5
tests/fixtures/dmesg/test_kmsg_time_format_notime.expected
vendored
Normal file
5
tests/fixtures/dmesg/test_kmsg_time_format_notime.expected
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
LOG_EMERG LOG_AUTH
|
||||
LOG_EMERG LOG_AUTHPRIV
|
||||
LOG_EMERG LOG_CRON
|
||||
LOG_EMERG LOG_DAEMON
|
||||
LOG_EMERG LOG_FTP
|
5
tests/fixtures/dmesg/test_kmsg_time_format_raw.expected
vendored
Normal file
5
tests/fixtures/dmesg/test_kmsg_time_format_raw.expected
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
[ 0.000000] LOG_EMERG LOG_AUTH
|
||||
[ 0.500000] LOG_EMERG LOG_AUTHPRIV
|
||||
[ 0.333333] LOG_EMERG LOG_CRON
|
||||
[ 1.000000] LOG_EMERG LOG_DAEMON
|
||||
[ 48.000000] LOG_EMERG LOG_FTP
|
5
tests/fixtures/dmesg/test_kmsg_time_format_reltime.expected
vendored
Normal file
5
tests/fixtures/dmesg/test_kmsg_time_format_reltime.expected
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
[Nov18 19:34] LOG_EMERG LOG_AUTH
|
||||
[ +0.000000] LOG_EMERG LOG_AUTHPRIV
|
||||
[ -0.166667] LOG_EMERG LOG_CRON
|
||||
[ +0.666667] LOG_EMERG LOG_DAEMON
|
||||
[Nov18 19:35] LOG_EMERG LOG_FTP
|
Reference in New Issue
Block a user