]>
Commit | Line | Data |
---|---|---|
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 |
19 | typedef 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 | ||
27 | struct 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 |
70 | static 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 | ||
77 | static 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 | 86 | static 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 |
106 | DEFINE_PUBLIC_TRIVIAL_REF_UNREF_FUNC(sd_device_enumerator, sd_device_enumerator, device_enumerator_free); |
107 | ||
f5caa8fa MB |
108 | int 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 | 183 | static 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 | ||
221 | int 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 | 260 | int 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 |
271 | static 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 | ||
303 | static bool devpath_is_late_block(const char *devpath) { | |
304 | assert(devpath); | |
305 | ||
306 | return strstr(devpath, "/block/md") || strstr(devpath, "/block/dm-"); | |
307 | } | |
308 | ||
309 | static 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 | ||
333 | static 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 | ||
433 | failed: | |
434 | device_unref_many(devices, n); | |
435 | free(devices); | |
436 | return r; | |
e3bff60a MP |
437 | } |
438 | ||
439 | int 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 | 462 | static 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 | ||
489 | static 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 | 502 | static 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 |
509 | static 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 |
541 | static 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 | ||
550 | typedef 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 |
559 | static 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 |
614 | static 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 | ||
653 | int 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 | ||
657 | static 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 | ||
668 | static 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 |
743 | static 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 | ||
782 | static 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 | ||
837 | static 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 | 854 | static 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 | 872 | static 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 | ||
920 | static 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 | ||
950 | static 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 | ||
964 | int 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 | ||
1024 | int 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 | 1091 | int 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 |
1135 | sd_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 | ||
1152 | sd_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 | ||
1163 | sd_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 | } |