|
|
|
|
@ -1,4 +1,4 @@
|
|
|
|
|
/* Copyright (C) 2017 Open Information Security Foundation
|
|
|
|
|
/* Copyright (C) 2017-2025 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
|
|
|
|
|
@ -15,13 +15,16 @@
|
|
|
|
|
* 02110-1301, USA.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
//! Logging utility module.
|
|
|
|
|
//! Logging and debug utilities, like util-debug.c.
|
|
|
|
|
|
|
|
|
|
use std;
|
|
|
|
|
use std::ffi::CString;
|
|
|
|
|
use std::path::Path;
|
|
|
|
|
use std::{ffi::CString, path::Path};
|
|
|
|
|
|
|
|
|
|
use crate::core::*;
|
|
|
|
|
use crate::core::SC;
|
|
|
|
|
|
|
|
|
|
/// cbindgen:ignore
|
|
|
|
|
extern {
|
|
|
|
|
pub fn SCLogGetLogLevel() -> i32;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Debug)]
|
|
|
|
|
#[repr(C)]
|
|
|
|
|
@ -40,21 +43,13 @@ pub enum Level {
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Set the Rust context's idea of the log level.
|
|
|
|
|
///
|
|
|
|
|
/// This will be called during Suricata initialization with the
|
|
|
|
|
/// runtime log level.
|
|
|
|
|
#[no_mangle]
|
|
|
|
|
pub extern "C" fn rs_log_set_level(level: i32) {
|
|
|
|
|
log_set_level(level);
|
|
|
|
|
pub unsafe extern "C" fn SCSetRustLogLevel(level: i32) {
|
|
|
|
|
LEVEL = level;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn basename(filename: &str) -> &str {
|
|
|
|
|
@ -67,17 +62,55 @@ fn basename(filename: &str) -> &str {
|
|
|
|
|
return filename;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn sclog(level: Level, file: &str, line: u32, function: &str,
|
|
|
|
|
message: &str)
|
|
|
|
|
{
|
|
|
|
|
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);
|
|
|
|
|
sc_log_message(level, filename, line, function, noext, message);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// 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(),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// This macro returns the function name.
|
|
|
|
|
@ -86,23 +119,24 @@ pub fn sclog(level: Level, file: &str, line: u32, function: &str,
|
|
|
|
|
// 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 {
|
|
|
|
|
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]
|
|
|
|
|
}}
|
|
|
|
|
// 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!(),
|
|
|
|
|
#[allow(unused_unsafe)]
|
|
|
|
|
if unsafe { $crate::debug::LEVEL } >= $level as i32 {
|
|
|
|
|
$crate::debug::sclog($level, file!(), line!(), $crate::function!(),
|
|
|
|
|
&(format!($($arg)*)));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
@ -111,42 +145,42 @@ macro_rules!do_log {
|
|
|
|
|
#[macro_export]
|
|
|
|
|
macro_rules!SCLogError {
|
|
|
|
|
($($arg:tt)*) => {
|
|
|
|
|
$crate::do_log!($crate::log::Level::Error, $($arg)*);
|
|
|
|
|
$crate::do_log!($crate::debug::Level::Error, $($arg)*);
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[macro_export]
|
|
|
|
|
macro_rules!SCLogWarning {
|
|
|
|
|
($($arg:tt)*) => {
|
|
|
|
|
$crate::do_log!($crate::log::Level::Warning, $($arg)*);
|
|
|
|
|
$crate::do_log!($crate::debug::Level::Warning, $($arg)*);
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[macro_export]
|
|
|
|
|
macro_rules!SCLogNotice {
|
|
|
|
|
($($arg:tt)*) => {
|
|
|
|
|
$crate::do_log!($crate::log::Level::Notice, $($arg)*);
|
|
|
|
|
$crate::do_log!($crate::debug::Level::Notice, $($arg)*);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[macro_export]
|
|
|
|
|
macro_rules!SCLogInfo {
|
|
|
|
|
($($arg:tt)*) => {
|
|
|
|
|
$crate::do_log!($crate::log::Level::Info, $($arg)*);
|
|
|
|
|
$crate::do_log!($crate::debug::Level::Info, $($arg)*);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[macro_export]
|
|
|
|
|
macro_rules!SCLogPerf {
|
|
|
|
|
($($arg:tt)*) => {
|
|
|
|
|
$crate::do_log!($crate::log::Level::Perf, $($arg)*);
|
|
|
|
|
$crate::do_log!($crate::debug::Level::Perf, $($arg)*);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[macro_export]
|
|
|
|
|
macro_rules!SCLogConfig {
|
|
|
|
|
($($arg:tt)*) => {
|
|
|
|
|
$crate::do_log!($crate::log::Level::Config, $($arg)*);
|
|
|
|
|
$crate::do_log!($crate::debug::Level::Config, $($arg)*);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@ -155,7 +189,7 @@ macro_rules!SCLogConfig {
|
|
|
|
|
#[macro_export]
|
|
|
|
|
macro_rules!SCLogDebug {
|
|
|
|
|
($($arg:tt)*) => {
|
|
|
|
|
do_log!($crate::log::Level::Debug, $($arg)*);
|
|
|
|
|
do_log!($crate::debug::Level::Debug, $($arg)*);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@ -165,55 +199,40 @@ macro_rules!SCLogDebug {
|
|
|
|
|
// about unused variables, but is otherwise the equivalent to a no-op.
|
|
|
|
|
#[cfg(not(feature = "debug"))]
|
|
|
|
|
#[macro_export]
|
|
|
|
|
macro_rules!SCLogDebug {
|
|
|
|
|
($($arg:tt)*) => ()
|
|
|
|
|
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());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
#[cfg(not(feature = "debug-validate"))]
|
|
|
|
|
#[macro_export]
|
|
|
|
|
macro_rules! debug_validate_bug_on (
|
|
|
|
|
($item:expr) => {};
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
// 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);
|
|
|
|
|
}
|
|
|
|
|
#[cfg(feature = "debug-validate")]
|
|
|
|
|
#[macro_export]
|
|
|
|
|
macro_rules! debug_validate_bug_on (
|
|
|
|
|
($item:expr) => {
|
|
|
|
|
if $item {
|
|
|
|
|
panic!("Condition check failed");
|
|
|
|
|
}
|
|
|
|
|
match CString::new(safe) {
|
|
|
|
|
Ok(cstr) => cstr,
|
|
|
|
|
_ => {
|
|
|
|
|
CString::new("<failed to encode string>").unwrap()
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
#[cfg(not(feature = "debug-validate"))]
|
|
|
|
|
#[macro_export]
|
|
|
|
|
macro_rules! debug_validate_fail (
|
|
|
|
|
($msg:expr) => {};
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
#[cfg(feature = "debug-validate")]
|
|
|
|
|
#[macro_export]
|
|
|
|
|
macro_rules! debug_validate_fail (
|
|
|
|
|
($msg:expr) => {
|
|
|
|
|
// Wrap in a conditional to prevent unreachable code warning in caller.
|
|
|
|
|
if true {
|
|
|
|
|
panic!($msg);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
);
|