diff --git a/doc/keventd.md b/doc/keventd.md index 67bb5a1b..b6a934ff 100644 --- a/doc/keventd.md +++ b/doc/keventd.md @@ -7,7 +7,9 @@ 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` +external device manager instead: + + ./configure --without-keventd Features @@ -31,6 +33,8 @@ kernel events and handles: `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 @@ -49,18 +53,18 @@ conditions are cleaned up. 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 | +| **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 @@ -122,6 +126,53 @@ 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 ---------- @@ -154,7 +205,7 @@ the dependent services. keventd monitors the `power_supply` subsystem and provides: -- `sys/pwr/ac` -- asserted when AC power is connected +- `sys/pwr/ac` — asserted when AC power is connected This is useful for preventing power-hungry services from running on battery: @@ -170,11 +221,13 @@ battery: Usage ----- - keventd [-cdhnv] + 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 @@ -193,7 +246,7 @@ 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/`. +condition system — creating and removing symlinks in `/run/finit/cond/`. This means keventd can also be tested independently: @@ -203,8 +256,14 @@ This means keventd can also be tested independently: # 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/ diff --git a/man/keventd.8 b/man/keventd.8 index 90d5b983..1eac4ddb 100644 --- a/man/keventd.8 +++ b/man/keventd.8 @@ -13,6 +13,8 @@ .Nm .Op Fl c .Op Fl d +.Op Fl g Ar group +.Op Fl G .Op Fl h .Op Fl n .Op Fl v @@ -53,6 +55,21 @@ Enable debug mode. Implies All received uevents are logged to stderr. .It Fl h Show help text and exit. +.It Fl g Ar group +Override the netlink multicast group mask used for uevent rebroadcast. +The default is 4 (group 0x4), which is the +.Sy libudev-zero +convention. +Bit 0 is always masked out to prevent a feedback loop with the kernel +group. +See +.Sx NETLINK REBROADCAST +below. +.It Fl G +Disable netlink uevent rebroadcast entirely. +See +.Sx NETLINK REBROADCAST +below. .It Fl n Run in foreground, do not daemonize. Log messages are written to stderr instead of syslog. @@ -198,6 +215,40 @@ service cron { command = "cron -f" } .Ed +.Sh NETLINK REBROADCAST +The Linux kernel sends uevents to netlink multicast group 1 (bit 0) of +.Dv NETLINK_KOBJECT_UEVENT . +Only the device manager listens on this raw kernel group. Userspace +consumers such as applications using +.Sy libudev +expect to receive processed events on a separate netlink group. +.Pp +.Sy systemd/udevd +established the convention of rebroadcasting to a separate group, and +.Sy libudev-zero , +a daemonless replacement for libudev, listens on group 0x4 for these +events. Without rebroadcast, graphical applications, Wayland and X11 +compositors, libinput, and other libudev consumers will not receive +device hotplug events. +.Pp +.Nm +rebroadcasts by default to netlink group 4 (bit 2, i.e.\& +.Li 0x4 ) . +A second netlink socket is created at startup, and after each uevent +has been fully processed (device nodes created, modules loaded, +etc.\&), the original event is sent to the configured group. This +ensures that device nodes and symlinks already exist by the time +consumers receive the event. +.Pp +Use +.Fl g +to override the default group mask, or +.Fl G +to disable rebroadcast entirely. +.Pp +See also: +.Lk https://github.com/illiliti/libudev-zero "libudev-zero" , +.Lk https://skarnet.org/software/mdevd/ "mdevd" . .Sh SIGNALS .Bl -tag -width SIGUSR1 .It Dv SIGUSR1