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 <troglobit@gmail.com>
This commit is contained in:
Joachim Wiberg committed 2026-08-16 21:43:26 +02:00
1 parent 8ea299f39d
commit 8cf98a7bfd
3 files changed
+221 -19

No files matched your search

+10 -6
View File
@@ -177,15 +177,19 @@ Built-in Conditions
Finit comes with a set of plugins for conditions: Finit comes with a set of plugins for conditions:
- `devmon` (built-in) - `keventd`: provides `<dev/...>` and `<sys/pwr/...>`
- `netlink` - `devmon` (built-in fallback for `<dev/...>` without keventd)
- `pidfile` - `netlink`: provides `<net/...>`
- `pidfile`: provides `<pid/...>`
- `sys` - `sys`
- `usr` - `usr`
The `devmon` (built-in) plugin monitors `/dev` and `/dev/dir` for device The `dev/` conditions are provided by `keventd`, the built-in device
nodes being created and removed. It is active only when a run, task, or manager. When keventd creates a device node in `/dev`, it also asserts
service has declared a `<dev/foo>` or `<dev/dir/bar>` condition. 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 The `pidfile` plugin recursively watches `/run/` for PID files created
by the monitored services, and sets a corresponding condition in the by the monitored services, and sets a corresponding condition in the
+4 -2
View File
@@ -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 Deprecated, there is both a `modules-load.so` and a `modprobe.so` plugin
that can handle module loading better. The former supports loading from that can handle module loading better. The former supports loading from
`/etc/modules-load.d/`, the latter uses kernel modinfo to automatically `/etc/modules-load.d/`, the latter uses kernel modinfo to automatically
load (or coldplug) every required module. For hotplug we recommend the load (or coldplug) every required module. Hotplug module loading is
BusyBox mdev tool, add to `/etc/mdev.conf`: 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" $MODALIAS=.* root:root 0660 @modprobe -b "$MODALIAS"
+207 -11
View File
@@ -1,14 +1,210 @@
keventd Bundled Device Manager
======= ======================
The kernel event daemon bundled with Finit is a simple uevent monitor The kernel event daemon `keventd` is a built-in device manager bundled
for `/sys/class/power_supply`. It provides the `sys/pwr/ac` condition, with Finit. It replaces the need for external device managers like
which can be useful to prevent power hungry services like anacron to run mdev, mdevd, or udevd on systems where a lighter-weight solution is
when a laptop is only running on battery, for instance. preferred, particularly on embedded systems.
Since keventd is not an integral part of Finit yet it is not enabled by It is enabled by default since Finit v5. To disable it and use an
default. Enable it using `./configure --with-keventd`. The bundled external device manager instead: `./configure --without-keventd`
`contrib/` build scripts for Debian, Alpine, and Void have this enabled.
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/<kernel-version>/<name>`
- `/lib/firmware/updates/<name>`
- `/lib/firmware/<kernel-version>/<name>`
- `/lib/firmware/<name>`
2. Writing `1` to `/sys/<devpath>/loading` to signal start
3. Copying the firmware data to `/sys/<devpath>/data`
4. Writing `0` to `/sys/<devpath>/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 <modalias>` 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