treewide: set up rustfmt and taplo with custom rules
Signed-off-by: NotAShelf <raf@notashelf.dev> Change-Id: I794f9152bb02e3dd91c9738369b94fc66a6a6964
This commit is contained in:
parent
a4a0b9135a
commit
ffae695240
27 changed files with 1851 additions and 1618 deletions
28
.rustfmt.toml
Normal file
28
.rustfmt.toml
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
# float_literal_trailing_zero = "Always" # TODO: Warning for some reason?
|
||||||
|
condense_wildcard_suffixes = true
|
||||||
|
doc_comment_code_block_width = 80
|
||||||
|
edition = "2024" # Keep in sync with Cargo.toml.
|
||||||
|
enum_discrim_align_threshold = 60
|
||||||
|
force_explicit_abi = false
|
||||||
|
force_multiline_blocks = true
|
||||||
|
format_code_in_doc_comments = true
|
||||||
|
format_macro_matchers = true
|
||||||
|
format_strings = true
|
||||||
|
group_imports = "StdExternalCrate"
|
||||||
|
hex_literal_case = "Upper"
|
||||||
|
imports_granularity = "Crate"
|
||||||
|
imports_layout = "Vertical"
|
||||||
|
inline_attribute_width = 60
|
||||||
|
match_block_trailing_comma = true
|
||||||
|
max_width = 80
|
||||||
|
newline_style = "Unix"
|
||||||
|
normalize_comments = true
|
||||||
|
normalize_doc_attributes = true
|
||||||
|
overflow_delimited_expr = true
|
||||||
|
struct_field_align_threshold = 60
|
||||||
|
tab_spaces = 2
|
||||||
|
unstable_features = true
|
||||||
|
use_field_init_shorthand = true
|
||||||
|
use_try_shorthand = true
|
||||||
|
wrap_comments = true
|
||||||
|
|
||||||
14
.taplo.toml
Normal file
14
.taplo.toml
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
[formatting]
|
||||||
|
align_entries = true
|
||||||
|
column_width = 100
|
||||||
|
compact_arrays = false
|
||||||
|
reorder_inline_tables = true
|
||||||
|
reorder_keys = true
|
||||||
|
|
||||||
|
[[rule]]
|
||||||
|
include = [ "**/Cargo.toml" ]
|
||||||
|
keys = [ "package" ]
|
||||||
|
|
||||||
|
[rule.formatting]
|
||||||
|
reorder_keys = false
|
||||||
|
|
||||||
40
Cargo.toml
40
Cargo.toml
|
|
@ -1,5 +1,4 @@
|
||||||
[workspace]
|
[workspace]
|
||||||
resolver = "3"
|
|
||||||
members = [
|
members = [
|
||||||
"crates/*",
|
"crates/*",
|
||||||
"scanners/scanner-system",
|
"scanners/scanner-system",
|
||||||
|
|
@ -7,34 +6,35 @@ members = [
|
||||||
"scanners/scanner-power",
|
"scanners/scanner-power",
|
||||||
"scanners/scanner-proc",
|
"scanners/scanner-proc",
|
||||||
]
|
]
|
||||||
|
resolver = "3"
|
||||||
|
|
||||||
[workspace.package]
|
[workspace.package]
|
||||||
version = "0.1.0"
|
authors = [ "NotAShelf <raf@notashelf.dev>" ]
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "MPL-2.0"
|
license = "MPL-2.0"
|
||||||
authors = ["NotAShelf <raf@notashelf.dev>"]
|
version = "0.1.0"
|
||||||
|
|
||||||
[workspace.dependencies]
|
[workspace.dependencies]
|
||||||
pscand-core = { path = "./crates/pscand-core" }
|
pscand-core = { path = "./crates/pscand-core" }
|
||||||
pscand-macros = { path = "./crates/pscand-macros" }
|
pscand-macros = { path = "./crates/pscand-macros" }
|
||||||
|
|
||||||
tokio = { version = "1.49.0", features = ["full"] }
|
chrono = { features = [ "serde" ], version = "0.4.43" }
|
||||||
serde = { version = "1.0.228", features = ["derive"] }
|
clap = { features = [ "derive" ], version = "4.5.59" }
|
||||||
serde_json = "1.0.149"
|
dirs = "6.0.0"
|
||||||
toml = "1.0.0"
|
env_logger = "0.11.9"
|
||||||
libloading = "0.9.0"
|
libloading = "0.9.0"
|
||||||
chrono = { version = "0.4.43", features = ["serde"] }
|
log = "0.4.29"
|
||||||
sysinfo = "0.38.2"
|
|
||||||
log = "0.4.29"
|
|
||||||
env_logger = "0.11.9"
|
|
||||||
thiserror = "2.0.18"
|
|
||||||
parking_lot = "0.12.5"
|
parking_lot = "0.12.5"
|
||||||
ringbuf = "0.4.8"
|
ringbuf = "0.4.8"
|
||||||
dirs = "6.0.0"
|
serde = { features = [ "derive" ], version = "1.0.228" }
|
||||||
clap = { version = "4.5.59", features = ["derive"] }
|
serde_json = "1.0.149"
|
||||||
sha2 = "0.10.9"
|
sha2 = "0.10.9"
|
||||||
|
sysinfo = "0.38.2"
|
||||||
|
thiserror = "2.0.18"
|
||||||
|
tokio = { features = [ "full" ], version = "1.49.0" }
|
||||||
|
toml = "1.0.0"
|
||||||
|
|
||||||
[profile.release]
|
[profile.release]
|
||||||
lto = true
|
|
||||||
opt-level = "z"
|
|
||||||
codegen-units = 1
|
codegen-units = 1
|
||||||
|
lto = true
|
||||||
|
opt-level = "z"
|
||||||
|
|
|
||||||
|
|
@ -4,10 +4,10 @@
|
||||||
# Directories to load scanner plugins from
|
# Directories to load scanner plugins from
|
||||||
# Set via PSCAND_SCANNER_DIRS environment variable or configure here
|
# Set via PSCAND_SCANNER_DIRS environment variable or configure here
|
||||||
scanner_dirs = [
|
scanner_dirs = [
|
||||||
# Examples (uncomment and adjust for your system):
|
# Examples (uncomment and adjust for your system):
|
||||||
# "/usr/lib/pscand/scanners",
|
# "/usr/lib/pscand/scanners",
|
||||||
# "/var/lib/pscand/scanners",
|
# "/var/lib/pscand/scanners",
|
||||||
"~/.local/share/pscand/scanners",
|
"~/.local/share/pscand/scanners",
|
||||||
]
|
]
|
||||||
|
|
||||||
# Where to store log files
|
# Where to store log files
|
||||||
|
|
@ -27,19 +27,19 @@ retention_days = 7
|
||||||
|
|
||||||
# Per-scanner configuration
|
# Per-scanner configuration
|
||||||
[scanners.system]
|
[scanners.system]
|
||||||
enabled = true
|
enabled = true
|
||||||
interval_secs = 5 # Override default 1-second interval
|
interval_secs = 5 # Override default 1-second interval
|
||||||
|
|
||||||
[scanners.sensor]
|
[scanners.sensor]
|
||||||
enabled = true
|
enabled = true
|
||||||
interval_secs = 10 # Sensors don't change as fast
|
interval_secs = 10 # Sensors don't change as fast
|
||||||
|
|
||||||
[scanners.power]
|
[scanners.power]
|
||||||
enabled = true
|
enabled = true
|
||||||
interval_secs = 30 # Battery status changes slowly
|
interval_secs = 30 # Battery status changes slowly
|
||||||
|
|
||||||
[scanners.proc]
|
[scanners.proc]
|
||||||
enabled = true
|
enabled = true
|
||||||
interval_secs = 5
|
interval_secs = 5
|
||||||
|
|
||||||
# Example: Custom scanner with extra parameters
|
# Example: Custom scanner with extra parameters
|
||||||
|
|
|
||||||
|
|
@ -1,27 +1,25 @@
|
||||||
log_dir = "/var/log/pscand"
|
file_enabled = true
|
||||||
retention_days = 7
|
journal_enabled = true
|
||||||
|
log_dir = "/var/log/pscand"
|
||||||
|
retention_days = 7
|
||||||
ring_buffer_size = 60
|
ring_buffer_size = 60
|
||||||
journal_enabled = true
|
|
||||||
file_enabled = true
|
|
||||||
|
|
||||||
[scanner_dirs]
|
[scanner_dirs]
|
||||||
# Directories to load scanner plugins from
|
# Directories to load scanner plugins from
|
||||||
dirs = [
|
dirs = [ "~/.local/share/pscand/scanners" ]
|
||||||
"~/.local/share/pscand/scanners",
|
|
||||||
]
|
|
||||||
|
|
||||||
[scanners.system]
|
[scanners.system]
|
||||||
enabled = true
|
enabled = true
|
||||||
interval_secs = 1
|
interval_secs = 1
|
||||||
|
|
||||||
[scanners.sensor]
|
[scanners.sensor]
|
||||||
enabled = true
|
enabled = true
|
||||||
interval_secs = 2
|
interval_secs = 2
|
||||||
|
|
||||||
[scanners.power]
|
[scanners.power]
|
||||||
enabled = true
|
enabled = true
|
||||||
interval_secs = 2
|
interval_secs = 2
|
||||||
|
|
||||||
[scanners.proc]
|
[scanners.proc]
|
||||||
enabled = true
|
enabled = true
|
||||||
interval_secs = 5
|
interval_secs = 5
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
[package]
|
[package]
|
||||||
name = "pscand-cli"
|
name = "pscand-cli"
|
||||||
version.workspace = true
|
version.workspace = true
|
||||||
edition.workspace = true
|
edition.workspace = true
|
||||||
license.workspace = true
|
license.workspace = true
|
||||||
|
|
@ -10,20 +10,20 @@ name = "pscand"
|
||||||
path = "src/main.rs"
|
path = "src/main.rs"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
pscand-core.workspace = true
|
chrono.workspace = true
|
||||||
|
clap.workspace = true
|
||||||
|
dirs.workspace = true
|
||||||
|
env_logger.workspace = true
|
||||||
|
libloading.workspace = true
|
||||||
|
log.workspace = true
|
||||||
|
parking_lot.workspace = true
|
||||||
|
pscand-core.workspace = true
|
||||||
pscand-macros.workspace = true
|
pscand-macros.workspace = true
|
||||||
tokio.workspace = true
|
ringbuf.workspace = true
|
||||||
serde.workspace = true
|
serde.workspace = true
|
||||||
serde_json.workspace = true
|
serde_json.workspace = true
|
||||||
toml.workspace = true
|
sha2.workspace = true
|
||||||
libloading.workspace = true
|
sysinfo.workspace = true
|
||||||
chrono.workspace = true
|
thiserror.workspace = true
|
||||||
log.workspace = true
|
tokio.workspace = true
|
||||||
env_logger.workspace = true
|
toml.workspace = true
|
||||||
thiserror.workspace = true
|
|
||||||
parking_lot.workspace = true
|
|
||||||
ringbuf.workspace = true
|
|
||||||
dirs.workspace = true
|
|
||||||
sysinfo.workspace = true
|
|
||||||
clap.workspace = true
|
|
||||||
sha2.workspace = true
|
|
||||||
|
|
|
||||||
|
|
@ -1,19 +1,45 @@
|
||||||
#![allow(improper_ctypes_definitions)]
|
#![allow(improper_ctypes_definitions)]
|
||||||
|
|
||||||
|
use std::{
|
||||||
|
fs,
|
||||||
|
io::Read,
|
||||||
|
path::{
|
||||||
|
Path,
|
||||||
|
PathBuf,
|
||||||
|
},
|
||||||
|
sync::{
|
||||||
|
atomic::{
|
||||||
|
AtomicBool,
|
||||||
|
Ordering,
|
||||||
|
},
|
||||||
|
Arc,
|
||||||
|
},
|
||||||
|
time::{
|
||||||
|
Duration,
|
||||||
|
Instant,
|
||||||
|
SystemTime,
|
||||||
|
UNIX_EPOCH,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use libloading::Library;
|
use libloading::Library;
|
||||||
use pscand_core::Config as CoreConfig;
|
use pscand_core::{
|
||||||
use pscand_core::logging::{LogLevel, RingBufferLogger};
|
logging::{
|
||||||
use pscand_core::scanner::Scanner;
|
LogLevel,
|
||||||
use sha2::{Digest, Sha256};
|
RingBufferLogger,
|
||||||
use std::fs;
|
},
|
||||||
use std::io::Read;
|
scanner::Scanner,
|
||||||
use std::path::{Path, PathBuf};
|
Config as CoreConfig,
|
||||||
use std::sync::Arc;
|
};
|
||||||
use std::sync::atomic::{AtomicBool, Ordering};
|
use sha2::{
|
||||||
use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH};
|
Digest,
|
||||||
use tokio::sync::RwLock;
|
Sha256,
|
||||||
use tokio::time::interval;
|
};
|
||||||
|
use tokio::{
|
||||||
|
sync::RwLock,
|
||||||
|
time::interval,
|
||||||
|
};
|
||||||
|
|
||||||
type ScannerCreator = pscand_core::ScannerCreatorFfi;
|
type ScannerCreator = pscand_core::ScannerCreatorFfi;
|
||||||
|
|
||||||
|
|
@ -79,22 +105,22 @@ fn verify_library(path: &Path) -> Result<(), String> {
|
||||||
}
|
}
|
||||||
|
|
||||||
struct LoadedScanner {
|
struct LoadedScanner {
|
||||||
name: String,
|
name: String,
|
||||||
scanner: Arc<RwLock<Box<dyn Scanner>>>,
|
scanner: Arc<RwLock<Box<dyn Scanner>>>,
|
||||||
interval: Duration,
|
interval: Duration,
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
library: Library,
|
library: Library,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
struct DaemonState {
|
struct DaemonState {
|
||||||
running: Arc<AtomicBool>,
|
running: Arc<AtomicBool>,
|
||||||
shutdown_requested: Arc<AtomicBool>,
|
shutdown_requested: Arc<AtomicBool>,
|
||||||
start_time: Arc<RwLock<SystemTime>>,
|
start_time: Arc<RwLock<SystemTime>>,
|
||||||
last_collection: Arc<RwLock<SystemTime>>,
|
last_collection: Arc<RwLock<SystemTime>>,
|
||||||
collection_count: Arc<RwLock<u64>>,
|
collection_count: Arc<RwLock<u64>>,
|
||||||
error_count: Arc<RwLock<u64>>,
|
error_count: Arc<RwLock<u64>>,
|
||||||
heartbeat_path: PathBuf,
|
heartbeat_path: PathBuf,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DaemonState {
|
impl DaemonState {
|
||||||
|
|
@ -334,7 +360,8 @@ async fn run_daemon(args: RunArgs) -> Result<(), Box<dyn std::error::Error>> {
|
||||||
" 1. Scanner plugins are installed in one of the configured directories"
|
" 1. Scanner plugins are installed in one of the configured directories"
|
||||||
);
|
);
|
||||||
log::error!(
|
log::error!(
|
||||||
" 2. Scanner directories are correctly set in config file or PSCAND_SCANNER_DIRS env var"
|
" 2. Scanner directories are correctly set in config file or \
|
||||||
|
PSCAND_SCANNER_DIRS env var"
|
||||||
);
|
);
|
||||||
log::error!(" 3. Scanners are not disabled in the configuration");
|
log::error!(" 3. Scanners are not disabled in the configuration");
|
||||||
logger.log(
|
logger.log(
|
||||||
|
|
@ -664,7 +691,8 @@ async fn list_scanners() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
"\nDynamic scanners are loaded from $PSCAND_SCANNER_DIRS (colon-separated)"
|
"\nDynamic scanners are loaded from $PSCAND_SCANNER_DIRS (colon-separated)"
|
||||||
);
|
);
|
||||||
println!(
|
println!(
|
||||||
" Default fallback: ~/.local/share/pscand/scanners/ or ~/.config/pscand/scanners/"
|
" Default fallback: ~/.local/share/pscand/scanners/ or \
|
||||||
|
~/.config/pscand/scanners/"
|
||||||
);
|
);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,22 +1,22 @@
|
||||||
[package]
|
[package]
|
||||||
name = "pscand-core"
|
name = "pscand-core"
|
||||||
version.workspace = true
|
version.workspace = true
|
||||||
edition.workspace = true
|
edition.workspace = true
|
||||||
license.workspace = true
|
license.workspace = true
|
||||||
authors.workspace = true
|
authors.workspace = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
tokio.workspace = true
|
chrono.workspace = true
|
||||||
serde.workspace = true
|
dirs.workspace = true
|
||||||
serde_json.workspace = true
|
log.workspace = true
|
||||||
toml.workspace = true
|
|
||||||
chrono.workspace = true
|
|
||||||
log.workspace = true
|
|
||||||
thiserror.workspace = true
|
|
||||||
parking_lot.workspace = true
|
parking_lot.workspace = true
|
||||||
dirs.workspace = true
|
ringbuf.workspace = true
|
||||||
sysinfo.workspace = true
|
serde.workspace = true
|
||||||
ringbuf.workspace = true
|
serde_json.workspace = true
|
||||||
|
sysinfo.workspace = true
|
||||||
|
thiserror.workspace = true
|
||||||
|
tokio.workspace = true
|
||||||
|
toml.workspace = true
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
name = "pscand_core"
|
name = "pscand_core"
|
||||||
|
|
|
||||||
|
|
@ -1,126 +1,135 @@
|
||||||
use serde::{Deserialize, Serialize};
|
use std::{
|
||||||
use std::collections::HashMap;
|
collections::HashMap,
|
||||||
use std::path::{Path, PathBuf};
|
path::{
|
||||||
|
Path,
|
||||||
|
PathBuf,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
use serde::{
|
||||||
|
Deserialize,
|
||||||
|
Serialize,
|
||||||
|
};
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
#[derive(Error, Debug)]
|
#[derive(Error, Debug)]
|
||||||
pub enum ConfigError {
|
pub enum ConfigError {
|
||||||
#[error("IO error: {0}")]
|
#[error("IO error: {0}")]
|
||||||
Io(#[from] std::io::Error),
|
Io(#[from] std::io::Error),
|
||||||
#[error("Parse error: {0}")]
|
#[error("Parse error: {0}")]
|
||||||
Parse(#[from] toml::de::Error),
|
Parse(#[from] toml::de::Error),
|
||||||
#[error("Scanner {0} not configured")]
|
#[error("Scanner {0} not configured")]
|
||||||
ScannerNotConfigured(String),
|
ScannerNotConfigured(String),
|
||||||
#[error("Invalid scanner name: {0}")]
|
#[error("Invalid scanner name: {0}")]
|
||||||
InvalidScannerName(String),
|
InvalidScannerName(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type ConfigResult<T> = std::result::Result<T, ConfigError>;
|
pub type ConfigResult<T> = std::result::Result<T, ConfigError>;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
pub struct ScannerConfig {
|
pub struct ScannerConfig {
|
||||||
pub enabled: bool,
|
pub enabled: bool,
|
||||||
pub interval_secs: Option<u64>,
|
pub interval_secs: Option<u64>,
|
||||||
pub extra: HashMap<String, toml::Value>,
|
pub extra: HashMap<String, toml::Value>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
#[serde(default = "default_scanner_dirs")]
|
#[serde(default = "default_scanner_dirs")]
|
||||||
pub scanner_dirs: Vec<PathBuf>,
|
pub scanner_dirs: Vec<PathBuf>,
|
||||||
#[serde(default = "default_log_dir")]
|
#[serde(default = "default_log_dir")]
|
||||||
pub log_dir: PathBuf,
|
pub log_dir: PathBuf,
|
||||||
#[serde(default = "default_retention_days")]
|
#[serde(default = "default_retention_days")]
|
||||||
pub retention_days: u32,
|
pub retention_days: u32,
|
||||||
#[serde(default = "default_ring_buffer_size")]
|
#[serde(default = "default_ring_buffer_size")]
|
||||||
pub ring_buffer_size: usize,
|
pub ring_buffer_size: usize,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub scanners: HashMap<String, ScannerConfig>,
|
pub scanners: HashMap<String, ScannerConfig>,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub journal_enabled: bool,
|
pub journal_enabled: bool,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub file_enabled: bool,
|
pub file_enabled: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn default_scanner_dirs() -> Vec<PathBuf> {
|
fn default_scanner_dirs() -> Vec<PathBuf> {
|
||||||
let mut dirs = Vec::new();
|
let mut dirs = Vec::new();
|
||||||
|
|
||||||
if let Ok(env_var) = std::env::var("PSCAND_SCANNER_DIRS") {
|
if let Ok(env_var) = std::env::var("PSCAND_SCANNER_DIRS") {
|
||||||
for path in env_var.split(':') {
|
for path in env_var.split(':') {
|
||||||
let path = PathBuf::from(path);
|
let path = PathBuf::from(path);
|
||||||
if !path.as_os_str().is_empty() {
|
if !path.as_os_str().is_empty() {
|
||||||
dirs.push(path);
|
dirs.push(path);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
if !dirs.is_empty() {
|
|
||||||
return dirs;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
if !dirs.is_empty() {
|
||||||
if let Some(lib) = std::env::var_os("LIB_PSCAND") {
|
return dirs;
|
||||||
dirs.push(PathBuf::from(lib));
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if let Ok(lib_dir) = std::env::var("LIBDIR_PSCAND") {
|
if let Some(lib) = std::env::var_os("LIB_PSCAND") {
|
||||||
dirs.push(PathBuf::from(lib_dir));
|
dirs.push(PathBuf::from(lib));
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(local) = dirs::data_local_dir() {
|
if let Ok(lib_dir) = std::env::var("LIBDIR_PSCAND") {
|
||||||
dirs.push(local.join("pscand/scanners"));
|
dirs.push(PathBuf::from(lib_dir));
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(config) = dirs::config_dir() {
|
if let Some(local) = dirs::data_local_dir() {
|
||||||
dirs.push(config.join("pscand/scanners"));
|
dirs.push(local.join("pscand/scanners"));
|
||||||
}
|
}
|
||||||
|
|
||||||
if dirs.is_empty() {
|
if let Some(config) = dirs::config_dir() {
|
||||||
dirs.push(PathBuf::from(".pscand/scanners"));
|
dirs.push(config.join("pscand/scanners"));
|
||||||
}
|
}
|
||||||
|
|
||||||
dirs
|
if dirs.is_empty() {
|
||||||
|
dirs.push(PathBuf::from(".pscand/scanners"));
|
||||||
|
}
|
||||||
|
|
||||||
|
dirs
|
||||||
}
|
}
|
||||||
|
|
||||||
fn default_log_dir() -> PathBuf {
|
fn default_log_dir() -> PathBuf {
|
||||||
dirs::data_local_dir()
|
dirs::data_local_dir()
|
||||||
.map(|p| p.join("pscand/logs"))
|
.map(|p| p.join("pscand/logs"))
|
||||||
.unwrap_or_else(|| PathBuf::from(".pscand/logs"))
|
.unwrap_or_else(|| PathBuf::from(".pscand/logs"))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn default_retention_days() -> u32 {
|
fn default_retention_days() -> u32 {
|
||||||
7
|
7
|
||||||
}
|
}
|
||||||
|
|
||||||
fn default_ring_buffer_size() -> usize {
|
fn default_ring_buffer_size() -> usize {
|
||||||
60
|
60
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Config {
|
impl Default for Config {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
scanner_dirs: default_scanner_dirs(),
|
scanner_dirs: default_scanner_dirs(),
|
||||||
log_dir: default_log_dir(),
|
log_dir: default_log_dir(),
|
||||||
retention_days: default_retention_days(),
|
retention_days: default_retention_days(),
|
||||||
ring_buffer_size: default_ring_buffer_size(),
|
ring_buffer_size: default_ring_buffer_size(),
|
||||||
scanners: HashMap::new(),
|
scanners: HashMap::new(),
|
||||||
journal_enabled: true,
|
journal_enabled: true,
|
||||||
file_enabled: true,
|
file_enabled: true,
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Config {
|
impl Config {
|
||||||
pub fn load(path: &Path) -> ConfigResult<Self> {
|
pub fn load(path: &Path) -> ConfigResult<Self> {
|
||||||
let content = std::fs::read_to_string(path)?;
|
let content = std::fs::read_to_string(path)?;
|
||||||
let mut config: Config = toml::from_str(&content)?;
|
let mut config: Config = toml::from_str(&content)?;
|
||||||
config.scanner_dirs.retain(|p| p.exists());
|
config.scanner_dirs.retain(|p| p.exists());
|
||||||
Ok(config)
|
Ok(config)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn scanner_config(&self, name: &str) -> Option<&ScannerConfig> {
|
pub fn scanner_config(&self, name: &str) -> Option<&ScannerConfig> {
|
||||||
self.scanners.get(name)
|
self.scanners.get(name)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_scanner_enabled(&self, name: &str) -> bool {
|
pub fn is_scanner_enabled(&self, name: &str) -> bool {
|
||||||
self.scanners.get(name).map(|c| c.enabled).unwrap_or(true)
|
self.scanners.get(name).map(|c| c.enabled).unwrap_or(true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,157 +1,160 @@
|
||||||
use std::collections::HashMap;
|
use std::{
|
||||||
use std::fs;
|
collections::HashMap,
|
||||||
use std::path::PathBuf;
|
fs,
|
||||||
|
path::PathBuf,
|
||||||
|
};
|
||||||
|
|
||||||
pub struct PowerHelper;
|
pub struct PowerHelper;
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct BatteryInfo {
|
pub struct BatteryInfo {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub status: String,
|
pub status: String,
|
||||||
pub capacity: u32,
|
pub capacity: u32,
|
||||||
pub charge_percent: i32,
|
pub charge_percent: i32,
|
||||||
pub voltage: f64,
|
pub voltage: f64,
|
||||||
pub current_now: i64,
|
pub current_now: i64,
|
||||||
pub power_now: i64,
|
pub power_now: i64,
|
||||||
pub present: bool,
|
pub present: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PowerHelper {
|
impl PowerHelper {
|
||||||
pub fn battery_info() -> std::io::Result<Option<BatteryInfo>> {
|
pub fn battery_info() -> std::io::Result<Option<BatteryInfo>> {
|
||||||
let battery_path = PathBuf::from("/sys/class/power_supply");
|
let battery_path = PathBuf::from("/sys/class/power_supply");
|
||||||
|
|
||||||
for entry in fs::read_dir(&battery_path)? {
|
for entry in fs::read_dir(&battery_path)? {
|
||||||
let entry = entry?;
|
let entry = entry?;
|
||||||
let path = entry.path();
|
let path = entry.path();
|
||||||
let type_path = path.join("type");
|
let type_path = path.join("type");
|
||||||
|
|
||||||
if type_path.exists() {
|
if type_path.exists() {
|
||||||
let battery_type = fs::read_to_string(&type_path)?.trim().to_string();
|
let battery_type = fs::read_to_string(&type_path)?.trim().to_string();
|
||||||
if battery_type == "Battery" {
|
if battery_type == "Battery" {
|
||||||
let present_path = path.join("present");
|
let present_path = path.join("present");
|
||||||
let present = fs::read_to_string(&present_path)
|
let present = fs::read_to_string(&present_path)
|
||||||
.map(|s| s.trim() == "1")
|
.map(|s| s.trim() == "1")
|
||||||
.unwrap_or(false);
|
.unwrap_or(false);
|
||||||
|
|
||||||
if !present {
|
if !present {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
let name = fs::read_to_string(path.join("name"))
|
let name = fs::read_to_string(path.join("name"))
|
||||||
.unwrap_or_else(|_| "Unknown".to_string())
|
.unwrap_or_else(|_| "Unknown".to_string())
|
||||||
.trim()
|
.trim()
|
||||||
.to_string();
|
.to_string();
|
||||||
|
|
||||||
let status = fs::read_to_string(path.join("status"))
|
let status = fs::read_to_string(path.join("status"))
|
||||||
.unwrap_or_else(|_| "Unknown".to_string())
|
.unwrap_or_else(|_| "Unknown".to_string())
|
||||||
.trim()
|
.trim()
|
||||||
.to_string();
|
.to_string();
|
||||||
|
|
||||||
let capacity = fs::read_to_string(path.join("capacity"))
|
let capacity = fs::read_to_string(path.join("capacity"))
|
||||||
.ok()
|
.ok()
|
||||||
.and_then(|s| s.trim().parse::<u32>().ok())
|
.and_then(|s| s.trim().parse::<u32>().ok())
|
||||||
.unwrap_or(0);
|
.unwrap_or(0);
|
||||||
|
|
||||||
let charge_now = fs::read_to_string(path.join("charge_now"))
|
let charge_now = fs::read_to_string(path.join("charge_now"))
|
||||||
.or_else(|_| fs::read_to_string(path.join("energy_now")))
|
.or_else(|_| fs::read_to_string(path.join("energy_now")))
|
||||||
.ok()
|
.ok()
|
||||||
.and_then(|s| s.trim().parse::<i64>().ok())
|
.and_then(|s| s.trim().parse::<i64>().ok())
|
||||||
.unwrap_or(0);
|
.unwrap_or(0);
|
||||||
|
|
||||||
let charge_full = fs::read_to_string(path.join("charge_full"))
|
let charge_full = fs::read_to_string(path.join("charge_full"))
|
||||||
.or_else(|_| fs::read_to_string(path.join("energy_full")))
|
.or_else(|_| fs::read_to_string(path.join("energy_full")))
|
||||||
.ok()
|
.ok()
|
||||||
.and_then(|s| s.trim().parse::<i64>().ok())
|
.and_then(|s| s.trim().parse::<i64>().ok())
|
||||||
.unwrap_or(1);
|
.unwrap_or(1);
|
||||||
|
|
||||||
let charge_percent = if charge_full > 0 {
|
let charge_percent = if charge_full > 0 {
|
||||||
((charge_now as f64 / charge_full as f64) * 100.0) as i32
|
((charge_now as f64 / charge_full as f64) * 100.0) as i32
|
||||||
} else {
|
} else {
|
||||||
0
|
0
|
||||||
};
|
};
|
||||||
|
|
||||||
let voltage = fs::read_to_string(path.join("voltage_now"))
|
let voltage = fs::read_to_string(path.join("voltage_now"))
|
||||||
.ok()
|
.ok()
|
||||||
.and_then(|s| s.trim().parse::<f64>().ok())
|
.and_then(|s| s.trim().parse::<f64>().ok())
|
||||||
.unwrap_or(0.0)
|
.unwrap_or(0.0)
|
||||||
/ 1_000_000.0;
|
/ 1_000_000.0;
|
||||||
|
|
||||||
let current_now = fs::read_to_string(path.join("current_now"))
|
let current_now = fs::read_to_string(path.join("current_now"))
|
||||||
.ok()
|
.ok()
|
||||||
.and_then(|s| s.trim().parse::<i64>().ok())
|
.and_then(|s| s.trim().parse::<i64>().ok())
|
||||||
.unwrap_or(0);
|
.unwrap_or(0);
|
||||||
|
|
||||||
let power_now = fs::read_to_string(path.join("power_now"))
|
let power_now = fs::read_to_string(path.join("power_now"))
|
||||||
.ok()
|
.ok()
|
||||||
.and_then(|s| s.trim().parse::<i64>().ok())
|
.and_then(|s| s.trim().parse::<i64>().ok())
|
||||||
.unwrap_or(0);
|
.unwrap_or(0);
|
||||||
|
|
||||||
return Ok(Some(BatteryInfo {
|
return Ok(Some(BatteryInfo {
|
||||||
name,
|
name,
|
||||||
status,
|
status,
|
||||||
capacity,
|
capacity,
|
||||||
charge_percent,
|
charge_percent,
|
||||||
voltage,
|
voltage,
|
||||||
current_now,
|
current_now,
|
||||||
power_now,
|
power_now,
|
||||||
present,
|
present,
|
||||||
}));
|
}));
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
Ok(None)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn power_supplies() -> std::io::Result<HashMap<String, HashMap<String, String>>> {
|
Ok(None)
|
||||||
let mut supplies = HashMap::new();
|
}
|
||||||
let power_supply_path = PathBuf::from("/sys/class/power_supply");
|
|
||||||
|
|
||||||
if !power_supply_path.exists() {
|
pub fn power_supplies(
|
||||||
return Ok(supplies);
|
) -> std::io::Result<HashMap<String, HashMap<String, String>>> {
|
||||||
|
let mut supplies = HashMap::new();
|
||||||
|
let power_supply_path = PathBuf::from("/sys/class/power_supply");
|
||||||
|
|
||||||
|
if !power_supply_path.exists() {
|
||||||
|
return Ok(supplies);
|
||||||
|
}
|
||||||
|
|
||||||
|
for entry in fs::read_dir(&power_supply_path)? {
|
||||||
|
let entry = entry?;
|
||||||
|
let path = entry.path();
|
||||||
|
let name = path
|
||||||
|
.file_name()
|
||||||
|
.map(|n| n.to_string_lossy().to_string())
|
||||||
|
.unwrap_or_default();
|
||||||
|
|
||||||
|
let mut info = HashMap::new();
|
||||||
|
|
||||||
|
for attr in [
|
||||||
|
"type",
|
||||||
|
"status",
|
||||||
|
"capacity",
|
||||||
|
"voltage_now",
|
||||||
|
"power_now",
|
||||||
|
"online",
|
||||||
|
] {
|
||||||
|
let attr_path = path.join(attr);
|
||||||
|
if attr_path.exists() {
|
||||||
|
if let Ok(content) = fs::read_to_string(&attr_path) {
|
||||||
|
info.insert(attr.to_string(), content.trim().to_string());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for entry in fs::read_dir(&power_supply_path)? {
|
if !info.is_empty() {
|
||||||
let entry = entry?;
|
supplies.insert(name, info);
|
||||||
let path = entry.path();
|
}
|
||||||
let name = path
|
|
||||||
.file_name()
|
|
||||||
.map(|n| n.to_string_lossy().to_string())
|
|
||||||
.unwrap_or_default();
|
|
||||||
|
|
||||||
let mut info = HashMap::new();
|
|
||||||
|
|
||||||
for attr in [
|
|
||||||
"type",
|
|
||||||
"status",
|
|
||||||
"capacity",
|
|
||||||
"voltage_now",
|
|
||||||
"power_now",
|
|
||||||
"online",
|
|
||||||
] {
|
|
||||||
let attr_path = path.join(attr);
|
|
||||||
if attr_path.exists() {
|
|
||||||
if let Ok(content) = fs::read_to_string(&attr_path) {
|
|
||||||
info.insert(attr.to_string(), content.trim().to_string());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !info.is_empty() {
|
|
||||||
supplies.insert(name, info);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(supplies)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn suspend_state() -> std::io::Result<String> {
|
Ok(supplies)
|
||||||
let state_path = PathBuf::from("/sys/power/state");
|
}
|
||||||
fs::read_to_string(state_path).map(|s| s.trim().to_string())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn mem_sleep_state() -> std::io::Result<String> {
|
pub fn suspend_state() -> std::io::Result<String> {
|
||||||
let state_path = PathBuf::from("/sys/power/mem_sleep");
|
let state_path = PathBuf::from("/sys/power/state");
|
||||||
fs::read_to_string(state_path).map(|s| s.trim().to_string())
|
fs::read_to_string(state_path).map(|s| s.trim().to_string())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn mem_sleep_state() -> std::io::Result<String> {
|
||||||
|
let state_path = PathBuf::from("/sys/power/mem_sleep");
|
||||||
|
fs::read_to_string(state_path).map(|s| s.trim().to_string())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,136 +1,144 @@
|
||||||
use std::collections::HashMap;
|
use std::{
|
||||||
use std::fs;
|
collections::HashMap,
|
||||||
|
fs,
|
||||||
|
};
|
||||||
|
|
||||||
pub struct ProcessHelper;
|
pub struct ProcessHelper;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct ProcessInfo {
|
pub struct ProcessInfo {
|
||||||
pub pid: u32,
|
pub pid: u32,
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub state: String,
|
pub state: String,
|
||||||
pub ppid: u32,
|
pub ppid: u32,
|
||||||
pub memory_kb: u64,
|
pub memory_kb: u64,
|
||||||
pub cpu_percent: f32,
|
pub cpu_percent: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ProcessHelper {
|
impl ProcessHelper {
|
||||||
pub fn list_processes() -> std::io::Result<Vec<ProcessInfo>> {
|
pub fn list_processes() -> std::io::Result<Vec<ProcessInfo>> {
|
||||||
let mut processes = Vec::new();
|
let mut processes = Vec::new();
|
||||||
let proc_path = fs::read_dir("/proc")?;
|
let proc_path = fs::read_dir("/proc")?;
|
||||||
|
|
||||||
for entry in proc_path.flatten() {
|
for entry in proc_path.flatten() {
|
||||||
let path = entry.path();
|
let path = entry.path();
|
||||||
if !path.is_dir() {
|
if !path.is_dir() {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
let pid: u32 = match path.file_name() {
|
let pid: u32 = match path.file_name() {
|
||||||
Some(name) => match name.to_str() {
|
Some(name) => {
|
||||||
Some(s) => s.parse().ok(),
|
match name.to_str() {
|
||||||
None => None,
|
Some(s) => s.parse().ok(),
|
||||||
}
|
None => None,
|
||||||
.ok_or(std::io::Error::new(
|
}
|
||||||
std::io::ErrorKind::InvalidData,
|
.ok_or(std::io::Error::new(
|
||||||
"invalid pid",
|
std::io::ErrorKind::InvalidData,
|
||||||
))?,
|
"invalid pid",
|
||||||
None => continue,
|
))?
|
||||||
};
|
},
|
||||||
|
None => continue,
|
||||||
|
};
|
||||||
|
|
||||||
if let Ok(info) = Self::process_info(pid) {
|
if let Ok(info) = Self::process_info(pid) {
|
||||||
processes.push(info);
|
processes.push(info);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
Ok(processes)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn process_info(pid: u32) -> std::io::Result<ProcessInfo> {
|
Ok(processes)
|
||||||
let status_path = format!("/proc/{}/status", pid);
|
}
|
||||||
let content = fs::read_to_string(status_path)?;
|
|
||||||
|
|
||||||
let mut name = String::new();
|
pub fn process_info(pid: u32) -> std::io::Result<ProcessInfo> {
|
||||||
let mut state = String::new();
|
let status_path = format!("/proc/{}/status", pid);
|
||||||
let mut ppid: u32 = 0;
|
let content = fs::read_to_string(status_path)?;
|
||||||
let mut memory_kb: u64 = 0;
|
|
||||||
|
|
||||||
for line in content.lines() {
|
let mut name = String::new();
|
||||||
if line.starts_with("Name:") {
|
let mut state = String::new();
|
||||||
name = line
|
let mut ppid: u32 = 0;
|
||||||
.split_whitespace()
|
let mut memory_kb: u64 = 0;
|
||||||
.skip(1)
|
|
||||||
.collect::<Vec<_>>()
|
|
||||||
.join(" ");
|
|
||||||
} else if line.starts_with("State:") {
|
|
||||||
state = line.split_whitespace().nth(1).unwrap_or("").to_string();
|
|
||||||
} else if line.starts_with("PPid:") {
|
|
||||||
ppid = line
|
|
||||||
.split_whitespace()
|
|
||||||
.nth(1)
|
|
||||||
.and_then(|s| s.parse().ok())
|
|
||||||
.unwrap_or(0);
|
|
||||||
} else if line.starts_with("VmRSS:") {
|
|
||||||
memory_kb = line
|
|
||||||
.split_whitespace()
|
|
||||||
.nth(1)
|
|
||||||
.and_then(|s| s.parse().ok())
|
|
||||||
.unwrap_or(0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(ProcessInfo {
|
for line in content.lines() {
|
||||||
pid,
|
if line.starts_with("Name:") {
|
||||||
name,
|
name = line
|
||||||
state,
|
.split_whitespace()
|
||||||
ppid,
|
.skip(1)
|
||||||
memory_kb,
|
.collect::<Vec<_>>()
|
||||||
cpu_percent: 0.0,
|
.join(" ");
|
||||||
})
|
} else if line.starts_with("State:") {
|
||||||
|
state = line.split_whitespace().nth(1).unwrap_or("").to_string();
|
||||||
|
} else if line.starts_with("PPid:") {
|
||||||
|
ppid = line
|
||||||
|
.split_whitespace()
|
||||||
|
.nth(1)
|
||||||
|
.and_then(|s| s.parse().ok())
|
||||||
|
.unwrap_or(0);
|
||||||
|
} else if line.starts_with("VmRSS:") {
|
||||||
|
memory_kb = line
|
||||||
|
.split_whitespace()
|
||||||
|
.nth(1)
|
||||||
|
.and_then(|s| s.parse().ok())
|
||||||
|
.unwrap_or(0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn zombie_processes() -> std::io::Result<Vec<ProcessInfo>> {
|
Ok(ProcessInfo {
|
||||||
Ok(Self::list_processes()?
|
pid,
|
||||||
.into_iter()
|
name,
|
||||||
.filter(|p| p.state.starts_with('Z'))
|
state,
|
||||||
.collect())
|
ppid,
|
||||||
|
memory_kb,
|
||||||
|
cpu_percent: 0.0,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn zombie_processes() -> std::io::Result<Vec<ProcessInfo>> {
|
||||||
|
Ok(
|
||||||
|
Self::list_processes()?
|
||||||
|
.into_iter()
|
||||||
|
.filter(|p| p.state.starts_with('Z'))
|
||||||
|
.collect(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn process_count() -> std::io::Result<HashMap<String, usize>> {
|
||||||
|
let mut counts = HashMap::new();
|
||||||
|
counts.insert("total".to_string(), 0);
|
||||||
|
counts.insert("running".to_string(), 0);
|
||||||
|
counts.insert("sleeping".to_string(), 0);
|
||||||
|
counts.insert("zombie".to_string(), 0);
|
||||||
|
|
||||||
|
for proc in Self::list_processes()? {
|
||||||
|
*counts.get_mut("total").unwrap() += 1;
|
||||||
|
|
||||||
|
let first_char = proc.state.chars().next().unwrap_or(' ');
|
||||||
|
match first_char {
|
||||||
|
'R' => *counts.get_mut("running").unwrap() += 1,
|
||||||
|
'S' | 'D' => *counts.get_mut("sleeping").unwrap() += 1,
|
||||||
|
'Z' => *counts.get_mut("zombie").unwrap() += 1,
|
||||||
|
_ => {},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn process_count() -> std::io::Result<HashMap<String, usize>> {
|
Ok(counts)
|
||||||
let mut counts = HashMap::new();
|
}
|
||||||
counts.insert("total".to_string(), 0);
|
|
||||||
counts.insert("running".to_string(), 0);
|
|
||||||
counts.insert("sleeping".to_string(), 0);
|
|
||||||
counts.insert("zombie".to_string(), 0);
|
|
||||||
|
|
||||||
for proc in Self::list_processes()? {
|
pub fn top_memory_processes(
|
||||||
*counts.get_mut("total").unwrap() += 1;
|
count: usize,
|
||||||
|
) -> std::io::Result<Vec<ProcessInfo>> {
|
||||||
|
let mut processes = Self::list_processes()?;
|
||||||
|
processes.sort_by(|a, b| b.memory_kb.cmp(&a.memory_kb));
|
||||||
|
processes.truncate(count);
|
||||||
|
Ok(processes)
|
||||||
|
}
|
||||||
|
|
||||||
let first_char = proc.state.chars().next().unwrap_or(' ');
|
pub fn top_cpu_processes(count: usize) -> std::io::Result<Vec<ProcessInfo>> {
|
||||||
match first_char {
|
let mut processes = Self::list_processes()?;
|
||||||
'R' => *counts.get_mut("running").unwrap() += 1,
|
processes.sort_by(|a, b| {
|
||||||
'S' | 'D' => *counts.get_mut("sleeping").unwrap() += 1,
|
b.cpu_percent
|
||||||
'Z' => *counts.get_mut("zombie").unwrap() += 1,
|
.partial_cmp(&a.cpu_percent)
|
||||||
_ => {}
|
.unwrap_or(std::cmp::Ordering::Equal)
|
||||||
}
|
});
|
||||||
}
|
processes.truncate(count);
|
||||||
|
Ok(processes)
|
||||||
Ok(counts)
|
}
|
||||||
}
|
|
||||||
|
|
||||||
pub fn top_memory_processes(count: usize) -> std::io::Result<Vec<ProcessInfo>> {
|
|
||||||
let mut processes = Self::list_processes()?;
|
|
||||||
processes.sort_by(|a, b| b.memory_kb.cmp(&a.memory_kb));
|
|
||||||
processes.truncate(count);
|
|
||||||
Ok(processes)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn top_cpu_processes(count: usize) -> std::io::Result<Vec<ProcessInfo>> {
|
|
||||||
let mut processes = Self::list_processes()?;
|
|
||||||
processes.sort_by(|a, b| {
|
|
||||||
b.cpu_percent
|
|
||||||
.partial_cmp(&a.cpu_percent)
|
|
||||||
.unwrap_or(std::cmp::Ordering::Equal)
|
|
||||||
});
|
|
||||||
processes.truncate(count);
|
|
||||||
Ok(processes)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,123 +1,129 @@
|
||||||
use std::collections::HashMap;
|
use std::{
|
||||||
use std::fs;
|
collections::HashMap,
|
||||||
|
fs,
|
||||||
|
};
|
||||||
|
|
||||||
pub struct ResourceHelper;
|
pub struct ResourceHelper;
|
||||||
|
|
||||||
impl ResourceHelper {
|
impl ResourceHelper {
|
||||||
pub fn cpu_usage() -> std::io::Result<HashMap<String, f64>> {
|
pub fn cpu_usage() -> std::io::Result<HashMap<String, f64>> {
|
||||||
let content = fs::read_to_string("/proc/stat")?;
|
let content = fs::read_to_string("/proc/stat")?;
|
||||||
let mut result = HashMap::new();
|
let mut result = HashMap::new();
|
||||||
|
|
||||||
for line in content.lines() {
|
for line in content.lines() {
|
||||||
if line.starts_with("cpu") {
|
if line.starts_with("cpu") {
|
||||||
let parts: Vec<&str> = line.split_whitespace().collect();
|
let parts: Vec<&str> = line.split_whitespace().collect();
|
||||||
if parts.len() >= 5 {
|
if parts.len() >= 5 {
|
||||||
let cpu = parts[0];
|
let cpu = parts[0];
|
||||||
let values: Vec<u64> = parts[1..]
|
let values: Vec<u64> = parts[1..]
|
||||||
.iter()
|
.iter()
|
||||||
.take(7)
|
.take(7)
|
||||||
.filter_map(|s| s.parse().ok())
|
.filter_map(|s| s.parse().ok())
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
if values.len() >= 4 {
|
if values.len() >= 4 {
|
||||||
let user = values[0] as f64;
|
let user = values[0] as f64;
|
||||||
let nice = values[1] as f64;
|
let nice = values[1] as f64;
|
||||||
let system = values[2] as f64;
|
let system = values[2] as f64;
|
||||||
let idle = values[3] as f64;
|
let idle = values[3] as f64;
|
||||||
let iowait = values.get(4).copied().unwrap_or(0) as f64;
|
let iowait = values.get(4).copied().unwrap_or(0) as f64;
|
||||||
let irq = values.get(5).copied().unwrap_or(0) as f64;
|
let irq = values.get(5).copied().unwrap_or(0) as f64;
|
||||||
let softirq = values.get(6).copied().unwrap_or(0) as f64;
|
let softirq = values.get(6).copied().unwrap_or(0) as f64;
|
||||||
|
|
||||||
let total = user + nice + system + idle + iowait + irq + softirq;
|
let total = user + nice + system + idle + iowait + irq + softirq;
|
||||||
let active = user + nice + system + irq + softirq;
|
let active = user + nice + system + irq + softirq;
|
||||||
|
|
||||||
if cpu == "cpu" {
|
if cpu == "cpu" {
|
||||||
result.insert("total_user".to_string(), user);
|
result.insert("total_user".to_string(), user);
|
||||||
result.insert("total_nice".to_string(), nice);
|
result.insert("total_nice".to_string(), nice);
|
||||||
result.insert("total_system".to_string(), system);
|
result.insert("total_system".to_string(), system);
|
||||||
result.insert("total_idle".to_string(), idle);
|
result.insert("total_idle".to_string(), idle);
|
||||||
result.insert("total_iowait".to_string(), iowait);
|
result.insert("total_iowait".to_string(), iowait);
|
||||||
result.insert(
|
result.insert(
|
||||||
"total_usage_percent".to_string(),
|
"total_usage_percent".to_string(),
|
||||||
(active / total) * 100.0,
|
(active / total) * 100.0,
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
let core = cpu.replace("cpu", "core_");
|
let core = cpu.replace("cpu", "core_");
|
||||||
result.insert(format!("{}_user", core), user);
|
result.insert(format!("{}_user", core), user);
|
||||||
result.insert(
|
result.insert(
|
||||||
format!("{}_usage_percent", core),
|
format!("{}_usage_percent", core),
|
||||||
(active / total) * 100.0,
|
(active / total) * 100.0,
|
||||||
);
|
);
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Ok(result)
|
}
|
||||||
}
|
}
|
||||||
|
Ok(result)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn memory_info() -> std::io::Result<HashMap<String, u64>> {
|
pub fn memory_info() -> std::io::Result<HashMap<String, u64>> {
|
||||||
let content = fs::read_to_string("/proc/meminfo")?;
|
let content = fs::read_to_string("/proc/meminfo")?;
|
||||||
let mut result = HashMap::new();
|
let mut result = HashMap::new();
|
||||||
|
|
||||||
for line in content.lines() {
|
for line in content.lines() {
|
||||||
let parts: Vec<&str> = line.split_whitespace().collect();
|
let parts: Vec<&str> = line.split_whitespace().collect();
|
||||||
if parts.len() >= 2 {
|
if parts.len() >= 2 {
|
||||||
let key = parts[0].trim_end_matches(':');
|
let key = parts[0].trim_end_matches(':');
|
||||||
if let Ok(value) = parts[1].parse::<u64>() {
|
if let Ok(value) = parts[1].parse::<u64>() {
|
||||||
result.insert(key.to_string(), value * 1024);
|
result.insert(key.to_string(), value * 1024);
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Ok(result)
|
}
|
||||||
}
|
}
|
||||||
|
Ok(result)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn disk_stats() -> std::io::Result<HashMap<String, HashMap<String, u64>>> {
|
pub fn disk_stats() -> std::io::Result<HashMap<String, HashMap<String, u64>>>
|
||||||
let content = fs::read_to_string("/proc/diskstats")?;
|
{
|
||||||
let mut result = HashMap::new();
|
let content = fs::read_to_string("/proc/diskstats")?;
|
||||||
|
let mut result = HashMap::new();
|
||||||
|
|
||||||
for line in content.lines() {
|
for line in content.lines() {
|
||||||
let parts: Vec<&str> = line.split_whitespace().collect();
|
let parts: Vec<&str> = line.split_whitespace().collect();
|
||||||
if parts.len() >= 14 {
|
if parts.len() >= 14 {
|
||||||
let device = parts[2].to_string();
|
let device = parts[2].to_string();
|
||||||
let mut stats = HashMap::new();
|
let mut stats = HashMap::new();
|
||||||
stats.insert("reads_completed".to_string(), parts[3].parse().unwrap_or(0));
|
stats
|
||||||
stats.insert("reads_merged".to_string(), parts[4].parse().unwrap_or(0));
|
.insert("reads_completed".to_string(), parts[3].parse().unwrap_or(0));
|
||||||
stats.insert("sectors_read".to_string(), parts[5].parse().unwrap_or(0));
|
stats.insert("reads_merged".to_string(), parts[4].parse().unwrap_or(0));
|
||||||
stats.insert("reads_ms".to_string(), parts[6].parse().unwrap_or(0));
|
stats.insert("sectors_read".to_string(), parts[5].parse().unwrap_or(0));
|
||||||
stats.insert(
|
stats.insert("reads_ms".to_string(), parts[6].parse().unwrap_or(0));
|
||||||
"writes_completed".to_string(),
|
stats.insert(
|
||||||
parts[7].parse().unwrap_or(0),
|
"writes_completed".to_string(),
|
||||||
);
|
parts[7].parse().unwrap_or(0),
|
||||||
stats.insert("writes_merged".to_string(), parts[8].parse().unwrap_or(0));
|
);
|
||||||
stats.insert("sectors_written".to_string(), parts[9].parse().unwrap_or(0));
|
stats
|
||||||
stats.insert("writes_ms".to_string(), parts[10].parse().unwrap_or(0));
|
.insert("writes_merged".to_string(), parts[8].parse().unwrap_or(0));
|
||||||
result.insert(device, stats);
|
stats
|
||||||
}
|
.insert("sectors_written".to_string(), parts[9].parse().unwrap_or(0));
|
||||||
}
|
stats.insert("writes_ms".to_string(), parts[10].parse().unwrap_or(0));
|
||||||
Ok(result)
|
result.insert(device, stats);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Ok(result)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn net_dev() -> std::io::Result<HashMap<String, HashMap<String, u64>>> {
|
pub fn net_dev() -> std::io::Result<HashMap<String, HashMap<String, u64>>> {
|
||||||
let content = fs::read_to_string("/proc/net/dev")?;
|
let content = fs::read_to_string("/proc/net/dev")?;
|
||||||
let mut result = HashMap::new();
|
let mut result = HashMap::new();
|
||||||
|
|
||||||
for line in content.lines().skip(2) {
|
for line in content.lines().skip(2) {
|
||||||
let parts: Vec<&str> = line.split_whitespace().collect();
|
let parts: Vec<&str> = line.split_whitespace().collect();
|
||||||
if parts.len() >= 17 {
|
if parts.len() >= 17 {
|
||||||
let iface = parts[0].trim_end_matches(':');
|
let iface = parts[0].trim_end_matches(':');
|
||||||
let mut stats = HashMap::new();
|
let mut stats = HashMap::new();
|
||||||
stats.insert("rx_bytes".to_string(), parts[1].parse().unwrap_or(0));
|
stats.insert("rx_bytes".to_string(), parts[1].parse().unwrap_or(0));
|
||||||
stats.insert("rx_packets".to_string(), parts[2].parse().unwrap_or(0));
|
stats.insert("rx_packets".to_string(), parts[2].parse().unwrap_or(0));
|
||||||
stats.insert("rx_errors".to_string(), parts[3].parse().unwrap_or(0));
|
stats.insert("rx_errors".to_string(), parts[3].parse().unwrap_or(0));
|
||||||
stats.insert("rx_dropped".to_string(), parts[4].parse().unwrap_or(0));
|
stats.insert("rx_dropped".to_string(), parts[4].parse().unwrap_or(0));
|
||||||
stats.insert("tx_bytes".to_string(), parts[9].parse().unwrap_or(0));
|
stats.insert("tx_bytes".to_string(), parts[9].parse().unwrap_or(0));
|
||||||
stats.insert("tx_packets".to_string(), parts[10].parse().unwrap_or(0));
|
stats.insert("tx_packets".to_string(), parts[10].parse().unwrap_or(0));
|
||||||
stats.insert("tx_errors".to_string(), parts[11].parse().unwrap_or(0));
|
stats.insert("tx_errors".to_string(), parts[11].parse().unwrap_or(0));
|
||||||
stats.insert("tx_dropped".to_string(), parts[12].parse().unwrap_or(0));
|
stats.insert("tx_dropped".to_string(), parts[12].parse().unwrap_or(0));
|
||||||
result.insert(iface.to_string(), stats);
|
result.insert(iface.to_string(), stats);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
Ok(result)
|
|
||||||
}
|
}
|
||||||
|
Ok(result)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,102 +1,117 @@
|
||||||
use std::collections::HashMap;
|
use std::{
|
||||||
use std::fs;
|
collections::HashMap,
|
||||||
use std::path::{Path, PathBuf};
|
fs,
|
||||||
|
path::{
|
||||||
|
Path,
|
||||||
|
PathBuf,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
pub struct SensorHelper;
|
pub struct SensorHelper;
|
||||||
|
|
||||||
impl SensorHelper {
|
impl SensorHelper {
|
||||||
pub fn discover_hwmon() -> std::io::Result<Vec<PathBuf>> {
|
pub fn discover_hwmon() -> std::io::Result<Vec<PathBuf>> {
|
||||||
let hwmon_path = PathBuf::from("/sys/class/hwmon");
|
let hwmon_path = PathBuf::from("/sys/class/hwmon");
|
||||||
let mut hwmons = Vec::new();
|
let mut hwmons = Vec::new();
|
||||||
|
|
||||||
if hwmon_path.exists() {
|
if hwmon_path.exists() {
|
||||||
for entry in fs::read_dir(&hwmon_path)? {
|
for entry in fs::read_dir(&hwmon_path)? {
|
||||||
let entry = entry?;
|
let entry = entry?;
|
||||||
let path = entry.path();
|
let path = entry.path();
|
||||||
if path.is_dir() {
|
if path.is_dir() {
|
||||||
hwmons.push(path);
|
hwmons.push(path);
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Ok(hwmons)
|
}
|
||||||
|
}
|
||||||
|
Ok(hwmons)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read_hwmon_sensor(
|
||||||
|
hwmon_path: &Path,
|
||||||
|
sensor: &str,
|
||||||
|
) -> std::io::Result<Option<f64>> {
|
||||||
|
let sensor_path = hwmon_path.join(sensor);
|
||||||
|
if sensor_path.exists() {
|
||||||
|
let content = fs::read_to_string(sensor_path)?;
|
||||||
|
Ok(content.trim().parse::<f64>().ok())
|
||||||
|
} else {
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn hwmon_info(
|
||||||
|
hwmon_path: &Path,
|
||||||
|
) -> std::io::Result<HashMap<String, String>> {
|
||||||
|
let mut info = HashMap::new();
|
||||||
|
|
||||||
|
let name_path = hwmon_path.join("name");
|
||||||
|
if name_path.exists() {
|
||||||
|
info.insert(
|
||||||
|
"name".to_string(),
|
||||||
|
fs::read_to_string(name_path)?.trim().to_string(),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn read_hwmon_sensor(hwmon_path: &Path, sensor: &str) -> std::io::Result<Option<f64>> {
|
if let Ok(files) = fs::read_dir(hwmon_path) {
|
||||||
let sensor_path = hwmon_path.join(sensor);
|
for file in files.flatten() {
|
||||||
if sensor_path.exists() {
|
let filename = file.file_name().to_string_lossy().to_string();
|
||||||
let content = fs::read_to_string(sensor_path)?;
|
if filename.starts_with("temp") && filename.ends_with("_input") {
|
||||||
Ok(content.trim().parse::<f64>().ok())
|
let id = filename
|
||||||
} else {
|
.trim_start_matches("temp")
|
||||||
Ok(None)
|
.trim_end_matches("_input");
|
||||||
}
|
if let Some(t) = Self::read_hwmon_sensor(hwmon_path, &filename)
|
||||||
}
|
.ok()
|
||||||
|
.flatten()
|
||||||
pub fn hwmon_info(hwmon_path: &Path) -> std::io::Result<HashMap<String, String>> {
|
{
|
||||||
let mut info = HashMap::new();
|
|
||||||
|
|
||||||
let name_path = hwmon_path.join("name");
|
|
||||||
if name_path.exists() {
|
|
||||||
info.insert(
|
info.insert(
|
||||||
"name".to_string(),
|
format!("temp_{}_celsius", id),
|
||||||
fs::read_to_string(name_path)?.trim().to_string(),
|
format!("{}", t / 1000.0),
|
||||||
);
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
if filename.starts_with("fan") && filename.ends_with("_input") {
|
||||||
if let Ok(files) = fs::read_dir(hwmon_path) {
|
let id = filename
|
||||||
for file in files.flatten() {
|
.trim_start_matches("fan")
|
||||||
let filename = file.file_name().to_string_lossy().to_string();
|
.trim_end_matches("_input");
|
||||||
if filename.starts_with("temp") && filename.ends_with("_input") {
|
if let Some(f) = Self::read_hwmon_sensor(hwmon_path, &filename)
|
||||||
let id = filename
|
.ok()
|
||||||
.trim_start_matches("temp")
|
.flatten()
|
||||||
.trim_end_matches("_input");
|
{
|
||||||
if let Some(t) = Self::read_hwmon_sensor(hwmon_path, &filename)
|
info.insert(format!("fan_{}_rpm", id), format!("{}", f));
|
||||||
.ok()
|
}
|
||||||
.flatten()
|
|
||||||
{
|
|
||||||
info.insert(format!("temp_{}_celsius", id), format!("{}", t / 1000.0));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if filename.starts_with("fan") && filename.ends_with("_input") {
|
|
||||||
let id = filename
|
|
||||||
.trim_start_matches("fan")
|
|
||||||
.trim_end_matches("_input");
|
|
||||||
if let Some(f) = Self::read_hwmon_sensor(hwmon_path, &filename)
|
|
||||||
.ok()
|
|
||||||
.flatten()
|
|
||||||
{
|
|
||||||
info.insert(format!("fan_{}_rpm", id), format!("{}", f));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if filename.starts_with("in") && filename.ends_with("_input") {
|
|
||||||
let id = filename.trim_start_matches("in").trim_end_matches("_input");
|
|
||||||
if let Some(v) = Self::read_hwmon_sensor(hwmon_path, &filename)
|
|
||||||
.ok()
|
|
||||||
.flatten()
|
|
||||||
{
|
|
||||||
info.insert(format!("voltage_{}_mv", id), format!("{}", v / 1000.0));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
if filename.starts_with("in") && filename.ends_with("_input") {
|
||||||
Ok(info)
|
let id = filename.trim_start_matches("in").trim_end_matches("_input");
|
||||||
|
if let Some(v) = Self::read_hwmon_sensor(hwmon_path, &filename)
|
||||||
|
.ok()
|
||||||
|
.flatten()
|
||||||
|
{
|
||||||
|
info
|
||||||
|
.insert(format!("voltage_{}_mv", id), format!("{}", v / 1000.0));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn all_sensors() -> std::io::Result<HashMap<String, HashMap<String, String>>> {
|
Ok(info)
|
||||||
let mut all = HashMap::new();
|
}
|
||||||
|
|
||||||
for hwmon in Self::discover_hwmon()? {
|
pub fn all_sensors(
|
||||||
if let Ok(info) = Self::hwmon_info(&hwmon) {
|
) -> std::io::Result<HashMap<String, HashMap<String, String>>> {
|
||||||
let name = info.get("name").cloned().unwrap_or_else(|| {
|
let mut all = HashMap::new();
|
||||||
hwmon
|
|
||||||
.file_name()
|
|
||||||
.map(|s| s.to_string_lossy().into_owned())
|
|
||||||
.unwrap_or_else(|| "unknown".to_string())
|
|
||||||
});
|
|
||||||
all.insert(name, info);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(all)
|
for hwmon in Self::discover_hwmon()? {
|
||||||
|
if let Ok(info) = Self::hwmon_info(&hwmon) {
|
||||||
|
let name = info.get("name").cloned().unwrap_or_else(|| {
|
||||||
|
hwmon
|
||||||
|
.file_name()
|
||||||
|
.map(|s| s.to_string_lossy().into_owned())
|
||||||
|
.unwrap_or_else(|| "unknown".to_string())
|
||||||
|
});
|
||||||
|
all.insert(name, info);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Ok(all)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,45 +1,50 @@
|
||||||
use std::fs;
|
use std::{
|
||||||
use std::time::Duration;
|
fs,
|
||||||
|
time::Duration,
|
||||||
|
};
|
||||||
|
|
||||||
pub struct SystemHelper;
|
pub struct SystemHelper;
|
||||||
|
|
||||||
impl SystemHelper {
|
impl SystemHelper {
|
||||||
pub fn uptime() -> std::io::Result<Duration> {
|
pub fn uptime() -> std::io::Result<Duration> {
|
||||||
let uptime_secs = fs::read_to_string("/proc/uptime")?
|
let uptime_secs = fs::read_to_string("/proc/uptime")?
|
||||||
.split_whitespace()
|
.split_whitespace()
|
||||||
.next()
|
.next()
|
||||||
.and_then(|s| s.parse::<f64>().ok())
|
.and_then(|s| s.parse::<f64>().ok())
|
||||||
.unwrap_or(0.0);
|
.unwrap_or(0.0);
|
||||||
Ok(Duration::from_secs_f64(uptime_secs))
|
Ok(Duration::from_secs_f64(uptime_secs))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn boot_id() -> std::io::Result<String> {
|
pub fn boot_id() -> std::io::Result<String> {
|
||||||
fs::read_to_string("/proc/sys/kernel/random/boot_id").map(|s| s.trim().to_string())
|
fs::read_to_string("/proc/sys/kernel/random/boot_id")
|
||||||
}
|
.map(|s| s.trim().to_string())
|
||||||
|
}
|
||||||
|
|
||||||
pub fn load_average() -> std::io::Result<(f64, f64, f64)> {
|
pub fn load_average() -> std::io::Result<(f64, f64, f64)> {
|
||||||
let content = fs::read_to_string("/proc/loadavg")?;
|
let content = fs::read_to_string("/proc/loadavg")?;
|
||||||
let mut parts = content.split_whitespace();
|
let mut parts = content.split_whitespace();
|
||||||
let load1 = parts
|
let load1 = parts
|
||||||
.next()
|
.next()
|
||||||
.and_then(|s| s.parse::<f64>().ok())
|
.and_then(|s| s.parse::<f64>().ok())
|
||||||
.unwrap_or(0.0);
|
.unwrap_or(0.0);
|
||||||
let load5 = parts
|
let load5 = parts
|
||||||
.next()
|
.next()
|
||||||
.and_then(|s| s.parse::<f64>().ok())
|
.and_then(|s| s.parse::<f64>().ok())
|
||||||
.unwrap_or(0.0);
|
.unwrap_or(0.0);
|
||||||
let load15 = parts
|
let load15 = parts
|
||||||
.next()
|
.next()
|
||||||
.and_then(|s| s.parse::<f64>().ok())
|
.and_then(|s| s.parse::<f64>().ok())
|
||||||
.unwrap_or(0.0);
|
.unwrap_or(0.0);
|
||||||
Ok((load1, load5, load15))
|
Ok((load1, load5, load15))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn hostname() -> std::io::Result<String> {
|
pub fn hostname() -> std::io::Result<String> {
|
||||||
fs::read_to_string("/proc/sys/kernel/hostname").map(|s| s.trim().to_string())
|
fs::read_to_string("/proc/sys/kernel/hostname")
|
||||||
}
|
.map(|s| s.trim().to_string())
|
||||||
|
}
|
||||||
|
|
||||||
pub fn kernel_version() -> std::io::Result<String> {
|
pub fn kernel_version() -> std::io::Result<String> {
|
||||||
fs::read_to_string("/proc/sys/kernel/osrelease").map(|s| s.trim().to_string())
|
fs::read_to_string("/proc/sys/kernel/osrelease")
|
||||||
}
|
.map(|s| s.trim().to_string())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,8 +4,17 @@ pub mod logging;
|
||||||
pub mod scanner;
|
pub mod scanner;
|
||||||
|
|
||||||
pub use config::Config;
|
pub use config::Config;
|
||||||
pub use logging::{DaemonLogEntry, LogLevel, RingBufferLogger};
|
pub use logging::{
|
||||||
|
DaemonLogEntry,
|
||||||
|
LogLevel,
|
||||||
|
RingBufferLogger,
|
||||||
|
};
|
||||||
pub use scanner::{
|
pub use scanner::{
|
||||||
get_scanner, register_scanner, MetricValue, Scanner, ScannerCreatorFfi, ScannerError,
|
get_scanner,
|
||||||
|
register_scanner,
|
||||||
|
MetricValue,
|
||||||
|
Scanner,
|
||||||
|
ScannerCreatorFfi,
|
||||||
|
ScannerError,
|
||||||
};
|
};
|
||||||
pub type Result<T> = std::result::Result<T, ScannerError>;
|
pub type Result<T> = std::result::Result<T, ScannerError>;
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,36 +1,48 @@
|
||||||
use std::collections::HashMap;
|
use std::{
|
||||||
use std::os::raw::c_void;
|
collections::HashMap,
|
||||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
os::raw::c_void,
|
||||||
use std::sync::LazyLock;
|
sync::{
|
||||||
use std::sync::Mutex;
|
atomic::{
|
||||||
use std::time::Duration;
|
AtomicUsize,
|
||||||
|
Ordering,
|
||||||
|
},
|
||||||
|
LazyLock,
|
||||||
|
Mutex,
|
||||||
|
},
|
||||||
|
time::Duration,
|
||||||
|
};
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{
|
||||||
|
Deserialize,
|
||||||
|
Serialize,
|
||||||
|
};
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
pub type ScannerBox = Box<dyn Scanner>;
|
pub type ScannerBox = Box<dyn Scanner>;
|
||||||
pub type ScannerResult = Result<ScannerBox>;
|
pub type ScannerResult = Result<ScannerBox>;
|
||||||
pub type ScannerMetrics = HashMap<String, MetricValue>;
|
pub type ScannerMetrics = HashMap<String, MetricValue>;
|
||||||
pub type ScannerCollectionResult = Result<ScannerMetrics>;
|
pub type ScannerCollectionResult = Result<ScannerMetrics>;
|
||||||
pub type ScannerCollectFn = Box<dyn Fn() -> ScannerCollectionResult + Send + Sync>;
|
pub type ScannerCollectFn =
|
||||||
pub type ScannerInitFnMut = Mutex<Box<dyn FnMut(&toml::Value) -> Result<()> + Send>>;
|
Box<dyn Fn() -> ScannerCollectionResult + Send + Sync>;
|
||||||
|
pub type ScannerInitFnMut =
|
||||||
|
Mutex<Box<dyn FnMut(&toml::Value) -> Result<()> + Send>>;
|
||||||
pub type ScannerCleanupFnMut = Mutex<Box<dyn FnMut() -> Result<()> + Send>>;
|
pub type ScannerCleanupFnMut = Mutex<Box<dyn FnMut() -> Result<()> + Send>>;
|
||||||
pub type ScannerCreatorFfi = unsafe extern "C" fn() -> *mut c_void;
|
pub type ScannerCreatorFfi = unsafe extern fn() -> *mut c_void;
|
||||||
pub type ScannerInitFn = unsafe extern "C" fn() -> *mut c_void;
|
pub type ScannerInitFn = unsafe extern fn() -> *mut c_void;
|
||||||
pub type ScannerDropFn = unsafe extern "C" fn(*mut c_void);
|
pub type ScannerDropFn = unsafe extern fn(*mut c_void);
|
||||||
|
|
||||||
#[derive(Error, Debug)]
|
#[derive(Error, Debug)]
|
||||||
pub enum ScannerError {
|
pub enum ScannerError {
|
||||||
#[error("IO error: {0}")]
|
#[error("IO error: {0}")]
|
||||||
Io(#[from] std::io::Error),
|
Io(#[from] std::io::Error),
|
||||||
#[error("Parse error: {0}")]
|
#[error("Parse error: {0}")]
|
||||||
Parse(String),
|
Parse(String),
|
||||||
#[error("Configuration error: {0}")]
|
#[error("Configuration error: {0}")]
|
||||||
Config(String),
|
Config(String),
|
||||||
#[error("Scanner not initialized")]
|
#[error("Scanner not initialized")]
|
||||||
NotInitialized,
|
NotInitialized,
|
||||||
#[error("Scanner {0} not found")]
|
#[error("Scanner {0} not found")]
|
||||||
NotFound(String),
|
NotFound(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type Result<T> = std::result::Result<T, ScannerError>;
|
pub type Result<T> = std::result::Result<T, ScannerError>;
|
||||||
|
|
@ -38,40 +50,40 @@ pub type Result<T> = std::result::Result<T, ScannerError>;
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
#[serde(untagged)]
|
#[serde(untagged)]
|
||||||
pub enum MetricValue {
|
pub enum MetricValue {
|
||||||
Integer(i64),
|
Integer(i64),
|
||||||
Float(f64),
|
Float(f64),
|
||||||
Boolean(bool),
|
Boolean(bool),
|
||||||
String(String),
|
String(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MetricValue {
|
impl MetricValue {
|
||||||
pub fn from_i64(v: i64) -> Self {
|
pub fn from_i64(v: i64) -> Self {
|
||||||
MetricValue::Integer(v)
|
MetricValue::Integer(v)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_f64(v: f64) -> Self {
|
pub fn from_f64(v: f64) -> Self {
|
||||||
MetricValue::Float(v)
|
MetricValue::Float(v)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_bool(v: bool) -> Self {
|
pub fn from_bool(v: bool) -> Self {
|
||||||
MetricValue::Boolean(v)
|
MetricValue::Boolean(v)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_string(v: impl Into<String>) -> Self {
|
pub fn from_string(v: impl Into<String>) -> Self {
|
||||||
MetricValue::String(v.into())
|
MetricValue::String(v.into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait Scanner: Send + Sync {
|
pub trait Scanner: Send + Sync {
|
||||||
fn name(&self) -> &'static str;
|
fn name(&self) -> &'static str;
|
||||||
fn interval(&self) -> Duration;
|
fn interval(&self) -> Duration;
|
||||||
fn init(&mut self, config: &toml::Value) -> Result<()>;
|
fn init(&mut self, config: &toml::Value) -> Result<()>;
|
||||||
fn collect(&self) -> ScannerCollectionResult;
|
fn collect(&self) -> ScannerCollectionResult;
|
||||||
fn cleanup(&mut self) -> Result<()>;
|
fn cleanup(&mut self) -> Result<()>;
|
||||||
}
|
}
|
||||||
|
|
||||||
static SCANNER_HANDLES: LazyLock<Mutex<HashMap<usize, ScannerBox>>> =
|
static SCANNER_HANDLES: LazyLock<Mutex<HashMap<usize, ScannerBox>>> =
|
||||||
LazyLock::new(|| Mutex::new(HashMap::new()));
|
LazyLock::new(|| Mutex::new(HashMap::new()));
|
||||||
static NEXT_HANDLE: AtomicUsize = AtomicUsize::new(1);
|
static NEXT_HANDLE: AtomicUsize = AtomicUsize::new(1);
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
|
@ -79,9 +91,9 @@ static NEXT_HANDLE: AtomicUsize = AtomicUsize::new(1);
|
||||||
/// # Safety
|
/// # Safety
|
||||||
/// The handle must only be used with `get_scanner` from the same process.
|
/// The handle must only be used with `get_scanner` from the same process.
|
||||||
pub unsafe fn register_scanner(scanner: ScannerBox) -> usize {
|
pub unsafe fn register_scanner(scanner: ScannerBox) -> usize {
|
||||||
let handle = NEXT_HANDLE.fetch_add(1, Ordering::SeqCst);
|
let handle = NEXT_HANDLE.fetch_add(1, Ordering::SeqCst);
|
||||||
SCANNER_HANDLES.lock().unwrap().insert(handle, scanner);
|
SCANNER_HANDLES.lock().unwrap().insert(handle, scanner);
|
||||||
handle
|
handle
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
|
@ -90,65 +102,65 @@ pub unsafe fn register_scanner(scanner: ScannerBox) -> usize {
|
||||||
/// The handle must have been obtained from `register_scanner` in this process,
|
/// The handle must have been obtained from `register_scanner` in this process,
|
||||||
/// and the returned Box must be properly dropped.
|
/// and the returned Box must be properly dropped.
|
||||||
pub unsafe fn get_scanner(handle: *mut c_void) -> Option<ScannerBox> {
|
pub unsafe fn get_scanner(handle: *mut c_void) -> Option<ScannerBox> {
|
||||||
let handle = handle as usize;
|
let handle = handle as usize;
|
||||||
if handle == 0 {
|
if handle == 0 {
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
SCANNER_HANDLES.lock().unwrap().remove(&handle)
|
SCANNER_HANDLES.lock().unwrap().remove(&handle)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait ScannerRegistry: Send + Sync {
|
pub trait ScannerRegistry: Send + Sync {
|
||||||
fn list_scanners(&self) -> Vec<&'static str>;
|
fn list_scanners(&self) -> Vec<&'static str>;
|
||||||
fn get_scanner(&self, name: &str) -> Option<ScannerBox>;
|
fn get_scanner(&self, name: &str) -> Option<ScannerBox>;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct DynamicScanner {
|
pub struct DynamicScanner {
|
||||||
name: &'static str,
|
name: &'static str,
|
||||||
interval: Duration,
|
interval: Duration,
|
||||||
collect_fn: ScannerCollectFn,
|
collect_fn: ScannerCollectFn,
|
||||||
init_fn: ScannerInitFnMut,
|
init_fn: ScannerInitFnMut,
|
||||||
cleanup_fn: ScannerCleanupFnMut,
|
cleanup_fn: ScannerCleanupFnMut,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DynamicScanner {
|
impl DynamicScanner {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
name: &'static str,
|
name: &'static str,
|
||||||
interval: Duration,
|
interval: Duration,
|
||||||
collect_fn: impl Fn() -> ScannerCollectionResult + Send + Sync + 'static,
|
collect_fn: impl Fn() -> ScannerCollectionResult + Send + Sync + 'static,
|
||||||
init_fn: impl FnMut(&toml::Value) -> Result<()> + Send + 'static,
|
init_fn: impl FnMut(&toml::Value) -> Result<()> + Send + 'static,
|
||||||
cleanup_fn: impl FnMut() -> Result<()> + Send + 'static,
|
cleanup_fn: impl FnMut() -> Result<()> + Send + 'static,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
name,
|
name,
|
||||||
interval,
|
interval,
|
||||||
collect_fn: Box::new(collect_fn),
|
collect_fn: Box::new(collect_fn),
|
||||||
init_fn: Mutex::new(Box::new(init_fn)),
|
init_fn: Mutex::new(Box::new(init_fn)),
|
||||||
cleanup_fn: Mutex::new(Box::new(cleanup_fn)),
|
cleanup_fn: Mutex::new(Box::new(cleanup_fn)),
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Scanner for DynamicScanner {
|
impl Scanner for DynamicScanner {
|
||||||
fn name(&self) -> &'static str {
|
fn name(&self) -> &'static str {
|
||||||
self.name
|
self.name
|
||||||
}
|
}
|
||||||
|
|
||||||
fn interval(&self) -> Duration {
|
fn interval(&self) -> Duration {
|
||||||
self.interval
|
self.interval
|
||||||
}
|
}
|
||||||
|
|
||||||
fn init(&mut self, config: &toml::Value) -> Result<()> {
|
fn init(&mut self, config: &toml::Value) -> Result<()> {
|
||||||
let mut init_fn = self.init_fn.lock().unwrap();
|
let mut init_fn = self.init_fn.lock().unwrap();
|
||||||
(init_fn)(config)
|
(init_fn)(config)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn collect(&self) -> ScannerCollectionResult {
|
fn collect(&self) -> ScannerCollectionResult {
|
||||||
(self.collect_fn)()
|
(self.collect_fn)()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn cleanup(&mut self) -> Result<()> {
|
fn cleanup(&mut self) -> Result<()> {
|
||||||
let mut cleanup_fn = self.cleanup_fn.lock().unwrap();
|
let mut cleanup_fn = self.cleanup_fn.lock().unwrap();
|
||||||
(cleanup_fn)()
|
(cleanup_fn)()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
[package]
|
[package]
|
||||||
name = "pscand-macros"
|
name = "pscand-macros"
|
||||||
version.workspace = true
|
version.workspace = true
|
||||||
edition.workspace = true
|
edition.workspace = true
|
||||||
license.workspace = true
|
license.workspace = true
|
||||||
|
|
@ -10,5 +10,5 @@ proc-macro = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
proc-macro2 = "1"
|
proc-macro2 = "1"
|
||||||
quote = "1"
|
quote = "1"
|
||||||
syn = { version = "2", features = ["full"] }
|
syn = { features = [ "full" ], version = "2" }
|
||||||
|
|
|
||||||
|
|
@ -1,64 +1,67 @@
|
||||||
use proc_macro::TokenStream;
|
use proc_macro::TokenStream;
|
||||||
use quote::quote;
|
use quote::quote;
|
||||||
use syn::{parse_macro_input, ItemFn};
|
use syn::{
|
||||||
|
parse_macro_input,
|
||||||
|
ItemFn,
|
||||||
|
};
|
||||||
|
|
||||||
#[proc_macro_attribute]
|
#[proc_macro_attribute]
|
||||||
pub fn scanner(name: TokenStream, input: TokenStream) -> TokenStream {
|
pub fn scanner(name: TokenStream, input: TokenStream) -> TokenStream {
|
||||||
let name_str = parse_macro_input!(name as syn::LitStr).value();
|
let name_str = parse_macro_input!(name as syn::LitStr).value();
|
||||||
let input = parse_macro_input!(input as ItemFn);
|
let input = parse_macro_input!(input as ItemFn);
|
||||||
|
|
||||||
let _fn_name = input.sig.ident.clone();
|
let _fn_name = input.sig.ident.clone();
|
||||||
let body = &input.block;
|
let body = &input.block;
|
||||||
|
|
||||||
let result = quote! {
|
let result = quote! {
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub extern "C" fn pscand_scanner() -> *mut std::os::raw::c_void {
|
pub extern "C" fn pscand_scanner() -> *mut std::os::raw::c_void {
|
||||||
struct ScannerImpl;
|
struct ScannerImpl;
|
||||||
|
|
||||||
impl pscand_core::Scanner for ScannerImpl {
|
impl pscand_core::Scanner for ScannerImpl {
|
||||||
fn name(&self) -> &'static str {
|
fn name(&self) -> &'static str {
|
||||||
#name_str
|
#name_str
|
||||||
}
|
}
|
||||||
|
|
||||||
fn interval(&self) -> std::time::Duration {
|
fn interval(&self) -> std::time::Duration {
|
||||||
std::time::Duration::from_secs(1)
|
std::time::Duration::from_secs(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn init(&mut self, _config: &toml::Value) -> pscand_core::Result<()> {
|
fn init(&mut self, _config: &toml::Value) -> pscand_core::Result<()> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn collect(&self) -> pscand_core::Result<std::collections::HashMap<String, pscand_core::MetricValue>> {
|
fn collect(&self) -> pscand_core::Result<std::collections::HashMap<String, pscand_core::MetricValue>> {
|
||||||
#body
|
#body
|
||||||
}
|
}
|
||||||
|
|
||||||
fn cleanup(&mut self) -> pscand_core::Result<()> {
|
fn cleanup(&mut self) -> pscand_core::Result<()> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let handle = unsafe { pscand_core::register_scanner(Box::new(ScannerImpl)) };
|
let handle = unsafe { pscand_core::register_scanner(Box::new(ScannerImpl)) };
|
||||||
handle as *mut std::os::raw::c_void
|
handle as *mut std::os::raw::c_void
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
result.into()
|
result.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[proc_macro]
|
#[proc_macro]
|
||||||
pub fn register_scanner(input: TokenStream) -> TokenStream {
|
pub fn register_scanner(input: TokenStream) -> TokenStream {
|
||||||
let input = parse_macro_input!(input as ItemFn);
|
let input = parse_macro_input!(input as ItemFn);
|
||||||
let fn_name = input.sig.ident.clone();
|
let fn_name = input.sig.ident.clone();
|
||||||
|
|
||||||
let result = quote! {
|
let result = quote! {
|
||||||
#input
|
#input
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub extern "C" fn pscand_scanner() -> *mut std::os::raw::c_void {
|
pub extern "C" fn pscand_scanner() -> *mut std::os::raw::c_void {
|
||||||
let handle = unsafe { pscand_core::register_scanner(Box::new(#fn_name())) };
|
let handle = unsafe { pscand_core::register_scanner(Box::new(#fn_name())) };
|
||||||
handle as *mut std::os::raw::c_void
|
handle as *mut std::os::raw::c_void
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
result.into()
|
result.into()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
[package]
|
[package]
|
||||||
name = "scanner-power"
|
name = "scanner-power"
|
||||||
version.workspace = true
|
version.workspace = true
|
||||||
edition.workspace = true
|
edition.workspace = true
|
||||||
license.workspace = true
|
license.workspace = true
|
||||||
|
|
@ -7,9 +7,9 @@ authors.workspace = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
pscand-core.workspace = true
|
pscand-core.workspace = true
|
||||||
toml.workspace = true
|
toml.workspace = true
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
name = "scanner_power"
|
crate-type = [ "cdylib" ]
|
||||||
path = "src/lib.rs"
|
name = "scanner_power"
|
||||||
crate-type = ["cdylib"]
|
path = "src/lib.rs"
|
||||||
|
|
|
||||||
|
|
@ -1,96 +1,104 @@
|
||||||
use pscand_core::helpers::PowerHelper;
|
use std::{
|
||||||
use pscand_core::scanner::{MetricValue, Scanner};
|
collections::HashMap,
|
||||||
use std::collections::HashMap;
|
time::Duration,
|
||||||
use std::time::Duration;
|
};
|
||||||
|
|
||||||
|
use pscand_core::{
|
||||||
|
helpers::PowerHelper,
|
||||||
|
scanner::{
|
||||||
|
MetricValue,
|
||||||
|
Scanner,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
struct PowerScanner;
|
struct PowerScanner;
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub extern "C" fn pscand_scanner() -> *mut std::os::raw::c_void {
|
pub extern fn pscand_scanner() -> *mut std::os::raw::c_void {
|
||||||
Box::into_raw(Box::new(PowerScanner)) as *mut std::os::raw::c_void
|
Box::into_raw(Box::new(PowerScanner)) as *mut std::os::raw::c_void
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for PowerScanner {
|
impl Default for PowerScanner {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self
|
Self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Scanner for PowerScanner {
|
impl Scanner for PowerScanner {
|
||||||
fn name(&self) -> &'static str {
|
fn name(&self) -> &'static str {
|
||||||
"power"
|
"power"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn interval(&self) -> Duration {
|
||||||
|
Duration::from_secs(2)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn init(&mut self, _config: &toml::Value) -> pscand_core::Result<()> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn collect(&self) -> pscand_core::Result<HashMap<String, MetricValue>> {
|
||||||
|
let mut metrics = HashMap::new();
|
||||||
|
|
||||||
|
if let Ok(Some(battery)) = PowerHelper::battery_info() {
|
||||||
|
metrics.insert(
|
||||||
|
"battery_present".to_string(),
|
||||||
|
MetricValue::from_bool(battery.present),
|
||||||
|
);
|
||||||
|
metrics.insert(
|
||||||
|
"battery_charge_percent".to_string(),
|
||||||
|
MetricValue::Integer(battery.charge_percent as i64),
|
||||||
|
);
|
||||||
|
metrics.insert(
|
||||||
|
"battery_voltage_v".to_string(),
|
||||||
|
MetricValue::from_f64(battery.voltage),
|
||||||
|
);
|
||||||
|
metrics.insert(
|
||||||
|
"battery_power_now_mw".to_string(),
|
||||||
|
MetricValue::Integer(battery.power_now),
|
||||||
|
);
|
||||||
|
metrics.insert(
|
||||||
|
"battery_status".to_string(),
|
||||||
|
MetricValue::from_string(&battery.status),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn interval(&self) -> Duration {
|
if let Ok(supplies) = PowerHelper::power_supplies() {
|
||||||
Duration::from_secs(2)
|
for (name, info) in supplies {
|
||||||
}
|
if let Some(status) = info.get("status") {
|
||||||
|
metrics.insert(
|
||||||
fn init(&mut self, _config: &toml::Value) -> pscand_core::Result<()> {
|
format!("supply_{}_status", name),
|
||||||
Ok(())
|
MetricValue::from_string(status),
|
||||||
}
|
);
|
||||||
|
|
||||||
fn collect(&self) -> pscand_core::Result<HashMap<String, MetricValue>> {
|
|
||||||
let mut metrics = HashMap::new();
|
|
||||||
|
|
||||||
if let Ok(Some(battery)) = PowerHelper::battery_info() {
|
|
||||||
metrics.insert(
|
|
||||||
"battery_present".to_string(),
|
|
||||||
MetricValue::from_bool(battery.present),
|
|
||||||
);
|
|
||||||
metrics.insert(
|
|
||||||
"battery_charge_percent".to_string(),
|
|
||||||
MetricValue::Integer(battery.charge_percent as i64),
|
|
||||||
);
|
|
||||||
metrics.insert(
|
|
||||||
"battery_voltage_v".to_string(),
|
|
||||||
MetricValue::from_f64(battery.voltage),
|
|
||||||
);
|
|
||||||
metrics.insert(
|
|
||||||
"battery_power_now_mw".to_string(),
|
|
||||||
MetricValue::Integer(battery.power_now),
|
|
||||||
);
|
|
||||||
metrics.insert(
|
|
||||||
"battery_status".to_string(),
|
|
||||||
MetricValue::from_string(&battery.status),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
if let Some(online) = info.get("online") {
|
||||||
if let Ok(supplies) = PowerHelper::power_supplies() {
|
metrics.insert(
|
||||||
for (name, info) in supplies {
|
format!("supply_{}_online", name),
|
||||||
if let Some(status) = info.get("status") {
|
MetricValue::from_bool(online == "1"),
|
||||||
metrics.insert(
|
);
|
||||||
format!("supply_{}_status", name),
|
|
||||||
MetricValue::from_string(status),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if let Some(online) = info.get("online") {
|
|
||||||
metrics.insert(
|
|
||||||
format!("supply_{}_online", name),
|
|
||||||
MetricValue::from_bool(online == "1"),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if let Some(capacity) = info.get("capacity") {
|
|
||||||
if let Ok(cap) = capacity.parse::<u32>() {
|
|
||||||
metrics.insert(
|
|
||||||
format!("supply_{}_capacity", name),
|
|
||||||
MetricValue::Integer(cap as i64),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
if let Some(capacity) = info.get("capacity") {
|
||||||
if let Ok(state) = PowerHelper::suspend_state() {
|
if let Ok(cap) = capacity.parse::<u32>() {
|
||||||
metrics.insert(
|
metrics.insert(
|
||||||
"suspend_state".to_string(),
|
format!("supply_{}_capacity", name),
|
||||||
MetricValue::from_string(&state),
|
MetricValue::Integer(cap as i64),
|
||||||
);
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
Ok(metrics)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn cleanup(&mut self) -> pscand_core::Result<()> {
|
if let Ok(state) = PowerHelper::suspend_state() {
|
||||||
Ok(())
|
metrics.insert(
|
||||||
|
"suspend_state".to_string(),
|
||||||
|
MetricValue::from_string(&state),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Ok(metrics)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cleanup(&mut self) -> pscand_core::Result<()> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
[package]
|
[package]
|
||||||
name = "scanner-proc"
|
name = "scanner-proc"
|
||||||
version.workspace = true
|
version.workspace = true
|
||||||
edition.workspace = true
|
edition.workspace = true
|
||||||
license.workspace = true
|
license.workspace = true
|
||||||
|
|
@ -7,9 +7,9 @@ authors.workspace = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
pscand-core.workspace = true
|
pscand-core.workspace = true
|
||||||
toml.workspace = true
|
toml.workspace = true
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
name = "scanner_proc"
|
crate-type = [ "cdylib" ]
|
||||||
path = "src/lib.rs"
|
name = "scanner_proc"
|
||||||
crate-type = ["cdylib"]
|
path = "src/lib.rs"
|
||||||
|
|
|
||||||
|
|
@ -1,99 +1,107 @@
|
||||||
use pscand_core::helpers::ProcessHelper;
|
use std::{
|
||||||
use pscand_core::scanner::{MetricValue, Scanner};
|
collections::HashMap,
|
||||||
use std::collections::HashMap;
|
time::Duration,
|
||||||
use std::time::Duration;
|
};
|
||||||
|
|
||||||
|
use pscand_core::{
|
||||||
|
helpers::ProcessHelper,
|
||||||
|
scanner::{
|
||||||
|
MetricValue,
|
||||||
|
Scanner,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
struct ProcScanner;
|
struct ProcScanner;
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub extern "C" fn pscand_scanner() -> *mut std::os::raw::c_void {
|
pub extern fn pscand_scanner() -> *mut std::os::raw::c_void {
|
||||||
Box::into_raw(Box::new(ProcScanner)) as *mut std::os::raw::c_void
|
Box::into_raw(Box::new(ProcScanner)) as *mut std::os::raw::c_void
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for ProcScanner {
|
impl Default for ProcScanner {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self
|
Self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Scanner for ProcScanner {
|
impl Scanner for ProcScanner {
|
||||||
fn name(&self) -> &'static str {
|
fn name(&self) -> &'static str {
|
||||||
"proc"
|
"proc"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn interval(&self) -> Duration {
|
||||||
|
Duration::from_secs(5)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn init(&mut self, _config: &toml::Value) -> pscand_core::Result<()> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn collect(&self) -> pscand_core::Result<HashMap<String, MetricValue>> {
|
||||||
|
let mut metrics = HashMap::new();
|
||||||
|
|
||||||
|
if let Ok(counts) = ProcessHelper::process_count() {
|
||||||
|
if let Some(total) = counts.get("total") {
|
||||||
|
metrics.insert(
|
||||||
|
"process_total".to_string(),
|
||||||
|
MetricValue::Integer(*total as i64),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if let Some(running) = counts.get("running") {
|
||||||
|
metrics.insert(
|
||||||
|
"process_running".to_string(),
|
||||||
|
MetricValue::Integer(*running as i64),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if let Some(sleeping) = counts.get("sleeping") {
|
||||||
|
metrics.insert(
|
||||||
|
"process_sleeping".to_string(),
|
||||||
|
MetricValue::Integer(*sleeping as i64),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if let Some(zombie) = counts.get("zombie") {
|
||||||
|
metrics.insert(
|
||||||
|
"process_zombie".to_string(),
|
||||||
|
MetricValue::Integer(*zombie as i64),
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn interval(&self) -> Duration {
|
if let Ok(zombies) = ProcessHelper::zombie_processes() {
|
||||||
Duration::from_secs(5)
|
metrics.insert(
|
||||||
}
|
"zombie_count".to_string(),
|
||||||
|
MetricValue::Integer(zombies.len() as i64),
|
||||||
|
);
|
||||||
|
|
||||||
fn init(&mut self, _config: &toml::Value) -> pscand_core::Result<()> {
|
if !zombies.is_empty() {
|
||||||
Ok(())
|
let mut zombie_info = Vec::new();
|
||||||
}
|
for z in zombies.iter().take(5) {
|
||||||
|
zombie_info.push(format!("{}({})", z.name, z.pid));
|
||||||
fn collect(&self) -> pscand_core::Result<HashMap<String, MetricValue>> {
|
|
||||||
let mut metrics = HashMap::new();
|
|
||||||
|
|
||||||
if let Ok(counts) = ProcessHelper::process_count() {
|
|
||||||
if let Some(total) = counts.get("total") {
|
|
||||||
metrics.insert(
|
|
||||||
"process_total".to_string(),
|
|
||||||
MetricValue::Integer(*total as i64),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if let Some(running) = counts.get("running") {
|
|
||||||
metrics.insert(
|
|
||||||
"process_running".to_string(),
|
|
||||||
MetricValue::Integer(*running as i64),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if let Some(sleeping) = counts.get("sleeping") {
|
|
||||||
metrics.insert(
|
|
||||||
"process_sleeping".to_string(),
|
|
||||||
MetricValue::Integer(*sleeping as i64),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if let Some(zombie) = counts.get("zombie") {
|
|
||||||
metrics.insert(
|
|
||||||
"process_zombie".to_string(),
|
|
||||||
MetricValue::Integer(*zombie as i64),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
metrics.insert(
|
||||||
if let Ok(zombies) = ProcessHelper::zombie_processes() {
|
"zombie_processes".to_string(),
|
||||||
metrics.insert(
|
MetricValue::from_string(zombie_info.join(",")),
|
||||||
"zombie_count".to_string(),
|
);
|
||||||
MetricValue::Integer(zombies.len() as i64),
|
}
|
||||||
);
|
|
||||||
|
|
||||||
if !zombies.is_empty() {
|
|
||||||
let mut zombie_info = Vec::new();
|
|
||||||
for z in zombies.iter().take(5) {
|
|
||||||
zombie_info.push(format!("{}({})", z.name, z.pid));
|
|
||||||
}
|
|
||||||
metrics.insert(
|
|
||||||
"zombie_processes".to_string(),
|
|
||||||
MetricValue::from_string(zombie_info.join(",")),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Ok(top_mem) = ProcessHelper::top_memory_processes(3) {
|
|
||||||
for (i, proc) in top_mem.iter().enumerate() {
|
|
||||||
metrics.insert(
|
|
||||||
format!("top_mem_{}_name", i + 1),
|
|
||||||
MetricValue::from_string(&proc.name),
|
|
||||||
);
|
|
||||||
metrics.insert(
|
|
||||||
format!("top_mem_{}_mb", i + 1),
|
|
||||||
MetricValue::Integer((proc.memory_kb / 1024) as i64),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(metrics)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn cleanup(&mut self) -> pscand_core::Result<()> {
|
if let Ok(top_mem) = ProcessHelper::top_memory_processes(3) {
|
||||||
Ok(())
|
for (i, proc) in top_mem.iter().enumerate() {
|
||||||
|
metrics.insert(
|
||||||
|
format!("top_mem_{}_name", i + 1),
|
||||||
|
MetricValue::from_string(&proc.name),
|
||||||
|
);
|
||||||
|
metrics.insert(
|
||||||
|
format!("top_mem_{}_mb", i + 1),
|
||||||
|
MetricValue::Integer((proc.memory_kb / 1024) as i64),
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Ok(metrics)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cleanup(&mut self) -> pscand_core::Result<()> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
[package]
|
[package]
|
||||||
name = "scanner-sensor"
|
name = "scanner-sensor"
|
||||||
version.workspace = true
|
version.workspace = true
|
||||||
edition.workspace = true
|
edition.workspace = true
|
||||||
license.workspace = true
|
license.workspace = true
|
||||||
|
|
@ -7,9 +7,9 @@ authors.workspace = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
pscand-core.workspace = true
|
pscand-core.workspace = true
|
||||||
toml.workspace = true
|
toml.workspace = true
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
name = "scanner_sensor"
|
crate-type = [ "cdylib" ]
|
||||||
path = "src/lib.rs"
|
name = "scanner_sensor"
|
||||||
crate-type = ["cdylib"]
|
path = "src/lib.rs"
|
||||||
|
|
|
||||||
|
|
@ -1,79 +1,92 @@
|
||||||
use pscand_core::helpers::SensorHelper;
|
use std::{
|
||||||
use pscand_core::scanner::{MetricValue, Scanner};
|
collections::HashMap,
|
||||||
use pscand_core::Result;
|
time::Duration,
|
||||||
use std::collections::HashMap;
|
};
|
||||||
use std::time::Duration;
|
|
||||||
|
use pscand_core::{
|
||||||
|
helpers::SensorHelper,
|
||||||
|
scanner::{
|
||||||
|
MetricValue,
|
||||||
|
Scanner,
|
||||||
|
},
|
||||||
|
Result,
|
||||||
|
};
|
||||||
|
|
||||||
struct SensorScanner;
|
struct SensorScanner;
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub extern "C" fn pscand_scanner() -> *mut std::os::raw::c_void {
|
pub extern fn pscand_scanner() -> *mut std::os::raw::c_void {
|
||||||
Box::into_raw(Box::new(SensorScanner)) as *mut std::os::raw::c_void
|
Box::into_raw(Box::new(SensorScanner)) as *mut std::os::raw::c_void
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for SensorScanner {
|
impl Default for SensorScanner {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self
|
Self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Scanner for SensorScanner {
|
impl Scanner for SensorScanner {
|
||||||
fn name(&self) -> &'static str {
|
fn name(&self) -> &'static str {
|
||||||
"sensor"
|
"sensor"
|
||||||
}
|
}
|
||||||
|
|
||||||
fn interval(&self) -> Duration {
|
fn interval(&self) -> Duration {
|
||||||
Duration::from_secs(2)
|
Duration::from_secs(2)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn init(&mut self, _config: &toml::Value) -> Result<()> {
|
fn init(&mut self, _config: &toml::Value) -> Result<()> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn collect(&self) -> Result<HashMap<String, MetricValue>> {
|
fn collect(&self) -> Result<HashMap<String, MetricValue>> {
|
||||||
let mut metrics = HashMap::new();
|
let mut metrics = HashMap::new();
|
||||||
|
|
||||||
if let Ok(sensors) = SensorHelper::all_sensors() {
|
if let Ok(sensors) = SensorHelper::all_sensors() {
|
||||||
let mut temp_count = 0;
|
let mut temp_count = 0;
|
||||||
let mut fan_count = 0;
|
let mut fan_count = 0;
|
||||||
|
|
||||||
for (hwmon, values) in sensors {
|
for (hwmon, values) in sensors {
|
||||||
for (key, value) in values {
|
for (key, value) in values {
|
||||||
if key.starts_with("temp_") && key.ends_with("_celsius") {
|
if key.starts_with("temp_") && key.ends_with("_celsius") {
|
||||||
if let Ok(v) = value.parse::<f64>() {
|
if let Ok(v) = value.parse::<f64>() {
|
||||||
metrics.insert(format!("{}_{}", hwmon, key), MetricValue::from_f64(v));
|
metrics
|
||||||
temp_count += 1;
|
.insert(format!("{}_{}", hwmon, key), MetricValue::from_f64(v));
|
||||||
if temp_count <= 3 {
|
temp_count += 1;
|
||||||
metrics.insert(
|
if temp_count <= 3 {
|
||||||
format!("temp_{}", temp_count),
|
metrics.insert(
|
||||||
MetricValue::from_f64(v),
|
format!("temp_{}", temp_count),
|
||||||
);
|
MetricValue::from_f64(v),
|
||||||
}
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
if key.starts_with("fan_") && key.ends_with("_rpm") {
|
|
||||||
if let Ok(v) = value.parse::<f64>() {
|
|
||||||
metrics.insert(format!("{}_{}", hwmon, key), MetricValue::from_f64(v));
|
|
||||||
fan_count += 1;
|
|
||||||
if fan_count <= 2 {
|
|
||||||
metrics
|
|
||||||
.insert(format!("fan_{}", fan_count), MetricValue::from_f64(v));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if key.starts_with("voltage_") {
|
|
||||||
if let Ok(v) = value.parse::<f64>() {
|
|
||||||
metrics.insert(format!("{}_{}", hwmon, key), MetricValue::from_f64(v));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
if key.starts_with("fan_") && key.ends_with("_rpm") {
|
||||||
|
if let Ok(v) = value.parse::<f64>() {
|
||||||
|
metrics
|
||||||
|
.insert(format!("{}_{}", hwmon, key), MetricValue::from_f64(v));
|
||||||
|
fan_count += 1;
|
||||||
|
if fan_count <= 2 {
|
||||||
|
metrics.insert(
|
||||||
|
format!("fan_{}", fan_count),
|
||||||
|
MetricValue::from_f64(v),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if key.starts_with("voltage_") {
|
||||||
|
if let Ok(v) = value.parse::<f64>() {
|
||||||
|
metrics
|
||||||
|
.insert(format!("{}_{}", hwmon, key), MetricValue::from_f64(v));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
Ok(metrics)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn cleanup(&mut self) -> Result<()> {
|
Ok(metrics)
|
||||||
Ok(())
|
}
|
||||||
}
|
|
||||||
|
fn cleanup(&mut self) -> Result<()> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
[package]
|
[package]
|
||||||
name = "scanner-system"
|
name = "scanner-system"
|
||||||
version.workspace = true
|
version.workspace = true
|
||||||
edition.workspace = true
|
edition.workspace = true
|
||||||
license.workspace = true
|
license.workspace = true
|
||||||
|
|
@ -7,9 +7,9 @@ authors.workspace = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
pscand-core.workspace = true
|
pscand-core.workspace = true
|
||||||
toml.workspace = true
|
toml.workspace = true
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
name = "scanner_system"
|
crate-type = [ "cdylib" ]
|
||||||
path = "src/lib.rs"
|
name = "scanner_system"
|
||||||
crate-type = ["cdylib"]
|
path = "src/lib.rs"
|
||||||
|
|
|
||||||
|
|
@ -1,80 +1,94 @@
|
||||||
use pscand_core::helpers::{ResourceHelper, SystemHelper};
|
use std::{
|
||||||
use pscand_core::scanner::{MetricValue, Scanner};
|
collections::HashMap,
|
||||||
use pscand_core::Result;
|
time::Duration,
|
||||||
use std::collections::HashMap;
|
};
|
||||||
use std::time::Duration;
|
|
||||||
|
use pscand_core::{
|
||||||
|
helpers::{
|
||||||
|
ResourceHelper,
|
||||||
|
SystemHelper,
|
||||||
|
},
|
||||||
|
scanner::{
|
||||||
|
MetricValue,
|
||||||
|
Scanner,
|
||||||
|
},
|
||||||
|
Result,
|
||||||
|
};
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
struct SystemScanner {
|
struct SystemScanner {
|
||||||
_prev_cpu: Option<HashMap<String, f64>>,
|
_prev_cpu: Option<HashMap<String, f64>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub extern "C" fn pscand_scanner() -> *mut std::os::raw::c_void {
|
pub extern fn pscand_scanner() -> *mut std::os::raw::c_void {
|
||||||
Box::into_raw(Box::new(SystemScanner::default())) as *mut std::os::raw::c_void
|
Box::into_raw(Box::new(SystemScanner::default())) as *mut std::os::raw::c_void
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Scanner for SystemScanner {
|
impl Scanner for SystemScanner {
|
||||||
fn name(&self) -> &'static str {
|
fn name(&self) -> &'static str {
|
||||||
"system"
|
"system"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn interval(&self) -> Duration {
|
||||||
|
Duration::from_secs(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn init(&mut self, _config: &toml::Value) -> Result<()> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn collect(&self) -> Result<HashMap<String, MetricValue>> {
|
||||||
|
let mut metrics = HashMap::new();
|
||||||
|
|
||||||
|
if let Ok(uptime) = SystemHelper::uptime() {
|
||||||
|
metrics.insert(
|
||||||
|
"uptime_secs".to_string(),
|
||||||
|
MetricValue::from_f64(uptime.as_secs_f64()),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn interval(&self) -> Duration {
|
if let Ok((load1, load5, load15)) = SystemHelper::load_average() {
|
||||||
Duration::from_secs(1)
|
metrics.insert("load_1m".to_string(), MetricValue::from_f64(load1));
|
||||||
|
metrics.insert("load_5m".to_string(), MetricValue::from_f64(load5));
|
||||||
|
metrics.insert("load_15m".to_string(), MetricValue::from_f64(load15));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn init(&mut self, _config: &toml::Value) -> Result<()> {
|
if let Ok(cpu) = ResourceHelper::cpu_usage() {
|
||||||
Ok(())
|
if let Some(total) = cpu.get("total_usage_percent") {
|
||||||
|
metrics
|
||||||
|
.insert("cpu_percent".to_string(), MetricValue::from_f64(*total));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn collect(&self) -> Result<HashMap<String, MetricValue>> {
|
if let Ok(mem) = ResourceHelper::memory_info() {
|
||||||
let mut metrics = HashMap::new();
|
if let Some(total) = mem.get("MemTotal") {
|
||||||
|
metrics.insert(
|
||||||
if let Ok(uptime) = SystemHelper::uptime() {
|
"mem_total_bytes".to_string(),
|
||||||
metrics.insert(
|
MetricValue::Integer(*total as i64),
|
||||||
"uptime_secs".to_string(),
|
);
|
||||||
MetricValue::from_f64(uptime.as_secs_f64()),
|
}
|
||||||
);
|
if let Some(available) = mem.get("MemAvailable") {
|
||||||
}
|
metrics.insert(
|
||||||
|
"mem_available_bytes".to_string(),
|
||||||
if let Ok((load1, load5, load15)) = SystemHelper::load_average() {
|
MetricValue::Integer(*available as i64),
|
||||||
metrics.insert("load_1m".to_string(), MetricValue::from_f64(load1));
|
);
|
||||||
metrics.insert("load_5m".to_string(), MetricValue::from_f64(load5));
|
}
|
||||||
metrics.insert("load_15m".to_string(), MetricValue::from_f64(load15));
|
if let (Some(used), Some(total)) =
|
||||||
}
|
(mem.get("MemAvailable"), mem.get("MemTotal"))
|
||||||
|
{
|
||||||
if let Ok(cpu) = ResourceHelper::cpu_usage() {
|
let used_mem = total.saturating_sub(*used);
|
||||||
if let Some(total) = cpu.get("total_usage_percent") {
|
metrics.insert(
|
||||||
metrics.insert("cpu_percent".to_string(), MetricValue::from_f64(*total));
|
"mem_used_bytes".to_string(),
|
||||||
}
|
MetricValue::Integer(used_mem as i64),
|
||||||
}
|
);
|
||||||
|
}
|
||||||
if let Ok(mem) = ResourceHelper::memory_info() {
|
|
||||||
if let Some(total) = mem.get("MemTotal") {
|
|
||||||
metrics.insert(
|
|
||||||
"mem_total_bytes".to_string(),
|
|
||||||
MetricValue::Integer(*total as i64),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if let Some(available) = mem.get("MemAvailable") {
|
|
||||||
metrics.insert(
|
|
||||||
"mem_available_bytes".to_string(),
|
|
||||||
MetricValue::Integer(*available as i64),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if let (Some(used), Some(total)) = (mem.get("MemAvailable"), mem.get("MemTotal")) {
|
|
||||||
let used_mem = total.saturating_sub(*used);
|
|
||||||
metrics.insert(
|
|
||||||
"mem_used_bytes".to_string(),
|
|
||||||
MetricValue::Integer(used_mem as i64),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(metrics)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn cleanup(&mut self) -> Result<()> {
|
Ok(metrics)
|
||||||
Ok(())
|
}
|
||||||
}
|
|
||||||
|
fn cleanup(&mut self) -> Result<()> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue