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