]>
Commit | Line | Data |
---|---|---|
53e1b683 | 1 | /* SPDX-License-Identifier: LGPL-2.1+ */ |
eb1f0e66 | 2 | |
eb1f0e66 | 3 | #include <dirent.h> |
07630cea | 4 | #include <errno.h> |
c97f839e | 5 | #include <fnmatch.h> |
871a36bd | 6 | #include <stdbool.h> |
07630cea LP |
7 | #include <stddef.h> |
8 | #include <stdio.h> | |
9 | #include <stdlib.h> | |
10 | #include <string.h> | |
eb1f0e66 KS |
11 | #include <sys/stat.h> |
12 | ||
b4bbcaa9 | 13 | #include "libudev.h" |
c32eb440 | 14 | #include "sd-device.h" |
c32eb440 | 15 | |
b5efdb8a | 16 | #include "alloc-util.h" |
07630cea LP |
17 | #include "device-enumerator-private.h" |
18 | #include "device-util.h" | |
19 | #include "libudev-device-internal.h" | |
eb1f0e66 | 20 | |
ce1d6d7f KS |
21 | /** |
22 | * SECTION:libudev-enumerate | |
23 | * @short_description: lookup and sort sys devices | |
24 | * | |
25 | * Lookup devices in the sys filesystem, filter devices by properties, | |
a7c140c7 | 26 | * and return a sorted list of devices. |
ce1d6d7f KS |
27 | */ |
28 | ||
29 | /** | |
30 | * udev_enumerate: | |
31 | * | |
32 | * Opaque object representing one device lookup/sort context. | |
33 | */ | |
bf7ad0ea | 34 | struct udev_enumerate { |
912541b0 | 35 | struct udev *udev; |
3c6ac219 | 36 | unsigned n_ref; |
dcf557f7 | 37 | struct udev_list *devices_list; |
912541b0 | 38 | bool devices_uptodate:1; |
c32eb440 TG |
39 | |
40 | sd_device_enumerator *enumerator; | |
bf7ad0ea KS |
41 | }; |
42 | ||
c97f839e KS |
43 | /** |
44 | * udev_enumerate_new: | |
45 | * @udev: udev library context | |
46 | * | |
21dbe43a KS |
47 | * Create an enumeration context to scan /sys. |
48 | * | |
49 | * Returns: an enumeration context. | |
c97f839e | 50 | **/ |
c32eb440 | 51 | _public_ struct udev_enumerate *udev_enumerate_new(struct udev *udev) { |
60fdee32 | 52 | _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL; |
dcf557f7 | 53 | _cleanup_(udev_list_freep) struct udev_list *list = NULL; |
60fdee32 | 54 | struct udev_enumerate *udev_enumerate; |
c32eb440 TG |
55 | int r; |
56 | ||
60fdee32 | 57 | r = sd_device_enumerator_new(&e); |
fd05c424 YW |
58 | if (r < 0) |
59 | return_with_errno(NULL, r); | |
c32eb440 | 60 | |
60fdee32 | 61 | r = sd_device_enumerator_allow_uninitialized(e); |
fd05c424 YW |
62 | if (r < 0) |
63 | return_with_errno(NULL, r); | |
dee5e0b6 | 64 | |
dcf557f7 YW |
65 | list = udev_list_new(false); |
66 | if (!list) | |
67 | return_with_errno(NULL, ENOMEM); | |
68 | ||
60fdee32 | 69 | udev_enumerate = new(struct udev_enumerate, 1); |
fd05c424 YW |
70 | if (!udev_enumerate) |
71 | return_with_errno(NULL, ENOMEM); | |
c32eb440 | 72 | |
60fdee32 YW |
73 | *udev_enumerate = (struct udev_enumerate) { |
74 | .udev = udev, | |
75 | .n_ref = 1, | |
76 | .enumerator = TAKE_PTR(e), | |
dcf557f7 | 77 | .devices_list = TAKE_PTR(list), |
60fdee32 | 78 | }; |
c32eb440 | 79 | |
60fdee32 | 80 | return udev_enumerate; |
c97f839e KS |
81 | } |
82 | ||
3c6ac219 YW |
83 | static struct udev_enumerate *udev_enumerate_free(struct udev_enumerate *udev_enumerate) { |
84 | assert(udev_enumerate); | |
85 | ||
dcf557f7 | 86 | udev_list_free(udev_enumerate->devices_list); |
3c6ac219 YW |
87 | sd_device_enumerator_unref(udev_enumerate->enumerator); |
88 | return mfree(udev_enumerate); | |
89 | } | |
90 | ||
ce1d6d7f KS |
91 | /** |
92 | * udev_enumerate_ref: | |
93 | * @udev_enumerate: context | |
94 | * | |
95 | * Take a reference of a enumeration context. | |
96 | * | |
97 | * Returns: the passed enumeration context | |
98 | **/ | |
bf7ad0ea | 99 | |
ce1d6d7f KS |
100 | /** |
101 | * udev_enumerate_unref: | |
102 | * @udev_enumerate: context | |
103 | * | |
104 | * Drop a reference of an enumeration context. If the refcount reaches zero, | |
105 | * all resources of the enumeration context will be released. | |
c1959569 | 106 | * |
725d7e6c | 107 | * Returns: #NULL |
ce1d6d7f | 108 | **/ |
3c6ac219 | 109 | DEFINE_PUBLIC_TRIVIAL_REF_UNREF_FUNC(struct udev_enumerate, udev_enumerate, udev_enumerate_free); |
bf7ad0ea | 110 | |
ce1d6d7f KS |
111 | /** |
112 | * udev_enumerate_get_udev: | |
113 | * @udev_enumerate: context | |
114 | * | |
21dbe43a KS |
115 | * Get the udev library context. |
116 | * | |
117 | * Returns: a pointer to the context. | |
ce1d6d7f | 118 | */ |
c32eb440 TG |
119 | _public_ struct udev *udev_enumerate_get_udev(struct udev_enumerate *udev_enumerate) { |
120 | assert_return_errno(udev_enumerate, NULL, EINVAL); | |
3bf76824 | 121 | |
c32eb440 | 122 | return udev_enumerate->udev; |
3bf76824 LP |
123 | } |
124 | ||
a7c140c7 KS |
125 | /** |
126 | * udev_enumerate_get_list_entry: | |
127 | * @udev_enumerate: context | |
128 | * | |
21dbe43a KS |
129 | * Get the first entry of the sorted list of device paths. |
130 | * | |
131 | * Returns: a udev_list_entry. | |
a7c140c7 | 132 | */ |
c32eb440 | 133 | _public_ struct udev_list_entry *udev_enumerate_get_list_entry(struct udev_enumerate *udev_enumerate) { |
309f631d LP |
134 | struct udev_list_entry *e; |
135 | ||
c32eb440 TG |
136 | assert_return_errno(udev_enumerate, NULL, EINVAL); |
137 | ||
912541b0 | 138 | if (!udev_enumerate->devices_uptodate) { |
c32eb440 | 139 | sd_device *device; |
912541b0 | 140 | |
dcf557f7 | 141 | udev_list_cleanup(udev_enumerate->devices_list); |
912541b0 | 142 | |
c32eb440 TG |
143 | FOREACH_DEVICE_AND_SUBSYSTEM(udev_enumerate->enumerator, device) { |
144 | const char *syspath; | |
145 | int r; | |
912541b0 | 146 | |
c32eb440 | 147 | r = sd_device_get_syspath(device, &syspath); |
fd05c424 YW |
148 | if (r < 0) |
149 | return_with_errno(NULL, r); | |
912541b0 | 150 | |
dcf557f7 | 151 | if (!udev_list_entry_add(udev_enumerate->devices_list, syspath, NULL)) |
3f14e721 | 152 | return_with_errno(NULL, ENOMEM); |
912541b0 | 153 | } |
912541b0 KS |
154 | |
155 | udev_enumerate->devices_uptodate = true; | |
156 | } | |
c32eb440 | 157 | |
dcf557f7 | 158 | e = udev_list_get_entry(udev_enumerate->devices_list); |
309f631d | 159 | if (!e) |
fd05c424 | 160 | return_with_errno(NULL, ENODATA); |
309f631d LP |
161 | |
162 | return e; | |
bf7ad0ea KS |
163 | } |
164 | ||
a7c140c7 KS |
165 | /** |
166 | * udev_enumerate_add_match_subsystem: | |
167 | * @udev_enumerate: context | |
168 | * @subsystem: filter for a subsystem of the device to include in the list | |
169 | * | |
21dbe43a KS |
170 | * Match only devices belonging to a certain kernel subsystem. |
171 | * | |
a7c140c7 KS |
172 | * Returns: 0 on success, otherwise a negative error value. |
173 | */ | |
c32eb440 | 174 | _public_ int udev_enumerate_add_match_subsystem(struct udev_enumerate *udev_enumerate, const char *subsystem) { |
56fa3682 YW |
175 | int r; |
176 | ||
c32eb440 TG |
177 | assert_return(udev_enumerate, -EINVAL); |
178 | ||
54f0b4d9 TG |
179 | if (!subsystem) |
180 | return 0; | |
181 | ||
56fa3682 YW |
182 | r = sd_device_enumerator_add_match_subsystem(udev_enumerate->enumerator, subsystem, true); |
183 | if (r < 0) | |
184 | return r; | |
185 | ||
186 | udev_enumerate->devices_uptodate = false; | |
187 | return 0; | |
c97f839e KS |
188 | } |
189 | ||
a7c140c7 KS |
190 | /** |
191 | * udev_enumerate_add_nomatch_subsystem: | |
192 | * @udev_enumerate: context | |
193 | * @subsystem: filter for a subsystem of the device to exclude from the list | |
194 | * | |
21dbe43a KS |
195 | * Match only devices not belonging to a certain kernel subsystem. |
196 | * | |
a7c140c7 KS |
197 | * Returns: 0 on success, otherwise a negative error value. |
198 | */ | |
c32eb440 | 199 | _public_ int udev_enumerate_add_nomatch_subsystem(struct udev_enumerate *udev_enumerate, const char *subsystem) { |
56fa3682 YW |
200 | int r; |
201 | ||
c32eb440 TG |
202 | assert_return(udev_enumerate, -EINVAL); |
203 | ||
54f0b4d9 TG |
204 | if (!subsystem) |
205 | return 0; | |
206 | ||
56fa3682 YW |
207 | r = sd_device_enumerator_add_match_subsystem(udev_enumerate->enumerator, subsystem, false); |
208 | if (r < 0) | |
209 | return r; | |
210 | ||
211 | udev_enumerate->devices_uptodate = false; | |
212 | return 0; | |
c97f839e KS |
213 | } |
214 | ||
a7c140c7 KS |
215 | /** |
216 | * udev_enumerate_add_match_sysattr: | |
217 | * @udev_enumerate: context | |
218 | * @sysattr: filter for a sys attribute at the device to include in the list | |
219 | * @value: optional value of the sys attribute | |
220 | * | |
21dbe43a KS |
221 | * Match only devices with a certain /sys device attribute. |
222 | * | |
a7c140c7 KS |
223 | * Returns: 0 on success, otherwise a negative error value. |
224 | */ | |
c32eb440 | 225 | _public_ int udev_enumerate_add_match_sysattr(struct udev_enumerate *udev_enumerate, const char *sysattr, const char *value) { |
56fa3682 YW |
226 | int r; |
227 | ||
c32eb440 TG |
228 | assert_return(udev_enumerate, -EINVAL); |
229 | ||
54f0b4d9 TG |
230 | if (!sysattr) |
231 | return 0; | |
232 | ||
56fa3682 YW |
233 | r = sd_device_enumerator_add_match_sysattr(udev_enumerate->enumerator, sysattr, value, true); |
234 | if (r < 0) | |
235 | return r; | |
236 | ||
237 | udev_enumerate->devices_uptodate = false; | |
238 | return 0; | |
c97f839e KS |
239 | } |
240 | ||
a7c140c7 KS |
241 | /** |
242 | * udev_enumerate_add_nomatch_sysattr: | |
243 | * @udev_enumerate: context | |
244 | * @sysattr: filter for a sys attribute at the device to exclude from the list | |
245 | * @value: optional value of the sys attribute | |
246 | * | |
21dbe43a KS |
247 | * Match only devices not having a certain /sys device attribute. |
248 | * | |
a7c140c7 KS |
249 | * Returns: 0 on success, otherwise a negative error value. |
250 | */ | |
c32eb440 | 251 | _public_ int udev_enumerate_add_nomatch_sysattr(struct udev_enumerate *udev_enumerate, const char *sysattr, const char *value) { |
56fa3682 YW |
252 | int r; |
253 | ||
c32eb440 | 254 | assert_return(udev_enumerate, -EINVAL); |
c97f839e | 255 | |
54f0b4d9 TG |
256 | if (!sysattr) |
257 | return 0; | |
258 | ||
56fa3682 YW |
259 | r = sd_device_enumerator_add_match_sysattr(udev_enumerate->enumerator, sysattr, value, false); |
260 | if (r < 0) | |
261 | return r; | |
262 | ||
263 | udev_enumerate->devices_uptodate = false; | |
264 | return 0; | |
c97f839e KS |
265 | } |
266 | ||
a7c140c7 KS |
267 | /** |
268 | * udev_enumerate_add_match_property: | |
269 | * @udev_enumerate: context | |
270 | * @property: filter for a property of the device to include in the list | |
271 | * @value: value of the property | |
272 | * | |
21dbe43a KS |
273 | * Match only devices with a certain property. |
274 | * | |
a7c140c7 KS |
275 | * Returns: 0 on success, otherwise a negative error value. |
276 | */ | |
c32eb440 | 277 | _public_ int udev_enumerate_add_match_property(struct udev_enumerate *udev_enumerate, const char *property, const char *value) { |
56fa3682 YW |
278 | int r; |
279 | ||
c32eb440 TG |
280 | assert_return(udev_enumerate, -EINVAL); |
281 | ||
54f0b4d9 TG |
282 | if (!property) |
283 | return 0; | |
284 | ||
56fa3682 YW |
285 | r = sd_device_enumerator_add_match_property(udev_enumerate->enumerator, property, value); |
286 | if (r < 0) | |
287 | return r; | |
288 | ||
289 | udev_enumerate->devices_uptodate = false; | |
290 | return 0; | |
f0893502 KS |
291 | } |
292 | ||
28460195 KS |
293 | /** |
294 | * udev_enumerate_add_match_tag: | |
295 | * @udev_enumerate: context | |
296 | * @tag: filter for a tag of the device to include in the list | |
297 | * | |
21dbe43a KS |
298 | * Match only devices with a certain tag. |
299 | * | |
28460195 KS |
300 | * Returns: 0 on success, otherwise a negative error value. |
301 | */ | |
c32eb440 | 302 | _public_ int udev_enumerate_add_match_tag(struct udev_enumerate *udev_enumerate, const char *tag) { |
56fa3682 YW |
303 | int r; |
304 | ||
c32eb440 TG |
305 | assert_return(udev_enumerate, -EINVAL); |
306 | ||
54f0b4d9 TG |
307 | if (!tag) |
308 | return 0; | |
309 | ||
56fa3682 YW |
310 | r = sd_device_enumerator_add_match_tag(udev_enumerate->enumerator, tag); |
311 | if (r < 0) | |
312 | return r; | |
313 | ||
314 | udev_enumerate->devices_uptodate = false; | |
315 | return 0; | |
28460195 KS |
316 | } |
317 | ||
b05211fa KS |
318 | /** |
319 | * udev_enumerate_add_match_parent: | |
320 | * @udev_enumerate: context | |
a07e0114 | 321 | * @parent: parent device where to start searching |
b05211fa | 322 | * |
a07e0114 KS |
323 | * Return the devices on the subtree of one given device. The parent |
324 | * itself is included in the list. | |
b05211fa | 325 | * |
b05211fa KS |
326 | * Returns: 0 on success, otherwise a negative error value. |
327 | */ | |
c32eb440 | 328 | _public_ int udev_enumerate_add_match_parent(struct udev_enumerate *udev_enumerate, struct udev_device *parent) { |
56fa3682 YW |
329 | int r; |
330 | ||
c32eb440 TG |
331 | assert_return(udev_enumerate, -EINVAL); |
332 | ||
333 | if (!parent) | |
912541b0 | 334 | return 0; |
c32eb440 | 335 | |
56fa3682 YW |
336 | r = sd_device_enumerator_add_match_parent(udev_enumerate->enumerator, parent->device); |
337 | if (r < 0) | |
338 | return r; | |
339 | ||
340 | udev_enumerate->devices_uptodate = false; | |
341 | return 0; | |
b05211fa KS |
342 | } |
343 | ||
48a0170b KS |
344 | /** |
345 | * udev_enumerate_add_match_is_initialized: | |
346 | * @udev_enumerate: context | |
347 | * | |
348 | * Match only devices which udev has set up already. This makes | |
349 | * sure, that the device node permissions and context are properly set | |
350 | * and that network devices are fully renamed. | |
351 | * | |
352 | * Usually, devices which are found in the kernel but not already | |
353 | * handled by udev, have still pending events. Services should subscribe | |
354 | * to monitor events and wait for these devices to become ready, instead | |
355 | * of using uninitialized devices. | |
356 | * | |
357 | * For now, this will not affect devices which do not have a device node | |
358 | * and are not network interfaces. | |
359 | * | |
360 | * Returns: 0 on success, otherwise a negative error value. | |
361 | */ | |
c32eb440 | 362 | _public_ int udev_enumerate_add_match_is_initialized(struct udev_enumerate *udev_enumerate) { |
56fa3682 YW |
363 | int r; |
364 | ||
c32eb440 TG |
365 | assert_return(udev_enumerate, -EINVAL); |
366 | ||
56fa3682 YW |
367 | r = device_enumerator_add_match_is_initialized(udev_enumerate->enumerator); |
368 | if (r < 0) | |
369 | return r; | |
370 | ||
371 | udev_enumerate->devices_uptodate = false; | |
372 | return 0; | |
48a0170b KS |
373 | } |
374 | ||
cf5bd040 KS |
375 | /** |
376 | * udev_enumerate_add_match_sysname: | |
377 | * @udev_enumerate: context | |
378 | * @sysname: filter for the name of the device to include in the list | |
379 | * | |
21dbe43a KS |
380 | * Match only devices with a given /sys device name. |
381 | * | |
cf5bd040 KS |
382 | * Returns: 0 on success, otherwise a negative error value. |
383 | */ | |
c32eb440 | 384 | _public_ int udev_enumerate_add_match_sysname(struct udev_enumerate *udev_enumerate, const char *sysname) { |
56fa3682 YW |
385 | int r; |
386 | ||
c32eb440 | 387 | assert_return(udev_enumerate, -EINVAL); |
c97f839e | 388 | |
54f0b4d9 TG |
389 | if (!sysname) |
390 | return 0; | |
391 | ||
56fa3682 YW |
392 | r = sd_device_enumerator_add_match_sysname(udev_enumerate->enumerator, sysname); |
393 | if (r < 0) | |
394 | return r; | |
395 | ||
396 | udev_enumerate->devices_uptodate = false; | |
397 | return 0; | |
eb1f0e66 KS |
398 | } |
399 | ||
a7c140c7 KS |
400 | /** |
401 | * udev_enumerate_add_syspath: | |
402 | * @udev_enumerate: context | |
403 | * @syspath: path of a device | |
404 | * | |
405 | * Add a device to the list of devices, to retrieve it back sorted in dependency order. | |
406 | * | |
407 | * Returns: 0 on success, otherwise a negative error value. | |
408 | */ | |
c32eb440 | 409 | _public_ int udev_enumerate_add_syspath(struct udev_enumerate *udev_enumerate, const char *syspath) { |
4afd3348 | 410 | _cleanup_(sd_device_unrefp) sd_device *device = NULL; |
c32eb440 | 411 | int r; |
b05211fa | 412 | |
c32eb440 | 413 | assert_return(udev_enumerate, -EINVAL); |
b05211fa | 414 | |
c32eb440 TG |
415 | if (!syspath) |
416 | return 0; | |
6ed03d1e | 417 | |
c32eb440 TG |
418 | r = sd_device_new_from_syspath(&device, syspath); |
419 | if (r < 0) | |
420 | return r; | |
6ed03d1e | 421 | |
19c9df44 | 422 | r = device_enumerator_add_device(udev_enumerate->enumerator, device); |
c32eb440 TG |
423 | if (r < 0) |
424 | return r; | |
b05211fa | 425 | |
56fa3682 | 426 | udev_enumerate->devices_uptodate = false; |
912541b0 | 427 | return 0; |
eb1f0e66 | 428 | } |
bc8184ed | 429 | |
b05211fa KS |
430 | /** |
431 | * udev_enumerate_scan_devices: | |
432 | * @udev_enumerate: udev enumeration context | |
433 | * | |
21dbe43a KS |
434 | * Scan /sys for all devices which match the given filters. No matches |
435 | * will return all currently available devices. | |
436 | * | |
b05211fa KS |
437 | * Returns: 0 on success, otherwise a negative error value. |
438 | **/ | |
c32eb440 TG |
439 | _public_ int udev_enumerate_scan_devices(struct udev_enumerate *udev_enumerate) { |
440 | assert_return(udev_enumerate, -EINVAL); | |
b05211fa | 441 | |
c32eb440 | 442 | return device_enumerator_scan_devices(udev_enumerate->enumerator); |
b05211fa KS |
443 | } |
444 | ||
438d4c3c KS |
445 | /** |
446 | * udev_enumerate_scan_subsystems: | |
447 | * @udev_enumerate: udev enumeration context | |
448 | * | |
21dbe43a KS |
449 | * Scan /sys for all kernel subsystems, including buses, classes, drivers. |
450 | * | |
a7c140c7 | 451 | * Returns: 0 on success, otherwise a negative error value. |
438d4c3c | 452 | **/ |
c32eb440 TG |
453 | _public_ int udev_enumerate_scan_subsystems(struct udev_enumerate *udev_enumerate) { |
454 | assert_return(udev_enumerate, -EINVAL); | |
455 | ||
456 | return device_enumerator_scan_subsystems(udev_enumerate->enumerator); | |
bc8184ed | 457 | } |