libinput, seatd and libseat; sockets from accept() can pass descriptors

Alpine's libinput, libseat, seatd, xf86-input-libinput and usbutils join the
image, and seatd runs from schinit. libinput lists the keyboard and pointer as
an ordinary user, Xorg now drives both through libinput, and a program linked
to libseat running as schos opens seat0 through seatd, is handed
/dev/input/event0 and reads its name through the descriptor.

Two kernel bugs stood in the way, and both would have stopped a Wayland
compositor too. The socket connect() queues for a listener was allocated with
its domain at zero, so sendmsg refused SCM_RIGHTS on the accepted end: a server
could receive descriptors and never send one. And sendmsg treated a control
buffer longer than its messages as an error, where Linux takes a following
header too short to be one as the end; seatd passes a zeroed CMSG_SPACE array
with one descriptor at the front.

Found with /proc/strace and a temporary kinfo in sendmsg's EINVAL sites.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EVRhxi9zpdW2uDUN4FVcao
This commit is contained in:
Nexoniarz 2026-10-26 16:51:39 +01:00
commit b411836478
8 changed files with 94 additions and 11 deletions

View file

@ -14,10 +14,10 @@ Wayland needs — udev, `/sys/devices`, libinput, a DRM device — is in [../pla
Every Wayland compositor worth running — labwc, and everything built on wlroots — needs
DRM/KMS, libinput, udev and libseat. DRM/KMS is the largest of them by far: a kernel graphics
subsystem, and in practice a great deal taken from Linux. Xorg runs on a plain framebuffer
through `fbdev` and reads input through `evdev`, both interfaces SchOS already had. That is
the shorter road to a desktop by a long way, and it brings the window managers with it.
DRM/KMS is not planned; libinput, udev and libseat are worth having for their own sake and
may come later.
through `fbdev`, which SchOS already had. That is the shorter road to a desktop by a long way,
and it brings the window managers with it. udev, libinput and libseat are now in the image
([udev.md](udev.md)); DRM/KMS is the part still missing, and it is the GPU plan
([../plan/gpu.md](../plan/gpu.md)).
## What it stands on

View file

@ -80,6 +80,32 @@ and regular files in `/dev` are refused.
A queue that is full drops the message, as Linux does, and `udevadm trigger`
is how a reader recovers.
## libinput and libseat
`libinput` (Alpine) reads the evdev nodes udev tagged and `xf86-input-libinput`
gives Xorg the same library; `libinput list-devices` as an ordinary user shows
`SchOS keyboard` and `SchOS pointer`. Xorg's `40-libinput.conf` now claims both,
and the log says `Using input driver 'libinput'`.
`seatd` (job `etc/schinit/seatd.job`, socket `/run/seatd.sock`, group `video`)
and `libseat` let a program that is not root take devices from the system. The
client asks seatd for `seat0`; seatd takes the VT (`VT_SETMODE`, `KDSETMODE`,
`KDSKBMODE`, all there already), opens the device as root and passes the
descriptor back over the socket with `SCM_RIGHTS`. Checked as `schos` against
`/dev/input/event0` and `event1` with a small program linked to libseat, reading
the device's name through the descriptor it was handed.
Getting there found two kernel bugs, both in descriptor passing:
- **A socket returned by `accept()` was not an `AF_UNIX` socket as far as
`sendmsg` was concerned.** The end `connect()` queues for the listener was
allocated with its domain left at zero, and `sendmsg` refuses `SCM_RIGHTS` on
anything else. So a *server* could receive descriptors and never send one:
seatd, and equally a Wayland compositor handing a client a buffer.
- **A control buffer longer than its messages was an error.** seatd passes a
zeroed array with one descriptor at the front; Linux treats a following header
too short to be one as the end. `sendmsg` now does too.
## What is not there
- USB interfaces (`1-3:1.0`), `/dev/bus/usb` nodes, and PCI bridges as parents.
@ -89,7 +115,7 @@ and regular files in `/dev` are refused.
image, so `60-input-id` logs "returned non-zero" for the hwdb import. The
properties that matter come from `input_id`.
- Block device partitions, `by-id` / `by-uuid` links, and a `/sys/block`.
- `libinput` and `libseat` (next; see [../plan/roadmap.md](../plan/roadmap.md)).
- A compositor. libinput and libseat are in place, DRM is not; see [../plan/roadmap.md](../plan/roadmap.md).
## Testing

View file

@ -62,12 +62,14 @@ compositor worth running is years of work that Alpine already packages.) The des
Wayland compositor (labwc, sway, weston, a GNOME or KDE session) is the larger step. What Wayland — and a modern X — need
from the system, in the order they are useful:
1. **udev — done for the kernel's own devices** ([udev.md](../architecture/udev.md)). eudev runs from `schinit`, the uevent
netlink socket and the `/sys/devices` tree exist, devtmpfs takes udev's links, and Xorg finds its keyboard and mouse
through it. **Left:** PCI and USB devices in the same tree, with `add`/`remove` events from the hotplug thread, so a USB
keyboard or a GPU appears to udev; libinput then enumerates evdev devices through it.
2. **libinput and libseat** (seatd is in Alpine): evdev with the properties libinput reads (`ID_INPUT_*` from udev), and a way
for a non-root compositor to take the keyboard and the display from the kernel — the `VT_PROCESS` handshake exists.
1. **udev — done** ([udev.md](../architecture/udev.md)). eudev runs from `schinit`, the uevent netlink socket and the
`/sys/devices` tree exist, PCI and USB devices are in it with `add`/`remove` events from USB hot-plug, devtmpfs takes udev's
links, and Xorg finds its keyboard and mouse through it. **Left:** USB interfaces, PCI bridges as parents, `hwdb`.
2. **libinput and libseat — done.** `libinput list-devices` sees the keyboard and the pointer as an ordinary user, Xorg now
drives both through `xf86-input-libinput`, and `seatd` runs from `schinit`: a non-root program opens `seat0` through
`libseat`, is handed `/dev/input/event0` over the socket, and reads it. Getting there found a real kernel bug (sockets
returned by `accept()` could not carry file descriptors) and a second one in `sendmsg`'s control-buffer parsing; both would
have stopped a Wayland compositor and its clients too. **Not yet tried:** a compositor.
3. **A DRM device.** Wayland compositors want `/dev/dri/card0` with KMS, and Mesa wants `renderD128`. SchOS has neither: it has
`/dev/fb0`. This is the GPU plan ([gpu.md](gpu.md)) — Linux's own `amdgpu` run by LDLS and `/dev/dri` served by that
process — and, for machines without a real GPU driver, a *simple-framebuffer* DRM shim (Linux's `simpledrm`, which is a

17
etc/schinit/seatd.job Normal file
View file

@ -0,0 +1,17 @@
# The seat daemon: hands a Wayland compositor (through libseat) the input and
# display devices it needs without running the compositor as root. seatd opens
# them as itself and passes the file descriptors over its socket; the socket is
# group `video`, which the first user is in.
#
# Alpine's seatd package. A compositor that runs as root, or one started with
# seatd-launch, needs none of this.
Label = os.schos.seatd
Program = /usr/bin/seatd
Args = seatd -g video
RunAtLoad = yes
KeepAlive = yes
ThrottleInterval = 3
StandardOut = /dev/null
StandardError = /dev/null

View file

@ -1112,6 +1112,12 @@ sockconnect(struct socket *s, uint dev, uint64 ino)
return -ENFILE;
}
// The accepted end is an AF_UNIX socket like the one that connected. It was
// left at domain 0, and nothing minded until a server passed a descriptor
// back through it: sendmsg refuses SCM_RIGHTS on a socket that is not
// AF_UNIX, so seatd could never hand over a device, and a Wayland
// compositor could never have sent its clients a buffer.
peer->domain = AF_UNIX;
peer->peer = s;
peer->state = SS_CONNECTED;
peer->cred_pid = srv->cred_pid;

