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