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