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