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