docs: document project motivation, usage and scanner development

Signed-off-by: NotAShelf <raf@notashelf.dev>
Change-Id: Ic762bb0d4b2fa619907a9e4f94278b5f6a6a6964
This commit is contained in:
raf 2026-02-19 00:56:43 +03:00
commit 040d620917
Signed by: NotAShelf
GPG key ID: 29D95B64378DB4BF
2 changed files with 270 additions and 0 deletions

210
docs/README.md Normal file
View file

@ -0,0 +1,210 @@
# 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
<!--markdownlint-disable MD013-->
| 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 |
<!--markdownlint-enable MD013-->
## 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).

60
docs/SCANNERS.md Normal file
View file

@ -0,0 +1,60 @@
# 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<serde_json::Value, Box<dyn std::error::Error>> {
// 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
```