Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 28 additions & 33 deletions udev-dev.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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));
Expand All @@ -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)
Expand All @@ -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
Expand All @@ -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);
Expand Down
Loading