]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/libsystemd/sd-device/device-enumerator.c
Merge pull request #22791 from keszybz/bootctl-invert-order
[thirdparty/systemd.git] / src / libsystemd / sd-device / device-enumerator.c
CommitLineData
db9ecf05 1/* SPDX-License-Identifier: LGPL-2.1-or-later */
96df036f 2
ca78ad1d
ZJS
3#include <fcntl.h>
4#include <unistd.h>
5
96df036f
TG
6#include "sd-device.h"
7
b5efdb8a 8#include "alloc-util.h"
96df036f 9#include "device-enumerator-private.h"
07630cea 10#include "device-util.h"
a0956174 11#include "dirent-util.h"
3ffd4af2 12#include "fd-util.h"
07630cea 13#include "set.h"
760877e9 14#include "sort-util.h"
07630cea
LP
15#include "string-util.h"
16#include "strv.h"
96df036f
TG
17
18#define DEVICE_ENUMERATE_MAX_DEPTH 256
19
20typedef enum DeviceEnumerationType {
21 DEVICE_ENUMERATION_TYPE_DEVICES,
22 DEVICE_ENUMERATION_TYPE_SUBSYSTEMS,
3c5cc23a 23 DEVICE_ENUMERATION_TYPE_ALL,
96df036f 24 _DEVICE_ENUMERATION_TYPE_MAX,
2d93c20e 25 _DEVICE_ENUMERATION_TYPE_INVALID = -EINVAL,
96df036f
TG
26} DeviceEnumerationType;
27
28struct sd_device_enumerator {
29 unsigned n_ref;
30
31 DeviceEnumerationType type;
87c3a0f9 32 Hashmap *devices_by_syspath;
0a166589 33 sd_device **devices;
319a4f4b 34 size_t n_devices, current_device_index;
96df036f 35 bool scan_uptodate;
87c3a0f9 36 bool sorted;
96df036f 37
95a6f969 38 char **prioritized_subsystems;
96df036f
TG
39 Set *match_subsystem;
40 Set *nomatch_subsystem;
41 Hashmap *match_sysattr;
42 Hashmap *nomatch_sysattr;
43 Hashmap *match_property;
44 Set *match_sysname;
45 Set *match_tag;
e022bf66 46 Set *match_parent;
d8b50e5d 47 MatchInitializedType match_initialized;
96df036f
TG
48};
49
50_public_ int sd_device_enumerator_new(sd_device_enumerator **ret) {
4afd3348 51 _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *enumerator = NULL;
96df036f
TG
52
53 assert(ret);
54
6116d2b2 55 enumerator = new(sd_device_enumerator, 1);
96df036f
TG
56 if (!enumerator)
57 return -ENOMEM;
58
6116d2b2
YW
59 *enumerator = (sd_device_enumerator) {
60 .n_ref = 1,
61 .type = _DEVICE_ENUMERATION_TYPE_INVALID,
d8b50e5d 62 .match_initialized = MATCH_INITIALIZED_COMPAT,
6116d2b2 63 };
96df036f 64
1cc6c93a 65 *ret = TAKE_PTR(enumerator);
96df036f
TG
66
67 return 0;
68}
69
deac0c9c
YW
70static void device_unref_many(sd_device **devices, size_t n) {
71 assert(devices || n == 0);
72
73 for (size_t i = 0; i < n; i++)
74 sd_device_unref(devices[i]);
75}
76
77static void device_enumerator_unref_devices(sd_device_enumerator *enumerator) {
78 assert(enumerator);
79
87c3a0f9 80 hashmap_clear_with_destructor(enumerator->devices_by_syspath, sd_device_unref);
deac0c9c 81 device_unref_many(enumerator->devices, enumerator->n_devices);
87c3a0f9 82 enumerator->devices = mfree(enumerator->devices);
deac0c9c
YW
83 enumerator->n_devices = 0;
84}
85
8301aa0b 86static sd_device_enumerator *device_enumerator_free(sd_device_enumerator *enumerator) {
8301aa0b 87 assert(enumerator);
96df036f 88
deac0c9c 89 device_enumerator_unref_devices(enumerator);
96df036f 90
87c3a0f9 91 hashmap_free(enumerator->devices_by_syspath);
95a6f969 92 strv_free(enumerator->prioritized_subsystems);
c73bb513
ZJS
93 set_free(enumerator->match_subsystem);
94 set_free(enumerator->nomatch_subsystem);
eb1c1dc0
ZJS
95 hashmap_free(enumerator->match_sysattr);
96 hashmap_free(enumerator->nomatch_sysattr);
97 hashmap_free(enumerator->match_property);
c73bb513
ZJS
98 set_free(enumerator->match_sysname);
99 set_free(enumerator->match_tag);
100 set_free(enumerator->match_parent);
96df036f 101
8301aa0b 102 return mfree(enumerator);
96df036f
TG
103}
104
8301aa0b
YW
105DEFINE_PUBLIC_TRIVIAL_REF_UNREF_FUNC(sd_device_enumerator, sd_device_enumerator, device_enumerator_free);
106
95a6f969
YW
107int device_enumerator_add_prioritized_subsystem(sd_device_enumerator *enumerator, const char *subsystem) {
108 int r;
109
110 assert(enumerator);
111 assert(subsystem);
112
113 if (strv_contains(enumerator->prioritized_subsystems, subsystem))
114 return 0;
115
116 r = strv_extend(&enumerator->prioritized_subsystems, subsystem);
117 if (r < 0)
118 return r;
119
120 enumerator->scan_uptodate = false;
121
122 return 1;
123}
124
96df036f
TG
125_public_ int sd_device_enumerator_add_match_subsystem(sd_device_enumerator *enumerator, const char *subsystem, int match) {
126 Set **set;
127 int r;
128
129 assert_return(enumerator, -EINVAL);
130 assert_return(subsystem, -EINVAL);
131
132 if (match)
133 set = &enumerator->match_subsystem;
134 else
135 set = &enumerator->nomatch_subsystem;
136
be327321 137 r = set_put_strdup(set, subsystem);
2204f018 138 if (r <= 0)
96df036f
TG
139 return r;
140
141 enumerator->scan_uptodate = false;
142
2204f018 143 return 1;
96df036f
TG
144}
145
eb1c1dc0 146_public_ int sd_device_enumerator_add_match_sysattr(sd_device_enumerator *enumerator, const char *sysattr, const char *value, int match) {
96df036f
TG
147 Hashmap **hashmap;
148 int r;
149
150 assert_return(enumerator, -EINVAL);
eb1c1dc0 151 assert_return(sysattr, -EINVAL);
96df036f
TG
152
153 if (match)
154 hashmap = &enumerator->match_sysattr;
155 else
156 hashmap = &enumerator->nomatch_sysattr;
157
30e2c8c7
YW
158 /* Do not use string_has_ops_free_free or hashmap_put_strdup() here, as this may be called
159 * multiple times with the same sysattr but different value. */
a0887abb 160 r = hashmap_put_strdup_full(hashmap, &trivial_hash_ops_free_free, sysattr, value);
2204f018 161 if (r <= 0)
96df036f
TG
162 return r;
163
96df036f
TG
164 enumerator->scan_uptodate = false;
165
2204f018 166 return 1;
96df036f
TG
167}
168
eb1c1dc0 169_public_ int sd_device_enumerator_add_match_property(sd_device_enumerator *enumerator, const char *property, const char *value) {
96df036f
TG
170 int r;
171
172 assert_return(enumerator, -EINVAL);
eb1c1dc0 173 assert_return(property, -EINVAL);
96df036f 174
30e2c8c7
YW
175 /* Do not use string_has_ops_free_free or hashmap_put_strdup() here, as this may be called
176 * multiple times with the same property but different value. */
a0887abb 177 r = hashmap_put_strdup_full(&enumerator->match_property, &trivial_hash_ops_free_free, property, value);
2204f018 178 if (r <= 0)
96df036f
TG
179 return r;
180
96df036f
TG
181 enumerator->scan_uptodate = false;
182
2204f018 183 return 1;
96df036f
TG
184}
185
186_public_ int sd_device_enumerator_add_match_sysname(sd_device_enumerator *enumerator, const char *sysname) {
187 int r;
188
189 assert_return(enumerator, -EINVAL);
190 assert_return(sysname, -EINVAL);
191
be327321 192 r = set_put_strdup(&enumerator->match_sysname, sysname);
2204f018 193 if (r <= 0)
96df036f
TG
194 return r;
195
196 enumerator->scan_uptodate = false;
197
2204f018 198 return 1;
96df036f
TG
199}
200
201_public_ int sd_device_enumerator_add_match_tag(sd_device_enumerator *enumerator, const char *tag) {
202 int r;
203
204 assert_return(enumerator, -EINVAL);
205 assert_return(tag, -EINVAL);
206
be327321 207 r = set_put_strdup(&enumerator->match_tag, tag);
2204f018 208 if (r <= 0)
96df036f
TG
209 return r;
210
211 enumerator->scan_uptodate = false;
212
2204f018 213 return 1;
96df036f
TG
214}
215
e022bf66
YW
216int device_enumerator_add_match_parent_incremental(sd_device_enumerator *enumerator, sd_device *parent) {
217 const char *path;
218 int r;
219
476a63e9
ZJS
220 assert(enumerator);
221 assert(parent);
96df036f 222
e022bf66
YW
223 r = sd_device_get_syspath(parent, &path);
224 if (r < 0)
225 return r;
226
be327321 227 r = set_put_strdup(&enumerator->match_parent, path);
2204f018 228 if (r <= 0)
e022bf66 229 return r;
96df036f
TG
230
231 enumerator->scan_uptodate = false;
232
2204f018 233 return 1;
96df036f
TG
234}
235
e022bf66 236_public_ int sd_device_enumerator_add_match_parent(sd_device_enumerator *enumerator, sd_device *parent) {
476a63e9
ZJS
237 assert_return(enumerator, -EINVAL);
238 assert_return(parent, -EINVAL);
239
240 set_clear(enumerator->match_parent);
241
e022bf66
YW
242 return device_enumerator_add_match_parent_incremental(enumerator, parent);
243}
244
dee5e0b6 245_public_ int sd_device_enumerator_allow_uninitialized(sd_device_enumerator *enumerator) {
96df036f
TG
246 assert_return(enumerator, -EINVAL);
247
d8b50e5d 248 enumerator->match_initialized = MATCH_INITIALIZED_ALL;
dee5e0b6
TG
249
250 enumerator->scan_uptodate = false;
251
2204f018 252 return 1;
dee5e0b6
TG
253}
254
d8b50e5d 255int device_enumerator_add_match_is_initialized(sd_device_enumerator *enumerator, MatchInitializedType type) {
dee5e0b6 256 assert_return(enumerator, -EINVAL);
d8b50e5d 257 assert_return(type >= 0 && type < _MATCH_INITIALIZED_MAX, -EINVAL);
dee5e0b6 258
d8b50e5d 259 enumerator->match_initialized = type;
96df036f
TG
260
261 enumerator->scan_uptodate = false;
262
2204f018 263 return 1;
96df036f
TG
264}
265
9e871965
YW
266static int sound_device_compare(const char *devpath_a, const char *devpath_b) {
267 const char *sound_a, *sound_b;
268 size_t prefix_len;
269
270 assert(devpath_a);
271 assert(devpath_b);
96df036f 272
9e871965
YW
273 /* For sound cards the control device must be enumerated last to make sure it's the final
274 * device node that gets ACLs applied. Applications rely on this fact and use ACL changes on
275 * the control node as an indicator that the ACL change of the entire sound card completed. The
276 * kernel makes this guarantee when creating those devices, and hence we should too when
277 * enumerating them. */
96df036f
TG
278
279 sound_a = strstr(devpath_a, "/sound/card");
9e871965
YW
280 if (!sound_a)
281 return 0;
282
283 sound_a += STRLEN("/sound/card");
284 sound_a = strchr(devpath_a, '/');
285 if (!sound_a)
286 return 0;
287
288 prefix_len = sound_a - devpath_a;
289
290 if (!strneq(devpath_a, devpath_b, prefix_len))
291 return 0;
292
293 sound_b = devpath_b + prefix_len;
294
295 return CMP(!!startswith(sound_a, "/controlC"),
296 !!startswith(sound_b, "/controlC"));
297}
298
299static bool devpath_is_late_block(const char *devpath) {
300 assert(devpath);
301
302 return strstr(devpath, "/block/md") || strstr(devpath, "/block/dm-");
303}
304
305static int device_compare(sd_device * const *a, sd_device * const *b) {
306 const char *devpath_a, *devpath_b;
307 int r;
308
309 assert(a);
310 assert(b);
311 assert(*a);
312 assert(*b);
313
314 assert_se(sd_device_get_devpath(*(sd_device**) a, &devpath_a) >= 0);
315 assert_se(sd_device_get_devpath(*(sd_device**) b, &devpath_b) >= 0);
316
317 r = sound_device_compare(devpath_a, devpath_b);
318 if (r != 0)
319 return r;
96df036f
TG
320
321 /* md and dm devices are enumerated after all other devices */
9e871965 322 r = CMP(devpath_is_late_block(devpath_a), devpath_is_late_block(devpath_b));
93bab288
YW
323 if (r != 0)
324 return r;
96df036f 325
63aac21c 326 return path_compare(devpath_a, devpath_b);
96df036f
TG
327}
328
87c3a0f9 329static int enumerator_sort_devices(sd_device_enumerator *enumerator) {
95a6f969 330 size_t n_sorted = 0, n = 0;
87c3a0f9
YW
331 sd_device **devices;
332 sd_device *device;
95a6f969 333 int r;
87c3a0f9
YW
334
335 assert(enumerator);
336
337 if (enumerator->sorted)
338 return 0;
339
340 devices = new(sd_device*, hashmap_size(enumerator->devices_by_syspath));
341 if (!devices)
342 return -ENOMEM;
343
95a6f969
YW
344 STRV_FOREACH(prioritized_subsystem, enumerator->prioritized_subsystems) {
345
346 for (;;) {
347 const char *syspath;
348 size_t m = n;
349
350 HASHMAP_FOREACH_KEY(device, syspath, enumerator->devices_by_syspath) {
351 _cleanup_free_ char *p = NULL;
352 const char *subsys;
353
354 if (sd_device_get_subsystem(device, &subsys) < 0)
355 continue;
356
357 if (!streq(subsys, *prioritized_subsystem))
358 continue;
359
360 devices[n++] = sd_device_ref(device);
361
362 for (;;) {
363 _cleanup_free_ char *q = NULL;
364
365 r = path_extract_directory(p ?: syspath, &q);
366 if (r == -EADDRNOTAVAIL)
367 break;
368 if (r < 0)
369 goto failed;
370
371 device = hashmap_get(enumerator->devices_by_syspath, q);
372 if (device)
373 devices[n++] = sd_device_ref(device);
374
375 free_and_replace(p, q);
376 }
377
378 break;
379 }
380
381 /* We cannot remove multiple entries in the loop HASHMAP_FOREACH_KEY() above. */
382 for (size_t i = m; i < n; i++) {
383 r = sd_device_get_syspath(devices[i], &syspath);
384 if (r < 0)
385 goto failed;
386
387 assert_se(hashmap_remove(enumerator->devices_by_syspath, syspath) == devices[i]);
388 sd_device_unref(devices[i]);
389 }
390
391 if (m == n)
392 break;
393 }
394
395 typesafe_qsort(devices + n_sorted, n - n_sorted, device_compare);
396 n_sorted = n;
397 }
398
87c3a0f9
YW
399 HASHMAP_FOREACH(device, enumerator->devices_by_syspath)
400 devices[n++] = sd_device_ref(device);
401
95a6f969
YW
402 /* Move all devices back to the hashmap. Otherwise, devices added by
403 * udev_enumerate_add_syspath() -> device_enumerator_add_device() may not be listed. */
404 for (size_t i = 0; i < n_sorted; i++) {
405 const char *syspath;
406
407 r = sd_device_get_syspath(devices[i], &syspath);
408 if (r < 0)
409 goto failed;
410
411 r = hashmap_put(enumerator->devices_by_syspath, syspath, devices[i]);
412 if (r < 0)
413 goto failed;
414 assert(r > 0);
415
416 sd_device_ref(devices[i]);
417 }
418
419 typesafe_qsort(devices + n_sorted, n - n_sorted, device_compare);
87c3a0f9
YW
420
421 device_unref_many(enumerator->devices, enumerator->n_devices);
422
423 enumerator->n_devices = n;
424 free_and_replace(enumerator->devices, devices);
425
426 enumerator->sorted = true;
427 return 0;
95a6f969
YW
428
429failed:
430 device_unref_many(devices, n);
431 free(devices);
432 return r;
87c3a0f9
YW
433}
434
19c9df44 435int device_enumerator_add_device(sd_device_enumerator *enumerator, sd_device *device) {
87c3a0f9
YW
436 const char *syspath;
437 int r;
438
96df036f
TG
439 assert_return(enumerator, -EINVAL);
440 assert_return(device, -EINVAL);
441
87c3a0f9
YW
442 r = sd_device_get_syspath(device, &syspath);
443 if (r < 0)
444 return r;
96df036f 445
87c3a0f9
YW
446 r = hashmap_ensure_put(&enumerator->devices_by_syspath, &string_hash_ops, syspath, device);
447 if (IN_SET(r, -EEXIST, 0))
448 return 0;
449 if (r < 0)
450 return r;
96df036f 451
87c3a0f9
YW
452 sd_device_ref(device);
453
454 enumerator->sorted = false;
455 return 1;
96df036f
TG
456}
457
96df036f
TG
458static bool match_property(sd_device_enumerator *enumerator, sd_device *device) {
459 const char *property;
460 const char *value;
96df036f
TG
461
462 assert(enumerator);
463 assert(device);
464
465 if (hashmap_isempty(enumerator->match_property))
466 return true;
467
90e74a66 468 HASHMAP_FOREACH_KEY(value, property, enumerator->match_property) {
96df036f
TG
469 const char *property_dev, *value_dev;
470
471 FOREACH_DEVICE_PROPERTY(device, property_dev, value_dev) {
472 if (fnmatch(property, property_dev, 0) != 0)
473 continue;
474
475 if (!value && !value_dev)
476 return true;
477
478 if (!value || !value_dev)
479 continue;
480
481 if (fnmatch(value, value_dev, 0) == 0)
482 return true;
483 }
484 }
485
486 return false;
487}
488
489static bool match_tag(sd_device_enumerator *enumerator, sd_device *device) {
490 const char *tag;
96df036f
TG
491
492 assert(enumerator);
493 assert(device);
494
90e74a66 495 SET_FOREACH(tag, enumerator->match_tag)
96df036f
TG
496 if (!sd_device_has_tag(device, tag))
497 return false;
498
499 return true;
500}
501
96df036f
TG
502static bool match_sysname(sd_device_enumerator *enumerator, const char *sysname) {
503 const char *sysname_match;
96df036f
TG
504
505 assert(enumerator);
506 assert(sysname);
507
508 if (set_isempty(enumerator->match_sysname))
509 return true;
510
90e74a66 511 SET_FOREACH(sysname_match, enumerator->match_sysname)
96df036f
TG
512 if (fnmatch(sysname_match, sysname, 0) == 0)
513 return true;
514
515 return false;
516}
517
d8b50e5d
YW
518static int match_initialized(sd_device_enumerator *enumerator, sd_device *device) {
519 int r;
520
521 assert(enumerator);
522 assert(device);
523
524 if (enumerator->match_initialized == MATCH_INITIALIZED_ALL)
525 return true;
526
527 r = sd_device_get_is_initialized(device);
528 if (r == -ENOENT) /* this is necessarily racey, so ignore missing devices */
529 return false;
530 if (r < 0)
531 return r;
532
533 if (enumerator->match_initialized == MATCH_INITIALIZED_COMPAT) {
534 /* only devices that have no devnode/ifindex or have a db entry are accepted. */
535 if (r > 0)
536 return true;
537
538 if (sd_device_get_devnum(device, NULL) >= 0)
539 return true;
540
541 if (sd_device_get_ifindex(device, NULL) >= 0)
542 return true;
543
544 return false;
545 }
546
547 return (enumerator->match_initialized == MATCH_INITIALIZED_NO) == (r == 0);
548}
549
96df036f
TG
550static int enumerator_scan_dir_and_add_devices(sd_device_enumerator *enumerator, const char *basedir, const char *subdir1, const char *subdir2) {
551 _cleanup_closedir_ DIR *dir = NULL;
552 char *path;
d8b50e5d 553 int k, r = 0;
96df036f
TG
554
555 assert(enumerator);
556 assert(basedir);
557
558 path = strjoina("/sys/", basedir, "/");
559
560 if (subdir1)
561 path = strjoina(path, subdir1, "/");
562
563 if (subdir2)
564 path = strjoina(path, subdir2, "/");
565
566 dir = opendir(path);
567 if (!dir)
cfb6197b
IP
568 /* this is necessarily racey, so ignore missing directories */
569 return (errno == ENOENT && (subdir1 || subdir2)) ? 0 : -errno;
96df036f 570
c7f0d9e5 571 FOREACH_DIRENT_ALL(de, dir, return -errno) {
4afd3348 572 _cleanup_(sd_device_unrefp) sd_device *device = NULL;
c7f0d9e5 573 char syspath[strlen(path) + 1 + strlen(de->d_name) + 1];
96df036f 574
c7f0d9e5 575 if (de->d_name[0] == '.')
96df036f
TG
576 continue;
577
c7f0d9e5 578 if (!match_sysname(enumerator, de->d_name))
96df036f
TG
579 continue;
580
c7f0d9e5 581 (void) sprintf(syspath, "%s%s", path, de->d_name);
96df036f
TG
582
583 k = sd_device_new_from_syspath(&device, syspath);
584 if (k < 0) {
08232a02
TG
585 if (k != -ENODEV)
586 /* this is necessarily racey, so ignore missing devices */
587 r = k;
588
96df036f
TG
589 continue;
590 }
591
d8b50e5d
YW
592 k = match_initialized(enumerator, device);
593 if (k <= 0) {
594 if (k < 0)
595 r = k;
96df036f
TG
596 continue;
597 }
598
bcfe746b 599 if (!device_match_parent(device, enumerator->match_parent, NULL))
96df036f
TG
600 continue;
601
602 if (!match_tag(enumerator, device))
603 continue;
604
605 if (!match_property(enumerator, device))
606 continue;
607
ac790e8b 608 if (!device_match_sysattr(device, enumerator->match_sysattr, enumerator->nomatch_sysattr))
96df036f
TG
609 continue;
610
19c9df44 611 k = device_enumerator_add_device(enumerator, device);
96df036f
TG
612 if (k < 0)
613 r = k;
614 }
615
616 return r;
617}
618
619static bool match_subsystem(sd_device_enumerator *enumerator, const char *subsystem) {
620 const char *subsystem_match;
96df036f
TG
621
622 assert(enumerator);
623
624 if (!subsystem)
625 return false;
626
90e74a66 627 SET_FOREACH(subsystem_match, enumerator->nomatch_subsystem)
96df036f
TG
628 if (fnmatch(subsystem_match, subsystem, 0) == 0)
629 return false;
630
631 if (set_isempty(enumerator->match_subsystem))
632 return true;
633
90e74a66 634 SET_FOREACH(subsystem_match, enumerator->match_subsystem)
96df036f
TG
635 if (fnmatch(subsystem_match, subsystem, 0) == 0)
636 return true;
637
638 return false;
639}
640
641static int enumerator_scan_dir(sd_device_enumerator *enumerator, const char *basedir, const char *subdir, const char *subsystem) {
642 _cleanup_closedir_ DIR *dir = NULL;
643 char *path;
96df036f
TG
644 int r = 0;
645
646 path = strjoina("/sys/", basedir);
647
648 dir = opendir(path);
649 if (!dir)
650 return -errno;
651
c7d54dae 652 log_debug("sd-device-enumerator: Scanning %s", path);
96df036f 653
c7f0d9e5 654 FOREACH_DIRENT_ALL(de, dir, return -errno) {
96df036f
TG
655 int k;
656
c7f0d9e5 657 if (de->d_name[0] == '.')
96df036f
TG
658 continue;
659
c7f0d9e5 660 if (!match_subsystem(enumerator, subsystem ? : de->d_name))
96df036f
TG
661 continue;
662
c7f0d9e5 663 k = enumerator_scan_dir_and_add_devices(enumerator, basedir, de->d_name, subdir);
96df036f
TG
664 if (k < 0)
665 r = k;
666 }
667
668 return r;
669}
670
671static int enumerator_scan_devices_tag(sd_device_enumerator *enumerator, const char *tag) {
672 _cleanup_closedir_ DIR *dir = NULL;
673 char *path;
96df036f
TG
674 int r = 0;
675
676 assert(enumerator);
677 assert(tag);
678
679 path = strjoina("/run/udev/tags/", tag);
680
681 dir = opendir(path);
682 if (!dir) {
c7d54dae
YW
683 if (errno != ENOENT)
684 return log_debug_errno(errno, "sd-device-enumerator: Failed to open tags directory %s: %m", path);
685 return 0;
96df036f
TG
686 }
687
688 /* TODO: filter away subsystems? */
689
c7f0d9e5 690 FOREACH_DIRENT_ALL(de, dir, return -errno) {
4afd3348 691 _cleanup_(sd_device_unrefp) sd_device *device = NULL;
96df036f
TG
692 const char *subsystem, *sysname;
693 int k;
694
c7f0d9e5 695 if (de->d_name[0] == '.')
96df036f
TG
696 continue;
697
c7f0d9e5 698 k = sd_device_new_from_device_id(&device, de->d_name);
96df036f 699 if (k < 0) {
08232a02
TG
700 if (k != -ENODEV)
701 /* this is necessarily racy, so ignore missing devices */
702 r = k;
703
96df036f
TG
704 continue;
705 }
706
707 k = sd_device_get_subsystem(device, &subsystem);
708 if (k < 0) {
3126d64e
YW
709 if (k != -ENOENT)
710 /* this is necessarily racy, so ignore missing devices */
711 r = k;
96df036f
TG
712 continue;
713 }
714
715 if (!match_subsystem(enumerator, subsystem))
716 continue;
717
718 k = sd_device_get_sysname(device, &sysname);
719 if (k < 0) {
720 r = k;
721 continue;
722 }
723
724 if (!match_sysname(enumerator, sysname))
725 continue;
726
bcfe746b 727 if (!device_match_parent(device, enumerator->match_parent, NULL))
96df036f
TG
728 continue;
729
730 if (!match_property(enumerator, device))
731 continue;
732
ac790e8b 733 if (!device_match_sysattr(device, enumerator->match_sysattr, enumerator->nomatch_sysattr))
96df036f
TG
734 continue;
735
19c9df44 736 k = device_enumerator_add_device(enumerator, device);
96df036f
TG
737 if (k < 0) {
738 r = k;
739 continue;
740 }
741 }
742
743 return r;
744}
745
746static int enumerator_scan_devices_tags(sd_device_enumerator *enumerator) {
747 const char *tag;
3172836b 748 int r = 0;
96df036f
TG
749
750 assert(enumerator);
751
90e74a66 752 SET_FOREACH(tag, enumerator->match_tag) {
3172836b
TG
753 int k;
754
755 k = enumerator_scan_devices_tag(enumerator, tag);
756 if (k < 0)
757 r = k;
96df036f
TG
758 }
759
3172836b 760 return r;
96df036f
TG
761}
762
763static int parent_add_child(sd_device_enumerator *enumerator, const char *path) {
4afd3348 764 _cleanup_(sd_device_unrefp) sd_device *device = NULL;
96df036f
TG
765 const char *subsystem, *sysname;
766 int r;
767
768 r = sd_device_new_from_syspath(&device, path);
08232a02
TG
769 if (r == -ENODEV)
770 /* this is necessarily racy, so ignore missing devices */
96df036f
TG
771 return 0;
772 else if (r < 0)
773 return r;
774
775 r = sd_device_get_subsystem(device, &subsystem);
9a9c7dc2
MM
776 if (r == -ENOENT)
777 return 0;
96df036f
TG
778 if (r < 0)
779 return r;
780
781 if (!match_subsystem(enumerator, subsystem))
782 return 0;
783
784 r = sd_device_get_sysname(device, &sysname);
785 if (r < 0)
786 return r;
787
788 if (!match_sysname(enumerator, sysname))
789 return 0;
790
791 if (!match_property(enumerator, device))
792 return 0;
793
ac790e8b 794 if (!device_match_sysattr(device, enumerator->match_sysattr, enumerator->nomatch_sysattr))
96df036f
TG
795 return 0;
796
19c9df44 797 r = device_enumerator_add_device(enumerator, device);
96df036f
TG
798 if (r < 0)
799 return r;
800
801 return 1;
802}
803
804static int parent_crawl_children(sd_device_enumerator *enumerator, const char *path, unsigned maxdepth) {
805 _cleanup_closedir_ DIR *dir = NULL;
96df036f
TG
806 int r = 0;
807
808 dir = opendir(path);
25f027c5 809 if (!dir)
c7d54dae 810 return log_debug_errno(errno, "sd-device-enumerator: Failed to open parent directory %s: %m", path);
96df036f 811
c7f0d9e5 812 FOREACH_DIRENT_ALL(de, dir, return -errno) {
96df036f
TG
813 _cleanup_free_ char *child = NULL;
814 int k;
815
c7f0d9e5 816 if (de->d_name[0] == '.')
96df036f
TG
817 continue;
818
c7f0d9e5 819 if (de->d_type != DT_DIR)
96df036f
TG
820 continue;
821
c7f0d9e5 822 child = path_join(path, de->d_name);
53fae771
ZJS
823 if (!child)
824 return -ENOMEM;
96df036f
TG
825
826 k = parent_add_child(enumerator, child);
827 if (k < 0)
828 r = k;
829
830 if (maxdepth > 0)
831 parent_crawl_children(enumerator, child, maxdepth - 1);
832 else
c7d54dae 833 log_debug("sd-device-enumerator: Max depth reached, %s: ignoring devices", child);
96df036f
TG
834 }
835
836 return r;
837}
838
839static int enumerator_scan_devices_children(sd_device_enumerator *enumerator) {
840 const char *path;
841 int r = 0, k;
842
90e74a66 843 SET_FOREACH(path, enumerator->match_parent) {
e022bf66
YW
844 k = parent_add_child(enumerator, path);
845 if (k < 0)
846 r = k;
96df036f 847
e022bf66
YW
848 k = parent_crawl_children(enumerator, path, DEVICE_ENUMERATE_MAX_DEPTH);
849 if (k < 0)
850 r = k;
851 }
96df036f
TG
852
853 return r;
854}
855
856static int enumerator_scan_devices_all(sd_device_enumerator *enumerator) {
37cf83d9 857 int k, r = 0;
96df036f 858
c7d54dae 859 log_debug("sd-device-enumerator: Scan all dirs");
96df036f 860
37cf83d9
YW
861 k = enumerator_scan_dir(enumerator, "bus", "devices", NULL);
862 if (k < 0)
863 r = log_debug_errno(k, "sd-device-enumerator: Failed to scan /sys/bus: %m");
96df036f 864
37cf83d9
YW
865 k = enumerator_scan_dir(enumerator, "class", NULL, NULL);
866 if (k < 0)
867 r = log_debug_errno(k, "sd-device-enumerator: Failed to scan /sys/class: %m");
96df036f
TG
868
869 return r;
870}
871
872int device_enumerator_scan_devices(sd_device_enumerator *enumerator) {
3172836b 873 int r = 0, k;
96df036f
TG
874
875 assert(enumerator);
876
877 if (enumerator->scan_uptodate &&
878 enumerator->type == DEVICE_ENUMERATION_TYPE_DEVICES)
879 return 0;
880
deac0c9c 881 device_enumerator_unref_devices(enumerator);
96df036f
TG
882
883 if (!set_isempty(enumerator->match_tag)) {
3172836b
TG
884 k = enumerator_scan_devices_tags(enumerator);
885 if (k < 0)
886 r = k;
96df036f 887 } else if (enumerator->match_parent) {
3172836b
TG
888 k = enumerator_scan_devices_children(enumerator);
889 if (k < 0)
890 r = k;
96df036f 891 } else {
3172836b
TG
892 k = enumerator_scan_devices_all(enumerator);
893 if (k < 0)
894 r = k;
96df036f
TG
895 }
896
897 enumerator->scan_uptodate = true;
0a166589 898 enumerator->type = DEVICE_ENUMERATION_TYPE_DEVICES;
96df036f 899
3172836b 900 return r;
96df036f
TG
901}
902
903_public_ sd_device *sd_device_enumerator_get_device_first(sd_device_enumerator *enumerator) {
96df036f
TG
904 assert_return(enumerator, NULL);
905
87c3a0f9
YW
906 if (device_enumerator_scan_devices(enumerator) < 0)
907 return NULL;
908
909 if (enumerator_sort_devices(enumerator) < 0)
96df036f
TG
910 return NULL;
911
0a166589
YW
912 enumerator->current_device_index = 0;
913
914 if (enumerator->n_devices == 0)
915 return NULL;
96df036f 916
0a166589 917 return enumerator->devices[0];
96df036f
TG
918}
919
920_public_ sd_device *sd_device_enumerator_get_device_next(sd_device_enumerator *enumerator) {
921 assert_return(enumerator, NULL);
922
923 if (!enumerator->scan_uptodate ||
87c3a0f9 924 !enumerator->sorted ||
0a166589
YW
925 enumerator->type != DEVICE_ENUMERATION_TYPE_DEVICES ||
926 enumerator->current_device_index + 1 >= enumerator->n_devices)
96df036f
TG
927 return NULL;
928
0a166589 929 return enumerator->devices[++enumerator->current_device_index];
96df036f
TG
930}
931
932int device_enumerator_scan_subsystems(sd_device_enumerator *enumerator) {
96df036f
TG
933 int r = 0, k;
934
935 assert(enumerator);
936
937 if (enumerator->scan_uptodate &&
938 enumerator->type == DEVICE_ENUMERATION_TYPE_SUBSYSTEMS)
939 return 0;
940
deac0c9c 941 device_enumerator_unref_devices(enumerator);
96df036f
TG
942
943 /* modules */
944 if (match_subsystem(enumerator, "module")) {
945 k = enumerator_scan_dir_and_add_devices(enumerator, "module", NULL, NULL);
fed66db0
YW
946 if (k < 0)
947 r = log_debug_errno(k, "sd-device-enumerator: Failed to scan modules: %m");
96df036f
TG
948 }
949
96df036f
TG
950 /* subsystems (only buses support coldplug) */
951 if (match_subsystem(enumerator, "subsystem")) {
37cf83d9 952 k = enumerator_scan_dir_and_add_devices(enumerator, "bus", NULL, NULL);
fed66db0
YW
953 if (k < 0)
954 r = log_debug_errno(k, "sd-device-enumerator: Failed to scan subsystems: %m");
96df036f
TG
955 }
956
957 /* subsystem drivers */
958 if (match_subsystem(enumerator, "drivers")) {
37cf83d9 959 k = enumerator_scan_dir(enumerator, "bus", "drivers", "drivers");
fed66db0
YW
960 if (k < 0)
961 r = log_debug_errno(k, "sd-device-enumerator: Failed to scan drivers: %m");
96df036f
TG
962 }
963
964 enumerator->scan_uptodate = true;
0a166589 965 enumerator->type = DEVICE_ENUMERATION_TYPE_SUBSYSTEMS;
96df036f
TG
966
967 return r;
968}
969
970_public_ sd_device *sd_device_enumerator_get_subsystem_first(sd_device_enumerator *enumerator) {
96df036f
TG
971 assert_return(enumerator, NULL);
972
87c3a0f9
YW
973 if (device_enumerator_scan_subsystems(enumerator) < 0)
974 return NULL;
975
976 if (enumerator_sort_devices(enumerator) < 0)
96df036f
TG
977 return NULL;
978
0a166589
YW
979 enumerator->current_device_index = 0;
980
981 if (enumerator->n_devices == 0)
982 return NULL;
96df036f 983
0a166589 984 return enumerator->devices[0];
96df036f
TG
985}
986
987_public_ sd_device *sd_device_enumerator_get_subsystem_next(sd_device_enumerator *enumerator) {
988 assert_return(enumerator, NULL);
989
4005d321 990 if (!enumerator->scan_uptodate ||
87c3a0f9 991 !enumerator->sorted ||
0a166589
YW
992 enumerator->type != DEVICE_ENUMERATION_TYPE_SUBSYSTEMS ||
993 enumerator->current_device_index + 1 >= enumerator->n_devices)
96df036f
TG
994 return NULL;
995
0a166589 996 return enumerator->devices[++enumerator->current_device_index];
96df036f
TG
997}
998
3c5cc23a
YW
999int device_enumerator_scan_devices_and_subsystems(sd_device_enumerator *enumerator) {
1000 int r = 0, k;
1001
1002 assert(enumerator);
1003
1004 if (enumerator->scan_uptodate &&
1005 enumerator->type == DEVICE_ENUMERATION_TYPE_ALL)
1006 return 0;
1007
1008 device_enumerator_unref_devices(enumerator);
1009
1010 if (!set_isempty(enumerator->match_tag)) {
1011 k = enumerator_scan_devices_tags(enumerator);
1012 if (k < 0)
1013 r = k;
1014 } else if (enumerator->match_parent) {
1015 k = enumerator_scan_devices_children(enumerator);
1016 if (k < 0)
1017 r = k;
1018 } else {
1019 k = enumerator_scan_dir(enumerator, "class", NULL, NULL);
1020 if (k < 0)
1021 r = log_debug_errno(k, "sd-device-enumerator: Failed to scan /sys/class: %m");
1022
1023 k = enumerator_scan_dir(enumerator, "bus", "devices", NULL);
1024 if (k < 0)
1025 r = log_debug_errno(k, "sd-device-enumerator: Failed to scan /sys/bus: %m");
1026
1027 if (match_subsystem(enumerator, "module")) {
1028 k = enumerator_scan_dir_and_add_devices(enumerator, "module", NULL, NULL);
1029 if (k < 0)
1030 r = log_debug_errno(k, "sd-device-enumerator: Failed to scan modules: %m");
1031 }
1032 if (match_subsystem(enumerator, "subsystem")) {
1033 k = enumerator_scan_dir_and_add_devices(enumerator, "bus", NULL, NULL);
1034 if (k < 0)
1035 r = log_debug_errno(k, "sd-device-enumerator: Failed to scan subsystems: %m");
1036 }
1037
1038 if (match_subsystem(enumerator, "drivers")) {
1039 k = enumerator_scan_dir(enumerator, "bus", "drivers", "drivers");
1040 if (k < 0)
1041 r = log_debug_errno(k, "sd-device-enumerator: Failed to scan drivers: %m");
1042 }
1043 }
1044
1045 enumerator->scan_uptodate = true;
1046 enumerator->type = DEVICE_ENUMERATION_TYPE_ALL;
1047
1048 return r;
1049}
1050
96df036f
TG
1051sd_device *device_enumerator_get_first(sd_device_enumerator *enumerator) {
1052 assert_return(enumerator, NULL);
1053
0a166589
YW
1054 if (!enumerator->scan_uptodate)
1055 return NULL;
1056
87c3a0f9
YW
1057 if (enumerator_sort_devices(enumerator) < 0)
1058 return NULL;
1059
0a166589
YW
1060 enumerator->current_device_index = 0;
1061
1062 if (enumerator->n_devices == 0)
1063 return NULL;
1064
1065 return enumerator->devices[0];
96df036f
TG
1066}
1067
1068sd_device *device_enumerator_get_next(sd_device_enumerator *enumerator) {
1069 assert_return(enumerator, NULL);
1070
0a166589 1071 if (!enumerator->scan_uptodate ||
87c3a0f9 1072 !enumerator->sorted ||
0a166589
YW
1073 enumerator->current_device_index + 1 >= enumerator->n_devices)
1074 return NULL;
96df036f 1075
0a166589 1076 return enumerator->devices[++enumerator->current_device_index];
96df036f 1077}
708474c5
YW
1078
1079sd_device **device_enumerator_get_devices(sd_device_enumerator *enumerator, size_t *ret_n_devices) {
1080 assert(enumerator);
1081 assert(ret_n_devices);
1082
1083 if (!enumerator->scan_uptodate)
1084 return NULL;
1085
87c3a0f9
YW
1086 if (enumerator_sort_devices(enumerator) < 0)
1087 return NULL;
1088
708474c5
YW
1089 *ret_n_devices = enumerator->n_devices;
1090 return enumerator->devices;
1091}