2015-04-14 16:22:39 +02:00
|
|
|
/***
|
|
|
|
This file is part of systemd.
|
|
|
|
|
|
|
|
Copyright 2008-2012 Kay Sievers <kay@vrfy.org>
|
|
|
|
Copyright 2014-2015 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 "sd-device.h"
|
|
|
|
|
2015-10-27 03:01:06 +01:00
|
|
|
#include "alloc-util.h"
|
2015-04-14 16:22:39 +02:00
|
|
|
#include "device-enumerator-private.h"
|
2015-10-24 22:58:24 +02:00
|
|
|
#include "device-util.h"
|
2015-10-26 20:07:55 +01:00
|
|
|
#include "dirent-util.h"
|
2015-10-25 13:14:12 +01:00
|
|
|
#include "fd-util.h"
|
2015-10-24 22:58:24 +02:00
|
|
|
#include "prioq.h"
|
|
|
|
#include "set.h"
|
|
|
|
#include "string-util.h"
|
|
|
|
#include "strv.h"
|
|
|
|
#include "util.h"
|
2015-04-14 16:22:39 +02:00
|
|
|
|
|
|
|
#define DEVICE_ENUMERATE_MAX_DEPTH 256
|
|
|
|
|
|
|
|
typedef enum DeviceEnumerationType {
|
|
|
|
DEVICE_ENUMERATION_TYPE_DEVICES,
|
|
|
|
DEVICE_ENUMERATION_TYPE_SUBSYSTEMS,
|
|
|
|
_DEVICE_ENUMERATION_TYPE_MAX,
|
|
|
|
_DEVICE_ENUMERATION_TYPE_INVALID = -1,
|
|
|
|
} DeviceEnumerationType;
|
|
|
|
|
|
|
|
struct sd_device_enumerator {
|
|
|
|
unsigned n_ref;
|
|
|
|
|
|
|
|
DeviceEnumerationType type;
|
|
|
|
Prioq *devices;
|
|
|
|
bool scan_uptodate;
|
|
|
|
|
|
|
|
Set *match_subsystem;
|
|
|
|
Set *nomatch_subsystem;
|
|
|
|
Hashmap *match_sysattr;
|
|
|
|
Hashmap *nomatch_sysattr;
|
|
|
|
Hashmap *match_property;
|
|
|
|
Set *match_sysname;
|
|
|
|
Set *match_tag;
|
|
|
|
sd_device *match_parent;
|
2015-04-17 14:11:00 +02:00
|
|
|
bool match_allow_uninitialized;
|
2015-04-14 16:22:39 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
_public_ int sd_device_enumerator_new(sd_device_enumerator **ret) {
|
tree-wide: expose "p"-suffix unref calls in public APIs to make gcc cleanup easy
GLIB has recently started to officially support the gcc cleanup
attribute in its public API, hence let's do the same for our APIs.
With this patch we'll define an xyz_unrefp() call for each public
xyz_unref() call, to make it easy to use inside a
__attribute__((cleanup())) expression. Then, all code is ported over to
make use of this.
The new calls are also documented in the man pages, with examples how to
use them (well, I only added docs where the _unref() call itself already
had docs, and the examples, only cover sd_bus_unrefp() and
sd_event_unrefp()).
This also renames sd_lldp_free() to sd_lldp_unref(), since that's how we
tend to call our destructors these days.
Note that this defines no public macro that wraps gcc's attribute and
makes it easier to use. While I think it's our duty in the library to
make our stuff easy to use, I figure it's not our duty to make gcc's own
features easy to use on its own. Most likely, client code which wants to
make use of this should define its own:
#define _cleanup_(function) __attribute__((cleanup(function)))
Or similar, to make the gcc feature easier to use.
Making this logic public has the benefit that we can remove three header
files whose only purpose was to define these functions internally.
See #2008.
2015-11-27 19:13:45 +01:00
|
|
|
_cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *enumerator = NULL;
|
2015-04-14 16:22:39 +02:00
|
|
|
|
|
|
|
assert(ret);
|
|
|
|
|
|
|
|
enumerator = new0(sd_device_enumerator, 1);
|
|
|
|
if (!enumerator)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
enumerator->n_ref = 1;
|
|
|
|
enumerator->type = _DEVICE_ENUMERATION_TYPE_INVALID;
|
|
|
|
|
|
|
|
*ret = enumerator;
|
|
|
|
enumerator = NULL;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
_public_ sd_device_enumerator *sd_device_enumerator_ref(sd_device_enumerator *enumerator) {
|
|
|
|
assert_return(enumerator, NULL);
|
|
|
|
|
|
|
|
assert_se((++ enumerator->n_ref) >= 2);
|
|
|
|
|
|
|
|
return enumerator;
|
|
|
|
}
|
|
|
|
|
|
|
|
_public_ sd_device_enumerator *sd_device_enumerator_unref(sd_device_enumerator *enumerator) {
|
|
|
|
if (enumerator && (-- enumerator->n_ref) == 0) {
|
|
|
|
sd_device *device;
|
|
|
|
|
|
|
|
while ((device = prioq_pop(enumerator->devices)))
|
|
|
|
sd_device_unref(device);
|
|
|
|
|
|
|
|
prioq_free(enumerator->devices);
|
|
|
|
|
|
|
|
set_free_free(enumerator->match_subsystem);
|
|
|
|
set_free_free(enumerator->nomatch_subsystem);
|
|
|
|
hashmap_free_free_free(enumerator->match_sysattr);
|
|
|
|
hashmap_free_free_free(enumerator->nomatch_sysattr);
|
|
|
|
hashmap_free_free_free(enumerator->match_property);
|
|
|
|
set_free_free(enumerator->match_sysname);
|
|
|
|
set_free_free(enumerator->match_tag);
|
|
|
|
sd_device_unref(enumerator->match_parent);
|
|
|
|
|
|
|
|
free(enumerator);
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
_public_ int sd_device_enumerator_add_match_subsystem(sd_device_enumerator *enumerator, const char *subsystem, int match) {
|
|
|
|
Set **set;
|
|
|
|
int r;
|
|
|
|
|
|
|
|
assert_return(enumerator, -EINVAL);
|
|
|
|
assert_return(subsystem, -EINVAL);
|
|
|
|
|
|
|
|
if (match)
|
|
|
|
set = &enumerator->match_subsystem;
|
|
|
|
else
|
|
|
|
set = &enumerator->nomatch_subsystem;
|
|
|
|
|
|
|
|
r = set_ensure_allocated(set, NULL);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
r = set_put_strdup(*set, subsystem);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
enumerator->scan_uptodate = false;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
_public_ int sd_device_enumerator_add_match_sysattr(sd_device_enumerator *enumerator, const char *_sysattr, const char *_value, int match) {
|
|
|
|
_cleanup_free_ char *sysattr = NULL, *value = NULL;
|
|
|
|
Hashmap **hashmap;
|
|
|
|
int r;
|
|
|
|
|
|
|
|
assert_return(enumerator, -EINVAL);
|
|
|
|
assert_return(_sysattr, -EINVAL);
|
|
|
|
|
|
|
|
if (match)
|
|
|
|
hashmap = &enumerator->match_sysattr;
|
|
|
|
else
|
|
|
|
hashmap = &enumerator->nomatch_sysattr;
|
|
|
|
|
|
|
|
r = hashmap_ensure_allocated(hashmap, NULL);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
sysattr = strdup(_sysattr);
|
|
|
|
if (!sysattr)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
2015-06-03 22:08:46 +02:00
|
|
|
if (_value) {
|
|
|
|
value = strdup(_value);
|
|
|
|
if (!value)
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
2015-04-14 16:22:39 +02:00
|
|
|
|
|
|
|
r = hashmap_put(*hashmap, sysattr, value);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
sysattr = NULL;
|
|
|
|
value = NULL;
|
|
|
|
|
|
|
|
enumerator->scan_uptodate = false;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
_public_ int sd_device_enumerator_add_match_property(sd_device_enumerator *enumerator, const char *_property, const char *_value) {
|
|
|
|
_cleanup_free_ char *property = NULL, *value = NULL;
|
|
|
|
int r;
|
|
|
|
|
|
|
|
assert_return(enumerator, -EINVAL);
|
|
|
|
assert_return(_property, -EINVAL);
|
|
|
|
|
|
|
|
r = hashmap_ensure_allocated(&enumerator->match_property, NULL);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
property = strdup(_property);
|
|
|
|
if (!property)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
2015-06-03 22:08:46 +02:00
|
|
|
if (_value) {
|
|
|
|
value = strdup(_value);
|
|
|
|
if (!value)
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
2015-04-14 16:22:39 +02:00
|
|
|
|
|
|
|
r = hashmap_put(enumerator->match_property, property, value);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
property = NULL;
|
|
|
|
value = NULL;
|
|
|
|
|
|
|
|
enumerator->scan_uptodate = false;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
_public_ int sd_device_enumerator_add_match_sysname(sd_device_enumerator *enumerator, const char *sysname) {
|
|
|
|
int r;
|
|
|
|
|
|
|
|
assert_return(enumerator, -EINVAL);
|
|
|
|
assert_return(sysname, -EINVAL);
|
|
|
|
|
|
|
|
r = set_ensure_allocated(&enumerator->match_sysname, NULL);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
r = set_put_strdup(enumerator->match_sysname, sysname);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
enumerator->scan_uptodate = false;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
_public_ int sd_device_enumerator_add_match_tag(sd_device_enumerator *enumerator, const char *tag) {
|
|
|
|
int r;
|
|
|
|
|
|
|
|
assert_return(enumerator, -EINVAL);
|
|
|
|
assert_return(tag, -EINVAL);
|
|
|
|
|
|
|
|
r = set_ensure_allocated(&enumerator->match_tag, NULL);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
r = set_put_strdup(enumerator->match_tag, tag);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
enumerator->scan_uptodate = false;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
_public_ int sd_device_enumerator_add_match_parent(sd_device_enumerator *enumerator, sd_device *parent) {
|
|
|
|
assert_return(enumerator, -EINVAL);
|
|
|
|
assert_return(parent, -EINVAL);
|
|
|
|
|
|
|
|
sd_device_unref(enumerator->match_parent);
|
|
|
|
enumerator->match_parent = sd_device_ref(parent);
|
|
|
|
|
|
|
|
enumerator->scan_uptodate = false;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-04-17 14:11:00 +02:00
|
|
|
_public_ int sd_device_enumerator_allow_uninitialized(sd_device_enumerator *enumerator) {
|
2015-04-14 16:22:39 +02:00
|
|
|
assert_return(enumerator, -EINVAL);
|
|
|
|
|
2015-04-17 14:11:00 +02:00
|
|
|
enumerator->match_allow_uninitialized = true;
|
|
|
|
|
|
|
|
enumerator->scan_uptodate = false;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int device_enumerator_add_match_is_initialized(sd_device_enumerator *enumerator) {
|
|
|
|
assert_return(enumerator, -EINVAL);
|
|
|
|
|
|
|
|
enumerator->match_allow_uninitialized = false;
|
2015-04-14 16:22:39 +02:00
|
|
|
|
|
|
|
enumerator->scan_uptodate = false;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int device_compare(const void *_a, const void *_b) {
|
|
|
|
sd_device *a = (sd_device *)_a, *b = (sd_device *)_b;
|
|
|
|
const char *devpath_a, *devpath_b, *sound_a;
|
2015-04-17 14:06:31 +02:00
|
|
|
bool delay_a, delay_b;
|
2015-04-14 16:22:39 +02:00
|
|
|
|
|
|
|
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 */
|
2015-04-17 14:06:31 +02:00
|
|
|
delay_a = strstr(devpath_a, "/block/md") || strstr(devpath_a, "/block/dm-");
|
|
|
|
delay_b = strstr(devpath_b, "/block/md") || strstr(devpath_b, "/block/dm-");
|
|
|
|
if (delay_a != delay_b)
|
|
|
|
return delay_a - delay_b;
|
2015-04-14 16:22:39 +02:00
|
|
|
|
|
|
|
return strcmp(devpath_a, devpath_b);
|
|
|
|
}
|
|
|
|
|
2015-04-17 13:50:10 +02:00
|
|
|
int device_enumerator_add_device(sd_device_enumerator *enumerator, sd_device *device) {
|
2015-04-14 16:22:39 +02:00
|
|
|
int r;
|
|
|
|
|
|
|
|
assert_return(enumerator, -EINVAL);
|
|
|
|
assert_return(device, -EINVAL);
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
sd_device_ref(device);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool match_sysattr_value(sd_device *device, const char *sysattr, const char *match_value) {
|
|
|
|
const char *value;
|
|
|
|
int r;
|
|
|
|
|
|
|
|
assert(device);
|
|
|
|
assert(sysattr);
|
|
|
|
|
|
|
|
r = sd_device_get_sysattr_value(device, sysattr, &value);
|
|
|
|
if (r < 0)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (!match_value)
|
|
|
|
return true;
|
|
|
|
|
|
|
|
if (fnmatch(match_value, value, 0) == 0)
|
|
|
|
return true;
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool match_sysattr(sd_device_enumerator *enumerator, sd_device *device) {
|
|
|
|
const char *sysattr;
|
|
|
|
const char *value;
|
|
|
|
Iterator i;
|
|
|
|
|
|
|
|
assert(enumerator);
|
|
|
|
assert(device);
|
|
|
|
|
2015-05-28 17:18:33 +02:00
|
|
|
HASHMAP_FOREACH_KEY(value, sysattr, enumerator->nomatch_sysattr, i)
|
2015-04-14 16:22:39 +02:00
|
|
|
if (match_sysattr_value(device, sysattr, value))
|
|
|
|
return false;
|
|
|
|
|
2015-05-28 17:18:33 +02:00
|
|
|
HASHMAP_FOREACH_KEY(value, sysattr, enumerator->match_sysattr, i)
|
2015-04-14 16:22:39 +02:00
|
|
|
if (!match_sysattr_value(device, sysattr, value))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool match_property(sd_device_enumerator *enumerator, sd_device *device) {
|
|
|
|
const char *property;
|
|
|
|
const char *value;
|
|
|
|
Iterator i;
|
|
|
|
|
|
|
|
assert(enumerator);
|
|
|
|
assert(device);
|
|
|
|
|
|
|
|
if (hashmap_isempty(enumerator->match_property))
|
|
|
|
return true;
|
|
|
|
|
2015-05-28 17:18:33 +02:00
|
|
|
HASHMAP_FOREACH_KEY(value, property, enumerator->match_property, i) {
|
2015-04-14 16:22:39 +02:00
|
|
|
const char *property_dev, *value_dev;
|
|
|
|
|
|
|
|
FOREACH_DEVICE_PROPERTY(device, property_dev, value_dev) {
|
|
|
|
if (fnmatch(property, property_dev, 0) != 0)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (!value && !value_dev)
|
|
|
|
return true;
|
|
|
|
|
|
|
|
if (!value || !value_dev)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (fnmatch(value, value_dev, 0) == 0)
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool match_tag(sd_device_enumerator *enumerator, sd_device *device) {
|
|
|
|
const char *tag;
|
|
|
|
Iterator i;
|
|
|
|
|
|
|
|
assert(enumerator);
|
|
|
|
assert(device);
|
|
|
|
|
|
|
|
SET_FOREACH(tag, enumerator->match_tag, i)
|
|
|
|
if (!sd_device_has_tag(device, tag))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool match_parent(sd_device_enumerator *enumerator, sd_device *device) {
|
|
|
|
const char *devpath, *devpath_dev;
|
|
|
|
int r;
|
|
|
|
|
|
|
|
assert(enumerator);
|
|
|
|
assert(device);
|
|
|
|
|
|
|
|
if (!enumerator->match_parent)
|
|
|
|
return true;
|
|
|
|
|
|
|
|
r = sd_device_get_devpath(enumerator->match_parent, &devpath);
|
|
|
|
assert(r >= 0);
|
|
|
|
|
|
|
|
r = sd_device_get_devpath(device, &devpath_dev);
|
|
|
|
assert(r >= 0);
|
|
|
|
|
|
|
|
return startswith(devpath_dev, devpath);
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool match_sysname(sd_device_enumerator *enumerator, const char *sysname) {
|
|
|
|
const char *sysname_match;
|
|
|
|
Iterator i;
|
|
|
|
|
|
|
|
assert(enumerator);
|
|
|
|
assert(sysname);
|
|
|
|
|
|
|
|
if (set_isempty(enumerator->match_sysname))
|
|
|
|
return true;
|
|
|
|
|
|
|
|
SET_FOREACH(sysname_match, enumerator->match_sysname, i)
|
|
|
|
if (fnmatch(sysname_match, sysname, 0) == 0)
|
|
|
|
return true;
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
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 = 0;
|
|
|
|
|
|
|
|
assert(enumerator);
|
|
|
|
assert(basedir);
|
|
|
|
|
|
|
|
path = strjoina("/sys/", basedir, "/");
|
|
|
|
|
|
|
|
if (subdir1)
|
|
|
|
path = strjoina(path, subdir1, "/");
|
|
|
|
|
|
|
|
if (subdir2)
|
|
|
|
path = strjoina(path, subdir2, "/");
|
|
|
|
|
|
|
|
dir = opendir(path);
|
|
|
|
if (!dir)
|
|
|
|
return -errno;
|
|
|
|
|
|
|
|
FOREACH_DIRENT_ALL(dent, dir, return -errno) {
|
tree-wide: expose "p"-suffix unref calls in public APIs to make gcc cleanup easy
GLIB has recently started to officially support the gcc cleanup
attribute in its public API, hence let's do the same for our APIs.
With this patch we'll define an xyz_unrefp() call for each public
xyz_unref() call, to make it easy to use inside a
__attribute__((cleanup())) expression. Then, all code is ported over to
make use of this.
The new calls are also documented in the man pages, with examples how to
use them (well, I only added docs where the _unref() call itself already
had docs, and the examples, only cover sd_bus_unrefp() and
sd_event_unrefp()).
This also renames sd_lldp_free() to sd_lldp_unref(), since that's how we
tend to call our destructors these days.
Note that this defines no public macro that wraps gcc's attribute and
makes it easier to use. While I think it's our duty in the library to
make our stuff easy to use, I figure it's not our duty to make gcc's own
features easy to use on its own. Most likely, client code which wants to
make use of this should define its own:
#define _cleanup_(function) __attribute__((cleanup(function)))
Or similar, to make the gcc feature easier to use.
Making this logic public has the benefit that we can remove three header
files whose only purpose was to define these functions internally.
See #2008.
2015-11-27 19:13:45 +01:00
|
|
|
_cleanup_(sd_device_unrefp) sd_device *device = NULL;
|
2015-04-14 16:22:39 +02:00
|
|
|
char syspath[strlen(path) + 1 + strlen(dent->d_name) + 1];
|
|
|
|
dev_t devnum;
|
|
|
|
int ifindex, initialized, k;
|
|
|
|
|
|
|
|
if (dent->d_name[0] == '.')
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (!match_sysname(enumerator, dent->d_name))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
(void)sprintf(syspath, "%s%s", path, dent->d_name);
|
|
|
|
|
|
|
|
k = sd_device_new_from_syspath(&device, syspath);
|
|
|
|
if (k < 0) {
|
2015-04-17 14:53:02 +02:00
|
|
|
if (k != -ENODEV)
|
|
|
|
/* this is necessarily racey, so ignore missing devices */
|
|
|
|
r = k;
|
|
|
|
|
2015-04-14 16:22:39 +02:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
k = sd_device_get_devnum(device, &devnum);
|
|
|
|
if (k < 0) {
|
|
|
|
r = k;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
k = sd_device_get_ifindex(device, &ifindex);
|
|
|
|
if (k < 0) {
|
|
|
|
r = k;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
k = sd_device_get_is_initialized(device, &initialized);
|
|
|
|
if (k < 0) {
|
|
|
|
r = k;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* All devices with a device node or network interfaces
|
|
|
|
* possibly need udev to adjust the device node permission
|
|
|
|
* or context, or rename the interface before it can be
|
|
|
|
* reliably used from other processes.
|
|
|
|
*
|
|
|
|
* For now, we can only check these types of devices, we
|
|
|
|
* might not store a database, and have no way to find out
|
|
|
|
* for all other types of devices.
|
|
|
|
*/
|
2015-04-17 14:11:00 +02:00
|
|
|
if (!enumerator->match_allow_uninitialized &&
|
2015-04-14 16:22:39 +02:00
|
|
|
!initialized &&
|
|
|
|
(major(devnum) > 0 || ifindex > 0))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (!match_parent(enumerator, device))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (!match_tag(enumerator, device))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (!match_property(enumerator, device))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (!match_sysattr(enumerator, device))
|
|
|
|
continue;
|
|
|
|
|
2015-04-17 13:50:10 +02:00
|
|
|
k = device_enumerator_add_device(enumerator, device);
|
2015-04-14 16:22:39 +02:00
|
|
|
if (k < 0)
|
|
|
|
r = k;
|
|
|
|
}
|
|
|
|
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool match_subsystem(sd_device_enumerator *enumerator, const char *subsystem) {
|
|
|
|
const char *subsystem_match;
|
|
|
|
Iterator i;
|
|
|
|
|
|
|
|
assert(enumerator);
|
|
|
|
|
|
|
|
if (!subsystem)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
SET_FOREACH(subsystem_match, enumerator->nomatch_subsystem, i)
|
|
|
|
if (fnmatch(subsystem_match, subsystem, 0) == 0)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (set_isempty(enumerator->match_subsystem))
|
|
|
|
return true;
|
|
|
|
|
|
|
|
SET_FOREACH(subsystem_match, enumerator->match_subsystem, i)
|
|
|
|
if (fnmatch(subsystem_match, subsystem, 0) == 0)
|
|
|
|
return true;
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int enumerator_scan_dir(sd_device_enumerator *enumerator, const char *basedir, const char *subdir, const char *subsystem) {
|
|
|
|
_cleanup_closedir_ DIR *dir = NULL;
|
|
|
|
char *path;
|
|
|
|
struct dirent *dent;
|
|
|
|
int r = 0;
|
|
|
|
|
|
|
|
path = strjoina("/sys/", basedir);
|
|
|
|
|
|
|
|
dir = opendir(path);
|
|
|
|
if (!dir)
|
|
|
|
return -errno;
|
|
|
|
|
|
|
|
log_debug(" device-enumerator: scanning %s", path);
|
|
|
|
|
|
|
|
FOREACH_DIRENT_ALL(dent, dir, return -errno) {
|
|
|
|
int k;
|
|
|
|
|
|
|
|
if (dent->d_name[0] == '.')
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (!match_subsystem(enumerator, subsystem ? : dent->d_name))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
k = enumerator_scan_dir_and_add_devices(enumerator, basedir, dent->d_name, subdir);
|
|
|
|
if (k < 0)
|
|
|
|
r = k;
|
|
|
|
}
|
|
|
|
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int enumerator_scan_devices_tag(sd_device_enumerator *enumerator, const char *tag) {
|
|
|
|
_cleanup_closedir_ DIR *dir = NULL;
|
|
|
|
char *path;
|
|
|
|
struct dirent *dent;
|
|
|
|
int r = 0;
|
|
|
|
|
|
|
|
assert(enumerator);
|
|
|
|
assert(tag);
|
|
|
|
|
|
|
|
path = strjoina("/run/udev/tags/", tag);
|
|
|
|
|
|
|
|
dir = opendir(path);
|
|
|
|
if (!dir) {
|
|
|
|
if (errno == ENOENT)
|
|
|
|
return 0;
|
|
|
|
else {
|
|
|
|
log_error("sd-device-enumerator: could not open tags directory %s: %m", path);
|
|
|
|
return -errno;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* TODO: filter away subsystems? */
|
|
|
|
|
|
|
|
FOREACH_DIRENT_ALL(dent, dir, return -errno) {
|
tree-wide: expose "p"-suffix unref calls in public APIs to make gcc cleanup easy
GLIB has recently started to officially support the gcc cleanup
attribute in its public API, hence let's do the same for our APIs.
With this patch we'll define an xyz_unrefp() call for each public
xyz_unref() call, to make it easy to use inside a
__attribute__((cleanup())) expression. Then, all code is ported over to
make use of this.
The new calls are also documented in the man pages, with examples how to
use them (well, I only added docs where the _unref() call itself already
had docs, and the examples, only cover sd_bus_unrefp() and
sd_event_unrefp()).
This also renames sd_lldp_free() to sd_lldp_unref(), since that's how we
tend to call our destructors these days.
Note that this defines no public macro that wraps gcc's attribute and
makes it easier to use. While I think it's our duty in the library to
make our stuff easy to use, I figure it's not our duty to make gcc's own
features easy to use on its own. Most likely, client code which wants to
make use of this should define its own:
#define _cleanup_(function) __attribute__((cleanup(function)))
Or similar, to make the gcc feature easier to use.
Making this logic public has the benefit that we can remove three header
files whose only purpose was to define these functions internally.
See #2008.
2015-11-27 19:13:45 +01:00
|
|
|
_cleanup_(sd_device_unrefp) sd_device *device = NULL;
|
2015-04-14 16:22:39 +02:00
|
|
|
const char *subsystem, *sysname;
|
|
|
|
int k;
|
|
|
|
|
|
|
|
if (dent->d_name[0] == '.')
|
|
|
|
continue;
|
|
|
|
|
|
|
|
k = sd_device_new_from_device_id(&device, dent->d_name);
|
|
|
|
if (k < 0) {
|
2015-04-17 14:53:02 +02:00
|
|
|
if (k != -ENODEV)
|
|
|
|
/* this is necessarily racy, so ignore missing devices */
|
|
|
|
r = k;
|
|
|
|
|
2015-04-14 16:22:39 +02:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
k = sd_device_get_subsystem(device, &subsystem);
|
|
|
|
if (k < 0) {
|
|
|
|
r = k;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!match_subsystem(enumerator, subsystem))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
k = sd_device_get_sysname(device, &sysname);
|
|
|
|
if (k < 0) {
|
|
|
|
r = k;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!match_sysname(enumerator, sysname))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (!match_parent(enumerator, device))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (!match_property(enumerator, device))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (!match_sysattr(enumerator, device))
|
|
|
|
continue;
|
|
|
|
|
2015-04-17 13:50:10 +02:00
|
|
|
k = device_enumerator_add_device(enumerator, device);
|
2015-04-14 16:22:39 +02:00
|
|
|
if (k < 0) {
|
|
|
|
r = k;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int enumerator_scan_devices_tags(sd_device_enumerator *enumerator) {
|
|
|
|
const char *tag;
|
|
|
|
Iterator i;
|
|
|
|
int r;
|
|
|
|
|
|
|
|
assert(enumerator);
|
|
|
|
|
|
|
|
SET_FOREACH(tag, enumerator->match_tag, i) {
|
|
|
|
r = enumerator_scan_devices_tag(enumerator, tag);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int parent_add_child(sd_device_enumerator *enumerator, const char *path) {
|
tree-wide: expose "p"-suffix unref calls in public APIs to make gcc cleanup easy
GLIB has recently started to officially support the gcc cleanup
attribute in its public API, hence let's do the same for our APIs.
With this patch we'll define an xyz_unrefp() call for each public
xyz_unref() call, to make it easy to use inside a
__attribute__((cleanup())) expression. Then, all code is ported over to
make use of this.
The new calls are also documented in the man pages, with examples how to
use them (well, I only added docs where the _unref() call itself already
had docs, and the examples, only cover sd_bus_unrefp() and
sd_event_unrefp()).
This also renames sd_lldp_free() to sd_lldp_unref(), since that's how we
tend to call our destructors these days.
Note that this defines no public macro that wraps gcc's attribute and
makes it easier to use. While I think it's our duty in the library to
make our stuff easy to use, I figure it's not our duty to make gcc's own
features easy to use on its own. Most likely, client code which wants to
make use of this should define its own:
#define _cleanup_(function) __attribute__((cleanup(function)))
Or similar, to make the gcc feature easier to use.
Making this logic public has the benefit that we can remove three header
files whose only purpose was to define these functions internally.
See #2008.
2015-11-27 19:13:45 +01:00
|
|
|
_cleanup_(sd_device_unrefp) sd_device *device = NULL;
|
2015-04-14 16:22:39 +02:00
|
|
|
const char *subsystem, *sysname;
|
|
|
|
int r;
|
|
|
|
|
|
|
|
r = sd_device_new_from_syspath(&device, path);
|
2015-04-17 14:53:02 +02:00
|
|
|
if (r == -ENODEV)
|
|
|
|
/* this is necessarily racy, so ignore missing devices */
|
2015-04-14 16:22:39 +02:00
|
|
|
return 0;
|
|
|
|
else if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
r = sd_device_get_subsystem(device, &subsystem);
|
sd-device: fix enumeration of devices without subsystem
Prior to commit c32eb440bab953a0169cd207dfef5cad16dfb340, libudev's
function udev_enumerate_scan_devices() had behaved differently. If
parent match was added with udev_enumerate_add_match_parent(),
udev_enumerate_scan_devices() did not return error if some child devices
had no subsystem symlink in sysfs. An example of such devices is USB
endpoints /sys/bus/usb/devices/*/ep_*. If there was a parent match
against USB device, old implementation of udev_enumerate_scan_devices()
did not treat ep_* device directories without subsystem symlink as error
and just ignored them, but new implementation returns -ENOENT (also
ignoring these devices) though correctly enumerates all other matching
devices.
To compare, you could look at 96df036fe3d25525a44f5efdb2fc8560e82e6cfd,
in src/libudev/libudev-enumerate.c, function parent_add_child():
if (!match_subsystem(enumerate, udev_device_get_subsystem(dev)))
goto nomatch;
udev_device_get_subsystem() was returning NULL, match_subsystem() was
returning false, and USB endpoint device was ignored.
New parent_add_child() from src/libsystemd/sd-device/device-enumerator.c
checks return value of sd_device_get_subsystem() and fails if subsystem
was not found. Absence of subsystem symlink should not be really treated
as error because all enumerations of children of USB devices will fail
with -ENOENT. This new behavior also breaks system-config-printer.
So restore old behavior and treat absence of subsystem symlink as no
match.
2015-08-22 10:33:32 +02:00
|
|
|
if (r == -ENOENT)
|
|
|
|
return 0;
|
2015-04-14 16:22:39 +02:00
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
if (!match_subsystem(enumerator, subsystem))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
r = sd_device_get_sysname(device, &sysname);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
if (!match_sysname(enumerator, sysname))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if (!match_property(enumerator, device))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if (!match_sysattr(enumerator, device))
|
|
|
|
return 0;
|
|
|
|
|
2015-04-17 13:50:10 +02:00
|
|
|
r = device_enumerator_add_device(enumerator, device);
|
2015-04-14 16:22:39 +02:00
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int parent_crawl_children(sd_device_enumerator *enumerator, const char *path, unsigned maxdepth) {
|
|
|
|
_cleanup_closedir_ DIR *dir = NULL;
|
|
|
|
struct dirent *dent;
|
|
|
|
int r = 0;
|
|
|
|
|
|
|
|
dir = opendir(path);
|
|
|
|
if (!dir) {
|
|
|
|
log_debug("sd-device-enumerate: could not open parent directory %s: %m", path);
|
|
|
|
return -errno;
|
|
|
|
}
|
|
|
|
|
|
|
|
FOREACH_DIRENT_ALL(dent, dir, return -errno) {
|
|
|
|
_cleanup_free_ char *child = NULL;
|
|
|
|
int k;
|
|
|
|
|
|
|
|
if (dent->d_name[0] == '.')
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (dent->d_type != DT_DIR)
|
|
|
|
continue;
|
|
|
|
|
2015-05-21 05:34:12 +02:00
|
|
|
child = strjoin(path, "/", dent->d_name, NULL);
|
|
|
|
if (!child)
|
|
|
|
return -ENOMEM;
|
2015-04-14 16:22:39 +02:00
|
|
|
|
|
|
|
k = parent_add_child(enumerator, child);
|
|
|
|
if (k < 0)
|
|
|
|
r = k;
|
|
|
|
|
|
|
|
if (maxdepth > 0)
|
|
|
|
parent_crawl_children(enumerator, child, maxdepth - 1);
|
|
|
|
else
|
|
|
|
log_debug("device-enumerate: max depth reached, %s: ignoring devices", child);
|
|
|
|
}
|
|
|
|
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int enumerator_scan_devices_children(sd_device_enumerator *enumerator) {
|
|
|
|
const char *path;
|
|
|
|
int r = 0, k;
|
|
|
|
|
|
|
|
r = sd_device_get_syspath(enumerator->match_parent, &path);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
k = parent_add_child(enumerator, path);
|
|
|
|
if (k < 0)
|
|
|
|
r = k;
|
|
|
|
|
|
|
|
k = parent_crawl_children(enumerator, path, DEVICE_ENUMERATE_MAX_DEPTH);
|
|
|
|
if (k < 0)
|
|
|
|
r = k;
|
|
|
|
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int enumerator_scan_devices_all(sd_device_enumerator *enumerator) {
|
|
|
|
int r = 0;
|
|
|
|
|
|
|
|
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", NULL);
|
2015-09-30 22:16:17 +02:00
|
|
|
if (r < 0)
|
|
|
|
return log_debug_errno(r, "device-enumerator: failed to scan /sys/subsystem: %m");
|
2015-04-14 16:22:39 +02:00
|
|
|
} else {
|
|
|
|
int k;
|
|
|
|
|
|
|
|
k = enumerator_scan_dir(enumerator, "bus", "devices", NULL);
|
|
|
|
if (k < 0) {
|
|
|
|
log_debug_errno(k, "device-enumerator: failed to scan /sys/bus: %m");
|
|
|
|
r = k;
|
|
|
|
}
|
|
|
|
|
|
|
|
k = enumerator_scan_dir(enumerator, "class", NULL, NULL);
|
|
|
|
if (k < 0) {
|
|
|
|
log_debug_errno(k, "device-enumerator: failed to scan /sys/class: %m");
|
|
|
|
r = k;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
int device_enumerator_scan_devices(sd_device_enumerator *enumerator) {
|
|
|
|
sd_device *device;
|
|
|
|
int r;
|
|
|
|
|
|
|
|
assert(enumerator);
|
|
|
|
|
|
|
|
if (enumerator->scan_uptodate &&
|
|
|
|
enumerator->type == DEVICE_ENUMERATION_TYPE_DEVICES)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
while ((device = prioq_pop(enumerator->devices)))
|
|
|
|
sd_device_unref(device);
|
|
|
|
|
|
|
|
if (!set_isempty(enumerator->match_tag)) {
|
|
|
|
r = enumerator_scan_devices_tags(enumerator);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
} else if (enumerator->match_parent) {
|
|
|
|
r = enumerator_scan_devices_children(enumerator);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
} else {
|
|
|
|
r = enumerator_scan_devices_all(enumerator);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
enumerator->scan_uptodate = true;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
_public_ sd_device *sd_device_enumerator_get_device_first(sd_device_enumerator *enumerator) {
|
|
|
|
int r;
|
|
|
|
|
|
|
|
assert_return(enumerator, NULL);
|
|
|
|
|
|
|
|
r = device_enumerator_scan_devices(enumerator);
|
|
|
|
if (r < 0)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
enumerator->type = DEVICE_ENUMERATION_TYPE_DEVICES;
|
|
|
|
|
|
|
|
return prioq_peek(enumerator->devices);
|
|
|
|
}
|
|
|
|
|
|
|
|
_public_ sd_device *sd_device_enumerator_get_device_next(sd_device_enumerator *enumerator) {
|
|
|
|
assert_return(enumerator, NULL);
|
|
|
|
|
|
|
|
if (!enumerator->scan_uptodate ||
|
|
|
|
enumerator->type != DEVICE_ENUMERATION_TYPE_DEVICES)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
sd_device_unref(prioq_pop(enumerator->devices));
|
|
|
|
|
|
|
|
return prioq_peek(enumerator->devices);
|
|
|
|
}
|
|
|
|
|
|
|
|
int device_enumerator_scan_subsystems(sd_device_enumerator *enumerator) {
|
|
|
|
sd_device *device;
|
|
|
|
const char *subsysdir;
|
|
|
|
int r = 0, k;
|
|
|
|
|
|
|
|
assert(enumerator);
|
|
|
|
|
|
|
|
if (enumerator->scan_uptodate &&
|
|
|
|
enumerator->type == DEVICE_ENUMERATION_TYPE_SUBSYSTEMS)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
while ((device = prioq_pop(enumerator->devices)))
|
|
|
|
sd_device_unref(device);
|
|
|
|
|
|
|
|
/* modules */
|
|
|
|
if (match_subsystem(enumerator, "module")) {
|
|
|
|
k = enumerator_scan_dir_and_add_devices(enumerator, "module", NULL, NULL);
|
|
|
|
if (k < 0) {
|
|
|
|
log_debug_errno(k, "device-enumerator: failed to scan modules: %m");
|
|
|
|
r = k;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (access("/sys/subsystem", F_OK) >= 0)
|
|
|
|
subsysdir = "subsystem";
|
|
|
|
else
|
|
|
|
subsysdir = "bus";
|
|
|
|
|
|
|
|
/* subsystems (only buses support coldplug) */
|
|
|
|
if (match_subsystem(enumerator, "subsystem")) {
|
|
|
|
k = enumerator_scan_dir_and_add_devices(enumerator, subsysdir, NULL, NULL);
|
|
|
|
if (k < 0) {
|
|
|
|
log_debug_errno(k, "device-enumerator: failed to scan subsystems: %m");
|
|
|
|
r = k;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* subsystem drivers */
|
|
|
|
if (match_subsystem(enumerator, "drivers")) {
|
|
|
|
k = enumerator_scan_dir(enumerator, subsysdir, "drivers", "drivers");
|
|
|
|
if (k < 0) {
|
|
|
|
log_debug_errno(k, "device-enumerator: failed to scan drivers: %m");
|
|
|
|
r = k;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
enumerator->scan_uptodate = true;
|
|
|
|
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
_public_ sd_device *sd_device_enumerator_get_subsystem_first(sd_device_enumerator *enumerator) {
|
|
|
|
int r;
|
|
|
|
|
|
|
|
assert_return(enumerator, NULL);
|
|
|
|
|
|
|
|
r = device_enumerator_scan_subsystems(enumerator);
|
|
|
|
if (r < 0)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
enumerator->type = DEVICE_ENUMERATION_TYPE_SUBSYSTEMS;
|
|
|
|
|
|
|
|
return prioq_peek(enumerator->devices);
|
|
|
|
}
|
|
|
|
|
|
|
|
_public_ sd_device *sd_device_enumerator_get_subsystem_next(sd_device_enumerator *enumerator) {
|
|
|
|
assert_return(enumerator, NULL);
|
|
|
|
|
|
|
|
if (enumerator->scan_uptodate ||
|
|
|
|
enumerator->type != DEVICE_ENUMERATION_TYPE_SUBSYSTEMS)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
sd_device_unref(prioq_pop(enumerator->devices));
|
|
|
|
|
|
|
|
return prioq_peek(enumerator->devices);
|
|
|
|
}
|
|
|
|
|
|
|
|
sd_device *device_enumerator_get_first(sd_device_enumerator *enumerator) {
|
|
|
|
assert_return(enumerator, NULL);
|
|
|
|
|
|
|
|
return prioq_peek(enumerator->devices);
|
|
|
|
}
|
|
|
|
|
|
|
|
sd_device *device_enumerator_get_next(sd_device_enumerator *enumerator) {
|
|
|
|
assert_return(enumerator, NULL);
|
|
|
|
|
|
|
|
sd_device_unref(prioq_pop(enumerator->devices));
|
|
|
|
|
|
|
|
return prioq_peek(enumerator->devices);
|
|
|
|
}
|