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