6 Commits

Author SHA1 Message Date
Daniel Heule
5aa718e50e Cargo version bump 2026-01-09 10:01:27 +01:00
Daniel Heule
9bd7685dc3 Bugfix for hash generation of non utf8 files 2026-01-09 09:54:52 +01:00
Daniel Heule
95b3076fe2 Informations for generating a rpm 2026-01-08 13:59:08 +01:00
Daniel Heule
c39d04d716 Informations for generating a rpm 2026-01-08 13:58:36 +01:00
Daniel Heule
7aba5c74a9 updated example config file 2026-01-06 10:11:48 +01:00
Daniel Heule
851d71ce7f some logging changes 2026-01-06 09:57:46 +01:00
6 changed files with 98 additions and 49 deletions

View File

@@ -1,6 +1,6 @@
[package] [package]
name = "rsnmpagent" name = "rsnmpagent"
version = "0.4.0" version = "0.4.2"
edition = "2024" edition = "2024"
[profile.release] [profile.release]
@@ -20,3 +20,11 @@ hex = "0.4"
sha3 = "0.10" sha3 = "0.10"
diff = "0.1" diff = "0.1"
glob = "0.3" glob = "0.3"
[package.metadata.generate-rpm]
assets = [
{ source = "target/release/rsnmpagent", dest = "/usr/sbin/rsnmpagent", mode = "750" },
]
license = "GPL-3"
description = "rsnmpagent for extending snmpd"

View File

@@ -9,3 +9,11 @@ intervals:
meminfo: 60 meminfo: 60
bonding: bonding:
multipath: multipath:
extra_config:
filesum:
passwd:
shadow:
group:
authorized_keys:
multipath:
bonding:

View File

