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