# Sensors

This directory's parent, `sensors/`, contains the executable sensor scripts (called modules before v3.8.0). Each one gathers one kind of system information and prints tab-delimited report lines for the sysinfo client. This file is the authoritative contract for writing a sensor; `template.basic` in this directory is a working example of it.

## Rules for a sensor

### Location and library

- Sensors live in `sensors/` under the client directory, or in any directory listed in the config key `moduleDirs`. Only plain files are run; subdirectories (such as this one) are ignored.
- `library.pm` lives in the client directory, one level above `sensors/`. Sensors find it through `FindBin` (see `template.basic`), which also searches the sensor's own directory.

### Name and permissions

- The file name must begin with an alphanumeric and contain only alphanumerics and underscores. Case does not matter.
- On Unix, a sensor runs only if it is owned by root and its mode is 0700 (execute bit set for root, all other bits 0). Anything else is skipped and recorded as `skipped_permission`.

### Naming: one job per sensor, for one OS

- Name a sensor `<os>_<job>`, for example `linux_network` or `freebsd_pci`. A job may cover a whole report section (uptime, OS values and name are one job).
- When the function is identical on every supported OS, name it `all_<job>`, for example `all_ipmi`. An `all_*` sensor keeps an explicit `checkOS` list of the OSes it has been tested on; adding an OS is a one-line change after testing.
- `linux_*` means any Linux that has the needed tools. A variant tied to one distribution's tools is named for the tool and gates on that command (e.g. `linux_dpkg_software` on `dpkg`), not on a distribution check.
- Code several sensors share goes in `library.pm`, not copied between sensors.

### Check before running

- Each sensor first calls `checkOS` (in `library.pm`) to verify it is meant for the current operating system.
- It then tests that the programs it needs exist, and that the hardware or role it reports on is present. Example: a sensor using `ipmitool` checks for `ipmitool` and for an IPMI device; some machines have `ipmitool` only to manage other hosts.
- If a check fails, it exits with the matching code below without running anything else: wrong OS is 1, and the right OS without the program, device or role is 2. Run nothing (and read nothing) before these checks pass.

### Exit codes

| Code | Meaning | Client records | STDERR |
|---|---|---|---|
| 0 | Everything is fine; results on STDOUT | `ok` | none |
| 1 | Incorrect operating system | `wrong_os` | optional one-line reason |
| 2 | A needed program, device or role is not present on this machine | `not_applicable` | optional one-line reason (e.g. `smartctl not found`) |
| 3 | Not due: a periodic sensor that does not run today (e.g. `all_smart`, monthly) | `not_due` | optional one-line reason |
| 4 and above | The sensor could run but failed | `error` | one line giving the reason |

The client acts only on the exit code. Whatever a sensor prints on STDERR never appears on screen or in cron mail: the client keeps the first line as the sensor's `message` in the report (`sysinfo.sensors.<sensor>.message`), where CAMP can process it later. A reason line is therefore free, and it helps when writing a sensor or finding out why one did not report on a platform: run the sensor by hand and you see it directly. STDOUT is used only when the code is 0.

### Output

- One fact per line: `category<TAB>key…<TAB>value`, 2 to 6 tab-separated fields. The last field is the value; the others are the key path in the report. Example: `system<TAB>kernel<TAB>6.1.0-53-amd64`.
- Never print a line whose value is empty: the parser drops a trailing empty field, which turns the key into the value.
- Output is UTF-8.
- The report format and every key each sensor emits are specified in `Documentation/Spec.md` §1 (in the client directory).

### Writing a new sensor

Copy `template.basic`, change the package name, `$commandsList`, `$osList` and `$CATEGORY`, and put the parsing in subs that take text and return lines, so tests in `t/` can feed them fixture files.

## Script inventory

- linux_system, freebsd_system: uptime and operatingsystem values
- linux_network, freebsd_network: network interface inventory
- all_ipmi: IPMI network and power facts
- linux_pci, freebsd_pci: PCI inventory
- linux_dpkg_software, freebsd_pkg_software: package inventory
- linux_virtual: Linux Xen/libvirt guest inventory emitted under xen.virtual
- all_filesystems: filesystem and mount usage to old diskinfo keys
- linux_storage, freebsd_storage: daily storage section (disks, health, slots, partitions); FreeBSD also the old diskinfo raw-disk keys
- linux_mdadm: daily md arrays (storage.md and old diskinfo md keys); not applicable without an array
- linux_lvm: daily LVM (storage.lvm and old diskinfo LVM keys); not applicable without `lvs` or volumes
- all_zfs: daily ZFS pools (storage.zfs and old diskinfo ZFS keys); not applicable without `zpool` or a pool
- all_smart: monthly (first of the month, or a periodic first run) old SMART attributes keys and storage.disks.*.smart details (rules W and E)
- linux_identity, freebsd_identity: daily identity section
- linux_havirt: daily havirt cluster node and its own guests (havirt_node, havirt_domain); not applicable without havirt or when this host is not a havirt node

Other files in this directory: `template.basic` (sensor template), `smartctl.description` and `checkPercController.pl` (notes and a helper kept from earlier versions).

## Usage

Run a single sensor directly for debugging, from `sensors/` (as root, since that is how the client runs it):

```bash
./linux_system "$(pwd)/"
echo $?
```

Run the full client in test mode to execute discovered sensors and save one report, from the client directory:

```bash
./sysinfo_client --test --config sysinfo_client.yaml
```

For unattended execution, use the main client from cron (daily/periodic) and let it handle discovery, status logging, and transport delivery.
