From 380e7f889018d5faaca4eebc2c615ef92c27ac2b Mon Sep 17 00:00:00 2001 From: illiliti Date: Fri, 14 Aug 2020 19:33:18 +0300 Subject: [PATCH 01/18] implement hotplugging support --- udev.h | 3 + udev_device.c | 99 +++++++++++++++++++----- udev_monitor.c | 204 ++++++++++++++++++++++++++++++++++++++++++++++--- 3 files changed, 278 insertions(+), 28 deletions(-) diff --git a/udev.h b/udev.h index b8b2454..08188ea 100644 --- a/udev.h +++ b/udev.h @@ -96,6 +96,9 @@ struct udev_hwdb *udev_hwdb_ref(struct udev_hwdb *hwdb); struct udev_hwdb *udev_hwdb_unref(struct udev_hwdb *hwdb); struct udev_list_entry *udev_hwdb_get_properties_list_entry(struct udev_hwdb *hwdb, const char *modalias, unsigned int flags); +// this is "libudev-zero" extension. do not use if portability is concern +struct udev_device *udev_device_new_from_file(struct udev *udev, const char *path); + #ifdef __cplusplus } #endif diff --git a/udev_device.c b/udev_device.c index e059ad3..a897cb3 100644 --- a/udev_device.c +++ b/udev_device.c @@ -9,7 +9,7 @@ #include "udev.h" #include "udev_list.h" -enum { BITS_SIZE = 96 }; +enum { BITS_MAX = 96 }; struct udev_device { struct udev_list_entry properties; @@ -148,7 +148,7 @@ int udev_device_get_is_initialized(struct udev_device *udev_device) const char *udev_device_get_action(struct udev_device *udev_device) { - return NULL; + return udev_device_get_property_value(udev_device, "ACTION"); } int udev_device_has_tag(struct udev_device *udev_device, const char *tag) @@ -328,7 +328,7 @@ static int populate_bit(unsigned long *arr, const char *val) bit = strtok_r(bits, " ", &save); - for (i = 0; bit && i < BITS_SIZE; i++) { + for (i = 0; bit && i < BITS_MAX; i++) { arr[i] = strtoul(bit, NULL, 16); bit = strtok_r(NULL, " ", &save); } @@ -357,12 +357,12 @@ static int find_bit(unsigned long *arr, int cnt, int bit) static void udev_device_set_properties_from_evdev(struct udev_device *udev_device) { int ev_cnt, rel_cnt, key_cnt, abs_cnt, prop_cnt; - unsigned long prop_bits[BITS_SIZE] = {0}; - unsigned long abs_bits[BITS_SIZE] = {0}; - unsigned long rel_bits[BITS_SIZE] = {0}; - unsigned long key_bits[BITS_SIZE] = {0}; - unsigned long ev_bits[BITS_SIZE] = {0}; - struct udev_device *parent; + unsigned long prop_bits[BITS_MAX] = {0}; + unsigned long abs_bits[BITS_MAX] = {0}; + unsigned long rel_bits[BITS_MAX] = {0}; + unsigned long key_bits[BITS_MAX] = {0}; + unsigned long ev_bits[BITS_MAX] = {0}; + struct udev_device *tmp; const char *subsystem; subsystem = udev_device_get_subsystem(udev_device); @@ -371,25 +371,25 @@ static void udev_device_set_properties_from_evdev(struct udev_device *udev_devic return; } - parent = udev_device_get_parent_with_subsystem_devtype(udev_device, "input", NULL); + tmp = udev_device; while (1) { - if (!parent) { + if (!tmp) { return; } - if (udev_device_get_property_value(parent, "EV")) { + if (udev_device_get_property_value(tmp, "EV")) { break; } - parent = udev_device_get_parent_with_subsystem_devtype(parent, "input", NULL); + tmp = udev_device_get_parent_with_subsystem_devtype(tmp, "input", NULL); } - ev_cnt = populate_bit(ev_bits, udev_device_get_property_value(parent, "EV")); - abs_cnt = populate_bit(abs_bits, udev_device_get_property_value(parent, "ABS")); - rel_cnt = populate_bit(rel_bits, udev_device_get_property_value(parent, "REL")); - key_cnt = populate_bit(key_bits, udev_device_get_property_value(parent, "KEY")); - prop_cnt = populate_bit(prop_bits, udev_device_get_property_value(parent, "PROP")); + ev_cnt = populate_bit(ev_bits, udev_device_get_property_value(tmp, "EV")); + abs_cnt = populate_bit(abs_bits, udev_device_get_property_value(tmp, "ABS")); + rel_cnt = populate_bit(rel_bits, udev_device_get_property_value(tmp, "REL")); + key_cnt = populate_bit(key_bits, udev_device_get_property_value(tmp, "KEY")); + prop_cnt = populate_bit(prop_bits, udev_device_get_property_value(tmp, "PROP")); udev_list_entry_add(&udev_device->properties, "ID_INPUT", "1", 0); @@ -548,6 +548,69 @@ struct udev_device *udev_device_new_from_subsystem_sysname(struct udev *udev, co return NULL; } +struct udev_device *udev_device_new_from_file(struct udev *udev, const char *path) +{ + char line[LINE_MAX], tmp[PATH_MAX]; + struct udev_device *udev_device; + char *key, *val, *sysname; + FILE *file; + int i; + + udev_device = calloc(1, sizeof(struct udev_device)); + + if (!udev_device) { + return NULL; + } + + udev_device->udev = udev; + udev_device->refcount = 1; + udev_device->parent = NULL; + + udev_list_entry_init(&udev_device->properties); + udev_list_entry_init(&udev_device->sysattrs); + + file = fopen(path, "r"); + + if (!file) { + return NULL; + } + + while (fgets(line, sizeof(line), file)) { + line[strcspn(line, "\n")] = '\0'; + + if (strncmp(line, "DEVPATH", 7) == 0) { + snprintf(tmp, sizeof(tmp), "/sys%s", line + 8); + udev_list_entry_add(&udev_device->properties, "SYSPATH", tmp, 0); + udev_list_entry_add(&udev_device->properties, "DEVPATH", line + 8, 0); + } + else if (strncmp(line, "DEVNAME", 7) == 0) { + snprintf(tmp, sizeof(tmp), "/dev/%s", line + 8); + udev_list_entry_add(&udev_device->properties, "DEVNAME", tmp, 0); + } + else if (strchr(line, '=')) { + val = strchr(line, '=') + 1; + key = line; + key[strcspn(key, "=")] = '\0'; + + udev_list_entry_add(&udev_device->properties, key, val, 1); + } + } + + fclose(file); + sysname = strrchr(udev_device_get_syspath(udev_device), '/'); + + for (i = 0; sysname[i] != '\0'; i++) { + if (sysname[i] >= '0' && sysname[i] <= '9') { + udev_list_entry_add(&udev_device->properties, "SYSNUM", sysname + i, 0); + break; + } + } + + udev_list_entry_add(&udev_device->properties, "SYSNAME", sysname, 0); + udev_device_set_properties_from_evdev(udev_device); + return udev_device; +} + struct udev_device *udev_device_new_from_device_id(struct udev *udev, const char *id) { // XXX NOT IMPLEMENTED diff --git a/udev_monitor.c b/udev_monitor.c index 1e88b70..3749817 100644 --- a/udev_monitor.c +++ b/udev_monitor.c @@ -1,33 +1,128 @@ -#include +#include +#include +#include #include #include +#include +#include +#include +#include +#include #include "udev.h" +#include "udev_list.h" + +#ifndef UDEV_MONITOR_DIR +#define UDEV_MONITOR_DIR "/tmp/.libudev-zero" +#endif + +enum { THREADS_MAX = 5 }; struct udev_monitor { + struct udev_list_entry subsystem_match; + struct udev_list_entry devtype_match; + pthread_t thread[THREADS_MAX]; struct udev *udev; int refcount; - int fd[2]; + int sfd[2]; + int ifd; + int efd; }; +static int udev_monitor_filter_devtype(struct udev_monitor *udev_monitor, struct udev_device *udev_device) +{ + struct udev_list_entry *list_entry; + const char *devtype; + + devtype = udev_device_get_devtype(udev_device); + list_entry = udev_list_entry_get_next(&udev_monitor->devtype_match); + + if (!list_entry) { + return 1; + } + + if (!devtype) { + return 0; + } + + while (list_entry) { + if (strcmp(devtype, udev_list_entry_get_name(list_entry)) == 0) { + return 1; + } + + list_entry = udev_list_entry_get_next(list_entry); + } + + return 0; +} + +static int udev_monitor_filter_subsystem(struct udev_monitor *udev_monitor, struct udev_device *udev_device) +{ + struct udev_list_entry *list_entry; + const char *subsystem; + + subsystem = udev_device_get_subsystem(udev_device); + list_entry = udev_list_entry_get_next(&udev_monitor->subsystem_match); + + if (!list_entry) { + return 1; + } + + if (!subsystem) { + return 0; + } + + while (list_entry) { + if (strcmp(subsystem, udev_list_entry_get_name(list_entry)) == 0) { + return 1; + } + + list_entry = udev_list_entry_get_next(list_entry); + } + + return 0; +} + struct udev_device *udev_monitor_receive_device(struct udev_monitor *udev_monitor) { - return (void *)1; + char file[PATH_MAX + sizeof(UDEV_MONITOR_DIR)], data[4096]; + struct udev_device *udev_device; + + if (recv(udev_monitor->sfd[0], data, sizeof(data), 0) == -1) { + return NULL; + } + + snprintf(file, sizeof(file), UDEV_MONITOR_DIR "/%s", data); + udev_device = udev_device_new_from_file(udev_monitor->udev, file); + + if (!udev_device) { + return NULL; + } + + if (!udev_monitor_filter_subsystem(udev_monitor, udev_device) || + !udev_monitor_filter_devtype(udev_monitor, udev_device)) { + udev_device_unref(udev_device); + return NULL; + } + + return NULL; } int udev_monitor_enable_receiving(struct udev_monitor *udev_monitor) { + // TODO pthread_create should be here return 0; } int udev_monitor_set_receive_buffer_size(struct udev_monitor *udev_monitor, int size) { + // XXX NOT IMPLEMENTED return 0; } int udev_monitor_get_fd(struct udev_monitor *udev_monitor) { - return udev_monitor ? udev_monitor->fd[0] : -1; + return udev_monitor ? udev_monitor->sfd[0] : -1; } struct udev *udev_monitor_get_udev(struct udev_monitor *udev_monitor) @@ -37,27 +132,71 @@ struct udev *udev_monitor_get_udev(struct udev_monitor *udev_monitor) int udev_monitor_filter_update(struct udev_monitor *udev_monitor) { + // XXX NOT IMPLEMENTED return 0; } int udev_monitor_filter_remove(struct udev_monitor *udev_monitor) { + // XXX NOT IMPLEMENTED return 0; } int udev_monitor_filter_add_match_subsystem_devtype(struct udev_monitor *udev_monitor, const char *subsystem, const char *devtype) { + if (!udev_monitor || !subsystem) { + return -1; + } + + udev_list_entry_add(&udev_monitor->subsystem_match, subsystem, NULL, 0); + + if (devtype) { + udev_list_entry_add(&udev_monitor->devtype_match, devtype, NULL, 0); + } + return 0; } int udev_monitor_filter_add_match_tag(struct udev_monitor *udev_monitor, const char *tag) { + // XXX NOT IMPLEMENTED return 0; } +static void *udev_monitor_handle_event(void *ptr) +{ + struct udev_monitor *udev_monitor = ptr; + struct inotify_event *event; + struct epoll_event epoll; + char data[4096]; + sigset_t mask; + ssize_t len; + int i; + + sigemptyset(&mask); + sigaddset(&mask, SIGUSR1); + + while (epoll_pwait(udev_monitor->efd, &epoll, 1, -1, &mask) != -1) { + len = read(udev_monitor->ifd, data, sizeof(data)); + + if (len == -1) { + continue; + } + + for (i = 0; i < len; i += sizeof(struct inotify_event) + event->len) { + event = (struct inotify_event *)&data[i]; + send(udev_monitor->sfd[1], event->name, event->len, 0); + } + } + + return NULL; +} + struct udev_monitor *udev_monitor_new_from_netlink(struct udev *udev, const char *name) { struct udev_monitor *udev_monitor; + struct epoll_event epoll; + pthread_attr_t attr; int i; if (!udev || !name) { @@ -70,16 +209,51 @@ struct udev_monitor *udev_monitor_new_from_netlink(struct udev *udev, const char return NULL; } - // TODO better no-op, HELP WANTED ! - if (pipe(udev_monitor->fd) == -1) { + udev_monitor->ifd = inotify_init1(IN_CLOEXEC | IN_NONBLOCK); + + if (udev_monitor->ifd == -1) { free(udev_monitor); return NULL; } - for (i = 0; i < 2; i++) { - fcntl(udev_monitor->fd[i], F_SETFD, FD_CLOEXEC | O_NONBLOCK); + if (inotify_add_watch(udev_monitor->ifd, UDEV_MONITOR_DIR, IN_CREATE) == -1) { + close(udev_monitor->ifd); + free(udev_monitor); + return NULL; } + udev_monitor->efd = epoll_create1(EPOLL_CLOEXEC); + + if (udev_monitor->efd == -1) { + close(udev_monitor->ifd); + free(udev_monitor); + return NULL; + } + + epoll.events = EPOLLIN | EPOLLET; + + if (epoll_ctl(udev_monitor->efd, EPOLL_CTL_ADD, udev_monitor->ifd, &epoll) == -1) { + close(udev_monitor->ifd); + close(udev_monitor->efd); + free(udev_monitor); + return NULL; + } + + if (socketpair(AF_UNIX, SOCK_SEQPACKET | SOCK_CLOEXEC | SOCK_NONBLOCK, 0, udev_monitor->sfd) == -1) { + close(udev_monitor->ifd); + close(udev_monitor->efd); + free(udev_monitor); + return NULL; + } + + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + + for (i = 0; i < THREADS_MAX; i++) { + pthread_create(&udev_monitor->thread[i], &attr, udev_monitor_handle_event, udev_monitor); + } + + pthread_attr_destroy(&attr); udev_monitor->refcount = 1; udev_monitor->udev = udev; return udev_monitor; @@ -107,10 +281,20 @@ struct udev_monitor *udev_monitor_unref(struct udev_monitor *udev_monitor) return NULL; } - for (i = 0; i < 2; i++) { - close(udev_monitor->fd[i]); + udev_list_entry_free_all(&udev_monitor->devtype_match); + udev_list_entry_free_all(&udev_monitor->subsystem_match); + + // TODO fix possible race condition and UB + for (i = 0; i < THREADS_MAX; i++) { + pthread_kill(udev_monitor->thread[i], SIGUSR1); } + for (i = 0; i < 2; i++) { + close(udev_monitor->sfd[i]); + } + + close(udev_monitor->ifd); + close(udev_monitor->efd); free(udev_monitor); return NULL; } From 3029436ea67a6d843e49477d6aa3d27cb7f9020a Mon Sep 17 00:00:00 2001 From: illiliti Date: Fri, 14 Aug 2020 19:44:05 +0300 Subject: [PATCH 02/18] oops --- udev_monitor.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/udev_monitor.c b/udev_monitor.c index 3749817..d7788d3 100644 --- a/udev_monitor.c +++ b/udev_monitor.c @@ -105,7 +105,7 @@ struct udev_device *udev_monitor_receive_device(struct udev_monitor *udev_monito return NULL; } - return NULL; + return udev_device; } int udev_monitor_enable_receiving(struct udev_monitor *udev_monitor) From 508424a3a2cead3c2ccf262ace44f8919e9ec981 Mon Sep 17 00:00:00 2001 From: illiliti Date: Sat, 15 Aug 2020 07:39:02 +0300 Subject: [PATCH 03/18] fix sysname --- udev_device.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/udev_device.c b/udev_device.c index a897cb3..bd1c55b 100644 --- a/udev_device.c +++ b/udev_device.c @@ -597,7 +597,7 @@ struct udev_device *udev_device_new_from_file(struct udev *udev, const char *pat } fclose(file); - sysname = strrchr(udev_device_get_syspath(udev_device), '/'); + sysname = strrchr(udev_device_get_syspath(udev_device), '/') + 1; for (i = 0; sysname[i] != '\0'; i++) { if (sysname[i] >= '0' && sysname[i] <= '9') { From f8c3526a631beb96d37a6bbe2587105b652d6622 Mon Sep 17 00:00:00 2001 From: illiliti Date: Sat, 15 Aug 2020 07:56:36 +0300 Subject: [PATCH 04/18] move pthread_create to udev_device_enable_receiving --- udev_monitor.c | 84 ++++++++++++++++++++++++++------------------------ 1 file changed, 44 insertions(+), 40 deletions(-) diff --git a/udev_monitor.c b/udev_monitor.c index d7788d3..5f7cb91 100644 --- a/udev_monitor.c +++ b/udev_monitor.c @@ -108,9 +108,52 @@ struct udev_device *udev_monitor_receive_device(struct udev_monitor *udev_monito return udev_device; } +static void *udev_monitor_handle_event(void *ptr) +{ + struct udev_monitor *udev_monitor = ptr; + struct inotify_event *event; + struct epoll_event epoll; + char data[4096]; + sigset_t mask; + ssize_t len; + int i; + + sigemptyset(&mask); + sigaddset(&mask, SIGUSR1); + + while (epoll_pwait(udev_monitor->efd, &epoll, 1, -1, &mask) != -1) { + len = read(udev_monitor->ifd, data, sizeof(data)); + + if (len == -1) { + continue; + } + + for (i = 0; i < len; i += sizeof(struct inotify_event) + event->len) { + event = (struct inotify_event *)&data[i]; + send(udev_monitor->sfd[1], event->name, event->len, 0); + } + } + + return NULL; +} + int udev_monitor_enable_receiving(struct udev_monitor *udev_monitor) { - // TODO pthread_create should be here + pthread_attr_t attr; + int i; + + if (!udev_monitor) { + return -1; + } + + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + + for (i = 0; i < THREADS_MAX; i++) { + pthread_create(&udev_monitor->thread[i], &attr, udev_monitor_handle_event, udev_monitor); + } + + pthread_attr_destroy(&attr); return 0; } @@ -163,41 +206,10 @@ int udev_monitor_filter_add_match_tag(struct udev_monitor *udev_monitor, const c return 0; } -static void *udev_monitor_handle_event(void *ptr) -{ - struct udev_monitor *udev_monitor = ptr; - struct inotify_event *event; - struct epoll_event epoll; - char data[4096]; - sigset_t mask; - ssize_t len; - int i; - - sigemptyset(&mask); - sigaddset(&mask, SIGUSR1); - - while (epoll_pwait(udev_monitor->efd, &epoll, 1, -1, &mask) != -1) { - len = read(udev_monitor->ifd, data, sizeof(data)); - - if (len == -1) { - continue; - } - - for (i = 0; i < len; i += sizeof(struct inotify_event) + event->len) { - event = (struct inotify_event *)&data[i]; - send(udev_monitor->sfd[1], event->name, event->len, 0); - } - } - - return NULL; -} - struct udev_monitor *udev_monitor_new_from_netlink(struct udev *udev, const char *name) { struct udev_monitor *udev_monitor; struct epoll_event epoll; - pthread_attr_t attr; - int i; if (!udev || !name) { return NULL; @@ -246,14 +258,6 @@ struct udev_monitor *udev_monitor_new_from_netlink(struct udev *udev, const char return NULL; } - pthread_attr_init(&attr); - pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); - - for (i = 0; i < THREADS_MAX; i++) { - pthread_create(&udev_monitor->thread[i], &attr, udev_monitor_handle_event, udev_monitor); - } - - pthread_attr_destroy(&attr); udev_monitor->refcount = 1; udev_monitor->udev = udev; return udev_monitor; From f1388e805d13c9203861a592fb38ec7b7965eb36 Mon Sep 17 00:00:00 2001 From: illiliti Date: Sat, 15 Aug 2020 10:53:14 +0300 Subject: [PATCH 05/18] create UDEV_MONITOR_DIR --- udev_monitor.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/udev_monitor.c b/udev_monitor.c index 5f7cb91..0b44766 100644 --- a/udev_monitor.c +++ b/udev_monitor.c @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include @@ -210,6 +211,7 @@ struct udev_monitor *udev_monitor_new_from_netlink(struct udev *udev, const char { struct udev_monitor *udev_monitor; struct epoll_event epoll; + struct stat st; if (!udev || !name) { return NULL; @@ -221,6 +223,22 @@ struct udev_monitor *udev_monitor_new_from_netlink(struct udev *udev, const char return NULL; } + if (lstat(UDEV_MONITOR_DIR, &st) != 0) { + if (mkdir(UDEV_MONITOR_DIR, 0) == -1) { + free(udev_monitor); + return NULL; + } + + if (chmod(UDEV_MONITOR_DIR, 0777) == -1) { + free(udev_monitor); + return NULL; + } + } + else if (!S_ISDIR(st.st_mode)) { + free(udev_monitor); + return NULL; + } + udev_monitor->ifd = inotify_init1(IN_CLOEXEC | IN_NONBLOCK); if (udev_monitor->ifd == -1) { From ca2fb1d5faf4729c77a809833f2b96e06bba3b39 Mon Sep 17 00:00:00 2001 From: illiliti Date: Sat, 15 Aug 2020 11:36:12 +0300 Subject: [PATCH 06/18] update readme --- README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 6105d9d..a26abc5 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ What Works * [x] libinput * [x] wlroots * [x] weston -* [ ] libusb - doesn't work for now. temporary workaround is compiling libusb with --disable-udev +* [x] libusb * [x] kwin - [fix](https://github.com/dilyn-corner/KISS-kde/commit/0cc72748e46f859a0fced55b0c3fcc1dd9586a38) * [ ] ??? @@ -19,6 +19,7 @@ Dependencies * C99 compiler (build time) * POSIX make (build time) * POSIX & XSI libc +* epoll & inotify * Linux >= 2.6.39 Installation @@ -36,7 +37,7 @@ TODO * [x] speed up performance * [x] extend devices support -* [ ] implement hotplugging support +* [x] implement hotplugging support Donate ------ From 752f500a90360e7b0815866f59b915ca67baee0d Mon Sep 17 00:00:00 2001 From: illiliti Date: Sat, 15 Aug 2020 12:20:24 +0300 Subject: [PATCH 07/18] grab properties from uevent --- udev_device.c | 39 ++++++++++++++++++++++++++++++--------- 1 file changed, 30 insertions(+), 9 deletions(-) diff --git a/udev_device.c b/udev_device.c index bd1c55b..a217c94 100644 --- a/udev_device.c +++ b/udev_device.c @@ -297,7 +297,7 @@ static void udev_device_set_properties_from_uevent(struct udev_device *udev_devi if (strncmp(line, "DEVNAME", 7) == 0) { snprintf(devnode, sizeof(devnode), "/dev/%s", line + 8); - udev_list_entry_add(&udev_device->properties, "DEVNAME", devnode, 0); + udev_list_entry_add(&udev_device->properties, "DEVNAME", devnode, 1); } else if (strchr(line, '=')) { val = strchr(line, '=') + 1; @@ -551,8 +551,9 @@ struct udev_device *udev_device_new_from_subsystem_sysname(struct udev *udev, co struct udev_device *udev_device_new_from_file(struct udev *udev, const char *path) { char line[LINE_MAX], tmp[PATH_MAX]; + char *subsystem, *driver, *sysname; struct udev_device *udev_device; - char *key, *val, *sysname; + char *key, *val; FILE *file; int i; @@ -582,6 +583,16 @@ struct udev_device *udev_device_new_from_file(struct udev *udev, const char *pat snprintf(tmp, sizeof(tmp), "/sys%s", line + 8); udev_list_entry_add(&udev_device->properties, "SYSPATH", tmp, 0); udev_list_entry_add(&udev_device->properties, "DEVPATH", line + 8, 0); + + sysname = strrchr(tmp, '/') + 1; + udev_list_entry_add(&udev_device->properties, "SYSNAME", sysname, 0); + + for (i = 0; sysname[i] != '\0'; i++) { + if (sysname[i] >= '0' && sysname[i] <= '9') { + udev_list_entry_add(&udev_device->properties, "SYSNUM", sysname + i, 0); + break; + } + } } else if (strncmp(line, "DEVNAME", 7) == 0) { snprintf(tmp, sizeof(tmp), "/dev/%s", line + 8); @@ -596,18 +607,28 @@ struct udev_device *udev_device_new_from_file(struct udev *udev, const char *pat } } - fclose(file); - sysname = strrchr(udev_device_get_syspath(udev_device), '/') + 1; + if (!udev_device_get_driver(udev_device)) { + driver = udev_device_read_symlink(udev_device, "driver"); - for (i = 0; sysname[i] != '\0'; i++) { - if (sysname[i] >= '0' && sysname[i] <= '9') { - udev_list_entry_add(&udev_device->properties, "SYSNUM", sysname + i, 0); - break; + if (driver) { + udev_list_entry_add(&udev_device->properties, "DRIVER", driver, 0); + free(driver); } } - udev_list_entry_add(&udev_device->properties, "SYSNAME", sysname, 0); + if (!udev_device_get_subsystem(udev_device)) { + subsystem = udev_device_read_symlink(udev_device, "subsystem"); + + if (subsystem) { + udev_list_entry_add(&udev_device->properties, "SUBSYSTEM", subsystem, 0); + free(subsystem); + } + } + + udev_device_set_properties_from_uevent(udev_device); udev_device_set_properties_from_evdev(udev_device); + + fclose(file); return udev_device; } From 0105b25e70eba69938362d673865620ad89e2f9f Mon Sep 17 00:00:00 2001 From: illiliti Date: Sat, 15 Aug 2020 15:49:36 +0300 Subject: [PATCH 08/18] add examples --- contrib/helper.sh | 13 +++++++++++++ contrib/mdev.conf | 13 +++++++++++++ 2 files changed, 26 insertions(+) create mode 100644 contrib/helper.sh create mode 100644 contrib/mdev.conf diff --git a/contrib/helper.sh b/contrib/helper.sh new file mode 100644 index 0000000..ef9877a --- /dev/null +++ b/contrib/helper.sh @@ -0,0 +1,13 @@ +#!/bin/sh -f +# +# this helper required for CONFIG_UEVENT_HELPER aka /proc/sys/kernel/hotplug +# for mdev you can use simple one-liner, refer to mdev.conf for more info +# +# usage: +# echo /full/path/to/helper.sh > /proc/sys/kernel/hotplug +# echo "/full/path/to/helper.sh UDEV_MONITOR_DIR" > /proc/sys/kernel/hotplug +# + +# NOTE: writing variables to file (e.g PWD or PATH) +# that are not related to uevent properties is harmless +env > "${1:-/tmp/.libudev-zero}/uevent.$$" diff --git a/contrib/mdev.conf b/contrib/mdev.conf new file mode 100644 index 0000000..ff5334c --- /dev/null +++ b/contrib/mdev.conf @@ -0,0 +1,13 @@ +# +# example basic mdev config representing libudev-zero usage +# uncomment needed rules +# +# NOTE: you must change "/tmp/.libudev-zero" if you use non-default UDEV_MONITOR_DIR +# NOTE: mdev can only handle ADD and/or REMOVE events + +# will handle all uevents +#-.* root:root 660 *env > /tmp/.libudev-zero/uevent.$$ + +# will handle only drm and input uevents (recommended) +#SUBSYSTEM=drm;.* root:video 660 *env > /tmp/.libudev-zero/uevent.$$ +#SUBSYSTEM=input;.* root:input 660 *env > /tmp/.libudev-zero/uevent.$$ From 408249b34b26801f604b6574e07168ae9aadfeea Mon Sep 17 00:00:00 2001 From: illiliti Date: Sun, 16 Aug 2020 12:53:13 +0300 Subject: [PATCH 09/18] simplify key=val parsing --- udev_device.c | 22 ++++++++-------------- 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/udev_device.c b/udev_device.c index a217c94..7256df5 100644 --- a/udev_device.c +++ b/udev_device.c @@ -282,7 +282,7 @@ static char *udev_device_read_symlink(struct udev_device *udev_device, const cha static void udev_device_set_properties_from_uevent(struct udev_device *udev_device) { char line[LINE_MAX], path[PATH_MAX]; - char *key, *val, devnode[PATH_MAX]; + char *pos, devnode[PATH_MAX]; FILE *file; snprintf(path, sizeof(path), "%s/uevent", udev_device_get_syspath(udev_device)); @@ -299,12 +299,9 @@ static void udev_device_set_properties_from_uevent(struct udev_device *udev_devi snprintf(devnode, sizeof(devnode), "/dev/%s", line + 8); udev_list_entry_add(&udev_device->properties, "DEVNAME", devnode, 1); } - else if (strchr(line, '=')) { - val = strchr(line, '=') + 1; - key = line; - key[strcspn(key, "=")] = '\0'; - - udev_list_entry_add(&udev_device->properties, key, val, 1); + else if ((pos = strchr(line, '='))) { + *pos = '\0'; + udev_list_entry_add(&udev_device->properties, line, pos + 1, 1); } } @@ -553,8 +550,8 @@ struct udev_device *udev_device_new_from_file(struct udev *udev, const char *pat char line[LINE_MAX], tmp[PATH_MAX]; char *subsystem, *driver, *sysname; struct udev_device *udev_device; - char *key, *val; FILE *file; + char *pos; int i; udev_device = calloc(1, sizeof(struct udev_device)); @@ -598,12 +595,9 @@ struct udev_device *udev_device_new_from_file(struct udev *udev, const char *pat snprintf(tmp, sizeof(tmp), "/dev/%s", line + 8); udev_list_entry_add(&udev_device->properties, "DEVNAME", tmp, 0); } - else if (strchr(line, '=')) { - val = strchr(line, '=') + 1; - key = line; - key[strcspn(key, "=")] = '\0'; - - udev_list_entry_add(&udev_device->properties, key, val, 1); + else if ((pos = strchr(line, '='))) { + *pos = '\0'; + udev_list_entry_add(&udev_device->properties, line, pos + 1, 1); } } From bcd28de8583f40ec64dd234ac8e28d3b2bd44e9b Mon Sep 17 00:00:00 2001 From: illiliti Date: Sun, 16 Aug 2020 14:00:03 +0300 Subject: [PATCH 10/18] fix potential UB while reading sysattr --- udev_device.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/udev_device.c b/udev_device.c index 7256df5..92019ae 100644 --- a/udev_device.c +++ b/udev_device.c @@ -193,7 +193,9 @@ const char *udev_device_get_sysattr_value(struct udev_device *udev_device, const struct udev_list_entry *list_entry; char data[BUFSIZ], path[PATH_MAX]; struct stat st; + size_t len; FILE *file; + char *pos; if (!udev_device || !sysattr) { return NULL; @@ -217,13 +219,21 @@ const char *udev_device_get_sysattr_value(struct udev_device *udev_device, const return NULL; } - if (fread(data, 1, sizeof(data), file) != sizeof(data) && ferror(file)) { + len = fread(data, 1, sizeof(data), file); + + if (len != sizeof(data) && ferror(file)) { fclose(file); return NULL; } + if ((pos = memchr(data, '\n', len))) { + *pos = '\0'; + } + else { + data[len] = '\0'; + } + fclose(file); - data[strcspn(data, "\n")] = '\0'; list_entry = udev_list_entry_add(&udev_device->sysattrs, sysattr, data, 0); return udev_list_entry_get_value(list_entry); } From f6afe3602b2a595af547ec190a1fa76cae8d2e87 Mon Sep 17 00:00:00 2001 From: illiliti Date: Mon, 17 Aug 2020 16:57:50 +0300 Subject: [PATCH 11/18] eliminate race conditions --- udev_monitor.c | 105 +++++++++++++++++++++++++++++++++---------------- 1 file changed, 72 insertions(+), 33 deletions(-) diff --git a/udev_monitor.c b/udev_monitor.c index 0b44766..c434ad8 100644 --- a/udev_monitor.c +++ b/udev_monitor.c @@ -1,3 +1,4 @@ +#include #include #include #include @@ -23,9 +24,11 @@ struct udev_monitor { struct udev_list_entry subsystem_match; struct udev_list_entry devtype_match; pthread_t thread[THREADS_MAX]; + pthread_barrier_t barrier; struct udev *udev; int refcount; int sfd[2]; + int pfd[2]; int ifd; int efd; }; @@ -113,16 +116,26 @@ static void *udev_monitor_handle_event(void *ptr) { struct udev_monitor *udev_monitor = ptr; struct inotify_event *event; - struct epoll_event epoll; + struct epoll_event epoll[2]; char data[4096]; sigset_t mask; ssize_t len; int i; - sigemptyset(&mask); - sigaddset(&mask, SIGUSR1); + sigfillset(&mask); + pthread_sigmask(SIG_BLOCK, &mask, NULL); + + while (epoll_wait(udev_monitor->efd, epoll, 2, -1) != -1) { + for (i = 0; i < 2; i++) { + if (epoll[i].data.fd == udev_monitor->pfd[0]) { + read(udev_monitor->pfd[0], data, sizeof(data)); + write(udev_monitor->pfd[1], "1", 1); + + pthread_barrier_wait(&udev_monitor->barrier); + return NULL; + } + } - while (epoll_pwait(udev_monitor->efd, &epoll, 1, -1, &mask) != -1) { len = read(udev_monitor->ifd, data, sizeof(data)); if (len == -1) { @@ -135,6 +148,7 @@ static void *udev_monitor_handle_event(void *ptr) } } + pthread_barrier_wait(&udev_monitor->barrier); return NULL; } @@ -149,6 +163,7 @@ int udev_monitor_enable_receiving(struct udev_monitor *udev_monitor) pthread_attr_init(&attr); pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + pthread_barrier_init(&udev_monitor->barrier, NULL, THREADS_MAX + 1); for (i = 0; i < THREADS_MAX; i++) { pthread_create(&udev_monitor->thread[i], &attr, udev_monitor_handle_event, udev_monitor); @@ -210,8 +225,9 @@ int udev_monitor_filter_add_match_tag(struct udev_monitor *udev_monitor, const c struct udev_monitor *udev_monitor_new_from_netlink(struct udev *udev, const char *name) { struct udev_monitor *udev_monitor; - struct epoll_event epoll; + struct epoll_event epoll[2]; struct stat st; + int i; if (!udev || !name) { return NULL; @@ -224,12 +240,8 @@ struct udev_monitor *udev_monitor_new_from_netlink(struct udev *udev, const char } if (lstat(UDEV_MONITOR_DIR, &st) != 0) { - if (mkdir(UDEV_MONITOR_DIR, 0) == -1) { - free(udev_monitor); - return NULL; - } - - if (chmod(UDEV_MONITOR_DIR, 0777) == -1) { + if (mkdir(UDEV_MONITOR_DIR, 0) == -1 || + chmod(UDEV_MONITOR_DIR, 0777) == -1) { free(udev_monitor); return NULL; } @@ -239,37 +251,63 @@ struct udev_monitor *udev_monitor_new_from_netlink(struct udev *udev, const char return NULL; } - udev_monitor->ifd = inotify_init1(IN_CLOEXEC | IN_NONBLOCK); - - if (udev_monitor->ifd == -1) { - free(udev_monitor); - return NULL; - } - - if (inotify_add_watch(udev_monitor->ifd, UDEV_MONITOR_DIR, IN_CREATE) == -1) { - close(udev_monitor->ifd); - free(udev_monitor); - return NULL; - } - udev_monitor->efd = epoll_create1(EPOLL_CLOEXEC); if (udev_monitor->efd == -1) { - close(udev_monitor->ifd); free(udev_monitor); return NULL; } - epoll.events = EPOLLIN | EPOLLET; + udev_monitor->ifd = inotify_init1(IN_CLOEXEC | IN_NONBLOCK); - if (epoll_ctl(udev_monitor->efd, EPOLL_CTL_ADD, udev_monitor->ifd, &epoll) == -1) { + if (udev_monitor->ifd == -1) { + close(udev_monitor->efd); + free(udev_monitor); + return NULL; + } + + if (inotify_add_watch(udev_monitor->ifd, UDEV_MONITOR_DIR, IN_CLOSE_WRITE) == -1) { close(udev_monitor->ifd); close(udev_monitor->efd); free(udev_monitor); return NULL; } - if (socketpair(AF_UNIX, SOCK_SEQPACKET | SOCK_CLOEXEC | SOCK_NONBLOCK, 0, udev_monitor->sfd) == -1) { + if (pipe(udev_monitor->pfd) == -1) { + close(udev_monitor->ifd); + close(udev_monitor->efd); + free(udev_monitor); + return NULL; + } + + for (i = 0; i < 2; i++) { + fcntl(udev_monitor->pfd[i], F_SETFD, FD_CLOEXEC); + fcntl(udev_monitor->pfd[i], F_SETFL, O_NONBLOCK); + } + + for (i = 0; i < 2; i++) { + epoll[i].events = EPOLLIN | EPOLLET; + } + + epoll[1].data.fd = udev_monitor->pfd[0]; + + if (epoll_ctl(udev_monitor->efd, EPOLL_CTL_ADD, udev_monitor->ifd, &epoll[0]) == -1 || + epoll_ctl(udev_monitor->efd, EPOLL_CTL_ADD, udev_monitor->pfd[0], &epoll[1]) == -1) { + for (i = 0; i < 2; i++) { + close(udev_monitor->pfd[i]); + } + + close(udev_monitor->ifd); + close(udev_monitor->efd); + free(udev_monitor); + return NULL; + } + + if (socketpair(AF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0, udev_monitor->sfd) == -1) { + for (i = 0; i < 2; i++) { + close(udev_monitor->pfd[i]); + } + close(udev_monitor->ifd); close(udev_monitor->efd); free(udev_monitor); @@ -303,16 +341,17 @@ struct udev_monitor *udev_monitor_unref(struct udev_monitor *udev_monitor) return NULL; } - udev_list_entry_free_all(&udev_monitor->devtype_match); udev_list_entry_free_all(&udev_monitor->subsystem_match); + udev_list_entry_free_all(&udev_monitor->devtype_match); - // TODO fix possible race condition and UB - for (i = 0; i < THREADS_MAX; i++) { - pthread_kill(udev_monitor->thread[i], SIGUSR1); - } + write(udev_monitor->pfd[1], "1", 1); + + pthread_barrier_wait(&udev_monitor->barrier); + pthread_barrier_destroy(&udev_monitor->barrier); for (i = 0; i < 2; i++) { close(udev_monitor->sfd[i]); + close(udev_monitor->pfd[i]); } close(udev_monitor->ifd); From b17173163f73be305b79e72377e4a5dd2d1e582d Mon Sep 17 00:00:00 2001 From: illiliti Date: Mon, 17 Aug 2020 17:30:07 +0300 Subject: [PATCH 12/18] drop sizeof --- udev_monitor.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/udev_monitor.c b/udev_monitor.c index c434ad8..9ceebdb 100644 --- a/udev_monitor.c +++ b/udev_monitor.c @@ -128,7 +128,7 @@ static void *udev_monitor_handle_event(void *ptr) while (epoll_wait(udev_monitor->efd, epoll, 2, -1) != -1) { for (i = 0; i < 2; i++) { if (epoll[i].data.fd == udev_monitor->pfd[0]) { - read(udev_monitor->pfd[0], data, sizeof(data)); + read(udev_monitor->pfd[0], data, 1); write(udev_monitor->pfd[1], "1", 1); pthread_barrier_wait(&udev_monitor->barrier); From b53bb9eb8266c72e1f4a03fa78113bef8e8ed717 Mon Sep 17 00:00:00 2001 From: illiliti Date: Tue, 18 Aug 2020 12:28:22 +0300 Subject: [PATCH 13/18] add helper.c --- contrib/helper.c | 63 ++++++++++++++++++++++++++++++++++++++++++++++++ udev_monitor.c | 2 +- 2 files changed, 64 insertions(+), 1 deletion(-) create mode 100644 contrib/helper.c diff --git a/contrib/helper.c b/contrib/helper.c new file mode 100644 index 0000000..ba46094 --- /dev/null +++ b/contrib/helper.c @@ -0,0 +1,63 @@ +/* + * this helper pretty similar to helper.sh, but it + * doesn't write unrelated variables to file (e.g PWD or PATH) + * + * build: + * cc helper.c -o helper + * + * usage: + * echo /full/path/to/helper > /proc/sys/kernel/hotplug + * echo "/full/path/to/helper UDEV_MONITOR_DIR" > /proc/sys/kernel/hotplug + */ + +#include +#include +#include +#include +#include + +int main(int argc, char **argv) +{ + extern char **environ; + char path[PATH_MAX]; + char *dir; + int fd, i; + + switch (argc) { + case 1: + dir = "/tmp/.libudev-zero"; + break; + case 2: + dir = argv[1]; + break; + default: + fprintf(stderr, "usage: %s [dir]\n", argv[0]); + return 1; + } + + snprintf(path, sizeof(path), "%s/uevent.XXXXXX", dir); + fd = mkstemp(path); + + if (fd == -1) { + perror("mkstemp"); + return 1; + } + + for (i = 0; environ[i]; i++) { + if (strncmp(environ[i], "PATH", 4) == 0 || + strncmp(environ[i], "HOME", 4) == 0) { + continue; + } + + if (write(fd, environ[i], strlen(environ[i])) == -1 || + write(fd, "\n", 1) == -1) { + perror("write"); + close(fd); + unlink(path); + return 1; + } + } + + close(fd); + return 0; +} diff --git a/udev_monitor.c b/udev_monitor.c index 9ceebdb..b24479c 100644 --- a/udev_monitor.c +++ b/udev_monitor.c @@ -266,7 +266,7 @@ struct udev_monitor *udev_monitor_new_from_netlink(struct udev *udev, const char return NULL; } - if (inotify_add_watch(udev_monitor->ifd, UDEV_MONITOR_DIR, IN_CLOSE_WRITE) == -1) { + if (inotify_add_watch(udev_monitor->ifd, UDEV_MONITOR_DIR, IN_CLOSE_WRITE | IN_EXCL_UNLINK) == -1) { close(udev_monitor->ifd); close(udev_monitor->efd); free(udev_monitor); From 25a9dd70b0080d82600cf1eac34e7bc7051a3323 Mon Sep 17 00:00:00 2001 From: illiliti Date: Wed, 19 Aug 2020 13:17:10 +0300 Subject: [PATCH 14/18] replace self-pipe trick with conditional variable --- udev_monitor.c | 89 +++++++++++++++++++++----------------------------- 1 file changed, 37 insertions(+), 52 deletions(-) diff --git a/udev_monitor.c b/udev_monitor.c index b24479c..f527f05 100644 --- a/udev_monitor.c +++ b/udev_monitor.c @@ -1,4 +1,3 @@ -#include #include #include #include @@ -24,11 +23,12 @@ struct udev_monitor { struct udev_list_entry subsystem_match; struct udev_list_entry devtype_match; pthread_t thread[THREADS_MAX]; - pthread_barrier_t barrier; + pthread_cond_t condition; + pthread_mutex_t mutex; struct udev *udev; + int thread_alive; int refcount; int sfd[2]; - int pfd[2]; int ifd; int efd; }; @@ -112,30 +112,30 @@ struct udev_device *udev_monitor_receive_device(struct udev_monitor *udev_monito return udev_device; } +static void udev_monitor_abort_thread(void *ptr) +{ + struct udev_monitor *udev_monitor = ptr; + + pthread_mutex_lock(&udev_monitor->mutex); + udev_monitor->thread_alive--; + pthread_cond_signal(&udev_monitor->condition); + pthread_mutex_unlock(&udev_monitor->mutex); +} + static void *udev_monitor_handle_event(void *ptr) { struct udev_monitor *udev_monitor = ptr; struct inotify_event *event; - struct epoll_event epoll[2]; + struct epoll_event epoll; char data[4096]; sigset_t mask; ssize_t len; int i; - sigfillset(&mask); - pthread_sigmask(SIG_BLOCK, &mask, NULL); - - while (epoll_wait(udev_monitor->efd, epoll, 2, -1) != -1) { - for (i = 0; i < 2; i++) { - if (epoll[i].data.fd == udev_monitor->pfd[0]) { - read(udev_monitor->pfd[0], data, 1); - write(udev_monitor->pfd[1], "1", 1); - - pthread_barrier_wait(&udev_monitor->barrier); - return NULL; - } - } + sigemptyset(&mask); + pthread_cleanup_push(udev_monitor_abort_thread, udev_monitor); + while (epoll_pwait(udev_monitor->efd, &epoll, 1, -1, &mask) != -1) { len = read(udev_monitor->ifd, data, sizeof(data)); if (len == -1) { @@ -148,7 +148,7 @@ static void *udev_monitor_handle_event(void *ptr) } } - pthread_barrier_wait(&udev_monitor->barrier); + pthread_cleanup_pop(1); return NULL; } @@ -161,9 +161,12 @@ int udev_monitor_enable_receiving(struct udev_monitor *udev_monitor) return -1; } + udev_monitor->thread_alive = THREADS_MAX; + pthread_attr_init(&attr); + pthread_mutex_init(&udev_monitor->mutex, NULL); + pthread_cond_init(&udev_monitor->condition, NULL); pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); - pthread_barrier_init(&udev_monitor->barrier, NULL, THREADS_MAX + 1); for (i = 0; i < THREADS_MAX; i++) { pthread_create(&udev_monitor->thread[i], &attr, udev_monitor_handle_event, udev_monitor); @@ -225,9 +228,8 @@ int udev_monitor_filter_add_match_tag(struct udev_monitor *udev_monitor, const c struct udev_monitor *udev_monitor_new_from_netlink(struct udev *udev, const char *name) { struct udev_monitor *udev_monitor; - struct epoll_event epoll[2]; + struct epoll_event epoll; struct stat st; - int i; if (!udev || !name) { return NULL; @@ -273,30 +275,9 @@ struct udev_monitor *udev_monitor_new_from_netlink(struct udev *udev, const char return NULL; } - if (pipe(udev_monitor->pfd) == -1) { - close(udev_monitor->ifd); - close(udev_monitor->efd); - free(udev_monitor); - return NULL; - } - - for (i = 0; i < 2; i++) { - fcntl(udev_monitor->pfd[i], F_SETFD, FD_CLOEXEC); - fcntl(udev_monitor->pfd[i], F_SETFL, O_NONBLOCK); - } - - for (i = 0; i < 2; i++) { - epoll[i].events = EPOLLIN | EPOLLET; - } - - epoll[1].data.fd = udev_monitor->pfd[0]; - - if (epoll_ctl(udev_monitor->efd, EPOLL_CTL_ADD, udev_monitor->ifd, &epoll[0]) == -1 || - epoll_ctl(udev_monitor->efd, EPOLL_CTL_ADD, udev_monitor->pfd[0], &epoll[1]) == -1) { - for (i = 0; i < 2; i++) { - close(udev_monitor->pfd[i]); - } + epoll.events = EPOLLIN | EPOLLET; + if (epoll_ctl(udev_monitor->efd, EPOLL_CTL_ADD, udev_monitor->ifd, &epoll) == -1) { close(udev_monitor->ifd); close(udev_monitor->efd); free(udev_monitor); @@ -304,10 +285,6 @@ struct udev_monitor *udev_monitor_new_from_netlink(struct udev *udev, const char } if (socketpair(AF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0, udev_monitor->sfd) == -1) { - for (i = 0; i < 2; i++) { - close(udev_monitor->pfd[i]); - } - close(udev_monitor->ifd); close(udev_monitor->efd); free(udev_monitor); @@ -344,14 +321,22 @@ struct udev_monitor *udev_monitor_unref(struct udev_monitor *udev_monitor) udev_list_entry_free_all(&udev_monitor->subsystem_match); udev_list_entry_free_all(&udev_monitor->devtype_match); - write(udev_monitor->pfd[1], "1", 1); + for (i = 0; i < THREADS_MAX; i++) { + pthread_cancel(udev_monitor->thread[i]); + } - pthread_barrier_wait(&udev_monitor->barrier); - pthread_barrier_destroy(&udev_monitor->barrier); + pthread_mutex_lock(&udev_monitor->mutex); + + while (udev_monitor->thread_alive) { + pthread_cond_wait(&udev_monitor->condition, &udev_monitor->mutex); + } + + pthread_mutex_unlock(&udev_monitor->mutex); + pthread_mutex_destroy(&udev_monitor->mutex); + pthread_cond_destroy(&udev_monitor->condition); for (i = 0; i < 2; i++) { close(udev_monitor->sfd[i]); - close(udev_monitor->pfd[i]); } close(udev_monitor->ifd); From d6e774460df9f5f2ccfb647d7281af718befaf93 Mon Sep 17 00:00:00 2001 From: illiliti Date: Wed, 19 Aug 2020 18:09:47 +0300 Subject: [PATCH 15/18] simplify using barrier --- udev_monitor.c | 33 +++++---------------------------- 1 file changed, 5 insertions(+), 28 deletions(-) diff --git a/udev_monitor.c b/udev_monitor.c index f527f05..61d4449 100644 --- a/udev_monitor.c +++ b/udev_monitor.c @@ -23,10 +23,8 @@ struct udev_monitor { struct udev_list_entry subsystem_match; struct udev_list_entry devtype_match; pthread_t thread[THREADS_MAX]; - pthread_cond_t condition; - pthread_mutex_t mutex; + pthread_barrier_t barrier; struct udev *udev; - int thread_alive; int refcount; int sfd[2]; int ifd; @@ -112,16 +110,6 @@ struct udev_device *udev_monitor_receive_device(struct udev_monitor *udev_monito return udev_device; } -static void udev_monitor_abort_thread(void *ptr) -{ - struct udev_monitor *udev_monitor = ptr; - - pthread_mutex_lock(&udev_monitor->mutex); - udev_monitor->thread_alive--; - pthread_cond_signal(&udev_monitor->condition); - pthread_mutex_unlock(&udev_monitor->mutex); -} - static void *udev_monitor_handle_event(void *ptr) { struct udev_monitor *udev_monitor = ptr; @@ -133,7 +121,6 @@ static void *udev_monitor_handle_event(void *ptr) int i; sigemptyset(&mask); - pthread_cleanup_push(udev_monitor_abort_thread, udev_monitor); while (epoll_pwait(udev_monitor->efd, &epoll, 1, -1, &mask) != -1) { len = read(udev_monitor->ifd, data, sizeof(data)); @@ -148,7 +135,7 @@ static void *udev_monitor_handle_event(void *ptr) } } - pthread_cleanup_pop(1); + pthread_barrier_wait(&udev_monitor->barrier); return NULL; } @@ -161,12 +148,9 @@ int udev_monitor_enable_receiving(struct udev_monitor *udev_monitor) return -1; } - udev_monitor->thread_alive = THREADS_MAX; - pthread_attr_init(&attr); - pthread_mutex_init(&udev_monitor->mutex, NULL); - pthread_cond_init(&udev_monitor->condition, NULL); pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + pthread_barrier_init(&udev_monitor->barrier, NULL, THREADS_MAX + 1); for (i = 0; i < THREADS_MAX; i++) { pthread_create(&udev_monitor->thread[i], &attr, udev_monitor_handle_event, udev_monitor); @@ -325,15 +309,8 @@ struct udev_monitor *udev_monitor_unref(struct udev_monitor *udev_monitor) pthread_cancel(udev_monitor->thread[i]); } - pthread_mutex_lock(&udev_monitor->mutex); - - while (udev_monitor->thread_alive) { - pthread_cond_wait(&udev_monitor->condition, &udev_monitor->mutex); - } - - pthread_mutex_unlock(&udev_monitor->mutex); - pthread_mutex_destroy(&udev_monitor->mutex); - pthread_cond_destroy(&udev_monitor->condition); + pthread_barrier_wait(&udev_monitor->barrier); + pthread_barrier_destroy(&udev_monitor->barrier); for (i = 0; i < 2; i++) { close(udev_monitor->sfd[i]); From 662062604e2233eb5dc455cefb9a9e3bef369448 Mon Sep 17 00:00:00 2001 From: illiliti Date: Wed, 19 Aug 2020 22:51:27 +0300 Subject: [PATCH 16/18] check IN_IGNORED --- udev_monitor.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/udev_monitor.c b/udev_monitor.c index 61d4449..77fac66 100644 --- a/udev_monitor.c +++ b/udev_monitor.c @@ -131,6 +131,12 @@ static void *udev_monitor_handle_event(void *ptr) for (i = 0; i < len; i += sizeof(struct inotify_event) + event->len) { event = (struct inotify_event *)&data[i]; + + // TODO user deleted directory, what should we do ? + if (event->mask & IN_IGNORED) { + break; + } + send(udev_monitor->sfd[1], event->name, event->len, 0); } } From 44409c16e7e3c5875b642a088b4b24bb637eaa67 Mon Sep 17 00:00:00 2001 From: illiliti Date: Fri, 21 Aug 2020 14:55:24 +0300 Subject: [PATCH 17/18] attempt to write docs --- README.md | 43 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 42 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index a26abc5..3bf3882 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,12 @@ libudev-zero ============ -Drop-in replacement for libudev intended to work without daemon +Drop-in replacement for libudev enables you to use whatever +device manager you like without worrying about udev dependency at all! What Works ---------- + * [x] xorg-server * [x] libinput * [x] wlroots @@ -32,6 +34,45 @@ make PREFIX=/usr install # will overwrite existing udev libraries if any # here we go ! ``` +Hotplugging +----------- + +There is no complicated or overengineered way to use hotplugging. Everything is +portable as much as possible. To use hotplugging the only thing you need is +uevent's receiver (device manager, busybox `uevent`, CONFIG_UEVENT_HELPER, ...). +I will describe only mdev and CONFIG_UEVENT_HELPER because their usage is very basic. +For busybox `uevent` you need to write your own parser which is kinda ... complex. + +UDEV_MONITOR_DIR is arbitrary directory where uevent files stored. +Default is `/tmp/.libudev-zero`. You can change it at build time by appending +`-DUDEV_MONITOR_DIR=` to CFLAGS. I don't recommend setting UDEV_MONITOR_DIR +to regular fs (i.e non-tmpfs) because unneeded files aren't automatically discarded +after reboot or termination (yet). + +* mdev + + - merge [mdev.conf](contrib/mdev.conf) with your mdev.conf + - restart mdev daemon + +* CONFIG_UEVENT_HELPER + + - ensure that CONFIG_UEVENT_HELPER enabled in kernel + - add full path of [helper.sh](contrib/helper.sh) (must be executable) or + [helper.c](contrib/helper.c) (compile it first) to /proc/sys/kernel/hotplug + + example: + ```sh + echo /full/path/to/helper > /proc/sys/kernel/hotplug # will use default UDEV_MONITOR_DIR + OR + echo "/full/path/to/helper " > /proc/sys/kernel/hotplug # change to your UDEV_MONITOR_DIR + ``` + +* run application which uses hotplugging (e.g xorg-server) +* unplug and plug something to test working capacity + +That's all! If you realized that this doesn't work for you, +you can always open an issue and describe your bug. + TODO ---- From b54f639cfdc720c549bce8e483fae31b1a584a3d Mon Sep 17 00:00:00 2001 From: illiliti Date: Sun, 23 Aug 2020 00:51:10 +0300 Subject: [PATCH 18/18] final fixes --- udev_device.c | 67 ++++++++++++++++++++------------------------------- 1 file changed, 26 insertions(+), 41 deletions(-) diff --git a/udev_device.c b/udev_device.c index 92019ae..bebfd2c 100644 --- a/udev_device.c +++ b/udev_device.c @@ -307,7 +307,7 @@ static void udev_device_set_properties_from_uevent(struct udev_device *udev_devi if (strncmp(line, "DEVNAME", 7) == 0) { snprintf(devnode, sizeof(devnode), "/dev/%s", line + 8); - udev_list_entry_add(&udev_device->properties, "DEVNAME", devnode, 1); + udev_list_entry_add(&udev_device->properties, "DEVNAME", devnode, 0); } else if ((pos = strchr(line, '='))) { *pos = '\0'; @@ -369,7 +369,7 @@ static void udev_device_set_properties_from_evdev(struct udev_device *udev_devic unsigned long rel_bits[BITS_MAX] = {0}; unsigned long key_bits[BITS_MAX] = {0}; unsigned long ev_bits[BITS_MAX] = {0}; - struct udev_device *tmp; + struct udev_device *parent; const char *subsystem; subsystem = udev_device_get_subsystem(udev_device); @@ -378,25 +378,25 @@ static void udev_device_set_properties_from_evdev(struct udev_device *udev_devic return; } - tmp = udev_device; + parent = udev_device; while (1) { - if (!tmp) { + if (!parent) { return; } - if (udev_device_get_property_value(tmp, "EV")) { + if (udev_device_get_property_value(parent, "EV")) { break; } - tmp = udev_device_get_parent_with_subsystem_devtype(tmp, "input", NULL); + parent = udev_device_get_parent_with_subsystem_devtype(parent, "input", NULL); } - ev_cnt = populate_bit(ev_bits, udev_device_get_property_value(tmp, "EV")); - abs_cnt = populate_bit(abs_bits, udev_device_get_property_value(tmp, "ABS")); - rel_cnt = populate_bit(rel_bits, udev_device_get_property_value(tmp, "REL")); - key_cnt = populate_bit(key_bits, udev_device_get_property_value(tmp, "KEY")); - prop_cnt = populate_bit(prop_bits, udev_device_get_property_value(tmp, "PROP")); + ev_cnt = populate_bit(ev_bits, udev_device_get_property_value(parent, "EV")); + abs_cnt = populate_bit(abs_bits, udev_device_get_property_value(parent, "ABS")); + rel_cnt = populate_bit(rel_bits, udev_device_get_property_value(parent, "REL")); + key_cnt = populate_bit(key_bits, udev_device_get_property_value(parent, "KEY")); + prop_cnt = populate_bit(prop_bits, udev_device_get_property_value(parent, "PROP")); udev_list_entry_add(&udev_device->properties, "ID_INPUT", "1", 0); @@ -557,11 +557,10 @@ struct udev_device *udev_device_new_from_subsystem_sysname(struct udev *udev, co struct udev_device *udev_device_new_from_file(struct udev *udev, const char *path) { - char line[LINE_MAX], tmp[PATH_MAX]; - char *subsystem, *driver, *sysname; + char line[LINE_MAX], syspath[PATH_MAX], devnode[PATH_MAX]; struct udev_device *udev_device; + char *pos, *sysname; FILE *file; - char *pos; int i; udev_device = calloc(1, sizeof(struct udev_device)); @@ -587,11 +586,11 @@ struct udev_device *udev_device_new_from_file(struct udev *udev, const char *pat line[strcspn(line, "\n")] = '\0'; if (strncmp(line, "DEVPATH", 7) == 0) { - snprintf(tmp, sizeof(tmp), "/sys%s", line + 8); - udev_list_entry_add(&udev_device->properties, "SYSPATH", tmp, 0); + snprintf(syspath, sizeof(syspath), "/sys%s", line + 8); + udev_list_entry_add(&udev_device->properties, "SYSPATH", syspath, 0); udev_list_entry_add(&udev_device->properties, "DEVPATH", line + 8, 0); - sysname = strrchr(tmp, '/') + 1; + sysname = strrchr(syspath, '/') + 1; udev_list_entry_add(&udev_device->properties, "SYSNAME", sysname, 0); for (i = 0; sysname[i] != '\0'; i++) { @@ -602,37 +601,23 @@ struct udev_device *udev_device_new_from_file(struct udev *udev, const char *pat } } else if (strncmp(line, "DEVNAME", 7) == 0) { - snprintf(tmp, sizeof(tmp), "/dev/%s", line + 8); - udev_list_entry_add(&udev_device->properties, "DEVNAME", tmp, 0); + snprintf(devnode, sizeof(devnode), "/dev/%s", line + 8); + udev_list_entry_add(&udev_device->properties, "DEVNAME", devnode, 0); } else if ((pos = strchr(line, '='))) { *pos = '\0'; - udev_list_entry_add(&udev_device->properties, line, pos + 1, 1); + udev_list_entry_add(&udev_device->properties, line, pos + 1, 0); } } - if (!udev_device_get_driver(udev_device)) { - driver = udev_device_read_symlink(udev_device, "driver"); - - if (driver) { - udev_list_entry_add(&udev_device->properties, "DRIVER", driver, 0); - free(driver); - } - } - - if (!udev_device_get_subsystem(udev_device)) { - subsystem = udev_device_read_symlink(udev_device, "subsystem"); - - if (subsystem) { - udev_list_entry_add(&udev_device->properties, "SUBSYSTEM", subsystem, 0); - free(subsystem); - } - } - - udev_device_set_properties_from_uevent(udev_device); - udev_device_set_properties_from_evdev(udev_device); - fclose(file); + + if (!udev_device_get_syspath(udev_device)) { + udev_device_unref(udev_device); + return NULL; + } + + udev_device_set_properties_from_evdev(udev_device); return udev_device; }