diff options
author | Tom Gundersen <teg@jklm.no> | 2014-11-21 10:19:43 +0100 |
---|---|---|
committer | Tom Gundersen <teg@jklm.no> | 2014-12-11 13:54:19 +0100 |
commit | e616e642e1da7a453ec5142a47cfa512d67d85fd (patch) | |
tree | 58ff7a9b3c11a6147b7a9d187a0d256ac6c4f153 | |
parent | 167c2c6c530c3c48a0c615e2742cef3150a0f27c (diff) |
sd-device: add sd_device_enumerator
-rw-r--r-- | Makefile.am | 1 | ||||
-rw-r--r-- | src/libsystemd/sd-device/device-enumerator.c | 270 | ||||
-rw-r--r-- | src/libsystemd/sd-device/device-util.h | 9 | ||||
-rw-r--r-- | src/libsystemd/sd-device/sd-device.c | 2 | ||||
-rw-r--r-- | src/systemd/sd-device.h | 13 |
5 files changed, 292 insertions, 3 deletions
diff --git a/Makefile.am b/Makefile.am index 8b87c7ae1..4ceca5853 100644 --- a/Makefile.am +++ b/Makefile.am @@ -2711,6 +2711,7 @@ libsystemd_internal_la_SOURCES = \ src/libsystemd/sd-hwdb/hwdb-util.h \ src/libsystemd/sd-hwdb/hwdb-intenal.h \ src/libsystemd/sd-device/sd-device.c \ + src/libsystemd/sd-device/device-enumerator.c \ src/libsystemd/sd-device/device-util.h \ src/libsystemd/sd-device/device-monitor.c \ src/libsystemd/sd-device/device-monitor.h diff --git a/src/libsystemd/sd-device/device-enumerator.c b/src/libsystemd/sd-device/device-enumerator.c new file mode 100644 index 000000000..0121e6e4b --- /dev/null +++ b/src/libsystemd/sd-device/device-enumerator.c @@ -0,0 +1,270 @@ +/*** + This file is part of systemd. + + Copyright 2008-2012 Kay Sievers <kay@vrfy.org> + Copyright 2014 Tom Gundersen <teg@jklm.no> + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +#include <ctype.h> +#include <sys/types.h> +#include <net/if.h> +#include <unistd.h> + +#include "util.h" +#include "macro.h" +#include "refcnt.h" +#include "path-util.h" +#include "strxcpyx.h" +#include "fileio.h" +#include "prioq.h" +#include "strv.h" + +#include "sd-device.h" + +#include "device-util.h" +#include "device-internal.h" + +struct sd_device_enumerator { + RefCount n_ref; + + Prioq *devices; + bool match_modified; +}; + +_public_ int sd_device_enumerator_new(sd_device_enumerator **ret) { + _cleanup_device_enumerator_unref_ sd_device_enumerator *enumerator = NULL; + + assert(ret); + + enumerator = new0(sd_device_enumerator, 1); + if (!enumerator) + return -ENOMEM; + + enumerator->n_ref = REFCNT_INIT; + + *ret = enumerator; + enumerator = NULL; + + return 0; +} + +_public_ sd_device_enumerator *sd_device_enumerator_ref(sd_device_enumerator *enumerator) { + assert_return(enumerator, NULL); + + assert_se(REFCNT_INC(enumerator->n_ref) >= 2); + + return enumerator; +} + +_public_ sd_device_enumerator *sd_device_enumerator_unref(sd_device_enumerator *enumerator) { + if (enumerator && REFCNT_DEC(enumerator->n_ref) <= 0) { + sd_device *device; + + while ((device = prioq_pop(enumerator->devices))) + sd_device_unref(device); + + prioq_free(enumerator->devices); + + free(enumerator); + } + + return NULL; +} + +static int device_compare(const void *_a, const void *_b) { + const sd_device *a = _a, *b = _b; + const char *devpath_a, *devpath_b, *sound_a; + bool delay_a = false, delay_b = false; + + assert_se(sd_device_get_devpath(a, &devpath_a) >= 0); + assert_se(sd_device_get_devpath(b, &devpath_b) >= 0); + + sound_a = strstr(devpath_a, "/sound/card"); + if (sound_a) { + /* For sound cards the control device must be enumerated last to + * make sure it's the final device node that gets ACLs applied. + * Applications rely on this fact and use ACL changes on the + * control node as an indicator that the ACL change of the + * entire sound card completed. The kernel makes this guarantee + * when creating those devices, and hence we should too when + * enumerating them. */ + sound_a += strlen("/sound/card"); + sound_a = strchr(sound_a, '/'); + + if (sound_a) { + unsigned prefix_len; + + prefix_len = sound_a - devpath_a; + + if (strncmp(devpath_a, devpath_b, prefix_len) == 0) { + const char *sound_b; + + sound_b = devpath_b + prefix_len; + + if (startswith(sound_a, "/controlC") && + !startswith(sound_b, "/contolC")) + return 1; + + if (!startswith(sound_a, "/controlC") && + startswith(sound_b, "/controlC")) + return -1; + } + } + } + + /* md and dm devices are enumerated after all other devices */ + if (strstr(devpath_a, "/block/md") || strstr(devpath_a, "/block/dm-")) + delay_a = true; + + if (strstr(devpath_b, "/block/md") || strstr(devpath_b, "/block/dm-")) + delay_b = true; + + if (delay_a && !delay_b) + return 1; + + if (!delay_a && delay_b) + return -1; + + return strcmp(devpath_a, devpath_b); +} + +static int enumerator_scan_dir_and_add_devices(sd_device_enumerator *enumerator, const char *basedir, const char *subdir1, const char *subdir2) { + _cleanup_closedir_ DIR *dir = NULL; + char *path; + struct dirent *dent; + int r; + + assert(enumerator); + assert(basedir); + assert(subdir1); + + if (subdir2) + path = strappenda("/sys/", basedir, "/", subdir1, "/", subdir2); + else + path = strappenda("/sys/", basedir, "/", subdir1); + + log_debug(" device-enumerator: scanning %s", path); + + dir = opendir(path); + if (!dir) + return -errno; + + FOREACH_DIRENT(dent, dir, return -errno) { + _cleanup_device_unref_ sd_device *device = NULL; + char *syspath; + + syspath = strappenda(path, "/", dent->d_name); + + r = sd_device_new_from_syspath(&device, syspath); + if (r < 0) + continue; + + r = prioq_ensure_allocated(&enumerator->devices, device_compare); + if (r < 0) + return r; + + r = prioq_put(enumerator->devices, device, NULL); + if (r < 0) + return r; + + log_debug(" device-enumerator: added %s", syspath); + + device = NULL; + } + + return 0; +} + +static int enumerator_scan_dir(sd_device_enumerator *enumerator, const char *basedir, const char *subdir) { + _cleanup_closedir_ DIR *dir = NULL; + char *path; + struct dirent *dent; + int r; + + path = strappenda("/sys/", basedir); + + dir = opendir(path); + if (!dir) + return -errno; + + log_debug(" device-enumerator: scanning %s", path); + + FOREACH_DIRENT(dent, dir, return -errno) { + r = enumerator_scan_dir_and_add_devices(enumerator, basedir, dent->d_name, subdir); + if (r < 0) + return r; + } + + return 0; +} + +static int enumerator_scan_dirs_all(sd_device_enumerator *enumerator) { + int r; + + log_debug("device-enumerator: scan all dirs"); + + if (access("/sys/subsystem", F_OK) >= 0) { + /* we have /subsystem/, forget all the old stuff */ + r = enumerator_scan_dir(enumerator, "subsystem", "devices"); + if (r < 0) { + log_debug("device-enumerator: failed to scan /sys/subsystem: %s", strerror(-r)); + return r; + } + } else { + r = enumerator_scan_dir(enumerator, "bus", "devices"); + if (r < 0) { + log_debug("device-enumerator: failed to scan /sys/bus: %s", strerror(-r)); + return r; + } + + r = enumerator_scan_dir(enumerator, "class", NULL); + if (r < 0) { + log_debug("device-enumerator: failed to scan /sys/class: %s", strerror(-r)); + return r; + } + } + + return 0; +} + +_public_ sd_device *sd_device_enumerator_get_device_first(sd_device_enumerator *enumerator) { + sd_device *device; + int r; + + assert_return(enumerator, NULL); + + while ((device = prioq_pop(enumerator->devices))) + sd_device_unref(device); + + r = enumerator_scan_dirs_all(enumerator); + if (r < 0) + return NULL; + + enumerator->match_modified = false; + + return prioq_peek(enumerator->devices); +} + +_public_ sd_device *sd_device_enumerator_get_device_next(sd_device_enumerator *enumerator) { + assert_return(enumerator, NULL); + + if (enumerator->match_modified) + return NULL; + + sd_device_unref(prioq_pop(enumerator->devices)); + + return prioq_peek(enumerator->devices); +} diff --git a/src/libsystemd/sd-device/device-util.h b/src/libsystemd/sd-device/device-util.h index a5f79cf73..7602ef221 100644 --- a/src/libsystemd/sd-device/device-util.h +++ b/src/libsystemd/sd-device/device-util.h @@ -27,9 +27,11 @@ #include "device-monitor.h" DEFINE_TRIVIAL_CLEANUP_FUNC(sd_device*, sd_device_unref); - #define _cleanup_device_unref_ _cleanup_(sd_device_unrefp) +DEFINE_TRIVIAL_CLEANUP_FUNC(sd_device_enumerator*, sd_device_enumerator_unref); +#define _cleanup_device_enumerator_unref_ _cleanup_(sd_device_enumerator_unrefp) + #define FOREACH_DEVICE_PROPERTY(device, key, value) \ for (key = sd_device_get_property_first(device, &(value)); \ key; \ @@ -50,5 +52,10 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(sd_device*, sd_device_unref); devlink; \ devlink = sd_device_get_devlink_next(device)) +#define FOREACH_DEVICE(enumerator, device) \ + for (device = sd_device_enumerator_get_device_first(enumerator); \ + device; \ + device = sd_device_enumerator_get_device_next(enumerator)) + DeviceAction device_action_from_string(const char *s) _pure_; const char *device_action_to_string(DeviceAction a) _const_; diff --git a/src/libsystemd/sd-device/sd-device.c b/src/libsystemd/sd-device/sd-device.c index 5ef9d399e..1b43b2929 100644 --- a/src/libsystemd/sd-device/sd-device.c +++ b/src/libsystemd/sd-device/sd-device.c @@ -1044,7 +1044,7 @@ _public_ int sd_device_get_driver(sd_device *device, const char **ret) { return 0; } -_public_ int sd_device_get_devpath(sd_device *device, const char **devpath) { +_public_ int sd_device_get_devpath(const sd_device *device, const char **devpath) { assert_return(device, -EINVAL); assert_return(devpath, -EINVAL); diff --git a/src/systemd/sd-device.h b/src/systemd/sd-device.h index 46dab7e5f..e5f4adbb5 100644 --- a/src/systemd/sd-device.h +++ b/src/systemd/sd-device.h @@ -31,6 +31,9 @@ _SD_BEGIN_DECLARATIONS; typedef struct sd_device sd_device; +typedef struct sd_device_enumerator sd_device_enumerator; + +/* device */ sd_device *sd_device_ref(sd_device *device); sd_device *sd_device_unref(sd_device *device); @@ -47,7 +50,7 @@ int sd_device_get_devtype(sd_device *device, const char **ret); int sd_device_get_parent_with_subsystem_devtype(sd_device *child, const char *subsystem, const char *devtype, sd_device **ret); int sd_device_get_devnum(sd_device *device, dev_t *devnum); int sd_device_get_driver(sd_device *device, const char **ret); -int sd_device_get_devpath(sd_device *device, const char **ret); +int sd_device_get_devpath(const sd_device *device, const char **ret); int sd_device_get_devnode(sd_device *device, const char **ret); int sd_device_get_sysname(sd_device *device, const char **ret); int sd_device_get_sysnum(sd_device *device, const char **ret); @@ -67,6 +70,14 @@ const char *sd_device_get_sysattr_next(sd_device *device, const char **value); const char *sd_device_get_devlink_first(sd_device *device); const char *sd_device_get_devlink_next(sd_device *device); +/* devcie enumerator */ + +int sd_device_enumerator_new(sd_device_enumerator **ret); +sd_device_enumerator *sd_device_enumerator_ref(sd_device_enumerator *enumerator); +sd_device_enumerator *sd_device_enumerator_unref(sd_device_enumerator *enumerator); +sd_device *sd_device_enumerator_get_device_first(sd_device_enumerator *enumerator); +sd_device *sd_device_enumerator_get_device_next(sd_device_enumerator *enumerator); + _SD_END_DECLARATIONS; #endif |