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