chiark / gitweb /
libudev: doc - add section headers
[elogind.git] / libudev / libudev-enumerate.c
1 /*
2  * libudev - interface to udev device information
3  *
4  * Copyright (C) 2008 Kay Sievers <kay.sievers@vrfy.org>
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  */
11
12 #include <stdio.h>
13 #include <stdlib.h>
14 #include <stddef.h>
15 #include <unistd.h>
16 #include <errno.h>
17 #include <string.h>
18 #include <dirent.h>
19 #include <fnmatch.h>
20 #include <sys/stat.h>
21
22 #include "libudev.h"
23 #include "libudev-private.h"
24
25 static int devices_sort(struct udev_enumerate *udev_enumerate);
26
27 /**
28  * SECTION:libudev-enumerate
29  * @short_description: lookup and sort sys devices
30  *
31  * Lookup devices in the sys filesystem, filter devices by properties,
32  * and return a sorted list of matching devices.
33  */
34
35 /**
36  * udev_enumerate:
37  *
38  * Opaque object representing one device lookup/sort context.
39  */
40 struct udev_enumerate {
41         struct udev *udev;
42         int refcount;
43         struct udev_list_node sysattr_match_list;
44         struct udev_list_node sysattr_nomatch_list;
45         struct udev_list_node subsystem_match_list;
46         struct udev_list_node subsystem_nomatch_list;
47         struct udev_list_node properties_match_list;
48         struct udev_list_node devices_list;
49         int devices_sorted;
50 };
51
52 /**
53  * udev_enumerate_new:
54  * @udev: udev library context
55  *
56  * Returns: an enumeration context
57  **/
58 struct udev_enumerate *udev_enumerate_new(struct udev *udev)
59 {
60         struct udev_enumerate *udev_enumerate;
61
62         udev_enumerate = calloc(1, sizeof(struct udev_enumerate));
63         if (udev_enumerate == NULL)
64                 return NULL;
65         udev_enumerate->refcount = 1;
66         udev_enumerate->udev = udev;
67         udev_list_init(&udev_enumerate->devices_list);
68         udev_list_init(&udev_enumerate->sysattr_match_list);
69         udev_list_init(&udev_enumerate->sysattr_nomatch_list);
70         udev_list_init(&udev_enumerate->subsystem_match_list);
71         udev_list_init(&udev_enumerate->subsystem_nomatch_list);
72         udev_list_init(&udev_enumerate->properties_match_list);
73         return udev_enumerate;
74 }
75
76 /**
77  * udev_enumerate_ref:
78  * @udev_enumerate: context
79  *
80  * Take a reference of a enumeration context.
81  *
82  * Returns: the passed enumeration context
83  **/
84 struct udev_enumerate *udev_enumerate_ref(struct udev_enumerate *udev_enumerate)
85 {
86         if (udev_enumerate == NULL)
87                 return NULL;
88         udev_enumerate->refcount++;
89         return udev_enumerate;
90 }
91
92 /**
93  * udev_enumerate_unref:
94  * @udev_enumerate: context
95  *
96  * Drop a reference of an enumeration context. If the refcount reaches zero,
97  * all resources of the enumeration context will be released.
98  *
99  **/
100 void udev_enumerate_unref(struct udev_enumerate *udev_enumerate)
101 {
102         if (udev_enumerate == NULL)
103                 return;
104         udev_enumerate->refcount--;
105         if (udev_enumerate->refcount > 0)
106                 return;
107         udev_list_cleanup_entries(udev_enumerate->udev, &udev_enumerate->devices_list);
108         udev_list_cleanup_entries(udev_enumerate->udev, &udev_enumerate->sysattr_match_list);
109         udev_list_cleanup_entries(udev_enumerate->udev, &udev_enumerate->sysattr_nomatch_list);
110         udev_list_cleanup_entries(udev_enumerate->udev, &udev_enumerate->subsystem_match_list);
111         udev_list_cleanup_entries(udev_enumerate->udev, &udev_enumerate->subsystem_nomatch_list);
112         udev_list_cleanup_entries(udev_enumerate->udev, &udev_enumerate->properties_match_list);
113         free(udev_enumerate);
114 }
115
116 /**
117  * udev_enumerate_get_udev:
118  * @udev_enumerate: context
119  *
120  * Returns: the udev library context.
121  */
122 struct udev *udev_enumerate_get_udev(struct udev_enumerate *udev_enumerate)
123 {
124         if (udev_enumerate == NULL)
125                 return NULL;
126         return udev_enumerate->udev;
127 }
128
129 struct udev_list_entry *udev_enumerate_get_list_entry(struct udev_enumerate *udev_enumerate)
130 {
131         if (udev_enumerate == NULL)
132                 return NULL;
133         if (!udev_enumerate->devices_sorted)
134                 devices_sort(udev_enumerate);
135         return udev_list_get_entry(&udev_enumerate->devices_list);
136 }
137
138 int udev_enumerate_add_match_subsystem(struct udev_enumerate *udev_enumerate, const char *subsystem)
139 {
140         if (udev_enumerate == NULL)
141                 return -EINVAL;
142         if (subsystem == NULL)
143                 return 0;
144         if (udev_list_entry_add(udev_enumerate_get_udev(udev_enumerate),
145                                 &udev_enumerate->subsystem_match_list, subsystem, NULL, 1, 0) == NULL)
146                 return -ENOMEM;
147         return 0;
148 }
149
150 int udev_enumerate_add_nomatch_subsystem(struct udev_enumerate *udev_enumerate, const char *subsystem)
151 {
152         if (udev_enumerate == NULL)
153                 return -EINVAL;
154         if (subsystem == NULL)
155                 return 0;
156         if (udev_list_entry_add(udev_enumerate_get_udev(udev_enumerate),
157                                 &udev_enumerate->subsystem_nomatch_list, subsystem, NULL, 1, 0) == NULL)
158                 return -ENOMEM;
159         return 0;
160 }
161
162 int udev_enumerate_add_match_sysattr(struct udev_enumerate *udev_enumerate, const char *sysattr, const char *value)
163 {
164         if (udev_enumerate == NULL)
165                 return -EINVAL;
166         if (sysattr == NULL)
167                 return 0;
168         if (udev_list_entry_add(udev_enumerate_get_udev(udev_enumerate),
169                            &udev_enumerate->sysattr_match_list, sysattr, value, 0, 0) == NULL)
170                 return -ENOMEM;
171         return 0;
172 }
173
174 int udev_enumerate_add_nomatch_sysattr(struct udev_enumerate *udev_enumerate, const char *sysattr, const char *value)
175 {
176         if (udev_enumerate == NULL)
177                 return -EINVAL;
178         if (sysattr == NULL)
179                 return 0;
180         if (udev_list_entry_add(udev_enumerate_get_udev(udev_enumerate),
181                            &udev_enumerate->sysattr_nomatch_list, sysattr, value, 0, 0) == NULL)
182                 return -ENOMEM;
183         return 0;
184 }
185
186 static int match_sysattr_value(struct udev *udev, const char *syspath, const char *sysattr, const char *match_val)
187 {
188         struct udev_device *device;
189         const char *val = NULL;
190         int match = 0;
191
192         device = udev_device_new_from_syspath(udev, syspath);
193         if (device == NULL)
194                 return -EINVAL;
195         val = udev_device_get_sysattr_value(device, sysattr);
196         if (val == NULL)
197                 goto exit;
198         if (match_val == NULL) {
199                 match = 1;
200                 goto exit;
201         }
202         if (fnmatch(match_val, val, 0) == 0) {
203                 match = 1;
204                 goto exit;
205         }
206 exit:
207         udev_device_unref(device);
208         return match;
209 }
210
211 int udev_enumerate_add_match_property(struct udev_enumerate *udev_enumerate, const char *property, const char *value)
212 {
213         if (udev_enumerate == NULL)
214                 return -EINVAL;
215         if (property == NULL)
216                 return 0;
217         if (udev_list_entry_add(udev_enumerate_get_udev(udev_enumerate),
218                                 &udev_enumerate->properties_match_list, property, value, 0, 0) == NULL)
219                 return -ENOMEM;
220         return 0;
221 }
222
223 static int match_sysattr(struct udev_enumerate *udev_enumerate, const char *syspath)
224 {
225         struct udev *udev = udev_enumerate_get_udev(udev_enumerate);
226         struct udev_list_entry *list_entry;
227
228         /* skip list */
229         udev_list_entry_foreach(list_entry, udev_list_get_entry(&udev_enumerate->sysattr_nomatch_list)) {
230                 if (match_sysattr_value(udev, syspath,
231                                      udev_list_entry_get_name(list_entry),
232                                      udev_list_entry_get_value(list_entry)))
233                         return 0;
234         }
235         /* include list */
236         if (udev_list_get_entry(&udev_enumerate->sysattr_match_list) != NULL) {
237                 udev_list_entry_foreach(list_entry, udev_list_get_entry(&udev_enumerate->sysattr_match_list)) {
238                         /* anything that does not match, will make it FALSE */
239                         if (!match_sysattr_value(udev, syspath,
240                                               udev_list_entry_get_name(list_entry),
241                                               udev_list_entry_get_value(list_entry)))
242                                 return 0;
243                 }
244                 return 1;
245         }
246         return 1;
247 }
248
249 static int match_property(struct udev_enumerate *udev_enumerate, const char *syspath)
250 {
251         struct udev_device *dev;
252         struct udev_list_entry *list_entry;
253         int match = 0;
254
255         /* no match always matches */
256         if (udev_list_get_entry(&udev_enumerate->properties_match_list) == NULL)
257                 return 1;
258
259         /* no device does not match */
260         dev = udev_device_new_from_syspath(udev_enumerate->udev, syspath);
261         if (dev == NULL)
262                 return 0;
263
264         /* loop over matches */
265         udev_list_entry_foreach(list_entry, udev_list_get_entry(&udev_enumerate->properties_match_list)) {
266                 const char *match_key = udev_list_entry_get_name(list_entry);
267                 const char *match_value = udev_list_entry_get_value(list_entry);
268                 struct udev_list_entry *property_entry;
269
270                 /* loop over device properties */
271                 udev_list_entry_foreach(property_entry, udev_device_get_properties_list_entry(dev)) {
272                         const char *dev_key = udev_list_entry_get_name(property_entry);
273                         const char *dev_value = udev_list_entry_get_value(property_entry);
274
275                         if (fnmatch(match_key, dev_key, 0) != 0)
276                                 continue;
277                         if (match_value == NULL && dev_value == NULL) {
278                                 match = 1;
279                                 goto out;
280                         }
281                         if (match_value == NULL || dev_value == NULL)
282                                 continue;
283                         if (fnmatch(match_value, dev_value, 0) == 0) {
284                                 match = 1;
285                                 goto out;
286                         }
287                 }
288         }
289 out:
290         udev_device_unref(dev);
291         return match;
292 }
293
294 static int scan_dir_and_add_devices(struct udev_enumerate *udev_enumerate,
295                                     const char *basedir, const char *subdir1, const char *subdir2)
296 {
297         struct udev *udev = udev_enumerate_get_udev(udev_enumerate);
298         char path[UTIL_PATH_SIZE];
299         size_t l;
300         char *s;
301         DIR *dir;
302         struct dirent *dent;
303
304         s = path;
305         l = util_strpcpyl(&s, sizeof(path), udev_get_sys_path(udev), "/", basedir, NULL);
306         if (subdir1 != NULL)
307                 l = util_strpcpyl(&s, l, "/", subdir1, NULL);
308         if (subdir2 != NULL)
309                 l = util_strpcpyl(&s, l, "/", subdir2, NULL);
310         dir = opendir(path);
311         if (dir == NULL)
312                 return -1;
313         for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) {
314                 char syspath[UTIL_PATH_SIZE];
315                 char filename[UTIL_PATH_SIZE];
316                 struct stat statbuf;
317
318                 if (dent->d_name[0] == '.')
319                         continue;
320                 util_strscpyl(syspath, sizeof(syspath), path, "/", dent->d_name, NULL);
321                 if (lstat(syspath, &statbuf) != 0)
322                         continue;
323                 if (S_ISREG(statbuf.st_mode))
324                         continue;
325                 if (S_ISLNK(statbuf.st_mode))
326                         util_resolve_sys_link(udev, syspath, sizeof(syspath));
327
328                 util_strscpyl(filename, sizeof(filename), syspath, "/uevent", NULL);
329                 if (stat(filename, &statbuf) != 0)
330                         continue;
331                 if (!match_sysattr(udev_enumerate, syspath))
332                         continue;
333                 if (!match_property(udev_enumerate, syspath))
334                         continue;
335                 udev_list_entry_add(udev, &udev_enumerate->devices_list, syspath, NULL, 1, 1);
336         }
337         closedir(dir);
338         return 0;
339 }
340
341 static int match_subsystem(struct udev_enumerate *udev_enumerate, const char *subsystem)
342 {
343         struct udev_list_entry *list_entry;
344
345         udev_list_entry_foreach(list_entry, udev_list_get_entry(&udev_enumerate->subsystem_nomatch_list)) {
346                 if (fnmatch(udev_list_entry_get_name(list_entry), subsystem, 0) == 0)
347                         return 0;
348         }
349         if (udev_list_get_entry(&udev_enumerate->subsystem_match_list) != NULL) {
350                 udev_list_entry_foreach(list_entry, udev_list_get_entry(&udev_enumerate->subsystem_match_list)) {
351                         if (fnmatch(udev_list_entry_get_name(list_entry), subsystem, 0) == 0)
352                                 return 1;
353                 }
354                 return 0;
355         }
356         return 1;
357 }
358
359 static int scan_dir(struct udev_enumerate *udev_enumerate, const char *basedir, const char *subdir, const char *subsystem)
360 {
361         struct udev *udev = udev_enumerate_get_udev(udev_enumerate);
362
363         char path[UTIL_PATH_SIZE];
364         DIR *dir;
365         struct dirent *dent;
366
367         util_strscpyl(path, sizeof(path), udev_get_sys_path(udev), "/", basedir, NULL);
368         dir = opendir(path);
369         if (dir == NULL)
370                 return -1;
371         for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) {
372                 if (dent->d_name[0] == '.')
373                         continue;
374                 if (!match_subsystem(udev_enumerate, subsystem != NULL ? subsystem : dent->d_name))
375                         continue;
376                 scan_dir_and_add_devices(udev_enumerate, basedir, dent->d_name, subdir);
377         }
378         closedir(dir);
379         return 0;
380 }
381
382 static int devices_delay(struct udev *udev, const char *syspath)
383 {
384         static const char *delay_device_list[] = {
385                 "/block/md",
386                 "/block/dm-",
387                 NULL
388         };
389         size_t len;
390         int i;
391
392         len = strlen(udev_get_sys_path(udev));
393         for (i = 0; delay_device_list[i] != NULL; i++) {
394                 if (strstr(&syspath[len], delay_device_list[i]) != NULL) {
395                         dbg(udev, "delaying: %s\n", syspath);
396                         return 1;
397                 }
398         }
399         return 0;
400 }
401
402 /* sort delayed devices to the end of the list */
403 static int devices_sort(struct udev_enumerate *udev_enumerate)
404 {
405         struct udev_list_entry *entry_loop;
406         struct udev_list_entry *entry_tmp;
407         struct udev_list_node devices_list;
408
409         udev_list_init(&devices_list);
410         /* move delayed to delay list */
411         udev_list_entry_foreach_safe(entry_loop, entry_tmp, udev_list_get_entry(&udev_enumerate->devices_list)) {
412                 if (devices_delay(udev_enumerate->udev, udev_list_entry_get_name(entry_loop))) {
413                         udev_list_entry_remove(entry_loop);
414                         udev_list_entry_append(entry_loop, &devices_list);
415                 }
416         }
417         /* move delayed back to end of list */
418         udev_list_entry_foreach_safe(entry_loop, entry_tmp, udev_list_get_entry(&devices_list)) {
419                 udev_list_entry_remove(entry_loop);
420                 udev_list_entry_append(entry_loop, &udev_enumerate->devices_list);
421         }
422         udev_enumerate->devices_sorted = 1;
423         return 0;
424 }
425
426 int udev_enumerate_add_syspath(struct udev_enumerate *udev_enumerate, const char *syspath)
427 {
428         struct udev *udev = udev_enumerate_get_udev(udev_enumerate);
429         struct udev_device *udev_device;
430
431         if (udev_enumerate == NULL)
432                 return -EINVAL;
433         if (syspath == NULL)
434                 return 0;
435         /* resolve to real syspath */
436         udev_device = udev_device_new_from_syspath(udev_enumerate->udev, syspath);
437         if (udev_device == NULL)
438                 return -EINVAL;
439         udev_list_entry_add(udev, &udev_enumerate->devices_list,
440                             udev_device_get_syspath(udev_device), NULL, 1, 1);
441         udev_device_unref(udev_device);
442         return 0;
443 }
444
445 /**
446  * udev_enumerate_scan_devices:
447  * @udev_enumerate: udev enumeration context
448  *
449  * Returns: a negative value on error.
450  **/
451 int udev_enumerate_scan_devices(struct udev_enumerate *udev_enumerate)
452 {
453         struct udev *udev = udev_enumerate_get_udev(udev_enumerate);
454         char base[UTIL_PATH_SIZE];
455         struct stat statbuf;
456
457         if (udev_enumerate == NULL)
458                 return -EINVAL;
459         util_strscpyl(base, sizeof(base), udev_get_sys_path(udev), "/subsystem", NULL);
460         if (stat(base, &statbuf) == 0) {
461                 /* we have /subsystem/, forget all the old stuff */
462                 dbg(udev, "searching '/subsystem/*/devices/*' dir\n");
463                 scan_dir(udev_enumerate, "subsystem", "devices", NULL);
464         } else {
465                 dbg(udev, "searching '/bus/*/devices/*' dir\n");
466                 scan_dir(udev_enumerate, "bus", "devices", NULL);
467                 dbg(udev, "searching '/class/*' dir\n");
468                 scan_dir(udev_enumerate, "class", NULL, NULL);
469                 /* if block isn't a class, scan /block/ */
470                 util_strscpyl(base, sizeof(base), udev_get_sys_path(udev), "/class/block", NULL);
471                 if (stat(base, &statbuf) != 0) {
472                         if (match_subsystem(udev_enumerate, "block")) {
473                                 dbg(udev, "searching '/block/*' dir\n");
474                                 /* scan disks */
475                                 scan_dir_and_add_devices(udev_enumerate, "block", NULL, NULL);
476                                 /* scan partitions */
477                                 dbg(udev, "searching '/block/*/*' dir\n");
478                                 scan_dir(udev_enumerate, "block", NULL, "block");
479                         }
480                 }
481         }
482         return 0;
483 }
484
485 /**
486  * udev_enumerate_scan_subsystems:
487  * @udev_enumerate: udev enumeration context
488  *
489  * Returns: a negative value on error.
490  **/
491 int udev_enumerate_scan_subsystems(struct udev_enumerate *udev_enumerate)
492 {
493         struct udev *udev = udev_enumerate_get_udev(udev_enumerate);
494         char base[UTIL_PATH_SIZE];
495         struct stat statbuf;
496         const char *subsysdir;
497
498         if (udev_enumerate == NULL)
499                 return -EINVAL;
500         util_strscpyl(base, sizeof(base), udev_get_sys_path(udev), "/subsystem", NULL);
501         if (stat(base, &statbuf) == 0)
502                 subsysdir = "subsystem";
503         else
504                 subsysdir = "bus";
505         if (match_subsystem(udev_enumerate, "subsystem")) {
506                 dbg(udev, "searching '%s/*' dir\n", subsysdir);
507                 scan_dir_and_add_devices(udev_enumerate, subsysdir, NULL, NULL);
508         }
509         if (match_subsystem(udev_enumerate, "drivers")) {
510                 dbg(udev, "searching '%s/*/drivers/*' dir\n", subsysdir);
511                 scan_dir(udev_enumerate, subsysdir, "drivers", "drivers");
512         }
513         return 0;
514 }