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
240
kernel/dev/kdev.c
Normal file
240
kernel/dev/kdev.c
Normal file
|
|
@ -0,0 +1,240 @@
|
|||
// 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);
|
||||
}
|
||||
Loading…
Reference in a new issue