diff options
Diffstat (limited to 'rust/src/log.rs')
-rw-r--r-- | rust/src/log.rs | 219 |
1 files changed, 219 insertions, 0 deletions
diff --git a/rust/src/log.rs b/rust/src/log.rs new file mode 100644 index 0000000..7bf0be8 --- /dev/null +++ b/rust/src/log.rs @@ -0,0 +1,219 @@ +/* Copyright (C) 2017 Open Information Security Foundation + * + * You can copy, redistribute or modify this Program under the terms of + * the GNU General Public License version 2 as published by the Free + * Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * version 2 along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +//! Logging utility module. + +use std; +use std::ffi::CString; +use std::path::Path; + +use crate::core::*; + +#[derive(Debug)] +#[repr(C)] +pub enum Level { + NotSet = -1, + _None = 0, + Error, + Warning, + Notice, + Info, + _Perf, + Config, + #[cfg(feature = "debug")] + Debug, +} + +pub static mut LEVEL: i32 = Level::NotSet as i32; + +pub fn get_log_level() -> i32 { + unsafe { + LEVEL + } +} + +pub fn log_set_level(level: i32) { + unsafe { + LEVEL = level; + } +} + +#[no_mangle] +pub extern "C" fn rs_log_set_level(level: i32) { + log_set_level(level); +} + +fn basename(filename: &str) -> &str { + let path = Path::new(filename); + if let Some(os_str) = path.file_name() { + if let Some(basename) = os_str.to_str() { + return basename; + } + } + return filename; +} + +pub fn sclog(level: Level, file: &str, line: u32, function: &str, + message: &str) +{ + let filename = basename(file); + let noext = &filename[0..filename.len() - 3]; + sc_log_message(level, + filename, + line, + function, + noext, + message); +} + +// This macro returns the function name. +// +// This macro has been borrowed from https://github.com/popzxc/stdext-rs, which +// is released under the MIT license as there is currently no macro in Rust +// to provide the function name. +#[macro_export(local_inner_macros)] +macro_rules!function { + () => {{ + // Okay, this is ugly, I get it. However, this is the best we can get on a stable rust. + fn __f() {} + fn type_name_of<T>(_: T) -> &'static str { + std::any::type_name::<T>() + } + let name = type_name_of(__f); + &name[..name.len() - 5] + }} +} + +#[macro_export] +macro_rules!do_log { + ($level:expr, $($arg:tt)*) => { + if $crate::log::get_log_level() >= $level as i32 { + $crate::log::sclog($level, file!(), line!(), $crate::function!(), + &(format!($($arg)*))); + } + } +} + +#[macro_export] +macro_rules!SCLogError { + ($($arg:tt)*) => { + $crate::do_log!($crate::log::Level::Error, $($arg)*); + }; +} + +#[macro_export] +macro_rules!SCLogWarning { + ($($arg:tt)*) => { + $crate::do_log!($crate::log::Level::Warning, $($arg)*); + }; +} + +#[macro_export] +macro_rules!SCLogNotice { + ($($arg:tt)*) => { + $crate::do_log!($crate::log::Level::Notice, $($arg)*); + } +} + +#[macro_export] +macro_rules!SCLogInfo { + ($($arg:tt)*) => { + $crate::do_log!($crate::log::Level::Info, $($arg)*); + } +} + +#[macro_export] +macro_rules!SCLogPerf { + ($($arg:tt)*) => { + $crate::do_log!($crate::log::Level::Perf, $($arg)*); + } +} + +#[macro_export] +macro_rules!SCLogConfig { + ($($arg:tt)*) => { + $crate::do_log!($crate::log::Level::Config, $($arg)*); + } +} + +// Debug mode: call C SCLogDebug +#[cfg(feature = "debug")] +#[macro_export] +macro_rules!SCLogDebug { + ($($arg:tt)*) => { + do_log!($crate::log::Level::Debug, $($arg)*); + } +} + +// SCLogDebug variation to use when not compiled with debug support. +// +// This macro will only use the parameters passed to prevent warnings +// about unused variables, but is otherwise the equivalent to a no-op. +#[cfg(not(feature = "debug"))] +#[macro_export] +macro_rules!SCLogDebug { + ($($arg:tt)*) => () +} + +/// SCLogMessage wrapper. If the Suricata C context is not registered +/// a more basic log format will be used (for example, when running +/// Rust unit tests). +pub fn sc_log_message(level: Level, + filename: &str, + line: std::os::raw::c_uint, + function: &str, + module: &str, + message: &str) -> std::os::raw::c_int +{ + unsafe { + if let Some(c) = SC { + return (c.SCLogMessage)( + level as i32, + to_safe_cstring(filename).as_ptr(), + line, + to_safe_cstring(function).as_ptr(), + to_safe_cstring(module).as_ptr(), + to_safe_cstring(message).as_ptr()); + } + } + + // Fall back if the Suricata C context is not registered which is + // the case when Rust unit tests are running. + // + // We don't log the time right now as I don't think it can be done + // with Rust 1.7.0 without using an external crate. With Rust + // 1.8.0 and newer we can unix UNIX_EPOCH.elapsed() to get the + // unix time. + println!("{}:{} <{:?}> -- {}", filename, line, level, message); + return 0; +} + +// Convert a &str into a CString by first stripping NUL bytes. +fn to_safe_cstring(val: &str) -> CString { + let mut safe = Vec::with_capacity(val.len()); + for c in val.as_bytes() { + if *c != 0 { + safe.push(*c); + } + } + match CString::new(safe) { + Ok(cstr) => cstr, + _ => { + CString::new("<failed to encode string>").unwrap() + } + } +} |