]>
Commit | Line | Data |
---|---|---|
1 | /* SPDX-License-Identifier: LGPL-2.1+ */ | |
2 | ||
3 | #include <dirent.h> | |
4 | #include <errno.h> | |
5 | #include <fnmatch.h> | |
6 | #include <stdbool.h> | |
7 | #include <stddef.h> | |
8 | #include <stdio.h> | |
9 | #include <stdlib.h> | |
10 | #include <string.h> | |
11 | #include <sys/stat.h> | |
12 | ||
13 | #include "libudev.h" | |
14 | #include "sd-device.h" | |
15 | ||
16 | #include "alloc-util.h" | |
17 | #include "device-enumerator-private.h" | |
18 | #include "device-util.h" | |
19 | #include "libudev-device-internal.h" | |
20 | ||
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, | |
26 | * and return a sorted list of devices. | |
27 | */ | |
28 | ||
29 | /** | |
30 | * udev_enumerate: | |
31 | * | |
32 | * Opaque object representing one device lookup/sort context. | |
33 | */ | |
34 | struct udev_enumerate { | |
35 | struct udev *udev; | |
36 | unsigned n_ref; | |
37 | struct udev_list *devices_list; | |
38 | bool devices_uptodate:1; | |
39 | ||
40 | sd_device_enumerator *enumerator; | |
41 | }; | |
42 | ||
43 | /** | |
44 | * udev_enumerate_new: | |
45 | * @udev: udev library context | |
46 | * | |
47 | * Create an enumeration context to scan /sys. | |
48 | * | |
49 | * Returns: an enumeration context. | |
50 | **/ | |
51 | _public_ struct udev_enumerate *udev_enumerate_new(struct udev *udev) { | |
52 | _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL; | |
53 | _cleanup_(udev_list_freep) struct udev_list *list = NULL; | |
54 | struct udev_enumerate *udev_enumerate; | |
55 | int r; | |
56 | ||
57 | r = sd_device_enumerator_new(&e); | |
58 | if (r < 0) | |
59 | return_with_errno(NULL, r); | |
60 | ||
61 | r = sd_device_enumerator_allow_uninitialized(e); | |
62 | if (r < 0) | |
63 | return_with_errno(NULL, r); | |
64 | ||
65 | list = udev_list_new(false); | |
66 | if (!list) | |
67 | return_with_errno(NULL, ENOMEM); | |
68 | ||
69 | udev_enumerate = new(struct udev_enumerate, 1); | |
70 | if (!udev_enumerate) | |
71 | return_with_errno(NULL, ENOMEM); | |
72 | ||
73 | *udev_enumerate = (struct udev_enumerate) { | |
74 | .udev = udev, | |
75 | .n_ref = 1, | |
76 | .enumerator = TAKE_PTR(e), | |
77 | .devices_list = TAKE_PTR(list), | |
78 | }; | |
79 | ||
80 | return udev_enumerate; | |
81 | } | |
82 | ||
83 | static struct udev_enumerate *udev_enumerate_free(struct udev_enumerate *udev_enumerate) { | |
84 | assert(udev_enumerate); | |
85 | ||
86 | udev_list_free(udev_enumerate->devices_list); | |
87 | sd_device_enumerator_unref(udev_enumerate->enumerator); | |
88 | return mfree(udev_enumerate); | |
89 | } | |
90 | ||
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 | **/ | |
99 | ||
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. | |
106 | * | |
107 | * Returns: #NULL | |
108 | **/ | |
109 | DEFINE_PUBLIC_TRIVIAL_REF_UNREF_FUNC(struct udev_enumerate, udev_enumerate, udev_enumerate_free); | |
110 | ||
111 | /** | |
112 | * udev_enumerate_get_udev: | |
113 | * @udev_enumerate: context | |
114 | * | |
115 | * Get the udev library context. | |
116 | * | |
117 | * Returns: a pointer to the context. | |
118 | */ | |
119 | _public_ struct udev *udev_enumerate_get_udev(struct udev_enumerate *udev_enumerate) { | |
120 | assert_return_errno(udev_enumerate, NULL, EINVAL); | |
121 | ||
122 | return udev_enumerate->udev; | |
123 | } | |
124 | ||
125 | /** | |
126 | * udev_enumerate_get_list_entry: | |
127 | * @udev_enumerate: context | |
128 | * | |
129 | * Get the first entry of the sorted list of device paths. | |
130 | * | |
131 | * Returns: a udev_list_entry. | |
132 | */ | |
133 | _public_ struct udev_list_entry *udev_enumerate_get_list_entry(struct udev_enumerate *udev_enumerate) { | |
134 | struct udev_list_entry *e; | |
135 | ||
136 | assert_return_errno(udev_enumerate, NULL, EINVAL); | |
137 | ||
138 | if (!udev_enumerate->devices_uptodate) { | |
139 | sd_device *device; | |
140 | ||
141 | udev_list_cleanup(udev_enumerate->devices_list); | |
142 | ||
143 | FOREACH_DEVICE_AND_SUBSYSTEM(udev_enumerate->enumerator, device) { | |
144 | const char *syspath; | |
145 | int r; | |
146 | ||
147 | r = sd_device_get_syspath(device, &syspath); | |
148 | if (r < 0) | |
149 | return_with_errno(NULL, r); | |
150 | ||
151 | if (!udev_list_entry_add(udev_enumerate->devices_list, syspath, NULL)) | |
152 | return_with_errno(NULL, ENOMEM); | |
153 | } | |
154 | ||
155 | udev_enumerate->devices_uptodate = true; | |
156 | } | |
157 | ||
158 | e = udev_list_get_entry(udev_enumerate->devices_list); | |
159 | if (!e) | |
160 | return_with_errno(NULL, ENODATA); | |
161 | ||
162 | return e; | |
163 | } | |
164 | ||
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 | * | |
170 | * Match only devices belonging to a certain kernel subsystem. | |
171 | * | |
172 | * Returns: 0 on success, otherwise a negative error value. | |
173 | */ | |
174 | _public_ int udev_enumerate_add_match_subsystem(struct udev_enumerate *udev_enumerate, const char *subsystem) { | |
175 | int r; | |
176 | ||
177 | assert_return(udev_enumerate, -EINVAL); | |
178 | ||
179 | if (!subsystem) | |
180 | return 0; | |
181 | ||
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; | |
188 | } | |
189 | ||
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 | * | |
195 | * Match only devices not belonging to a certain kernel subsystem. | |
196 | * | |
197 | * Returns: 0 on success, otherwise a negative error value. | |
198 | */ | |
199 | _public_ int udev_enumerate_add_nomatch_subsystem(struct udev_enumerate *udev_enumerate, const char *subsystem) { | |
200 | int r; | |
201 | ||
202 | assert_return(udev_enumerate, -EINVAL); | |
203 | ||
204 | if (!subsystem) | |
205 | return 0; | |
206 | ||
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; | |
213 | } | |
214 | ||
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 | * | |
221 | * Match only devices with a certain /sys device attribute. | |
222 | * | |
223 | * Returns: 0 on success, otherwise a negative error value. | |
224 | */ | |
225 | _public_ int udev_enumerate_add_match_sysattr(struct udev_enumerate *udev_enumerate, const char *sysattr, const char *value) { | |
226 | int r; | |
227 | ||
228 | assert_return(udev_enumerate, -EINVAL); | |
229 | ||
230 | if (!sysattr) | |
231 | return 0; | |
232 | ||
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; | |
239 | } | |
240 | ||
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 | * | |
247 | * Match only devices not having a certain /sys device attribute. | |
248 | * | |
249 | * Returns: 0 on success, otherwise a negative error value. | |
250 | */ | |
251 | _public_ int udev_enumerate_add_nomatch_sysattr(struct udev_enumerate *udev_enumerate, const char *sysattr, const char *value) { | |
252 | int r; | |
253 | ||
254 | assert_return(udev_enumerate, -EINVAL); | |
255 | ||
256 | if (!sysattr) | |
257 | return 0; | |
258 | ||
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; | |
265 | } | |
266 | ||
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 | * | |
273 | * Match only devices with a certain property. | |
274 | * | |
275 | * Returns: 0 on success, otherwise a negative error value. | |
276 | */ | |
277 | _public_ int udev_enumerate_add_match_property(struct udev_enumerate *udev_enumerate, const char *property, const char *value) { | |
278 | int r; | |
279 | ||
280 | assert_return(udev_enumerate, -EINVAL); | |
281 | ||
282 | if (!property) | |
283 | return 0; | |
284 | ||
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; | |
291 | } | |
292 | ||
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 | * | |
298 | * Match only devices with a certain tag. | |
299 | * | |
300 | * Returns: 0 on success, otherwise a negative error value. | |
301 | */ | |
302 | _public_ int udev_enumerate_add_match_tag(struct udev_enumerate *udev_enumerate, const char *tag) { | |
303 | int r; | |
304 | ||
305 | assert_return(udev_enumerate, -EINVAL); | |
306 | ||
307 | if (!tag) | |
308 | return 0; | |
309 | ||
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; | |
316 | } | |
317 | ||
318 | /** | |
319 | * udev_enumerate_add_match_parent: | |
320 | * @udev_enumerate: context | |
321 | * @parent: parent device where to start searching | |
322 | * | |
323 | * Return the devices on the subtree of one given device. The parent | |
324 | * itself is included in the list. | |
325 | * | |
326 | * Returns: 0 on success, otherwise a negative error value. | |
327 | */ | |
328 | _public_ int udev_enumerate_add_match_parent(struct udev_enumerate *udev_enumerate, struct udev_device *parent) { | |
329 | int r; | |
330 | ||
331 | assert_return(udev_enumerate, -EINVAL); | |
332 | ||
333 | if (!parent) | |
334 | return 0; | |
335 | ||
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; | |
342 | } | |
343 | ||
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 | */ | |
362 | _public_ int udev_enumerate_add_match_is_initialized(struct udev_enumerate *udev_enumerate) { | |
363 | int r; | |
364 | ||
365 | assert_return(udev_enumerate, -EINVAL); | |
366 | ||
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; | |
373 | } | |
374 | ||
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 | * | |
380 | * Match only devices with a given /sys device name. | |
381 | * | |
382 | * Returns: 0 on success, otherwise a negative error value. | |
383 | */ | |
384 | _public_ int udev_enumerate_add_match_sysname(struct udev_enumerate *udev_enumerate, const char *sysname) { | |
385 | int r; | |
386 | ||
387 | assert_return(udev_enumerate, -EINVAL); | |
388 | ||
389 | if (!sysname) | |
390 | return 0; | |
391 | ||
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; | |
398 | } | |
399 | ||
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 | */ | |
409 | _public_ int udev_enumerate_add_syspath(struct udev_enumerate *udev_enumerate, const char *syspath) { | |
410 | _cleanup_(sd_device_unrefp) sd_device *device = NULL; | |
411 | int r; | |
412 | ||
413 | assert_return(udev_enumerate, -EINVAL); | |
414 | ||
415 | if (!syspath) | |
416 | return 0; | |
417 | ||
418 | r = sd_device_new_from_syspath(&device, syspath); | |
419 | if (r < 0) | |
420 | return r; | |
421 | ||
422 | r = device_enumerator_add_device(udev_enumerate->enumerator, device); | |
423 | if (r < 0) | |
424 | return r; | |
425 | ||
426 | udev_enumerate->devices_uptodate = false; | |
427 | return 0; | |
428 | } | |
429 | ||
430 | /** | |
431 | * udev_enumerate_scan_devices: | |
432 | * @udev_enumerate: udev enumeration context | |
433 | * | |
434 | * Scan /sys for all devices which match the given filters. No matches | |
435 | * will return all currently available devices. | |
436 | * | |
437 | * Returns: 0 on success, otherwise a negative error value. | |
438 | **/ | |
439 | _public_ int udev_enumerate_scan_devices(struct udev_enumerate *udev_enumerate) { | |
440 | assert_return(udev_enumerate, -EINVAL); | |
441 | ||
442 | return device_enumerator_scan_devices(udev_enumerate->enumerator); | |
443 | } | |
444 | ||
445 | /** | |
446 | * udev_enumerate_scan_subsystems: | |
447 | * @udev_enumerate: udev enumeration context | |
448 | * | |
449 | * Scan /sys for all kernel subsystems, including buses, classes, drivers. | |
450 | * | |
451 | * Returns: 0 on success, otherwise a negative error value. | |
452 | **/ | |
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); | |
457 | } |