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