@@ -1,6 +1,6 @@
use diff::Result; use diff::Result;
use hex; use hex;
use log::{debug, trace}; use log::{info, trace};
use sha3::{Digest, Sha3_256}; use sha3::{Digest, Sha3_256};
use std::fs; use std::fs;
use std::io::{self, Error}; use std::io::{self, Error};
@@ -8,40 +8,47 @@ use std::path::Path;
pub(crate) fn filesum_filtered( pub(crate) fn filesum_filtered(
path: &Path, path: &Path,
oldfile: &mut String, oldfile: &mut Vec<u8>,
diff_string: &mut String, diff_string: &mut String,
re: Option<&regex::Regex>, re: Option<&regex::bytes::Regex>,
) -> io::Result<(bool, String)> { ) -> io::Result<(bool, String)> {
// Open file for hashing // Open file for hashing
match re { match re {
Some(v) => debug!("try to open file {:?} for hashing using filter regex {}", path, v), Some(v) => trace!("try to open file {:?} for hashing using filter regex {}", path, v),
None => debug!("try to open file {:?} for hashing", path), None => trace!("try to open file {:?} for hashing", path),
} }
let mut hasher = Sha3_256::new(); let mut hasher = Sha3_256::new();
let mut filedata = String::with_capacity(2048); let mut filedata = Vec::with_capacity(2048);
let mut changed = false; let mut changed = false;
// we read only smal files, so its fast to read the whole file to memory, so we can also du a diff // we read only smal files, so its fast to read the whole file to memory, so we can also du a diff
if let Ok(file_contents) = fs::read_to_string(path) { if let Ok(file_contents) = fs::read(path) {
if let Some(re) = re { if let Some(re) = re {
debug!("Filter lines with regex {:?}", re); trace!("Filter lines with regex {:?}", re);
for line in file_contents.lines() { for line in file_contents.split_inclusive(|&b| b == b'\n') {
if re.is_match(line) { if re.is_match(line) {
trace!("line {} skipped by filter regex", line); trace!("line {} skipped by filter regex", String::from_utf8_lossy(line).trim());
continue; continue;
} }
// Update the hasher with the bytes // Update the hasher with the bytes
filedata.push_str(line); filedata.extend_from_slice(line);
} }
} else { } else {
// we do not have a filter regex, so we could simply paste the file to the hasher // we do not have a filter regex, so we could simply paste the file to the hasher
debug!("Hash file without filter regex"); trace!("Hash file without filter regex");
filedata = file_contents; filedata = file_contents;
} }
hasher.update(format!("{}\n", filedata).as_bytes());
// Debug to find errors
//trace!("Hasher input data {:?}",String::from_utf8_lossy(&filedata));
// Update the hasher with the bytes
hasher.update(&filedata);
if !oldfile.is_empty() && *oldfile != filedata { if !oldfile.is_empty() && *oldfile != filedata {
diff_string.clear(); diff_string.clear();
for diff in diff::lines(oldfile, &filedata) { let ofs = String::from_utf8_lossy(oldfile);
let nfs = String::from_utf8_lossy(&filedata);
for diff in diff::lines(&ofs, &nfs) {
match diff { match diff {
Result::Left(l) => { Result::Left(l) => {
trace!("Diff - {}", l); // Removed line trace!("Diff - {}", l); // Removed line
@@ -56,7 +63,7 @@ pub(crate) fn filesum_filtered(
} }
} }
} }
debug!("Diff for {:?} is now {}", path, diff_string); info!("Diff for {:?} is now {}", path, diff_string);
changed = true; changed = true;
*oldfile = filedata; *oldfile = filedata;
} else if oldfile.is_empty() { } else if oldfile.is_empty() {

View File

@@ -1,5 +1,6 @@
use log::{debug, error, info}; use log::{debug, error, info};
use regex::Regex; use regex::Regex;
use regex::bytes::Regex as BRegex;
pub(crate) fn compile_re(regex: Option<String>, name: &str) -> Option<Regex> { pub(crate) fn compile_re(regex: Option<String>, name: &str) -> Option<Regex> {
if let Some(r) = regex { if let Some(r) = regex {
@@ -19,3 +20,22 @@ pub(crate) fn compile_re(regex: Option<String>, name: &str) -> Option<Regex> {
None None
} }
} }
pub(crate) fn compile_re_bin(regex: Option<String>, name: &str) -> Option<BRegex> {
if let Some(r) = regex {
let re = BRegex::new(&r);
match re {
Ok(r) => {
debug!("Sucessfull compiled {} filter regex: {:?}", name, r);
Some(r)
}
Err(e) => {
error!("Error compiling {} filter regex: {:?}", name, e);
None
}
}
} else {
info!("No filter regex for {} supplied", name);
None
}
}

View File

@@ -19,7 +19,7 @@ pub mod snmp;
use bonding::bonding_status; use bonding::bonding_status;
use config::DataFunctionsFilesum; use config::DataFunctionsFilesum;
use filesum::filesum_filtered; use filesum::filesum_filtered;
use helper::compile_re; use helper::{compile_re, compile_re_bin};
use multipath::multipath_status; use multipath::multipath_status;
use processes::Ptypes; use processes::Ptypes;
use snmp::{Oid, OidData, SnmpData}; use snmp::{Oid, OidData, SnmpData};
@@ -59,8 +59,9 @@ fn t_multipath(
} }
let now = Utc::now().timestamp().try_into().unwrap_or(0); let now = Utc::now().timestamp().try_into().unwrap_or(0);
{ {
debug!("try to lock mutex snmp_data to update multipath {:?}", now); trace!("try to lock mutex snmp_data to update multipath {:?}", now);
let mut guard = snmp_data.lock().unwrap(); let mut guard = snmp_data.lock().unwrap();
trace!("mutex for update multipath snmp_data now locked");
let snmp_data = &mut guard.data; let snmp_data = &mut guard.data;
snmp_data.insert(oidt.clone(), SnmpData::Gauge(now)); snmp_data.insert(oidt.clone(), SnmpData::Gauge(now));
snmp_data.insert(oidc.clone(), SnmpData::Gauge(mplist.len().try_into().unwrap_or(0))); snmp_data.insert(oidc.clone(), SnmpData::Gauge(mplist.len().try_into().unwrap_or(0)));
@@ -137,8 +138,9 @@ fn t_bonding(
} }
let now = Utc::now().timestamp().try_into().unwrap_or(0); let now = Utc::now().timestamp().try_into().unwrap_or(0);
{ {
debug!("try to lock mutex snmp_data to update bonding with {:?}", now); trace!("try to lock mutex snmp_data to update bonding with {:?}", now);
let mut guard = snmp_data.lock().unwrap(); let mut guard = snmp_data.lock().unwrap();
trace!("mutex for update bonding snmp_data now locked");
let snmp_data = &mut guard.data; let snmp_data = &mut guard.data;
snmp_data.insert(oidt.clone(), SnmpData::Gauge(now)); snmp_data.insert(oidt.clone(), SnmpData::Gauge(now));
snmp_data.insert(oidc.clone(), SnmpData::Gauge(bl.len().try_into().unwrap_or(0))); snmp_data.insert(oidc.clone(), SnmpData::Gauge(bl.len().try_into().unwrap_or(0)));
@@ -187,10 +189,10 @@ fn t_filesum(
#[cfg(target_os = "linux")] #[cfg(target_os = "linux")]
{ {
// allocate some strings for holding file contents between check runs // allocate some strings for holding file contents between check runs
let mut oldpasswd = String::with_capacity(2048); let mut oldpasswd = Vec::with_capacity(2048);
let mut oldshadow = String::with_capacity(2048); let mut oldshadow = Vec::with_capacity(2048);
let mut oldgroup = String::with_capacity(2048); let mut oldgroup = Vec::with_capacity(2048);
let mut oldauthkey = String::with_capacity(2048); let mut oldauthkey = Vec::with_capacity(2048);
let mut hash_passwd = String::with_capacity(128); let mut hash_passwd = String::with_capacity(128);
let mut diff_passwd = String::with_capacity(2048); let mut diff_passwd = String::with_capacity(2048);
let mut hash_shadow = String::with_capacity(128); let mut hash_shadow = String::with_capacity(128);
@@ -201,10 +203,10 @@ fn t_filesum(
let mut diff_authkey = String::with_capacity(2048); let mut diff_authkey = String::with_capacity(2048);
// allocate Option<Regex> for our regex ... // allocate Option<Regex> for our regex ...
let re_passwd = compile_re(options.passwd, "passwd"); let re_passwd = compile_re_bin(options.passwd, "passwd");
let re_shadow = compile_re(options.shadow, "shadow"); let re_shadow = compile_re_bin(options.shadow, "shadow");
let re_group = compile_re(options.group, "group"); let re_group = compile_re_bin(options.group, "group");
let re_authkey = compile_re(options.authorized_keys, "authorized_keys"); let re_authkey = compile_re_bin(options.authorized_keys, "authorized_keys");
// prepare variables which we use in the whole function // prepare variables which we use in the whole function
let oid_filesum_time = Oid::from_str("6.1.0").unwrap(); let oid_filesum_time = Oid::from_str("6.1.0").unwrap();
@@ -231,8 +233,9 @@ fn t_filesum(
let oid_shadow_filename = Oid::from_str("6.3.1.1.2").unwrap(); let oid_shadow_filename = Oid::from_str("6.3.1.1.2").unwrap();
let oid_group_filename = Oid::from_str("6.3.1.1.3").unwrap(); let oid_group_filename = Oid::from_str("6.3.1.1.3").unwrap();
let oid_authkey_filename = Oid::from_str("6.3.1.1.4").unwrap(); let oid_authkey_filename = Oid::from_str("6.3.1.1.4").unwrap();
debug!("try to lock mutex snmp_data to update filesum header data"); trace!("try to lock mutex snmp_data to update filesum header data");
let mut guard = snmp_data.lock().unwrap(); let mut guard = snmp_data.lock().unwrap();
trace!("mutex for update filesum snmp_data now locked");
let snmp_data = &mut guard.data; let snmp_data = &mut guard.data;
snmp_data.insert(oid_filesum_cnt, SnmpData::Gauge(4)); snmp_data.insert(oid_filesum_cnt, SnmpData::Gauge(4));
snmp_data.insert(oid_passwd_filename, SnmpData::String(fn_passwd_str.clone())); snmp_data.insert(oid_passwd_filename, SnmpData::String(fn_passwd_str.clone()));
@@ -251,7 +254,7 @@ fn t_filesum(
hash_passwd = hash; hash_passwd = hash;
is_changed = true; is_changed = true;
} else { } else {
debug!("Hash of {} is still now {}", fn_passwd_str, hash); trace!("Hash of {} is still now {}", fn_passwd_str, hash);
if hash_passwd.is_empty() { if hash_passwd.is_empty() {
hash_passwd = hash; hash_passwd = hash;
} }
@@ -268,7 +271,7 @@ fn t_filesum(
hash_shadow = hash; hash_shadow = hash;
is_changed = true; is_changed = true;
} else { } else {
debug!("Hash of {} is still now {}", fn_shadow_str, hash); trace!("Hash of {} is still now {}", fn_shadow_str, hash);
if hash_shadow.is_empty() { if hash_shadow.is_empty() {
hash_shadow = hash; hash_shadow = hash;
} }
@@ -285,7 +288,7 @@ fn t_filesum(
hash_group = hash; hash_group = hash;
is_changed = true; is_changed = true;
} else { } else {
debug!("Hash of {} is still now {}", fn_group_str, hash); trace!("Hash of {} is still now {}", fn_group_str, hash);
if hash_group.is_empty() { if hash_group.is_empty() {
hash_group = hash; hash_group = hash;
} }
@@ -302,7 +305,7 @@ fn t_filesum(
hash_authkey = hash; hash_authkey = hash;
is_changed = true; is_changed = true;
} else { } else {
debug!("Hash of {} is still now {}", fn_authkey_str, hash); trace!("Hash of {} is still now {}", fn_authkey_str, hash);
if hash_authkey.is_empty() { if hash_authkey.is_empty() {
hash_authkey = hash; hash_authkey = hash;
} }
@@ -314,11 +317,12 @@ fn t_filesum(
} }
let now = Utc::now().timestamp().try_into().unwrap_or(0); let now = Utc::now().timestamp().try_into().unwrap_or(0);
{ {
debug!( trace!(
"try to lock mutex snmp_data to update filesum data, timestamp {:?}", "try to lock mutex snmp_data to update filesum data, timestamp {:?}",
now now
); );
let mut guard = snmp_data.lock().unwrap(); let mut guard = snmp_data.lock().unwrap();
trace!("mutex for update filesum snmp_data now locked");
let snmp_data = &mut guard.data; let snmp_data = &mut guard.data;
snmp_data.insert(oid_filesum_time.clone(), SnmpData::Gauge(now)); snmp_data.insert(oid_filesum_time.clone(), SnmpData::Gauge(now));
if is_changed { if is_changed {
@@ -411,11 +415,12 @@ fn t_processes(t_quit: Arc<(Mutex<bool>, Condvar)>, t_check_interval: u64, snmp_
} }
let now = Utc::now().timestamp().try_into().unwrap_or(0); let now = Utc::now().timestamp().try_into().unwrap_or(0);
{ {
debug!( trace!(
"try to lock mutex snmp_data to update processdata with {:#?} => {:?}", "try to lock mutex snmp_data to update processdata with {:#?} => {:?}",
now, proc_data now, proc_data
); );
let mut guard = snmp_data.lock().unwrap(); let mut guard = snmp_data.lock().unwrap();
trace!("mutex for update processdata snmp_data now locked");
let snmp_data = &mut guard.data; let snmp_data = &mut guard.data;
snmp_data.insert(oidt.clone(), SnmpData::Gauge(now)); snmp_data.insert(oidt.clone(), SnmpData::Gauge(now));
snmp_data.insert(oidz.clone(), SnmpData::Gauge(proc_data.zombie)); snmp_data.insert(oidz.clone(), SnmpData::Gauge(proc_data.zombie));
@@ -461,13 +466,13 @@ fn t_meminfo(t_quit: Arc<(Mutex<bool>, Condvar)>, t_check_interval: u64, snmp_da
if let Ok(meminfo_contents) = fs::read_to_string(&fpath) { if let Ok(meminfo_contents) = fs::read_to_string(&fpath) {
trace!("Read /prod/meminfo, contents: {:#?}", meminfo_contents); trace!("Read /prod/meminfo, contents: {:#?}", meminfo_contents);
if let Some(m) = re.captures(&meminfo_contents) { if let Some(m) = re.captures(&meminfo_contents) {
debug!("regex mached {:?}", m); trace!("regex mached {:?}", m);
if let Some(m) = m.get(1) { if let Some(m) = m.get(1) {
freekb += m.as_str().parse().unwrap_or(0); freekb += m.as_str().parse().unwrap_or(0);
debug!("freekb via regex parsed as {:#?}", freekb); debug!("freekb via regex parsed as {:#?}", freekb);
} }
} else if let Some(m) = re_old.captures(&meminfo_contents) { } else if let Some(m) = re_old.captures(&meminfo_contents) {
debug!("old regex mached {:#?}", m); trace!("old regex mached {:#?}", m);
if let Some(m) = m.get(1) { if let Some(m) = m.get(1) {
freekb += m.as_str().parse().unwrap_or(0); freekb += m.as_str().parse().unwrap_or(0);
} }
@@ -478,11 +483,12 @@ fn t_meminfo(t_quit: Arc<(Mutex<bool>, Condvar)>, t_check_interval: u64, snmp_da
} }
let now = Utc::now().timestamp().try_into().unwrap_or(0); let now = Utc::now().timestamp().try_into().unwrap_or(0);
{ {
debug!( trace!(
"try to lock mutex snmp_data to update meminfo with {:#?} => {:#?}", "try to lock mutex snmp_data to update meminfo with {:#?} => {:#?}",
now, freekb now, freekb
); );
let mut guard = snmp_data.lock().unwrap(); let mut guard = snmp_data.lock().unwrap();
trace!("mutex for update meminfo snmp_data now locked");
let snmp_data = &mut guard.data; let snmp_data = &mut guard.data;
snmp_data.insert(oidt.clone(), SnmpData::Gauge(now)); snmp_data.insert(oidt.clone(), SnmpData::Gauge(now));
//snmp_data.insert(oidm.clone(), SnmpData::String(freekb.to_string())); //snmp_data.insert(oidm.clone(), SnmpData::String(freekb.to_string()));
@@ -517,7 +523,7 @@ fn log_debug_watcher(
match t_marker.try_exists() { match t_marker.try_exists() {
Ok(v) => { Ok(v) => {
if v != last { if v != last {
debug!("marker file {} is now readable: {:?}", t_marker.display(), v); trace!("marker file {} is now readable: {:?}", t_marker.display(), v);
let r = match v { let r = match v {
true => log.parse_and_push_temp_spec("trace"), true => log.parse_and_push_temp_spec("trace"),
false => { false => {
@@ -526,7 +532,7 @@ fn log_debug_watcher(
} }
}; };
match r { match r {
Ok(_) => info!( Ok(_) => warn!(
"Log config changed to {}", "Log config changed to {}",
log.current_max_level() log.current_max_level()
.expect("Retrive the current log level not possible") .expect("Retrive the current log level not possible")
@@ -587,7 +593,7 @@ pub fn start_workers(
} }
Err(e) => { Err(e) => {
error!("Unable to start log_debug_watcher thread: {:#?}", e); error!("Unable to start log_debug_watcher thread: {:#?}", e);
eprintln!("Unable to start log_debug_watcher thread: {:#?}", e); //eprintln!("Unable to start log_debug_watcher thread: {:#?}", e);
} }
}; };
} else { } else {
@@ -608,7 +614,7 @@ pub fn start_workers(
} }
Err(e) => { Err(e) => {
error!("Unable to start meminfo thread: {:#?}", e); error!("Unable to start meminfo thread: {:#?}", e);
eprintln!("Unable to start meminfo thread: {:#?}", e); //eprintln!("Unable to start meminfo thread: {:#?}", e);
} }
}; };
} else { } else {
@@ -632,7 +638,7 @@ pub fn start_workers(
} }
Err(e) => { Err(e) => {
error!("Unable to start processes thread: {:#?}", e); error!("Unable to start processes thread: {:#?}", e);
eprintln!("Unable to start processes thread: {:#?}", e); //eprintln!("Unable to start processes thread: {:#?}", e);
} }
}; };
} else { } else {
@@ -654,7 +660,7 @@ pub fn start_workers(
} }
Err(e) => { Err(e) => {
error!("Unable to start filesum thread: {:#?}", e); error!("Unable to start filesum thread: {:#?}", e);
eprintln!("Unable to start filesum thread: {:#?}", e); //eprintln!("Unable to start filesum thread: {:#?}", e);
} }
}; };
} else { } else {
@@ -679,7 +685,7 @@ pub fn start_workers(
} }
Err(e) => { Err(e) => {
error!("Unable to start multipath thread: {:#?}", e); error!("Unable to start multipath thread: {:#?}", e);
eprintln!("Unable to start multipath thread: {:#?}", e); //eprintln!("Unable to start multipath thread: {:#?}", e);
} }
}; };
} else { } else {
@@ -701,7 +707,7 @@ pub fn start_workers(
} }
Err(e) => { Err(e) => {
error!("Unable to start bonding thread: {:#?}", e); error!("Unable to start bonding thread: {:#?}", e);
eprintln!("Unable to start bonding thread: {:#?}", e); //eprintln!("Unable to start bonding thread: {:#?}", e);
} }
}; };
} else { } else {

View File

@@ -49,7 +49,7 @@ impl Oid {
x x
} }
/* pub fn add_suffix(&self, o: &Oid) -> Oid { /* pub fn add_suffix(&self, o: &Oid) -> Oid {
let mut x = self.clone(); let mut x = self.clone();
x.0.extend(o.clone().0); x.0.extend(o.clone().0);
x x