udev works: /sys device tree, uevent netlink, datagram sockets with per-message credentials

Kernel devices (input, mem, tty, graphics, block) get /sys/devices/virtual
entries with dev/uevent/subsystem, /sys/class and /sys/dev links, input
capabilities, and writable uevent files that announce on the netlink socket.
eudev now runs, processes a full trigger and settles.

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

View file

@ -133,6 +133,16 @@ 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
// -----------------------------------------------------------------------

View file

@ -24,6 +24,7 @@
#include "uapi/posix.h"
#include "core/syscall.h"
#include "core/uaccess.h"
#include "core/kmem.h"
#include "uapi/errno.h"
#include "net/socket.h"
@ -272,12 +273,13 @@ SYSCALL_DEFINE(bind)
// sockaddr_nl: family, pad, port id, groups. Only the groups matter here.
if (sock_domain(s) == AF_NETLINK) {
uint groups = 0;
uint pid = 0, groups = 0;
if (ulen < 12)
return -EINVAL;
if (copy_from_user(&groups, uaddr + 8, 4) < 0)
if (copy_from_user(&pid, uaddr + 4, 4) < 0 ||
copy_from_user(&groups, uaddr + 8, 4) < 0)
return -EFAULT;
return sock_netlink_bind(s, groups);
return sock_netlink_bind(s, pid, groups);
}
uint64 key;
@ -608,6 +610,9 @@ 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)
@ -666,6 +671,12 @@ 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)
@ -971,6 +982,7 @@ SYSCALL_DEFINE(sendmsg)
uint ip = 0;
ushort port = 0;
int addressed = 0;
int anydata = 0;
if ((r = argsockfile(0, &s, &file)) < 0)
return r;
@ -996,6 +1008,61 @@ 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;
@ -1006,6 +1073,7 @@ SYSCALL_DEFINE(sendmsg)
}
if (len == 0)
continue;
anydata = 1;
if (addressed)
w = socksendto(s, p, len, ip, port);
@ -1036,6 +1104,12 @@ 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:
@ -1046,6 +1120,7 @@ fail:
SYSCALL_DEFINE(recvmsg)
{
int didread = 0;
struct socket *s;
struct file *file;
struct msghdr m;
@ -1119,6 +1194,7 @@ 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);
@ -1137,6 +1213,19 @@ SYSCALL_DEFINE(recvmsg)
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 (nf > 0) {
int room = 0, installed = 0, j;
uint64 writelen;
@ -1197,7 +1286,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) {
if (sock_passcred(s) && (got > 0 || sock_is_packet(s))) {
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) {
@ -1240,6 +1329,9 @@ 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;