Merge pull request from kreijack/correct-dmesg

dmesg: fix a non working support of /dev/kmesg
This commit is contained in:
Daniel Hofstetter 2025-01-07 09:34:31 +01:00 committed by GitHub
commit f832e8b883
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -9,15 +9,23 @@ use regex::Regex;
use std::{ use std::{
collections::HashSet, collections::HashSet,
fs::File, fs::File,
fs::OpenOptions,
hash::Hash, hash::Hash,
io::{BufRead, BufReader}, io::{BufRead, BufReader, ErrorKind},
sync::OnceLock, sync::OnceLock,
}; };
#[cfg(not(target_os = "windows"))]
use std::{os::fd::AsRawFd, os::unix::fs::OpenOptionsExt};
use uucore::{ use uucore::{
error::{FromIo, UError, UResult, USimpleError}, error::{FromIo, UError, UIoError, UResult, USimpleError},
format_usage, help_about, help_usage, format_usage, help_about, help_usage,
}; };
#[cfg(not(target_os = "windows"))]
use uucore::libc;
mod json; mod json;
mod time_formatter; mod time_formatter;
@ -28,9 +36,6 @@ const USAGE: &str = help_usage!("dmesg.md");
pub fn uumain(args: impl uucore::Args) -> UResult<()> { pub fn uumain(args: impl uucore::Args) -> UResult<()> {
let mut dmesg = Dmesg::new(); let mut dmesg = Dmesg::new();
let matches: clap::ArgMatches = uu_app().try_get_matches_from(args)?; let matches: clap::ArgMatches = uu_app().try_get_matches_from(args)?;
if let Some(kmsg_file) = matches.get_one::<String>(options::KMSG_FILE) {
dmesg.kmsg_file = kmsg_file;
}
if matches.get_flag(options::JSON) { if matches.get_flag(options::JSON) {
dmesg.output_format = OutputFormat::Json; dmesg.output_format = OutputFormat::Json;
} }
@ -112,6 +117,12 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
if let Some(until) = matches.get_one::<String>(options::UNTIL) { if let Some(until) = matches.get_one::<String>(options::UNTIL) {
dmesg.until_filter = Some(time_formatter::parse_datetime(until)?); dmesg.until_filter = Some(time_formatter::parse_datetime(until)?);
} }
if let Some(kmsg_file) = matches.get_one::<String>(options::KMSG_FILE) {
dmesg.kmsg_file = kmsg_file;
dmesg.kmsg_record_separator = 0;
} else if cfg!(target_os = "windows") {
return Err(USimpleError::new(1, "Windows requires the use of '-K'"));
}
dmesg.print()?; dmesg.print()?;
Ok(()) Ok(())
} }
@ -184,6 +195,7 @@ mod options {
struct Dmesg<'a> { struct Dmesg<'a> {
kmsg_file: &'a str, kmsg_file: &'a str,
kmsg_record_separator: u8,
output_format: OutputFormat, output_format: OutputFormat,
time_format: TimeFormat, time_format: TimeFormat,
facility_filters: Option<HashSet<Facility>>, facility_filters: Option<HashSet<Facility>>,
@ -196,6 +208,7 @@ impl Dmesg<'_> {
fn new() -> Self { fn new() -> Self {
Dmesg { Dmesg {
kmsg_file: "/dev/kmsg", kmsg_file: "/dev/kmsg",
kmsg_record_separator: b'\n',
output_format: OutputFormat::Normal, output_format: OutputFormat::Normal,
time_format: TimeFormat::Raw, time_format: TimeFormat::Raw,
facility_filters: None, facility_filters: None,
@ -256,10 +269,27 @@ impl Dmesg<'_> {
} }
fn try_iter(&self) -> UResult<RecordIterator> { fn try_iter(&self) -> UResult<RecordIterator> {
let file = File::open(self.kmsg_file) let mut open_option = OpenOptions::new();
open_option.read(true);
#[cfg(not(target_os = "windows"))]
open_option.custom_flags(libc::O_NONBLOCK);
let file = open_option
.open(self.kmsg_file)
.map_err_context(|| format!("cannot open {}", self.kmsg_file))?; .map_err_context(|| format!("cannot open {}", self.kmsg_file))?;
#[cfg(not(target_os = "windows"))]
{
let fd = file.as_raw_fd();
unsafe { libc::lseek(fd, 0, libc::SEEK_DATA) };
}
let file_reader = BufReader::new(file); let file_reader = BufReader::new(file);
Ok(RecordIterator { file_reader }) Ok(RecordIterator {
file_reader,
kmsg_record_separator: self.kmsg_record_separator,
})
} }
fn is_record_in_set<T>( fn is_record_in_set<T>(
@ -359,6 +389,7 @@ enum Level {
struct RecordIterator { struct RecordIterator {
file_reader: BufReader<File>, file_reader: BufReader<File>,
kmsg_record_separator: u8,
} }
impl Iterator for RecordIterator { impl Iterator for RecordIterator {
@ -379,10 +410,20 @@ impl Iterator for RecordIterator {
impl RecordIterator { impl RecordIterator {
fn read_record_line(&mut self) -> UResult<Option<String>> { fn read_record_line(&mut self) -> UResult<Option<String>> {
let mut buf = vec![]; let mut buf = vec![];
let num_bytes = self.file_reader.read_until(0, &mut buf)?; match self
match num_bytes { .file_reader
0 => Ok(None), .read_until(self.kmsg_record_separator, &mut buf)
_ => Ok(Some(String::from_utf8_lossy(&buf).to_string())), {
/*
* - a read(2) from /dev/kmsg returns WouldBlock if there aren't
* any new record
* - a read(2) from a file returns 0 if the we reached the end
* In these cases return Ok(None)
*/
Ok(0) => Ok(None),
Ok(_) => Ok(Some(String::from_utf8_lossy(&buf).to_string())),
Err(e) if e.kind() == ErrorKind::WouldBlock => Ok(None),
Err(e) => Err(Box::new(UIoError::from(e))),
} }
} }