chiark / gitweb /
272b2981fe41106d5c6cdad3b1dfad38e34322c1
[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 <sys/stat.h>
28
29 #include "libudev.h"
30 #include "libudev-private.h"
31
32 struct udev_enumerate {
33         struct udev *udev;
34         int refcount;
35         struct list_node devices_list;
36 };
37
38 struct udev_enumerate *udev_enumerate_ref(struct udev_enumerate *udev_enumerate)
39 {
40         if (udev_enumerate == NULL)
41                 return NULL;
42         udev_enumerate->refcount++;
43         return udev_enumerate;
44 }
45
46 void udev_enumerate_unref(struct udev_enumerate *udev_enumerate)
47 {
48         if (udev_enumerate == NULL)
49                 return;
50         udev_enumerate->refcount--;
51         if (udev_enumerate->refcount > 0)
52                 return;
53         list_cleanup(udev_enumerate->udev, &udev_enumerate->devices_list);
54         free(udev_enumerate);
55 }
56
57 struct udev *udev_enumerate_get_udev(struct udev_enumerate *udev_enumerate)
58 {
59         if (udev_enumerate == NULL)
60                 return NULL;
61         return udev_enumerate->udev;
62 }
63
64 struct udev_list_entry *udev_enumerate_get_list_entry(struct udev_enumerate *udev_enumerate)
65 {
66         if (udev_enumerate == NULL)
67                 return NULL;
68         return list_get_entry(&udev_enumerate->devices_list);
69 }
70
71 static int devices_scan_subsystem(struct udev *udev,
72                                   const char *basedir, const char *subsystem, const char *subdir,
73                                   struct list_node *devices_list)
74 {
75         char path[UTIL_PATH_SIZE];
76         DIR *dir;
77         struct dirent *dent;
78
79         util_strlcpy(path, udev_get_sys_path(udev), sizeof(path));
80         util_strlcat(path, basedir, sizeof(path));
81         if (subsystem != NULL) {
82                 util_strlcat(path, "/", sizeof(path));
83                 util_strlcat(path, subsystem, sizeof(path));
84         }
85         if (subdir != NULL)
86                 util_strlcat(path, subdir, sizeof(path));
87         dir = opendir(path);
88         if (dir == NULL)
89                 return -1;
90         for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) {
91                 char syspath[UTIL_PATH_SIZE];
92                 char filename[UTIL_PATH_SIZE];
93                 struct stat statbuf;
94
95                 if (dent->d_name[0] == '.')
96                         continue;
97                 util_strlcpy(syspath, path, sizeof(syspath));
98                 util_strlcat(syspath, "/", sizeof(syspath));
99                 util_strlcat(syspath, dent->d_name, sizeof(syspath));
100                 util_strlcpy(filename, syspath, sizeof(filename));
101                 util_strlcat(filename, "/uevent", sizeof(filename));
102                 if (stat(filename, &statbuf) != 0)
103                         continue;
104                 util_resolve_sys_link(udev, syspath, sizeof(syspath));
105                 list_entry_add(udev, devices_list, syspath, NULL, 1, 1);
106         }
107         closedir(dir);
108         return 0;
109 }
110
111 static int devices_scan_subsystems(struct udev *udev,
112                                    const char *basedir, const char *subdir,
113                                    struct udev_list_entry *subsystem_include_list,
114                                    struct udev_list_entry *subsystem_exclude_list,
115                                    struct list_node *devices_list)
116 {
117         if (subsystem_include_list != NULL) {
118                 struct udev_list_entry *list_entry;
119
120                 /* if list of subsystems to scan is given, just use this list */
121                 udev_list_entry_foreach(list_entry, subsystem_include_list) {
122                         if (udev_list_entry_get_by_name(subsystem_exclude_list, udev_list_entry_get_name(list_entry)) != NULL)
123                                         continue;
124                         devices_scan_subsystem(udev, basedir, udev_list_entry_get_name(list_entry), subdir, devices_list);
125                 }
126         } else {
127                 char path[UTIL_PATH_SIZE];
128                 DIR *dir;
129                 struct dirent *dent;
130
131                 /* if no list of subsystems to scan is given, scan all, and possible exclude some subsystems */
132                 util_strlcpy(path, udev_get_sys_path(udev), sizeof(path));
133                 util_strlcat(path, basedir, sizeof(path));
134                 dir = opendir(path);
135                 if (dir == NULL)
136                         return -1;
137                 for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) {
138                         if (dent->d_name[0] == '.')
139                                 continue;
140                         if (udev_list_entry_get_by_name(subsystem_exclude_list, dent->d_name) != NULL)
141                                         continue;
142                         devices_scan_subsystem(udev, basedir, dent->d_name, subdir, devices_list);
143                 }
144                 closedir(dir);
145         }
146         return 0;
147 }
148
149 static int devices_delay(struct udev *udev, const char *syspath)
150 {
151         static const char *delay_device_list[] = {
152                 "/block/md",
153                 "/block/dm-",
154                 NULL
155         };
156         size_t len;
157         int i;
158
159         len = strlen(udev_get_sys_path(udev));
160
161         for (i = 0; delay_device_list[i] != NULL; i++) {
162                 if (strstr(&syspath[len], delay_device_list[i]) != NULL) {
163                         info(udev, "delaying: %s\n", syspath);
164                         return 1;
165                 }
166         }
167         return 0;
168 }
169
170 static struct udev_enumerate *enumerate_new(struct udev *udev)
171 {
172         struct udev_enumerate *udev_enumerate;
173
174         udev_enumerate = malloc(sizeof(struct udev_enumerate));
175         if (udev_enumerate == NULL)
176                 return NULL;
177         memset(udev_enumerate, 0x00, (sizeof(struct udev_enumerate)));
178         udev_enumerate->refcount = 1;
179         udev_enumerate->udev = udev;
180         list_init(&udev_enumerate->devices_list);
181         return udev_enumerate;
182 }
183
184 /**
185  * udev_enumerate_new_from_devices:
186  * @udev: udev library context
187  * @subsystem: the list of names of subsystems to look for devices
188  *
189  * Returns: an enumeration context
190  **/
191 struct udev_enumerate *udev_enumerate_new_from_devices(struct udev *udev, const char *subsystem, ...)
192 {
193         struct udev_enumerate *udev_enumerate;
194         va_list vargs;
195         const char *arg;
196         char base[UTIL_PATH_SIZE];
197         struct stat statbuf;
198         struct list_node subsystem_include_list;
199         struct list_node subsystem_exclude_list;
200         struct udev_list_entry *list_entry;
201
202         if (udev == NULL)
203                 return NULL;
204
205         udev_enumerate = enumerate_new(udev);
206         if (udev_enumerate == NULL)
207                 return NULL;
208
209         va_start(vargs, subsystem);
210         list_init(&subsystem_include_list);
211         list_init(&subsystem_exclude_list);
212         for (arg = subsystem; arg != NULL; arg = va_arg(vargs, const char *)) {
213                 if (arg[0] != '!')
214                         list_entry_add(udev, &subsystem_include_list, arg, NULL, 1, 0);
215                 else
216                         list_entry_add(udev, &subsystem_exclude_list, &arg[1], NULL, 1, 0);
217         }
218         va_end(vargs);
219
220         util_strlcpy(base, udev_get_sys_path(udev), sizeof(base));
221         util_strlcat(base, "/subsystem", sizeof(base));
222         if (stat(base, &statbuf) == 0) {
223                 /* we have /subsystem/, forget all the old stuff */
224                 info(udev, "searching '/subsystem/*/devices/*' dir\n");
225                 devices_scan_subsystems(udev, "/subsystem", "/devices",
226                                         list_get_entry(&subsystem_include_list),
227                                         list_get_entry(&subsystem_exclude_list),
228                                         &udev_enumerate->devices_list);
229         } else {
230                 info(udev, "searching '/bus/*/devices/*' dir\n");
231                 devices_scan_subsystems(udev, "/bus", "/devices",
232                                         list_get_entry(&subsystem_include_list),
233                                         list_get_entry(&subsystem_exclude_list),
234                                         &udev_enumerate->devices_list);
235                 info(udev, "searching '/class/*' dir\n");
236                 devices_scan_subsystems(udev, "/class", NULL,
237                                         list_get_entry(&subsystem_include_list),
238                                         list_get_entry(&subsystem_exclude_list),
239                                         &udev_enumerate->devices_list);
240                 /* if block isn't a class, scan /block/ */
241                 util_strlcpy(base, udev_get_sys_path(udev), sizeof(base));
242                 util_strlcat(base, "/class/block", sizeof(base));
243                 if (stat(base, &statbuf) != 0) {
244                         struct udev_list_entry *include_list = list_get_entry(&subsystem_include_list);
245                         struct udev_list_entry *exclude_list = list_get_entry(&subsystem_exclude_list);
246                         int include_block = (include_list == NULL || udev_list_entry_get_by_name(include_list, "block") != NULL);
247                         int exclude_block = (udev_list_entry_get_by_name(exclude_list, "block") != NULL);
248
249                         if (include_block && !exclude_block) {
250                                 info(udev, "searching '/block/*/*' dir\n");
251                                 /* scan disks */
252                                 devices_scan_subsystem(udev, "/block", NULL, NULL, &udev_enumerate->devices_list);
253                                 /* scan partitions */
254                                 devices_scan_subsystems(udev, "/block", NULL,
255                                                         NULL, NULL,
256                                                         &udev_enumerate->devices_list);
257                         }
258                 }
259         }
260
261         list_cleanup(udev, &subsystem_include_list);
262         list_cleanup(udev, &subsystem_exclude_list);
263
264         /* sort delayed devices to the end of the list */
265         udev_list_entry_foreach(list_entry, list_get_entry(&udev_enumerate->devices_list)) {
266                 if (devices_delay(udev, udev_list_entry_get_name(list_entry)))
267                         list_entry_move_to_end(list_entry);
268         }
269         return udev_enumerate;
270 }
271
272 struct udev_enumerate *udev_enumerate_new_from_subsystems(struct udev *udev)
273 {
274         struct udev_enumerate *udev_enumerate;
275         char base[UTIL_PATH_SIZE];
276         struct stat statbuf;
277         const char *subsysdir;
278
279         if (udev == NULL)
280                 return NULL;
281
282         udev_enumerate = enumerate_new(udev);
283         if (udev_enumerate == NULL)
284                 return NULL;
285
286         util_strlcpy(base, udev_get_sys_path(udev), sizeof(base));
287         util_strlcat(base, "/subsystem", sizeof(base));
288         if (stat(base, &statbuf) == 0)
289                 subsysdir = "/subsystem";
290         else
291                 subsysdir = "/bus";
292         info(udev, "searching '%s/*' dir\n", subsysdir);
293         devices_scan_subsystem(udev, subsysdir, NULL, NULL, &udev_enumerate->devices_list);
294         info(udev, "searching '%s/*/drivers/*' dir\n", subsysdir);
295         devices_scan_subsystems(udev, subsysdir, "/drivers",
296                                 NULL, NULL,
297                                 &udev_enumerate->devices_list);
298         return udev_enumerate;
299 }