]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/libsystemd/sd-device/device-enumerator.c
tree-wide: define iterator inside of the macro
[thirdparty/systemd.git] / src / libsystemd / sd-device / device-enumerator.c
CommitLineData
53e1b683 1/* SPDX-License-Identifier: LGPL-2.1+ */
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,
23 _DEVICE_ENUMERATION_TYPE_MAX,
24 _DEVICE_ENUMERATION_TYPE_INVALID = -1,
25} DeviceEnumerationType;
26
27struct sd_device_enumerator {
28 unsigned n_ref;
29
30 DeviceEnumerationType type;
0a166589
YW
31 sd_device **devices;
32 size_t n_devices, n_allocated, current_device_index;
96df036f
TG
33 bool scan_uptodate;
34
35 Set *match_subsystem;
36 Set *nomatch_subsystem;
37 Hashmap *match_sysattr;
38 Hashmap *nomatch_sysattr;
39 Hashmap *match_property;
40 Set *match_sysname;
41 Set *match_tag;
e022bf66 42 Set *match_parent;
dee5e0b6 43 bool match_allow_uninitialized;
96df036f
TG
44};
45
46_public_ int sd_device_enumerator_new(sd_device_enumerator **ret) {
4afd3348 47 _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *enumerator = NULL;
96df036f
TG
48
49 assert(ret);
50
6116d2b2 51 enumerator = new(sd_device_enumerator, 1);
96df036f
TG
52 if (!enumerator)
53 return -ENOMEM;
54
6116d2b2
YW
55 *enumerator = (sd_device_enumerator) {
56 .n_ref = 1,
57 .type = _DEVICE_ENUMERATION_TYPE_INVALID,
58 };
96df036f 59
1cc6c93a 60 *ret = TAKE_PTR(enumerator);
96df036f
TG
61
62 return 0;
63}
64
8301aa0b 65static sd_device_enumerator *device_enumerator_free(sd_device_enumerator *enumerator) {
0a166589 66 size_t i;
96df036f 67
8301aa0b 68 assert(enumerator);
96df036f 69
0a166589
YW
70 for (i = 0; i < enumerator->n_devices; i++)
71 sd_device_unref(enumerator->devices[i]);
96df036f 72
0a166589 73 free(enumerator->devices);
c73bb513
ZJS
74 set_free(enumerator->match_subsystem);
75 set_free(enumerator->nomatch_subsystem);
eb1c1dc0
ZJS
76 hashmap_free(enumerator->match_sysattr);
77 hashmap_free(enumerator->nomatch_sysattr);
78 hashmap_free(enumerator->match_property);
c73bb513
ZJS
79 set_free(enumerator->match_sysname);
80 set_free(enumerator->match_tag);
81 set_free(enumerator->match_parent);
96df036f 82
8301aa0b 83 return mfree(enumerator);
96df036f
TG
84}
85
8301aa0b
YW
86DEFINE_PUBLIC_TRIVIAL_REF_UNREF_FUNC(sd_device_enumerator, sd_device_enumerator, device_enumerator_free);
87
96df036f
TG
88_public_ int sd_device_enumerator_add_match_subsystem(sd_device_enumerator *enumerator, const char *subsystem, int match) {
89 Set **set;
90 int r;
91
92 assert_return(enumerator, -EINVAL);
93 assert_return(subsystem, -EINVAL);
94
95 if (match)
96 set = &enumerator->match_subsystem;
97 else
98 set = &enumerator->nomatch_subsystem;
99
be327321 100 r = set_put_strdup(set, subsystem);
2204f018 101 if (r <= 0)
96df036f
TG
102 return r;
103
104 enumerator->scan_uptodate = false;
105
2204f018 106 return 1;
96df036f
TG
107}
108
eb1c1dc0 109_public_ int sd_device_enumerator_add_match_sysattr(sd_device_enumerator *enumerator, const char *sysattr, const char *value, int match) {
96df036f
TG
110 Hashmap **hashmap;
111 int r;
112
113 assert_return(enumerator, -EINVAL);
eb1c1dc0 114 assert_return(sysattr, -EINVAL);
96df036f
TG
115
116 if (match)
117 hashmap = &enumerator->match_sysattr;
118 else
119 hashmap = &enumerator->nomatch_sysattr;
120
eb1c1dc0 121 r = hashmap_put_strdup(hashmap, sysattr, value);
2204f018 122 if (r <= 0)
96df036f
TG
123 return r;
124
96df036f
TG
125 enumerator->scan_uptodate = false;
126
2204f018 127 return 1;
96df036f
TG
128}
129
eb1c1dc0 130_public_ int sd_device_enumerator_add_match_property(sd_device_enumerator *enumerator, const char *property, const char *value) {
96df036f
TG
131 int r;
132
133 assert_return(enumerator, -EINVAL);
eb1c1dc0 134 assert_return(property, -EINVAL);
96df036f 135
eb1c1dc0 136 r = hashmap_put_strdup(&enumerator->match_property, property, value);
2204f018 137 if (r <= 0)
96df036f
TG
138 return r;
139
96df036f
TG
140 enumerator->scan_uptodate = false;
141
2204f018 142 return 1;
96df036f
TG
143}
144
145_public_ int sd_device_enumerator_add_match_sysname(sd_device_enumerator *enumerator, const char *sysname) {
146 int r;
147
148 assert_return(enumerator, -EINVAL);
149 assert_return(sysname, -EINVAL);
150
be327321 151 r = set_put_strdup(&enumerator->match_sysname, sysname);
2204f018 152 if (r <= 0)
96df036f
TG
153 return r;
154
155 enumerator->scan_uptodate = false;
156
2204f018 157 return 1;
96df036f
TG
158}
159
160_public_ int sd_device_enumerator_add_match_tag(sd_device_enumerator *enumerator, const char *tag) {
161 int r;
162
163 assert_return(enumerator, -EINVAL);
164 assert_return(tag, -EINVAL);
165
be327321 166 r = set_put_strdup(&enumerator->match_tag, tag);
2204f018 167 if (r <= 0)
96df036f
TG
168 return r;
169
170 enumerator->scan_uptodate = false;
171
2204f018 172 return 1;
96df036f
TG
173}
174
e022bf66
YW
175int device_enumerator_add_match_parent_incremental(sd_device_enumerator *enumerator, sd_device *parent) {
176 const char *path;
177 int r;
178
476a63e9
ZJS
179 assert(enumerator);
180 assert(parent);
96df036f 181
e022bf66
YW
182 r = sd_device_get_syspath(parent, &path);
183 if (r < 0)
184 return r;
185
be327321 186 r = set_put_strdup(&enumerator->match_parent, path);
2204f018 187 if (r <= 0)
e022bf66 188 return r;
96df036f
TG
189
190 enumerator->scan_uptodate = false;
191
2204f018 192 return 1;
96df036f
TG
193}
194
e022bf66 195_public_ int sd_device_enumerator_add_match_parent(sd_device_enumerator *enumerator, sd_device *parent) {
476a63e9
ZJS
196 assert_return(enumerator, -EINVAL);
197 assert_return(parent, -EINVAL);
198
199 set_clear(enumerator->match_parent);
200
e022bf66
YW
201 return device_enumerator_add_match_parent_incremental(enumerator, parent);
202}
203
dee5e0b6 204_public_ int sd_device_enumerator_allow_uninitialized(sd_device_enumerator *enumerator) {
96df036f
TG
205 assert_return(enumerator, -EINVAL);
206
dee5e0b6
TG
207 enumerator->match_allow_uninitialized = true;
208
209 enumerator->scan_uptodate = false;
210
2204f018 211 return 1;
dee5e0b6
TG
212}
213
214int device_enumerator_add_match_is_initialized(sd_device_enumerator *enumerator) {
215 assert_return(enumerator, -EINVAL);
216
217 enumerator->match_allow_uninitialized = false;
96df036f
TG
218
219 enumerator->scan_uptodate = false;
220
2204f018 221 return 1;
96df036f
TG
222}
223
93bab288 224static int device_compare(sd_device * const *_a, sd_device * const *_b) {
0a166589 225 sd_device *a = *(sd_device **)_a, *b = *(sd_device **)_b;
96df036f 226 const char *devpath_a, *devpath_b, *sound_a;
18ae3d98 227 bool delay_a, delay_b;
93bab288 228 int r;
96df036f
TG
229
230 assert_se(sd_device_get_devpath(a, &devpath_a) >= 0);
231 assert_se(sd_device_get_devpath(b, &devpath_b) >= 0);
232
233 sound_a = strstr(devpath_a, "/sound/card");
234 if (sound_a) {
235 /* For sound cards the control device must be enumerated last to
236 * make sure it's the final device node that gets ACLs applied.
237 * Applications rely on this fact and use ACL changes on the
238 * control node as an indicator that the ACL change of the
239 * entire sound card completed. The kernel makes this guarantee
240 * when creating those devices, and hence we should too when
241 * enumerating them. */
fbd0b64f 242 sound_a += STRLEN("/sound/card");
96df036f
TG
243 sound_a = strchr(sound_a, '/');
244
245 if (sound_a) {
246 unsigned prefix_len;
247
248 prefix_len = sound_a - devpath_a;
249
250 if (strncmp(devpath_a, devpath_b, prefix_len) == 0) {
251 const char *sound_b;
252
253 sound_b = devpath_b + prefix_len;
254
255 if (startswith(sound_a, "/controlC") &&
256 !startswith(sound_b, "/contolC"))
257 return 1;
258
259 if (!startswith(sound_a, "/controlC") &&
260 startswith(sound_b, "/controlC"))
261 return -1;
262 }
263 }
264 }
265
266 /* md and dm devices are enumerated after all other devices */
18ae3d98
DH
267 delay_a = strstr(devpath_a, "/block/md") || strstr(devpath_a, "/block/dm-");
268 delay_b = strstr(devpath_b, "/block/md") || strstr(devpath_b, "/block/dm-");
93bab288
YW
269 r = CMP(delay_a, delay_b);
270 if (r != 0)
271 return r;
96df036f
TG
272
273 return strcmp(devpath_a, devpath_b);
274}
275
19c9df44 276int device_enumerator_add_device(sd_device_enumerator *enumerator, sd_device *device) {
96df036f
TG
277 assert_return(enumerator, -EINVAL);
278 assert_return(device, -EINVAL);
279
0a166589
YW
280 if (!GREEDY_REALLOC(enumerator->devices, enumerator->n_allocated, enumerator->n_devices + 1))
281 return -ENOMEM;
96df036f 282
0a166589 283 enumerator->devices[enumerator->n_devices++] = sd_device_ref(device);
96df036f
TG
284
285 return 0;
286}
287
288static bool match_sysattr_value(sd_device *device, const char *sysattr, const char *match_value) {
289 const char *value;
290 int r;
291
292 assert(device);
293 assert(sysattr);
294
295 r = sd_device_get_sysattr_value(device, sysattr, &value);
296 if (r < 0)
297 return false;
298
299 if (!match_value)
300 return true;
301
302 if (fnmatch(match_value, value, 0) == 0)
303 return true;
304
305 return false;
306}
307
308static bool match_sysattr(sd_device_enumerator *enumerator, sd_device *device) {
309 const char *sysattr;
310 const char *value;
96df036f
TG
311
312 assert(enumerator);
313 assert(device);
314
90e74a66 315 HASHMAP_FOREACH_KEY(value, sysattr, enumerator->nomatch_sysattr)
96df036f
TG
316 if (match_sysattr_value(device, sysattr, value))
317 return false;
318
90e74a66 319 HASHMAP_FOREACH_KEY(value, sysattr, enumerator->match_sysattr)
96df036f
TG
320 if (!match_sysattr_value(device, sysattr, value))
321 return false;
322
323 return true;
324}
325
326static bool match_property(sd_device_enumerator *enumerator, sd_device *device) {
327 const char *property;
328 const char *value;
96df036f
TG
329
330 assert(enumerator);
331 assert(device);
332
333 if (hashmap_isempty(enumerator->match_property))
334 return true;
335
90e74a66 336 HASHMAP_FOREACH_KEY(value, property, enumerator->match_property) {
96df036f
TG
337 const char *property_dev, *value_dev;
338
339 FOREACH_DEVICE_PROPERTY(device, property_dev, value_dev) {
340 if (fnmatch(property, property_dev, 0) != 0)
341 continue;
342
343 if (!value && !value_dev)
344 return true;
345
346 if (!value || !value_dev)
347 continue;
348
349 if (fnmatch(value, value_dev, 0) == 0)
350 return true;
351 }
352 }
353
354 return false;
355}
356
357static bool match_tag(sd_device_enumerator *enumerator, sd_device *device) {
358 const char *tag;
96df036f
TG
359
360 assert(enumerator);
361 assert(device);
362
90e74a66 363 SET_FOREACH(tag, enumerator->match_tag)
96df036f
TG
364 if (!sd_device_has_tag(device, tag))
365 return false;
366
367 return true;
368}
369
370static bool match_parent(sd_device_enumerator *enumerator, sd_device *device) {
e022bf66 371 const char *syspath_parent, *syspath;
96df036f
TG
372
373 assert(enumerator);
374 assert(device);
375
e022bf66 376 if (set_isempty(enumerator->match_parent))
96df036f
TG
377 return true;
378
10c353e1 379 assert_se(sd_device_get_syspath(device, &syspath) >= 0);
96df036f 380
90e74a66 381 SET_FOREACH(syspath_parent, enumerator->match_parent)
e022bf66
YW
382 if (path_startswith(syspath, syspath_parent))
383 return true;
96df036f 384
e022bf66 385 return false;
96df036f
TG
386}
387
388static bool match_sysname(sd_device_enumerator *enumerator, const char *sysname) {
389 const char *sysname_match;
96df036f
TG
390
391 assert(enumerator);
392 assert(sysname);
393
394 if (set_isempty(enumerator->match_sysname))
395 return true;
396
90e74a66 397 SET_FOREACH(sysname_match, enumerator->match_sysname)
96df036f
TG
398 if (fnmatch(sysname_match, sysname, 0) == 0)
399 return true;
400
401 return false;
402}
403
404static int enumerator_scan_dir_and_add_devices(sd_device_enumerator *enumerator, const char *basedir, const char *subdir1, const char *subdir2) {
405 _cleanup_closedir_ DIR *dir = NULL;
406 char *path;
407 struct dirent *dent;
408 int r = 0;
409
410 assert(enumerator);
411 assert(basedir);
412
413 path = strjoina("/sys/", basedir, "/");
414
415 if (subdir1)
416 path = strjoina(path, subdir1, "/");
417
418 if (subdir2)
419 path = strjoina(path, subdir2, "/");
420
421 dir = opendir(path);
422 if (!dir)
423 return -errno;
424
425 FOREACH_DIRENT_ALL(dent, dir, return -errno) {
4afd3348 426 _cleanup_(sd_device_unrefp) sd_device *device = NULL;
96df036f 427 char syspath[strlen(path) + 1 + strlen(dent->d_name) + 1];
5a937ea2 428 int initialized, k;
96df036f
TG
429
430 if (dent->d_name[0] == '.')
431 continue;
432
433 if (!match_sysname(enumerator, dent->d_name))
434 continue;
435
db2f8a2e 436 (void) sprintf(syspath, "%s%s", path, dent->d_name);
96df036f
TG
437
438 k = sd_device_new_from_syspath(&device, syspath);
439 if (k < 0) {
08232a02
TG
440 if (k != -ENODEV)
441 /* this is necessarily racey, so ignore missing devices */
442 r = k;
443
96df036f
TG
444 continue;
445 }
446
5a937ea2
YW
447 initialized = sd_device_get_is_initialized(device);
448 if (initialized < 0) {
3126d64e
YW
449 if (initialized != -ENOENT)
450 /* this is necessarily racey, so ignore missing devices */
451 r = initialized;
452
96df036f
TG
453 continue;
454 }
455
456 /*
457 * All devices with a device node or network interfaces
458 * possibly need udev to adjust the device node permission
459 * or context, or rename the interface before it can be
460 * reliably used from other processes.
461 *
462 * For now, we can only check these types of devices, we
463 * might not store a database, and have no way to find out
464 * for all other types of devices.
465 */
dee5e0b6 466 if (!enumerator->match_allow_uninitialized &&
96df036f 467 !initialized &&
5a937ea2
YW
468 (sd_device_get_devnum(device, NULL) >= 0 ||
469 sd_device_get_ifindex(device, NULL) >= 0))
96df036f
TG
470 continue;
471
472 if (!match_parent(enumerator, device))
473 continue;
474
475 if (!match_tag(enumerator, device))
476 continue;
477
478 if (!match_property(enumerator, device))
479 continue;
480
481 if (!match_sysattr(enumerator, device))
482 continue;
483
19c9df44 484 k = device_enumerator_add_device(enumerator, device);
96df036f
TG
485 if (k < 0)
486 r = k;
487 }
488
489 return r;
490}
491
492static bool match_subsystem(sd_device_enumerator *enumerator, const char *subsystem) {
493 const char *subsystem_match;
96df036f
TG
494
495 assert(enumerator);
496
497 if (!subsystem)
498 return false;
499
90e74a66 500 SET_FOREACH(subsystem_match, enumerator->nomatch_subsystem)
96df036f
TG
501 if (fnmatch(subsystem_match, subsystem, 0) == 0)
502 return false;
503
504 if (set_isempty(enumerator->match_subsystem))
505 return true;
506
90e74a66 507 SET_FOREACH(subsystem_match, enumerator->match_subsystem)
96df036f
TG
508 if (fnmatch(subsystem_match, subsystem, 0) == 0)
509 return true;
510
511 return false;
512}
513
514static int enumerator_scan_dir(sd_device_enumerator *enumerator, const char *basedir, const char *subdir, const char *subsystem) {
515 _cleanup_closedir_ DIR *dir = NULL;
516 char *path;
517 struct dirent *dent;
518 int r = 0;
519
520 path = strjoina("/sys/", basedir);
521
522 dir = opendir(path);
523 if (!dir)
524 return -errno;
525
c7d54dae 526 log_debug("sd-device-enumerator: Scanning %s", path);
96df036f
TG
527
528 FOREACH_DIRENT_ALL(dent, dir, return -errno) {
529 int k;
530
531 if (dent->d_name[0] == '.')
532 continue;
533
534 if (!match_subsystem(enumerator, subsystem ? : dent->d_name))
535 continue;
536
537 k = enumerator_scan_dir_and_add_devices(enumerator, basedir, dent->d_name, subdir);
538 if (k < 0)
539 r = k;
540 }
541
542 return r;
543}
544
545static int enumerator_scan_devices_tag(sd_device_enumerator *enumerator, const char *tag) {
546 _cleanup_closedir_ DIR *dir = NULL;
547 char *path;
548 struct dirent *dent;
549 int r = 0;
550
551 assert(enumerator);
552 assert(tag);
553
554 path = strjoina("/run/udev/tags/", tag);
555
556 dir = opendir(path);
557 if (!dir) {
c7d54dae
YW
558 if (errno != ENOENT)
559 return log_debug_errno(errno, "sd-device-enumerator: Failed to open tags directory %s: %m", path);
560 return 0;
96df036f
TG
561 }
562
563 /* TODO: filter away subsystems? */
564
565 FOREACH_DIRENT_ALL(dent, dir, return -errno) {
4afd3348 566 _cleanup_(sd_device_unrefp) sd_device *device = NULL;
96df036f
TG
567 const char *subsystem, *sysname;
568 int k;
569
570 if (dent->d_name[0] == '.')
571 continue;
572
573 k = sd_device_new_from_device_id(&device, dent->d_name);
574 if (k < 0) {
08232a02
TG
575 if (k != -ENODEV)
576 /* this is necessarily racy, so ignore missing devices */
577 r = k;
578
96df036f
TG
579 continue;
580 }
581
582 k = sd_device_get_subsystem(device, &subsystem);
583 if (k < 0) {
3126d64e
YW
584 if (k != -ENOENT)
585 /* this is necessarily racy, so ignore missing devices */
586 r = k;
96df036f
TG
587 continue;
588 }
589
590 if (!match_subsystem(enumerator, subsystem))
591 continue;
592
593 k = sd_device_get_sysname(device, &sysname);
594 if (k < 0) {
595 r = k;
596 continue;
597 }
598
599 if (!match_sysname(enumerator, sysname))
600 continue;
601
602 if (!match_parent(enumerator, device))
603 continue;
604
605 if (!match_property(enumerator, device))
606 continue;
607
608 if (!match_sysattr(enumerator, device))
609 continue;
610
19c9df44 611 k = device_enumerator_add_device(enumerator, device);
96df036f
TG
612 if (k < 0) {
613 r = k;
614 continue;
615 }
616 }
617
618 return r;
619}
620
621static int enumerator_scan_devices_tags(sd_device_enumerator *enumerator) {
622 const char *tag;
3172836b 623 int r = 0;
96df036f
TG
624
625 assert(enumerator);
626
90e74a66 627 SET_FOREACH(tag, enumerator->match_tag) {
3172836b
TG
628 int k;
629
630 k = enumerator_scan_devices_tag(enumerator, tag);
631 if (k < 0)
632 r = k;
96df036f
TG
633 }
634
3172836b 635 return r;
96df036f
TG
636}
637
638static int parent_add_child(sd_device_enumerator *enumerator, const char *path) {
4afd3348 639 _cleanup_(sd_device_unrefp) sd_device *device = NULL;
96df036f
TG
640 const char *subsystem, *sysname;
641 int r;
642
643 r = sd_device_new_from_syspath(&device, path);
08232a02
TG
644 if (r == -ENODEV)
645 /* this is necessarily racy, so ignore missing devices */
96df036f
TG
646 return 0;
647 else if (r < 0)
648 return r;
649
650 r = sd_device_get_subsystem(device, &subsystem);
9a9c7dc2
MM
651 if (r == -ENOENT)
652 return 0;
96df036f
TG
653 if (r < 0)
654 return r;
655
656 if (!match_subsystem(enumerator, subsystem))
657 return 0;
658
659 r = sd_device_get_sysname(device, &sysname);
660 if (r < 0)
661 return r;
662
663 if (!match_sysname(enumerator, sysname))
664 return 0;
665
666 if (!match_property(enumerator, device))
667 return 0;
668
669 if (!match_sysattr(enumerator, device))
670 return 0;
671
19c9df44 672 r = device_enumerator_add_device(enumerator, device);
96df036f
TG
673 if (r < 0)
674 return r;
675
676 return 1;
677}
678
679static int parent_crawl_children(sd_device_enumerator *enumerator, const char *path, unsigned maxdepth) {
680 _cleanup_closedir_ DIR *dir = NULL;
681 struct dirent *dent;
682 int r = 0;
683
684 dir = opendir(path);
25f027c5 685 if (!dir)
c7d54dae 686 return log_debug_errno(errno, "sd-device-enumerator: Failed to open parent directory %s: %m", path);
96df036f
TG
687
688 FOREACH_DIRENT_ALL(dent, dir, return -errno) {
689 _cleanup_free_ char *child = NULL;
690 int k;
691
692 if (dent->d_name[0] == '.')
693 continue;
694
695 if (dent->d_type != DT_DIR)
696 continue;
697
657ee2d8 698 child = path_join(path, dent->d_name);
53fae771
ZJS
699 if (!child)
700 return -ENOMEM;
96df036f
TG
701
702 k = parent_add_child(enumerator, child);
703 if (k < 0)
704 r = k;
705
706 if (maxdepth > 0)
707 parent_crawl_children(enumerator, child, maxdepth - 1);
708 else
c7d54dae 709 log_debug("sd-device-enumerator: Max depth reached, %s: ignoring devices", child);
96df036f
TG
710 }
711
712 return r;
713}
714
715static int enumerator_scan_devices_children(sd_device_enumerator *enumerator) {
716 const char *path;
717 int r = 0, k;
718
90e74a66 719 SET_FOREACH(path, enumerator->match_parent) {
e022bf66
YW
720 k = parent_add_child(enumerator, path);
721 if (k < 0)
722 r = k;
96df036f 723
e022bf66
YW
724 k = parent_crawl_children(enumerator, path, DEVICE_ENUMERATE_MAX_DEPTH);
725 if (k < 0)
726 r = k;
727 }
96df036f
TG
728
729 return r;
730}
731
732static int enumerator_scan_devices_all(sd_device_enumerator *enumerator) {
733 int r = 0;
734
c7d54dae 735 log_debug("sd-device-enumerator: Scan all dirs");
96df036f
TG
736
737 if (access("/sys/subsystem", F_OK) >= 0) {
738 /* we have /subsystem/, forget all the old stuff */
739 r = enumerator_scan_dir(enumerator, "subsystem", "devices", NULL);
e53fc357 740 if (r < 0)
c7d54dae 741 return log_debug_errno(r, "sd-device-enumerator: Failed to scan /sys/subsystem: %m");
96df036f
TG
742 } else {
743 int k;
744
745 k = enumerator_scan_dir(enumerator, "bus", "devices", NULL);
746 if (k < 0) {
c7d54dae 747 log_debug_errno(k, "sd-device-enumerator: Failed to scan /sys/bus: %m");
96df036f
TG
748 r = k;
749 }
750
751 k = enumerator_scan_dir(enumerator, "class", NULL, NULL);
752 if (k < 0) {
c7d54dae 753 log_debug_errno(k, "sd-device-enumerator: Failed to scan /sys/class: %m");
96df036f
TG
754 r = k;
755 }
756 }
757
758 return r;
759}
760
cd8d816d
YW
761static void device_enumerator_dedup_devices(sd_device_enumerator *enumerator) {
762 sd_device **a, **b, **end;
763
764 assert(enumerator);
765
766 if (enumerator->n_devices <= 1)
767 return;
768
769 a = enumerator->devices + 1;
770 b = enumerator->devices;
771 end = enumerator->devices + enumerator->n_devices;
772
773 for (; a < end; a++) {
774 const char *devpath_a, *devpath_b;
775
776 assert_se(sd_device_get_devpath(*a, &devpath_a) >= 0);
777 assert_se(sd_device_get_devpath(*b, &devpath_b) >= 0);
778
779 if (path_equal(devpath_a, devpath_b))
780 sd_device_unref(*a);
781 else
782 *(++b) = *a;
783 }
784
785 enumerator->n_devices = b - enumerator->devices + 1;
786}
787
96df036f 788int device_enumerator_scan_devices(sd_device_enumerator *enumerator) {
3172836b 789 int r = 0, k;
0a166589 790 size_t i;
96df036f
TG
791
792 assert(enumerator);
793
794 if (enumerator->scan_uptodate &&
795 enumerator->type == DEVICE_ENUMERATION_TYPE_DEVICES)
796 return 0;
797
0a166589
YW
798 for (i = 0; i < enumerator->n_devices; i++)
799 sd_device_unref(enumerator->devices[i]);
800
801 enumerator->n_devices = 0;
96df036f
TG
802
803 if (!set_isempty(enumerator->match_tag)) {
3172836b
TG
804 k = enumerator_scan_devices_tags(enumerator);
805 if (k < 0)
806 r = k;
96df036f 807 } else if (enumerator->match_parent) {
3172836b
TG
808 k = enumerator_scan_devices_children(enumerator);
809 if (k < 0)
810 r = k;
96df036f 811 } else {
3172836b
TG
812 k = enumerator_scan_devices_all(enumerator);
813 if (k < 0)
814 r = k;
96df036f
TG
815 }
816
93bab288 817 typesafe_qsort(enumerator->devices, enumerator->n_devices, device_compare);
cd8d816d 818 device_enumerator_dedup_devices(enumerator);
0a166589 819
96df036f 820 enumerator->scan_uptodate = true;
0a166589 821 enumerator->type = DEVICE_ENUMERATION_TYPE_DEVICES;
96df036f 822
3172836b 823 return r;
96df036f
TG
824}
825
826_public_ sd_device *sd_device_enumerator_get_device_first(sd_device_enumerator *enumerator) {
827 int r;
828
829 assert_return(enumerator, NULL);
830
831 r = device_enumerator_scan_devices(enumerator);
832 if (r < 0)
833 return NULL;
834
0a166589
YW
835 enumerator->current_device_index = 0;
836
837 if (enumerator->n_devices == 0)
838 return NULL;
96df036f 839
0a166589 840 return enumerator->devices[0];
96df036f
TG
841}
842
843_public_ sd_device *sd_device_enumerator_get_device_next(sd_device_enumerator *enumerator) {
844 assert_return(enumerator, NULL);
845
846 if (!enumerator->scan_uptodate ||
0a166589
YW
847 enumerator->type != DEVICE_ENUMERATION_TYPE_DEVICES ||
848 enumerator->current_device_index + 1 >= enumerator->n_devices)
96df036f
TG
849 return NULL;
850
0a166589 851 return enumerator->devices[++enumerator->current_device_index];
96df036f
TG
852}
853
854int device_enumerator_scan_subsystems(sd_device_enumerator *enumerator) {
96df036f
TG
855 const char *subsysdir;
856 int r = 0, k;
0a166589 857 size_t i;
96df036f
TG
858
859 assert(enumerator);
860
861 if (enumerator->scan_uptodate &&
862 enumerator->type == DEVICE_ENUMERATION_TYPE_SUBSYSTEMS)
863 return 0;
864
0a166589
YW
865 for (i = 0; i < enumerator->n_devices; i++)
866 sd_device_unref(enumerator->devices[i]);
867
868 enumerator->n_devices = 0;
96df036f
TG
869
870 /* modules */
871 if (match_subsystem(enumerator, "module")) {
872 k = enumerator_scan_dir_and_add_devices(enumerator, "module", NULL, NULL);
873 if (k < 0) {
c7d54dae 874 log_debug_errno(k, "sd-device-enumerator: Failed to scan modules: %m");
96df036f
TG
875 r = k;
876 }
877 }
878
879 if (access("/sys/subsystem", F_OK) >= 0)
880 subsysdir = "subsystem";
881 else
882 subsysdir = "bus";
883
884 /* subsystems (only buses support coldplug) */
885 if (match_subsystem(enumerator, "subsystem")) {
886 k = enumerator_scan_dir_and_add_devices(enumerator, subsysdir, NULL, NULL);
887 if (k < 0) {
c7d54dae 888 log_debug_errno(k, "sd-device-enumerator: Failed to scan subsystems: %m");
96df036f
TG
889 r = k;
890 }
891 }
892
893 /* subsystem drivers */
894 if (match_subsystem(enumerator, "drivers")) {
895 k = enumerator_scan_dir(enumerator, subsysdir, "drivers", "drivers");
896 if (k < 0) {
c7d54dae 897 log_debug_errno(k, "sd-device-enumerator: Failed to scan drivers: %m");
96df036f
TG
898 r = k;
899 }
900 }
901
93bab288 902 typesafe_qsort(enumerator->devices, enumerator->n_devices, device_compare);
cd8d816d 903 device_enumerator_dedup_devices(enumerator);
0a166589 904
96df036f 905 enumerator->scan_uptodate = true;
0a166589 906 enumerator->type = DEVICE_ENUMERATION_TYPE_SUBSYSTEMS;
96df036f
TG
907
908 return r;
909}
910
911_public_ sd_device *sd_device_enumerator_get_subsystem_first(sd_device_enumerator *enumerator) {
912 int r;
913
914 assert_return(enumerator, NULL);
915
916 r = device_enumerator_scan_subsystems(enumerator);
917 if (r < 0)
918 return NULL;
919
0a166589
YW
920 enumerator->current_device_index = 0;
921
922 if (enumerator->n_devices == 0)
923 return NULL;
96df036f 924
0a166589 925 return enumerator->devices[0];
96df036f
TG
926}
927
928_public_ sd_device *sd_device_enumerator_get_subsystem_next(sd_device_enumerator *enumerator) {
929 assert_return(enumerator, NULL);
930
4005d321 931 if (!enumerator->scan_uptodate ||
0a166589
YW
932 enumerator->type != DEVICE_ENUMERATION_TYPE_SUBSYSTEMS ||
933 enumerator->current_device_index + 1 >= enumerator->n_devices)
96df036f
TG
934 return NULL;
935
0a166589 936 return enumerator->devices[++enumerator->current_device_index];
96df036f
TG
937}
938
939sd_device *device_enumerator_get_first(sd_device_enumerator *enumerator) {
940 assert_return(enumerator, NULL);
941
0a166589
YW
942 if (!enumerator->scan_uptodate)
943 return NULL;
944
945 enumerator->current_device_index = 0;
946
947 if (enumerator->n_devices == 0)
948 return NULL;
949
950 return enumerator->devices[0];
96df036f
TG
951}
952
953sd_device *device_enumerator_get_next(sd_device_enumerator *enumerator) {
954 assert_return(enumerator, NULL);
955
0a166589
YW
956 if (!enumerator->scan_uptodate ||
957 enumerator->current_device_index + 1 >= enumerator->n_devices)
958 return NULL;
96df036f 959
0a166589 960 return enumerator->devices[++enumerator->current_device_index];
96df036f 961}
708474c5
YW
962
963sd_device **device_enumerator_get_devices(sd_device_enumerator *enumerator, size_t *ret_n_devices) {
964 assert(enumerator);
965 assert(ret_n_devices);
966
967 if (!enumerator->scan_uptodate)
968 return NULL;
969
970 *ret_n_devices = enumerator->n_devices;
971 return enumerator->devices;
972}