diff --git a/Makefile b/Makefile index f5696b4..70dc3e3 100644 --- a/Makefile +++ b/Makefile @@ -26,7 +26,7 @@ KERN_CORE = $(addprefix $(BUILD)/kernel/core/, \ KERN_DEV = $(addprefix $(BUILD)/kernel/dev/, \ bio.o blkdev.o cdev.o device.o disk.o fb.o ide.o input.o irq.o kbd.o keymap.o mouse.o \ pci.o pciuser.o text.o ttf.o uart.o gfx.o vt.o ramdisk.o procfs.o \ - dbg.o netuser.o audio.o dmi.o pty.o kdev.o) + dbg.o netuser.o audio.o dmi.o pty.o) # Drivers for hardware that has to be *found* rather than assumed. Anything # discovered by walking a bus belongs here; the fixed-address legacy devices diff --git a/README.md b/README.md index e8244db..7150da7 100644 --- a/README.md +++ b/README.md @@ -204,8 +204,8 @@ Another: **syscalls are declared in exactly one place**, - **No GPU acceleration.** Xorg draws in software; the plan is Linux's `amdgpu` through LDLS ([docs/plan/gpu.md](docs/plan/gpu.md)). There is no IOMMU either, so a driver in a process is isolated against its bugs, not against malice. -- **udev sees the kernel's own devices only.** eudev runs and Xorg finds its input devices - through it; PCI and USB devices are not yet in the `/sys/devices` tree. +- **No udev.** Xorg is told its input devices by a config file instead of + finding them; `/sys` has no device tree and no uevent socket. - **WebKit browsers do not start.** NetSurf does; Epiphany's web process dies at start-up, and it has not been run to ground. - **USB 2.0 controllers (EHCI)** have no driver. On older machines the ports diff --git a/docs/architecture/README.md b/docs/architecture/README.md index 2afb526..025fa1f 100644 --- a/docs/architecture/README.md +++ b/docs/architecture/README.md @@ -94,7 +94,6 @@ Read in this order if you are new: | [fat.md](fat.md) | FAT12/16/32 with long names: USB sticks, SD cards, the ESP. | | [devices.md](devices.md) | Buses, classes, binding, and the vtables consumers talk to. | | [usb.md](usb.md) | xHCI, the USB core, and the three class drivers. | -| [udev.md](udev.md) | eudev on SchOS: the device table, the `/sys` tree, uevents, datagram sockets. | | [graphics.md](graphics.md) | Framebuffer, `/dev/fb0`, text rendering, the console, PSOGM and GMMOS. | | [desktop.md](desktop.md) | Xorg on the framebuffer, Window Maker, and what they stand on. | | [packages.md](packages.md) | The apk root: Alpine's packages, and SchOS as one of them. | diff --git a/docs/architecture/desktop.md b/docs/architecture/desktop.md index c5b45e7..92b80c8 100644 --- a/docs/architecture/desktop.md +++ b/docs/architecture/desktop.md @@ -49,9 +49,11 @@ the real program is for. `xterm` runs and exits, and the server stays up. - **`/root/.xinitrc`** is `exec wmaker`. Without it xinit falls back to twm and an xterm, which are not in the image. -- **`/etc/X11/xorg.conf.d/10-schos.conf`** holds what the defaults do not give: Ctrl+Alt+Backspace - enabled, the keyboard options, and the framebuffer driver. Input devices are *found*, not - named: Xorg asks libudev, which reads what `udevd` wrote to `/run/udev` ([udev.md](udev.md)). +- **`/etc/X11/xorg.conf.d/10-schos.conf`** names the input devices. Alpine's Xorg asks udev for + them, SchOS has no udev, and X came up with no keyboard and no mouse: the pointer did not + move and nothing could end the session. SchOS has exactly two input nodes whatever the + machine, so a fixed list is the whole answer (`AutoAddDevices` off, Ctrl+Alt+Backspace + enabled). - **`/etc/asound.conf`** is for sound, not X ([audio.md](audio.md)). ## Using it @@ -69,9 +71,8 @@ through ACPI, and reach X as a mouse: pointer, click and tap. - **No acceleration.** Software rendering to the framebuffer: no DRM, no glamor, no DRI, no OpenGL beyond what software gives. GPU drivers are a plan ([../plan/gpu.md](../plan/gpu.md)). -- **udev covers the kernel's own devices only.** Input, terminals, memory devices, the - framebuffer and disks are announced; PCI and USB devices are in `/sys/bus/*/devices` but are - not yet in the `/sys/devices` tree or on the uevent socket ([udev.md](udev.md)). +- **No udev.** Input devices are named in a config file, not found; `/sys` has no `devices` + tree and no uevent socket. - **Touchpads are mice**: no two-finger scroll, no gestures, no palm rejection. - **No resolution change from inside X.** The mode is the console's; `schmode` changes the `vgpud` head only, and the loader's menu is where a real panel's mode is chosen. diff --git a/docs/architecture/filesystem.md b/docs/architecture/filesystem.md index c60488e..bde8768 100644 --- a/docs/architecture/filesystem.md +++ b/docs/architecture/filesystem.md @@ -275,15 +275,10 @@ at the moment it is read, so a stick shows up in `/sys/bus/usb/devices` as soon as it is attached. Text files report size 0, as `/proc`'s do; binary ones (the tables, `config`) report their real length, since tools copy them by it. -The kernel's own devices (input, tty, mem, graphics, block) have the Linux tree: -`/sys/devices/virtual//` with `dev`, `uevent` and a `subsystem` -link, `/sys/class//` and `/sys/dev/{char,block}/MAJ:MIN` as symlinks -into it, and input capabilities; `uevent` is writable, and a write is announced -on the uevent netlink socket ([udev.md](udev.md)). - -What is missing, compared with Linux: PCI and USB devices in that tree (the +What is missing, compared with Linux: `/sys/devices` and its symlinks (the `bus/*/devices` entries are directories rather than links into it), -`/sys/class` entries for them, `/sys/block`, and most of what is writable. PCI `config` is +`/sys/class` beyond `dmi` and `power_supply`, `/sys/block`, a uevent socket (so +no udev), and anything writable. PCI `config` is 256 bytes, because config space is read through port `0xCF8`, which reaches no further. diff --git a/docs/architecture/networking.md b/docs/architecture/networking.md index a7b9fa5..029e53a 100644 --- a/docs/architecture/networking.md +++ b/docs/architecture/networking.md @@ -649,11 +649,7 @@ information was there and the code did not ask. `SO_BROADCAST`, `TCP_NODELAY` reach lwIP's pcb; `SO_SNDBUF`/`SO_RCVBUF` report the real size; `SO_PASSCRED` on Unix sockets) and refuses the rest with `ENOPROTOOPT`. Notably absent: `SO_RCVTIMEO`/`SO_SNDTIMEO`. -- **`SOCK_DGRAM` and `SOCK_SEQPACKET` over AF_UNIX** keep message boundaries (each message carries a - 16-byte header in the same ring) but have no unconnected form: `socketpair` and listeners work, - `sendto` a path does not. -- **Netlink `NETLINK_KOBJECT_UEVENT`** is the one multicast family: sockets bound to group 1 hear the - kernel's device events, group 2 what udevd re-announces, and a datagram can be addressed to a port id. +- **`SOCK_DGRAM` over AF_UNIX does not exist** — stream only. - **No IPv6**, no firewall or packet filter of any kind, and a TCP stack that has been used for downloads at tens of megabits and no more than that. - **Three drivers in the kernel, one Linux driver outside it**, and the class is diff --git a/docs/architecture/udev.md b/docs/architecture/udev.md deleted file mode 100644 index ddba380..0000000 --- a/docs/architecture/udev.md +++ /dev/null @@ -1,82 +0,0 @@ -# udev - -Alpine's `eudev` runs on SchOS as an ordinary process, started by `schinit` -(`etc/schinit/udevd.job`, with `udev-trigger.job` replaying the devices that -were there first). It is what lets Xorg *find* its keyboard and mouse instead of -being told, and the road to a Wayland compositor starts here: libinput and -libseat both ask udev what exists. - -The kernel's half is three pieces, all small, none of them specific to udev. - -## The device table: `kernel/dev/kdev.c` - -The devices the kernel implements itself — input event nodes, the memory -devices, terminals, the framebuffer, the disks — are listed once, with their -class, name, `/dev` name and parent. Everything below is generated from it. - -The device numbers are the ones `stat()` reports for the node, not SchOS's -internal ones: `/dev/tty1` is `4:1` to a Linux program (`linux_tty_rdev` in -`kernel/sys/sys_fs.c`), and udev compares what it reads in `/sys` with what -`stat()` says about the node. `dev_stat_numbers()` is the one mapping both use. - -## `/sys`: `kernel/fs/sysfs.c` - -``` -/sys/devices/virtual/input/input0/ name capabilities/{ev,key,rel,abs} properties -/sys/devices/virtual/input/input0/event0/ dev uevent subsystem -> ../../../../../class/input -/sys/class/input/event0 -> ../../devices/virtual/input/input0/event0 -/sys/dev/char/13:64 ... -> ../../devices/virtual/... -``` - -Symlinks are real (`T_SYMLINK` inodes whose target is generated). `uevent` is -readable (`DEVPATH=`, `SUBSYSTEM=`, `MAJOR=`, `MINOR=`, `DEVNAME=`) and -writable: `echo add > uevent` — which is what `udevadm trigger` does — makes the -kernel announce the device again. A write to a PCI or USB device's `uevent` is -accepted and says nothing yet, because those devices are not in the -`/sys/devices` tree and an event's `DEVPATH` has to name something that exists. - -The input capability files are what `input_id` reads to decide -`ID_INPUT_KEYBOARD` or `ID_INPUT_MOUSE`. SchOS has two input devices whatever -the machine — every keyboard on `event0`, every mouse on `event1` -(`kernel/dev/input.c`) — so each has a fixed, plausible capability set. - -## `/dev`: `kernel/fs/devfs.c` - -devtmpfs is no longer read-only: udev creates `/dev/char/MAJ:MIN`, `/dev/fd`, -`/dev/input/by-*` and the like. A small in-memory overlay (192 entries) holds -directories and symlinks, listed after the kernel's own nodes in whichever -directory they are in. Device nodes themselves are still the kernel's: `mknod` -and regular files in `/dev` are refused. - -## The sockets: `kernel/net/unix.c` - -- **`AF_UNIX` `SOCK_DGRAM` and `SOCK_SEQPACKET`** keep message boundaries. - udevd's control socket is `SEQPACKET`, and its manager and workers talk over - a `DGRAM` socketpair. A message carries its sender's credentials with it - (several workers share one end), a zero-length datagram is a datagram, and a - datagram socket never reads end-of-stream. -- **`AF_NETLINK` / `NETLINK_KOBJECT_UEVENT`** sockets bind to multicast groups: - group 1 is the kernel's events, group 2 is what udevd re-announces once its - rules have run. A datagram can also be addressed to a port id — udevd hands - an event to an idle worker that way, and the worker believes it only from the - manager's port. The kernel's messages arrive as credentials with uid 0, pid 0. - A queue that is full drops the message, as Linux does, and `udevadm trigger` - is how a reader recovers. - -## What is not there - -- PCI and USB devices in `/sys/devices`, and `add`/`remove` events from the - hotplug thread. A USB keyboard works because every keyboard is `event0`, not - because udev knew it arrived. -- `hwdb` (`/etc/udev/hwdb.bin`): `udevadm hwdb --update` has not been run in the - 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)). - -## Testing - -Boot the ISO and run `udevd --debug &`, then `udevadm trigger; udevadm settle`. -`/run/udev/data/c7:0` should contain -`ID_INPUT_KEYBOARD=1`, and `udevadm info /dev/input/event0` should print the -device. Under X, `/var/log/Xorg.0.log` says `config/udev: Adding input device`. diff --git a/docs/plan/1.0.md b/docs/plan/1.0.md index 9a44833..ee7ab5c 100644 --- a/docs/plan/1.0.md +++ b/docs/plan/1.0.md @@ -114,7 +114,7 @@ it lives. | `mprotect` is a no-op, nothing has NX | [kernel.md](../architecture/kernel.md) | | No routing table | first interface wins, which is a rule and not routing — [networking.md](../architecture/networking.md) | | No descriptor garbage collector | a socket passed over itself and never received leaks the pair | -| udev sees only the kernel's own devices | PCI and USB are not in the `/sys/devices` tree — [udev.md](../architecture/udev.md) | +| No udev / uevent socket | Xorg is told its input devices — [desktop.md](../architecture/desktop.md) | | No EHCI driver | USB 2.0 ports wired only to EHCI do nothing — [usb.md](../architecture/usb.md) | | Touchpads are mice | no two-finger scroll — [desktop.md](../architecture/desktop.md) | | `ru_stime` is zero | [kernel.md](../architecture/kernel.md) | diff --git a/docs/plan/roadmap.md b/docs/plan/roadmap.md index 0ee2c1c..deb7a14 100644 --- a/docs/plan/roadmap.md +++ b/docs/plan/roadmap.md @@ -62,10 +62,11 @@ 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. +1. **udev.** Today Xorg is *told* its input devices (`xorg.conf.d/10-schos.conf`) because nothing announces them. The fix is + Alpine's `eudev` (or `udev`) running on SchOS, which wants: a **uevent netlink socket** (`NETLINK_KOBJECT_UEVENT`, with the + `add`/`remove`/`change` messages and their environment), a real **`/sys/devices` tree** with `/sys/bus/*/devices/*` and + `/sys/class/*/*` as symlinks into it, `uevent` files that can be written to replay events (`udevadm trigger`), and devtmpfs + that udev can create nodes in. 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. 3. **A DRM device.** Wayland compositors want `/dev/dri/card0` with KMS, and Mesa wants `renderD128`. SchOS has neither: it has diff --git a/etc/schinit/udev-trigger.job b/etc/schinit/udev-trigger.job deleted file mode 100644 index aaf57d6..0000000 --- a/etc/schinit/udev-trigger.job +++ /dev/null @@ -1,14 +0,0 @@ -# Replay the devices that were already there when udevd started, so /run/udev -# knows about them. Runs once; see /etc/udev-trigger. -# -# udevd is started by its own job and schinit has no ordering, so the script -# waits for the daemon's control socket. - -Label = os.schos.udev-trigger -Program = /bin/sh -Args = sh /etc/udev-trigger -RunAtLoad = yes -KeepAlive = no - -StandardOut = /dev/tty1 -StandardError = /dev/tty1 diff --git a/etc/schinit/udevd.job b/etc/schinit/udevd.job deleted file mode 100644 index 4e8a55e..0000000 --- a/etc/schinit/udevd.job +++ /dev/null @@ -1,16 +0,0 @@ -# eudev's daemon: the thing that turns the kernel's uevents into /run/udev -# data and /dev symlinks, which is how Xorg and every Wayland compositor learn -# what input and display devices exist. -# -# Alpine's eudev package; the kernel side is the uevent netlink socket and the -# /sys device tree (kernel/dev/kdev.c, kernel/fs/sysfs.c). - -Label = os.schos.udevd -Program = /sbin/udevd -Args = udevd -RunAtLoad = yes -KeepAlive = yes -ThrottleInterval = 3 - -StandardOut = /dev/tty1 -StandardError = /dev/tty1 diff --git a/etc/udev-trigger b/etc/udev-trigger deleted file mode 100644 index 52f13ab..0000000 --- a/etc/udev-trigger +++ /dev/null @@ -1,9 +0,0 @@ -#!/bin/sh -# Wait for udevd to listen, then ask the kernel to announce every device again. -i=0 -while [ ! -S /run/udev/control ] && [ $i -lt 100 ]; do - sleep 0.1 - i=$((i + 1)) -done -udevadm trigger --action=add -udevadm settle --timeout=60 diff --git a/include/core/defs.h b/include/core/defs.h index 6b2fb10..63705ae 100644 --- a/include/core/defs.h +++ b/include/core/defs.h @@ -539,16 +539,6 @@ int socknl_copy(struct socket*, uint, char*, int); void socknl_take(struct socket*, uint); int sock_shutdown(struct socket*, int how); int sockpair(struct socket **a, struct socket **b); -int sockpair_type(struct socket **a, struct socket **b, int type); -int sock_is_packet(struct socket *s); -uint sock_nl_groups(struct socket *s); -int sock_netlink_bind(struct socket *s, uint pid, uint groups); -int sock_uevent_send(struct socket *s, const char *msg, int len, uint dpid, uint dgroups); -uint sock_nl_from(struct socket *s); -uint sock_nl_pid(struct socket *s); -uint sock_nl_bound_groups(struct socket *s); -void dev_stat_numbers(int major, int minor, int *maj, int *min); -void uevent_send(uint groups, const char *msg, int len, struct socket *from); const char* sockname(struct socket*); // AF_INET addressing. Separate from sockbind/sockconnect because the families // do not name the same kind of thing; `addr` and `port` are network order. diff --git a/include/dev/kdev.h b/include/dev/kdev.h deleted file mode 100644 index 6cedaaa..0000000 --- a/include/dev/kdev.h +++ /dev/null @@ -1,44 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -// -// The devices the kernel implements itself, as /sys and udev see them. -// -// /dev holds them already (kernel/fs/devfs.c). This is the other half of what -// udev needs: for each node a place in /sys/devices, the class and dev/ links -// that lead back to it, the facts udev's rules read (MAJOR, MINOR, DEVNAME, -// input capabilities), and a way to announce it on the uevent netlink socket. -#pragma once - -#include "core/types.h" - -#define KDEV_NAME 24 -#define KDEV_DEVNAME 32 -#define KDEV_BLK0 256 // kdev indexes from here are block devices - -enum { KD_NONE, KD_KEYBOARD, KD_MOUSE }; - -struct kdev { - char subsys[16]; // class: input, mem, tty, graphics, block - char name[KDEV_NAME]; // directory name under the parent - char devname[KDEV_DEVNAME]; // path below /dev, "" for a device with no node - short major, minor; - int block; - int parent; // kdev index of the device it hangs off, or -1 - int cap; // KD_*: what an input device can do -}; - -int kdev_nsubsys(void); -const char *kdev_subsys(int s); - -// Index of the Nth device that exists, 0.. until -1. -int kdev_nth(int n); -int kdev_get(int i, struct kdev *k); -// The numbers stat() shows for its node, which are what /sys must show too. -void kdev_numbers(const struct kdev *k, int *maj, int *min); - -// "devices/virtual/input/input0/event0": where it lives below /sys. -int kdev_path(int i, char *buf, int cap); - -// Announce a device on the uevent socket: action is "add", "remove", "change". -void kdev_uevent(int i, const char *action); -// What reading a device's `uevent` file says. -int kdev_uevent_text(int i, char *buf, int cap); diff --git a/include/net/socket.h b/include/net/socket.h index 583f140..027ac1c 100644 --- a/include/net/socket.h +++ b/include/net/socket.h @@ -24,14 +24,10 @@ #define AF_NETLINK 16 #define PF_NETLINK AF_NETLINK #define NETLINK_ROUTE 0 -#define NETLINK_KOBJECT_UEVENT 15 -#define UEVENT_GROUP_KERNEL 1 // what the kernel announces -#define UEVENT_GROUP_UDEV 2 // what udevd re-announces after its rules #define SOCK_STREAM 1 #define SOCK_DGRAM 2 #define SOCK_RAW 3 -#define SOCK_SEQPACKET 5 // Flags ORed into socket()'s and accept4()'s `type`/`flags` argument. Linux's // values, because a musl program passes Linux's constants. @@ -341,15 +337,6 @@ struct socket { char data[SOCKBUF]; uint nread, nwrite; - // AF_NETLINK only: which protocol, and the multicast groups bound. - int nlproto; - uint nlgroups; - uint nlpid; // port id, assigned at bind - uint nllast; // groups of the datagram read last - uint nlfrom; // port id (netlink) or pid (Unix) of its sender - uint pk_uid, pk_gid; // Unix packet: credentials of its sender - int pk_valid; // a packet has been read - // Connections waiting to be accepted, on a listener. struct socket *queue[NBACKLOG]; int nqueue; diff --git a/kernel/dev/kdev.c b/kernel/dev/kdev.c deleted file mode 100644 index 70d3471..0000000 --- a/kernel/dev/kdev.c +++ /dev/null @@ -1,240 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -// -// See include/dev/kdev.h. - -#include "core/types.h" -#include "core/defs.h" -#include "core/param.h" -#include "core/spinlock.h" -#include "core/sleeplock.h" -#include "fs/fs.h" -#include "fs/blkdev.h" -#include "dev/kdev.h" -#include "dev/pty.h" -#include "net/socket.h" - -static const char *const subsys[] = { - "input", "mem", "tty", "graphics", "block", -}; -#define NSUBSYS ((int)(sizeof subsys / sizeof subsys[0])) - -#define NO_PARENT (-1) - -// Majors and minors are the ones /dev reports, devsw indices and all -- the -// devnodes table in kernel/fs/devfs.c. udev compares what it reads here with -// what stat() says about the node, so these have to be the same numbers. -static const struct kdev table[] = { - { "input", "input0", "", 0, 0, 0, NO_PARENT, KD_KEYBOARD }, - { "input", "event0", "input/event0", 7, 0, 0, 0, KD_NONE }, - { "input", "input1", "", 0, 0, 0, NO_PARENT, KD_MOUSE }, - { "input", "event1", "input/event1", 7, 1, 0, 2, KD_NONE }, - - { "mem", "null", "null", 1, 2, 0, NO_PARENT, KD_NONE }, - { "mem", "zero", "zero", 1, 5, 0, NO_PARENT, KD_NONE }, - { "mem", "full", "full", 1, 7, 0, NO_PARENT, KD_NONE }, - { "mem", "random", "random", 1, 8, 0, NO_PARENT, KD_NONE }, - { "mem", "urandom", "urandom", 1, 9, 0, NO_PARENT, KD_NONE }, - - { "tty", "console", "console", 1, 1, 0, NO_PARENT, KD_NONE }, - { "tty", "tty", "tty", 1, 10, 0, NO_PARENT, KD_NONE }, - { "tty", "tty0", "tty0", 1, 0, 0, NO_PARENT, KD_NONE }, - { "tty", "tty1", "tty1", 1, 11, 0, NO_PARENT, KD_NONE }, - { "tty", "tty2", "tty2", 1, 12, 0, NO_PARENT, KD_NONE }, - { "tty", "tty3", "tty3", 1, 13, 0, NO_PARENT, KD_NONE }, - { "tty", "tty4", "tty4", 1, 14, 0, NO_PARENT, KD_NONE }, - { "tty", "tty5", "tty5", 1, 15, 0, NO_PARENT, KD_NONE }, - { "tty", "tty6", "tty6", 1, 16, 0, NO_PARENT, KD_NONE }, - { "tty", "tty7", "tty7", 1, 17, 0, NO_PARENT, KD_NONE }, - { "tty", "ptmx", "ptmx", PTMX_MAJOR, 0, 0, NO_PARENT, KD_NONE }, - - { "graphics", "fb0", "fb0", 2, 0, 0, NO_PARENT, KD_NONE }, -}; -#define NTABLE ((int)(sizeof table / sizeof table[0])) - -int kdev_nsubsys(void) { return NSUBSYS; } -const char *kdev_subsys(int s) { return s >= 0 && s < NSUBSYS ? subsys[s] : 0; } - -int -kdev_nth(int n) -{ - if (n < NTABLE) - return n < 0 ? -1 : n; - n -= NTABLE; - for (int k = 0, b; (b = blkdev_nth(k)) >= 0; k++) - if (n-- == 0) - return KDEV_BLK0 + b; - return -1; -} - -static void -copy(char *d, const char *s, int cap) -{ - int i = 0; - while (s[i] && i < cap - 1) { d[i] = s[i]; i++; } - d[i] = 0; -} - -int -kdev_get(int i, struct kdev *k) -{ - if (i >= 0 && i < NTABLE) { - *k = table[i]; - return 0; - } - if (i >= KDEV_BLK0 && blkdev_present((uint)(i - KDEV_BLK0))) { - memset(k, 0, sizeof(*k)); - copy(k->subsys, "block", sizeof k->subsys); - copy(k->name, blkdev_name((uint)(i - KDEV_BLK0)), sizeof k->name); - copy(k->devname, k->name, sizeof k->devname); - k->major = 8; - k->minor = (short)(i - KDEV_BLK0); - k->block = 1; - k->parent = NO_PARENT; - return 0; - } - return -1; -} - -void -kdev_numbers(const struct kdev *k, int *maj, int *min) -{ - if (k->block) { - *maj = k->major; - *min = k->minor; - return; - } - dev_stat_numbers(k->major, k->minor, maj, min); -} - -int -kdev_path(int i, char *buf, int cap) -{ - struct kdev k; - char tmp[160]; - int n = 0; - - if (kdev_get(i, &k) < 0) - return -1; - // The top ancestor names the subsystem directory; below it, one directory - // per level. - int chain[4], depth = 0; - for (int c = i; c >= 0 && depth < 4; ) { - struct kdev ck; - if (kdev_get(c, &ck) < 0) - break; - chain[depth++] = c; - c = ck.parent; - } - copy(tmp, "devices/virtual/", sizeof tmp); - n = (int)strlen(tmp); - { - struct kdev top; - kdev_get(chain[depth - 1], &top); - copy(tmp + n, top.subsys, (int)sizeof tmp - n); - n += (int)strlen(tmp + n); - } - for (int d = depth - 1; d >= 0 && n < (int)sizeof tmp - 2; d--) { - struct kdev ck; - kdev_get(chain[d], &ck); - tmp[n++] = '/'; - copy(tmp + n, ck.name, (int)sizeof tmp - n); - n += (int)strlen(tmp + n); - } - copy(buf, tmp, cap); - return (int)strlen(buf); -} - -static uint kseq; - -static int -put(char *b, int cap, int n, const char *s) -{ - while (*s && n < cap - 1) - b[n++] = *s++; - return n; -} - -static int -putnum(char *b, int cap, int n, uint v) -{ - char t[12]; - int k = 0; - do { t[k++] = '0' + v % 10; v /= 10; } while (v); - while (k && n < cap - 1) - b[n++] = t[--k]; - return n; -} - -// KEY=value pairs, NUL separated, as the kernel sends them; `nl` swaps the -// NULs for newlines, which is what the uevent file shows. -static int -vars(int i, char *buf, int cap, const char *action, int nl) -{ - struct kdev k; - char path[160]; - int n = 0; - char sep = nl ? '\n' : 0; - - if (kdev_get(i, &k) < 0 || kdev_path(i, path, sizeof path) < 0) - return -1; - if (action) { - n = put(buf, cap, n, "ACTION="); - n = put(buf, cap, n, action); - buf[n++] = sep; - } - n = put(buf, cap, n, "DEVPATH=/"); - n = put(buf, cap, n, path); - buf[n++] = sep; - n = put(buf, cap, n, "SUBSYSTEM="); - n = put(buf, cap, n, k.subsys); - buf[n++] = sep; - if (action) { - n = put(buf, cap, n, "SEQNUM="); - n = putnum(buf, cap, n, ++kseq); - buf[n++] = sep; - } - if (k.devname[0]) { - int maj, min; - kdev_numbers(&k, &maj, &min); - n = put(buf, cap, n, "MAJOR="); - n = putnum(buf, cap, n, (uint)maj); - buf[n++] = sep; - n = put(buf, cap, n, "MINOR="); - n = putnum(buf, cap, n, (uint)min); - buf[n++] = sep; - n = put(buf, cap, n, "DEVNAME="); - n = put(buf, cap, n, k.devname); - buf[n++] = sep; - } - if (k.block) { - n = put(buf, cap, n, "DEVTYPE=disk"); - buf[n++] = sep; - } - return n; -} - -int -kdev_uevent_text(int i, char *buf, int cap) -{ - return vars(i, buf, cap, 0, 1); -} - -void -kdev_uevent(int i, const char *action) -{ - char msg[512]; - char path[160]; - int n = 0; - - if (kdev_path(i, path, sizeof path) < 0) - return; - n = put(msg, sizeof msg, n, action); - msg[n++] = '@'; - n = put(msg, sizeof msg, n, "/"); - n = put(msg, sizeof msg, n, path); - msg[n++] = 0; - int v = vars(i, msg + n, (int)sizeof msg - n, action, 0); - if (v < 0) - return; - uevent_send(UEVENT_GROUP_KERNEL, msg, n + v, 0); -} diff --git a/kernel/fs/devfs.c b/kernel/fs/devfs.c index f802c76..cca520c 100644 --- a/kernel/fs/devfs.c +++ b/kernel/fs/devfs.c @@ -39,7 +39,6 @@ #include "uapi/errno.h" #include "fs/blkdev.h" #include "dev/pty.h" -#include "core/uaccess.h" // Inode numbering. Two directories and a flat table of nodes, so the number is // either a directory id or an index into devnodes[] offset past them. @@ -58,52 +57,6 @@ #define DEVFS_PTS0 8192ULL #define DEVFS_PTM0 12288ULL -// What udev adds to /dev: directories (/dev/input/by-path, /dev/disk/...) and -// symlinks (/dev/fd, /dev/stdin, /dev/char/13:64, /dev/disk/by-id/...). The -// device nodes themselves stay the kernel's; this is a small table of names -// that live only in memory, as they would in a tmpfs, listed after the real -// entries of whichever directory they are in. Nothing here can hold data: a -// regular file in /dev is refused. -#define DEVFS_EXTRA0 32768ULL -#define NEXTRA 192 -#define EXTRA_NAME 48 -#define EXTRA_TARGET 112 - -struct extra { - uchar used; // allocated (an inode exists or is about to) - uchar linked; // has a name in a directory - short type; // T_DIR or T_SYMLINK - uint64 parent; // inum of the directory it is in - ushort tlen; - char name[EXTRA_NAME]; - char target[EXTRA_TARGET]; -}; - -static struct { - struct spinlock lock; - struct extra e[NEXTRA]; -} extras; - -static int extras_ready; - -static void -extras_init(void) -{ - if (!extras_ready) { - initlock(&extras.lock, "devfsx"); - extras_ready = 1; - } -} - -static int -extra_of(uint64 inum) -{ - if (inum < DEVFS_EXTRA0) - return -1; - uint64 i = inum - DEVFS_EXTRA0; - return i < NEXTRA ? (int)i : -1; -} - // Which directory a node lives in. Only two exist and a third would want a real // tree rather than a flag, which is the point at which this file should grow // one instead of adding DEV_DIR_THIRD. @@ -205,21 +158,7 @@ devfs_iload(struct inode *ip) ip->major = ip->minor = 0; ip->size = 0; - int x = extra_of(ip->inum); - if (x >= 0) { - extras_init(); - acquire(&extras.lock); - if (extras.e[x].used) { - ip->type = extras.e[x].type; - ip->nlink = extras.e[x].type == T_DIR ? 2 : 1; - ip->size = extras.e[x].tlen; - ip->mode = extras.e[x].type == T_DIR ? 0755 : 0777; - } else { - ip->type = 0; - ip->nlink = 0; - } - release(&extras.lock); - } else if (ip->inum == DEVFS_ROOT || ip->inum == DEVFS_INPUT || + if (ip->inum == DEVFS_ROOT || ip->inum == DEVFS_INPUT || ip->inum == DEVFS_PTS) { ip->type = T_DIR; ip->nlink = 2; @@ -254,90 +193,16 @@ devfs_iload(struct inode *ip) } static void devfs_istore(struct inode *ip) { (void)ip; } -static void -devfs_ifree(struct inode *ip) -{ - int x = extra_of(ip->inum); - if (x < 0) - return; - extras_init(); - acquire(&extras.lock); - extras.e[x].used = 0; - release(&extras.lock); -} +static void devfs_ifree(struct inode *ip) { (void)ip; } static void devfs_truncate(struct inode *ip, uint64 n) { (void)ip; (void)n; } // Never reached for a device node: fileread and filewrite see T_DEV and go // through devsw. Present because fs_ops has the slots, and answering -EIO is // better than a null pointer if something ever does arrive here. -static int -devfs_read(struct inode *ip, int u, uint64 d, uint64 o, uint n) -{ - int x = extra_of(ip->inum); - // mkdir lays down the "." and ".." of a v1-style directory by reading and - // rewriting its first two entries (build_dir_tree). A directory here has - // none stored, so the read gives zeros and the write is swallowed. - if (x >= 0 && ip->type == T_DIR) { - char z[64]; - memset(z, 0, sizeof z); - for (uint done = 0; done < n; ) { - uint c = n - done < sizeof z ? n - done : (uint)sizeof z; - if (u) { - if (copy_to_user(d + done, z, c) < 0) - return -EFAULT; - } else { - memmove((char *)d + done, z, c); - } - done += c; - } - (void)o; - return (int)n; - } - if (x < 0 || ip->type != T_SYMLINK) - return -1; - extras_init(); - char buf[EXTRA_TARGET]; - acquire(&extras.lock); - uint len = extras.e[x].tlen; - memmove(buf, extras.e[x].target, len); - release(&extras.lock); - if (o >= len) - return 0; - if (o + n > len) - n = (uint)(len - o); - if (u) { - if (copy_to_user(d, buf + o, n) < 0) - return -EFAULT; - } else { - memmove((char *)d, buf + o, n); - } - return (int)n; -} - -// Only a symlink's target can be written, once, from offset 0. -static int -devfs_write(struct inode *ip, int u, uint64 s, uint64 o, uint n) -{ - int x = extra_of(ip->inum); - if (x >= 0 && ip->type == T_DIR) - return (int)n; // see devfs_read - if (x < 0 || ip->type != T_SYMLINK || o != 0 || n >= EXTRA_TARGET) - return -1; - extras_init(); - char buf[EXTRA_TARGET]; - if (u) { - if (copy_from_user(buf, s, n) < 0) - return -EFAULT; - } else { - memmove(buf, (char *)s, n); - } - acquire(&extras.lock); - memmove(extras.e[x].target, buf, n); - extras.e[x].tlen = (ushort)n; - release(&extras.lock); - ip->size = n; - return (int)n; -} +static int devfs_read(struct inode *ip, int u, uint64 d, uint64 o, uint n) +{ (void)ip; (void)u; (void)d; (void)o; (void)n; return -1; } +static int devfs_write(struct inode *ip, int u, uint64 s, uint64 o, uint n) +{ (void)ip; (void)u; (void)s; (void)o; (void)n; return -1; } static int streq(const char *a, const char *b) @@ -346,41 +211,12 @@ streq(const char *a, const char *b) return *a == *b; } -static struct inode * -extra_find(uint64 parent, const char *name) -{ - extras_init(); - acquire(&extras.lock); - for (int i = 0; i < NEXTRA; i++) - if (extras.e[i].used && extras.e[i].linked && extras.e[i].parent == parent && - streq(name, extras.e[i].name)) { - release(&extras.lock); - return iget(DEVFS_DEV, DEVFS_EXTRA0 + (uint64)i); - } - release(&extras.lock); - return 0; -} - static struct inode * devfs_dirlookup(struct inode *dp, char *name, uint *poff) { if (poff) *poff = 0; - int dx = extra_of(dp->inum); - if (dx >= 0) { - extras_init(); - if (streq(name, ".")) - return iget(DEVFS_DEV, dp->inum); - if (streq(name, "..")) { - acquire(&extras.lock); - uint64 up = extras.e[dx].parent; - release(&extras.lock); - return iget(DEVFS_DEV, up ? up : DEVFS_ROOT); - } - return extra_find(dp->inum, name); - } - if (streq(name, ".") || (streq(name, "..") && dp->inum == DEVFS_ROOT)) return iget(DEVFS_DEV, dp->inum); if (streq(name, "..")) @@ -425,85 +261,19 @@ devfs_dirlookup(struct inode *dp, char *name, uint *poff) if (streq(name, blkdev_name((uint)b))) return iget(DEVFS_DEV, DEVFS_BLK0 + (uint64)b); - return extra_find(dp->inum, name); -} - -// What can be created here: a directory or a symlink, by ialloc and then -// dirlink -- the order every filesystem is driven in. The device nodes -// themselves are the kernel's: mknod answers EROFS by never getting an inode. -static struct inode * -devfs_ialloc(uint dev, short type) -{ - (void)dev; - if (type != T_DIR && type != T_SYMLINK) - return 0; - extras_init(); - acquire(&extras.lock); - for (int i = 0; i < NEXTRA; i++) - if (!extras.e[i].used) { - memset(&extras.e[i], 0, sizeof(extras.e[i])); - extras.e[i].used = 1; - extras.e[i].type = type; - release(&extras.lock); - return iget(DEVFS_DEV, DEVFS_EXTRA0 + (uint64)i); - } - release(&extras.lock); return 0; } -static int -devfs_dirlink(struct inode *dp, char *name, uint64 inum) -{ - int x = extra_of(inum); - if (streq(name, ".") || streq(name, "..")) - return 0; /* a new directory's own dots */ - if (x < 0 || strlen(name) >= EXTRA_NAME) - return -EINVAL; - extras_init(); - acquire(&extras.lock); - if (!extras.e[x].used || extras.e[x].linked) { - release(&extras.lock); - return -EINVAL; - } - extras.e[x].parent = dp->inum; - extras.e[x].linked = 1; - memmove(extras.e[x].name, name, strlen(name) + 1); - release(&extras.lock); - return 0; -} - -static int -devfs_dirempty(struct inode *dp) -{ - if (extra_of(dp->inum) < 0) - return 0; - extras_init(); - acquire(&extras.lock); - for (int i = 0; i < NEXTRA; i++) - if (extras.e[i].used && extras.e[i].linked && extras.e[i].parent == dp->inum) { - release(&extras.lock); - return 0; - } - release(&extras.lock); - return 1; -} - -static int -devfs_dirunlink(struct inode *dp, char *name, uint off) -{ - (void)off; - extras_init(); - acquire(&extras.lock); - for (int i = 0; i < NEXTRA; i++) - if (extras.e[i].used && extras.e[i].linked && extras.e[i].parent == dp->inum && - streq(name, extras.e[i].name)) { - extras.e[i].linked = 0; /* the inode goes when nobody holds it */ - release(&extras.lock); - return 0; - } - release(&extras.lock); - return -EROFS; /* a device node is the kernel's */ -} +// A read-only filesystem, and these say so rather than pretending. mknod on +// /dev returning EROFS is the correct answer: the node either exists because +// the kernel has the device, or it does not exist because it has not. +static int devfs_dirlink(struct inode *dp, char *name, uint64 inum) +{ (void)dp; (void)name; (void)inum; return -EROFS; } +static int devfs_dirunlink(struct inode *dp, char *name, uint off) +{ (void)dp; (void)name; (void)off; return -EROFS; } +static int devfs_dirempty(struct inode *dp) { (void)dp; return 0; } +static struct inode *devfs_ialloc(uint dev, short type) +{ (void)dev; (void)type; return 0; } struct linux_dirent64 { uint64 d_ino; @@ -542,9 +312,8 @@ devfs_getdents(struct inode *dp, char *out, int cap, uint64 *poff) int used = 0; uint64 n = *poff; - int isx = extra_of(dp->inum) >= 0; if (dp->inum != DEVFS_ROOT && dp->inum != DEVFS_INPUT && - dp->inum != DEVFS_PTS && !isx) + dp->inum != DEVFS_PTS) return -1; if (n == 0) { @@ -557,25 +326,6 @@ devfs_getdents(struct inode *dp, char *out, int cap, uint64 *poff) n++; } - // A directory udev made: the dots, then what is in it. - if (isx) { - uint64 base = 2; - extras_init(); - acquire(&extras.lock); - for (int i = 0; i < NEXTRA; i++) { - if (!extras.e[i].used || !extras.e[i].linked || extras.e[i].parent != dp->inum) - continue; - if (n > base) { base++; continue; } - if (emit(out, cap, &used, DEVFS_EXTRA0 + (uint64)i, - extras.e[i].type == T_DIR ? 4 : 10 /* DT_LNK */, extras.e[i].name) < 0) - break; - n++; - base++; - } - release(&extras.lock); - goto out; - } - // /dev/pts: the dots, then the pairs that exist. Ordinal 2 + N is pair N, // whether or not the ones before it exist, so a pair opened between two // calls does not shift the rest. @@ -628,21 +378,6 @@ devfs_getdents(struct inode *dp, char *out, int cap, uint64 *poff) base++; } - // What udev added to this directory, after everything the kernel put there. - extras_init(); - acquire(&extras.lock); - for (int i = 0; i < NEXTRA; i++) { - if (!extras.e[i].used || !extras.e[i].linked || extras.e[i].parent != dp->inum) - continue; - if (n > base) { base++; continue; } - if (emit(out, cap, &used, DEVFS_EXTRA0 + (uint64)i, - extras.e[i].type == T_DIR ? 4 : 10, extras.e[i].name) < 0) - break; - n++; - base++; - } - release(&extras.lock); - out: *poff = n; return used; diff --git a/kernel/fs/sysfs.c b/kernel/fs/sysfs.c index 796d6a4..6718e28 100644 --- a/kernel/fs/sysfs.c +++ b/kernel/fs/sysfs.c @@ -45,7 +45,6 @@ #include "core/hwinfo.h" #include "gfx/gfx.h" #include "dev/acpi.h" -#include "dev/kdev.h" // --------------------------------------------------------------------------- // Inode numbers: kind, index, file -- the whole identity, since iget gives @@ -60,16 +59,6 @@ #define K_DMIID 6ULL // file a DMI_* field + 1 #define K_DMITAB 7ULL // file 1 the entry point, 2 the table #define K_PSU 8ULL // idx an acpi_psu_get() index; file a psufiles[] index -#define K_KDEV 9ULL // idx a kdev index; file 0 the directory, KF_* its files -#define K_KSUBV 10ULL // idx a kdev subsystem: /sys/devices/virtual/ -#define K_KCLASS 11ULL // idx a kdev subsystem: /sys/class/ -#define K_KCLINK 12ULL // idx a kdev: the symlink in /sys/class/ -#define K_KDEVD 13ULL // idx 0 char, 1 block: /sys/dev/char, /sys/dev/block -#define K_KDEVL 14ULL // idx a kdev: the "MAJ:MIN" symlink in /sys/dev/* - -// Files in a kdev directory (K_KDEV). -enum { KF_DIR, KF_DEV, KF_UEVENT, KF_SUBSYSTEM, KF_NAME, KF_PROPERTIES, - KF_CAPS, KF_EV, KF_KEY, KF_REL, KF_ABS, KF_N }; #define INO(k, idx, f) (((uint64)(k) << 40) | ((uint64)(idx) << 8) | (uint64)(f)) #define I_KIND(i) ((int)((i) >> 40)) @@ -79,7 +68,6 @@ enum { KF_DIR, KF_DEV, KF_UEVENT, KF_SUBSYSTEM, KF_NAME, KF_PROPERTIES, enum { D_ROOT, D_BUS, D_PCI, D_PCIDEV, D_USB, D_USBDEV, D_FW, D_ACPI, D_ACPITAB, D_DMI, D_DMITAB, D_MEMMAP, D_EFI, D_CLASS, D_CLASSDMI, D_DMIID, D_PSU, - D_DEVICES, D_VIRTUAL, D_DEVT, D_NDIRS }; @@ -101,9 +89,6 @@ static const struct { const char *name; int parent; } dirs[D_NDIRS] = { [D_CLASSDMI] = { "dmi", D_CLASS }, [D_DMIID] = { "id", D_CLASSDMI }, [D_PSU] = { "power_supply", D_CLASS }, - [D_DEVICES] = { "devices", D_ROOT }, - [D_VIRTUAL] = { "virtual", D_DEVICES }, - [D_DEVT] = { "dev", D_ROOT }, }; #define SYSFS_ROOT INO(K_DIR, D_ROOT, 0) @@ -402,96 +387,6 @@ gen_psu(struct out *o, int idx, int f) } } -// ---- kernel-implemented devices (include/dev/kdev.h) ---- - -static int -kd_subsys_of(const struct kdev *k) -{ - for (int s = 0; s < kdev_nsubsys(); s++) { - const char *n = kdev_subsys(s), *m = k->subsys; - while (*n && *n == *m) { n++; m++; } - if (*n == *m) - return s; - } - return -1; -} - -static void -kd_devt(const struct kdev *k, char *name) -{ - struct out o = { name, 0, 30 }; - int maj, min; - kdev_numbers(k, &maj, &min); - putu(&o, (uint64)maj); - putc_(&o, ':'); - putu(&o, (uint64)min); - name[o.n] = 0; -} - -// Does file f exist in kdev k's directory? -static int -kd_has(const struct kdev *k, int f) -{ - switch (f) { - case KF_DEV: return k->devname[0] != 0; - case KF_UEVENT: - case KF_SUBSYSTEM: return 1; - case KF_NAME: case KF_PROPERTIES: case KF_CAPS: - return k->cap != KD_NONE; - } - return 0; -} - -static int -kd_children(uint64 dino, child_fn cb, void *ctx) -{ - int kind = I_KIND(dino), idx = I_IDX(dino); - char name[32]; - struct kdev k; - - if (kind == K_KSUBV) { - for (int n = 0, i; (i = kdev_nth(n)) >= 0; n++) - if (kdev_get(i, &k) == 0 && k.parent < 0 && kd_subsys_of(&k) == idx && - cb(ctx, INO(K_KDEV, i, 0), 1, k.name)) - return 1; - } else if (kind == K_KCLASS) { - for (int n = 0, i; (i = kdev_nth(n)) >= 0; n++) - if (kdev_get(i, &k) == 0 && kd_subsys_of(&k) == idx && - cb(ctx, INO(K_KCLINK, i, 0), 2, k.name)) - return 1; - } else if (kind == K_KDEVD) { - for (int n = 0, i; (i = kdev_nth(n)) >= 0; n++) - if (kdev_get(i, &k) == 0 && k.devname[0] && k.block == idx) { - kd_devt(&k, name); - if (cb(ctx, INO(K_KDEVL, i, 0), 2, name)) - return 1; - } - } else if (kind == K_KDEV && I_FILE(dino) == KF_DIR) { - if (kdev_get(idx, &k) < 0) - return 0; - for (int f = KF_DEV; f <= KF_CAPS; f++) - if (kd_has(&k, f)) { - static const char *const names[] = { 0, "dev", "uevent", "subsystem", - "name", "properties", "capabilities" }; - if (cb(ctx, INO(K_KDEV, idx, f), - f == KF_CAPS ? 1 : f == KF_SUBSYSTEM ? 2 : 0, names[f])) - return 1; - } - for (int n = 0, i; (i = kdev_nth(n)) >= 0; n++) { - struct kdev c; - if (kdev_get(i, &c) == 0 && c.parent == idx && - cb(ctx, INO(K_KDEV, i, 0), 1, c.name)) - return 1; - } - } else if (kind == K_KDEV && I_FILE(dino) == KF_CAPS) { - static const char *const names[] = { "ev", "key", "rel", "abs" }; - for (int f = KF_EV; f <= KF_ABS; f++) - if (cb(ctx, INO(K_KDEV, idx, f), 0, names[f - KF_EV])) - return 1; - } - return 0; -} - static int children(uint64 dino, child_fn cb, void *ctx) { @@ -508,21 +403,6 @@ children(uint64 dino, child_fn cb, void *ctx) return 1; } switch (idx) { - case D_VIRTUAL: - for (int s = 0; s < kdev_nsubsys(); s++) - if (cb(ctx, INO(K_KSUBV, s, 0), 1, kdev_subsys(s))) - return 1; - break; - case D_DEVT: - if (cb(ctx, INO(K_KDEVD, 0, 0), 1, "char") || - cb(ctx, INO(K_KDEVD, 1, 0), 1, "block")) - return 1; - break; - case D_CLASS: - for (int s = 0; s < kdev_nsubsys(); s++) - if (cb(ctx, INO(K_KCLASS, s, 0), 1, kdev_subsys(s))) - return 1; - break; case D_PCIDEV: for (int i = 0; i < pci_count(); i++) { struct out o = { name, 0, sizeof name - 1 }; @@ -606,9 +486,6 @@ children(uint64 dino, child_fn cb, void *ctx) for (int f = 1; f < NPSUFILES; f++) if (psu_has(&b, f) && cb(ctx, INO(K_PSU, idx, f), 0, psufiles[f])) return 1; - } else if (kind == K_KSUBV || kind == K_KCLASS || kind == K_KDEVD || - kind == K_KDEV) { - return kd_children(dino, cb, ctx); } else if (kind == K_MEMMAP && I_FILE(dino) == 0) { for (int f = 1; f < 4; f++) if (cb(ctx, INO(K_MEMMAP, idx, f), 0, memfiles[f])) @@ -629,19 +506,6 @@ parent_of(uint64 dino) case K_USB: return INO(K_DIR, D_USBDEV, 0); case K_MEMMAP: return INO(K_DIR, D_MEMMAP, 0); case K_PSU: return INO(K_DIR, D_PSU, 0); - case K_KSUBV: return INO(K_DIR, D_VIRTUAL, 0); - case K_KCLASS: return INO(K_DIR, D_CLASS, 0); - case K_KDEVD: return INO(K_DIR, D_DEVT, 0); - case K_KDEV: { - struct kdev k; - if (I_FILE(dino) == KF_CAPS) - return INO(K_KDEV, I_IDX(dino), 0); - if (kdev_get(I_IDX(dino), &k) < 0) - return SYSFS_ROOT; - if (k.parent >= 0) - return INO(K_KDEV, k.parent, 0); - return INO(K_KSUBV, kd_subsys_of(&k), 0); - } } return SYSFS_ROOT; } @@ -823,67 +687,6 @@ text(uint64 ino, char *buf, int cap) putc_(&o, '\n'); return o.n; } - case K_KDEV: { - struct kdev k; - char path[160]; - if (kdev_get(idx, &k) < 0 || f < KF_DEV || f >= KF_N || f == KF_CAPS || - (f != KF_UEVENT && f != KF_SUBSYSTEM && !kd_has(&k, f) && - !(f >= KF_EV && k.cap != KD_NONE))) - return -1; - switch (f) { - case KF_DEV: - kd_devt(&k, buf); - o.n = (int)strlen(buf); - putc_(&o, '\n'); - return o.n; - case KF_UEVENT: - return kdev_uevent_text(idx, buf, cap); - case KF_SUBSYSTEM: { - // ../ once for each directory between here and /sys, then class/. - if (kdev_path(idx, path, sizeof path) < 0) - return -1; - for (const char *p = path; ; p++) { - if (*p == '/' || *p == 0) - puts_(&o, "../"); - if (*p == 0) - break; - } - puts_(&o, "class/"); - puts_(&o, k.subsys); - return o.n; - } - case KF_NAME: - puts_(&o, k.cap == KD_KEYBOARD ? "SchOS keyboard\n" : "SchOS mouse\n"); - return o.n; - case KF_PROPERTIES: - puts_(&o, "0\n"); - return o.n; - case KF_EV: - puts_(&o, k.cap == KD_KEYBOARD ? "120013\n" : "17\n"); - return o.n; - case KF_KEY: - puts_(&o, k.cap == KD_KEYBOARD - ? "ffffffffffffffff ffffffffffffffff ffffffffffffffff fffffffffffffffe\n" - : "70000 0 0 0 0\n"); - return o.n; - case KF_REL: - puts_(&o, k.cap == KD_MOUSE ? "903\n" : "0\n"); - return o.n; - case KF_ABS: - puts_(&o, "0\n"); - return o.n; - } - return -1; - } - case K_KCLINK: - case K_KDEVL: { - char path[160]; - if (kdev_path(idx, path, sizeof path) < 0) - return -1; - puts_(&o, "../../"); - puts_(&o, path); - return o.n; - } case K_DMIID: { int n = dmi_field(f - 1, buf, cap - 1); if (n < 0) @@ -919,30 +722,10 @@ exists_dir(uint64 ino) } case K_PSU: return I_IDX(ino) < acpi_psu_count(); - case K_KSUBV: - case K_KCLASS: - return I_IDX(ino) < kdev_nsubsys(); - case K_KDEVD: - return I_IDX(ino) < 2; - case K_KDEV: { - struct kdev k; - if (kdev_get(I_IDX(ino), &k) < 0) - return 0; - return I_FILE(ino) == KF_DIR || (I_FILE(ino) == KF_CAPS && k.cap != KD_NONE); - } } return 0; } -// Symlinks: the class and dev/ entries, and a device's own `subsystem`. -static int -is_link(uint64 ino) -{ - int k = I_KIND(ino); - return k == K_KCLINK || k == K_KDEVL || - (k == K_KDEV && I_FILE(ino) == KF_SUBSYSTEM); -} - static void sysfs_iload(struct inode *ip) { @@ -957,24 +740,13 @@ sysfs_iload(struct inode *ip) ip->size = 0; ip->nlink = 1; - int subdir = (I_KIND(ino) == K_PCI && - (I_FILE(ino) == PCI_GRAPHICS || I_FILE(ino) == PCI_FB0)) || - (I_KIND(ino) == K_KDEV && I_FILE(ino) == KF_CAPS); + int subdir = I_KIND(ino) == K_PCI && + (I_FILE(ino) == PCI_GRAPHICS || I_FILE(ino) == PCI_FB0); if ((I_FILE(ino) == 0 || subdir) && exists_dir(ino)) { ip->type = T_DIR; ip->nlink = 2; return; } - if (is_link(ino)) { - char lb[200]; - int ln = text(ino, lb, sizeof lb); - if (ln >= 0) { - ip->type = T_SYMLINK; - ip->size = (uint64)ln; // readlink(2) trusts this length - ip->mode = 0777; - return; - } - } int n = binary(ino, 0, 0); if (n >= 0) { ip->type = T_FILE; @@ -989,10 +761,7 @@ sysfs_iload(struct inode *ip) kmfree(buf); if (n >= 0) { ip->type = T_FILE; - if ((I_KIND(ino) == K_PCI && (I_FILE(ino) == PCI_ENABLE || - I_FILE(ino) == 10 /* uevent */)) || - (I_KIND(ino) == K_USB && I_FILE(ino) == NUSBFILES - 1) || - (I_KIND(ino) == K_KDEV && I_FILE(ino) == KF_UEVENT)) + if (I_KIND(ino) == K_PCI && I_FILE(ino) == PCI_ENABLE) ip->rwmode = 3; return; } @@ -1053,31 +822,9 @@ sysfs_read(struct inode *ip, int user_dst, uint64 dst, uint64 off, uint n) static int sysfs_write(struct inode *ip, int u, uint64 s, uint64 o, uint n) { - (void)o; + (void)u; (void)s; (void)o; if (I_KIND(ip->inum) == K_PCI && I_FILE(ip->inum) == PCI_ENABLE) return (int)n; - // `echo add > uevent`: udevadm trigger asks the kernel to say again what it - // said when the device appeared. Only the kernel's own devices are announced; - // a PCI or USB device's uevent accepts the write and says nothing, because - // their /sys paths are not the /devices tree an event's DEVPATH must name. - if (I_KIND(ip->inum) == K_PCI || I_KIND(ip->inum) == K_USB) - return (int)n; - if (I_KIND(ip->inum) == K_KDEV && I_FILE(ip->inum) == KF_UEVENT) { - char act[16]; - uint k = n < sizeof(act) - 1 ? n : (uint)sizeof(act) - 1; - if (u) { - if (copy_from_user(act, s, k) < 0) - return -EFAULT; - } else { - memmove(act, (void *)s, k); - } - uint e = 0; - while (e < k && act[e] != '\n' && act[e] != ' ') - e++; - act[e] = 0; - kdev_uevent(I_IDX(ip->inum), e ? act : "change"); - return (int)n; - } return -EROFS; } @@ -1149,7 +896,7 @@ emit_cb(void *ctx, uint64 ino, int isdir, const char *name) de->d_ino = ino; de->d_off = l->used + rec; de->d_reclen = (ushort)rec; - de->d_type = isdir == 2 ? 10 : isdir ? 4 : 8; + de->d_type = isdir ? 4 : 8; memmove(de->d_name, name, nlen); memset(de->d_name + nlen, 0, rec - (int)sizeof(*de) - nlen); l->used += rec; diff --git a/kernel/net/unix.c b/kernel/net/unix.c index 13d992f..9383782 100644 --- a/kernel/net/unix.c +++ b/kernel/net/unix.c @@ -15,18 +15,15 @@ // is the property sleep/wakeup actually requires. Thirty-two sockets carrying // GUI messages will not notice the contention. // -// **Names live in the filesystem.** bind() creates a socket node -// (kernel/sys/sys_net.c) and connect() resolves a path to the inode and looks -// for the socket bound to that inode, so every spelling of a path arrives at -// the same place and the node can be stat()ed and unlink()ed. -// -// **Datagram and sequenced-packet sockets** share the ring with streams: each -// message is a 16-byte header -- length and multicast groups, sender port or -// pid, sender uid and gid -- and then its bytes, so a read takes exactly one -// message and credentials belong to the message, not to the socket. udev's -// workers share one end of a socketpair and the manager has to know which of -// them wrote what. The kernel's uevents travel the same way, to netlink -// sockets of protocol NETLINK_KOBJECT_UEVENT. +// **Names are held in a table here, not in the filesystem.** A real Unix +// socket appears in a directory listing as a special file, and binding one +// creates that node. Doing that properly means a new inode type threaded +// through both filesystems, iget, dirlookup and stat -- a VFS change, not a +// socket change. The table gives every behaviour that matters: a client +// finds a server by a name it agreed in advance, which is the whole job. +// What is lost is `ls -l /tmp/schcom` showing an `s`, and `unlink()` of a +// socket path failing with ENOENT. Both are worth fixing when the VFS next +// gets opened up; neither blocks anything. #include "core/types.h" #include "core/defs.h" @@ -116,139 +113,6 @@ sockinit(void) "the socket table, with buffered bytes and descriptors in flight"); } -// SOCK_SEQPACKET and SOCK_DGRAM keep message boundaries. They ride in the same -// ring as a stream, each message behind a four-byte length, so a write is one -// message and a read takes exactly one -- the rest of a message that does not -// fit the buffer is dropped, as Linux drops it. A message is never split, so -// one has to fit the ring whole. -static int -is_packet(struct socket *s) -{ - if(s->domain == AF_NETLINK) - return s->nlproto == NETLINK_KOBJECT_UEVENT; - return s->domain == AF_UNIX && (s->type == SOCK_DGRAM || s->type == SOCK_SEQPACKET); -} - -int -sock_is_packet(struct socket *s) -{ - return is_packet(s); -} - -// Every packet in the ring is an eight-byte header and then the payload: the -// length with the multicast groups it arrived on in the top byte, and the port -// id of whoever sent it. Netlink receivers need both -- a datagram that says -// "no group" is unicast, and is believed only from the one sender they trust. -#define PKTHDR 16 - -// The multicast groups the datagram last read from this socket was sent to, -// and who sent it. -uint -sock_nl_groups(struct socket *s) -{ - return s->domain == AF_NETLINK ? s->nllast : 0; -} - -uint -sock_nl_from(struct socket *s) -{ - return s->domain == AF_NETLINK ? s->nlfrom : 0; -} - -uint -sock_nl_pid(struct socket *s) -{ - return s->nlpid; -} - -uint -sock_nl_bound_groups(struct socket *s) -{ - return s->nlgroups; -} - -static uint nlport = 4000; - -// bind(2) on a netlink socket. A port id of 0 asks the kernel to choose one, -// which is how a socket becomes something a datagram can be addressed to. -int -sock_netlink_bind(struct socket *s, uint pid, uint groups) -{ - acquire(&stab.lock); - s->nlgroups = groups; - if(s->nlproto == NETLINK_KOBJECT_UEVENT && s->nlpid == 0) - s->nlpid = pid ? pid : ++nlport; - release(&stab.lock); - return 0; -} - -// Caller holds stab.lock. Append one packet to dst's ring; 0, or -1 if it -// does not fit. -static int -pkt_put(struct socket *dst, const char *msg, int len, uint groups, uint from, - uint uid, uint gid) -{ - if(len < 0 || len + PKTHDR > SOCKBUF || - dst->nwrite - dst->nread + (uint)len + PKTHDR > SOCKBUF) - return -1; - uint h0 = (uint)len | ((groups & 0xFF) << 24); - for(int i = 0; i < 4; i++) - dst->data[dst->nwrite++ % SOCKBUF] = (char)(h0 >> (8 * i)); - for(int i = 0; i < 4; i++) - dst->data[dst->nwrite++ % SOCKBUF] = (char)(from >> (8 * i)); - for(int i = 0; i < 4; i++) - dst->data[dst->nwrite++ % SOCKBUF] = (char)(uid >> (8 * i)); - for(int i = 0; i < 4; i++) - dst->data[dst->nwrite++ % SOCKBUF] = (char)(gid >> (8 * i)); - for(int i = 0; i < len; i++) - dst->data[dst->nwrite++ % SOCKBUF] = msg[i]; - return 0; -} - -// Queue one uevent datagram: on the socket whose port id is `dpid` if there is -// one, and on every uevent socket bound to any of `groups`, except `from`. A -// full queue drops the message, as Linux does: udev recovers with a trigger, -// and the sender must never wait on a reader. -static void -uevent_dispatch(uint groups, uint dpid, const char *msg, int len, struct socket *from) -{ - struct socket *s; - uint spid = from ? from->nlpid : 0; - - if(len <= 0) - return; - acquire(&stab.lock); - for(s = stab.sock; s < stab.sock + NSOCK; s++){ - if(s->state == SS_FREE || s == from || s->domain != AF_NETLINK || - s->nlproto != NETLINK_KOBJECT_UEVENT) - continue; - uint g = groups & s->nlgroups; - if(dpid && s->nlpid == dpid) - g = 0; // unicast: no group, so the reader checks the sender - else if(!g) - continue; - if(pkt_put(s, msg, len, g, spid, 0, 0) == 0) - wakeup(&s->nread); - } - release(&stab.lock); - poll_kick(); -} - -void -uevent_send(uint groups, const char *msg, int len, struct socket *from) -{ - uevent_dispatch(groups, 0, msg, len, from); -} - -int -sock_uevent_send(struct socket *s, const char *msg, int len, uint dpid, uint dgroups) -{ - if(len + PKTHDR > SOCKBUF) - return -EMSGSIZE; - uevent_dispatch(dgroups, dpid, msg, len, s); - return len; -} - // Caller holds stab.lock. static struct socket * alloc_locked(int type) @@ -551,7 +415,7 @@ sockread_fds(struct socket *s, char *addr, int n, struct file **fout, int *nfout // so an empty buffer means the caller has already drained this dump rather // than that more is coming. Blocking here would hang a getifaddrs() that // asked one question too many. - if(s->domain == AF_NETLINK && !is_packet(s)){ + if(s->domain == AF_NETLINK){ acquire(&stab.lock); uint have = s->nwrite - s->nread; if(have == 0){ @@ -566,10 +430,8 @@ sockread_fds(struct socket *s, char *addr, int n, struct file **fout, int *nfout return n; } - // A datagram socket has no end of stream: a reader whose peer has gone just - // waits, as on Linux, instead of reading "zero bytes" for ever. acquire(&stab.lock); - while(s->nread == s->nwrite && !(s->peer_closed && s->type != SOCK_DGRAM)){ + while(s->nread == s->nwrite && !s->peer_closed){ if(myproc()->killed || s->state == SS_FREE){ release(&stab.lock); return -EINTR; @@ -577,51 +439,6 @@ sockread_fds(struct socket *s, char *addr, int n, struct file **fout, int *nfout sleep(&s->nread, &stab.lock); } - // One message, whole or truncated; descriptors ride with the header. - if(is_packet(s) && s->nread != s->nwrite){ - uint len = 0, from = 0, puid = 0, pgid = 0; - for(i = 0; i < 4; i++) - len |= (uint)(uchar)s->data[(s->nread + i) % SOCKBUF] << (8 * i); - for(i = 0; i < 4; i++) - from |= (uint)(uchar)s->data[(s->nread + 4 + i) % SOCKBUF] << (8 * i); - for(i = 0; i < 4; i++) - puid |= (uint)(uchar)s->data[(s->nread + 8 + i) % SOCKBUF] << (8 * i); - for(i = 0; i < 4; i++) - pgid |= (uint)(uchar)s->data[(s->nread + 12 + i) % SOCKBUF] << (8 * i); - if(s->nscm > 0 && s->scm[0].pos == s->nread){ - got = s->scm[0].n; - for(i = 0; i < got; i++) - taken[i] = s->scm[0].f[i]; - for(i = 1; i < s->nscm; i++) - s->scm[i - 1] = s->scm[i]; - s->nscm--; - } - s->nread += PKTHDR; - s->nllast = len >> 24; - s->nlfrom = from; - s->pk_uid = puid; - s->pk_gid = pgid; - s->pk_valid = 1; - len &= 0xFFFFFF; - uint take = len < (uint)n ? len : (uint)n; - for(i = 0; i < (int)take; i++) - addr[i] = s->data[(s->nread + i) % SOCKBUF]; - s->nread += len; - wakeup(&s->nwrite); - release(&stab.lock); - poll_kick(); - if(fout){ - for(int j = 0; j < got; j++) - fout[j] = taken[j]; - if(nfout) - *nfout = got; - } else { - for(int j = 0; j < got; j++) - fileclose(taken[j]); - } - return (int)take; - } - // A message carrying descriptors is a boundary. This read either starts // exactly on one -- and takes them -- or is cut short so that it stops // before it, leaving the next read to start there. @@ -702,11 +519,6 @@ sockwrite_fds(struct socket *s, char *addr, int n, struct file **f, int nf) // rather than a peer's -- there is no peer. Doing the work here is what lets // the read that follows never block, which matters because musl reads with // MSG_DONTWAIT and treats a short read as the end of the dump. - if(s->domain == AF_NETLINK && is_packet(s)){ - // udevd announces what it did on group 2; the sender does not hear itself. - uevent_dispatch(UEVENT_GROUP_UDEV, 0, addr, n, s); - return n; - } if(s->domain == AF_NETLINK){ acquire(&stab.lock); s->nread = s->nwrite = 0; @@ -736,49 +548,6 @@ sockwrite_fds(struct socket *s, char *addr, int n, struct file **f, int nf) return -ENOBUFS; } - if(is_packet(s)){ - if(n + PKTHDR > SOCKBUF){ - release(&stab.lock); - return -EMSGSIZE; - } - for(;;){ - dst = s->peer; - if(dst == 0){ - release(&stab.lock); - sigsend(myproc()->pid, SIGPIPE); - return -EPIPE; - } - if(dst->nwrite - dst->nread + (uint)n + PKTHDR <= SOCKBUF) - break; - if(myproc()->killed){ - release(&stab.lock); - return -EINTR; - } - wakeup(&dst->nread); - sleep(&dst->nwrite, &stab.lock); - } - if(nf > 0){ - if(dst->nscm >= NSCM){ - release(&stab.lock); - return -ENOBUFS; - } - struct scm_fds *b = &dst->scm[dst->nscm++]; - b->pos = dst->nwrite; // the header, where a packet read starts - b->n = nf; - for(int j = 0; j < nf; j++) - b->f[j] = f[j]; - } - // The sender's credentials travel with the datagram, not with the socket: - // several processes can share one end (udev's workers do) and the reader - // has to know which of them each message came from. - pkt_put(dst, addr, n, 0, myproc() ? (uint)myproc()->pid : 0, - myproc() ? (uint)myproc()->euid : 0, myproc() ? (uint)myproc()->egid : 0); - wakeup(&dst->nread); - release(&stab.lock); - poll_kick(); - return n; - } - for(i = 0; i < n; i++){ // Re-read every iteration: the peer can go away while we sleep below. dst = s->peer; @@ -838,11 +607,9 @@ socknew(int domain, int type, int proto) if(domain != AF_UNIX && domain != AF_INET && domain != AF_NETLINK) return 0; - // AF_UNIX: streams, and message-preserving SEQPACKET and DGRAM (connected - // pairs and listeners; an unconnected DGRAM has no address to send to and - // will be refused when it tries). AF_INET has datagrams too, and inet_new is + // AF_UNIX is stream-only here; AF_INET has datagrams too, and inet_new is // what decides whether it can serve the type asked for. - if(domain == AF_UNIX && type != SOCK_STREAM && type != SOCK_DGRAM && type != SOCK_SEQPACKET) + if(domain == AF_UNIX && type != SOCK_STREAM) return 0; // AF_NETLINK carries whole messages and musl asks for SOCK_RAW; SOCK_DGRAM is // accepted too because Linux does. NETLINK_ROUTE is the only protocol served @@ -851,16 +618,14 @@ socknew(int domain, int type, int proto) if(domain == AF_NETLINK){ if(type != SOCK_RAW && type != SOCK_DGRAM) return 0; - if(proto != NETLINK_ROUTE && proto != NETLINK_KOBJECT_UEVENT) + if(proto != NETLINK_ROUTE) return 0; } acquire(&stab.lock); s = alloc_locked(type); - if(s){ + if(s) s->domain = domain; - s->nlproto = domain == AF_NETLINK ? proto : 0; - } release(&stab.lock); if(s == 0) return 0; @@ -879,16 +644,16 @@ socknew(int domain, int type, int proto) // A connected pair with no name and no listener, for socketpair(2). int -sockpair_type(struct socket **pa, struct socket **pb, int type) +sockpair(struct socket **pa, struct socket **pb) { struct socket *a, *b; acquire(&stab.lock); - if((a = alloc_locked(type)) == 0){ + if((a = alloc_locked(SOCK_STREAM)) == 0){ release(&stab.lock); return -ENFILE; } - if((b = alloc_locked(type)) == 0){ + if((b = alloc_locked(SOCK_STREAM)) == 0){ a->state = SS_FREE; a->ref = 0; release(&stab.lock); @@ -909,12 +674,6 @@ sockpair_type(struct socket **pa, struct socket **pb, int type) return 0; } -int -sockpair(struct socket **pa, struct socket **pb) -{ - return sockpair_type(pa, pb, SOCK_STREAM); -} - // bind(2) for AF_UNIX. The caller has already created the filesystem node and // hands over its identity; see sys_bind() in kernel/sys/sys_net.c for why the // creation lives there and not here. @@ -1213,7 +972,7 @@ sockreadable(struct socket *s) return inet_readable(s->inet); if(s->state == SS_LISTENING) return s->nqueue > 0; - return s->nread != s->nwrite || (s->peer_closed && s->type != SOCK_DGRAM); + return s->nread != s->nwrite || s->peer_closed; } // The peer is gone and everything it sent has been read. @@ -1332,19 +1091,6 @@ sock_sendercred(struct socket *s, int *pid, uint *uid, uint *gid) { int r = -1; - // Uevents come from the kernel -- or from udevd re-announcing them -- and a - // receiver that asked for credentials discards a datagram without them. - if(s->domain == AF_NETLINK && s->nlproto == NETLINK_KOBJECT_UEVENT){ - *pid = 0; *uid = 0; *gid = 0; - return 0; - } - - // A packet socket reports whoever sent the datagram just read. - if(s->domain == AF_UNIX && is_packet(s) && s->pk_valid){ - *pid = (int)s->nlfrom; *uid = s->pk_uid; *gid = s->pk_gid; - return 0; - } - acquire(&stab.lock); if(s->domain != AF_INET && s->peer){ struct socket *p = s->peer; diff --git a/kernel/sys/sys_fs.c b/kernel/sys/sys_fs.c index 71c9a5a..54baf20 100644 --- a/kernel/sys/sys_fs.c +++ b/kernel/sys/sys_fs.c @@ -133,16 +133,6 @@ stati_linux(struct inode *ip, struct linux_stat *st) st->st_atime = ip->atime; st->st_mtime = ip->mtime; st->st_ctime = ip->ctime; } -// The number stat() reports for a device node, split back into major and -// minor. /sys must agree with stat() -- udev matches one against the other. -void -dev_stat_numbers(int major, int minor, int *maj, int *min) -{ - uint64 r = linux_tty_rdev((uint32)major, (uint32)minor); - *maj = (int)(((r >> 8) & 0xfff) | ((r >> 32) & ~0xfffULL)); - *min = (int)((r & 0xff) | ((r >> 12) & ~0xffULL)); -} - // ----------------------------------------------------------------------- // Internal helpers // ----------------------------------------------------------------------- diff --git a/kernel/sys/sys_net.c b/kernel/sys/sys_net.c index 8e5456b..a47d701 100644 --- a/kernel/sys/sys_net.c +++ b/kernel/sys/sys_net.c @@ -24,7 +24,6 @@ #include "uapi/posix.h" #include "core/syscall.h" #include "core/uaccess.h" -#include "core/kmem.h" #include "uapi/errno.h" #include "net/socket.h" @@ -165,7 +164,7 @@ SYSCALL_DEFINE(socket) if (type & ~(0xFF | SOCK_CLOEXEC | SOCK_NONBLOCK)) return -EINVAL; type &= 0xFF; - if (domain == AF_UNIX && type != SOCK_STREAM && type != SOCK_DGRAM && type != SOCK_SEQPACKET) + if (domain == AF_UNIX && type != SOCK_STREAM) return -EPROTOTYPE; if (domain == AF_INET && type != SOCK_STREAM && type != SOCK_DGRAM && type != SOCK_RAW) @@ -186,7 +185,7 @@ SYSCALL_DEFINE(socket) // default rather than departing from it, and programs do write them out: // toybox's `nc` passes IPPROTO_TCP, and refusing it meant "Protocol not // supported" for TCP on a system whose whole network is TCP. - if (protocol != 0 && domain != AF_NETLINK) { + if (protocol != 0) { int redundant = (domain == AF_INET && type == SOCK_STREAM && protocol == IPPROTO_TCP) || (domain == AF_INET && type == SOCK_DGRAM && protocol == IPPROTO_UDP); @@ -271,17 +270,6 @@ SYSCALL_DEFINE(bind) return sockbind_in(s, ip, port_from_net(port)); } - // sockaddr_nl: family, pad, port id, groups. Only the groups matter here. - if (sock_domain(s) == AF_NETLINK) { - uint pid = 0, groups = 0; - if (ulen < 12) - return -EINVAL; - if (copy_from_user(&pid, uaddr + 4, 4) < 0 || - copy_from_user(&groups, uaddr + 8, 4) < 0) - return -EFAULT; - return sock_netlink_bind(s, pid, groups); - } - uint64 key; if (sun_abstract(uaddr, ulen, &key)) return sockbind_abstract(s, key); @@ -471,11 +459,15 @@ SYSCALL_DEFINE(socketpair) return -EAFNOSUPPORT; int sflags = type & (SOCK_CLOEXEC | SOCK_NONBLOCK); type &= 0xFF; - // Datagram and sequenced-packet pairs keep message boundaries (kernel/net/unix.c). - if (type != SOCK_STREAM && type != SOCK_DGRAM && type != SOCK_SEQPACKET) + // Datagram and sequenced-packet pairs are carried as a stream. What they + // are used for between two processes is passing a descriptor or a status + // word -- Rust's process spawning asks for SOCK_SEQPACKET to hear back + // from its child -- and one message at a time is all of that. A reader + // that sends two and wants them kept apart would see them joined. + if (type != SOCK_STREAM && type != 2 /* DGRAM */ && type != 5 /* SEQPACKET */) return -EPROTOTYPE; - if ((r = sockpair_type(&a, &b, type)) < 0) + if ((r = sockpair(&a, &b)) < 0) return r; if ((fd0 = sock_to_fd(a, sflags)) < 0) { @@ -610,9 +602,6 @@ SYSCALL_DEFINE(recvfrom) // believes a message, and with no address at all it hit an assertion // and every GNOME program that watches the network aborted. uint16 nl[6] = { AF_NETLINK, 0, 0, 0, 0, 0 }; - uint g = sock_nl_groups(s), from = sock_nl_from(s); - nl[2] = (uint16)from; nl[3] = (uint16)(from >> 16); - nl[4] = (uint16)g; nl[5] = (uint16)(g >> 16); alen = (int)sizeof(nl); int cap = alen; if (ulenp && get_user(cap, ulenp) < 0) @@ -671,12 +660,6 @@ sock_name(int peer) // A sockaddr_nl of zeros: family, padding, port id 0 (the kernel), // no groups. sa.un.sun_family = AF_NETLINK; - { - // Port id and bound groups, at their offsets in sockaddr_nl. - uint pid = sock_nl_pid(s), grp = sock_nl_bound_groups(s); - memmove((char *)&sa + 4, &pid, 4); - memmove((char *)&sa + 8, &grp, 4); - } alen = 12; } else { if ((r = sockname_unix(s, peer, sa.un.sun_path)) < 0) @@ -982,7 +965,6 @@ SYSCALL_DEFINE(sendmsg) uint ip = 0; ushort port = 0; int addressed = 0; - int anydata = 0; if ((r = argsockfile(0, &s, &file)) < 0) return r; @@ -1008,61 +990,6 @@ SYSCALL_DEFINE(sendmsg) addressed = 1; } - // A datagram socket sends the whole message as one datagram, however many - // iovecs it is spread over -- udev's header and properties are two. - if (sock_is_packet(s)) { - char *buf = kalloc(); // a page: kmalloc has no 4 KB class - int total = 0; - if (!buf) { - r = -ENOMEM; - goto fail; - } - for (i = 0; i < m.msg_iovlen; i++) { - char *p; - int len; - if ((len = fetch_iov(&m, i, &p)) < 0) { - kfree(buf); - r = len; - goto fail; - } - if (total + len > 4096 - 8) { - kfree(buf); - r = -EMSGSIZE; - goto fail; - } - memmove(buf + total, p, (uint)len); - total += len; - } - if (sock_domain(s) == AF_NETLINK) { - // sockaddr_nl: where it goes, by port id and/or multicast group. - uint dpid = 0, dgrp = 0; - if (m.msg_name && m.msg_namelen >= 12) { - if (copy_from_user(&dpid, (addr_t)m.msg_name + 4, 4) < 0 || - copy_from_user(&dgrp, (addr_t)m.msg_name + 8, 4) < 0) { - kfree(buf); - r = -EFAULT; - goto fail; - } - } else { - dgrp = UEVENT_GROUP_UDEV; - } - r = sock_uevent_send(s, buf, total, dpid, dgrp); - } else if (nf > 0) { - r = sockwrite_fds(s, buf, total, fs, nf); - if (r < 0) { - kfree(buf); - goto fail; // not queued: the descriptors are still ours - } - nf = 0; - } else { - r = sockwrite(s, buf, total); - } - kfree(buf); - if (r < 0) - goto fail; - return total; - } - for (i = 0; i < m.msg_iovlen; i++) { char *p; int len, w; @@ -1073,7 +1000,6 @@ SYSCALL_DEFINE(sendmsg) } if (len == 0) continue; - anydata = 1; if (addressed) w = socksendto(s, p, len, ip, port); @@ -1104,12 +1030,6 @@ SYSCALL_DEFINE(sendmsg) r = -EINVAL; goto fail; } - // A datagram with nothing in it is still a datagram: udev's workers say - // "done" with exactly that, and the credentials it carries are the message. - if (!anydata && sock_is_packet(s)) { - if ((r = sockwrite(s, "", 0)) < 0) - return r; - } return (int)sent; fail: @@ -1120,7 +1040,6 @@ fail: SYSCALL_DEFINE(recvmsg) { - int didread = 0; struct socket *s; struct file *file; struct msghdr m; @@ -1157,7 +1076,7 @@ SYSCALL_DEFINE(recvmsg) // one rather than discarding it as Linux would: musl's getifaddrs reads // in chunks, and this whole queue is one "datagram" only by convention. int nlret = -1; - if (sock_domain(s) == AF_NETLINK && !sock_is_packet(s)) { + if (sock_domain(s) == AF_NETLINK) { uint have = socknl_pending(s); if (have == 0) return -EAGAIN; // never blocks; see sockread @@ -1194,7 +1113,6 @@ SYSCALL_DEFINE(recvmsg) // one ended and the next began. if (got > 0 && (nf > 0 || !sockreadable(s))) break; - didread = 1; if (sock_domain(s) == AF_UNIX) rd = sockread_fds(s, p, len, fs, &nf); @@ -1209,21 +1127,8 @@ SYSCALL_DEFINE(recvmsg) return rd; } got += rd; - if (rd < len || sock_is_packet(s)) - break; // one message per call - } - - // A receive buffer of no bytes still takes a datagram off the queue (it is - // truncated to nothing). udev's workers report in with empty datagrams and - // read them this way; skipping the read left the message queued, the - // socket readable and the manager looping on it. - if (!didread && sock_is_packet(s)) { - char z; - int rd = sock_domain(s) == AF_UNIX - ? sockread_fds(s, &z, 0, fs, &nf) - : sockread(s, &z, 0); - if (rd < 0) - return rd; + if (rd < len) + break; } if (nf > 0) { @@ -1286,7 +1191,7 @@ SYSCALL_DEFINE(recvmsg) // socket that is always the peer, so its recorded credentials are the // answer. WebKit sets the option on its IPC sockets and aborts if it is // refused. - if (sock_passcred(s) && (got > 0 || sock_is_packet(s))) { + if (sock_passcred(s) && got > 0) { struct { int pid; uint uid, gid; } cr; uint64 off = clen ? CMSG_SPACE(clen - CMSG_LEN(0)) : 0; if (sock_sendercred(s, &cr.pid, &cr.uid, &cr.gid) == 0) { @@ -1329,9 +1234,6 @@ SYSCALL_DEFINE(recvmsg) } else if (sock_domain(s) == AF_NETLINK) { // The kernel, as in recvfrom above. uint16 nl[6] = { AF_NETLINK, 0, 0, 0, 0, 0 }; - uint g = sock_nl_groups(s), from = sock_nl_from(s); - nl[2] = (uint16)from; nl[3] = (uint16)(from >> 16); - nl[4] = (uint16)g; nl[5] = (uint16)(g >> 16); alen = (uint32)sizeof(nl); if (alen > m.msg_namelen) alen = m.msg_namelen; diff --git a/ports/alpine/world b/ports/alpine/world index e0c1b2a..ae250c5 100644 --- a/ports/alpine/world +++ b/ports/alpine/world @@ -56,9 +56,3 @@ curl # not ALSA's kernel side, and SDL 3 has no OSS backend of its own. alsa-lib alsa-plugins -# udev: the daemon that names devices and tells Xorg (and libinput, and every -# Wayland compositor) what is plugged in. eudev is udev without systemd. -# `udevadm`, the rules, and libudev come with it. -eudev -eudev-libs -udev-init-scripts diff --git a/ports/alpine/world.lock b/ports/alpine/world.lock index 5a093d8..2db1fbf 100644 --- a/ports/alpine/world.lock +++ b/ports/alpine/world.lock @@ -19,12 +19,11 @@ cairo-gobject-1.18.4-r1.apk ec302c75a24edaa4a2f867cfb5eec85cf30efe184044bab6e2b9 c-ares-1.34.9-r0.apk 3539a3e11c1ef1516117d8179a546ac882086e94e0d78ad51d687b3fa2b3b5f2 main cmatrix-2.0-r2.apk d9f6bea3851492a6db2a9cd3f7e2df0f0756c6651a702b36ed2b887328ad1f00 community cups-libs-2.4.19-r1.apk 5739c568d90f23006d74ff82619e1a18447a6f698ae2af61970debe6658a7bea main -curl-8.23.0-r0.apk 531ae298451a4c1c44f563fc3b2eedc2c1d7e0a26a33bdfc0efe6faaff6a5942 main -curl-zsh-completion-8.23.0-r0.apk 123ff531444fe6c3095c49553f8798bcf6f6ca8c7668b4140cc33daf24c351ec main +curl-8.22.0-r0.apk 9262352e6341bb87e3047d1ca0b4051b2a9f4011409df25e4190e6be0334b860 main +curl-zsh-completion-8.22.0-r0.apk 182a5f0afb5fd8786137f4610c065c305aa593fd18994df908c146335fb6011c main dbus-libs-1.16.2-r2.apk 105f6ba17454c587ca9345f9deb348012d1950588a22da42120c954d396c99b6 main e2fsprogs-libs-1.47.4-r0.apk 2e067395a6981b06cfd2cd3abf0e6c6574f7ef64d02e01f75fc5323d4a337fb9 main encodings-1.1.0-r0.apk f7c0eb762638d0249b4fbbe07a8e4ed6aab8e86b74c239c40e47a36219777d52 main -eudev-3.2.14-r6.apk 540c78af1eed3bc411cae4e9171e911afc0704210c0b97315e978e05c3b39dce main eudev-libs-3.2.14-r6.apk 45fdbcf37c5c0e82067e98a818d3c1fd16d0261c62128dcb6b935b86b4ec6c86 main fastfetch-2.64.2-r0.apk 9c593d689f1c04625d3b433066ad92e430a384fa8e99585fee71310a20a965cb community fastfetch-zsh-completion-2.64.2-r0.apk ded7a5c7911c4db60ab71a320104f9af3f629976429c897db67ea87c5ac53ec8 community @@ -49,7 +48,6 @@ harfbuzz-13.2.1-r0.apk 01f7862265ef2256bbadd0fc998df3d1243dee606df3bd8284471cf39 htop-3.5.3-r0.apk 6f6d444ca9a4133c2c13323ded04128783d59f924bcff0276d150a982904a2f3 main hwdata-pci-0.408-r0.apk fe61404e6d2bd1e04c021d53641c0ebce97911084d77994e43a288a1278686c1 main hwdata-usb-0.408-r0.apk 272d365cb05dd085dd8022154cf71e3f3b0aaddd41c1269eeaf939c9d38675c8 main -kmod-libs-34.2-r1.apk d6504eb204a47a35e1dd5eaa1018301b2dc0b1809f84d88fab2fdbadfc9c3c73 main lbreakouthd-1.2.2-r0.apk 4d7f78d866a7c544132e5f159d18f978515ac948517f2b8a6c625756bb81a3e0 community lbreakouthd-data-1.2.2-r0.apk 28ce98df27bfbb442fa397fbb9eb3eb5464e5b7b040ea034f207be52f4f4f07f community lcms2-2.19-r0.apk b4cb038af3c2f02bedd8456ae876c97ac0a9f29789add77871bfd6ce084ff136 main @@ -61,7 +59,7 @@ libbz2-1.0.8-r6.apk 2737e2b23ed08d4911ee62cd0163b5f4bf0379cacf4dc35b23a00209584c libcap2-2.78-r0.apk 42886a7b4bcde26347c712925c8dc7db202b6029ef9b8c7ee81577ab7bdd8fad main libcom_err-1.47.4-r0.apk 4723d0fb8e286dc6adb54dd3cdaedcd8db54260f75ee778a5acb4c16922c0f52 main libcrypto3-3.5.9-r0.apk 6632d758d8f5e9ea3b650fe966f23bbf9a202f8b8dceecac93da135dec5e3689 main -libcurl-8.23.0-r0.apk 1f70ca404818805a6465b615bbfe84ef861488a963d7e630108ebd7fbaa51665 main +libcurl-8.22.0-r0.apk 40b2a8e15a8d24bc95a5f42aaeb018313a1ff457cee71d8e15aaf1328da765e8 main libdav1d-1.5.3-r0.apk 40a2b1b92cdcc6fd295934f799ee80ef67e2e384097624d565c6222d827f8480 main libdrm-2.4.134-r0.apk 584fd84ad5030040e613fa1816097cd1706ddd542100070852b0eab6e09e12ca main libeconf-0.8.3-r0.apk 2a791c7957961f830cf31b04f35f814a63afcf633badc9052425ff1cf2f64380 main @@ -159,7 +157,6 @@ ssl_client-1.37.0-r31.apk e578a140670e4cf9816117b0c2063632bf3754ecbe8fa593a7fd2b tiff-4.7.2-r0.apk 757ce87ebe4923a6be9869d026e250958ca09696529c464781e8a91d2b81670b main tmux-3.7c-r0.apk 16f3438f5a24124dd500f70bf6f2a232c9b3dfe5e7f7e615969ac5c00c2c8d0b main tmux-zsh-completion-5.9-r7.apk e505b2a8c0d56beb65b6f47fd732f6ee6d8d8a72a1a1ea27a6922dad48d9ce89 main -udev-init-scripts-35-r1.apk b707bc50e5c8d2faec08f90e7603b76c2c42dfb0e90e713f82be0f98411d8161 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 diff --git a/tools/posix/mkaroot.sh b/tools/posix/mkaroot.sh index 572fe10..dcfb269 100644 --- a/tools/posix/mkaroot.sh +++ b/tools/posix/mkaroot.sh @@ -121,26 +121,37 @@ cat > "$P/root/.xinitrc" <<'XEOF' exec wmaker XEOF -# X's input devices are found, not named: udevd (etc/schinit/udevd.job) turns -# the kernel's uevents into /run/udev data, and Xorg asks libudev for every -# device with ID_INPUT set. That was a hand-written list of event0 and event1 -# while SchOS had no udev. The file is SchOS's, in xorg.conf.d, which no Alpine -# package writes into; what is left here is the options the defaults do not -# give, and the framebuffer driver. +# X's input devices, named rather than found. Alpine's Xorg asks udev for +# them, SchOS has no udev, and X came up with no keyboard and no mouse: the +# pointer did not move, Ctrl+Alt+F-keys did nothing and nothing could end the +# session. SchOS has exactly two input nodes whatever the machine -- every +# keyboard on event0, every mouse on event1 (kernel/dev/input.c) -- so a +# fixed list is the whole answer. The file is SchOS's, in xorg.conf.d, which +# no Alpine package writes into. mkdir -p "$P/etc/X11/xorg.conf.d" cat > "$P/etc/X11/xorg.conf.d/10-schos.conf" <<'XEOF' +# SchOS has no udev, so X is told its input devices instead of finding them. +# event0 is every keyboard and event1 every mouse (kernel/dev/input.c). Section "ServerFlags" + Option "AutoAddDevices" "false" # Ctrl+Alt+Backspace ends X. Option "DontZap" "false" EndSection -Section "InputClass" - Identifier "SchOS keyboards" - MatchIsKeyboard "on" +Section "InputDevice" + Identifier "keyboard" + Driver "evdev" + Option "Device" "/dev/input/event0" Option "XkbRules" "evdev" Option "XkbOptions" "terminate:ctrl_alt_bksp" EndSection +Section "InputDevice" + Identifier "mouse" + Driver "evdev" + Option "Device" "/dev/input/event1" +EndSection + Section "Device" Identifier "fb" Driver "fbdev" @@ -154,6 +165,8 @@ EndSection Section "ServerLayout" Identifier "schos" Screen "screen" + InputDevice "keyboard" "CoreKeyboard" + InputDevice "mouse" "CorePointer" EndSection XEOF