summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTom Gundersen <teg@jklm.no>2014-11-21 10:19:43 +0100
committerTom Gundersen <teg@jklm.no>2014-12-11 13:54:19 +0100
commite616e642e1da7a453ec5142a47cfa512d67d85fd (patch)
tree58ff7a9b3c11a6147b7a9d187a0d256ac6c4f153
parent167c2c6c530c3c48a0c615e2742cef3150a0f27c (diff)
sd-device: add sd_device_enumerator
-rw-r--r--Makefile.am1
-rw-r--r--src/libsystemd/sd-device/device-enumerator.c270
-rw-r--r--src/libsystemd/sd-device/device-util.h9
-rw-r--r--src/libsystemd/sd-device/sd-device.c2
-rw-r--r--src/systemd/sd-device.h13
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