1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
//! Logger implementation for low level kernel log (using `/dev/kmsg`)
//!
//! Usually intended for low level implementations, like [systemd generators][1],
//! which have to use `/dev/kmsg`:
//!
//! > Since syslog is not available (see above) write log messages to /dev/kmsg instead.
//!
//! [1]: http://www.freedesktop.org/wiki/Software/systemd/Generators/
//!
//! # Examples
//!
//! ```toml
//! [dependencies]
//! log = "*"
//! kernlog = "*"
//! ```
//! 
//! ```rust
//! #[macro_use]
//! extern crate log;
//! extern crate kernlog;
//! 
//! fn main() {
//!     kernlog::init().unwrap();
//!     warn!("something strange happened");
//! }
//! ```
//! Note you have to have permissions to write to `/dev/kmsg`,
//! which normal users (not root) usually don't.
//! 
//! If compiled with nightly it can use libc feature to get process id
//! and report it into log. This feature is unavailable for stable release
//! for now. To enable nightly features, compile with `--features nightly`:
//!
//! ```toml
//! [dependencies.kernlog]
//! version = "*"
//! features = ["nightly"]
//! ```

#![deny(missing_docs)]
#![cfg_attr(feature="nightly", feature(libc))]

#[macro_use]
extern crate log;
#[cfg(feature="nightly")]
extern crate libc;

use std::fs::{OpenOptions, File};
use std::io::Write;
use std::sync::Mutex;
use std::env;

use log::{Log, LogMetadata, LogRecord, LogLevel, MaxLogLevelFilter, LogLevelFilter, SetLoggerError};

/// Kernel logger implementation
pub struct KernelLog {
    kmsg: Mutex<File>,
    maxlevel: LogLevelFilter
}

impl KernelLog {
    /// Create new kernel logger
    pub fn new() -> KernelLog {
        KernelLog::with_level(LogLevelFilter::Trace)
    }

    /// Create new kernel logger with error level filter
    pub fn with_level(filter: LogLevelFilter) -> KernelLog {
        KernelLog {
            kmsg: Mutex::new(OpenOptions::new().write(true).open("/dev/kmsg").unwrap()),
            maxlevel: filter
        }
    }

    /// Setup new kernel logger for log framework
    pub fn init(filter: MaxLogLevelFilter) -> Box<Log> {
        let logger = KernelLog::new();
        filter.set(logger.maxlevel);
        Box::new(logger)
    }

    /// Setup new kernel logger with error level from `KERNLOG_LEVEL` environment variable
    pub fn init_env(filter: MaxLogLevelFilter) -> Box<Log> {
        match env::var("KERNLOG_LEVEL") {
            Err(_) => KernelLog::init(filter),
            Ok(s) => match s.parse() {
                Ok(level) => KernelLog::init_level(level, filter),
                Err(_) => KernelLog::init(filter)
            }
        }
    }

    /// Setup new kernel logger with error level filter
    pub fn init_level(level: LogLevelFilter, filter: MaxLogLevelFilter) -> Box<Log> {
        filter.set(level);
        Box::new(KernelLog::with_level(level))
    }
}

impl Log for KernelLog {
    fn enabled(&self, meta: &LogMetadata) -> bool {
        meta.level() <= self.maxlevel
    }

    #[cfg(feature="nightly")]
    fn log(&self, record: &LogRecord) {
        if record.level() > self.maxlevel {
            return;
        }

        let level: u8 = match record.level() {
            LogLevel::Error => 3,
            LogLevel::Warn => 4,
            LogLevel::Info => 5,
            LogLevel::Debug => 6,
            LogLevel::Trace => 7,
        };

        let mut buf = Vec::new();
        writeln!(buf, "<{}>{}[{}]: {}", level, record.target(),
                 unsafe { ::libc::funcs::posix88::unistd::getpid() },
                 record.args()).unwrap();

        if let Ok(mut kmsg) = self.kmsg.lock() {
            let _ = kmsg.write(&buf);
            let _ = kmsg.flush();
        }
    }

    #[cfg(not(feature="nightly"))]
    fn log(&self, record: &LogRecord) {
        let level: u8 = match record.level() {
            LogLevel::Error => 3,
            LogLevel::Warn => 4,
            LogLevel::Info => 5,
            LogLevel::Debug => 6,
            LogLevel::Trace => 7,
        };

        let mut buf = Vec::new();
        writeln!(buf, "<{}>{}: {}", level, record.target(), record.args()).unwrap();

        if let Ok(mut kmsg) = self.kmsg.lock() {
            let _ = kmsg.write(&buf);
            let _ = kmsg.flush();
        }
    }
}

/// Setup kernel logger as a default logger
pub fn init() -> Result<(), SetLoggerError> {
    log::set_logger(KernelLog::init)
}

#[cfg(test)]
mod tests {
    use super::{KernelLog, init};

    #[test]
    fn log_to_kernel() {
        init().unwrap();
        debug!("hello, world!");
    }
}