chiark / gitweb /
libudev: enumerate - sort with qsort()
[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 <stdbool.h>
21 #include <sys/stat.h>
22 #include <sys/param.h>
23
24 #include "libudev.h"
25 #include "libudev-private.h"
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 devices.
33  */
34
35 struct syspath {
36         char *syspath;
37         size_t len;
38 };
39
40 /**
41  * udev_enumerate:
42  *
43  * Opaque object representing one device lookup/sort context.
44  */
45 struct udev_enumerate {
46         struct udev *udev;
47         int refcount;
48         struct udev_list_node sysattr_match_list;
49         struct udev_list_node sysattr_nomatch_list;
50         struct udev_list_node subsystem_match_list;
51         struct udev_list_node subsystem_nomatch_list;
52         struct udev_list_node properties_match_list;
53         struct udev_list_node devices_list;
54         struct syspath *devices;
55         unsigned int devices_cur;
56         unsigned int devices_max;
57         bool devices_uptodate:1;
58 };
59
60 /**
61  * udev_enumerate_new:
62  * @udev: udev library context
63  *
64  * Returns: an enumeration context
65  **/
66 struct udev_enumerate *udev_enumerate_new(struct udev *udev)
67 {
68         struct udev_enumerate *udev_enumerate;
69
70         udev_enumerate = calloc(1, sizeof(struct udev_enumerate));
71         if (udev_enumerate == NULL)
72                 return NULL;
73         udev_enumerate->refcount = 1;
74         udev_enumerate->udev = udev;
75         udev_list_init(&udev_enumerate->sysattr_match_list);
76         udev_list_init(&udev_enumerate->sysattr_nomatch_list);
77         udev_list_init(&udev_enumerate->subsystem_match_list);
78         udev_list_init(&udev_enumerate->subsystem_nomatch_list);
79         udev_list_init(&udev_enumerate->properties_match_list);
80         udev_list_init(&udev_enumerate->devices_list);
81         return udev_enumerate;
82 }
83
84 /**
85  * udev_enumerate_ref:
86  * @udev_enumerate: context
87  *
88  * Take a reference of a enumeration context.
89  *
90  * Returns: the passed enumeration context
91  **/
92 struct udev_enumerate *udev_enumerate_ref(struct udev_enumerate *udev_enumerate)
93 {
94         if (udev_enumerate == NULL)
95                 return NULL;
96         udev_enumerate->refcount++;
97         return udev_enumerate;
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 void udev_enumerate_unref(struct udev_enumerate *udev_enumerate)
108 {
109         unsigned int i;
110
111         if (udev_enumerate == NULL)
112                 return;
113         udev_enumerate->refcount--;
114         if (udev_enumerate->refcount > 0)
115                 return;
116         udev_list_cleanup_entries(udev_enumerate->udev, &udev_enumerate->sysattr_match_list);
117         udev_list_cleanup_entries(udev_enumerate->udev, &udev_enumerate->sysattr_nomatch_list);
118         udev_list_cleanup_entries(udev_enumerate->udev, &udev_enumerate->subsystem_match_list);
119         udev_list_cleanup_entries(udev_enumerate->udev, &udev_enumerate->subsystem_nomatch_list);
120         udev_list_cleanup_entries(udev_enumerate->udev, &udev_enumerate->properties_match_list);
121         udev_list_cleanup_entries(udev_enumerate->udev, &udev_enumerate->devices_list);
122         for (i = 0; i < udev_enumerate->devices_cur; i++)
123                 free(udev_enumerate->devices[i].syspath);
124         free(udev_enumerate->devices);
125         free(udev_enumerate);
126 }
127
128 /**
129  * udev_enumerate_get_udev:
130  * @udev_enumerate: context
131  *
132  * Returns: the udev library context.
133  */
134 struct udev *udev_enumerate_get_udev(struct udev_enumerate *udev_enumerate)
135 {
136         if (udev_enumerate == NULL)
137                 return NULL;
138         return udev_enumerate->udev;
139 }
140
141 static int syspath_add(struct udev_enumerate *udev_enumerate, const char *syspath)
142 {
143         char *path;
144         struct syspath *entry;
145
146         /* double array size if needed */
147         if (udev_enumerate->devices_cur >= udev_enumerate->devices_max) {
148                 struct syspath *buf;
149                 unsigned int add;
150
151                 add = udev_enumerate->devices_max;
152                 if (add < 1024)
153                         add = 1024;
154                 buf = realloc(udev_enumerate->devices, (udev_enumerate->devices_max + add) * sizeof(struct syspath));
155                 if (buf == NULL)
156                         return -ENOMEM;
157                 udev_enumerate->devices = buf;
158                 udev_enumerate->devices_max += add;
159         }
160
161         path = strdup(syspath);
162         if (path == NULL)
163                 return -ENOMEM;
164         entry = &udev_enumerate->devices[udev_enumerate->devices_cur];
165         entry->syspath = path;
166         entry->len = strlen(path);
167         udev_enumerate->devices_cur++;
168         udev_enumerate->devices_uptodate = false;
169         return 0;
170 }
171
172 static int syspath_cmp(const void *p1, const void *p2)
173 {
174         const struct syspath *path1 = p1;
175         const struct syspath *path2 = p2;
176         size_t len;
177         int ret;
178
179         len = MIN(path1->len, path2->len);
180         ret = memcmp(path1->syspath, path2->syspath, len);
181         if (ret == 0) {
182                 if (path1->len < path2->len)
183                         ret = -1;
184                 else if (path1->len > path2->len)
185                         ret = 1;
186         }
187         return ret;
188 }
189
190 static int devices_delay(struct udev *udev, const char *syspath)
191 {
192         static const char *delay_device_list[] = {
193                 "/block/md",
194                 "/block/dm-",
195                 NULL
196         };
197         size_t len;
198         int i;
199
200         len = strlen(udev_get_sys_path(udev));
201         for (i = 0; delay_device_list[i] != NULL; i++) {
202                 if (strstr(&syspath[len], delay_device_list[i]) != NULL) {
203                         dbg(udev, "delaying: %s\n", syspath);
204                         return 1;
205                 }
206         }
207         return 0;
208 }
209
210 /**
211  * udev_enumerate_get_list_entry:
212  * @udev_enumerate: context
213  *
214  * Returns: the first entry of the sorted list of device paths.
215  */
216 struct udev_list_entry *udev_enumerate_get_list_entry(struct udev_enumerate *udev_enumerate)
217 {
218         if (udev_enumerate == NULL)
219                 return NULL;
220         if (!udev_enumerate->devices_uptodate) {
221                 unsigned int i;
222                 unsigned int max;
223                 struct syspath *prev = NULL;
224
225                 udev_list_cleanup_entries(udev_enumerate->udev, &udev_enumerate->devices_list);
226                 qsort(udev_enumerate->devices, udev_enumerate->devices_cur, sizeof(struct syspath), syspath_cmp);
227
228                 max = udev_enumerate->devices_cur;
229                 for (i = 0; i < max; i++) {
230                         struct syspath *entry = &udev_enumerate->devices[i];
231
232                         /* skip duplicated entries */
233                         if (prev != NULL &&
234                             entry->len == prev->len &&
235                             memcmp(entry->syspath, prev->syspath, entry->len) == 0)
236                                 continue;
237                         prev = entry;
238
239                         /* skip to be delayed devices, and add them to the end of the list */
240                         if (devices_delay(udev_enumerate->udev, entry->syspath)) {
241                                 syspath_add(udev_enumerate, entry->syspath);
242                                 continue;
243                         }
244
245                         udev_list_entry_add(udev_enumerate->udev, &udev_enumerate->devices_list,
246                                             entry->syspath, NULL, 0, 0);
247                 }
248                 /* add and cleanup delayed devices from end of list */
249                 for (i = max; i < udev_enumerate->devices_cur; i++) {
250                         struct syspath *entry = &udev_enumerate->devices[i];
251
252                         udev_list_entry_add(udev_enumerate->udev, &udev_enumerate->devices_list,
253                                             entry->syspath, NULL, 0, 0);
254                         free(entry->syspath);
255                 }
256                 udev_enumerate->devices_cur = max;
257
258                 udev_enumerate->devices_uptodate = true;
259         }
260         return udev_list_get_entry(&udev_enumerate->devices_list);
261 }
262
263 /**
264  * udev_enumerate_add_match_subsystem:
265  * @udev_enumerate: context
266  * @subsystem: filter for a subsystem of the device to include in the list
267  *
268  * Returns: 0 on success, otherwise a negative error value.
269  */
270 int udev_enumerate_add_match_subsystem(struct udev_enumerate *udev_enumerate, const char *subsystem)
271 {
272         if (udev_enumerate == NULL)
273                 return -EINVAL;
274         if (subsystem == NULL)
275                 return 0;
276         if (udev_list_entry_add(udev_enumerate_get_udev(udev_enumerate),
277                                 &udev_enumerate->subsystem_match_list, subsystem, NULL, 1, 0) == NULL)
278                 return -ENOMEM;
279         return 0;
280 }
281
282 /**
283  * udev_enumerate_add_nomatch_subsystem:
284  * @udev_enumerate: context
285  * @subsystem: filter for a subsystem of the device to exclude from the list
286  *
287  * Returns: 0 on success, otherwise a negative error value.
288  */
289 int udev_enumerate_add_nomatch_subsystem(struct udev_enumerate *udev_enumerate, const char *subsystem)
290 {
291         if (udev_enumerate == NULL)
292                 return -EINVAL;
293         if (subsystem == NULL)
294                 return 0;
295         if (udev_list_entry_add(udev_enumerate_get_udev(udev_enumerate),
296                                 &udev_enumerate->subsystem_nomatch_list, subsystem, NULL, 1, 0) == NULL)
297                 return -ENOMEM;
298         return 0;
299 }
300
301 /**
302  * udev_enumerate_add_match_sysattr:
303  * @udev_enumerate: context
304  * @sysattr: filter for a sys attribute at the device to include in the list
305  * @value: optional value of the sys attribute
306  *
307  * Returns: 0 on success, otherwise a negative error value.
308  */
309 int udev_enumerate_add_match_sysattr(struct udev_enumerate *udev_enumerate, const char *sysattr, const char *value)
310 {
311         if (udev_enumerate == NULL)
312                 return -EINVAL;
313         if (sysattr == NULL)
314                 return 0;
315         if (udev_list_entry_add(udev_enumerate_get_udev(udev_enumerate),
316                            &udev_enumerate->sysattr_match_list, sysattr, value, 0, 0) == NULL)
317                 return -ENOMEM;
318         return 0;
319 }
320
321 /**
322  * udev_enumerate_add_nomatch_sysattr:
323  * @udev_enumerate: context
324  * @sysattr: filter for a sys attribute at the device to exclude from the list
325  * @value: optional value of the sys attribute
326  *
327  * Returns: 0 on success, otherwise a negative error value.
328  */
329 int udev_enumerate_add_nomatch_sysattr(struct udev_enumerate *udev_enumerate, const char *sysattr, const char *value)
330 {
331         if (udev_enumerate == NULL)
332                 return -EINVAL;
333         if (sysattr == NULL)
334                 return 0;
335         if (udev_list_entry_add(udev_enumerate_get_udev(udev_enumerate),
336                            &udev_enumerate->sysattr_nomatch_list, sysattr, value, 0, 0) == NULL)
337                 return -ENOMEM;
338         return 0;
339 }
340
341 static int match_sysattr_value(struct udev *udev, const char *syspath, const char *sysattr, const char *match_val)
342 {
343         struct udev_device *device;
344         const char *val = NULL;
345         bool match = false;
346
347         device = udev_device_new_from_syspath(udev, syspath);
348         if (device == NULL)
349                 return -EINVAL;
350         val = udev_device_get_sysattr_value(device, sysattr);
351         if (val == NULL)
352                 goto exit;
353         if (match_val == NULL) {
354                 match = true;
355                 goto exit;
356         }
357         if (fnmatch(match_val, val, 0) == 0) {
358                 match = true;
359                 goto exit;
360         }
361 exit:
362         udev_device_unref(device);
363         return match;
364 }
365
366 /**
367  * udev_enumerate_add_match_property:
368  * @udev_enumerate: context
369  * @property: filter for a property of the device to include in the list
370  * @value: value of the property
371  *
372  * Returns: 0 on success, otherwise a negative error value.
373  */
374 int udev_enumerate_add_match_property(struct udev_enumerate *udev_enumerate, const char *property, const char *value)
375 {
376         if (udev_enumerate == NULL)
377                 return -EINVAL;
378         if (property == NULL)
379                 return 0;
380         if (udev_list_entry_add(udev_enumerate_get_udev(udev_enumerate),
381                                 &udev_enumerate->properties_match_list, property, value, 0, 0) == NULL)
382                 return -ENOMEM;
383         return 0;
384 }
385
386 static int match_sysattr(struct udev_enumerate *udev_enumerate, const char *syspath)
387 {
388         struct udev *udev = udev_enumerate_get_udev(udev_enumerate);
389         struct udev_list_entry *list_entry;
390
391         /* skip list */
392         udev_list_entry_foreach(list_entry, udev_list_get_entry(&udev_enumerate->sysattr_nomatch_list)) {
393                 if (match_sysattr_value(udev, syspath,
394                                      udev_list_entry_get_name(list_entry),
395                                      udev_list_entry_get_value(list_entry)))
396                         return 0;
397         }
398         /* include list */
399         if (udev_list_get_entry(&udev_enumerate->sysattr_match_list) != NULL) {
400                 udev_list_entry_foreach(list_entry, udev_list_get_entry(&udev_enumerate->sysattr_match_list)) {
401                         /* anything that does not match, will make it FALSE */
402                         if (!match_sysattr_value(udev, syspath,
403                                               udev_list_entry_get_name(list_entry),
404                                               udev_list_entry_get_value(list_entry)))
405                                 return 0;
406                 }
407                 return 1;
408         }
409         return 1;
410 }
411
412 static int match_property(struct udev_enumerate *udev_enumerate, const char *syspath)
413 {
414         struct udev_device *dev;
415         struct udev_list_entry *list_entry;
416         int match = false;
417
418         /* no match always matches */
419         if (udev_list_get_entry(&udev_enumerate->properties_match_list) == NULL)
420                 return 1;
421
422         /* no device does not match */
423         dev = udev_device_new_from_syspath(udev_enumerate->udev, syspath);
424         if (dev == NULL)
425                 return 0;
426
427         /* loop over matches */
428         udev_list_entry_foreach(list_entry, udev_list_get_entry(&udev_enumerate->properties_match_list)) {
429                 const char *match_key = udev_list_entry_get_name(list_entry);
430                 const char *match_value = udev_list_entry_get_value(list_entry);
431                 struct udev_list_entry *property_entry;
432
433                 /* loop over device properties */
434                 udev_list_entry_foreach(property_entry, udev_device_get_properties_list_entry(dev)) {
435                         const char *dev_key = udev_list_entry_get_name(property_entry);
436                         const char *dev_value = udev_list_entry_get_value(property_entry);
437
438                         if (fnmatch(match_key, dev_key, 0) != 0)
439                                 continue;
440                         if (match_value == NULL && dev_value == NULL) {
441                                 match = true;
442                                 goto out;
443                         }
444                         if (match_value == NULL || dev_value == NULL)
445                                 continue;
446                         if (fnmatch(match_value, dev_value, 0) == 0) {
447                                 match = true;
448                                 goto out;
449                         }
450                 }
451         }
452 out:
453         udev_device_unref(dev);
454         return match;
455 }
456
457 static int scan_dir_and_add_devices(struct udev_enumerate *udev_enumerate,
458                                     const char *basedir, const char *subdir1, const char *subdir2)
459 {
460         struct udev *udev = udev_enumerate_get_udev(udev_enumerate);
461         char path[UTIL_PATH_SIZE];
462         size_t l;
463         char *s;
464         DIR *dir;
465         struct dirent *dent;
466
467         s = path;
468         l = util_strpcpyl(&s, sizeof(path), udev_get_sys_path(udev), "/", basedir, NULL);
469         if (subdir1 != NULL)
470                 l = util_strpcpyl(&s, l, "/", subdir1, NULL);
471         if (subdir2 != NULL)
472                 l = util_strpcpyl(&s, l, "/", subdir2, NULL);
473         dir = opendir(path);
474         if (dir == NULL)
475                 return -1;
476         for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) {
477                 char syspath[UTIL_PATH_SIZE];
478                 char filename[UTIL_PATH_SIZE];
479                 struct stat statbuf;
480
481                 if (dent->d_name[0] == '.')
482                         continue;
483                 util_strscpyl(syspath, sizeof(syspath), path, "/", dent->d_name, NULL);
484                 if (lstat(syspath, &statbuf) != 0)
485                         continue;
486                 if (S_ISREG(statbuf.st_mode))
487                         continue;
488                 if (S_ISLNK(statbuf.st_mode))
489                         util_resolve_sys_link(udev, syspath, sizeof(syspath));
490
491                 util_strscpyl(filename, sizeof(filename), syspath, "/uevent", NULL);
492                 if (stat(filename, &statbuf) != 0)
493                         continue;
494                 if (!match_sysattr(udev_enumerate, syspath))
495                         continue;
496                 if (!match_property(udev_enumerate, syspath))
497                         continue;
498                 syspath_add(udev_enumerate, syspath);
499         }
500         closedir(dir);
501         return 0;
502 }
503
504 static int match_subsystem(struct udev_enumerate *udev_enumerate, const char *subsystem)
505 {
506         struct udev_list_entry *list_entry;
507
508         udev_list_entry_foreach(list_entry, udev_list_get_entry(&udev_enumerate->subsystem_nomatch_list)) {
509                 if (fnmatch(udev_list_entry_get_name(list_entry), subsystem, 0) == 0)
510                         return 0;
511         }
512         if (udev_list_get_entry(&udev_enumerate->subsystem_match_list) != NULL) {
513                 udev_list_entry_foreach(list_entry, udev_list_get_entry(&udev_enumerate->subsystem_match_list)) {
514                         if (fnmatch(udev_list_entry_get_name(list_entry), subsystem, 0) == 0)
515                                 return 1;
516                 }
517                 return 0;
518         }
519         return 1;
520 }
521
522 static int scan_dir(struct udev_enumerate *udev_enumerate, const char *basedir, const char *subdir, const char *subsystem)
523 {
524         struct udev *udev = udev_enumerate_get_udev(udev_enumerate);
525
526         char path[UTIL_PATH_SIZE];
527         DIR *dir;
528         struct dirent *dent;
529
530         util_strscpyl(path, sizeof(path), udev_get_sys_path(udev), "/", basedir, NULL);
531         dir = opendir(path);
532         if (dir == NULL)
533                 return -1;
534         for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) {
535                 if (dent->d_name[0] == '.')
536                         continue;
537                 if (!match_subsystem(udev_enumerate, subsystem != NULL ? subsystem : dent->d_name))
538                         continue;
539                 scan_dir_and_add_devices(udev_enumerate, basedir, dent->d_name, subdir);
540         }
541         closedir(dir);
542         return 0;
543 }
544
545 /**
546  * udev_enumerate_add_syspath:
547  * @udev_enumerate: context
548  * @syspath: path of a device
549  *
550  * Add a device to the list of devices, to retrieve it back sorted in dependency order.
551  *
552  * Returns: 0 on success, otherwise a negative error value.
553  */
554 int udev_enumerate_add_syspath(struct udev_enumerate *udev_enumerate, const char *syspath)
555 {
556         struct udev_device *udev_device;
557
558         if (udev_enumerate == NULL)
559                 return -EINVAL;
560         if (syspath == NULL)
561                 return 0;
562         /* resolve to real syspath */
563         udev_device = udev_device_new_from_syspath(udev_enumerate->udev, syspath);
564         if (udev_device == NULL)
565                 return -EINVAL;
566         syspath_add(udev_enumerate, udev_device_get_syspath(udev_device));
567         udev_device_unref(udev_device);
568         return 0;
569 }
570
571 /**
572  * udev_enumerate_scan_devices:
573  * @udev_enumerate: udev enumeration context
574  *
575  * Returns: 0 on success, otherwise a negative error value.
576  **/
577 int udev_enumerate_scan_devices(struct udev_enumerate *udev_enumerate)
578 {
579         struct udev *udev = udev_enumerate_get_udev(udev_enumerate);
580         char base[UTIL_PATH_SIZE];
581         struct stat statbuf;
582
583         if (udev_enumerate == NULL)
584                 return -EINVAL;
585         util_strscpyl(base, sizeof(base), udev_get_sys_path(udev), "/subsystem", NULL);
586         if (stat(base, &statbuf) == 0) {
587                 /* we have /subsystem/, forget all the old stuff */
588                 dbg(udev, "searching '/subsystem/*/devices/*' dir\n");
589                 scan_dir(udev_enumerate, "subsystem", "devices", NULL);
590         } else {
591                 dbg(udev, "searching '/bus/*/devices/*' dir\n");
592                 scan_dir(udev_enumerate, "bus", "devices", NULL);
593                 dbg(udev, "searching '/class/*' dir\n");
594                 scan_dir(udev_enumerate, "class", NULL, NULL);
595                 /* if block isn't a class, scan /block/ */
596                 util_strscpyl(base, sizeof(base), udev_get_sys_path(udev), "/class/block", NULL);
597                 if (stat(base, &statbuf) != 0) {
598                         if (match_subsystem(udev_enumerate, "block")) {
599                                 dbg(udev, "searching '/block/*' dir\n");
600                                 /* scan disks */
601                                 scan_dir_and_add_devices(udev_enumerate, "block", NULL, NULL);
602                                 /* scan partitions */
603                                 dbg(udev, "searching '/block/*/*' dir\n");
604                                 scan_dir(udev_enumerate, "block", NULL, "block");
605                         }
606                 }
607         }
608         return 0;
609 }
610
611 /**
612  * udev_enumerate_scan_subsystems:
613  * @udev_enumerate: udev enumeration context
614  *
615  * Returns: 0 on success, otherwise a negative error value.
616  **/
617 int udev_enumerate_scan_subsystems(struct udev_enumerate *udev_enumerate)
618 {
619         struct udev *udev = udev_enumerate_get_udev(udev_enumerate);
620         char base[UTIL_PATH_SIZE];
621         struct stat statbuf;
622         const char *subsysdir;
623
624         if (udev_enumerate == NULL)
625                 return -EINVAL;
626         util_strscpyl(base, sizeof(base), udev_get_sys_path(udev), "/subsystem", NULL);
627         if (stat(base, &statbuf) == 0)
628                 subsysdir = "subsystem";
629         else
630                 subsysdir = "bus";
631         if (match_subsystem(udev_enumerate, "subsystem")) {
632                 dbg(udev, "searching '%s/*' dir\n", subsysdir);
633                 scan_dir_and_add_devices(udev_enumerate, subsysdir, NULL, NULL);
634         }
635         if (match_subsystem(udev_enumerate, "drivers")) {
636                 dbg(udev, "searching '%s/*/drivers/*' dir\n", subsysdir);
637                 scan_dir(udev_enumerate, subsysdir, "drivers", "drivers");
638         }
639         return 0;
640 }