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:
parent
a9f8cb7e0f
commit
5d4d318ebf
10 changed files with 813 additions and 37 deletions
|
|
@ -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;
|
||||
|
|
|
|||
Loading…
Reference in a new issue