diff --git a/Cargo.lock b/Cargo.lock index f755659b..93d2fdcf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -712,6 +712,7 @@ dependencies = [ "jay-geometry", "jay-io-uring", "jay-layout-animation", + "jay-logger", "jay-pr-caps", "jay-sighand", "jay-time", @@ -866,6 +867,24 @@ dependencies = [ "jay-geometry", ] +[[package]] +name = "jay-logger" +version = "0.1.0" +dependencies = [ + "backtrace", + "bstr", + "clap", + "dirs", + "humantime", + "jay-config", + "jay-utils", + "linearize", + "log", + "parking_lot", + "thiserror", + "uapi", +] + [[package]] name = "jay-pr-caps" version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml index d8f8d6cf..f2512308 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -41,6 +41,7 @@ members = [ "sighand", "pr-caps", "bugs", + "logger", "toml-config", "algorithms", "toml-spec", @@ -83,6 +84,7 @@ jay-cpu-worker = { version = "0.1.0", path = "cpu-worker" } jay-sighand = { version = "0.1.0", path = "sighand" } jay-pr-caps = { version = "0.1.0", path = "pr-caps" } jay-bugs = { version = "0.1.0", path = "bugs" } +jay-logger = { version = "0.1.0", path = "logger" } uapi = "0.2.13" thiserror = "2.0.11" diff --git a/logger/Cargo.toml b/logger/Cargo.toml new file mode 100644 index 00000000..b59202a5 --- /dev/null +++ b/logger/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "jay-logger" +version = "0.1.0" +edition = "2024" +license = "GPL-3.0-only" + +[dependencies] +jay-config = { version = "1.10.0", path = "../jay-config" } +jay-utils = { version = "0.1.0", path = "../utils" } + +backtrace = "0.3.69" +bstr = { version = "1.9.0", default-features = false, features = ["std"] } +clap = { version = "4.4.18", features = ["derive", "wrap_help"] } +dirs = "6.0.0" +humantime = "2.1.0" +linearize = { version = "0.1.3", features = ["derive"] } +log = { version = "0.4.20", features = ["std"] } +parking_lot = "0.12.1" +thiserror = "2.0.11" +uapi = "0.2.13" diff --git a/logger/src/lib.rs b/logger/src/lib.rs new file mode 100644 index 00000000..8752fdee --- /dev/null +++ b/logger/src/lib.rs @@ -0,0 +1,374 @@ +use { + jay_config::logging::LogLevel as ConfigLogLevel, + jay_utils::{ + atomic_enum::AtomicEnum, + errorfmt::ErrorFmt, + oserror::{OsError, OsErrorExt, OsErrorExt2}, + static_text::StaticText, + }, + backtrace::Backtrace, + bstr::{BStr, BString, ByteSlice}, + clap::ValueEnum, + linearize::Linearize, + log::{LevelFilter, Log, Metadata, Record}, + parking_lot::Mutex, + std::{ + cell::Cell, + fmt::Arguments, + fs::DirBuilder, + io::Write, + os::unix::{ffi::OsStringExt, fs::DirBuilderExt}, + ptr, + sync::{ + Arc, + atomic::{AtomicI32, AtomicU32, Ordering::Relaxed}, + }, + thread, + time::SystemTime, + }, + thiserror::Error, + uapi::{AsUstr, Dirent, Fd, OwnedFd, Ustring, c, format_ustr}, +}; + +#[derive(ValueEnum, Debug, Copy, Clone, Hash, Default, Eq, PartialEq, Linearize)] +pub enum LogLevel { + Trace, + Debug, + #[default] + Info, + Warn, + Error, + Off, +} + +impl From for LevelFilter { + fn from(value: LogLevel) -> Self { + match value { + LogLevel::Trace => LevelFilter::Trace, + LogLevel::Debug => LevelFilter::Debug, + LogLevel::Info => LevelFilter::Info, + LogLevel::Warn => LevelFilter::Warn, + LogLevel::Error => LevelFilter::Error, + LogLevel::Off => LevelFilter::Off, + } + } +} + +impl From for LogLevel { + fn from(value: LevelFilter) -> Self { + match value { + LevelFilter::Trace => LogLevel::Trace, + LevelFilter::Debug => LogLevel::Debug, + LevelFilter::Info => LogLevel::Info, + LevelFilter::Warn => LogLevel::Warn, + LevelFilter::Error => LogLevel::Error, + LevelFilter::Off => LogLevel::Off, + } + } +} + +impl StaticText for LogLevel { + fn text(&self) -> &'static str { + match self { + LogLevel::Off => "Off", + LogLevel::Error => "Error", + LogLevel::Warn => "Warn", + LogLevel::Info => "Info", + LogLevel::Debug => "Debug", + LogLevel::Trace => "Trace", + } + } +} + +impl From for LogLevel { + fn from(value: ConfigLogLevel) -> Self { + match value { + ConfigLogLevel::Trace => LogLevel::Trace, + ConfigLogLevel::Debug => LogLevel::Debug, + ConfigLogLevel::Info => LogLevel::Info, + ConfigLogLevel::Warn => LogLevel::Warn, + ConfigLogLevel::Error => LogLevel::Error, + } + } +} + +fn fatal(args: Arguments<'_>) -> ! { + log::error!("{}", args); + std::process::exit(1); +} + +thread_local! { + static BUFFER: Cell<*mut Vec> = const { Cell::new(ptr::null_mut()) }; +} + +pub struct Logger { + level: AtomicEnum, + filter: AtomicU32, + path: Mutex>, + _file: Mutex, + file_fd: AtomicI32, +} + +impl Logger { + pub fn install_stderr(level: LogLevel) -> Arc { + let file = match uapi::fcntl_dupfd_cloexec(2, 0).to_os_error() { + Ok(fd) => fd, + Err(e) => { + fatal(format_args!("Error: Could not dup stderr: {}", ErrorFmt(e))); + } + }; + Self::install(level, b"STDERR", file) + } + + pub fn install_compositor(level: LogLevel) -> Arc { + let (path, file) = open_log_file("jay"); + Self::install(level, path.as_bytes(), file) + } + + pub fn install_pipe(file: OwnedFd, level: LogLevel) -> Arc { + Self::install(level, b"PIPE", file) + } + + fn install(level: LogLevel, path: &[u8], file: OwnedFd) -> Arc { + let filter: LevelFilter = level.into(); + let slf = Arc::new(Self { + level: AtomicEnum::new(level), + filter: AtomicU32::new(filter as _), + path: Mutex::new(Arc::new(path.to_vec().into())), + file_fd: AtomicI32::new(file.raw()), + _file: Mutex::new(file), + }); + log::set_boxed_logger(Box::new(LogWrapper { + logger: slf.clone(), + })) + .unwrap(); + log::set_max_level(filter); + set_panic_hook(); + slf + } + + pub fn set_level(&self, level: LogLevel) { + let filter: LevelFilter = level.into(); + self.level.store(level, Relaxed); + self.filter.store(filter as _, Relaxed); + log::set_max_level(filter); + } + + pub fn clean_logs_older_than(&self, time: SystemTime) { + let time_formatted = humantime::format_rfc3339_millis(time); + log::info!("Cleaning unused log files older than {}", time_formatted); + let path = self.path(); + thread::spawn(move || { + if let Err(e) = clean_logs_older_than(path.as_bstr(), time) { + log::error!("Could not clean log files: {}", ErrorFmt(e)); + } + }); + } + + pub fn path(&self) -> Arc { + self.path.lock().clone() + } + + pub fn redirect(&self, ty: &str) -> Ustring { + let (file, fd) = open_log_file(ty); + log::info!("Redirecting logs to {}", file.display()); + *self.path.lock() = Arc::new(file.as_bytes().into()); + self.file_fd.store(fd.raw(), Relaxed); + *self._file.lock() = fd; + file + } + + pub fn write_raw(&self, buf: &[u8]) { + let mut fd = Fd::new(self.file_fd.load(Relaxed)); + let _ = fd.write_all(buf); + } +} + +pub fn open_log_file(ty: &str) -> (Ustring, OwnedFd) { + let log_dir = create_log_dir(ty); + let mut flock_fail_count = 0; + for i in 0.. { + let file_name = format_ustr!( + "{}/{ty}-{}-{}.txt", + log_dir, + humantime::format_rfc3339_millis(SystemTime::now()), + i, + ); + match uapi::open( + &file_name, + c::O_CREAT | c::O_EXCL | c::O_CLOEXEC | c::O_WRONLY, + 0o644, + ) + .to_os_error() + { + Ok(f) => { + if let Err(e) = uapi::flock(f.raw(), c::LOCK_EX | c::LOCK_NB) { + log::warn!("Unable to flock just-opened logfile: {}", ErrorFmt(e)); + flock_fail_count += 1; + if flock_fail_count > 10 { + log::error!(concat!( + "Failed to flock just-opened logfile more than 10 times in a row. ", + "Not flocking the logfile, if the cleanup routine later succeeds to ", + "flock this logfile, it will be deleted even if it is still in use." + )); + } else { + continue; + } + } + return (file_name, f); + } + Err(OsError(c::EEXIST)) => {} + Err(e) => { + fatal(format_args!( + "Error: Could not create log file: {}", + ErrorFmt(e) + )); + } + } + } + unreachable!() +} + +fn create_log_dir(ty: &str) -> BString { + let mut log_dir = match dirs::data_local_dir() { + Some(d) => d, + None => fatal(format_args!("Error: $HOME is not set")), + }; + log_dir.push("jay"); + log_dir.push("logs"); + log_dir.push(ty); + let res = DirBuilder::new() + .recursive(true) + .mode(0o755) + .create(&log_dir); + if let Err(e) = res { + fatal(format_args!( + "Error: Could not create log directory {}: {}", + log_dir.display(), + ErrorFmt(e) + )); + } + log_dir.into_os_string().into_vec().into() +} + +fn set_panic_hook() { + std::panic::set_hook(Box::new(|p| { + if let Some(loc) = p.location() { + log::error!( + "Panic at {} line {} column {}", + loc.file(), + loc.line(), + loc.column() + ); + } else { + log::error!("Panic at unknown location"); + } + if let Some(msg) = p.payload().downcast_ref::<&str>() { + log::error!("Message: {}", msg); + } + if let Some(msg) = p.payload().downcast_ref::() { + log::error!("Message: {}", msg); + } + log::error!("Backtrace:\n{:?}", Backtrace::new()); + })); +} + +struct LogWrapper { + logger: Arc, +} + +impl Log for LogWrapper { + fn enabled(&self, metadata: &Metadata) -> bool { + metadata.level() as u32 <= self.logger.filter.load(Relaxed) + } + + fn log(&self, record: &Record) { + if record.level() as u32 > self.logger.filter.load(Relaxed) { + return; + } + let mut buffer = BUFFER.get(); + if buffer.is_null() { + buffer = Box::into_raw(Box::default()); + BUFFER.set(buffer); + } + let buffer = unsafe { &mut *buffer }; + buffer.clear(); + let now = SystemTime::now(); + let _ = writeln!( + buffer, + "[{} {:5} {}] {}", + humantime::format_rfc3339_millis(now), + record.level(), + record.target(), + record.args(), + ); + let mut fd = Fd::new(self.logger.file_fd.load(Relaxed)); + let _ = fd.write_all(buffer); + } + + fn flush(&self) { + // nothing + } +} + +#[derive(Debug, Error)] +enum CleanLogsError { + #[error("Log path has no parent")] + NoParent, + #[error("Could not open the log directory")] + OpenDir(#[source] OsError), + #[error("Could not enumerate directory entry")] + ReadDir(#[source] OsError), + #[error("Could not open the log file")] + OpenFile(#[source] OsError), + #[error("Could not stat the log file")] + Stat(#[source] OsError), + #[error("Could not unlink the log file")] + Unlink(#[source] OsError), +} + +fn clean_logs_older_than(current_log_path: &BStr, time: SystemTime) -> Result<(), CleanLogsError> { + let current_log_path = current_log_path.to_path_lossy(); + let parent = current_log_path.parent().ok_or(CleanLogsError::NoParent)?; + let mut dir = uapi::opendir(parent).map_os_err(CleanLogsError::OpenDir)?; + let parent = uapi::open(parent, c::O_PATH | c::O_CLOEXEC | c::O_DIRECTORY, 0) + .map_os_err(CleanLogsError::OpenDir)?; + let time = time + .duration_since(SystemTime::UNIX_EPOCH) + .unwrap_or_default() + .as_secs() as c::time_t; + while let Some(entry) = uapi::readdir(&mut dir) { + let entry = entry.map_os_err(CleanLogsError::ReadDir)?; + if let Err(err) = process_entry(parent.raw(), &entry, time) { + log::error!( + "Could not clean log file {}: {}", + entry.name().as_ustr().display(), + ErrorFmt(err), + ); + } + } + fn process_entry( + parent: c::c_int, + entry: &Dirent, + time: c::time_t, + ) -> Result<(), CleanLogsError> { + if entry.d_type != c::DT_REG { + return Ok(()); + } + let name = entry.name(); + let file = uapi::openat(parent, name, c::O_RDONLY | c::O_CLOEXEC, 0) + .map_os_err(CleanLogsError::OpenFile)?; + let stat = uapi::fstat(*file).map_os_err(CleanLogsError::Stat)?; + if stat.st_mtime >= time { + return Ok(()); + } + if uapi::flock(file.raw(), c::LOCK_EX | c::LOCK_NB).is_err() { + log::info!("Preserving file still in use: {}", name.as_ustr().display()); + return Ok(()); + } + uapi::unlinkat(parent, name, 0).map_os_err(CleanLogsError::Unlink)?; + log::info!("Deleted {}", name.as_ustr().display()); + Ok(()) + } + Ok(()) +} diff --git a/src/cli.rs b/src/cli.rs index 21aa3ec6..951fbe85 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -30,7 +30,8 @@ use { json::VERBOSE_JSON, randr::RandrArgs, reexec::ReexecArgs, tree::TreeArgs, xwayland::XwaylandArgs, }, - compositor::{LogLevel, start_compositor}, + compositor::start_compositor, + logger::LogLevel, pr_caps::drop_all_pr_caps, }, clap::{Args, Parser, Subcommand, ValueEnum, ValueHint}, diff --git a/src/compositor.rs b/src/compositor.rs index c9ca9269..17db782f 100644 --- a/src/compositor.rs +++ b/src/compositor.rs @@ -77,7 +77,6 @@ use { rc_eq::RcEq, refcounted::RefCounted, run_toplevel::RunToplevel, - static_text::StaticText, tri::Try, }, version::VERSION, @@ -85,11 +84,8 @@ use { wheel::{Wheel, WheelError}, }, ahash::AHashSet, - clap::ValueEnum, forker::ForkerProxy, - jay_config::{_private::DEFAULT_SEAT_NAME, logging::LogLevel as ConfigLogLevel}, - linearize::Linearize, - log::LevelFilter, + jay_config::_private::DEFAULT_SEAT_NAME, std::{ cell::{Cell, RefCell}, env, @@ -805,65 +801,3 @@ pub fn config_dir() -> Option { None } } - -#[derive(ValueEnum, Debug, Copy, Clone, Hash, Default, Eq, PartialEq, Linearize)] -pub enum LogLevel { - Trace, - Debug, - #[default] - Info, - Warn, - Error, - Off, -} - -impl Into for LogLevel { - fn into(self) -> LevelFilter { - match self { - LogLevel::Trace => LevelFilter::Trace, - LogLevel::Debug => LevelFilter::Debug, - LogLevel::Info => LevelFilter::Info, - LogLevel::Warn => LevelFilter::Warn, - LogLevel::Error => LevelFilter::Error, - LogLevel::Off => LevelFilter::Off, - } - } -} - -impl From for LogLevel { - fn from(value: LevelFilter) -> Self { - match value { - LevelFilter::Trace => LogLevel::Trace, - LevelFilter::Debug => LogLevel::Debug, - LevelFilter::Info => LogLevel::Info, - LevelFilter::Warn => LogLevel::Warn, - LevelFilter::Error => LogLevel::Error, - LevelFilter::Off => LogLevel::Off, - } - } -} - -impl StaticText for LogLevel { - fn text(&self) -> &'static str { - match self { - LogLevel::Off => "Off", - LogLevel::Error => "Error", - LogLevel::Warn => "Warn", - LogLevel::Info => "Info", - LogLevel::Debug => "Debug", - LogLevel::Trace => "Trace", - } - } -} - -impl From for LogLevel { - fn from(value: ConfigLogLevel) -> Self { - match value { - ConfigLogLevel::Trace => LogLevel::Trace, - ConfigLogLevel::Debug => LogLevel::Debug, - ConfigLogLevel::Info => LogLevel::Info, - ConfigLogLevel::Warn => LogLevel::Warn, - ConfigLogLevel::Error => LogLevel::Error, - } - } -} diff --git a/src/ifs/jay_compositor.rs b/src/ifs/jay_compositor.rs index c4be67f8..4e2bcbda 100644 --- a/src/ifs/jay_compositor.rs +++ b/src/ifs/jay_compositor.rs @@ -2,7 +2,6 @@ use { crate::{ backend::transaction::BackendConnectorTransactionError, client::{CAP_JAY_COMPOSITOR, Client, ClientCaps, ClientError, ClientId}, - compositor::LogLevel, globals::{Global, GlobalName}, ifs::{ jay_client_query::JayClientQuery, @@ -27,6 +26,7 @@ use { wl_surface::jay_sync_file_surface::JaySyncFileSurface, }, leaks::Tracker, + logger::LogLevel, object::{Object, Version}, screenshoter::take_screenshot, tree::ToplevelIdentifier, diff --git a/src/logger.rs b/src/logger.rs index f5e2266d..52e3bb65 100644 --- a/src/logger.rs +++ b/src/logger.rs @@ -1,302 +1 @@ -use { - crate::{ - compositor::LogLevel, - utils::{ - atomic_enum::AtomicEnum, - errorfmt::ErrorFmt, - oserror::{OsError, OsErrorExt, OsErrorExt2}, - }, - }, - backtrace::Backtrace, - bstr::{BStr, BString, ByteSlice}, - log::{LevelFilter, Log, Metadata, Record}, - parking_lot::Mutex, - std::{ - cell::Cell, - fs::DirBuilder, - io::Write, - os::unix::{ffi::OsStringExt, fs::DirBuilderExt}, - ptr, - sync::{ - Arc, - atomic::{AtomicI32, AtomicU32, Ordering::Relaxed}, - }, - thread, - time::SystemTime, - }, - thiserror::Error, - uapi::{AsUstr, Dirent, Fd, OwnedFd, Ustring, c, format_ustr}, -}; - -thread_local! { - static BUFFER: Cell<*mut Vec> = const { Cell::new(ptr::null_mut()) }; -} - -pub struct Logger { - level: AtomicEnum, - filter: AtomicU32, - path: Mutex>, - _file: Mutex, - file_fd: AtomicI32, -} - -impl Logger { - pub fn install_stderr(level: LogLevel) -> Arc { - let file = match uapi::fcntl_dupfd_cloexec(2, 0).to_os_error() { - Ok(fd) => fd, - Err(e) => { - fatal!("Error: Could not dup stderr: {}", ErrorFmt(e)); - } - }; - Self::install(level, b"STDERR", file) - } - - pub fn install_compositor(level: LogLevel) -> Arc { - let (path, file) = open_log_file("jay"); - Self::install(level, path.as_bytes(), file) - } - - pub fn install_pipe(file: OwnedFd, level: LogLevel) -> Arc { - Self::install(level, b"PIPE", file) - } - - fn install(level: LogLevel, path: &[u8], file: OwnedFd) -> Arc { - let filter: LevelFilter = level.into(); - let slf = Arc::new(Self { - level: AtomicEnum::new(level), - filter: AtomicU32::new(filter as _), - path: Mutex::new(Arc::new(path.to_vec().into())), - file_fd: AtomicI32::new(file.raw()), - _file: Mutex::new(file), - }); - log::set_boxed_logger(Box::new(LogWrapper { - logger: slf.clone(), - })) - .unwrap(); - log::set_max_level(filter); - set_panic_hook(); - slf - } - - pub fn set_level(&self, level: LogLevel) { - let filter: LevelFilter = level.into(); - self.level.store(level, Relaxed); - self.filter.store(filter as _, Relaxed); - log::set_max_level(filter); - } - - pub fn clean_logs_older_than(&self, time: SystemTime) { - let time_formatted = humantime::format_rfc3339_millis(time); - log::info!("Cleaning unused log files older than {}", time_formatted); - let path = self.path(); - thread::spawn(move || { - if let Err(e) = clean_logs_older_than(path.as_bstr(), time) { - log::error!("Could not clean log files: {}", ErrorFmt(e)); - } - }); - } - - pub fn path(&self) -> Arc { - self.path.lock().clone() - } - - pub fn redirect(&self, ty: &str) -> Ustring { - let (file, fd) = open_log_file(ty); - log::info!("Redirecting logs to {}", file.display()); - *self.path.lock() = Arc::new(file.as_bytes().into()); - self.file_fd.store(fd.raw(), Relaxed); - *self._file.lock() = fd; - file - } - - pub fn write_raw(&self, buf: &[u8]) { - let mut fd = Fd::new(self.file_fd.load(Relaxed)); - let _ = fd.write_all(buf); - } -} - -pub fn open_log_file(ty: &str) -> (Ustring, OwnedFd) { - let log_dir = create_log_dir(ty); - let mut flock_fail_count = 0; - for i in 0.. { - let file_name = format_ustr!( - "{}/{ty}-{}-{}.txt", - log_dir, - humantime::format_rfc3339_millis(SystemTime::now()), - i, - ); - match uapi::open( - &file_name, - c::O_CREAT | c::O_EXCL | c::O_CLOEXEC | c::O_WRONLY, - 0o644, - ) - .to_os_error() - { - Ok(f) => { - if let Err(e) = uapi::flock(f.raw(), c::LOCK_EX | c::LOCK_NB) { - log::warn!("Unable to flock just-opened logfile: {}", ErrorFmt(e)); - flock_fail_count += 1; - if flock_fail_count > 10 { - log::error!(concat!( - "Failed to flock just-opened logfile more than 10 times in a row. ", - "Not flocking the logfile, if the cleanup routine later succeeds to ", - "flock this logfile, it will be deleted even if it is still in use." - )); - } else { - continue; - } - } - return (file_name, f); - } - Err(OsError(c::EEXIST)) => {} - Err(e) => { - fatal!("Error: Could not create log file: {}", ErrorFmt(e)); - } - } - } - unreachable!() -} - -fn create_log_dir(ty: &str) -> BString { - let mut log_dir = match dirs::data_local_dir() { - Some(d) => d, - None => fatal!("Error: $HOME is not set"), - }; - log_dir.push("jay"); - log_dir.push("logs"); - log_dir.push(ty); - let res = DirBuilder::new() - .recursive(true) - .mode(0o755) - .create(&log_dir); - if let Err(e) = res { - fatal!( - "Error: Could not create log directory {}: {}", - log_dir.display(), - ErrorFmt(e) - ); - } - log_dir.into_os_string().into_vec().into() -} - -fn set_panic_hook() { - std::panic::set_hook(Box::new(|p| { - if let Some(loc) = p.location() { - log::error!( - "Panic at {} line {} column {}", - loc.file(), - loc.line(), - loc.column() - ); - } else { - log::error!("Panic at unknown location"); - } - if let Some(msg) = p.payload().downcast_ref::<&str>() { - log::error!("Message: {}", msg); - } - if let Some(msg) = p.payload().downcast_ref::() { - log::error!("Message: {}", msg); - } - log::error!("Backtrace:\n{:?}", Backtrace::new()); - })); -} - -struct LogWrapper { - logger: Arc, -} - -impl Log for LogWrapper { - fn enabled(&self, metadata: &Metadata) -> bool { - metadata.level() as u32 <= self.logger.filter.load(Relaxed) - } - - fn log(&self, record: &Record) { - if record.level() as u32 > self.logger.filter.load(Relaxed) { - return; - } - let mut buffer = BUFFER.get(); - if buffer.is_null() { - buffer = Box::into_raw(Box::default()); - BUFFER.set(buffer); - } - let buffer = unsafe { &mut *buffer }; - buffer.clear(); - let now = SystemTime::now(); - let _ = writeln!( - buffer, - "[{} {:5} {}] {}", - humantime::format_rfc3339_millis(now), - record.level(), - record.target(), - record.args(), - ); - let mut fd = Fd::new(self.logger.file_fd.load(Relaxed)); - let _ = fd.write_all(buffer); - } - - fn flush(&self) { - // nothing - } -} - -#[derive(Debug, Error)] -enum CleanLogsError { - #[error("Log path has no parent")] - NoParent, - #[error("Could not open the log directory")] - OpenDir(#[source] OsError), - #[error("Could not enumerate directory entry")] - ReadDir(#[source] OsError), - #[error("Could not open the log file")] - OpenFile(#[source] OsError), - #[error("Could not stat the log file")] - Stat(#[source] OsError), - #[error("Could not unlink the log file")] - Unlink(#[source] OsError), -} - -fn clean_logs_older_than(current_log_path: &BStr, time: SystemTime) -> Result<(), CleanLogsError> { - let current_log_path = current_log_path.to_path_lossy(); - let parent = current_log_path.parent().ok_or(CleanLogsError::NoParent)?; - let mut dir = uapi::opendir(parent).map_os_err(CleanLogsError::OpenDir)?; - let parent = uapi::open(parent, c::O_PATH | c::O_CLOEXEC | c::O_DIRECTORY, 0) - .map_os_err(CleanLogsError::OpenDir)?; - let time = time - .duration_since(SystemTime::UNIX_EPOCH) - .unwrap_or_default() - .as_secs() as c::time_t; - while let Some(entry) = uapi::readdir(&mut dir) { - let entry = entry.map_os_err(CleanLogsError::ReadDir)?; - if let Err(err) = process_entry(parent.raw(), &entry, time) { - log::error!( - "Could not clean log file {}: {}", - entry.name().as_ustr().display(), - ErrorFmt(err), - ); - } - } - fn process_entry( - parent: c::c_int, - entry: &Dirent, - time: c::time_t, - ) -> Result<(), CleanLogsError> { - if entry.d_type != c::DT_REG { - return Ok(()); - } - let name = entry.name(); - let file = uapi::openat(parent, name, c::O_RDONLY | c::O_CLOEXEC, 0) - .map_os_err(CleanLogsError::OpenFile)?; - let stat = uapi::fstat(*file).map_os_err(CleanLogsError::Stat)?; - if stat.st_mtime >= time { - return Ok(()); - } - if uapi::flock(file.raw(), c::LOCK_EX | c::LOCK_NB).is_err() { - log::info!("Preserving file still in use: {}", name.as_ustr().display()); - return Ok(()); - } - uapi::unlinkat(parent, name, 0).map_os_err(CleanLogsError::Unlink)?; - log::info!("Deleted {}", name.as_ustr().display()); - Ok(()) - } - Ok(()) -} +pub use jay_logger::*; diff --git a/src/state.rs b/src/state.rs index 7c85feb0..6ec3475c 100644 --- a/src/state.rs +++ b/src/state.rs @@ -29,7 +29,7 @@ use { cmm_description::ColorDescription, cmm_manager::ColorManager, cmm_render_intent::RenderIntent, }, - compositor::{LIBEI_SOCKET, LogLevel}, + compositor::LIBEI_SOCKET, config::ConfigProxy, copy_device::CopyDeviceRegistry, cpu_worker::CpuWorker, @@ -101,6 +101,7 @@ use { }, io_uring::IoUring, kbvm::{KbvmContext, KbvmMap}, + logger::LogLevel, keyboard::{KeyboardStateIds, LedsListener}, leaks::Tracker, logger::Logger, diff --git a/src/tools/tool_client.rs b/src/tools/tool_client.rs index 9b1b5bba..e0b72aaf 100644 --- a/src/tools/tool_client.rs +++ b/src/tools/tool_client.rs @@ -2,9 +2,9 @@ use { crate::{ async_engine::{AsyncEngine, SpawnedFuture}, client::{EventFormatter, RequestParser}, - compositor::{LogLevel, WAYLAND_DISPLAY}, + compositor::WAYLAND_DISPLAY, io_uring::{IoUring, IoUringError}, - logger::Logger, + logger::{LogLevel, Logger}, object::{ObjectId, WL_DISPLAY_ID}, utils::{ asyncevent::AsyncEvent,