]>
Commit | Line | Data |
---|---|---|
88a6477e KS |
1 | /*** |
2 | This file is part of systemd. | |
3 | ||
4 | Copyright 2008-2012 Kay Sievers <kay@vrfy.org> | |
5 | ||
6 | systemd is free software; you can redistribute it and/or modify it | |
7 | under the terms of the GNU Lesser General Public License as published by | |
8 | the Free Software Foundation; either version 2.1 of the License, or | |
9 | (at your option) any later version. | |
10 | ||
11 | systemd is distributed in the hope that it will be useful, but | |
12 | WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
14 | Lesser General Public License for more details. | |
15 | ||
16 | You should have received a copy of the GNU Lesser General Public License | |
17 | along with systemd; If not, see <http://www.gnu.org/licenses/>. | |
18 | ***/ | |
eb1f0e66 | 19 | |
eb1f0e66 KS |
20 | #include <stdio.h> |
21 | #include <stdlib.h> | |
22 | #include <stddef.h> | |
23 | #include <unistd.h> | |
24 | #include <errno.h> | |
25 | #include <string.h> | |
26 | #include <dirent.h> | |
c97f839e | 27 | #include <fnmatch.h> |
871a36bd | 28 | #include <stdbool.h> |
eb1f0e66 | 29 | #include <sys/stat.h> |
871a36bd | 30 | #include <sys/param.h> |
eb1f0e66 KS |
31 | |
32 | #include "libudev.h" | |
33 | #include "libudev-private.h" | |
eb1f0e66 | 34 | |
ce1d6d7f KS |
35 | /** |
36 | * SECTION:libudev-enumerate | |
37 | * @short_description: lookup and sort sys devices | |
38 | * | |
39 | * Lookup devices in the sys filesystem, filter devices by properties, | |
a7c140c7 | 40 | * and return a sorted list of devices. |
ce1d6d7f KS |
41 | */ |
42 | ||
871a36bd | 43 | struct syspath { |
912541b0 KS |
44 | char *syspath; |
45 | size_t len; | |
871a36bd KS |
46 | }; |
47 | ||
ce1d6d7f KS |
48 | /** |
49 | * udev_enumerate: | |
50 | * | |
51 | * Opaque object representing one device lookup/sort context. | |
52 | */ | |
bf7ad0ea | 53 | struct udev_enumerate { |
912541b0 KS |
54 | struct udev *udev; |
55 | int refcount; | |
56 | struct udev_list sysattr_match_list; | |
57 | struct udev_list sysattr_nomatch_list; | |
58 | struct udev_list subsystem_match_list; | |
59 | struct udev_list subsystem_nomatch_list; | |
60 | struct udev_list sysname_match_list; | |
61 | struct udev_list properties_match_list; | |
62 | struct udev_list tags_match_list; | |
63 | struct udev_device *parent_match; | |
64 | struct udev_list devices_list; | |
65 | struct syspath *devices; | |
66 | unsigned int devices_cur; | |
67 | unsigned int devices_max; | |
68 | bool devices_uptodate:1; | |
69 | bool match_is_initialized; | |
bf7ad0ea KS |
70 | }; |
71 | ||
c97f839e KS |
72 | /** |
73 | * udev_enumerate_new: | |
74 | * @udev: udev library context | |
75 | * | |
21dbe43a KS |
76 | * Create an enumeration context to scan /sys. |
77 | * | |
78 | * Returns: an enumeration context. | |
c97f839e | 79 | **/ |
54cf0b7f | 80 | _public_ struct udev_enumerate *udev_enumerate_new(struct udev *udev) |
c97f839e | 81 | { |
912541b0 | 82 | struct udev_enumerate *udev_enumerate; |
c97f839e | 83 | |
e6889307 MT |
84 | if (udev == NULL) |
85 | return NULL; | |
912541b0 KS |
86 | udev_enumerate = calloc(1, sizeof(struct udev_enumerate)); |
87 | if (udev_enumerate == NULL) | |
88 | return NULL; | |
89 | udev_enumerate->refcount = 1; | |
90 | udev_enumerate->udev = udev; | |
91 | udev_list_init(udev, &udev_enumerate->sysattr_match_list, false); | |
92 | udev_list_init(udev, &udev_enumerate->sysattr_nomatch_list, false); | |
93 | udev_list_init(udev, &udev_enumerate->subsystem_match_list, true); | |
94 | udev_list_init(udev, &udev_enumerate->subsystem_nomatch_list, true); | |
95 | udev_list_init(udev, &udev_enumerate->sysname_match_list, true); | |
96 | udev_list_init(udev, &udev_enumerate->properties_match_list, false); | |
97 | udev_list_init(udev, &udev_enumerate->tags_match_list, true); | |
98 | udev_list_init(udev, &udev_enumerate->devices_list, false); | |
99 | return udev_enumerate; | |
c97f839e KS |
100 | } |
101 | ||
ce1d6d7f KS |
102 | /** |
103 | * udev_enumerate_ref: | |
104 | * @udev_enumerate: context | |
105 | * | |
106 | * Take a reference of a enumeration context. | |
107 | * | |
108 | * Returns: the passed enumeration context | |
109 | **/ | |
54cf0b7f | 110 | _public_ struct udev_enumerate *udev_enumerate_ref(struct udev_enumerate *udev_enumerate) |
bf7ad0ea | 111 | { |
912541b0 KS |
112 | if (udev_enumerate == NULL) |
113 | return NULL; | |
114 | udev_enumerate->refcount++; | |
115 | return udev_enumerate; | |
bf7ad0ea KS |
116 | } |
117 | ||
ce1d6d7f KS |
118 | /** |
119 | * udev_enumerate_unref: | |
120 | * @udev_enumerate: context | |
121 | * | |
122 | * Drop a reference of an enumeration context. If the refcount reaches zero, | |
123 | * all resources of the enumeration context will be released. | |
c1959569 KS |
124 | * |
125 | * Returns: the passed enumeration context if it has still an active reference, or #NULL otherwise. | |
ce1d6d7f | 126 | **/ |
20bbd54f | 127 | _public_ struct udev_enumerate *udev_enumerate_unref(struct udev_enumerate *udev_enumerate) |
bf7ad0ea | 128 | { |
912541b0 KS |
129 | unsigned int i; |
130 | ||
131 | if (udev_enumerate == NULL) | |
20bbd54f | 132 | return NULL; |
912541b0 KS |
133 | udev_enumerate->refcount--; |
134 | if (udev_enumerate->refcount > 0) | |
20bbd54f | 135 | return udev_enumerate; |
912541b0 KS |
136 | udev_list_cleanup(&udev_enumerate->sysattr_match_list); |
137 | udev_list_cleanup(&udev_enumerate->sysattr_nomatch_list); | |
138 | udev_list_cleanup(&udev_enumerate->subsystem_match_list); | |
139 | udev_list_cleanup(&udev_enumerate->subsystem_nomatch_list); | |
140 | udev_list_cleanup(&udev_enumerate->sysname_match_list); | |
141 | udev_list_cleanup(&udev_enumerate->properties_match_list); | |
142 | udev_list_cleanup(&udev_enumerate->tags_match_list); | |
143 | udev_device_unref(udev_enumerate->parent_match); | |
144 | udev_list_cleanup(&udev_enumerate->devices_list); | |
145 | for (i = 0; i < udev_enumerate->devices_cur; i++) | |
146 | free(udev_enumerate->devices[i].syspath); | |
147 | free(udev_enumerate->devices); | |
148 | free(udev_enumerate); | |
20bbd54f | 149 | return NULL; |
bf7ad0ea KS |
150 | } |
151 | ||
ce1d6d7f KS |
152 | /** |
153 | * udev_enumerate_get_udev: | |
154 | * @udev_enumerate: context | |
155 | * | |
21dbe43a KS |
156 | * Get the udev library context. |
157 | * | |
158 | * Returns: a pointer to the context. | |
ce1d6d7f | 159 | */ |
54cf0b7f | 160 | _public_ struct udev *udev_enumerate_get_udev(struct udev_enumerate *udev_enumerate) |
bc8184ed | 161 | { |
912541b0 KS |
162 | if (udev_enumerate == NULL) |
163 | return NULL; | |
164 | return udev_enumerate->udev; | |
bc8184ed KS |
165 | } |
166 | ||
871a36bd KS |
167 | static int syspath_add(struct udev_enumerate *udev_enumerate, const char *syspath) |
168 | { | |
912541b0 KS |
169 | char *path; |
170 | struct syspath *entry; | |
171 | ||
172 | /* double array size if needed */ | |
173 | if (udev_enumerate->devices_cur >= udev_enumerate->devices_max) { | |
174 | struct syspath *buf; | |
175 | unsigned int add; | |
176 | ||
177 | add = udev_enumerate->devices_max; | |
178 | if (add < 1024) | |
179 | add = 1024; | |
180 | buf = realloc(udev_enumerate->devices, (udev_enumerate->devices_max + add) * sizeof(struct syspath)); | |
181 | if (buf == NULL) | |
182 | return -ENOMEM; | |
183 | udev_enumerate->devices = buf; | |
184 | udev_enumerate->devices_max += add; | |
185 | } | |
186 | ||
187 | path = strdup(syspath); | |
188 | if (path == NULL) | |
189 | return -ENOMEM; | |
190 | entry = &udev_enumerate->devices[udev_enumerate->devices_cur]; | |
191 | entry->syspath = path; | |
192 | entry->len = strlen(path); | |
193 | udev_enumerate->devices_cur++; | |
194 | udev_enumerate->devices_uptodate = false; | |
195 | return 0; | |
871a36bd KS |
196 | } |
197 | ||
198 | static int syspath_cmp(const void *p1, const void *p2) | |
199 | { | |
912541b0 KS |
200 | const struct syspath *path1 = p1; |
201 | const struct syspath *path2 = p2; | |
202 | size_t len; | |
203 | int ret; | |
871a36bd | 204 | |
912541b0 KS |
205 | len = MIN(path1->len, path2->len); |
206 | ret = memcmp(path1->syspath, path2->syspath, len); | |
207 | if (ret == 0) { | |
208 | if (path1->len < path2->len) | |
209 | ret = -1; | |
210 | else if (path1->len > path2->len) | |
211 | ret = 1; | |
212 | } | |
213 | return ret; | |
871a36bd KS |
214 | } |
215 | ||
3bf76824 | 216 | /* For devices that should be moved to the absolute end of the list */ |
28460195 | 217 | static bool devices_delay_end(struct udev *udev, const char *syspath) |
871a36bd | 218 | { |
912541b0 KS |
219 | static const char *delay_device_list[] = { |
220 | "/block/md", | |
221 | "/block/dm-", | |
222 | NULL | |
223 | }; | |
912541b0 | 224 | int i; |
871a36bd | 225 | |
912541b0 | 226 | for (i = 0; delay_device_list[i] != NULL; i++) { |
6ada823a | 227 | if (strstr(syspath + strlen("/sys"), delay_device_list[i]) != NULL) |
912541b0 | 228 | return true; |
912541b0 KS |
229 | } |
230 | return false; | |
871a36bd KS |
231 | } |
232 | ||
3bf76824 LP |
233 | /* For devices that should just be moved a little bit later, just |
234 | * before the point where some common path prefix changes. Returns the | |
235 | * number of characters that make up that common prefix */ | |
236 | static size_t devices_delay_later(struct udev *udev, const char *syspath) | |
237 | { | |
912541b0 | 238 | const char *c; |
3bf76824 | 239 | |
912541b0 KS |
240 | /* For sound cards the control device must be enumerated last |
241 | * to make sure it's the final device node that gets ACLs | |
242 | * applied. Applications rely on this fact and use ACL changes | |
243 | * on the control node as an indicator that the ACL change of | |
244 | * the entire sound card completed. The kernel makes this | |
245 | * guarantee when creating those devices, and hence we should | |
246 | * too when enumerating them. */ | |
3bf76824 | 247 | |
912541b0 KS |
248 | if ((c = strstr(syspath, "/sound/card"))) { |
249 | c += 11; | |
250 | c += strcspn(c, "/"); | |
3bf76824 | 251 | |
33502ffe | 252 | if (startswith(c, "/controlC")) |
912541b0 KS |
253 | return c - syspath + 1; |
254 | } | |
3bf76824 | 255 | |
912541b0 | 256 | return 0; |
3bf76824 LP |
257 | } |
258 | ||
a7c140c7 KS |
259 | /** |
260 | * udev_enumerate_get_list_entry: | |
261 | * @udev_enumerate: context | |
262 | * | |
21dbe43a KS |
263 | * Get the first entry of the sorted list of device paths. |
264 | * | |
265 | * Returns: a udev_list_entry. | |
a7c140c7 | 266 | */ |
54cf0b7f | 267 | _public_ struct udev_list_entry *udev_enumerate_get_list_entry(struct udev_enumerate *udev_enumerate) |
bf7ad0ea | 268 | { |
912541b0 KS |
269 | if (udev_enumerate == NULL) |
270 | return NULL; | |
271 | if (!udev_enumerate->devices_uptodate) { | |
272 | unsigned int i; | |
273 | unsigned int max; | |
274 | struct syspath *prev = NULL, *move_later = NULL; | |
275 | size_t move_later_prefix = 0; | |
276 | ||
277 | udev_list_cleanup(&udev_enumerate->devices_list); | |
278 | qsort(udev_enumerate->devices, udev_enumerate->devices_cur, sizeof(struct syspath), syspath_cmp); | |
279 | ||
280 | max = udev_enumerate->devices_cur; | |
281 | for (i = 0; i < max; i++) { | |
282 | struct syspath *entry = &udev_enumerate->devices[i]; | |
283 | ||
284 | /* skip duplicated entries */ | |
285 | if (prev != NULL && | |
286 | entry->len == prev->len && | |
287 | memcmp(entry->syspath, prev->syspath, entry->len) == 0) | |
288 | continue; | |
289 | prev = entry; | |
290 | ||
291 | /* skip to be delayed devices, and add them to the end of the list */ | |
292 | if (devices_delay_end(udev_enumerate->udev, entry->syspath)) { | |
293 | syspath_add(udev_enumerate, entry->syspath); | |
294 | /* need to update prev here for the case realloc() gives a different address */ | |
295 | prev = &udev_enumerate->devices[i]; | |
296 | continue; | |
297 | } | |
298 | ||
299 | /* skip to be delayed devices, and move the to | |
300 | * the point where the prefix changes. We can | |
301 | * only move one item at a time. */ | |
302 | if (!move_later) { | |
303 | move_later_prefix = devices_delay_later(udev_enumerate->udev, entry->syspath); | |
304 | ||
305 | if (move_later_prefix > 0) { | |
306 | move_later = entry; | |
307 | continue; | |
308 | } | |
309 | } | |
310 | ||
311 | if (move_later && | |
641906e9 | 312 | !strneq(entry->syspath, move_later->syspath, move_later_prefix)) { |
912541b0 KS |
313 | |
314 | udev_list_entry_add(&udev_enumerate->devices_list, move_later->syspath, NULL); | |
315 | move_later = NULL; | |
316 | } | |
317 | ||
318 | udev_list_entry_add(&udev_enumerate->devices_list, entry->syspath, NULL); | |
319 | } | |
320 | ||
321 | if (move_later) | |
322 | udev_list_entry_add(&udev_enumerate->devices_list, move_later->syspath, NULL); | |
323 | ||
324 | /* add and cleanup delayed devices from end of list */ | |
325 | for (i = max; i < udev_enumerate->devices_cur; i++) { | |
326 | struct syspath *entry = &udev_enumerate->devices[i]; | |
327 | ||
328 | udev_list_entry_add(&udev_enumerate->devices_list, entry->syspath, NULL); | |
329 | free(entry->syspath); | |
330 | } | |
331 | udev_enumerate->devices_cur = max; | |
332 | ||
333 | udev_enumerate->devices_uptodate = true; | |
334 | } | |
335 | return udev_list_get_entry(&udev_enumerate->devices_list); | |
bf7ad0ea KS |
336 | } |
337 | ||
a7c140c7 KS |
338 | /** |
339 | * udev_enumerate_add_match_subsystem: | |
340 | * @udev_enumerate: context | |
341 | * @subsystem: filter for a subsystem of the device to include in the list | |
342 | * | |
21dbe43a KS |
343 | * Match only devices belonging to a certain kernel subsystem. |
344 | * | |
a7c140c7 KS |
345 | * Returns: 0 on success, otherwise a negative error value. |
346 | */ | |
54cf0b7f | 347 | _public_ int udev_enumerate_add_match_subsystem(struct udev_enumerate *udev_enumerate, const char *subsystem) |
eb1f0e66 | 348 | { |
912541b0 KS |
349 | if (udev_enumerate == NULL) |
350 | return -EINVAL; | |
351 | if (subsystem == NULL) | |
352 | return 0; | |
353 | if (udev_list_entry_add(&udev_enumerate->subsystem_match_list, subsystem, NULL) == NULL) | |
354 | return -ENOMEM; | |
355 | return 0; | |
c97f839e KS |
356 | } |
357 | ||
a7c140c7 KS |
358 | /** |
359 | * udev_enumerate_add_nomatch_subsystem: | |
360 | * @udev_enumerate: context | |
361 | * @subsystem: filter for a subsystem of the device to exclude from the list | |
362 | * | |
21dbe43a KS |
363 | * Match only devices not belonging to a certain kernel subsystem. |
364 | * | |
a7c140c7 KS |
365 | * Returns: 0 on success, otherwise a negative error value. |
366 | */ | |
54cf0b7f | 367 | _public_ int udev_enumerate_add_nomatch_subsystem(struct udev_enumerate *udev_enumerate, const char *subsystem) |
c97f839e | 368 | { |
912541b0 KS |
369 | if (udev_enumerate == NULL) |
370 | return -EINVAL; | |
371 | if (subsystem == NULL) | |
372 | return 0; | |
373 | if (udev_list_entry_add(&udev_enumerate->subsystem_nomatch_list, subsystem, NULL) == NULL) | |
374 | return -ENOMEM; | |
375 | return 0; | |
c97f839e KS |
376 | } |
377 | ||
a7c140c7 KS |
378 | /** |
379 | * udev_enumerate_add_match_sysattr: | |
380 | * @udev_enumerate: context | |
381 | * @sysattr: filter for a sys attribute at the device to include in the list | |
382 | * @value: optional value of the sys attribute | |
383 | * | |
21dbe43a KS |
384 | * Match only devices with a certain /sys device attribute. |
385 | * | |
a7c140c7 KS |
386 | * Returns: 0 on success, otherwise a negative error value. |
387 | */ | |
54cf0b7f | 388 | _public_ int udev_enumerate_add_match_sysattr(struct udev_enumerate *udev_enumerate, const char *sysattr, const char *value) |
c97f839e | 389 | { |
912541b0 KS |
390 | if (udev_enumerate == NULL) |
391 | return -EINVAL; | |
392 | if (sysattr == NULL) | |
393 | return 0; | |
394 | if (udev_list_entry_add(&udev_enumerate->sysattr_match_list, sysattr, value) == NULL) | |
395 | return -ENOMEM; | |
396 | return 0; | |
c97f839e KS |
397 | } |
398 | ||
a7c140c7 KS |
399 | /** |
400 | * udev_enumerate_add_nomatch_sysattr: | |
401 | * @udev_enumerate: context | |
402 | * @sysattr: filter for a sys attribute at the device to exclude from the list | |
403 | * @value: optional value of the sys attribute | |
404 | * | |
21dbe43a KS |
405 | * Match only devices not having a certain /sys device attribute. |
406 | * | |
a7c140c7 KS |
407 | * Returns: 0 on success, otherwise a negative error value. |
408 | */ | |
54cf0b7f | 409 | _public_ int udev_enumerate_add_nomatch_sysattr(struct udev_enumerate *udev_enumerate, const char *sysattr, const char *value) |
c97f839e | 410 | { |
912541b0 KS |
411 | if (udev_enumerate == NULL) |
412 | return -EINVAL; | |
413 | if (sysattr == NULL) | |
414 | return 0; | |
415 | if (udev_list_entry_add(&udev_enumerate->sysattr_nomatch_list, sysattr, value) == NULL) | |
416 | return -ENOMEM; | |
417 | return 0; | |
c97f839e KS |
418 | } |
419 | ||
28460195 | 420 | static int match_sysattr_value(struct udev_device *dev, const char *sysattr, const char *match_val) |
c97f839e | 421 | { |
912541b0 KS |
422 | const char *val = NULL; |
423 | bool match = false; | |
424 | ||
425 | val = udev_device_get_sysattr_value(dev, sysattr); | |
426 | if (val == NULL) | |
427 | goto exit; | |
428 | if (match_val == NULL) { | |
429 | match = true; | |
430 | goto exit; | |
431 | } | |
432 | if (fnmatch(match_val, val, 0) == 0) { | |
433 | match = true; | |
434 | goto exit; | |
435 | } | |
c97f839e | 436 | exit: |
912541b0 | 437 | return match; |
c97f839e KS |
438 | } |
439 | ||
a7c140c7 KS |
440 | /** |
441 | * udev_enumerate_add_match_property: | |
442 | * @udev_enumerate: context | |
443 | * @property: filter for a property of the device to include in the list | |
444 | * @value: value of the property | |
445 | * | |
21dbe43a KS |
446 | * Match only devices with a certain property. |
447 | * | |
a7c140c7 KS |
448 | * Returns: 0 on success, otherwise a negative error value. |
449 | */ | |
54cf0b7f | 450 | _public_ int udev_enumerate_add_match_property(struct udev_enumerate *udev_enumerate, const char *property, const char *value) |
f0893502 | 451 | { |
912541b0 KS |
452 | if (udev_enumerate == NULL) |
453 | return -EINVAL; | |
454 | if (property == NULL) | |
455 | return 0; | |
456 | if (udev_list_entry_add(&udev_enumerate->properties_match_list, property, value) == NULL) | |
457 | return -ENOMEM; | |
458 | return 0; | |
f0893502 KS |
459 | } |
460 | ||
28460195 KS |
461 | /** |
462 | * udev_enumerate_add_match_tag: | |
463 | * @udev_enumerate: context | |
464 | * @tag: filter for a tag of the device to include in the list | |
465 | * | |
21dbe43a KS |
466 | * Match only devices with a certain tag. |
467 | * | |
28460195 KS |
468 | * Returns: 0 on success, otherwise a negative error value. |
469 | */ | |
54cf0b7f | 470 | _public_ int udev_enumerate_add_match_tag(struct udev_enumerate *udev_enumerate, const char *tag) |
28460195 | 471 | { |
912541b0 KS |
472 | if (udev_enumerate == NULL) |
473 | return -EINVAL; | |
474 | if (tag == NULL) | |
475 | return 0; | |
476 | if (udev_list_entry_add(&udev_enumerate->tags_match_list, tag, NULL) == NULL) | |
477 | return -ENOMEM; | |
478 | return 0; | |
28460195 KS |
479 | } |
480 | ||
b05211fa KS |
481 | /** |
482 | * udev_enumerate_add_match_parent: | |
483 | * @udev_enumerate: context | |
a07e0114 | 484 | * @parent: parent device where to start searching |
b05211fa | 485 | * |
a07e0114 KS |
486 | * Return the devices on the subtree of one given device. The parent |
487 | * itself is included in the list. | |
b05211fa KS |
488 | * |
489 | * A reference for the device is held until the udev_enumerate context | |
490 | * is cleaned up. | |
491 | * | |
492 | * Returns: 0 on success, otherwise a negative error value. | |
493 | */ | |
54cf0b7f | 494 | _public_ int udev_enumerate_add_match_parent(struct udev_enumerate *udev_enumerate, struct udev_device *parent) |
b05211fa | 495 | { |
912541b0 KS |
496 | if (udev_enumerate == NULL) |
497 | return -EINVAL; | |
498 | if (parent == NULL) | |
499 | return 0; | |
500 | if (udev_enumerate->parent_match != NULL) | |
501 | udev_device_unref(udev_enumerate->parent_match); | |
502 | udev_enumerate->parent_match = udev_device_ref(parent); | |
503 | return 0; | |
b05211fa KS |
504 | } |
505 | ||
48a0170b KS |
506 | /** |
507 | * udev_enumerate_add_match_is_initialized: | |
508 | * @udev_enumerate: context | |
509 | * | |
510 | * Match only devices which udev has set up already. This makes | |
511 | * sure, that the device node permissions and context are properly set | |
512 | * and that network devices are fully renamed. | |
513 | * | |
514 | * Usually, devices which are found in the kernel but not already | |
515 | * handled by udev, have still pending events. Services should subscribe | |
516 | * to monitor events and wait for these devices to become ready, instead | |
517 | * of using uninitialized devices. | |
518 | * | |
519 | * For now, this will not affect devices which do not have a device node | |
520 | * and are not network interfaces. | |
521 | * | |
522 | * Returns: 0 on success, otherwise a negative error value. | |
523 | */ | |
54cf0b7f | 524 | _public_ int udev_enumerate_add_match_is_initialized(struct udev_enumerate *udev_enumerate) |
48a0170b | 525 | { |
912541b0 KS |
526 | if (udev_enumerate == NULL) |
527 | return -EINVAL; | |
528 | udev_enumerate->match_is_initialized = true; | |
529 | return 0; | |
48a0170b KS |
530 | } |
531 | ||
cf5bd040 KS |
532 | /** |
533 | * udev_enumerate_add_match_sysname: | |
534 | * @udev_enumerate: context | |
535 | * @sysname: filter for the name of the device to include in the list | |
536 | * | |
21dbe43a KS |
537 | * Match only devices with a given /sys device name. |
538 | * | |
cf5bd040 KS |
539 | * Returns: 0 on success, otherwise a negative error value. |
540 | */ | |
54cf0b7f | 541 | _public_ int udev_enumerate_add_match_sysname(struct udev_enumerate *udev_enumerate, const char *sysname) |
cf5bd040 | 542 | { |
912541b0 KS |
543 | if (udev_enumerate == NULL) |
544 | return -EINVAL; | |
545 | if (sysname == NULL) | |
546 | return 0; | |
547 | if (udev_list_entry_add(&udev_enumerate->sysname_match_list, sysname, NULL) == NULL) | |
548 | return -ENOMEM; | |
549 | return 0; | |
cf5bd040 KS |
550 | } |
551 | ||
28460195 | 552 | static bool match_sysattr(struct udev_enumerate *udev_enumerate, struct udev_device *dev) |
c97f839e | 553 | { |
912541b0 KS |
554 | struct udev_list_entry *list_entry; |
555 | ||
556 | /* skip list */ | |
557 | udev_list_entry_foreach(list_entry, udev_list_get_entry(&udev_enumerate->sysattr_nomatch_list)) { | |
558 | if (match_sysattr_value(dev, udev_list_entry_get_name(list_entry), | |
559 | udev_list_entry_get_value(list_entry))) | |
560 | return false; | |
561 | } | |
562 | /* include list */ | |
563 | if (udev_list_get_entry(&udev_enumerate->sysattr_match_list) != NULL) { | |
564 | udev_list_entry_foreach(list_entry, udev_list_get_entry(&udev_enumerate->sysattr_match_list)) { | |
565 | /* anything that does not match, will make it FALSE */ | |
566 | if (!match_sysattr_value(dev, udev_list_entry_get_name(list_entry), | |
567 | udev_list_entry_get_value(list_entry))) | |
568 | return false; | |
569 | } | |
570 | return true; | |
571 | } | |
572 | return true; | |
c97f839e KS |
573 | } |
574 | ||
28460195 | 575 | static bool match_property(struct udev_enumerate *udev_enumerate, struct udev_device *dev) |
f0893502 | 576 | { |
912541b0 KS |
577 | struct udev_list_entry *list_entry; |
578 | bool match = false; | |
579 | ||
580 | /* no match always matches */ | |
581 | if (udev_list_get_entry(&udev_enumerate->properties_match_list) == NULL) | |
582 | return true; | |
583 | ||
584 | /* loop over matches */ | |
585 | udev_list_entry_foreach(list_entry, udev_list_get_entry(&udev_enumerate->properties_match_list)) { | |
586 | const char *match_key = udev_list_entry_get_name(list_entry); | |
587 | const char *match_value = udev_list_entry_get_value(list_entry); | |
588 | struct udev_list_entry *property_entry; | |
589 | ||
590 | /* loop over device properties */ | |
591 | udev_list_entry_foreach(property_entry, udev_device_get_properties_list_entry(dev)) { | |
592 | const char *dev_key = udev_list_entry_get_name(property_entry); | |
593 | const char *dev_value = udev_list_entry_get_value(property_entry); | |
594 | ||
595 | if (fnmatch(match_key, dev_key, 0) != 0) | |
596 | continue; | |
597 | if (match_value == NULL && dev_value == NULL) { | |
598 | match = true; | |
599 | goto out; | |
600 | } | |
601 | if (match_value == NULL || dev_value == NULL) | |
602 | continue; | |
603 | if (fnmatch(match_value, dev_value, 0) == 0) { | |
604 | match = true; | |
605 | goto out; | |
606 | } | |
607 | } | |
608 | } | |
f0893502 | 609 | out: |
912541b0 | 610 | return match; |
f0893502 KS |
611 | } |
612 | ||
28460195 KS |
613 | static bool match_tag(struct udev_enumerate *udev_enumerate, struct udev_device *dev) |
614 | { | |
912541b0 | 615 | struct udev_list_entry *list_entry; |
28460195 | 616 | |
912541b0 KS |
617 | /* no match always matches */ |
618 | if (udev_list_get_entry(&udev_enumerate->tags_match_list) == NULL) | |
619 | return true; | |
28460195 | 620 | |
912541b0 KS |
621 | /* loop over matches */ |
622 | udev_list_entry_foreach(list_entry, udev_list_get_entry(&udev_enumerate->tags_match_list)) | |
623 | if (!udev_device_has_tag(dev, udev_list_entry_get_name(list_entry))) | |
624 | return false; | |
28460195 | 625 | |
912541b0 | 626 | return true; |
28460195 KS |
627 | } |
628 | ||
b05211fa KS |
629 | static bool match_parent(struct udev_enumerate *udev_enumerate, struct udev_device *dev) |
630 | { | |
912541b0 KS |
631 | if (udev_enumerate->parent_match == NULL) |
632 | return true; | |
b05211fa | 633 | |
33502ffe | 634 | return startswith(udev_device_get_devpath(dev), udev_device_get_devpath(udev_enumerate->parent_match)); |
b05211fa KS |
635 | } |
636 | ||
28460195 | 637 | static bool match_sysname(struct udev_enumerate *udev_enumerate, const char *sysname) |
cf5bd040 | 638 | { |
912541b0 | 639 | struct udev_list_entry *list_entry; |
cf5bd040 | 640 | |
912541b0 KS |
641 | if (udev_list_get_entry(&udev_enumerate->sysname_match_list) == NULL) |
642 | return true; | |
cf5bd040 | 643 | |
912541b0 KS |
644 | udev_list_entry_foreach(list_entry, udev_list_get_entry(&udev_enumerate->sysname_match_list)) { |
645 | if (fnmatch(udev_list_entry_get_name(list_entry), sysname, 0) != 0) | |
646 | continue; | |
647 | return true; | |
648 | } | |
649 | return false; | |
cf5bd040 KS |
650 | } |
651 | ||
cabfd8d0 | 652 | static int scan_dir_and_add_devices(struct udev_enumerate *udev_enumerate, |
912541b0 KS |
653 | const char *basedir, const char *subdir1, const char *subdir2) |
654 | { | |
912541b0 KS |
655 | char path[UTIL_PATH_SIZE]; |
656 | size_t l; | |
657 | char *s; | |
658 | DIR *dir; | |
659 | struct dirent *dent; | |
660 | ||
661 | s = path; | |
d5a89d7d | 662 | l = strpcpyl(&s, sizeof(path), "/sys/", basedir, NULL); |
912541b0 | 663 | if (subdir1 != NULL) |
d5a89d7d | 664 | l = strpcpyl(&s, l, "/", subdir1, NULL); |
912541b0 | 665 | if (subdir2 != NULL) |
d5a89d7d | 666 | strpcpyl(&s, l, "/", subdir2, NULL); |
912541b0 KS |
667 | dir = opendir(path); |
668 | if (dir == NULL) | |
669 | return -ENOENT; | |
670 | for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) { | |
671 | char syspath[UTIL_PATH_SIZE]; | |
672 | struct udev_device *dev; | |
673 | ||
674 | if (dent->d_name[0] == '.') | |
675 | continue; | |
676 | ||
677 | if (!match_sysname(udev_enumerate, dent->d_name)) | |
678 | continue; | |
679 | ||
d5a89d7d | 680 | strscpyl(syspath, sizeof(syspath), path, "/", dent->d_name, NULL); |
912541b0 KS |
681 | dev = udev_device_new_from_syspath(udev_enumerate->udev, syspath); |
682 | if (dev == NULL) | |
683 | continue; | |
684 | ||
685 | if (udev_enumerate->match_is_initialized) { | |
686 | /* | |
687 | * All devices with a device node or network interfaces | |
688 | * possibly need udev to adjust the device node permission | |
689 | * or context, or rename the interface before it can be | |
690 | * reliably used from other processes. | |
691 | * | |
692 | * For now, we can only check these types of devices, we | |
693 | * might not store a database, and have no way to find out | |
694 | * for all other types of devices. | |
695 | */ | |
696 | if (!udev_device_get_is_initialized(dev) && | |
697 | (major(udev_device_get_devnum(dev)) > 0 || udev_device_get_ifindex(dev) > 0)) | |
698 | goto nomatch; | |
699 | } | |
700 | if (!match_parent(udev_enumerate, dev)) | |
701 | goto nomatch; | |
702 | if (!match_tag(udev_enumerate, dev)) | |
703 | goto nomatch; | |
704 | if (!match_property(udev_enumerate, dev)) | |
705 | goto nomatch; | |
706 | if (!match_sysattr(udev_enumerate, dev)) | |
707 | goto nomatch; | |
708 | ||
709 | syspath_add(udev_enumerate, udev_device_get_syspath(dev)); | |
28460195 | 710 | nomatch: |
912541b0 KS |
711 | udev_device_unref(dev); |
712 | } | |
713 | closedir(dir); | |
714 | return 0; | |
eb1f0e66 KS |
715 | } |
716 | ||
28460195 | 717 | static bool match_subsystem(struct udev_enumerate *udev_enumerate, const char *subsystem) |
eb1f0e66 | 718 | { |
912541b0 | 719 | struct udev_list_entry *list_entry; |
c97f839e | 720 | |
912541b0 KS |
721 | udev_list_entry_foreach(list_entry, udev_list_get_entry(&udev_enumerate->subsystem_nomatch_list)) { |
722 | if (fnmatch(udev_list_entry_get_name(list_entry), subsystem, 0) == 0) | |
723 | return false; | |
724 | } | |
725 | if (udev_list_get_entry(&udev_enumerate->subsystem_match_list) != NULL) { | |
726 | udev_list_entry_foreach(list_entry, udev_list_get_entry(&udev_enumerate->subsystem_match_list)) { | |
727 | if (fnmatch(udev_list_entry_get_name(list_entry), subsystem, 0) == 0) | |
728 | return true; | |
729 | } | |
730 | return false; | |
731 | } | |
732 | return true; | |
c97f839e KS |
733 | } |
734 | ||
cabfd8d0 | 735 | static int scan_dir(struct udev_enumerate *udev_enumerate, const char *basedir, const char *subdir, const char *subsystem) |
c97f839e | 736 | { |
912541b0 KS |
737 | char path[UTIL_PATH_SIZE]; |
738 | DIR *dir; | |
739 | struct dirent *dent; | |
c97f839e | 740 | |
d5a89d7d | 741 | strscpyl(path, sizeof(path), "/sys/", basedir, NULL); |
912541b0 KS |
742 | dir = opendir(path); |
743 | if (dir == NULL) | |
744 | return -1; | |
745 | for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) { | |
746 | if (dent->d_name[0] == '.') | |
747 | continue; | |
748 | if (!match_subsystem(udev_enumerate, subsystem != NULL ? subsystem : dent->d_name)) | |
749 | continue; | |
750 | scan_dir_and_add_devices(udev_enumerate, basedir, dent->d_name, subdir); | |
751 | } | |
752 | closedir(dir); | |
753 | return 0; | |
eb1f0e66 KS |
754 | } |
755 | ||
a7c140c7 KS |
756 | /** |
757 | * udev_enumerate_add_syspath: | |
758 | * @udev_enumerate: context | |
759 | * @syspath: path of a device | |
760 | * | |
761 | * Add a device to the list of devices, to retrieve it back sorted in dependency order. | |
762 | * | |
763 | * Returns: 0 on success, otherwise a negative error value. | |
764 | */ | |
54cf0b7f | 765 | _public_ int udev_enumerate_add_syspath(struct udev_enumerate *udev_enumerate, const char *syspath) |
13ddea81 | 766 | { |
912541b0 | 767 | struct udev_device *udev_device; |
13ddea81 | 768 | |
912541b0 KS |
769 | if (udev_enumerate == NULL) |
770 | return -EINVAL; | |
771 | if (syspath == NULL) | |
772 | return 0; | |
773 | /* resolve to real syspath */ | |
774 | udev_device = udev_device_new_from_syspath(udev_enumerate->udev, syspath); | |
775 | if (udev_device == NULL) | |
776 | return -EINVAL; | |
777 | syspath_add(udev_enumerate, udev_device_get_syspath(udev_device)); | |
778 | udev_device_unref(udev_device); | |
779 | return 0; | |
6f67f1df KS |
780 | } |
781 | ||
b05211fa | 782 | static int scan_devices_tags(struct udev_enumerate *udev_enumerate) |
eb1f0e66 | 783 | { |
912541b0 KS |
784 | struct udev_list_entry *list_entry; |
785 | ||
786 | /* scan only tagged devices, use tags reverse-index, instead of searching all devices in /sys */ | |
787 | udev_list_entry_foreach(list_entry, udev_list_get_entry(&udev_enumerate->tags_match_list)) { | |
788 | DIR *dir; | |
789 | struct dirent *dent; | |
790 | char path[UTIL_PATH_SIZE]; | |
791 | ||
d5a89d7d | 792 | strscpyl(path, sizeof(path), "/run/udev/tags/", udev_list_entry_get_name(list_entry), NULL); |
912541b0 KS |
793 | dir = opendir(path); |
794 | if (dir == NULL) | |
795 | continue; | |
796 | for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) { | |
797 | struct udev_device *dev; | |
798 | ||
799 | if (dent->d_name[0] == '.') | |
800 | continue; | |
801 | ||
dbf61afb | 802 | dev = udev_device_new_from_device_id(udev_enumerate->udev, dent->d_name); |
912541b0 KS |
803 | if (dev == NULL) |
804 | continue; | |
805 | ||
806 | if (!match_subsystem(udev_enumerate, udev_device_get_subsystem(dev))) | |
807 | goto nomatch; | |
808 | if (!match_sysname(udev_enumerate, udev_device_get_sysname(dev))) | |
809 | goto nomatch; | |
810 | if (!match_parent(udev_enumerate, dev)) | |
811 | goto nomatch; | |
812 | if (!match_property(udev_enumerate, dev)) | |
813 | goto nomatch; | |
814 | if (!match_sysattr(udev_enumerate, dev)) | |
815 | goto nomatch; | |
816 | ||
817 | syspath_add(udev_enumerate, udev_device_get_syspath(dev)); | |
19e47d97 | 818 | nomatch: |
912541b0 KS |
819 | udev_device_unref(dev); |
820 | } | |
821 | closedir(dir); | |
822 | } | |
823 | return 0; | |
b05211fa KS |
824 | } |
825 | ||
826 | static int parent_add_child(struct udev_enumerate *enumerate, const char *path) | |
827 | { | |
912541b0 | 828 | struct udev_device *dev; |
b05211fa | 829 | |
912541b0 KS |
830 | dev = udev_device_new_from_syspath(enumerate->udev, path); |
831 | if (dev == NULL) | |
832 | return -ENODEV; | |
b05211fa | 833 | |
912541b0 KS |
834 | if (!match_subsystem(enumerate, udev_device_get_subsystem(dev))) |
835 | return 0; | |
836 | if (!match_sysname(enumerate, udev_device_get_sysname(dev))) | |
837 | return 0; | |
838 | if (!match_property(enumerate, dev)) | |
839 | return 0; | |
840 | if (!match_sysattr(enumerate, dev)) | |
841 | return 0; | |
b05211fa | 842 | |
912541b0 KS |
843 | syspath_add(enumerate, udev_device_get_syspath(dev)); |
844 | udev_device_unref(dev); | |
845 | return 1; | |
b05211fa KS |
846 | } |
847 | ||
848 | static int parent_crawl_children(struct udev_enumerate *enumerate, const char *path, int maxdepth) | |
849 | { | |
912541b0 KS |
850 | DIR *d; |
851 | struct dirent *dent; | |
b05211fa | 852 | |
912541b0 KS |
853 | d = opendir(path); |
854 | if (d == NULL) | |
855 | return -errno; | |
b05211fa | 856 | |
912541b0 KS |
857 | for (dent = readdir(d); dent != NULL; dent = readdir(d)) { |
858 | char *child; | |
b05211fa | 859 | |
912541b0 KS |
860 | if (dent->d_name[0] == '.') |
861 | continue; | |
862 | if (dent->d_type != DT_DIR) | |
863 | continue; | |
864 | if (asprintf(&child, "%s/%s", path, dent->d_name) < 0) | |
865 | continue; | |
866 | parent_add_child(enumerate, child); | |
867 | if (maxdepth > 0) | |
868 | parent_crawl_children(enumerate, child, maxdepth-1); | |
869 | free(child); | |
870 | } | |
b05211fa | 871 | |
912541b0 KS |
872 | closedir(d); |
873 | return 0; | |
b05211fa KS |
874 | } |
875 | ||
6ed03d1e KS |
876 | static int scan_devices_children(struct udev_enumerate *enumerate) |
877 | { | |
912541b0 | 878 | const char *path; |
6ed03d1e | 879 | |
912541b0 KS |
880 | path = udev_device_get_syspath(enumerate->parent_match); |
881 | parent_add_child(enumerate, path); | |
882 | return parent_crawl_children(enumerate, path, 256); | |
6ed03d1e KS |
883 | } |
884 | ||
b05211fa KS |
885 | static int scan_devices_all(struct udev_enumerate *udev_enumerate) |
886 | { | |
912541b0 | 887 | struct stat statbuf; |
b05211fa | 888 | |
6ada823a | 889 | if (stat("/sys/subsystem", &statbuf) == 0) { |
912541b0 | 890 | /* we have /subsystem/, forget all the old stuff */ |
912541b0 KS |
891 | scan_dir(udev_enumerate, "subsystem", "devices", NULL); |
892 | } else { | |
912541b0 | 893 | scan_dir(udev_enumerate, "bus", "devices", NULL); |
912541b0 KS |
894 | scan_dir(udev_enumerate, "class", NULL, NULL); |
895 | } | |
896 | return 0; | |
eb1f0e66 | 897 | } |
bc8184ed | 898 | |
b05211fa KS |
899 | /** |
900 | * udev_enumerate_scan_devices: | |
901 | * @udev_enumerate: udev enumeration context | |
902 | * | |
21dbe43a KS |
903 | * Scan /sys for all devices which match the given filters. No matches |
904 | * will return all currently available devices. | |
905 | * | |
b05211fa KS |
906 | * Returns: 0 on success, otherwise a negative error value. |
907 | **/ | |
54cf0b7f | 908 | _public_ int udev_enumerate_scan_devices(struct udev_enumerate *udev_enumerate) |
b05211fa | 909 | { |
912541b0 KS |
910 | if (udev_enumerate == NULL) |
911 | return -EINVAL; | |
b05211fa | 912 | |
912541b0 KS |
913 | /* efficiently lookup tags only, we maintain a reverse-index */ |
914 | if (udev_list_get_entry(&udev_enumerate->tags_match_list) != NULL) | |
915 | return scan_devices_tags(udev_enumerate); | |
b05211fa | 916 | |
912541b0 KS |
917 | /* walk the subtree of one parent device only */ |
918 | if (udev_enumerate->parent_match != NULL) | |
919 | return scan_devices_children(udev_enumerate); | |
b05211fa | 920 | |
912541b0 KS |
921 | /* scan devices of all subsystems */ |
922 | return scan_devices_all(udev_enumerate); | |
b05211fa KS |
923 | } |
924 | ||
438d4c3c KS |
925 | /** |
926 | * udev_enumerate_scan_subsystems: | |
927 | * @udev_enumerate: udev enumeration context | |
928 | * | |
21dbe43a KS |
929 | * Scan /sys for all kernel subsystems, including buses, classes, drivers. |
930 | * | |
a7c140c7 | 931 | * Returns: 0 on success, otherwise a negative error value. |
438d4c3c | 932 | **/ |
54cf0b7f | 933 | _public_ int udev_enumerate_scan_subsystems(struct udev_enumerate *udev_enumerate) |
bc8184ed | 934 | { |
912541b0 KS |
935 | struct stat statbuf; |
936 | const char *subsysdir; | |
937 | ||
938 | if (udev_enumerate == NULL) | |
939 | return -EINVAL; | |
940 | ||
941 | /* all kernel modules */ | |
baa30fbc | 942 | if (match_subsystem(udev_enumerate, "module")) |
912541b0 | 943 | scan_dir_and_add_devices(udev_enumerate, "module", NULL, NULL); |
912541b0 | 944 | |
6ada823a | 945 | if (stat("/sys/subsystem", &statbuf) == 0) |
912541b0 KS |
946 | subsysdir = "subsystem"; |
947 | else | |
948 | subsysdir = "bus"; | |
949 | ||
950 | /* all subsystems (only buses support coldplug) */ | |
baa30fbc | 951 | if (match_subsystem(udev_enumerate, "subsystem")) |
912541b0 | 952 | scan_dir_and_add_devices(udev_enumerate, subsysdir, NULL, NULL); |
912541b0 KS |
953 | |
954 | /* all subsystem drivers */ | |
baa30fbc | 955 | if (match_subsystem(udev_enumerate, "drivers")) |
912541b0 | 956 | scan_dir(udev_enumerate, subsysdir, "drivers", "drivers"); |
912541b0 | 957 | return 0; |
bc8184ed | 958 | } |