• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //! 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]
45 extern crate log;
46 extern crate libc;
47 
48 use std::fs::{OpenOptions, File};
49 use std::io::{Error, ErrorKind, Write, self};
50 use std::sync::Mutex;
51 use std::env;
52 
53 use log::{Log, Metadata, Record, Level, LevelFilter, SetLoggerError};
54 
55 /// Kernel logger implementation
56 pub struct KernelLog {
57     kmsg: Mutex<File>,
58     maxlevel: LevelFilter
59 }
60 
61 impl KernelLog {
62     /// Create new kernel logger
new() -> io::Result<KernelLog>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
from_env() -> io::Result<KernelLog>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 
78     #[cfg(not(target_os = "android"))]
open_kmsg() -> io::Result<File>79     fn open_kmsg() -> io::Result<File> {
80         OpenOptions::new().write(true).open("/dev/kmsg")
81     }
82 
83     #[cfg(target_os = "android")]
open_kmsg() -> io::Result<File>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 
96     /// Create new kernel logger with error level filter
with_level(filter: LevelFilter) -> io::Result<KernelLog>97     pub fn with_level(filter: LevelFilter) -> io::Result<KernelLog> {
98         Ok(KernelLog {
99             kmsg: Mutex::new(Self::open_kmsg()?),
100             maxlevel: filter
101         })
102     }
103 }
104 
105 impl Log for KernelLog {
enabled(&self, meta: &Metadata) -> bool106     fn enabled(&self, meta: &Metadata) -> bool {
107         meta.level() <= self.maxlevel
108     }
109 
log(&self, record: &Record)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 
flush(&self)134     fn flush(&self) {}
135 }
136 
137 /// KernelLog initialization error
138 #[derive(Debug)]
139 pub enum KernelLogInitError {
140     /// IO error
141     Io(io::Error),
142     /// Set logger error
143     Log(SetLoggerError)
144 }
145 
146 impl std::fmt::Display for KernelLogInitError {
fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result147     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 
155 impl std::error::Error for KernelLogInitError {
source(&self) -> Option<&(dyn std::error::Error + 'static)>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 
164 impl From<SetLoggerError> for KernelLogInitError {
from(err: SetLoggerError) -> Self165     fn from(err: SetLoggerError) -> Self {
166         KernelLogInitError::Log(err)
167     }
168 }
169 impl From<io::Error> for KernelLogInitError {
from(err: io::Error) -> Self170     fn from(err: io::Error) -> Self {
171         KernelLogInitError::Io(err)
172     }
173 }
174 
175 /// Setup kernel logger as a default logger
init() -> Result<(), KernelLogInitError>176 pub 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)]
185 mod tests {
186     use super::{KernelLog, init};
187 
188     #[test]
log_to_kernel()189     fn log_to_kernel() {
190         init().unwrap();
191         debug!("hello, world!");
192     }
193 }
194