chiark / gitweb /
move some info() to dbg()
[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 *list_entry;
324
325         udev_list_entry_foreach(list_entry, udev_list_get_entry(&udev_enumerate->devices_list)) {
326                 if (devices_delay(udev_enumerate->udev, udev_list_entry_get_name(list_entry)))
327                         udev_list_entry_move_to_end(list_entry);
328         }
329         udev_enumerate->devices_sorted = 1;
330         return 0;
331 }
332
333 int udev_enumerate_add_syspath(struct udev_enumerate *udev_enumerate, const char *syspath)
334 {
335         struct udev *udev = udev_enumerate_get_udev(udev_enumerate);
336         struct udev_device *udev_device;
337
338         if (udev_enumerate == NULL)
339                 return -EINVAL;
340         if (syspath == NULL)
341                 return 0;
342         /* resolve to real syspath */
343         udev_device = udev_device_new_from_syspath(udev_enumerate->udev, syspath);
344         if (udev_device == NULL)
345                 return -EINVAL;
346         udev_list_entry_add(udev, &udev_enumerate->devices_list,
347                             udev_device_get_syspath(udev_device), NULL, 1, 1);
348         udev_device_unref(udev_device);
349         return 0;
350 }
351
352 /**
353  * udev_enumerate_scan_devices:
354  * @udev_enumerate: udev enumeration context
355  *
356  * Returns: a negative value on error.
357  **/
358 int udev_enumerate_scan_devices(struct udev_enumerate *udev_enumerate)
359 {
360         struct udev *udev = udev_enumerate_get_udev(udev_enumerate);
361         char base[UTIL_PATH_SIZE];
362         struct stat statbuf;
363         struct udev_list_entry *list_entry;
364
365         if (udev_enumerate == NULL)
366                 return -EINVAL;
367         util_strlcpy(base, udev_get_sys_path(udev), sizeof(base));
368         util_strlcat(base, "/subsystem", sizeof(base));
369         if (stat(base, &statbuf) == 0) {
370                 /* we have /subsystem/, forget all the old stuff */
371                 dbg(udev, "searching '/subsystem/*/devices/*' dir\n");
372                 scan_dir(udev_enumerate, "subsystem", "devices", NULL);
373         } else {
374                 dbg(udev, "searching '/bus/*/devices/*' dir\n");
375                 scan_dir(udev_enumerate, "bus", "devices", NULL);
376                 dbg(udev, "searching '/class/*' dir\n");
377                 scan_dir(udev_enumerate, "class", NULL, NULL);
378                 /* if block isn't a class, scan /block/ */
379                 util_strlcpy(base, udev_get_sys_path(udev), sizeof(base));
380                 util_strlcat(base, "/class/block", sizeof(base));
381                 if (stat(base, &statbuf) != 0) {
382                         if (match_subsystem(udev_enumerate, "block")) {
383                                 dbg(udev, "searching '/block/*' dir\n");
384                                 /* scan disks */
385                                 scan_dir_and_add_devices(udev_enumerate, "block", NULL, NULL);
386                                 /* scan partitions */
387                                 dbg(udev, "searching '/block/*/*' dir\n");
388                                 scan_dir(udev_enumerate, "block", NULL, "block");
389                         }
390                 }
391         }
392         /* sort delayed devices to the end of the list */
393         udev_list_entry_foreach(list_entry, udev_list_get_entry(&udev_enumerate->devices_list)) {
394                 if (devices_delay(udev, udev_list_entry_get_name(list_entry)))
395                         udev_list_entry_move_to_end(list_entry);
396         }
397         return 0;
398 }
399
400 /**
401  * udev_enumerate_scan_subsystems:
402  * @udev_enumerate: udev enumeration context
403  *
404  * Returns: a negative value on error.
405  **/
406 int udev_enumerate_scan_subsystems(struct udev_enumerate *udev_enumerate)
407 {
408         struct udev *udev = udev_enumerate_get_udev(udev_enumerate);
409         char base[UTIL_PATH_SIZE];
410         struct stat statbuf;
411         const char *subsysdir;
412
413         if (udev_enumerate == NULL)
414                 return -EINVAL;
415         util_strlcpy(base, udev_get_sys_path(udev), sizeof(base));
416         util_strlcat(base, "/subsystem", sizeof(base));
417         if (stat(base, &statbuf) == 0)
418                 subsysdir = "subsystem";
419         else
420                 subsysdir = "bus";
421         if (match_subsystem(udev_enumerate, "subsystem")) {
422                 dbg(udev, "searching '%s/*' dir\n", subsysdir);
423                 scan_dir_and_add_devices(udev_enumerate, subsysdir, NULL, NULL);
424         }
425         if (match_subsystem(udev_enumerate, "drivers")) {
426                 dbg(udev, "searching '%s/*/drivers/*' dir\n", subsysdir);
427                 scan_dir(udev_enumerate, subsysdir, "drivers", "drivers");
428         }
429         return 0;
430 }