mirror of
https://github.com/troglobit/finit.git
synced 2026-10-19 11:09:35 +00:00
270 lines
9.7 KiB
Markdown
270 lines
9.7 KiB
Markdown
Bundled Device Manager
|
|
======================
|
|
|
|
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.
|
|
|
|
It is enabled by default since Finit v5. To disable it and use an
|
|
external device manager instead:
|
|
|
|
./configure --without-keventd
|
|
|
|
|
|
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
|
|
- **Netlink rebroadcast**: rebroadcasts processed uevents to netlink
|
|
group 0x4 for [libudev-zero][] consumers (enabled by default)
|
|
|
|
|
|
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.
|
|
|
|
|
|
Netlink Rebroadcast
|
|
-------------------
|
|
|
|
The Linux kernel sends uevents to netlink multicast group 1 (bit 0) of
|
|
`NETLINK_KOBJECT_UEVENT`. Only the device manager listens on this raw
|
|
kernel group. Userspace consumers — applications using libudev —
|
|
expect to receive processed events on a separate netlink group.
|
|
|
|
systemd/udevd established the convention of rebroadcasting processed
|
|
events to a separate group, and [libudev-zero][], a daemonless drop-in
|
|
replacement for libudev, listens on group `0x4` for these events.
|
|
Without a device manager rebroadcasting, graphical applications,
|
|
Wayland/X11 compositors, libinput, and anything else using libudev to
|
|
monitor device hotplug will never see any events.
|
|
|
|
keventd rebroadcasts by default to netlink group 4 (`0x4`). A second
|
|
netlink socket is created at startup, and after each uevent has been
|
|
fully processed (device nodes created, symlinks set up, modules loaded),
|
|
the original raw event is sent to the configured group(s).
|
|
Rebroadcasting after processing ensures that device nodes and symlinks
|
|
already exist by the time consumers receive the notification.
|
|
|
|
Use `-g GROUP` to override the default group mask, or `-G` to disable
|
|
rebroadcast entirely. Bit 0 of the group mask is always forced off to
|
|
prevent a feedback loop with the kernel's own multicast group.
|
|
|
|
### Background
|
|
|
|
The netlink uevent architecture uses separate multicast groups to
|
|
isolate the kernel-to-device-manager channel from the device-manager-to-
|
|
application channel:
|
|
|
|
| **Group** | **Bit** | **Purpose** |
|
|
|-----------|---------|---------------------------------------------|
|
|
| 1 | 0 | Kernel events (device manager listens here) |
|
|
| 4 | 2 | Processed events (libudev consumers listen) |
|
|
|
|
This two-group design was established by systemd/udevd and is the de
|
|
facto standard. [mdevd][] implements the same mechanism via its `-O`
|
|
flag, and keventd follows the same convention.
|
|
|
|
For more details, see:
|
|
|
|
- [libudev-zero][] — daemonless replacement for libudev
|
|
- [mdevd][] — mdev-compatible device manager with rebroadcast support
|
|
|
|
|
|
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 [-cdGhnv] [-g GROUP]
|
|
|
|
Options:
|
|
-c Run coldplug at startup
|
|
-d Enable debug mode (foreground, verbose)
|
|
-g GROUP Override netlink rebroadcast group (default: 4)
|
|
-G Disable netlink rebroadcast entirely
|
|
-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
|
|
|
|
# Run without rebroadcast (e.g., headless embedded system)
|
|
keventd -c -G
|
|
|
|
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
|
|
|
|
[libudev-zero]: https://github.com/illiliti/libudev-zero
|
|
[mdevd]: https://skarnet.org/software/mdevd/
|