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