From 8cf98a7bfdd39940eeccd5252ff461344476d9c8 Mon Sep 17 00:00:00 2001 From: Joachim Wiberg Date: Thu, 29 Jan 2026 12:16:33 +0100 Subject: [PATCH] doc: rewrite keventd documentation for unified device manager Rewrite doc/keventd.md from a 14-line stub into comprehensive documentation covering all features of the new unified keventd: device node creation, persistent symlinks, firmware loading, module loading, coldplug, conditions, and command-line usage. Update doc/conditions.md to list keventd as the primary provider of dev/* and sys/pwr/* conditions, with devmon as fallback when an external device manager is used instead. Signed-off-by: Joachim Wiberg --- doc/conditions.md | 16 +-- doc/config/runlevels.md | 6 +- doc/keventd.md | 218 ++++++++++++++++++++++++++++++++++++++-- 3 files changed, 221 insertions(+), 19 deletions(-) diff --git a/doc/conditions.md b/doc/conditions.md index bb425c4e..e61687bb 100644 --- a/doc/conditions.md +++ b/doc/conditions.md @@ -177,15 +177,19 @@ Built-in Conditions Finit comes with a set of plugins for conditions: - - `devmon` (built-in) - - `netlink` - - `pidfile` + - `keventd`: provides `` and `` + - `devmon` (built-in fallback for `` without keventd) + - `netlink`: provides `` + - `pidfile`: provides `` - `sys` - `usr` -The `devmon` (built-in) plugin monitors `/dev` and `/dev/dir` for device -nodes being created and removed. It is active only when a run, task, or -service has declared a `` or `` condition. +The `dev/` conditions are provided by `keventd`, the built-in device +manager. When keventd creates a device node in `/dev`, it also asserts +the corresponding `dev/` condition. When a device is removed, the +condition is cleared. If keventd is not in use (an external device +manager like udevd is used instead), the `devmon` built-in provides the +same conditions by monitoring `/dev` and `/dev/dir` with inotify. The `pidfile` plugin recursively watches `/run/` for PID files created by the monitored services, and sets a corresponding condition in the diff --git a/doc/config/runlevels.md b/doc/config/runlevels.md index 905270ca..fef72efb 100644 --- a/doc/config/runlevels.md +++ b/doc/config/runlevels.md @@ -125,8 +125,10 @@ Load kernel modules, each with optional arguments. Similar to the Deprecated, there is both a `modules-load.so` and a `modprobe.so` plugin that can handle module loading better. The former supports loading from `/etc/modules-load.d/`, the latter uses kernel modinfo to automatically -load (or coldplug) every required module. For hotplug we recommend the -BusyBox mdev tool, add to `/etc/mdev.conf`: +load (or coldplug) every required module. Hotplug module loading is +handled by [keventd](../keventd.md), the built-in device manager. On +systems using the hotplug plugin with BusyBox mdev instead, add to +`/etc/mdev.conf`: $MODALIAS=.* root:root 0660 @modprobe -b "$MODALIAS" diff --git a/doc/keventd.md b/doc/keventd.md index 7651b0d8..67bb5a1b 100644 --- a/doc/keventd.md +++ b/doc/keventd.md @@ -1,14 +1,210 @@ -keventd -======= +Bundled Device Manager +====================== -The kernel event daemon bundled with Finit is a simple uevent monitor -for `/sys/class/power_supply`. It provides the `sys/pwr/ac` condition, -which can be useful to prevent power hungry services like anacron to run -when a laptop is only running on battery, for instance. +The kernel event daemon `keventd` is a built-in device manager bundled +with Finit. It replaces the need for external device managers like +mdev, mdevd, or udevd on systems where a lighter-weight solution is +preferred, particularly on embedded systems. -Since keventd is not an integral part of Finit yet it is not enabled by -default. Enable it using `./configure --with-keventd`. The bundled -`contrib/` build scripts for Debian, Alpine, and Void have this enabled. +It is enabled by default since Finit v5. To disable it and use an +external device manager instead: `./configure --without-keventd` -This daemon is planned to be extended with monitoring of other uevents, -patches and ideas are welcome in the issue tracker. + +Features +-------- + +When started, keventd listens on a `NETLINK_KOBJECT_UEVENT` socket for +kernel events and handles: + +- **Device node creation**: creates and removes `/dev` nodes with + correct permissions on device add/remove events +- **Persistent symlinks**: creates `/dev/disk/by-id/`, `/dev/disk/by-path/`, + and `/dev/input/by-id/`, `/dev/input/by-path/` symlinks for stable + device naming +- **Firmware loading**: responds to kernel firmware requests by searching + `/lib/firmware/` and writing firmware data to sysfs +- **Module loading**: parses `MODALIAS` from uevents and spawns `modprobe` + to load the appropriate kernel module +- **Coldplug**: with the `-c` flag, walks `/sys/devices` and triggers + add events for all devices present at boot +- **Power supply monitoring**: tracks AC power status and provides the + `sys/pwr/ac` condition +- **Device conditions**: sets `dev/*` conditions in the Finit condition + system when device nodes appear or disappear + + +Device Nodes +------------ + +On receiving an `add` event with `MAJOR`, `MINOR`, and `DEVNAME` +fields, keventd creates the corresponding device node in `/dev` using +`mknod()`. Parent directories are created automatically (e.g., +`/dev/input/` for `/dev/input/event0`). + +On `remove` events, the device node and its associated symlinks and +conditions are cleaned up. + +### Default Permissions + +keventd applies permissions based on built-in rules that match on +device subsystem and name: + +| Subsystem | Pattern | Mode | Owner:Group | +|---------------|-------------|--------|----------------| +| block | sd*, vd*, nvme*, mmcblk*, loop*, dm-*, md* | 0660 | root:disk | +| tty | tty[0-9]* | 0620 | root:tty | +| tty | ttyS*, ttyUSB*, ttyACM* | 0660 | root:dialout | +| input | event*, mouse*, mice | 0660 | root:root | +| sound | * | 0660 | root:audio | +| video4linux | * | 0660 | root:video | +| drm | card*, render* | 0660 | root:video | +| (any) | null, zero, full, random, urandom | 0666 | root:root | +| (any) | console | 0600 | root:root | +| (default) | | 0660 | root:root | + + +Persistent Symlinks +------------------- + +For block devices, keventd creates symlinks under `/dev/disk/`: + +- **by-id**: based on the device serial number and model, read from + sysfs attributes (`/sys/.../device/vendor`, `model`, `serial`) +- **by-path**: based on the device topology path + +For input devices, symlinks are created under `/dev/input/`: + +- **by-id**: based on the device name from sysfs +- **by-path**: based on the physical device path + +These symlinks are tracked internally and automatically removed when the +corresponding device is unplugged. + + +Firmware Loading +---------------- + +When a kernel driver requests firmware (via `request_firmware()`), the +kernel sends a uevent with a `FIRMWARE=` field. keventd handles this +by: + +1. Searching for the firmware file in order: + - `/lib/firmware/updates//` + - `/lib/firmware/updates/` + - `/lib/firmware//` + - `/lib/firmware/` +2. Writing `1` to `/sys//loading` to signal start +3. Copying the firmware data to `/sys//data` +4. Writing `0` to `/sys//loading` on success (or `-1` on failure) + +This is particularly important early in boot when drivers for graphics +cards, network adapters, and other hardware need firmware before they +can operate. + + +Module Loading +-------------- + +When a device add event includes a `MODALIAS` field, keventd spawns +`modprobe -bq ` to load the matching kernel module. Module +loading is done asynchronously (keventd does not wait for modprobe to +complete) to avoid blocking other event processing. + + +Coldplug +-------- + +To handle devices that were present before keventd started, it supports +a coldplug mode activated with the `-c` flag. This walks the entire +`/sys/devices` tree and writes `add` to each `uevent` file, causing the +kernel to re-emit add events for all existing devices. + +This replaces the separate `coldplug` script previously used with mdev. + + +Conditions +---------- + +keventd provides conditions in two namespaces: + +### Device Conditions (`dev/`) + +When a device node is created, keventd asserts a corresponding condition +in `/run/finit/cond/dev/`. This allows services to wait for specific +devices: + + service mdadm { + description = "RAID monitor" + runlevel = "2345" + conditions = { "dev/sda" } + command = "/usr/sbin/mdadm --monitor /dev/md0" + } + + service gps-daemon { + description = "GPS daemon" + runlevel = "2345" + conditions = { "dev/ttyUSB0" } + command = "/usr/sbin/gps-daemon" + } + +When the device is removed, the condition is cleared and Finit stops +the dependent services. + +### Power Supply Conditions (`sys/pwr/`) + +keventd monitors the `power_supply` subsystem and provides: + +- `sys/pwr/ac` -- asserted when AC power is connected + +This is useful for preventing power-hungry services from running on +battery: + + service cron { + description = "Cron daemon" + runlevel = "2345" + conditions = { "sys/pwr/ac", "pid/syslogd" } + command = "cron -f" + } + + +Usage +----- + + keventd [-cdhnv] + + Options: + -c Run coldplug at startup + -d Enable debug mode (foreground, verbose) + -h Show help text + -n Run in foreground (no daemon) + -v Show version + +In normal operation, Finit starts keventd automatically via its system +configuration. The `-d` flag is useful for debugging device issues -- +it runs keventd in the foreground and logs all received uevents. + +Debug logging can also be toggled at runtime by sending `SIGUSR1`: + + kill -USR1 $(pidof keventd) + + +Integration with Finit +---------------------- + +keventd is a standalone daemon started by Finit as an internal service. +It communicates with Finit exclusively through the filesystem-based +condition system -- creating and removing symlinks in `/run/finit/cond/`. + +This means keventd can also be tested independently: + + # Run in debug mode to see all kernel events + keventd -d + + # Run with coldplug to populate /dev from scratch + keventd -c -n + +When keventd is enabled, it conflicts with external device managers. +Only one device manager should be active at a time. The system +configuration uses the `conflict:` directive to enforce this: + + service conflict:udevd,mdevd,mdev [...] keventd -c -- Finit device manager