From 18af5d2236702fcbefae93fd7048c38370fc160a Mon Sep 17 00:00:00 2001 From: Ella Stanforth Date: Wed, 10 Jul 2024 17:55:54 +0000 Subject: [PATCH] Allow invoking helper script as a replacement for udev rules --- Makefile | 5 +- udev_device.c | 124 +++++++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 117 insertions(+), 12 deletions(-) diff --git a/Makefile b/Makefile index 1fc5a1f4..f44609f1 100644 --- a/Makefile +++ b/Makefile @@ -3,11 +3,14 @@ PREFIX = /usr/local LIBDIR = ${PREFIX}/lib +LIBEXECDIR = ${PREFIX}/libexec INCLUDEDIR = ${PREFIX}/include PKGCONFIGDIR = ${LIBDIR}/pkgconfig +RULES_HELPER_PATH=${LIBEXECDIR}/libudev-zero-rules-helper XCFLAGS = ${CPPFLAGS} ${CFLAGS} -std=c99 -fPIC -D_XOPEN_SOURCE=700 \ -Wall -Wextra -Wpedantic -Wmissing-prototypes -Wstrict-prototypes \ - -Wno-unused-parameter + -Wno-unused-parameter \ + -DRULES_HELPER_PATH=\"${RULES_HELPER_PATH}\" XLDFLAGS = ${LDFLAGS} -shared -Wl,-soname,libudev.so.1 XARFLAGS = -rc AR = ar diff --git a/udev_device.c b/udev_device.c index b0577964..f270deb7 100644 --- a/udev_device.c +++ b/udev_device.c @@ -15,12 +15,16 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include +#include #include #include #include #include #include +#include #include +#include #include #include "udev.h" @@ -52,6 +56,18 @@ #define INPUT_PROP_CNT 0x20 #endif +#ifndef RULES_HELPER_PATH +#error "RULES_HELPER_PATH not specified" +#endif + +#ifndef UEVENT_NUM_ENVP +#define UEVENT_NUM_ENVP 64 +#endif + +#ifndef UEVENT_BUFFER_SIZE +#define UEVENT_BUFFER_SIZE 2048 +#endif + struct udev_device { struct udev_list_entry properties; struct udev_list_entry sysattrs; @@ -349,19 +365,10 @@ static char *read_symlink(const char *syspath, const char *name) return strdup(strrchr(link, '/') + 1); } -static int set_properties_from_uevent(struct udev_device *udev_device, const char *syspath) +static void set_properties_from_file(struct udev_device *udev_device, FILE *file) { - char line[LINE_MAX], path[PATH_MAX + sizeof("/uevent")], devnode[PATH_MAX]; - FILE *file; + char line[LINE_MAX], devnode[PATH_MAX]; char *pos; - - snprintf(path, sizeof(path), "%s/uevent", syspath); - file = fopen(path, "r"); - - if (!file) { - return -1; - } - while (fgets(line, sizeof(line), file)) { line[strlen(line) - 1] = '\0'; @@ -374,7 +381,21 @@ static int set_properties_from_uevent(struct udev_device *udev_device, const cha udev_list_entry_add(&udev_device->properties, line, pos + 1, 0); } } +} + +static int set_properties_from_uevent(struct udev_device *udev_device, const char *syspath) +{ + char path[PATH_MAX + sizeof("/uevent")]; + FILE *file; + + snprintf(path, sizeof(path), "%s/uevent", syspath); + file = fopen(path, "r"); + if (!file) { + return -1; + } + + set_properties_from_file(udev_device, file); fclose(file); return 0; } @@ -540,6 +561,85 @@ static void set_properties_from_props(struct udev_device *udev_device) udev_list_entry_add(&udev_device->properties, "ID_PATH", id, 0); } +static int set_properties_from_helper(struct udev_device *udev_device) +{ + char *envp[UEVENT_NUM_ENVP]; + char env[UEVENT_BUFFER_SIZE]; + + size_t env_offset = 0; + size_t env_idx = 0; + struct udev_list_entry *entry; + + for (entry = udev_device_get_properties_list_entry(udev_device); + entry; + entry = udev_list_entry_get_next(entry)) { + const char *name = entry->name; + const char *value = entry->value ? entry->value : ""; + + // '{name}={value}\0' + size_t max_size = strlen(name) + 1 + strlen(value) + 1; + + assert(env_offset + max_size < UEVENT_BUFFER_SIZE); + snprintf(&env[env_offset], max_size, "%s=%s", name, value); + + assert(env_idx < UEVENT_NUM_ENVP); + envp[env_idx] = &env[env_offset]; + + env_offset += max_size; + env_idx++; + } + + assert(env_idx < UEVENT_NUM_ENVP); + envp[env_idx] = NULL; + + int out_pipe[2]; + + if (pipe2(out_pipe, O_CLOEXEC)) { + return -1; + } + + int null_fd = open("/dev/null", O_CLOEXEC | O_RDWR); + + if (null_fd < 0) { + return -1; + } + + posix_spawn_file_actions_t actions; + + posix_spawn_file_actions_init(&actions); + posix_spawn_file_actions_addclose(&actions, out_pipe[0]); + posix_spawn_file_actions_adddup2(&actions, null_fd, 0); + posix_spawn_file_actions_adddup2(&actions, out_pipe[1], 1); + posix_spawn_file_actions_adddup2(&actions, null_fd, 2); + posix_spawn_file_actions_addclose(&actions, out_pipe[1]); + + pid_t pid; + + char argv0[] = RULES_HELPER_PATH; + char *argv[] = { argv0, NULL }; + + if (posix_spawn(&pid, argv0, &actions, NULL, argv, envp)) { + return -1; + } + + posix_spawn_file_actions_destroy(&actions); + close(out_pipe[1]); + close(null_fd); + + FILE *file = fdopen(out_pipe[0], "r"); + + if (!file) { + return -1; + } + + set_properties_from_file(udev_device, file); + + int status; + waitpid(pid, &status, 0); + fclose(file); + return 0; +} + struct udev_device *udev_device_new_from_syspath(struct udev *udev, const char *syspath) { char *subsystem, *driver, *sysname; @@ -593,6 +693,7 @@ struct udev_device *udev_device_new_from_syspath(struct udev *udev, const char * set_properties_from_evdev(udev_device); set_properties_from_props(udev_device); + set_properties_from_helper(udev_device); free(driver); free(subsystem); @@ -712,6 +813,7 @@ struct udev_device *udev_device_new_from_uevent(struct udev *udev, char *buf, si set_properties_from_props(udev_device); set_properties_from_evdev(udev_device); + set_properties_from_helper(udev_device); return udev_device; }