From b4118364783c7eb2867535a62d8bc0dd45c47d83 Mon Sep 17 00:00:00 2001 From: Nexoniarz Date: Mon, 26 Oct 2026 16:51:39 +0100 Subject: [PATCH] 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 Claude-Session: https://claude.ai/code/session_01EVRhxi9zpdW2uDUN4FVcao --- docs/architecture/desktop.md | 8 ++++---- docs/architecture/udev.md | 28 +++++++++++++++++++++++++++- docs/plan/roadmap.md | 14 ++++++++------ etc/schinit/seatd.job | 17 +++++++++++++++++ kernel/net/unix.c | 6 ++++++ kernel/sys/sys_net.c | 7 +++++++ ports/alpine/world | 14 ++++++++++++++ ports/alpine/world.lock | 11 +++++++++++ 8 files changed, 94 insertions(+), 11 deletions(-) create mode 100644 etc/schinit/seatd.job diff --git a/docs/architecture/desktop.md b/docs/architecture/desktop.md index 0a98624..7108238 100644 --- a/docs/architecture/desktop.md +++ b/docs/architecture/desktop.md @@ -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 diff --git a/docs/architecture/udev.md b/docs/architecture/udev.md index 0a4b3c1..4b871e1 100644 --- a/docs/architecture/udev.md +++ b/docs/architecture/udev.md @@ -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 diff --git a/docs/plan/roadmap.md b/docs/plan/roadmap.md index b9042a4..9f3da6f 100644 --- a/docs/plan/roadmap.md +++ b/docs/plan/roadmap.md @@ -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 diff --git a/etc/schinit/seatd.job b/etc/schinit/seatd.job new file mode 100644 index 0000000..7849386 --- /dev/null +++ b/etc/schinit/seatd.job @@ -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 diff --git a/kernel/net/unix.c b/kernel/net/unix.c index 819bf75..449b3f1 100644 --- a/kernel/net/unix.c +++ b/kernel/net/unix.c @@ -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; diff --git a/kernel/sys/sys_net.c b/kernel/sys/sys_net.c index 8e5456b..44368b1 100644 --- a/kernel/sys/sys_net.c +++ b/kernel/sys/sys_net.c @@ -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; diff --git a/ports/alpine/world b/ports/alpine/world index 76681f7..4fdf5c3 100644 --- a/ports/alpine/world +++ b/ports/alpine/world @@ -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 diff --git a/ports/alpine/world.lock b/ports/alpine/world.lock index dfaaa98..9bdfaa3 100644 --- a/ports/alpine/world.lock +++ b/ports/alpine/world.lock @@ -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