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