chiark / gitweb /
libudev: device - handle /sys/block/<disk-device-link>/<partition>
[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, const char *basedir, const char *subdir1, const char *subdir2)
204 {
205         struct udev *udev = udev_enumerate_get_udev(udev_enumerate);
206         char path[UTIL_PATH_SIZE];
207         DIR *dir;
208         struct dirent *dent;
209
210         util_strlcpy(path, udev_get_sys_path(udev), sizeof(path));
211         util_strlcat(path, "/", sizeof(path));
212         util_strlcat(path, basedir, sizeof(path));
213         if (subdir1 != NULL) {
214                 util_strlcat(path, "/", sizeof(path));
215                 util_strlcat(path, subdir1, sizeof(path));
216         }
217         if (subdir2 != NULL) {
218                 util_strlcat(path, "/", sizeof(path));
219                 util_strlcat(path, subdir2, sizeof(path));
220         }
221         dir = opendir(path);
222         if (dir == NULL)
223                 return -1;
224         for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) {
225                 char syspath[UTIL_PATH_SIZE];
226                 char filename[UTIL_PATH_SIZE];
227                 struct stat statbuf;
228
229                 if (dent->d_name[0] == '.')
230                         continue;
231                 util_strlcpy(syspath, path, sizeof(syspath));
232                 util_strlcat(syspath, "/", sizeof(syspath));
233                 util_strlcat(syspath, dent->d_name, sizeof(syspath));
234                 util_strlcpy(filename, syspath, sizeof(filename));
235                 util_strlcat(filename, "/uevent", sizeof(filename));
236                 if (stat(filename, &statbuf) != 0)
237                         continue;
238                 util_resolve_sys_link(udev, syspath, sizeof(syspath));
239                 if (!match_attr(udev_enumerate, syspath))
240                         continue;
241                 list_entry_add(udev, &udev_enumerate->devices_list, syspath, NULL, 1, 1);
242         }
243         closedir(dir);
244         return 0;
245 }
246
247 static int match_subsystem(struct udev_enumerate *udev_enumerate, const char *subsystem)
248 {
249         struct udev_list_entry *list_entry;
250
251         udev_list_entry_foreach(list_entry, list_get_entry(&udev_enumerate->subsystem_nomatch_list)) {
252                 if (fnmatch(udev_list_entry_get_name(list_entry), subsystem, 0) == 0)
253                         return 0;
254         }
255         if (list_get_entry(&udev_enumerate->subsystem_match_list) != NULL) {
256                 udev_list_entry_foreach(list_entry, list_get_entry(&udev_enumerate->subsystem_match_list)) {
257                         if (fnmatch(udev_list_entry_get_name(list_entry), subsystem, 0) == 0)
258                                 return 1;
259                 }
260                 return 0;
261         }
262         return 1;
263 }
264
265 static int scan_dir(struct udev_enumerate *udev_enumerate, const char *basedir, const char *subdir, const char *match)
266 {
267         struct udev *udev = udev_enumerate_get_udev(udev_enumerate);
268
269         char path[UTIL_PATH_SIZE];
270         DIR *dir;
271         struct dirent *dent;
272
273         util_strlcpy(path, udev_get_sys_path(udev), sizeof(path));
274         util_strlcat(path, "/", sizeof(path));
275         util_strlcat(path, basedir, sizeof(path));
276         dir = opendir(path);
277         if (dir == NULL)
278                 return -1;
279         for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) {
280                 if (dent->d_name[0] == '.')
281                         continue;
282                 if (!match_subsystem(udev_enumerate, match != NULL ? match : dent->d_name))
283                         continue;
284                 scan_dir_and_add_devices(udev_enumerate, basedir, dent->d_name, subdir);
285         }
286         closedir(dir);
287         return 0;
288 }
289
290 static int devices_delay(struct udev *udev, const char *syspath)
291 {
292         static const char *delay_device_list[] = {
293                 "/block/md",
294                 "/block/dm-",
295                 NULL
296         };
297         size_t len;
298         int i;
299
300         len = strlen(udev_get_sys_path(udev));
301         for (i = 0; delay_device_list[i] != NULL; i++) {
302                 if (strstr(&syspath[len], delay_device_list[i]) != NULL) {
303                         info(udev, "delaying: %s\n", syspath);
304                         return 1;
305                 }
306         }
307         return 0;
308 }
309
310 int udev_enumerate_add_device(struct udev_enumerate *udev_enumerate, struct udev_device *udev_device)
311 {
312         struct udev *udev = udev_enumerate_get_udev(udev_enumerate);
313         struct udev_list_entry *list_entry;
314
315         if (udev_enumerate == NULL)
316                 return -EINVAL;
317         if (udev_device == NULL)
318                 return 0;
319         list_entry_add(udev,
320                        &udev_enumerate->devices_list,
321                        udev_device_get_syspath(udev_device), NULL, 1, 1);
322         /* sort delayed devices to the end of the list */
323         udev_list_entry_foreach(list_entry, list_get_entry(&udev_enumerate->devices_list)) {
324                 if (devices_delay(udev, udev_list_entry_get_name(list_entry)))
325                         list_entry_move_to_end(list_entry);
326         }
327         return 0;
328 }
329
330 /**
331  * udev_enumerate_scan_devices:
332  * @udev_enumerate: udev enumeration context
333  *
334  * Returns: a negative value on error.
335  **/
336 int udev_enumerate_scan_devices(struct udev_enumerate *udev_enumerate)
337 {
338         struct udev *udev = udev_enumerate_get_udev(udev_enumerate);
339         char base[UTIL_PATH_SIZE];
340         struct stat statbuf;
341         struct udev_list_entry *list_entry;
342
343         if (udev_enumerate == NULL)
344                 return -EINVAL;
345         util_strlcpy(base, udev_get_sys_path(udev), sizeof(base));
346         util_strlcat(base, "/subsystem", sizeof(base));
347         if (stat(base, &statbuf) == 0) {
348                 /* we have /subsystem/, forget all the old stuff */
349                 info(udev, "searching '/subsystem/*/devices/*' dir\n");
350                 scan_dir(udev_enumerate, "subsystem", "devices", NULL);
351         } else {
352                 info(udev, "searching '/bus/*/devices/*' dir\n");
353                 scan_dir(udev_enumerate, "bus", "devices", NULL);
354                 info(udev, "searching '/class/*' dir\n");
355                 scan_dir(udev_enumerate, "class", NULL, NULL);
356                 /* if block isn't a class, scan /block/ */
357                 util_strlcpy(base, udev_get_sys_path(udev), sizeof(base));
358                 util_strlcat(base, "/class/block", sizeof(base));
359                 if (stat(base, &statbuf) != 0) {
360                         if (match_subsystem(udev_enumerate, "block")) {
361                                 info(udev, "searching '/block/*' dir\n");
362                                 /* scan disks */
363                                 scan_dir_and_add_devices(udev_enumerate, "block", NULL, NULL);
364                                 /* scan partitions */
365                                 info(udev, "searching '/block/*/*' dir\n");
366                                 scan_dir(udev_enumerate, "block", NULL, "block");
367                         }
368                 }
369         }
370         /* sort delayed devices to the end of the list */
371         udev_list_entry_foreach(list_entry, list_get_entry(&udev_enumerate->devices_list)) {
372                 if (devices_delay(udev, udev_list_entry_get_name(list_entry)))
373                         list_entry_move_to_end(list_entry);
374         }
375         return 0;
376 }
377
378 /**
379  * udev_enumerate_scan_subsystems:
380  * @udev_enumerate: udev enumeration context
381  *
382  * Returns: a negative value on error.
383  **/
384 int udev_enumerate_scan_subsystems(struct udev_enumerate *udev_enumerate)
385 {
386         struct udev *udev = udev_enumerate_get_udev(udev_enumerate);
387         char base[UTIL_PATH_SIZE];
388         struct stat statbuf;
389         const char *subsysdir;
390
391         if (udev_enumerate == NULL)
392                 return -EINVAL;
393         util_strlcpy(base, udev_get_sys_path(udev), sizeof(base));
394         util_strlcat(base, "/subsystem", sizeof(base));
395         if (stat(base, &statbuf) == 0)
396                 subsysdir = "subsystem";
397         else
398                 subsysdir = "bus";
399         if (match_subsystem(udev_enumerate, "subsystem")) {
400                 info(udev, "searching '%s/*' dir\n", subsysdir);
401                 scan_dir_and_add_devices(udev_enumerate, subsysdir, NULL, NULL);
402         }
403         info(udev, "searching '%s/*/drivers/*' dir\n", subsysdir);
404         scan_dir(udev_enumerate, subsysdir, "drivers", "drivers");
405         return 0;
406 }