chiark / gitweb /
libudev: move to top-level directory
[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 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, 0, 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, 0, 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, 0, 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                 const char *match_key = udev_list_entry_get_name(list_entry);
232                 const char *match_value = udev_list_entry_get_value(list_entry);
233                 struct udev_list_entry *property_entry;
234
235                 /* loop over device properties */
236                 udev_list_entry_foreach(property_entry, udev_device_get_properties_list_entry(dev)) {
237                         const char *dev_key = udev_list_entry_get_name(property_entry);
238                         const char *dev_value = udev_list_entry_get_value(property_entry);
239
240                         if (fnmatch(match_key, dev_key, 0) != 0)
241                                 continue;
242                         if (match_value == NULL && dev_value == NULL) {
243                                 match = 1;
244                                 goto out;
245                         }
246                         if (match_value == NULL || dev_value == NULL)
247                                 continue;
248                         if (fnmatch(match_value, dev_value, 0) == 0) {
249                                 match = 1;
250                                 goto out;
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         size_t l;
265         char *s;
266         DIR *dir;
267         struct dirent *dent;
268
269         s = path;
270         l = util_strpcpyl(&s, sizeof(path), udev_get_sys_path(udev), "/", basedir, NULL);
271         if (subdir1 != NULL)
272                 l = util_strpcpyl(&s, l, "/", subdir1, NULL);
273         if (subdir2 != NULL)
274                 l = util_strpcpyl(&s, l, "/", subdir2, NULL);
275         dir = opendir(path);
276         if (dir == NULL)
277                 return -1;
278         for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) {
279                 char syspath[UTIL_PATH_SIZE];
280                 char filename[UTIL_PATH_SIZE];
281                 struct stat statbuf;
282
283                 if (dent->d_name[0] == '.')
284                         continue;
285                 util_strscpyl(syspath, sizeof(syspath), path, "/", dent->d_name, NULL);
286                 if (lstat(syspath, &statbuf) != 0)
287                         continue;
288                 if (S_ISREG(statbuf.st_mode))
289                         continue;
290                 if (S_ISLNK(statbuf.st_mode))
291                         util_resolve_sys_link(udev, syspath, sizeof(syspath));
292
293                 util_strscpyl(filename, sizeof(filename), syspath, "/uevent", NULL);
294                 if (stat(filename, &statbuf) != 0)
295                         continue;
296                 if (!match_sysattr(udev_enumerate, syspath))
297                         continue;
298                 if (!match_property(udev_enumerate, syspath))
299                         continue;
300                 udev_list_entry_add(udev, &udev_enumerate->devices_list, syspath, NULL, 1, 1);
301         }
302         closedir(dir);
303         return 0;
304 }
305
306 static int match_subsystem(struct udev_enumerate *udev_enumerate, const char *subsystem)
307 {
308         struct udev_list_entry *list_entry;
309
310         udev_list_entry_foreach(list_entry, udev_list_get_entry(&udev_enumerate->subsystem_nomatch_list)) {
311                 if (fnmatch(udev_list_entry_get_name(list_entry), subsystem, 0) == 0)
312                         return 0;
313         }
314         if (udev_list_get_entry(&udev_enumerate->subsystem_match_list) != NULL) {
315                 udev_list_entry_foreach(list_entry, udev_list_get_entry(&udev_enumerate->subsystem_match_list)) {
316                         if (fnmatch(udev_list_entry_get_name(list_entry), subsystem, 0) == 0)
317                                 return 1;
318                 }
319                 return 0;
320         }
321         return 1;
322 }
323
324 static int scan_dir(struct udev_enumerate *udev_enumerate, const char *basedir, const char *subdir, const char *subsystem)
325 {
326         struct udev *udev = udev_enumerate_get_udev(udev_enumerate);
327
328         char path[UTIL_PATH_SIZE];
329         DIR *dir;
330         struct dirent *dent;
331
332         util_strscpyl(path, sizeof(path), udev_get_sys_path(udev), "/", basedir, NULL);
333         dir = opendir(path);
334         if (dir == NULL)
335                 return -1;
336         for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) {
337                 if (dent->d_name[0] == '.')
338                         continue;
339                 if (!match_subsystem(udev_enumerate, subsystem != NULL ? subsystem : dent->d_name))
340                         continue;
341                 scan_dir_and_add_devices(udev_enumerate, basedir, dent->d_name, subdir);
342         }
343         closedir(dir);
344         return 0;
345 }
346
347 static int devices_delay(struct udev *udev, const char *syspath)
348 {
349         static const char *delay_device_list[] = {
350                 "/block/md",
351                 "/block/dm-",
352                 NULL
353         };
354         size_t len;
355         int i;
356
357         len = strlen(udev_get_sys_path(udev));
358         for (i = 0; delay_device_list[i] != NULL; i++) {
359                 if (strstr(&syspath[len], delay_device_list[i]) != NULL) {
360                         dbg(udev, "delaying: %s\n", syspath);
361                         return 1;
362                 }
363         }
364         return 0;
365 }
366
367 /* sort delayed devices to the end of the list */
368 static int devices_sort(struct udev_enumerate *udev_enumerate)
369 {
370         struct udev_list_entry *entry_loop;
371         struct udev_list_entry *entry_tmp;
372         struct udev_list_node devices_list;
373
374         udev_list_init(&devices_list);
375         /* move delayed to delay list */
376         udev_list_entry_foreach_safe(entry_loop, entry_tmp, udev_list_get_entry(&udev_enumerate->devices_list)) {
377                 if (devices_delay(udev_enumerate->udev, udev_list_entry_get_name(entry_loop))) {
378                         udev_list_entry_remove(entry_loop);
379                         udev_list_entry_append(entry_loop, &devices_list);
380                 }
381         }
382         /* move delayed back to end of list */
383         udev_list_entry_foreach_safe(entry_loop, entry_tmp, udev_list_get_entry(&devices_list)) {
384                 udev_list_entry_remove(entry_loop);
385                 udev_list_entry_append(entry_loop, &udev_enumerate->devices_list);
386         }
387         udev_enumerate->devices_sorted = 1;
388         return 0;
389 }
390
391 int udev_enumerate_add_syspath(struct udev_enumerate *udev_enumerate, const char *syspath)
392 {
393         struct udev *udev = udev_enumerate_get_udev(udev_enumerate);
394         struct udev_device *udev_device;
395
396         if (udev_enumerate == NULL)
397                 return -EINVAL;
398         if (syspath == NULL)
399                 return 0;
400         /* resolve to real syspath */
401         udev_device = udev_device_new_from_syspath(udev_enumerate->udev, syspath);
402         if (udev_device == NULL)
403                 return -EINVAL;
404         udev_list_entry_add(udev, &udev_enumerate->devices_list,
405                             udev_device_get_syspath(udev_device), NULL, 1, 1);
406         udev_device_unref(udev_device);
407         return 0;
408 }
409
410 /**
411  * udev_enumerate_scan_devices:
412  * @udev_enumerate: udev enumeration context
413  *
414  * Returns: a negative value on error.
415  **/
416 int udev_enumerate_scan_devices(struct udev_enumerate *udev_enumerate)
417 {
418         struct udev *udev = udev_enumerate_get_udev(udev_enumerate);
419         char base[UTIL_PATH_SIZE];
420         struct stat statbuf;
421
422         if (udev_enumerate == NULL)
423                 return -EINVAL;
424         util_strscpyl(base, sizeof(base), udev_get_sys_path(udev), "/subsystem", NULL);
425         if (stat(base, &statbuf) == 0) {
426                 /* we have /subsystem/, forget all the old stuff */
427                 dbg(udev, "searching '/subsystem/*/devices/*' dir\n");
428                 scan_dir(udev_enumerate, "subsystem", "devices", NULL);
429         } else {
430                 dbg(udev, "searching '/bus/*/devices/*' dir\n");
431                 scan_dir(udev_enumerate, "bus", "devices", NULL);
432                 dbg(udev, "searching '/class/*' dir\n");
433                 scan_dir(udev_enumerate, "class", NULL, NULL);
434                 /* if block isn't a class, scan /block/ */
435                 util_strscpyl(base, sizeof(base), udev_get_sys_path(udev), "/class/block", NULL);
436                 if (stat(base, &statbuf) != 0) {
437                         if (match_subsystem(udev_enumerate, "block")) {
438                                 dbg(udev, "searching '/block/*' dir\n");
439                                 /* scan disks */
440                                 scan_dir_and_add_devices(udev_enumerate, "block", NULL, NULL);
441                                 /* scan partitions */
442                                 dbg(udev, "searching '/block/*/*' dir\n");
443                                 scan_dir(udev_enumerate, "block", NULL, "block");
444                         }
445                 }
446         }
447         return 0;
448 }
449
450 /**
451  * udev_enumerate_scan_subsystems:
452  * @udev_enumerate: udev enumeration context
453  *
454  * Returns: a negative value on error.
455  **/
456 int udev_enumerate_scan_subsystems(struct udev_enumerate *udev_enumerate)
457 {
458         struct udev *udev = udev_enumerate_get_udev(udev_enumerate);
459         char base[UTIL_PATH_SIZE];
460         struct stat statbuf;
461         const char *subsysdir;
462
463         if (udev_enumerate == NULL)
464                 return -EINVAL;
465         util_strscpyl(base, sizeof(base), udev_get_sys_path(udev), "/subsystem", NULL);
466         if (stat(base, &statbuf) == 0)
467                 subsysdir = "subsystem";
468         else
469                 subsysdir = "bus";
470         if (match_subsystem(udev_enumerate, "subsystem")) {
471                 dbg(udev, "searching '%s/*' dir\n", subsysdir);
472                 scan_dir_and_add_devices(udev_enumerate, subsysdir, NULL, NULL);
473         }
474         if (match_subsystem(udev_enumerate, "drivers")) {
475                 dbg(udev, "searching '%s/*/drivers/*' dir\n", subsysdir);
476                 scan_dir(udev_enumerate, subsysdir, "drivers", "drivers");
477         }
478         return 0;
479 }