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