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