From 44d942da4debfb3f92af5abda596e50e3441d5c0 Mon Sep 17 00:00:00 2001 From: Fuad Ismail Date: Thu, 14 Nov 2024 22:47:43 +0700 Subject: [PATCH 01/16] dmesg: initialize empty dmesg crate. --- Cargo.lock | 9 +++++++++ Cargo.toml | 4 +++- src/uu/dmesg/Cargo.toml | 15 +++++++++++++++ src/uu/dmesg/src/dmesg.rs | 11 +++++++++++ src/uu/dmesg/src/main.rs | 1 + 5 files changed, 39 insertions(+), 1 deletion(-) create mode 100644 src/uu/dmesg/Cargo.toml create mode 100644 src/uu/dmesg/src/dmesg.rs create mode 100644 src/uu/dmesg/src/main.rs diff --git a/Cargo.lock b/Cargo.lock index 9b6c94d..0efc2df 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -880,6 +880,7 @@ dependencies = [ "tempfile", "textwrap", "uu_ctrlaltdel", + "uu_dmesg", "uu_last", "uu_lscpu", "uu_lsmem", @@ -898,6 +899,14 @@ dependencies = [ "uucore", ] +[[package]] +name = "uu_dmesg" +version = "0.0.1" +dependencies = [ + "clap", + "uucore", +] + [[package]] name = "uu_last" version = "0.0.1" diff --git a/Cargo.toml b/Cargo.toml index d91e4aa..d56a19c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -32,7 +32,8 @@ feat_common_core = [ "ctrlaltdel", "rev", "setsid", - "last" + "last", + "dmesg" ] [workspace.dependencies] @@ -72,6 +73,7 @@ ctrlaltdel = { optional = true, version = "0.0.1", package = "uu_ctrlaltdel", pa rev = { optional = true, version = "0.0.1", package = "uu_rev", path = "src/uu/rev" } setsid = { optional = true, version = "0.0.1", package = "uu_setsid", path ="src/uu/setsid" } last = { optional = true, version = "0.0.1", package = "uu_last", path = "src/uu/last" } +dmesg = { optional = true, version = "0.0.1", package = "uu_dmesg", path = "src/uu/dmesg" } [dev-dependencies] pretty_assertions = "1" diff --git a/src/uu/dmesg/Cargo.toml b/src/uu/dmesg/Cargo.toml new file mode 100644 index 0000000..f63ee6a --- /dev/null +++ b/src/uu/dmesg/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "uu_dmesg" +version = "0.0.1" +edition = "2021" + +[lib] +path = "src/dmesg.rs" + +[[bin]] +name = "dmesg" +path = "src/main.rs" + +[dependencies] +clap = { workspace = true } +uucore = { workspace = true } diff --git a/src/uu/dmesg/src/dmesg.rs b/src/uu/dmesg/src/dmesg.rs new file mode 100644 index 0000000..c58d6bc --- /dev/null +++ b/src/uu/dmesg/src/dmesg.rs @@ -0,0 +1,11 @@ +use clap::{crate_version, Command}; +use uucore::error::UResult; + +#[uucore::main] +pub fn uumain(_args: impl uucore::Args) -> UResult<()> { + Ok(()) +} + +pub fn uu_app() -> Command { + Command::new(uucore::util_name()).version(crate_version!()) +} diff --git a/src/uu/dmesg/src/main.rs b/src/uu/dmesg/src/main.rs new file mode 100644 index 0000000..902d5bf --- /dev/null +++ b/src/uu/dmesg/src/main.rs @@ -0,0 +1 @@ +uucore::bin!(uu_dmesg); From 7c5caf69abe54cca637cb3b3c07751a23dcaabf4 Mon Sep 17 00:00:00 2001 From: Fuad Ismail Date: Thu, 14 Nov 2024 22:50:36 +0700 Subject: [PATCH 02/16] tests/dmesg: create test for --kmsg-file and --json option. --- tests/by-util/test_dmesg.rs | 21 + tests/fixtures/dmesg/kmsg.input | Bin 0 -> 6701 bytes tests/fixtures/dmesg/test_kmsg_json.expected | 645 +++++++++++++++++++ tests/tests.rs | 4 + 4 files changed, 670 insertions(+) create mode 100644 tests/by-util/test_dmesg.rs create mode 100644 tests/fixtures/dmesg/kmsg.input create mode 100644 tests/fixtures/dmesg/test_kmsg_json.expected diff --git a/tests/by-util/test_dmesg.rs b/tests/by-util/test_dmesg.rs new file mode 100644 index 0000000..095fb61 --- /dev/null +++ b/tests/by-util/test_dmesg.rs @@ -0,0 +1,21 @@ +// This file is part of the uutils coreutils package. +// +// For the full copyright and license information, please view the LICENSE +// file that was distributed with this source code. +use crate::common::util::TestScenario; + +#[test] +fn test_invalid_arg() { + new_ucmd!().arg("--definitely-invalid").fails().code_is(1); +} + +#[test] +fn test_kmsg_json() { + new_ucmd!() + .arg("--kmsg-file") + .arg("kmsg.input") + .arg("--json") + .run() + .no_stderr() + .stdout_is_fixture("test_kmsg_json.expected"); +} diff --git a/tests/fixtures/dmesg/kmsg.input b/tests/fixtures/dmesg/kmsg.input new file mode 100644 index 0000000000000000000000000000000000000000..a6468dc9a64524064a6da7ccc9a2eeb85b61a348 GIT binary patch literal 6701 zcmXpu(lO96(9yN_@pq4R^>qz$R{*gbLqj~c7%U8Q40Q|*U;wMCfFRE>E(UWW9U~nh zm_khRoP+%RxEPE~bc}V3ajSB1boB+Pv#`)H(J{fT&MhQ>i@`w0RL2yz8gJJiKQ0DC zBMTif9W&g@eEgjqeGIr542{in%yrBOC^Y0^Ff=jIvCy#~pwNhm!O+x5$5O|VfI?#~ z217Fg9YYWzpwtAU)I!J5P{$BYoPa%K%Ee%4Zl+^sq+^IDQou^hxEKsAOmqy5bqw*u z3|Of-7lVm~j-iQ;A)dJL2?zqYQ^(L$#}H5S_&R#}a50#g=@^0#ZlC+Ph6i&om>B37 zn(G+iiJsudU>|>XE(Qx7LlDAkU}&&w5Ep})iH@PAjv=1d2@Q1);9@W~)-f_bO4zWJ z?dang6oQ;n40Vh^2;E*(btNWTIn) z5i1Cl;2dM9V`Qphgb^tSRj@o`sbgfOV}y}w5DLM`0i0{hb&Nm=x4*$k!TH8i$H+p* z2qWj9nF`K1#yUopIz||=gQgUmcMNrm4Uozd%m@NI0GxL$b&L&lj4|>KnyKKtW3FRt zq+^UHiom9V^Ny*Gv9XRZo>&4a1?3$}9b*$6V?410$vfsc#-=*Pc%liKcg%H+&2)_M z#1b^`80r|C>lkC?9fY@GdB;-6*h0q`PXs~pj;W5ZrH(P4*n#F9P#KO?jKiYFImilo>n80wf{L<@>CXwEUwF)`9H!H5>PQfS68(=jpDF~NuuxH4G2u{6*zG0`!>h!MC# zaJDhhF)`IK!H5sIB51BLHqbFK(=ow_54bvTIsj)H3mp@49TSWgK~)LPGiEv_7CI&v zQG%)xoMlXOOe}RwFd_w2B{;_z>6jWI)tng70(K`T!TMs;*J@xN^pL$&@naA zF~uD*V3pwPVy0titYeBhZon$dxfnohG|@4|9XF87VxePds$+^fa-ey|LdVoh#}s$m zK(mUGj;XnhDej1YKJsVZ;PX6+Dv|>X@16m|;W&OdU9n z80(ms>X>0f156Dpix}#dndz8eL<3A2IPSqY#8Su1T*nL}B2W~9Gl;p4nT3uSMns?} z1m_P^9WzTEGmNM}Q3%c+#yaK(NDV>s$N;+$oI4D4%s~jf>O(ORoH;CY%#C!+aYhH& zL~!0P*D*KNF~=DrV1=NpVW?wnqGOITMj$!EQpem>#~f#rKr@D=j=7nRInEe?<_lvT zb8{VYoY4W#7KS?J7CPoQV*{Ei%yrByb$HGX*0wXq%RYNm~g^q=>js-?^AghFD5+fZ86CDeTC_z>W&L}22 z7N$BD7_ow^4whMrbS%ttEHI)5St&S0fHI7UfsTc_js->xVJHS?8Vel@5JFG67>dCe z$4tka>L8iQ%s|J|Ove&$%t14j ziH@bYjwRk`gJ&)y9ZL%xON`8g91+kAW}#zgsbh&Z%AlFd9MruBVf3`%=O5zf?226d znCciBfJOr_;tNeFG@F4;HqtRPz=$y<<7!N_!Q#gHs#Y@lOkWDLSMt%qkg6CFb%Fvh7AoZUd~G}ST09A1E14a;mG z!$7JrA_}e&oCv{L4dfYf9YZ6GtcI!tAu7Lkhw-WhM0p6 z2y>x%3S_3Sjv?l_141P{Q<>_3#*qy%haM1W!MO_LR*+hZ@(-a7maRa_%ybMf2Otni z!RZv7uR!hunT?UJ&=i9+7RY0uabJv#g{Bysvq0em#<)Wl> Date: Fri, 15 Nov 2024 09:48:48 +0700 Subject: [PATCH 03/16] dmesg: parse arguments and create essential structs. --- src/uu/dmesg/src/dmesg.rs | 65 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 62 insertions(+), 3 deletions(-) diff --git a/src/uu/dmesg/src/dmesg.rs b/src/uu/dmesg/src/dmesg.rs index c58d6bc..6839756 100644 --- a/src/uu/dmesg/src/dmesg.rs +++ b/src/uu/dmesg/src/dmesg.rs @@ -1,11 +1,70 @@ -use clap::{crate_version, Command}; +use clap::{crate_version, Arg, ArgAction, Command}; use uucore::error::UResult; #[uucore::main] -pub fn uumain(_args: impl uucore::Args) -> UResult<()> { +pub fn uumain(args: impl uucore::Args) -> UResult<()> { + let mut dmesg = Dmesg::new(); + let matches: clap::ArgMatches = uu_app().try_get_matches_from(args)?; + if let Some(kmsg_file) = matches.get_one::(options::KMSG_FILE) { + dmesg.kmsg_file = kmsg_file; + } + if matches.get_flag(options::JSON) { + dmesg.output_format = OutputFormat::Json; + } + dmesg.parse().print(); Ok(()) } pub fn uu_app() -> Command { - Command::new(uucore::util_name()).version(crate_version!()) + Command::new(uucore::util_name()) + .version(crate_version!()) + .arg( + Arg::new(options::KMSG_FILE) + .short('K') + .long("kmsg-file") + .action(ArgAction::Set), + ) + .arg( + Arg::new(options::JSON) + .short('J') + .long("json") + .action(ArgAction::SetTrue), + ) +} + +mod options { + pub const KMSG_FILE: &'static str = "kmsg-file"; + pub const JSON: &'static str = "json"; +} + +struct Dmesg<'a> { + kmsg_file: &'a str, + output_format: OutputFormat, + _records: Option>, +} + +impl Dmesg<'_> { + fn new() -> Self { + Dmesg { + kmsg_file: "/dev/kmsg", + output_format: OutputFormat::Normal, + _records: None, + } + } + + fn parse(self) -> Self { + self + } + + fn print(&self) { + + } +} + +enum OutputFormat { + Normal, + Json, +} + +struct Record { } From a044bece477e5f8747f5c342292fd7ba43aa77e0 Mon Sep 17 00:00:00 2001 From: Fuad Ismail Date: Fri, 15 Nov 2024 14:56:49 +0700 Subject: [PATCH 04/16] dmesg: implement parse function. --- Cargo.lock | 1 + src/uu/dmesg/Cargo.toml | 1 + src/uu/dmesg/src/dmesg.rs | 64 ++++++++++++++++++++++++++++++++++++--- 3 files changed, 61 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0efc2df..72ddd85 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -904,6 +904,7 @@ name = "uu_dmesg" version = "0.0.1" dependencies = [ "clap", + "regex", "uucore", ] diff --git a/src/uu/dmesg/Cargo.toml b/src/uu/dmesg/Cargo.toml index f63ee6a..df3ad04 100644 --- a/src/uu/dmesg/Cargo.toml +++ b/src/uu/dmesg/Cargo.toml @@ -13,3 +13,4 @@ path = "src/main.rs" [dependencies] clap = { workspace = true } uucore = { workspace = true } +regex = { workspace = true } diff --git a/src/uu/dmesg/src/dmesg.rs b/src/uu/dmesg/src/dmesg.rs index 6839756..d4ae899 100644 --- a/src/uu/dmesg/src/dmesg.rs +++ b/src/uu/dmesg/src/dmesg.rs @@ -1,4 +1,6 @@ use clap::{crate_version, Arg, ArgAction, Command}; +use regex::Regex; +use std::fs; use uucore::error::UResult; #[uucore::main] @@ -11,7 +13,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { if matches.get_flag(options::JSON) { dmesg.output_format = OutputFormat::Json; } - dmesg.parse().print(); + dmesg.parse()?.print(); Ok(()) } @@ -52,13 +54,44 @@ impl Dmesg<'_> { } } - fn parse(self) -> Self { - self + fn parse(mut self) -> UResult { + let mut records = vec![]; + let re = Self::record_regex(); + let lines = self.read_lines_from_kmsg_file()?; + for line in lines { + for (_, [pri_fac, seq, time, msg]) in re.captures_iter(&line).map(|c| c.extract()) { + records.push(Record::from_str_fields(pri_fac, seq, time, msg.to_string())); + } + } + self._records = Some(records); + Ok(self) } - fn print(&self) { - + fn record_regex() -> Regex { + let valid_number_pattern = "0|[1-9][0-9]*"; + let additional_fields_pattern = ",^[,;]*"; + let record_pattern = format!( + "(?m)^({0}),({0}),({0}),.(?:{1})*;(.*)$", + valid_number_pattern, additional_fields_pattern + ); + Regex::new(&record_pattern).expect("invalid regex.") } + + fn read_lines_from_kmsg_file(&self) -> UResult> { + let mut lines = vec![]; + let mut line = vec![]; + for byte in fs::read(self.kmsg_file)? { + if byte == 0 { + lines.push(String::from_utf8_lossy(&line).to_string()); + line.clear(); + } else { + line.push(byte); + } + } + Ok(lines) + } + + fn print(&self) {} } enum OutputFormat { @@ -67,4 +100,25 @@ enum OutputFormat { } struct Record { + _priority_facility: u32, + _sequence: u64, + _timestamp_us: u64, + _message: String, +} + +impl Record { + fn from_str_fields(pri_fac: &str, seq: &str, time: &str, msg: String) -> Record { + let pri_fac = str::parse(pri_fac); + let seq = str::parse(seq); + let time = str::parse(time); + match (pri_fac, seq, time) { + (Ok(pri_fac), Ok(seq), Ok(time)) => Record { + _priority_facility: pri_fac, + _sequence: seq, + _timestamp_us: time, + _message: msg, + }, + _ => panic!("parse error."), + } + } } From d1066420e7e35a84c43ba8c625ddbe0358ebbca0 Mon Sep 17 00:00:00 2001 From: Fuad Ismail Date: Fri, 15 Nov 2024 19:05:31 +0700 Subject: [PATCH 05/16] dmesg: implement print json. --- Cargo.lock | 2 + src/uu/dmesg/Cargo.toml | 2 + src/uu/dmesg/src/dmesg.rs | 21 ++++-- src/uu/dmesg/src/json.rs | 137 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 158 insertions(+), 4 deletions(-) create mode 100644 src/uu/dmesg/src/json.rs diff --git a/Cargo.lock b/Cargo.lock index 72ddd85..f565829 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -905,6 +905,8 @@ version = "0.0.1" dependencies = [ "clap", "regex", + "serde", + "serde_json", "uucore", ] diff --git a/src/uu/dmesg/Cargo.toml b/src/uu/dmesg/Cargo.toml index df3ad04..96df1cc 100644 --- a/src/uu/dmesg/Cargo.toml +++ b/src/uu/dmesg/Cargo.toml @@ -14,3 +14,5 @@ path = "src/main.rs" clap = { workspace = true } uucore = { workspace = true } regex = { workspace = true } +serde_json = { workspace = true } +serde = { workspace = true } diff --git a/src/uu/dmesg/src/dmesg.rs b/src/uu/dmesg/src/dmesg.rs index d4ae899..673b8f4 100644 --- a/src/uu/dmesg/src/dmesg.rs +++ b/src/uu/dmesg/src/dmesg.rs @@ -3,6 +3,8 @@ use regex::Regex; use std::fs; use uucore::error::UResult; +mod json; + #[uucore::main] pub fn uumain(args: impl uucore::Args) -> UResult<()> { let mut dmesg = Dmesg::new(); @@ -42,7 +44,7 @@ mod options { struct Dmesg<'a> { kmsg_file: &'a str, output_format: OutputFormat, - _records: Option>, + records: Option>, } impl Dmesg<'_> { @@ -50,7 +52,7 @@ impl Dmesg<'_> { Dmesg { kmsg_file: "/dev/kmsg", output_format: OutputFormat::Normal, - _records: None, + records: None, } } @@ -63,7 +65,7 @@ impl Dmesg<'_> { records.push(Record::from_str_fields(pri_fac, seq, time, msg.to_string())); } } - self._records = Some(records); + self.records = Some(records); Ok(self) } @@ -91,7 +93,18 @@ impl Dmesg<'_> { Ok(lines) } - fn print(&self) {} + fn print(&self) { + match self.output_format { + OutputFormat::Json => self.print_json(), + OutputFormat::Normal => unimplemented!(), + } + } + + fn print_json(&self) { + if let Some(records) = &self.records { + println!("{}", json::serialize_records(records)); + } + } } enum OutputFormat { diff --git a/src/uu/dmesg/src/json.rs b/src/uu/dmesg/src/json.rs new file mode 100644 index 0000000..b397e91 --- /dev/null +++ b/src/uu/dmesg/src/json.rs @@ -0,0 +1,137 @@ +use serde::Serialize; +use std::io; + +pub fn serialize_records(records: &Vec) -> String { + let json = Dmesg::from(records); + let formatter = DmesgFormatter::new(); + let mut buf = vec![]; + let mut serializer = serde_json::Serializer::with_formatter(&mut buf, formatter); + json.serialize(&mut serializer).unwrap(); + String::from_utf8_lossy(&buf).to_string() +} + +#[derive(serde::Serialize)] +struct Dmesg<'a> { + dmesg: Vec>, +} + +#[derive(serde::Serialize)] +struct Record<'a> { + pri: u32, + time: u64, + msg: &'a str, +} + +impl<'a> From<&'a Vec> for Dmesg<'a> { + fn from(value: &'a Vec) -> Self { + let mut dmesg_json = Dmesg { dmesg: vec![] }; + for record in value { + let record_json = Record { + pri: record._priority_facility, + time: record._timestamp_us, + msg: &record._message, + }; + dmesg_json.dmesg.push(record_json); + } + dmesg_json + } +} + +struct DmesgFormatter { + nesting_depth: i32, +} + +impl DmesgFormatter { + const SINGLE_INDENTATION: &[u8] = b" "; + + fn new() -> Self { + DmesgFormatter { nesting_depth: 0 } + } + + fn write_indentation(&mut self, writer: &mut W) -> io::Result<()> + where + W: ?Sized + io::Write, + { + for _ in 0..self.nesting_depth { + writer.write_all(Self::SINGLE_INDENTATION)?; + } + Ok(()) + } +} + +impl serde_json::ser::Formatter for DmesgFormatter { + fn begin_object(&mut self, writer: &mut W) -> io::Result<()> + where + W: ?Sized + io::Write, + { + self.nesting_depth += 1; + writer.write_all(b"{\n") + } + + fn end_object(&mut self, writer: &mut W) -> io::Result<()> + where + W: ?Sized + io::Write, + { + writer.write_all(b"\n")?; + self.nesting_depth -= 1; + self.write_indentation(writer)?; + writer.write_all(b"}") + } + + fn begin_array(&mut self, writer: &mut W) -> io::Result<()> + where + W: ?Sized + io::Write, + { + self.nesting_depth += 1; + writer.write_all(b"[\n") + } + + fn end_array(&mut self, writer: &mut W) -> io::Result<()> + where + W: ?Sized + io::Write, + { + writer.write_all(b"\n")?; + self.nesting_depth -= 1; + self.write_indentation(writer)?; + writer.write_all(b"]") + } + + fn begin_object_key(&mut self, writer: &mut W, first: bool) -> io::Result<()> + where + W: ?Sized + io::Write, + { + if !first { + writer.write_all(b",\n")?; + } + self.write_indentation(writer) + } + + fn begin_object_value(&mut self, writer: &mut W) -> io::Result<()> + where + W: ?Sized + io::Write, + { + writer.write_all(b": ") + } + + fn begin_array_value(&mut self, writer: &mut W, first: bool) -> io::Result<()> + where + W: ?Sized + io::Write, + { + if first { + self.write_indentation(writer) + } else { + writer.write_all(b",") + } + } + + fn write_u64(&mut self, writer: &mut W, value: u64) -> io::Result<()> + where + W: ?Sized + io::Write, + { + // The only u64 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); + writer.write_all(repr.as_bytes()) + } +} From 62d6c6d50956141ba17495be03caaba9f4b5cb37 Mon Sep 17 00:00:00 2001 From: Fuad Ismail Date: Fri, 15 Nov 2024 19:15:39 +0700 Subject: [PATCH 06/16] dmesg: remove underscore from used fields. --- src/uu/dmesg/src/dmesg.rs | 12 ++++++------ src/uu/dmesg/src/json.rs | 6 +++--- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/uu/dmesg/src/dmesg.rs b/src/uu/dmesg/src/dmesg.rs index 673b8f4..e03746b 100644 --- a/src/uu/dmesg/src/dmesg.rs +++ b/src/uu/dmesg/src/dmesg.rs @@ -113,10 +113,10 @@ enum OutputFormat { } struct Record { - _priority_facility: u32, + priority_facility: u32, _sequence: u64, - _timestamp_us: u64, - _message: String, + timestamp_us: u64, + message: String, } impl Record { @@ -126,10 +126,10 @@ impl Record { let time = str::parse(time); match (pri_fac, seq, time) { (Ok(pri_fac), Ok(seq), Ok(time)) => Record { - _priority_facility: pri_fac, + priority_facility: pri_fac, _sequence: seq, - _timestamp_us: time, - _message: msg, + timestamp_us: time, + message: msg, }, _ => panic!("parse error."), } diff --git a/src/uu/dmesg/src/json.rs b/src/uu/dmesg/src/json.rs index b397e91..481fd6f 100644 --- a/src/uu/dmesg/src/json.rs +++ b/src/uu/dmesg/src/json.rs @@ -27,9 +27,9 @@ impl<'a> From<&'a Vec> for Dmesg<'a> { let mut dmesg_json = Dmesg { dmesg: vec![] }; for record in value { let record_json = Record { - pri: record._priority_facility, - time: record._timestamp_us, - msg: &record._message, + pri: record.priority_facility, + time: record.timestamp_us, + msg: &record.message, }; dmesg_json.dmesg.push(record_json); } From 9a137921273bdf7b8a3f4ced7cff9e28b96f2380 Mon Sep 17 00:00:00 2001 From: Fuad Ismail Date: Fri, 15 Nov 2024 19:19:34 +0700 Subject: [PATCH 07/16] tests/dmesg: correct comment header. --- tests/by-util/test_dmesg.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/by-util/test_dmesg.rs b/tests/by-util/test_dmesg.rs index 095fb61..7d749eb 100644 --- a/tests/by-util/test_dmesg.rs +++ b/tests/by-util/test_dmesg.rs @@ -1,4 +1,4 @@ -// This file is part of the uutils coreutils package. +// 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. From 16b12c66f2dc1d16fcbbb5fcef4c663ac77df843 Mon Sep 17 00:00:00 2001 From: Fuad Ismail Date: Fri, 15 Nov 2024 20:05:05 +0700 Subject: [PATCH 08/16] dmesg: remove unnecessary static lifetime specifier. --- src/uu/dmesg/src/dmesg.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/uu/dmesg/src/dmesg.rs b/src/uu/dmesg/src/dmesg.rs index e03746b..f1644b4 100644 --- a/src/uu/dmesg/src/dmesg.rs +++ b/src/uu/dmesg/src/dmesg.rs @@ -37,8 +37,8 @@ pub fn uu_app() -> Command { } mod options { - pub const KMSG_FILE: &'static str = "kmsg-file"; - pub const JSON: &'static str = "json"; + pub const KMSG_FILE: &str = "kmsg-file"; + pub const JSON: &str = "json"; } struct Dmesg<'a> { From 0e3bab7600dc438071b99144fb519f04c12b7713 Mon Sep 17 00:00:00 2001 From: Fuad Ismail Date: Fri, 15 Nov 2024 21:57:16 +0700 Subject: [PATCH 09/16] tests/dmesg: handle added carriage return when fixture is checked out on Windows. --- tests/by-util/test_dmesg.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/by-util/test_dmesg.rs b/tests/by-util/test_dmesg.rs index 7d749eb..129fa0e 100644 --- a/tests/by-util/test_dmesg.rs +++ b/tests/by-util/test_dmesg.rs @@ -17,5 +17,5 @@ fn test_kmsg_json() { .arg("--json") .run() .no_stderr() - .stdout_is_fixture("test_kmsg_json.expected"); + .stdout_is_templated_fixture("test_kmsg_json.expected", &[("\r\n", "\n")]); } From e321aeefc3e4f719d67694edf0047fbc6b0ad723 Mon Sep 17 00:00:00 2001 From: Fuad Ismail Date: Sat, 16 Nov 2024 23:43:06 +0700 Subject: [PATCH 10/16] dmesg: add license header. --- src/uu/dmesg/src/dmesg.rs | 5 +++++ src/uu/dmesg/src/json.rs | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/src/uu/dmesg/src/dmesg.rs b/src/uu/dmesg/src/dmesg.rs index f1644b4..5d8857b 100644 --- a/src/uu/dmesg/src/dmesg.rs +++ b/src/uu/dmesg/src/dmesg.rs @@ -1,3 +1,8 @@ +// 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 clap::{crate_version, Arg, ArgAction, Command}; use regex::Regex; use std::fs; diff --git a/src/uu/dmesg/src/json.rs b/src/uu/dmesg/src/json.rs index 481fd6f..672d515 100644 --- a/src/uu/dmesg/src/json.rs +++ b/src/uu/dmesg/src/json.rs @@ -1,3 +1,8 @@ +// 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 serde::Serialize; use std::io; From 635c6955c33e40fa3aec8ce559fb1afac156c551 Mon Sep 17 00:00:00 2001 From: Fuad Ismail Date: Sun, 17 Nov 2024 00:00:34 +0700 Subject: [PATCH 11/16] dmesg: add help message to -K and -J options. --- src/uu/dmesg/src/dmesg.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/uu/dmesg/src/dmesg.rs b/src/uu/dmesg/src/dmesg.rs index 5d8857b..27ba0ef 100644 --- a/src/uu/dmesg/src/dmesg.rs +++ b/src/uu/dmesg/src/dmesg.rs @@ -31,12 +31,14 @@ pub fn uu_app() -> Command { Arg::new(options::KMSG_FILE) .short('K') .long("kmsg-file") + .help("use the file in kmsg format") .action(ArgAction::Set), ) .arg( Arg::new(options::JSON) .short('J') .long("json") + .help("use JSON output format") .action(ArgAction::SetTrue), ) } From 395c634c0c037fdcb5a139fd7241ddad6dc8a039 Mon Sep 17 00:00:00 2001 From: Fuad Ismail Date: Sun, 17 Nov 2024 00:09:55 +0700 Subject: [PATCH 12/16] dmesg: add about and usage to command. --- src/uu/dmesg/dmesg.md | 7 +++++++ src/uu/dmesg/src/dmesg.rs | 7 ++++++- 2 files changed, 13 insertions(+), 1 deletion(-) create mode 100644 src/uu/dmesg/dmesg.md diff --git a/src/uu/dmesg/dmesg.md b/src/uu/dmesg/dmesg.md new file mode 100644 index 0000000..25b1522 --- /dev/null +++ b/src/uu/dmesg/dmesg.md @@ -0,0 +1,7 @@ +# dmesg + +``` +dmesg [options] +``` + +Display or control the kernel ring buffer. diff --git a/src/uu/dmesg/src/dmesg.rs b/src/uu/dmesg/src/dmesg.rs index 27ba0ef..4f1a9c3 100644 --- a/src/uu/dmesg/src/dmesg.rs +++ b/src/uu/dmesg/src/dmesg.rs @@ -6,10 +6,13 @@ use clap::{crate_version, Arg, ArgAction, Command}; use regex::Regex; use std::fs; -use uucore::error::UResult; +use uucore::{error::UResult, format_usage, help_about, help_usage}; mod json; +const ABOUT: &str = help_about!("dmesg.md"); +const USAGE: &str = help_usage!("dmesg.md"); + #[uucore::main] pub fn uumain(args: impl uucore::Args) -> UResult<()> { let mut dmesg = Dmesg::new(); @@ -26,6 +29,8 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { pub fn uu_app() -> Command { Command::new(uucore::util_name()) + .override_usage(format_usage(USAGE)) + .about(ABOUT) .version(crate_version!()) .arg( Arg::new(options::KMSG_FILE) From ac6caf17e36f90669156a48e8ffa8d8e315a2690 Mon Sep 17 00:00:00 2001 From: Fuad Ismail Date: Sun, 17 Nov 2024 09:56:01 +0700 Subject: [PATCH 13/16] tests/dmesg: add kmsg-file option with nonexistent file test. --- tests/by-util/test_dmesg.rs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/tests/by-util/test_dmesg.rs b/tests/by-util/test_dmesg.rs index 129fa0e..08e398a 100644 --- a/tests/by-util/test_dmesg.rs +++ b/tests/by-util/test_dmesg.rs @@ -9,6 +9,17 @@ fn test_invalid_arg() { new_ucmd!().arg("--definitely-invalid").fails().code_is(1); } +#[test] +fn test_kmsg_nonexistent_file() { + new_ucmd!() + .arg("--kmsg-file") + .arg("definitely-nonexistent-file") + .fails() + .code_is(1) + .no_stdout() + .stderr_is("dmesg: cannot open definitely-nonexistent-file: No such file or directory\n"); +} + #[test] fn test_kmsg_json() { new_ucmd!() From e5441aa0bae5641ba2e56736f043f2d12b8d9603 Mon Sep 17 00:00:00 2001 From: Fuad Ismail Date: Sun, 17 Nov 2024 10:02:11 +0700 Subject: [PATCH 14/16] dmesg: add context message to UIoError. --- src/uu/dmesg/src/dmesg.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/uu/dmesg/src/dmesg.rs b/src/uu/dmesg/src/dmesg.rs index 4f1a9c3..41c7762 100644 --- a/src/uu/dmesg/src/dmesg.rs +++ b/src/uu/dmesg/src/dmesg.rs @@ -6,7 +6,7 @@ use clap::{crate_version, Arg, ArgAction, Command}; use regex::Regex; use std::fs; -use uucore::{error::UResult, format_usage, help_about, help_usage}; +use uucore::{error::FromIo, error::UResult, format_usage, help_about, help_usage}; mod json; @@ -94,7 +94,9 @@ impl Dmesg<'_> { fn read_lines_from_kmsg_file(&self) -> UResult> { let mut lines = vec![]; let mut line = vec![]; - for byte in fs::read(self.kmsg_file)? { + let kmsg_bytes = fs::read(self.kmsg_file) + .map_err_context(|| format!("cannot open {}", self.kmsg_file))?; + for byte in kmsg_bytes { if byte == 0 { lines.push(String::from_utf8_lossy(&line).to_string()); line.clear(); From df3fb0674d36b43ee82a2310c7c19f9873e3629c Mon Sep 17 00:00:00 2001 From: Fuad Ismail Date: Mon, 18 Nov 2024 11:28:36 +0700 Subject: [PATCH 15/16] dmesg: use functional approach to read lines. --- src/uu/dmesg/src/dmesg.rs | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/src/uu/dmesg/src/dmesg.rs b/src/uu/dmesg/src/dmesg.rs index 41c7762..6ecb512 100644 --- a/src/uu/dmesg/src/dmesg.rs +++ b/src/uu/dmesg/src/dmesg.rs @@ -92,18 +92,12 @@ impl Dmesg<'_> { } fn read_lines_from_kmsg_file(&self) -> UResult> { - let mut lines = vec![]; - let mut line = vec![]; let kmsg_bytes = fs::read(self.kmsg_file) .map_err_context(|| format!("cannot open {}", self.kmsg_file))?; - for byte in kmsg_bytes { - if byte == 0 { - lines.push(String::from_utf8_lossy(&line).to_string()); - line.clear(); - } else { - line.push(byte); - } - } + let lines = kmsg_bytes + .split(|&byte| byte == 0) + .map(|line| String::from_utf8_lossy(line).to_string()) + .collect(); Ok(lines) } From d796cb52242518a93902c092868abbd35bd22685 Mon Sep 17 00:00:00 2001 From: Fuad Ismail Date: Mon, 18 Nov 2024 11:52:47 +0700 Subject: [PATCH 16/16] dmesg: return UResult in Record constructor instead of panic. --- src/uu/dmesg/src/dmesg.rs | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/src/uu/dmesg/src/dmesg.rs b/src/uu/dmesg/src/dmesg.rs index 6ecb512..8fb64f7 100644 --- a/src/uu/dmesg/src/dmesg.rs +++ b/src/uu/dmesg/src/dmesg.rs @@ -6,7 +6,10 @@ use clap::{crate_version, Arg, ArgAction, Command}; use regex::Regex; use std::fs; -use uucore::{error::FromIo, error::UResult, format_usage, help_about, help_usage}; +use uucore::{ + error::{FromIo, UResult, USimpleError}, + format_usage, help_about, help_usage, +}; mod json; @@ -74,7 +77,12 @@ impl Dmesg<'_> { let lines = self.read_lines_from_kmsg_file()?; for line in lines { for (_, [pri_fac, seq, time, msg]) in re.captures_iter(&line).map(|c| c.extract()) { - records.push(Record::from_str_fields(pri_fac, seq, time, msg.to_string())); + records.push(Record::from_str_fields( + pri_fac, + seq, + time, + msg.to_string(), + )?); } } self.records = Some(records); @@ -128,18 +136,18 @@ struct Record { } impl Record { - fn from_str_fields(pri_fac: &str, seq: &str, time: &str, msg: String) -> Record { + fn from_str_fields(pri_fac: &str, seq: &str, time: &str, msg: String) -> UResult { let pri_fac = str::parse(pri_fac); let seq = str::parse(seq); let time = str::parse(time); match (pri_fac, seq, time) { - (Ok(pri_fac), Ok(seq), Ok(time)) => Record { + (Ok(pri_fac), Ok(seq), Ok(time)) => Ok(Record { priority_facility: pri_fac, _sequence: seq, timestamp_us: time, message: msg, - }, - _ => panic!("parse error."), + }), + _ => Err(USimpleError::new(1, "Failed to parse record field(s)")), } } }