From da7ea5a17a3a05c20a7697d6bffe489db65d5fa2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Quentin=20Th=C3=A9bault?= Date: Thu, 2 Jul 2026 04:38:08 +0000 Subject: [PATCH] udev-dev: enumerate DRM primary nodes via hw.dri sysctl in jails MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In a FreeBSD jail devfs may not expose /dev/dri/card* even though the hw.dri.* sysctl subtree is readable. Add a sysctl-based enumeration like the one for input devices. hw.dri only has entries for primary nodes (no render nodes), and the index N maps directly to the /dev/dri/cardN path that drm-kmod creates, so no type filtering or path translation is needed. Refactor the common OID tree-walk into enumerate_sysctl_units() to avoid duplication with udev_dev_enumerate_evdev_sysctl. Sponsored by: Defenso Signed-off-by: Quentin Thébault --- udev-dev.c | 61 +++++++++++++++++++++++++----------------------------- 1 file changed, 28 insertions(+), 33 deletions(-) diff --git a/udev-dev.c b/udev-dev.c index cbf75b3..2ed9535 100644 --- a/udev-dev.c +++ b/udev-dev.c @@ -111,23 +111,15 @@ udev_dev_enumerate_cb(const char *path, mode_t type, void *arg) #ifdef HAVE_SYSCTLBYNAME /* - * Enumerate evdev input devices via the kern.evdev.input sysctl subtree. - * This is a fallback for jails where /dev/input/ is not exposed via devfs - * but the kern.evdev.input.* sysctls remain readable. - * - * We use the FreeBSD sysctl tree-walk protocol (OID 0.2 = CTL_SYSCTL_NEXT) - * to discover exactly the children that exist under kern.evdev.input, with - * no fixed upper bound. CTL_SYSCTL_NEXT skips CTLTYPE_NODE containers, so - * the walk lands on leaves of depth parent_len+2 such as - * kern.evdev.input.N.name rather than on kern.evdev.input.N itself. - * Each group of consecutive leaves sharing the same parent OID component at - * index parent_len corresponds to one evdev unit. When the component at - * next[parent_len] changes, we call CTL_SYSCTL_NAME on the depth-(parent_len+1) - * prefix to resolve the unit number N as a string, then add /dev/input/eventN. - * This yields each unit exactly once with no fixed upper bound. + * Walk a sysctl subtree rooted at root_name, discovering child units by their + * trailing numeric component. For each unit N found, add syspath_fmt % N to + * the enumerate. Uses the FreeBSD OID tree-walk protocol: CTL_SYSCTL_NEXT + * (0.2) to iterate leaves, CTL_SYSCTL_NAME (0.1) to resolve the unit number. + * Returns 0 on success or if root_name is absent; -1 on allocation error. */ static int -udev_dev_enumerate_evdev_sysctl(struct udev_enumerate *ue) +enumerate_sysctl_units(struct udev_enumerate *ue, const char *root_name, + const char *syspath_fmt) { int parent_mib[CTL_MAXNAME]; size_t parent_len; @@ -146,10 +138,9 @@ udev_dev_enumerate_evdev_sysctl(struct udev_enumerate *ue) parent_len = CTL_MAXNAME; last_unit_oid = -1; - if (sysctlnametomib("kern.evdev.input", parent_mib, &parent_len) < 0) - return (0); /* no evdev support; nothing to enumerate */ + if (sysctlnametomib(root_name, parent_mib, &parent_len) < 0) + return (0); - /* Seed the walk: oid[0..1] = { 0, 2 } (CTL_SYSCTL_NEXT), then parent */ oid[0] = 0; oid[1] = 2; /* CTL_SYSCTL_NEXT */ memcpy(&oid[2], parent_mib, parent_len * sizeof(int)); @@ -158,30 +149,20 @@ udev_dev_enumerate_evdev_sysctl(struct udev_enumerate *ue) for (;;) { nextlen = sizeof(next); if (sysctl(oid, (u_int)oidlen, next, &nextlen, NULL, 0) < 0) - break; /* end of tree or error — done */ + break; next_count = nextlen / sizeof(int); - - /* Stop when we have walked out of the kern.evdev.input subtree */ if (next_count <= parent_len || memcmp(next, parent_mib, parent_len * sizeof(int)) != 0) break; - /* - * next[parent_len] is the internal OID number of the unit - * node. When it changes we have entered a new unit's subtree. - * Resolve the unit number by asking CTL_SYSCTL_NAME for the - * depth-(parent_len+1) prefix, which yields the string - * "kern.evdev.input.N"; parse N from the last token. - */ if (next_count >= parent_len + 2 && next[parent_len] != last_unit_oid) { last_unit_oid = next[parent_len]; name_oid[0] = 0; name_oid[1] = 1; /* CTL_SYSCTL_NAME */ - memcpy(&name_oid[2], next, - (parent_len + 1) * sizeof(int)); + memcpy(&name_oid[2], next, (parent_len + 1) * sizeof(int)); namelen = sizeof(name) - 1; if (sysctl(name_oid, (u_int)(parent_len + 3), name, &namelen, NULL, 0) < 0) @@ -195,19 +176,31 @@ udev_dev_enumerate_evdev_sysctl(struct udev_enumerate *ue) if (*endp != '\0') goto advance; - snprintf(syspath, sizeof(syspath), - DEV_PATH_ROOT "/input/event%d", unit); + snprintf(syspath, sizeof(syspath), syspath_fmt, unit); if (udev_enumerate_add_device(ue, syspath) == -1) return (-1); } advance: - /* Advance: ask for the next OID after `next` */ memcpy(&oid[2], next, nextlen); oidlen = next_count + 2; } return (0); } + +static int +udev_dev_enumerate_evdev_sysctl(struct udev_enumerate *ue) +{ + return (enumerate_sysctl_units(ue, "kern.evdev.input", + DEV_PATH_ROOT "/input/event%d")); +} + +static int +udev_dev_enumerate_drm_sysctl(struct udev_enumerate *ue) +{ + return (enumerate_sysctl_units(ue, "hw.dri", + DEV_PATH_ROOT "/dri/card%d")); +} #endif /* HAVE_SYSCTLBYNAME */ int @@ -233,6 +226,8 @@ udev_dev_enumerate(struct udev_enumerate *ue) if (sysctlbyname("security.jail.jailed", &jailed, &jailed_len, NULL, 0) == 0 && jailed != 0) { ret = udev_dev_enumerate_evdev_sysctl(ue); + if (ret == 0) + ret = udev_dev_enumerate_drm_sysctl(ue); } #endif return (ret);