2020-11-09 05:23:58 +01:00
|
|
|
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
2015-04-14 16:22:39 +02:00
|
|
|
|
2019-03-27 11:32:41 +01:00
|
|
|
#include <fcntl.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
|
2015-04-14 16:22:39 +02:00
|
|
|
#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 "set.h"
|
2019-03-13 12:14:47 +01:00
|
|
|
#include "sort-util.h"
|
2015-10-24 22:58:24 +02:00
|
|
|
#include "string-util.h"
|
|
|
|
#include "strv.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;
|
2018-08-27 08:35:45 +02:00
|
|
|
sd_device **devices;
|
|
|
|
size_t n_devices, n_allocated, current_device_index;
|
2015-04-14 16:22:39 +02:00
|
|
|
bool scan_uptodate;
|
|
|
|
|
|
|
|
Set *match_subsystem;
|
|
|
|
Set *nomatch_subsystem;
|
|
|
|
Hashmap *match_sysattr;
|
|
|
|
Hashmap *nomatch_sysattr;
|
|
|
|
Hashmap *match_property;
|
|
|
|
Set *match_sysname;
|
|
|
|
Set *match_tag;
|
2019-01-18 08:32:18 +01:00
|
|
|
Set *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);
|
|
|
|
|
2018-08-27 07:07:01 +02:00
|
|
|
enumerator = new(sd_device_enumerator, 1);
|
2015-04-14 16:22:39 +02:00
|
|
|
if (!enumerator)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
2018-08-27 07:07:01 +02:00
|
|
|
*enumerator = (sd_device_enumerator) {
|
|
|
|
.n_ref = 1,
|
|
|
|
.type = _DEVICE_ENUMERATION_TYPE_INVALID,
|
|
|
|
};
|
2015-04-14 16:22:39 +02:00
|
|
|
|
2018-04-05 07:26:26 +02:00
|
|
|
*ret = TAKE_PTR(enumerator);
|
2015-04-14 16:22:39 +02:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2018-08-27 07:01:46 +02:00
|
|
|
static sd_device_enumerator *device_enumerator_free(sd_device_enumerator *enumerator) {
|
2018-08-27 08:35:45 +02:00
|
|
|
size_t i;
|
2015-04-14 16:22:39 +02:00
|
|
|
|
2018-08-27 07:01:46 +02:00
|
|
|
assert(enumerator);
|
2015-04-14 16:22:39 +02:00
|
|
|
|
2018-08-27 08:35:45 +02:00
|
|
|
for (i = 0; i < enumerator->n_devices; i++)
|
|
|
|
sd_device_unref(enumerator->devices[i]);
|
2015-04-14 16:22:39 +02:00
|
|
|
|
2018-08-27 08:35:45 +02:00
|
|
|
free(enumerator->devices);
|
2020-04-29 09:01:18 +02:00
|
|
|
set_free(enumerator->match_subsystem);
|
|
|
|
set_free(enumerator->nomatch_subsystem);
|
2020-04-29 14:28:56 +02:00
|
|
|
hashmap_free(enumerator->match_sysattr);
|
|
|
|
hashmap_free(enumerator->nomatch_sysattr);
|
|
|
|
hashmap_free(enumerator->match_property);
|
2020-04-29 09:01:18 +02:00
|
|
|
set_free(enumerator->match_sysname);
|
|
|
|
set_free(enumerator->match_tag);
|
|
|
|
set_free(enumerator->match_parent);
|
2015-04-14 16:22:39 +02:00
|
|
|
|
2018-08-27 07:01:46 +02:00
|
|
|
return mfree(enumerator);
|
2015-04-14 16:22:39 +02:00
|
|
|
}
|
|
|
|
|
2018-08-27 07:01:46 +02:00
|
|
|
DEFINE_PUBLIC_TRIVIAL_REF_UNREF_FUNC(sd_device_enumerator, sd_device_enumerator, device_enumerator_free);
|
|
|
|
|
2015-04-14 16:22:39 +02:00
|
|
|
_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;
|
|
|
|
|
2020-04-29 08:47:51 +02:00
|
|
|
r = set_put_strdup(set, subsystem);
|
sd-device: optimize addition of already present matches
Our hashmap and set helpers return a different code whenever an entry
already exists, so let's use this to avoid unsetting scan_uptodate when
not necessary.
Thus, the return convention for
sd_device_enumerator_add_match_subsystem,
sd_device_enumerator_add_match_sysattr,
sd_device_enumerator_add_match_property,
sd_device_enumerator_add_match_sysname,
sd_device_enumerator_add_match_tag,
device_enumerator_add_match_parent_incremental,
sd_device_enumerator_add_match_parent,
sd_device_enumerator_allow_uninitialized,
device_enumerator_add_match_is_initialized
is that "1" is returned if action was taken, and "0" on noop.
2020-04-29 14:33:51 +02:00
|
|
|
if (r <= 0)
|
2015-04-14 16:22:39 +02:00
|
|
|
return r;
|
|
|
|
|
|
|
|
enumerator->scan_uptodate = false;
|
|
|
|
|
sd-device: optimize addition of already present matches
Our hashmap and set helpers return a different code whenever an entry
already exists, so let's use this to avoid unsetting scan_uptodate when
not necessary.
Thus, the return convention for
sd_device_enumerator_add_match_subsystem,
sd_device_enumerator_add_match_sysattr,
sd_device_enumerator_add_match_property,
sd_device_enumerator_add_match_sysname,
sd_device_enumerator_add_match_tag,
device_enumerator_add_match_parent_incremental,
sd_device_enumerator_add_match_parent,
sd_device_enumerator_allow_uninitialized,
device_enumerator_add_match_is_initialized
is that "1" is returned if action was taken, and "0" on noop.
2020-04-29 14:33:51 +02:00
|
|
|
return 1;
|
2015-04-14 16:22:39 +02:00
|
|
|
}
|
|
|
|
|
2020-04-29 14:28:56 +02:00
|
|
|
_public_ int sd_device_enumerator_add_match_sysattr(sd_device_enumerator *enumerator, const char *sysattr, const char *value, int match) {
|
2015-04-14 16:22:39 +02:00
|
|
|
Hashmap **hashmap;
|
|
|
|
int r;
|
|
|
|
|
|
|
|
assert_return(enumerator, -EINVAL);
|
2020-04-29 14:28:56 +02:00
|
|
|
assert_return(sysattr, -EINVAL);
|
2015-04-14 16:22:39 +02:00
|
|
|
|
|
|
|
if (match)
|
|
|
|
hashmap = &enumerator->match_sysattr;
|
|
|
|
else
|
|
|
|
hashmap = &enumerator->nomatch_sysattr;
|
|
|
|
|
2020-10-13 15:41:34 +02:00
|
|
|
r = hashmap_put_strdup_full(hashmap, &trivial_hash_ops_free_free, sysattr, value);
|
sd-device: optimize addition of already present matches
Our hashmap and set helpers return a different code whenever an entry
already exists, so let's use this to avoid unsetting scan_uptodate when
not necessary.
Thus, the return convention for
sd_device_enumerator_add_match_subsystem,
sd_device_enumerator_add_match_sysattr,
sd_device_enumerator_add_match_property,
sd_device_enumerator_add_match_sysname,
sd_device_enumerator_add_match_tag,
device_enumerator_add_match_parent_incremental,
sd_device_enumerator_add_match_parent,
sd_device_enumerator_allow_uninitialized,
device_enumerator_add_match_is_initialized
is that "1" is returned if action was taken, and "0" on noop.
2020-04-29 14:33:51 +02:00
|
|
|
if (r <= 0)
|
2015-04-14 16:22:39 +02:00
|
|
|
return r;
|
|
|
|
|
|
|
|
enumerator->scan_uptodate = false;
|
|
|
|
|
sd-device: optimize addition of already present matches
Our hashmap and set helpers return a different code whenever an entry
already exists, so let's use this to avoid unsetting scan_uptodate when
not necessary.
Thus, the return convention for
sd_device_enumerator_add_match_subsystem,
sd_device_enumerator_add_match_sysattr,
sd_device_enumerator_add_match_property,
sd_device_enumerator_add_match_sysname,
sd_device_enumerator_add_match_tag,
device_enumerator_add_match_parent_incremental,
sd_device_enumerator_add_match_parent,
sd_device_enumerator_allow_uninitialized,
device_enumerator_add_match_is_initialized
is that "1" is returned if action was taken, and "0" on noop.
2020-04-29 14:33:51 +02:00
|
|
|
return 1;
|
2015-04-14 16:22:39 +02:00
|
|
|
}
|
|
|
|
|
2020-04-29 14:28:56 +02:00
|
|
|
_public_ int sd_device_enumerator_add_match_property(sd_device_enumerator *enumerator, const char *property, const char *value) {
|
2015-04-14 16:22:39 +02:00
|
|
|
int r;
|
|
|
|
|
|
|
|
assert_return(enumerator, -EINVAL);
|
2020-04-29 14:28:56 +02:00
|
|
|
assert_return(property, -EINVAL);
|
2015-04-14 16:22:39 +02:00
|
|
|
|
2020-10-13 15:41:34 +02:00
|
|
|
r = hashmap_put_strdup_full(&enumerator->match_property, &trivial_hash_ops_free_free, property, value);
|
sd-device: optimize addition of already present matches
Our hashmap and set helpers return a different code whenever an entry
already exists, so let's use this to avoid unsetting scan_uptodate when
not necessary.
Thus, the return convention for
sd_device_enumerator_add_match_subsystem,
sd_device_enumerator_add_match_sysattr,
sd_device_enumerator_add_match_property,
sd_device_enumerator_add_match_sysname,
sd_device_enumerator_add_match_tag,
device_enumerator_add_match_parent_incremental,
sd_device_enumerator_add_match_parent,
sd_device_enumerator_allow_uninitialized,
device_enumerator_add_match_is_initialized
is that "1" is returned if action was taken, and "0" on noop.
2020-04-29 14:33:51 +02:00
|
|
|
if (r <= 0)
|
2015-04-14 16:22:39 +02:00
|
|
|
return r;
|
|
|
|
|
|
|
|
enumerator->scan_uptodate = false;
|
|
|
|
|
sd-device: optimize addition of already present matches
Our hashmap and set helpers return a different code whenever an entry
already exists, so let's use this to avoid unsetting scan_uptodate when
not necessary.
Thus, the return convention for
sd_device_enumerator_add_match_subsystem,
sd_device_enumerator_add_match_sysattr,
sd_device_enumerator_add_match_property,
sd_device_enumerator_add_match_sysname,
sd_device_enumerator_add_match_tag,
device_enumerator_add_match_parent_incremental,
sd_device_enumerator_add_match_parent,
sd_device_enumerator_allow_uninitialized,
device_enumerator_add_match_is_initialized
is that "1" is returned if action was taken, and "0" on noop.
2020-04-29 14:33:51 +02:00
|
|
|
return 1;
|
2015-04-14 16:22:39 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
_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);
|
|
|
|
|
2020-04-29 08:47:51 +02:00
|
|
|
r = set_put_strdup(&enumerator->match_sysname, sysname);
|
sd-device: optimize addition of already present matches
Our hashmap and set helpers return a different code whenever an entry
already exists, so let's use this to avoid unsetting scan_uptodate when
not necessary.
Thus, the return convention for
sd_device_enumerator_add_match_subsystem,
sd_device_enumerator_add_match_sysattr,
sd_device_enumerator_add_match_property,
sd_device_enumerator_add_match_sysname,
sd_device_enumerator_add_match_tag,
device_enumerator_add_match_parent_incremental,
sd_device_enumerator_add_match_parent,
sd_device_enumerator_allow_uninitialized,
device_enumerator_add_match_is_initialized
is that "1" is returned if action was taken, and "0" on noop.
2020-04-29 14:33:51 +02:00
|
|
|
if (r <= 0)
|
2015-04-14 16:22:39 +02:00
|
|
|
return r;
|
|
|
|
|
|
|
|
enumerator->scan_uptodate = false;
|
|
|
|
|
sd-device: optimize addition of already present matches
Our hashmap and set helpers return a different code whenever an entry
already exists, so let's use this to avoid unsetting scan_uptodate when
not necessary.
Thus, the return convention for
sd_device_enumerator_add_match_subsystem,
sd_device_enumerator_add_match_sysattr,
sd_device_enumerator_add_match_property,
sd_device_enumerator_add_match_sysname,
sd_device_enumerator_add_match_tag,
device_enumerator_add_match_parent_incremental,
sd_device_enumerator_add_match_parent,
sd_device_enumerator_allow_uninitialized,
device_enumerator_add_match_is_initialized
is that "1" is returned if action was taken, and "0" on noop.
2020-04-29 14:33:51 +02:00
|
|
|
return 1;
|
2015-04-14 16:22:39 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
_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);
|
|
|
|
|
2020-04-29 08:47:51 +02:00
|
|
|
r = set_put_strdup(&enumerator->match_tag, tag);
|
sd-device: optimize addition of already present matches
Our hashmap and set helpers return a different code whenever an entry
already exists, so let's use this to avoid unsetting scan_uptodate when
not necessary.
Thus, the return convention for
sd_device_enumerator_add_match_subsystem,
sd_device_enumerator_add_match_sysattr,
sd_device_enumerator_add_match_property,
sd_device_enumerator_add_match_sysname,
sd_device_enumerator_add_match_tag,
device_enumerator_add_match_parent_incremental,
sd_device_enumerator_add_match_parent,
sd_device_enumerator_allow_uninitialized,
device_enumerator_add_match_is_initialized
is that "1" is returned if action was taken, and "0" on noop.
2020-04-29 14:33:51 +02:00
|
|
|
if (r <= 0)
|
2015-04-14 16:22:39 +02:00
|
|
|
return r;
|
|
|
|
|
|
|
|
enumerator->scan_uptodate = false;
|
|
|
|
|
sd-device: optimize addition of already present matches
Our hashmap and set helpers return a different code whenever an entry
already exists, so let's use this to avoid unsetting scan_uptodate when
not necessary.
Thus, the return convention for
sd_device_enumerator_add_match_subsystem,
sd_device_enumerator_add_match_sysattr,
sd_device_enumerator_add_match_property,
sd_device_enumerator_add_match_sysname,
sd_device_enumerator_add_match_tag,
device_enumerator_add_match_parent_incremental,
sd_device_enumerator_add_match_parent,
sd_device_enumerator_allow_uninitialized,
device_enumerator_add_match_is_initialized
is that "1" is returned if action was taken, and "0" on noop.
2020-04-29 14:33:51 +02:00
|
|
|
return 1;
|
2015-04-14 16:22:39 +02:00
|
|
|
}
|
|
|
|
|
2019-01-18 08:32:18 +01:00
|
|
|
int device_enumerator_add_match_parent_incremental(sd_device_enumerator *enumerator, sd_device *parent) {
|
|
|
|
const char *path;
|
|
|
|
int r;
|
|
|
|
|
2020-04-29 14:44:25 +02:00
|
|
|
assert(enumerator);
|
|
|
|
assert(parent);
|
2015-04-14 16:22:39 +02:00
|
|
|
|
2019-01-18 08:32:18 +01:00
|
|
|
r = sd_device_get_syspath(parent, &path);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
2020-04-29 08:47:51 +02:00
|
|
|
r = set_put_strdup(&enumerator->match_parent, path);
|
sd-device: optimize addition of already present matches
Our hashmap and set helpers return a different code whenever an entry
already exists, so let's use this to avoid unsetting scan_uptodate when
not necessary.
Thus, the return convention for
sd_device_enumerator_add_match_subsystem,
sd_device_enumerator_add_match_sysattr,
sd_device_enumerator_add_match_property,
sd_device_enumerator_add_match_sysname,
sd_device_enumerator_add_match_tag,
device_enumerator_add_match_parent_incremental,
sd_device_enumerator_add_match_parent,
sd_device_enumerator_allow_uninitialized,
device_enumerator_add_match_is_initialized
is that "1" is returned if action was taken, and "0" on noop.
2020-04-29 14:33:51 +02:00
|
|
|
if (r <= 0)
|
2019-01-18 08:32:18 +01:00
|
|
|
return r;
|
2015-04-14 16:22:39 +02:00
|
|
|
|
|
|
|
enumerator->scan_uptodate = false;
|
|
|
|
|
sd-device: optimize addition of already present matches
Our hashmap and set helpers return a different code whenever an entry
already exists, so let's use this to avoid unsetting scan_uptodate when
not necessary.
Thus, the return convention for
sd_device_enumerator_add_match_subsystem,
sd_device_enumerator_add_match_sysattr,
sd_device_enumerator_add_match_property,
sd_device_enumerator_add_match_sysname,
sd_device_enumerator_add_match_tag,
device_enumerator_add_match_parent_incremental,
sd_device_enumerator_add_match_parent,
sd_device_enumerator_allow_uninitialized,
device_enumerator_add_match_is_initialized
is that "1" is returned if action was taken, and "0" on noop.
2020-04-29 14:33:51 +02:00
|
|
|
return 1;
|
2015-04-14 16:22:39 +02:00
|
|
|
}
|
|
|
|
|
2019-01-18 08:32:18 +01:00
|
|
|
_public_ int sd_device_enumerator_add_match_parent(sd_device_enumerator *enumerator, sd_device *parent) {
|
2020-04-29 14:44:25 +02:00
|
|
|
assert_return(enumerator, -EINVAL);
|
|
|
|
assert_return(parent, -EINVAL);
|
|
|
|
|
|
|
|
set_clear(enumerator->match_parent);
|
|
|
|
|
2019-01-18 08:32:18 +01:00
|
|
|
return device_enumerator_add_match_parent_incremental(enumerator, parent);
|
|
|
|
}
|
|
|
|
|
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;
|
|
|
|
|
sd-device: optimize addition of already present matches
Our hashmap and set helpers return a different code whenever an entry
already exists, so let's use this to avoid unsetting scan_uptodate when
not necessary.
Thus, the return convention for
sd_device_enumerator_add_match_subsystem,
sd_device_enumerator_add_match_sysattr,
sd_device_enumerator_add_match_property,
sd_device_enumerator_add_match_sysname,
sd_device_enumerator_add_match_tag,
device_enumerator_add_match_parent_incremental,
sd_device_enumerator_add_match_parent,
sd_device_enumerator_allow_uninitialized,
device_enumerator_add_match_is_initialized
is that "1" is returned if action was taken, and "0" on noop.
2020-04-29 14:33:51 +02:00
|
|
|
return 1;
|
2015-04-17 14:11:00 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
sd-device: optimize addition of already present matches
Our hashmap and set helpers return a different code whenever an entry
already exists, so let's use this to avoid unsetting scan_uptodate when
not necessary.
Thus, the return convention for
sd_device_enumerator_add_match_subsystem,
sd_device_enumerator_add_match_sysattr,
sd_device_enumerator_add_match_property,
sd_device_enumerator_add_match_sysname,
sd_device_enumerator_add_match_tag,
device_enumerator_add_match_parent_incremental,
sd_device_enumerator_add_match_parent,
sd_device_enumerator_allow_uninitialized,
device_enumerator_add_match_is_initialized
is that "1" is returned if action was taken, and "0" on noop.
2020-04-29 14:33:51 +02:00
|
|
|
return 1;
|
2015-04-14 16:22:39 +02:00
|
|
|
}
|
|
|
|
|
2018-09-18 01:39:24 +02:00
|
|
|
static int device_compare(sd_device * const *_a, sd_device * const *_b) {
|
2018-08-27 08:35:45 +02:00
|
|
|
sd_device *a = *(sd_device **)_a, *b = *(sd_device **)_b;
|
2015-04-14 16:22:39 +02:00
|
|
|
const char *devpath_a, *devpath_b, *sound_a;
|
2015-04-17 14:06:31 +02:00
|
|
|
bool delay_a, delay_b;
|
2018-09-18 01:39:24 +02:00
|
|
|
int r;
|
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. */
|
2017-12-14 19:02:29 +01:00
|
|
|
sound_a += STRLEN("/sound/card");
|
2015-04-14 16:22:39 +02:00
|
|
|
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-");
|
2018-09-18 01:39:24 +02:00
|
|
|
r = CMP(delay_a, delay_b);
|
|
|
|
if (r != 0)
|
|
|
|
return r;
|
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
|
|
|
assert_return(enumerator, -EINVAL);
|
|
|
|
assert_return(device, -EINVAL);
|
|
|
|
|
2018-08-27 08:35:45 +02:00
|
|
|
if (!GREEDY_REALLOC(enumerator->devices, enumerator->n_allocated, enumerator->n_devices + 1))
|
|
|
|
return -ENOMEM;
|
2015-04-14 16:22:39 +02:00
|
|
|
|
2018-08-27 08:35:45 +02:00
|
|
|
enumerator->devices[enumerator->n_devices++] = sd_device_ref(device);
|
2015-04-14 16:22:39 +02:00
|
|
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
assert(enumerator);
|
|
|
|
assert(device);
|
|
|
|
|
2020-09-08 11:58:29 +02:00
|
|
|
HASHMAP_FOREACH_KEY(value, sysattr, enumerator->nomatch_sysattr)
|
2015-04-14 16:22:39 +02:00
|
|
|
if (match_sysattr_value(device, sysattr, value))
|
|
|
|
return false;
|
|
|
|
|
2020-09-08 11:58:29 +02:00
|
|
|
HASHMAP_FOREACH_KEY(value, sysattr, enumerator->match_sysattr)
|
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;
|
|
|
|
|
|
|
|
assert(enumerator);
|
|
|
|
assert(device);
|
|
|
|
|
|
|
|
if (hashmap_isempty(enumerator->match_property))
|
|
|
|
return true;
|
|
|
|
|
2020-09-08 11:58:29 +02:00
|
|
|
HASHMAP_FOREACH_KEY(value, property, enumerator->match_property) {
|
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;
|
|
|
|
|
|
|
|
assert(enumerator);
|
|
|
|
assert(device);
|
|
|
|
|
2020-09-08 11:58:29 +02:00
|
|
|
SET_FOREACH(tag, enumerator->match_tag)
|
2015-04-14 16:22:39 +02:00
|
|
|
if (!sd_device_has_tag(device, tag))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool match_parent(sd_device_enumerator *enumerator, sd_device *device) {
|
2019-01-18 08:32:18 +01:00
|
|
|
const char *syspath_parent, *syspath;
|
2015-04-14 16:22:39 +02:00
|
|
|
|
|
|
|
assert(enumerator);
|
|
|
|
assert(device);
|
|
|
|
|
2019-01-18 08:32:18 +01:00
|
|
|
if (set_isempty(enumerator->match_parent))
|
2015-04-14 16:22:39 +02:00
|
|
|
return true;
|
|
|
|
|
2019-03-27 14:28:24 +01:00
|
|
|
assert_se(sd_device_get_syspath(device, &syspath) >= 0);
|
2015-04-14 16:22:39 +02:00
|
|
|
|
2020-09-08 11:58:29 +02:00
|
|
|
SET_FOREACH(syspath_parent, enumerator->match_parent)
|
2019-01-18 08:32:18 +01:00
|
|
|
if (path_startswith(syspath, syspath_parent))
|
|
|
|
return true;
|
2015-04-14 16:22:39 +02:00
|
|
|
|
2019-01-18 08:32:18 +01:00
|
|
|
return false;
|
2015-04-14 16:22:39 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static bool match_sysname(sd_device_enumerator *enumerator, const char *sysname) {
|
|
|
|
const char *sysname_match;
|
|
|
|
|
|
|
|
assert(enumerator);
|
|
|
|
assert(sysname);
|
|
|
|
|
|
|
|
if (set_isempty(enumerator->match_sysname))
|
|
|
|
return true;
|
|
|
|
|
2020-09-08 11:58:29 +02:00
|
|
|
SET_FOREACH(sysname_match, enumerator->match_sysname)
|
2015-04-14 16:22:39 +02:00
|
|
|
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];
|
2018-10-29 09:32:21 +01:00
|
|
|
int initialized, k;
|
2015-04-14 16:22:39 +02:00
|
|
|
|
|
|
|
if (dent->d_name[0] == '.')
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (!match_sysname(enumerator, dent->d_name))
|
|
|
|
continue;
|
|
|
|
|
2018-07-03 07:36:15 +02:00
|
|
|
(void) sprintf(syspath, "%s%s", path, dent->d_name);
|
2015-04-14 16:22:39 +02:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2018-10-29 09:32:21 +01:00
|
|
|
initialized = sd_device_get_is_initialized(device);
|
|
|
|
if (initialized < 0) {
|
2019-11-05 15:02:31 +01:00
|
|
|
if (initialized != -ENOENT)
|
|
|
|
/* this is necessarily racey, so ignore missing devices */
|
|
|
|
r = initialized;
|
|
|
|
|
2015-04-14 16:22:39 +02:00
|
|
|
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 &&
|
2018-10-29 09:32:21 +01:00
|
|
|
(sd_device_get_devnum(device, NULL) >= 0 ||
|
|
|
|
sd_device_get_ifindex(device, NULL) >= 0))
|
2015-04-14 16:22:39 +02:00
|
|
|
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;
|
|
|
|
|
|
|
|
assert(enumerator);
|
|
|
|
|
|
|
|
if (!subsystem)
|
|
|
|
return false;
|
|
|
|
|
2020-09-08 11:58:29 +02:00
|
|
|
SET_FOREACH(subsystem_match, enumerator->nomatch_subsystem)
|
2015-04-14 16:22:39 +02:00
|
|
|
if (fnmatch(subsystem_match, subsystem, 0) == 0)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (set_isempty(enumerator->match_subsystem))
|
|
|
|
return true;
|
|
|
|
|
2020-09-08 11:58:29 +02:00
|
|
|
SET_FOREACH(subsystem_match, enumerator->match_subsystem)
|
2015-04-14 16:22:39 +02:00
|
|
|
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;
|
|
|
|
|
2018-11-01 08:15:50 +01:00
|
|
|
log_debug("sd-device-enumerator: Scanning %s", path);
|
2015-04-14 16:22:39 +02:00
|
|
|
|
|
|
|
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) {
|
2018-11-01 08:15:50 +01:00
|
|
|
if (errno != ENOENT)
|
|
|
|
return log_debug_errno(errno, "sd-device-enumerator: Failed to open tags directory %s: %m", path);
|
|
|
|
return 0;
|
2015-04-14 16:22:39 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* 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) {
|
2019-11-05 15:02:31 +01:00
|
|
|
if (k != -ENOENT)
|
|
|
|
/* this is necessarily racy, so ignore missing devices */
|
|
|
|
r = k;
|
2015-04-14 16:22:39 +02:00
|
|
|
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;
|
2016-06-26 22:36:46 +02:00
|
|
|
int r = 0;
|
2015-04-14 16:22:39 +02:00
|
|
|
|
|
|
|
assert(enumerator);
|
|
|
|
|
2020-09-08 11:58:29 +02:00
|
|
|
SET_FOREACH(tag, enumerator->match_tag) {
|
2016-06-26 22:36:46 +02:00
|
|
|
int k;
|
|
|
|
|
|
|
|
k = enumerator_scan_devices_tag(enumerator, tag);
|
|
|
|
if (k < 0)
|
|
|
|
r = k;
|
2015-04-14 16:22:39 +02:00
|
|
|
}
|
|
|
|
|
2016-06-26 22:36:46 +02:00
|
|
|
return r;
|
2015-04-14 16:22:39 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
2017-05-13 17:26:55 +02:00
|
|
|
if (!dir)
|
2018-11-01 08:15:50 +01:00
|
|
|
return log_debug_errno(errno, "sd-device-enumerator: Failed to open parent directory %s: %m", path);
|
2015-04-14 16:22:39 +02:00
|
|
|
|
|
|
|
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;
|
|
|
|
|
2019-06-20 20:07:01 +02:00
|
|
|
child = path_join(path, dent->d_name);
|
2015-05-21 05:34:12 +02:00
|
|
|
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
|
2018-11-01 08:15:50 +01:00
|
|
|
log_debug("sd-device-enumerator: Max depth reached, %s: ignoring devices", child);
|
2015-04-14 16:22:39 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int enumerator_scan_devices_children(sd_device_enumerator *enumerator) {
|
|
|
|
const char *path;
|
|
|
|
int r = 0, k;
|
|
|
|
|
2020-09-08 11:58:29 +02:00
|
|
|
SET_FOREACH(path, enumerator->match_parent) {
|
2019-01-18 08:32:18 +01:00
|
|
|
k = parent_add_child(enumerator, path);
|
|
|
|
if (k < 0)
|
|
|
|
r = k;
|
2015-04-14 16:22:39 +02:00
|
|
|
|
2019-01-18 08:32:18 +01:00
|
|
|
k = parent_crawl_children(enumerator, path, DEVICE_ENUMERATE_MAX_DEPTH);
|
|
|
|
if (k < 0)
|
|
|
|
r = k;
|
|
|
|
}
|
2015-04-14 16:22:39 +02:00
|
|
|
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int enumerator_scan_devices_all(sd_device_enumerator *enumerator) {
|
|
|
|
int r = 0;
|
|
|
|
|
2018-11-01 08:15:50 +01:00
|
|
|
log_debug("sd-device-enumerator: Scan all dirs");
|
2015-04-14 16:22:39 +02:00
|
|
|
|
|
|
|
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)
|
2018-11-01 08:15:50 +01:00
|
|
|
return log_debug_errno(r, "sd-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);
|
2020-11-19 18:35:24 +01:00
|
|
|
if (k < 0)
|
|
|
|
r = log_debug_errno(k, "sd-device-enumerator: Failed to scan /sys/bus: %m");
|
2015-04-14 16:22:39 +02:00
|
|
|
|
|
|
|
k = enumerator_scan_dir(enumerator, "class", NULL, NULL);
|
2020-11-19 18:35:24 +01:00
|
|
|
if (k < 0)
|
|
|
|
r = log_debug_errno(k, "sd-device-enumerator: Failed to scan /sys/class: %m");
|
2015-04-14 16:22:39 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
2018-10-09 15:52:33 +02:00
|
|
|
static void device_enumerator_dedup_devices(sd_device_enumerator *enumerator) {
|
|
|
|
sd_device **a, **b, **end;
|
|
|
|
|
|
|
|
assert(enumerator);
|
|
|
|
|
|
|
|
if (enumerator->n_devices <= 1)
|
|
|
|
return;
|
|
|
|
|
|
|
|
a = enumerator->devices + 1;
|
|
|
|
b = enumerator->devices;
|
|
|
|
end = enumerator->devices + enumerator->n_devices;
|
|
|
|
|
|
|
|
for (; a < end; a++) {
|
|
|
|
const char *devpath_a, *devpath_b;
|
|
|
|
|
|
|
|
assert_se(sd_device_get_devpath(*a, &devpath_a) >= 0);
|
|
|
|
assert_se(sd_device_get_devpath(*b, &devpath_b) >= 0);
|
|
|
|
|
|
|
|
if (path_equal(devpath_a, devpath_b))
|
|
|
|
sd_device_unref(*a);
|
|
|
|
else
|
|
|
|
*(++b) = *a;
|
|
|
|
}
|
|
|
|
|
|
|
|
enumerator->n_devices = b - enumerator->devices + 1;
|
|
|
|
}
|
|
|
|
|
2015-04-14 16:22:39 +02:00
|
|
|
int device_enumerator_scan_devices(sd_device_enumerator *enumerator) {
|
2016-06-26 22:36:46 +02:00
|
|
|
int r = 0, k;
|
2018-08-27 08:35:45 +02:00
|
|
|
size_t i;
|
2015-04-14 16:22:39 +02:00
|
|
|
|
|
|
|
assert(enumerator);
|
|
|
|
|
|
|
|
if (enumerator->scan_uptodate &&
|
|
|
|
enumerator->type == DEVICE_ENUMERATION_TYPE_DEVICES)
|
|
|
|
return 0;
|
|
|
|
|
2018-08-27 08:35:45 +02:00
|
|
|
for (i = 0; i < enumerator->n_devices; i++)
|
|
|
|
sd_device_unref(enumerator->devices[i]);
|
|
|
|
|
|
|
|
enumerator->n_devices = 0;
|
2015-04-14 16:22:39 +02:00
|
|
|
|
|
|
|
if (!set_isempty(enumerator->match_tag)) {
|
2016-06-26 22:36:46 +02:00
|
|
|
k = enumerator_scan_devices_tags(enumerator);
|
|
|
|
if (k < 0)
|
|
|
|
r = k;
|
2015-04-14 16:22:39 +02:00
|
|
|
} else if (enumerator->match_parent) {
|
2016-06-26 22:36:46 +02:00
|
|
|
k = enumerator_scan_devices_children(enumerator);
|
|
|
|
if (k < 0)
|
|
|
|
r = k;
|
2015-04-14 16:22:39 +02:00
|
|
|
} else {
|
2016-06-26 22:36:46 +02:00
|
|
|
k = enumerator_scan_devices_all(enumerator);
|
|
|
|
if (k < 0)
|
|
|
|
r = k;
|
2015-04-14 16:22:39 +02:00
|
|
|
}
|
|
|
|
|
2018-09-18 01:39:24 +02:00
|
|
|
typesafe_qsort(enumerator->devices, enumerator->n_devices, device_compare);
|
2018-10-09 15:52:33 +02:00
|
|
|
device_enumerator_dedup_devices(enumerator);
|
2018-08-27 08:35:45 +02:00
|
|
|
|
2015-04-14 16:22:39 +02:00
|
|
|
enumerator->scan_uptodate = true;
|
2018-08-27 08:35:45 +02:00
|
|
|
enumerator->type = DEVICE_ENUMERATION_TYPE_DEVICES;
|
2015-04-14 16:22:39 +02:00
|
|
|
|
2016-06-26 22:36:46 +02:00
|
|
|
return r;
|
2015-04-14 16:22:39 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
_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;
|
|
|
|
|
2018-08-27 08:35:45 +02:00
|
|
|
enumerator->current_device_index = 0;
|
|
|
|
|
|
|
|
if (enumerator->n_devices == 0)
|
|
|
|
return NULL;
|
2015-04-14 16:22:39 +02:00
|
|
|
|
2018-08-27 08:35:45 +02:00
|
|
|
return enumerator->devices[0];
|
2015-04-14 16:22:39 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
_public_ sd_device *sd_device_enumerator_get_device_next(sd_device_enumerator *enumerator) {
|
|
|
|
assert_return(enumerator, NULL);
|
|
|
|
|
|
|
|
if (!enumerator->scan_uptodate ||
|
2018-08-27 08:35:45 +02:00
|
|
|
enumerator->type != DEVICE_ENUMERATION_TYPE_DEVICES ||
|
|
|
|
enumerator->current_device_index + 1 >= enumerator->n_devices)
|
2015-04-14 16:22:39 +02:00
|
|
|
return NULL;
|
|
|
|
|
2018-08-27 08:35:45 +02:00
|
|
|
return enumerator->devices[++enumerator->current_device_index];
|
2015-04-14 16:22:39 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
int device_enumerator_scan_subsystems(sd_device_enumerator *enumerator) {
|
|
|
|
const char *subsysdir;
|
|
|
|
int r = 0, k;
|
2018-08-27 08:35:45 +02:00
|
|
|
size_t i;
|
2015-04-14 16:22:39 +02:00
|
|
|
|
|
|
|
assert(enumerator);
|
|
|
|
|
|
|
|
if (enumerator->scan_uptodate &&
|
|
|
|
enumerator->type == DEVICE_ENUMERATION_TYPE_SUBSYSTEMS)
|
|
|
|
return 0;
|
|
|
|
|
2018-08-27 08:35:45 +02:00
|
|
|
for (i = 0; i < enumerator->n_devices; i++)
|
|
|
|
sd_device_unref(enumerator->devices[i]);
|
|
|
|
|
|
|
|
enumerator->n_devices = 0;
|
2015-04-14 16:22:39 +02:00
|
|
|
|
|
|
|
/* modules */
|
|
|
|
if (match_subsystem(enumerator, "module")) {
|
|
|
|
k = enumerator_scan_dir_and_add_devices(enumerator, "module", NULL, NULL);
|
2020-11-19 18:35:24 +01:00
|
|
|
if (k < 0)
|
|
|
|
r = log_debug_errno(k, "sd-device-enumerator: Failed to scan modules: %m");
|
2015-04-14 16:22:39 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
2020-11-19 18:35:24 +01:00
|
|
|
if (k < 0)
|
|
|
|
r = log_debug_errno(k, "sd-device-enumerator: Failed to scan subsystems: %m");
|
2015-04-14 16:22:39 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* subsystem drivers */
|
|
|
|
if (match_subsystem(enumerator, "drivers")) {
|
|
|
|
k = enumerator_scan_dir(enumerator, subsysdir, "drivers", "drivers");
|
2020-11-19 18:35:24 +01:00
|
|
|
if (k < 0)
|
|
|
|
r = log_debug_errno(k, "sd-device-enumerator: Failed to scan drivers: %m");
|
2015-04-14 16:22:39 +02:00
|
|
|
}
|
|
|
|
|
2018-09-18 01:39:24 +02:00
|
|
|
typesafe_qsort(enumerator->devices, enumerator->n_devices, device_compare);
|
2018-10-09 15:52:33 +02:00
|
|
|
device_enumerator_dedup_devices(enumerator);
|
2018-08-27 08:35:45 +02:00
|
|
|
|
2015-04-14 16:22:39 +02:00
|
|
|
enumerator->scan_uptodate = true;
|
2018-08-27 08:35:45 +02:00
|
|
|
enumerator->type = DEVICE_ENUMERATION_TYPE_SUBSYSTEMS;
|
2015-04-14 16:22:39 +02:00
|
|
|
|
|
|
|
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;
|
|
|
|
|
2018-08-27 08:35:45 +02:00
|
|
|
enumerator->current_device_index = 0;
|
|
|
|
|
|
|
|
if (enumerator->n_devices == 0)
|
|
|
|
return NULL;
|
2015-04-14 16:22:39 +02:00
|
|
|
|
2018-08-27 08:35:45 +02:00
|
|
|
return enumerator->devices[0];
|
2015-04-14 16:22:39 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
_public_ sd_device *sd_device_enumerator_get_subsystem_next(sd_device_enumerator *enumerator) {
|
|
|
|
assert_return(enumerator, NULL);
|
|
|
|
|
2018-08-27 08:33:17 +02:00
|
|
|
if (!enumerator->scan_uptodate ||
|
2018-08-27 08:35:45 +02:00
|
|
|
enumerator->type != DEVICE_ENUMERATION_TYPE_SUBSYSTEMS ||
|
|
|
|
enumerator->current_device_index + 1 >= enumerator->n_devices)
|
2015-04-14 16:22:39 +02:00
|
|
|
return NULL;
|
|
|
|
|
2018-08-27 08:35:45 +02:00
|
|
|
return enumerator->devices[++enumerator->current_device_index];
|
2015-04-14 16:22:39 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
sd_device *device_enumerator_get_first(sd_device_enumerator *enumerator) {
|
|
|
|
assert_return(enumerator, NULL);
|
|
|
|
|
2018-08-27 08:35:45 +02:00
|
|
|
if (!enumerator->scan_uptodate)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
enumerator->current_device_index = 0;
|
|
|
|
|
|
|
|
if (enumerator->n_devices == 0)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
return enumerator->devices[0];
|
2015-04-14 16:22:39 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
sd_device *device_enumerator_get_next(sd_device_enumerator *enumerator) {
|
|
|
|
assert_return(enumerator, NULL);
|
|
|
|
|
2018-08-27 08:35:45 +02:00
|
|
|
if (!enumerator->scan_uptodate ||
|
|
|
|
enumerator->current_device_index + 1 >= enumerator->n_devices)
|
|
|
|
return NULL;
|
2015-04-14 16:22:39 +02:00
|
|
|
|
2018-08-27 08:35:45 +02:00
|
|
|
return enumerator->devices[++enumerator->current_device_index];
|
2015-04-14 16:22:39 +02:00
|
|
|
}
|
2018-08-28 08:59:08 +02:00
|
|
|
|
|
|
|
sd_device **device_enumerator_get_devices(sd_device_enumerator *enumerator, size_t *ret_n_devices) {
|
|
|
|
assert(enumerator);
|
|
|
|
assert(ret_n_devices);
|
|
|
|
|
|
|
|
if (!enumerator->scan_uptodate)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
*ret_n_devices = enumerator->n_devices;
|
|
|
|
return enumerator->devices;
|
|
|
|
}
|