blob: d87df229e89ff93729cb09af8fd26ebe8879fe65 [file] [log] [blame]
Jiyong Parkd7178e82021-06-22 16:47:17 +09001//! Logger implementation for low level kernel log (using `/dev/kmsg`)
2//!
3//! Usually intended for low level implementations, like [systemd generators][1],
4//! which have to use `/dev/kmsg`:
5//!
6//! > Since syslog is not available (see above) write log messages to /dev/kmsg instead.
7//!
8//! [1]: http://www.freedesktop.org/wiki/Software/systemd/Generators/
9//!
10//! # Examples
11//!
12//! ```toml
13//! [dependencies]
14//! log = "0.4"
15//! kernlog = "0.3"
16//! ```
17//!
18//! ```rust
19//! #[macro_use]
20//! extern crate log;
21//! extern crate kernlog;
22//!
23//! fn main() {
24//! kernlog::init().unwrap();
25//! warn!("something strange happened");
26//! }
27//! ```
28//! Note you have to have permissions to write to `/dev/kmsg`,
29//! which normal users (not root) usually don't.
30//!
31//! If compiled with nightly it can use libc feature to get process id
32//! and report it into log. This feature is unavailable for stable release
33//! for now. To enable nightly features, compile with `--features nightly`:
34//!
35//! ```toml
36//! [dependencies.kernlog]
37//! version = "*"
38//! features = ["nightly"]
39//! ```
40
41#![deny(missing_docs)]
42#![cfg_attr(feature="nightly", feature(libc))]
43
44#[macro_use]
45extern crate log;
46extern crate libc;
47
48use std::fs::{OpenOptions, File};
Jiyong Park39bcfd92021-06-26 00:31:25 +090049use std::io::{Error, ErrorKind, Write, self};
Jiyong Parkd7178e82021-06-22 16:47:17 +090050use std::sync::Mutex;
51use std::env;
52
53use log::{Log, Metadata, Record, Level, LevelFilter, SetLoggerError};
54
55/// Kernel logger implementation
56pub struct KernelLog {
57 kmsg: Mutex<File>,
58 maxlevel: LevelFilter
59}
60
61impl KernelLog {
62 /// Create new kernel logger
63 pub fn new() -> io::Result<KernelLog> {
64 KernelLog::with_level(LevelFilter::Trace)
65 }
66
67 /// Create new kernel logger from `KERNLOG_LEVEL` environment variable
68 pub fn from_env() -> io::Result<KernelLog> {
69 match env::var("KERNLOG_LEVEL") {
70 Err(_) => KernelLog::new(),
71 Ok(s) => match s.parse() {
72 Ok(filter) => KernelLog::with_level(filter),
73 Err(_) => KernelLog::new(),
74 }
75 }
76 }
77
Jiyong Park39bcfd92021-06-26 00:31:25 +090078 #[cfg(not(target_os = "android"))]
79 fn open_kmsg() -> io::Result<File> {
80 OpenOptions::new().write(true).open("/dev/kmsg")
81 }
82
83 #[cfg(target_os = "android")]
84 fn open_kmsg() -> io::Result<File> {
85 // In Android, a process normally doesn't have the permission to open /dev/kmsg. Instead it
86 // is opened by init (via `file /dev/kmsg w` in the rc file) and the file descriptor is
87 // shared when executing the process. The file descriptor number is passed via an
88 // environment variable "ANDROID_FILE_<file_name>" where <file_name> is the path to the
89 // file where non alpha-numeric characters are replaced with '_'.
90 match env::var("ANDROID_FILE__dev_kmsg") {
91 Ok(val) => OpenOptions::new().write(true).open(format!("/proc/self/fd/{}", val)),
92 Err(e) => Err(Error::new(ErrorKind::Other, "ANDROID_FILE__dev_kmsg doesn't exist")),
93 }
94 }
95
Jiyong Parkd7178e82021-06-22 16:47:17 +090096 /// Create new kernel logger with error level filter
97 pub fn with_level(filter: LevelFilter) -> io::Result<KernelLog> {
98 Ok(KernelLog {
Jiyong Park39bcfd92021-06-26 00:31:25 +090099 kmsg: Mutex::new(Self::open_kmsg()?),
Jiyong Parkd7178e82021-06-22 16:47:17 +0900100 maxlevel: filter
101 })
102 }
103}
104
105impl Log for KernelLog {
106 fn enabled(&self, meta: &Metadata) -> bool {
107 meta.level() <= self.maxlevel
108 }
109
110 fn log(&self, record: &Record) {
111 if record.level() > self.maxlevel {
112 return;
113 }
114
115 let level: u8 = match record.level() {
116 Level::Error => 3,
117 Level::Warn => 4,
118 Level::Info => 5,
119 Level::Debug => 6,
120 Level::Trace => 7,
121 };
122
123 let mut buf = Vec::new();
124 writeln!(buf, "<{}>{}[{}]: {}", level, record.target(),
125 unsafe { ::libc::getpid() },
126 record.args()).unwrap();
127
128 if let Ok(mut kmsg) = self.kmsg.lock() {
129 let _ = kmsg.write(&buf);
130 let _ = kmsg.flush();
131 }
132 }
133
134 fn flush(&self) {}
135}
136
137/// KernelLog initialization error
138#[derive(Debug)]
139pub enum KernelLogInitError {
140 /// IO error
141 Io(io::Error),
142 /// Set logger error
143 Log(SetLoggerError)
144}
145
146impl std::fmt::Display for KernelLogInitError {
147 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
148 match self {
149 KernelLogInitError::Io(err) => err.fmt(f),
150 KernelLogInitError::Log(err) => err.fmt(f),
151 }
152 }
153}
154
155impl std::error::Error for KernelLogInitError {
156 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
157 match self {
158 KernelLogInitError::Io(err) => Some(err),
159 KernelLogInitError::Log(err) => Some(err),
160 }
161 }
162}
163
164impl From<SetLoggerError> for KernelLogInitError {
165 fn from(err: SetLoggerError) -> Self {
166 KernelLogInitError::Log(err)
167 }
168}
169impl From<io::Error> for KernelLogInitError {
170 fn from(err: io::Error) -> Self {
171 KernelLogInitError::Io(err)
172 }
173}
174
175/// Setup kernel logger as a default logger
176pub fn init() -> Result<(), KernelLogInitError> {
177 let klog = KernelLog::from_env()?;
178 let maxlevel = klog.maxlevel;
179 log::set_boxed_logger(Box::new(klog))?;
180 log::set_max_level(maxlevel);
181 Ok(())
182}
183
184#[cfg(test)]
185mod tests {
186 use super::{KernelLog, init};
187
188 #[test]
189 fn log_to_kernel() {
190 init().unwrap();
191 debug!("hello, world!");
192 }
193}