chiark / gitweb /
266164ae517ebfebe4da97cc34b9af44749b7728
[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 /**
171  * udev_enumerate_new:
172  * @udev: udev library context
173  *
174  * Returns: an enumeration context
175  **/
176 struct udev_enumerate *udev_enumerate_new(struct udev *udev)
177 {
178         struct udev_enumerate *udev_enumerate;
179
180         udev_enumerate = malloc(sizeof(struct udev_enumerate));
181         if (udev_enumerate == NULL)
182                 return NULL;
183         memset(udev_enumerate, 0x00, (sizeof(struct udev_enumerate)));
184         udev_enumerate->refcount = 1;
185         udev_enumerate->udev = udev;
186         list_init(&udev_enumerate->devices_list);
187         return udev_enumerate;
188 }
189
190 /**
191  * udev_enumerate_scan_devices:
192  * @udev_enumerate: udev enumeration context
193  * @subsystem: the list of names of subsystems to look for devices
194  *
195  * Returns: 0 on success.
196  **/
197 int udev_enumerate_scan_devices(struct udev_enumerate *udev_enumerate, const char *subsystem, ...)
198 {
199         struct udev *udev = udev_enumerate_get_udev(udev_enumerate);
200         va_list vargs;
201         const char *arg;
202         char base[UTIL_PATH_SIZE];
203         struct stat statbuf;
204         struct list_node subsystem_include_list;
205         struct list_node subsystem_exclude_list;
206         struct udev_list_entry *list_entry;
207
208         if (udev_enumerate == NULL)
209                 return -EINVAL;
210
211         va_start(vargs, subsystem);
212         list_init(&subsystem_include_list);
213         list_init(&subsystem_exclude_list);
214         for (arg = subsystem; arg != NULL; arg = va_arg(vargs, const char *)) {
215                 if (arg[0] != '!')
216                         list_entry_add(udev, &subsystem_include_list, arg, NULL, 1, 0);
217                 else
218                         list_entry_add(udev, &subsystem_exclude_list, &arg[1], NULL, 1, 0);
219         }
220         va_end(vargs);
221
222         util_strlcpy(base, udev_get_sys_path(udev), sizeof(base));
223         util_strlcat(base, "/subsystem", sizeof(base));
224         if (stat(base, &statbuf) == 0) {
225                 /* we have /subsystem/, forget all the old stuff */
226                 info(udev, "searching '/subsystem/*/devices/*' dir\n");
227                 devices_scan_subsystems(udev, "/subsystem", "/devices",
228                                         list_get_entry(&subsystem_include_list),
229                                         list_get_entry(&subsystem_exclude_list),
230                                         &udev_enumerate->devices_list);
231         } else {
232                 info(udev, "searching '/bus/*/devices/*' dir\n");
233                 devices_scan_subsystems(udev, "/bus", "/devices",
234                                         list_get_entry(&subsystem_include_list),
235                                         list_get_entry(&subsystem_exclude_list),
236                                         &udev_enumerate->devices_list);
237                 info(udev, "searching '/class/*' dir\n");
238                 devices_scan_subsystems(udev, "/class", NULL,
239                                         list_get_entry(&subsystem_include_list),
240                                         list_get_entry(&subsystem_exclude_list),
241                                         &udev_enumerate->devices_list);
242                 /* if block isn't a class, scan /block/ */
243                 util_strlcpy(base, udev_get_sys_path(udev), sizeof(base));
244                 util_strlcat(base, "/class/block", sizeof(base));
245                 if (stat(base, &statbuf) != 0) {
246                         struct udev_list_entry *include_list = list_get_entry(&subsystem_include_list);
247                         struct udev_list_entry *exclude_list = list_get_entry(&subsystem_exclude_list);
248                         int include_block = (include_list == NULL || udev_list_entry_get_by_name(include_list, "block") != NULL);
249                         int exclude_block = (udev_list_entry_get_by_name(exclude_list, "block") != NULL);
250
251                         if (include_block && !exclude_block) {
252                                 info(udev, "searching '/block/*' dir\n");
253                                 /* scan disks */
254                                 devices_scan_subsystem(udev, "/block", NULL, NULL, &udev_enumerate->devices_list);
255                                 /* scan partitions */
256                                 info(udev, "searching '/block/*/*' dir\n");
257                                 devices_scan_subsystems(udev, "/block", NULL,
258                                                         NULL, NULL,
259                                                         &udev_enumerate->devices_list);
260                         }
261                 }
262         }
263
264         list_cleanup(udev, &subsystem_include_list);
265         list_cleanup(udev, &subsystem_exclude_list);
266
267         /* sort delayed devices to the end of the list */
268         udev_list_entry_foreach(list_entry, list_get_entry(&udev_enumerate->devices_list)) {
269                 if (devices_delay(udev, udev_list_entry_get_name(list_entry)))
270                         list_entry_move_to_end(list_entry);
271         }
272         return 0;
273 }
274
275 /**
276  * udev_enumerate_scan_subsystems:
277  * @udev_enumerate: udev enumeration context
278  *
279  * Returns: 0 on success.
280  **/
281 int udev_enumerate_scan_subsystems(struct udev_enumerate *udev_enumerate)
282 {
283         struct udev *udev = udev_enumerate_get_udev(udev_enumerate);
284         char base[UTIL_PATH_SIZE];
285         struct stat statbuf;
286         const char *subsysdir;
287
288         if (udev_enumerate == NULL)
289                 return -EINVAL;
290
291         util_strlcpy(base, udev_get_sys_path(udev), sizeof(base));
292         util_strlcat(base, "/subsystem", sizeof(base));
293         if (stat(base, &statbuf) == 0)
294                 subsysdir = "/subsystem";
295         else
296                 subsysdir = "/bus";
297         info(udev, "searching '%s/*' dir\n", subsysdir);
298         devices_scan_subsystem(udev, subsysdir, NULL, NULL, &udev_enumerate->devices_list);
299         info(udev, "searching '%s/*/drivers/*' dir\n", subsysdir);
300         devices_scan_subsystems(udev, subsysdir, "/drivers",
301                                 NULL, NULL,
302                                 &udev_enumerate->devices_list);
303         return 0;
304 }