View file

@ -913,6 +913,13 @@ scm_gather(struct msghdr *m, struct file **fs, int *nfp)
for (off = 0; off + sizeof(struct cmsghdr) <= m->msg_controllen; ) {
c = (struct cmsghdr *)(buf + off);
// The control buffer may be longer than its messages: seatd passes
// the whole of a zeroed CMSG_SPACE(MAX_FDS) array with one descriptor
// in it. Linux's CMSG_NXTHDR treats a following header too short to
// be one as the end, not as an error, and so does this. (A first
// header that short is still wrong.)
if (off > 0 && c->cmsg_len < sizeof(struct cmsghdr))
break;
if (c->cmsg_len < CMSG_LEN(0) || c->cmsg_len > m->msg_controllen - off) {
r = -EINVAL;
goto fail;

View file

@ -65,3 +65,17 @@ udev-init-scripts
# Privilege for ordinary users: setuid doas (opendoas), configured in /etc/doas.d.
doas
# Input and seats for Wayland: libinput turns the evdev nodes udev tagged into
# pointer, keyboard and touch events; libseat hands a compositor its devices
# without making it root, through seatd's socket (or directly when it is root).
# xf86-input-libinput is the same library for Xorg.
libinput
libinput-libs
libinput-udev
libseat
seatd
seatd-launch
xf86-input-libinput
# lsusb that reads /sys/bus/usb/devices, which busybox's does not.
usbutils

View file

@ -67,6 +67,7 @@ libdav1d-1.5.3-r0.apk 40a2b1b92cdcc6fd295934f799ee80ef67e2e384097624d565c6222d82
libdrm-2.4.134-r0.apk 584fd84ad5030040e613fa1816097cd1706ddd542100070852b0eab6e09e12ca main
libeconf-0.8.3-r0.apk 2a791c7957961f830cf31b04f35f814a63afcf633badc9052425ff1cf2f64380 main
libelf-0.195-r0.apk 78c5a1993342ab1bdcdce30639fd0937e8000f6d0d85cea171f573d31decd036 main
libelogind-255.24-r0.apk 5f9d90571f5f097cb1067b73e0ae3b58778b974c67b574b257ecbad09f8c32ef community
libepoxy-1.5.10-r1.apk 0be13b4b524fe64315e0663ab615a244d75d7da73130ac1c630cd6096841444e main
libevdev-1.13.6-r0.apk b38a27a4ac55701fd2eb0032977af2664b6fc23b2f9c2cc6e893ad4452fd82d5 community
libevent-2.1.13-r0.apk 870d4b22a2e4068205b67bb8e4631342c9e84060d052fb36cf4721e18d4043a2 main
@ -77,6 +78,10 @@ libgcc-15.2.0-r5.apk 393dcd32629f06d7d85409c272d142d0c082772d10b87ef55ee82f47de3
libglycin-2.1.5-r0.apk 3541d1a5d4d2599d145904f4febf26e56942403b6d80834461e196636b35b280 community
libice-1.1.2-r0.apk 08daa589d57149b52b00c666b4009212aa3af95021ebdb2a359555c9bcaa76e1 main
libidn2-2.3.8-r0.apk 7d038ac558dde464964b033ce5126dd2eaf84cc414dc15e77c5424a321d0ebee main
libinput-1.31.3-r0.apk fb4fb12c9cea28a3d975ea7f2c7b714cfcabedb4aef6bbf3ff687c08bfc380e4 community
libinput-libs-1.31.3-r0.apk f3b764d72c1913a8c9d781206fd365ecdfe088e0b09009831e7804627f5bd18d community
libinput-udev-1.31.3-r0.apk 716bc1d1ba68ca0c4d41de8923cf88d94946796ed85961a585ae778561ccf06b community
libinput-zsh-completion-1.31.3-r0.apk 2d85a61b395d3a88cbab6cdb433cc9202faaf636267ac29892057cfd3238a45b community
libintl-1.0-r0.apk cb03919cba5624b5e8759d04f3b31913b02585355988b914bc31de4746ff5ef6 main
libjpeg-turbo-3.1.3-r0.apk 6299627dd0a9156a7463511e4915a6294dfb592d8b4bdf85e55265adf1b8ea04 main
libmd-1.2.0-r0.apk f6d68d072fcefc8edd4ffe3afc341ca391f8a9a209130628fb065d98b18a2792 main
@ -87,6 +92,7 @@ libpng-1.6.59-r0.apk d16aeb0597f2bc43e0583ac9ff779388ec160c3e22568b640122e94ba55
libpsl-0.21.5-r3.apk ec665a344b6e887f00f0a47015cc732c3a281b69e84641dc8662ea234fde6cdb main
librsvg-2.62.3-r0.apk e2aeb4a2b6b1ab20627dbb0e9136ea90f7120ec61412ed52f4335ee231361fde community
libsamplerate-0.2.2-r3.apk d47765972e0f4b7c5585a5b27957ffe9bc63d4cc9729134fd876ee5d3f5f986d main
libseat-0.9.3-r0.apk 967ddb2db477b2a30c17a85135db2cb3fcb3f355fa10acfa1c5d6e9c74737fc6 community
libseccomp-2.6.1-r0.apk b22ccfe8cb072e4c8d3bc17222eeda132e270ce6258c27622acb193bf27b48e1 main
libsharpyuv-1.6.0-r0.apk 34621ff53f43030d24d1224fe9cb53b1590ab614cbce6080005cde703e6c874a main
libsm-1.2.6-r0.apk dd7eff58a46fd60e4afee9c7c1c3508920b0e1d6560129e59aee82329f68afc8 main
@ -95,6 +101,7 @@ libssl3-3.5.9-r0.apk 05e3393fb95aa5751ca2f9d242f659f6cff82c1cc7767cc2df4a086f7ad
libstdc++-15.2.0-r5.apk 14c987b556f5385a5db18376e788c75f37d85321b8dc1920d926ea7daac1d6f6 main
libtasn1-4.21.0-r0.apk 407f255c5af989f4e22a5e9404c4558f6fda04f14a4209a2ca1e06c3f2ba476e main
libunistring-1.4.2-r0.apk 3aa6030e603e34d96192742f327c0509f409c6769ce2e7b1463685dede38d714 main
libusb-1.0.30-r0.apk dba6ff9dfcb06f9e9405414c10dc31dd45f73b853528127e6c91dfbe053ac212 main
libuuid-2.42.3-r1.apk 8306e5bb577696c9069fe1dfd9e1dcc39d2d481c6a1b0e707fd03c3e21aa6aa2 main
libwebp-1.6.0-r0.apk dc50c0280348330bb113079331c4a35d85e28351c7baa9268ee346aad8398288 main
libx11-1.8.14-r0.apk 016efc189c6d566061ffe206ed0780e32686c1a8c832a3917d35d2c66d61a40f main
@ -152,6 +159,8 @@ sdl2_image-2.8.12-r0.apk 9d9f337de7fe25c363700701b8dd76ff4026de9c3d977e321ada080
sdl2_mixer-2.8.2-r0.apk 504b68ae9cf6705942853bb6977d3b341615c72066031e5aff54d0c6aca76964 community
sdl2_ttf-2.24.0-r0.apk 0efcd7f03a1c70fc87706b0bb1e95b8747a1b796f089af56f296bf764ac8d988 community
sdl3-3.4.10-r0.apk e9e4537d455f8b710a1f6fed7607c3cdb492fdc155684cf38e1b054bba809427 community
seatd-0.9.3-r0.apk 11216c03221ae8719a7cbff17cda2d6be1f8fef10752556382d14482cf5f15ea community
seatd-launch-0.9.3-r0.apk b403f9a93eec026430bb477aaa7b3dc37bf445b4695b5c415dd28c6333729b67 community
shared-mime-info-2.4-r7.apk b0ed78b502cacfa2dfb6ff87aaef36a034258026873ab5b73f5e603d6623a147 main
sl-5.02-r1.apk aa3ceb845953904ab61459f0f97416903c56a80b428f8bc320d879b9e58c1a57 community
slang-2.3.3-r3.apk 96daf8e02cbcc0d4fd7163d087ae2a71a1aea279a0ac8d2a00a1612ccdccf644 main
@ -161,6 +170,7 @@ tiff-4.7.2-r0.apk 757ce87ebe4923a6be9869d026e250958ca09696529c464781e8a91d2b8167
tmux-3.7c-r0.apk 16f3438f5a24124dd500f70bf6f2a232c9b3dfe5e7f7e615969ac5c00c2c8d0b main
tmux-zsh-completion-5.9-r7.apk e505b2a8c0d56beb65b6f47fd732f6ee6d8d8a72a1a1ea27a6922dad48d9ce89 main
udev-init-scripts-35-r1.apk b707bc50e5c8d2faec08f90e7603b76c2c42dfb0e90e713f82be0f98411d8161 main
usbutils-019-r0.apk 423b81ce888c1a988e5d4b6f114e9fb1dfe3125faf513a4982a8a1b4f7222228 main
util-macros-1.20.2-r0.apk 7c129d085b646b13ccef3738e93038221aec840c1a6764279c5adc9950ba1bdc main
wayland-libs-client-1.25.0-r0.apk a728f7103217cafdc83ff6ad0d679c197dbbe280bd5788c8336259f7a39cb303 main
wayland-libs-cursor-1.25.0-r0.apk 16ec369226507abaeb84b496ce2bb1a4ef29987ea91ba6bedc40bb0e0af274e7 main
@ -171,6 +181,7 @@ xcalc-1.1.3-r0.apk 2f0d9ab5674a5f82f4adfb6dd5f0a9c944421feea2d6aaf18998b0326fe4c
xclock-1.1.1-r0.apk ae2a3cb515664cf34dd9cd0f81945cad75d0b6bbf44696830f29f1ab4713b475 community
xeyes-1.3.1-r0.apk 4b31dabe8d7f5000d82a24445972859bf960c1b07bca3d4f87e80b3c710902c6 community
xf86-input-evdev-2.11.0-r0.apk 1f3fcd3e837b43ad5e32005e03095b547ddbfc25973f0511e9ff74bf342d2fe0 community
xf86-input-libinput-1.5.0-r0.apk 2bd4b9257f755bc68c7460068821e954ee6ca484e6b05637e673d5b9abace0e8 community
xf86-video-fbdev-0.5.0-r6.apk 6a8a1b16f82d8810896f00c00b356c8b8d923e9aa0ce64737ae8274ca82f507a community
xinit-1.4.4-r0.apk efb5dc1e7929824f5add7d5d51d0ecb34ecfd0a773dfca4f061adc8a353b234f community
xkbcomp-1.5.0-r0.apk 8bfbd3c8ce7180e8cf599d5a0b791993b61fa98f3a19c47aaf5fcbe30c12d0ba main