From 64ccdf8269e1e0fbde7acbe4837c2c2f2c41659c Mon Sep 17 00:00:00 2001 From: Kay Sievers Date: Wed, 1 Oct 2008 09:34:07 +0200 Subject: [PATCH] libudev: add queue interface --- udev/lib/Makefile.am | 3 +- udev/lib/exported_symbols | 10 ++ udev/lib/libudev-private.h | 6 + udev/lib/libudev-queue.c | 286 +++++++++++++++++++++++++++++++++++++ udev/lib/libudev.h | 14 +- udev/lib/test-libudev.c | 51 ++++++- 6 files changed, 365 insertions(+), 5 deletions(-) create mode 100644 udev/lib/libudev-queue.c diff --git a/udev/lib/Makefile.am b/udev/lib/Makefile.am index e800cf7f4..08e7ff865 100644 --- a/udev/lib/Makefile.am +++ b/udev/lib/Makefile.am @@ -24,7 +24,8 @@ libudev_la_SOURCES =\ libudev-device.c \ libudev-enumerate.c \ libudev-ctrl.c \ - libudev-monitor.c + libudev-monitor.c \ + libudev-queue.c libudev_la_LDFLAGS = \ -version-info $(LIBUDEV_LT_CURRENT):$(LIBUDEV_LT_REVISION):$(LIBUDEV_LT_AGE) \ diff --git a/udev/lib/exported_symbols b/udev/lib/exported_symbols index edd3c76f3..81abb58d6 100644 --- a/udev/lib/exported_symbols +++ b/udev/lib/exported_symbols @@ -51,3 +51,13 @@ udev_monitor_unref udev_monitor_get_udev udev_monitor_get_fd udev_monitor_receive_device +udev_queue_new +udev_queue_ref +udev_queue_unref +udev_queue_get_udev +udev_queue_get_kernel_seqnum +udev_queue_get_udev_seqnum +udev_queue_get_queue_is_empty +udev_queue_get_seqnum_is_finished +udev_queue_get_queued_list_entry +udev_queue_get_failed_list_entry diff --git a/udev/lib/libudev-private.h b/udev/lib/libudev-private.h index fd7f4551e..7747b344f 100644 --- a/udev/lib/libudev-private.h +++ b/udev/lib/libudev-private.h @@ -120,6 +120,12 @@ extern void list_entry_move_to_end(struct udev_list_entry *list_entry); entry != NULL; \ entry = tmp, tmp = udev_list_entry_get_next(tmp)) +/* libudev-queue */ +extern int queue_export_udev_seqnum(struct udev_queue *udev_queue, unsigned long long int seqnum); +extern int queue_export_device_queued(struct udev_queue *udev_queue, struct udev_device *udev_device); +extern int queue_export_device_finished(struct udev_queue *udev_queue, struct udev_device *udev_device); +extern int queue_export_device_failed(struct udev_queue *udev_queue, struct udev_device *udev_device); + /* libudev-utils */ #define UTIL_PATH_SIZE 1024 #define UTIL_LINE_SIZE 2048 diff --git a/udev/lib/libudev-queue.c b/udev/lib/libudev-queue.c new file mode 100644 index 000000000..3ca533b1e --- /dev/null +++ b/udev/lib/libudev-queue.c @@ -0,0 +1,286 @@ +/* + * libudev - interface to udev device information + * + * Copyright (C) 2008 Kay Sievers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "libudev.h" +#include "libudev-private.h" + +struct udev_queue { + struct udev *udev; + int refcount; + unsigned long long int last_seen_udev_seqnum; + struct list_node queue_list; + struct list_node failed_list; +}; + +struct udev_queue *udev_queue_new(struct udev *udev) +{ + struct udev_queue *udev_queue; + + if (udev == NULL) + return NULL; + + udev_queue = malloc(sizeof(struct udev_queue)); + if (udev_queue == NULL) + return NULL; + memset(udev_queue, 0x00, sizeof(struct udev_queue)); + udev_queue->refcount = 1; + udev_queue->udev = udev; + list_init(&udev_queue->queue_list); + list_init(&udev_queue->failed_list); + return udev_queue; +} + +struct udev_queue *udev_queue_ref(struct udev_queue *udev_queue) +{ + if (udev_queue == NULL) + return NULL; + udev_queue->refcount++; + return udev_queue; +} + +void udev_queue_unref(struct udev_queue *udev_queue) +{ + if (udev_queue == NULL) + return; + udev_queue->refcount--; + if (udev_queue->refcount > 0) + return; + list_cleanup(udev_queue->udev, &udev_queue->queue_list); + list_cleanup(udev_queue->udev, &udev_queue->failed_list); + free(udev_queue); +} + +struct udev *udev_queue_get_udev(struct udev_queue *udev_queue) +{ + if (udev_queue == NULL) + return NULL; + return udev_queue->udev; +} + +unsigned long long int udev_queue_get_kernel_seqnum(struct udev_queue *udev_queue) +{ + char filename[UTIL_PATH_SIZE]; + unsigned long long int seqnum; + int fd; + char buf[32]; + ssize_t len; + + if (udev_queue == NULL) + return -EINVAL; + util_strlcpy(filename, udev_get_sys_path(udev_queue->udev), sizeof(filename)); + util_strlcat(filename, "/kernel/uevent_seqnum", sizeof(filename)); + fd = open(filename, O_RDONLY); + if (fd < 0) + return 0; + len = read(fd, buf, sizeof(buf)); + close(fd); + if (len <= 2) + return 0; + buf[len-1] = '\0'; + seqnum = strtoull(buf, NULL, 10); + info(udev_queue->udev, "seqnum=%llu\n", seqnum); + return seqnum; +} + +unsigned long long int udev_queue_get_udev_seqnum(struct udev_queue *udev_queue) +{ + char filename[UTIL_PATH_SIZE]; + unsigned long long int seqnum; + int fd; + char buf[32]; + ssize_t len; + + if (udev_queue == NULL) + return -EINVAL; + util_strlcpy(filename, udev_get_dev_path(udev_queue->udev), sizeof(filename)); + util_strlcat(filename, "/.udev/uevent_seqnum", sizeof(filename)); + fd = open(filename, O_RDONLY); + if (fd < 0) + return 0; + len = read(fd, buf, sizeof(buf)); + close(fd); + if (len <= 2) + return 0; + buf[len-1] = '\0'; + seqnum = strtoull(buf, NULL, 10); + info(udev_queue->udev, "seqnum=%llu\n", seqnum); + udev_queue->last_seen_udev_seqnum = seqnum; + return seqnum; +} + +int udev_queue_get_queue_is_empty(struct udev_queue *udev_queue) +{ + char queuename[UTIL_PATH_SIZE]; + struct stat statbuf; + unsigned long long int seqnum_kernel; + + if (udev_queue == NULL) + return -EINVAL; + util_strlcpy(queuename, udev_get_dev_path(udev_queue->udev), sizeof(queuename)); + util_strlcat(queuename, "/.udev/queue", sizeof(queuename)); + if (stat(queuename, &statbuf) == 0) { + info(udev_queue->udev, "queue is not empty\n"); + return 0; + } + seqnum_kernel = udev_queue_get_kernel_seqnum(udev_queue); + if (seqnum_kernel <= udev_queue->last_seen_udev_seqnum) { + info(udev_queue->udev, "queue is empty\n"); + return 1; + } + udev_queue_get_udev_seqnum(udev_queue); + if (seqnum_kernel <= udev_queue->last_seen_udev_seqnum) { + info(udev_queue->udev, "queue is empty\n"); + return 1; + } + info(udev_queue->udev, "queue is empty, but kernel events still pending [%llu]<->[%llu]\n", + seqnum_kernel, udev_queue->last_seen_udev_seqnum); + return 0; +} + +int udev_queue_get_seqnum_is_finished(struct udev_queue *udev_queue, unsigned long long int seqnum) +{ + char filename[UTIL_PATH_SIZE]; + struct stat statbuf; + + if (udev_queue == NULL) + return -EINVAL; + /* if we have not seen this seqnum, check if it is/was already queued */ + if (seqnum < udev_queue->last_seen_udev_seqnum) { + udev_queue_get_udev_seqnum(udev_queue); + if (seqnum < udev_queue->last_seen_udev_seqnum) + return 0; + } + snprintf(filename, sizeof(filename), "%s/.udev/queue/%llu", + udev_get_dev_path(udev_queue->udev), seqnum); + if (stat(filename, &statbuf) == 0) + return 0; + info(udev_queue->udev, "seqnum: %llu finished\n", seqnum); + return 1; +} + +struct udev_list_entry *udev_queue_get_queued_list_entry(struct udev_queue *udev_queue) +{ + char path[UTIL_PATH_SIZE]; + DIR *dir; + struct dirent *dent; + + if (udev_queue == NULL) + return NULL; + list_cleanup(udev_queue->udev, &udev_queue->queue_list); + util_strlcpy(path, udev_get_dev_path(udev_queue->udev), sizeof(path)); + util_strlcat(path, "/.udev/queue", sizeof(path)); + dir = opendir(path); + if (dir == NULL) + return NULL; + for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) { + char filename[UTIL_PATH_SIZE]; + char syspath[UTIL_PATH_SIZE]; + size_t syslen; + ssize_t len; + + if (dent->d_name[0] == '.') + continue; + util_strlcpy(filename, path, sizeof(filename)); + util_strlcat(filename, "/", sizeof(filename)); + util_strlcat(filename, dent->d_name, sizeof(filename)); + + syslen = util_strlcpy(syspath, udev_get_sys_path(udev_queue->udev), sizeof(syspath)); + len = readlink(filename, &syspath[syslen], sizeof(syspath)-syslen); + if (len < 0 || len >= (ssize_t)(sizeof(syspath)-syslen)) + continue; + syspath[syslen + len] = '\0'; + info(udev_queue->udev, "found '%s' [%s]\n", syspath, dent->d_name); + list_entry_add(udev_queue->udev, &udev_queue->queue_list, syspath, dent->d_name, 0, 0); + } + closedir(dir); + return list_get_entry(&udev_queue->queue_list); +} + +struct udev_list_entry *udev_queue_get_failed_list_entry(struct udev_queue *udev_queue) +{ + char path[UTIL_PATH_SIZE]; + DIR *dir; + struct dirent *dent; + + if (udev_queue == NULL) + return NULL; + list_cleanup(udev_queue->udev, &udev_queue->failed_list); + util_strlcpy(path, udev_get_dev_path(udev_queue->udev), sizeof(path)); + util_strlcat(path, "/.udev/failed", sizeof(path)); + dir = opendir(path); + if (dir == NULL) + return NULL; + for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) { + char filename[UTIL_PATH_SIZE]; + char syspath[UTIL_PATH_SIZE]; + struct stat statbuf; + size_t syslen; + ssize_t len; + + if (dent->d_name[0] == '.') + continue; + util_strlcpy(filename, path, sizeof(filename)); + util_strlcat(filename, "/", sizeof(filename)); + util_strlcat(filename, dent->d_name, sizeof(filename)); + + syslen = util_strlcpy(syspath, udev_get_sys_path(udev_queue->udev), sizeof(syspath)); + len = readlink(filename, &syspath[syslen], sizeof(syspath)-syslen); + if (len < 0 || len >= (ssize_t)(sizeof(syspath)-syslen)) + continue; + syspath[syslen + len] = '\0'; + info(udev_queue->udev, "found '%s' [%s]\n", syspath, dent->d_name); + util_strlcpy(filename, syspath, sizeof(filename)); + util_strlcat(filename, "/uevent", sizeof(filename)); + if (stat(filename, &statbuf) != 0) + continue; + list_entry_add(udev_queue->udev, &udev_queue->failed_list, syspath, NULL, 0, 0); + } + closedir(dir); + return list_get_entry(&udev_queue->failed_list); +} + +int queue_export_udev_seqnum(struct udev_queue *udev_queue, unsigned long long int seqnum) +{ + return 0; +} + +extern int queue_export_device_queued(struct udev_queue *udev_queue, struct udev_device *udev_device) +{ + return 0; +} + +extern int queue_export_device_finished(struct udev_queue *udev_queue, struct udev_device *udev_device) +{ + return 0; +} + +extern int queue_export_device_failed(struct udev_queue *udev_queue, struct udev_device *udev_device) +{ + return 0; +} diff --git a/udev/lib/libudev.h b/udev/lib/libudev.h index 49d24f147..cc6939c02 100644 --- a/udev/lib/libudev.h +++ b/udev/lib/libudev.h @@ -96,8 +96,8 @@ extern struct udev_device *udev_monitor_receive_device(struct udev_monitor *udev struct udev_enumerate; extern struct udev_enumerate *udev_enumerate_new(struct udev *udev); extern struct udev_enumerate *udev_enumerate_ref(struct udev_enumerate *udev_enumerate); -extern struct udev *udev_enumerate_get_udev(struct udev_enumerate *udev_enumerate); extern void udev_enumerate_unref(struct udev_enumerate *udev_enumerate); +extern struct udev *udev_enumerate_get_udev(struct udev_enumerate *udev_enumerate); extern int udev_enumerate_add_match_subsystem(struct udev_enumerate *udev_enumerate, const char *subsystem); extern int udev_enumerate_add_nomatch_subsystem(struct udev_enumerate *udev_enumerate, const char *subsystem); extern int udev_enumerate_add_match_attr(struct udev_enumerate *udev_enumerate, const char *attr, const char *value); @@ -107,4 +107,16 @@ extern int udev_enumerate_scan_devices(struct udev_enumerate *udev_enumerate); extern int udev_enumerate_scan_subsystems(struct udev_enumerate *udev_enumerate); extern struct udev_list_entry *udev_enumerate_get_list_entry(struct udev_enumerate *udev_enumerate); +/* event queue */ +struct udev_queue; +extern struct udev_queue *udev_queue_new(struct udev *udev); +extern struct udev_queue *udev_queue_ref(struct udev_queue *udev_queue); +extern void udev_queue_unref(struct udev_queue *udev_queue); +extern struct udev *udev_queue_get_udev(struct udev_queue *udev_queue); +extern unsigned long long int udev_queue_get_kernel_seqnum(struct udev_queue *udev_queue); +extern unsigned long long int udev_queue_get_udev_seqnum(struct udev_queue *udev_queue); +extern int udev_queue_get_queue_is_empty(struct udev_queue *udev_queue); +extern int udev_queue_get_seqnum_is_finished(struct udev_queue *udev_queue, unsigned long long int seqnum); +extern struct udev_list_entry *udev_queue_get_queued_list_entry(struct udev_queue *udev_queue); +extern struct udev_list_entry *udev_queue_get_failed_list_entry(struct udev_queue *udev_queue); #endif diff --git a/udev/lib/test-libudev.c b/udev/lib/test-libudev.c index 5e9897c71..9c7acb048 100644 --- a/udev/lib/test-libudev.c +++ b/udev/lib/test-libudev.c @@ -19,6 +19,7 @@ #include #include +#include #include #include #include @@ -168,10 +169,9 @@ static int test_enumerate_print_list(struct udev_enumerate *enumerate) device = udev_device_new_from_syspath(udev_enumerate_get_udev(enumerate), udev_list_entry_get_name(list_entry)); if (device != NULL) { - printf("device: '%s' (%s) '%s'\n", + printf("device: '%s' (%s)\n", udev_device_get_syspath(device), - udev_device_get_subsystem(device), - udev_device_get_sysname(device)); + udev_device_get_subsystem(device)); udev_device_unref(device); count++; } @@ -230,6 +230,49 @@ static int test_monitor(struct udev *udev, const char *socket_path) return 0; } +static int test_queue(struct udev *udev) +{ + struct udev_queue *udev_queue; + unsigned long long int seqnum; + struct udev_list_entry *list_entry; + + udev_queue = udev_queue_new(udev); + if (udev_queue == NULL) + return -1; + seqnum = udev_queue_get_kernel_seqnum(udev_queue); + printf("seqnum kernel: %llu\n", seqnum); + seqnum = udev_queue_get_udev_seqnum(udev_queue); + printf("seqnum udev : %llu\n", seqnum); + + if (udev_queue_get_queue_is_empty(udev_queue)) + printf("queue is empty\n"); + printf("get queue list\n"); + udev_list_entry_foreach(list_entry, udev_queue_get_queued_list_entry(udev_queue)) + printf("queued: '%s' [%s]\n", udev_list_entry_get_name(list_entry), udev_list_entry_get_value(list_entry)); + printf("\n"); + printf("get queue list again\n"); + udev_list_entry_foreach(list_entry, udev_queue_get_queued_list_entry(udev_queue)) + printf("queued: '%s' [%s]\n", udev_list_entry_get_name(list_entry), udev_list_entry_get_value(list_entry)); + printf("\n"); + printf("get failed list\n"); + udev_list_entry_foreach(list_entry, udev_queue_get_failed_list_entry(udev_queue)) + printf("failed: '%s'\n", udev_list_entry_get_name(list_entry)); + printf("\n"); + + list_entry = udev_queue_get_queued_list_entry(udev_queue); + if (list_entry != NULL) { + printf("event [%llu] is queued\n", seqnum); + seqnum = strtoull(udev_list_entry_get_value(list_entry), NULL, 10); + if (udev_queue_get_seqnum_is_finished(udev_queue, seqnum)) + printf("event [%llu] is not finished\n", seqnum); + else + printf("event [%llu] is finished\n", seqnum); + } + printf("\n"); + udev_queue_unref(udev_queue); + return 0; +} + int main(int argc, char *argv[], char *envp[]) { struct udev *udev = NULL; @@ -351,6 +394,8 @@ int main(int argc, char *argv[], char *envp[]) test_enumerate_print_list(udev_enumerate); udev_enumerate_unref(udev_enumerate); + test_queue(udev); + test_monitor(udev, socket); out: udev_unref(udev); -- 2.30.2