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