diff --git a/config/pscand.toml b/config/pscand.toml deleted file mode 100644 index 7c8baee..0000000 --- a/config/pscand.toml +++ /dev/null @@ -1,53 +0,0 @@ -# pscand configuration file -# Place this at /etc/pscand/pscand.toml or ~/.config/pscand/pscand.toml - -# Directories to load scanner plugins from -# Set via PSCAND_SCANNER_DIRS environment variable or configure here -scanner_dirs = [ - # Examples (uncomment and adjust for your system): - # "/usr/lib/pscand/scanners", - # "/var/lib/pscand/scanners", - "~/.local/share/pscand/scanners", -] - -# Where to store log files -log_dir = "/var/log/pscand" - -# Number of recent log entries to keep in memory for crash recovery -ring_buffer_size = 60 - -# Enable logging to systemd journal -journal_enabled = true - -# Enable logging to file -file_enabled = true - -# Log retention in days -retention_days = 7 - -# Per-scanner configuration -[scanners.system] -enabled = true -interval_secs = 5 # Override default 1-second interval - -[scanners.sensor] -enabled = true -interval_secs = 10 # Sensors don't change as fast - -[scanners.power] -enabled = true -interval_secs = 30 # Battery status changes slowly - -[scanners.proc] -enabled = true -interval_secs = 5 - -# Example: Disable a scanner -[scanners.system] -enabled = false - -# Example: Custom scanner with extra parameters -[scanners.custom] -enabled = true -interval_secs = 60 -extra = { custom_param = "value", threshold = 100 } diff --git a/contrib/pscand.example.toml b/contrib/pscand.example.conf similarity index 100% rename from contrib/pscand.example.toml rename to contrib/pscand.example.conf diff --git a/contrib/systemd/pscand.service b/contrib/systemd/pscand.service index b926480..d18b748 100644 --- a/contrib/systemd/pscand.service +++ b/contrib/systemd/pscand.service @@ -5,7 +5,7 @@ After=network.target [Service] Type=simple -ExecStart=pscand run --config /etc/pscand/pscand.toml +ExecStart=/usr/bin/pscand run --config /etc/pscand/pscand.conf Restart=on-failure RestartSec=5 User=root diff --git a/crates/pscand-cli/src/main.rs b/crates/pscand-cli/src/main.rs index ec1c362..a9d0b8b 100644 --- a/crates/pscand-cli/src/main.rs +++ b/crates/pscand-cli/src/main.rs @@ -27,7 +27,7 @@ enum Args { #[derive(Parser, Debug)] struct RunArgs { - #[arg(short, long, default_value = "/etc/pscand/pscand.toml")] + #[arg(short, long, default_value = "/etc/pscand/pscand.conf")] config: PathBuf, #[arg(short, long)] @@ -37,7 +37,6 @@ struct RunArgs { struct LoadedScanner { name: String, scanner: Arc>>, - interval: Duration, #[allow(dead_code)] library: Library, } @@ -284,11 +283,10 @@ async fn run_daemon(args: RunArgs) -> Result<(), Box> { let logger = Arc::clone(&logger_clone); let name = loaded.name.clone(); let scanner = loaded.scanner.clone(); - let scanner_interval = loaded.interval; let state = daemon_state_clone.clone(); let handle = tokio::spawn(async move { - let mut ticker = interval(scanner_interval); + let mut ticker = interval(Duration::from_secs(1)); let _collection_start = Instant::now(); loop { @@ -493,17 +491,9 @@ async fn load_scanners( } } - // Determine interval: config override > scanner default - let interval = config - .scanner_config(&name) - .and_then(|c| c.interval_secs) - .map(Duration::from_secs) - .unwrap_or_else(|| scanner.interval()); - loaded.push(LoadedScanner { name, scanner: Arc::new(RwLock::new(scanner)), - interval, library: lib, }); } diff --git a/docs/README.md b/docs/README.md deleted file mode 100644 index a4c4785..0000000 --- a/docs/README.md +++ /dev/null @@ -1,210 +0,0 @@ -# pscand - -A pluggable system condition monitoring daemon for Linux systems. - -## Overview - -`pscand` (Pluggable System Condition Monitoring Daemon) is a lightweight, -extensible monitoring daemon that collects system metrics through dynamically -loadable scanner plugins. Built with Rust and designed for systemd-based Linux -distributions, it provides real-time monitoring of system resources with minimal -overhead. - -### Motivation - -Sometime after updating to Linux 6.18, my system has started rebooting randomly. -While at first I've assumed this is some kind of hard failure, I have then -noticed that in the system's eyes the shutdown is _entirely graceful_. This lead -me to believe this is some hardware issue, where a certain anomaly prompts the -motherboard to poweroff. To understand what kind of an anomaly is triggering the -reboots, I've created `pscand`. - -It is a pluggable system daemon that collects system metrics through custom -scanner plugins that you load, and provides insight into your system. - -### Features - -- **Modular Architecture**: Scanner plugins are dynamically loaded as shared - libraries (`.so` files) -- **Configurable**: TOML-based configuration with per-scanner settings -- **Plugin System**: Easy to extend with custom scanners -- **Systemd Integration**: Native journal logging and service support -- **Runtime Metrics**: Built-in collection statistics and health monitoring - -## Included Scanners - - - -| Scanner | Description | Metrics Collected | -| ---------------- | -------------------------- | ---------------------------------------------- | -| `scanner-system` | System resource monitoring | CPU, memory, disk, network, load averages | -| `scanner-sensor` | Hardware sensor readings | Temperatures, fan speeds, voltages (via hwmon) | -| `scanner-power` | Power management | Battery status, power supply state | -| `scanner-proc` | Process monitoring | Process states, zombie detection | - - - -## Quick Start - -### Installation - -#### From Source - -```bash -# Clone the repository -$ git clone https://git.frzn.dev/NotAShelf/pscand -$ cd pscand - -# Build the project -$ cargo build --release - -# Install binaries (adjust paths as needed for your system) -$ install -Dm755 target/release/pscand ~/.local/bin/pscand -$ install -Dm644 config/pscand.toml ~/.config/pscand/pscand.toml -``` - -### Systemd Service - -Create `/etc/systemd/system/pscand.service`: - -```ini -[Unit] -Description=Pluggable System Condition Monitoring Daemon -After=network.target - -[Service] -Type=simple -ExecStart=%h/.local/bin/pscand run -Restart=on-failure -RestartSec=5 - -[Install] -WantedBy=multi-user.target -``` - -Then enable and start: - -```bash -sudo systemctl daemon-reload -sudo systemctl enable --now pscand -``` - -### Installing via Nix - -Keep in mind that the **recommended** way of installing pscand is through Nix. A -Nix package is provided, and is designed to work without additional -configuration on first run. To get the daemon functionality, you must use the -NixOS module. - -#### Run directly with `nix run` - -```bash -nix run git+https://git.frzn.dev/NotAShelf/pscand -``` - -#### NixOS Module - -The flake provides a NixOS module at `nixosModules.default`: - -```nix -{ inputs, config, pkgs, ...}: let - - pscandPkg = inputs.pscand.packages.${pkgs.hostPlatform.system}.default; -in { - imports = [inputs.pscand.nixosModules.default]; - services.pscand = { - enable = true; - package = pscandPkg; # or your custom package - }; -} -``` - -This will: - -- Install the `pscand` binary and scanner plugins -- Create and enable a Systemd service -- Configure scanner library paths automatically - -## Usage - -```bash -# Run the daemon with default configuration -pscand run - -# Run with debug logging -pscand run --debug - -# List available built-in scanners -pscand list -``` - -## Configuration - -Configuration is stored in `/etc/pscand/pscand.toml`: - -```toml -[daemon] -log_level = "info" -scanner_dirs = ["/usr/lib/pscand/scanners"] - -[scanners.system] -enabled = true -interval = 5000 # milliseconds - -[scanners.sensor] -enabled = true -interval = 10000 -``` - -### Scanner Directories - -Scanner directories can be configured via: - -1. Config file: `scanner_dirs` array -2. Environment variable: `PSCAND_SCANNER_DIRS` (colon-separated paths) - -Default search paths: - -- `$LIB_PSCAND/scanners` -- `~/.local/share/pscand/scanners` -- `./pscand/scanners` - -## Development - -### Prerequisites - -- Rust 1.90+ (stable toolchain) -- Cargo -- Linux system with systemd - -### Building - -```bash -# Build entire workspace -cargo build - -# Build release (optimized) -cargo build --release - -# Run tests -cargo test - -# Check formatting -cargo fmt --check - -# Run clippy -cargo clippy -- -D warnings -``` - -## Contributing - -Contributions are welcome! Please ensure your code: - -- Follows the existing code style (run `cargo fmt`) -- Passes clippy lints (`cargo clippy -- -D warnings`) -- Includes appropriate tests where applicable -- Maintains backward compatibility for existing scanners - -## License - -This project is licensed under the [Mozilla Public License 2.0](LICENSE). diff --git a/docs/SCANNERS.md b/docs/SCANNERS.md deleted file mode 100644 index 27471c3..0000000 --- a/docs/SCANNERS.md +++ /dev/null @@ -1,60 +0,0 @@ -# Creating a Custom Scanner - -pscand comes with four scanners built-in, but you may easily create your own -scanners for future extensibility. The process is simple. - -1. Create your own crate -2. Implement the `Scanner` trait -3. Build, and place it into a scanner directory - -See below: - -## Creating your scanner crate - -```toml -# scanners/scanner-custom/Cargo.toml -[package] -name = "scanner-custom" -version = "0.1.0" -edition = "2021" - -[lib] -crate-type = ["cdylib"] - -[dependencies] -pscand-core = { workspace = true } -``` - -## Implementing the `Scanner` trait - -```rust -use pscand_core::scanner::Scanner; -use pscand_macros::scanner; - -pub struct CustomScanner; - -impl Scanner for CustomScanner { - fn name(&self) -> &str { - "custom" - } - - fn collect(&self) -> Result> { - // Collect your metrics - Ok(serde_json::json!({ - "value": 42 - })) - } -} - -#[scanner] -static SCANNER: CustomScanner = CustomScanner; -``` - -## Building and installing - -```bash -cargo build --release -# Install to a directory in PSCAND_SCANNER_DIRS (e.g., ~/.local/share/pscand/scanners) -install -Dm755 target/release/libscanner_custom.so \ - ~/.local/share/pscand/scanners/scanner-custom.so -``` diff --git a/nix/modules/nixos.nix b/nix/modules/nixos.nix index 53de554..7df3b81 100644 --- a/nix/modules/nixos.nix +++ b/nix/modules/nixos.nix @@ -6,117 +6,33 @@ self: { }: let inherit (lib.modules) mkIf; inherit (lib.options) mkOption mkEnableOption; - inherit (lib.types) package bool str listOf attrsOf submodule int nullOr anything; + inherit (lib.types) package; cfg = config.services.pscand; - - settingsFormat = pkgs.formats.toml {}; - - configFile = settingsFormat.generate "pscand.toml" { - scanner_dirs = cfg.scannerDirs; - log_dir = cfg.logDir; - ring_buffer_size = cfg.ringBufferSize; - journal_enabled = cfg.journalEnabled; - file_enabled = cfg.fileEnabled; - scanners = - lib.mapAttrs (_: scanner: { - enabled = scanner.enabled; - interval_secs = scanner.interval; - extra = scanner.extra; - }) - cfg.scanners; - }; in { options.services.pscand = { enable = mkEnableOption "Pluggable System Condition Monitoring Daemon"; - package = mkOption { type = package; default = self.packages.${pkgs.hostPlatform.system.pscand}; defaultText = "self.packages.$${pkgs.hostPlatform.system}.pscand"; description = "The pscand package to use"; }; - - scannerDirs = mkOption { - type = listOf str; - default = ["${cfg.package}/lib/pscand/scanners"]; - description = "Directories to load scanner plugins from"; - }; - - logDir = mkOption { - type = str; - default = "/var/log/pscand"; - description = "Directory for log files and heartbeat"; - }; - - ringBufferSize = mkOption { - type = int; - default = 1000; - description = "Number of log entries to keep in memory for crash recovery"; - }; - - journal.enable = mkEnableOption "logging to Systemd journal"; - file.enable = mkEnableOption "logging to file"; - - scanners = mkOption { - type = attrsOf (submodule { - options = { - enabled = mkOption { - type = bool; - default = true; - description = "Whether this scanner is enabled"; - }; - - interval = mkOption { - type = nullOr int; - default = null; - description = "Collection interval in seconds (null = use scanner default)"; - }; - - extra = mkOption { - type = attrsOf anything; - default = {}; - description = "Scanner-specific configuration options"; - }; - }; - }); - default = {}; - description = "Per-scanner configuration settings"; - example = { - system = { - enabled = true; - interval = 5; - }; - sensor = { - enabled = true; - interval = 10; - extra = {sensors = ["coretemp" "nvme"];}; - }; - power = { - enabled = true; - interval = 30; - }; - }; - }; }; config = mkIf cfg.enable { - environment.etc."pscand/pscand.toml".source = configFile; + systemd.packages = [cfg.package]; systemd.services.pscand = { wantedBy = ["multi-user.target"]; after = ["network.target"]; serviceConfig = { Type = "simple"; - ExecStart = "${cfg.package}/bin/pscand run --config /etc/pscand/pscand.toml"; + ExecStart = "${cfg.package}/bin/pscand"; Restart = "on-failure"; RestartSec = "5s"; - Environment = "PSCAND_SCANNER_DIRS=${lib.concatStringsSep ":" cfg.scannerDirs}"; + Environment = ["${cfg.package}/lib/pscand/scanners"]; # FIXME: make this configurable }; }; - - systemd.tmpfiles.rules = [ - "d ${cfg.logDir} 0755 root root -" - ]; }; }