chiark / gitweb /
libudev: enumerate "subsystem"
[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                 struct stat statbuf;
93
94                 if (dent->d_name[0] == '.')
95                         continue;
96                 util_strlcpy(syspath, path, sizeof(syspath));
97                 util_strlcat(syspath, "/", sizeof(syspath));
98                 util_strlcat(syspath, dent->d_name, sizeof(syspath));
99                 if (stat(syspath, &statbuf) != 0)
100                         continue;
101                 if (!S_ISDIR(statbuf.st_mode))
102                         continue;
103                 util_resolve_sys_link(udev, syspath, sizeof(syspath));
104                 list_entry_add(udev, devices_list, syspath, NULL, 1, 1);
105         }
106         closedir(dir);
107         return 0;
108 }
109
110 static int devices_scan_subsystems(struct udev *udev,
111                                    const char *basedir, const char *subdir,
112                                    struct udev_list_entry *subsystem_include_list,
113                                    struct udev_list_entry *subsystem_exclude_list,
114                                    struct list_node *devices_list)
115 {
116         if (subsystem_include_list != NULL) {
117                 struct udev_list_entry *list_entry;
118
119                 /* if list of subsystems to scan is given, just use this list */
120                 udev_list_entry_foreach(list_entry, subsystem_include_list)
121                         devices_scan_subsystem(udev, basedir, udev_list_entry_get_name(list_entry), subdir, devices_list);
122         } else {
123                 char path[UTIL_PATH_SIZE];
124                 DIR *dir;
125                 struct dirent *dent;
126
127                 /* if no list of subsystems to scan is given, scan all, and possible exclude some subsystems */
128                 util_strlcpy(path, udev_get_sys_path(udev), sizeof(path));
129                 util_strlcat(path, basedir, sizeof(path));
130                 dir = opendir(path);
131                 if (dir == NULL)
132                         return -1;
133                 for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) {
134                         if (dent->d_name[0] == '.')
135                                 continue;
136                         if (udev_list_entry_get_by_name(subsystem_exclude_list, dent->d_name) != NULL)
137                                         continue;
138                         devices_scan_subsystem(udev, basedir, dent->d_name, subdir, devices_list);
139                 }
140                 closedir(dir);
141         }
142         return 0;
143 }
144
145 static int devices_delay(struct udev *udev, const char *syspath)
146 {
147         static const char *delay_device_list[] = {
148                 "/block/md",
149                 "/block/dm-",
150                 NULL
151         };
152         size_t len;
153         int i;
154
155         len = strlen(udev_get_sys_path(udev));
156
157         for (i = 0; delay_device_list[i] != NULL; i++) {
158                 if (strstr(&syspath[len], delay_device_list[i]) != NULL) {
159                         info(udev, "delaying: %s\n", syspath);
160                         return 1;
161                 }
162         }
163         return 0;
164 }
165
166 static struct udev_enumerate *enumerate_new(struct udev *udev)
167 {
168         struct udev_enumerate *udev_enumerate;
169
170         udev_enumerate = malloc(sizeof(struct udev_enumerate));
171         if (udev_enumerate == NULL)
172                 return NULL;
173         memset(udev_enumerate, 0x00, (sizeof(struct udev_enumerate)));
174         udev_enumerate->refcount = 1;
175         udev_enumerate->udev = udev;
176         list_init(&udev_enumerate->devices_list);
177         return udev_enumerate;
178 }
179
180 /**
181  * udev_enumerate_new_from_devices:
182  * @udev: udev library context
183  * @subsystem: the list of names of subsystems to look for devices
184  *
185  * Returns: an enumeration context
186  **/
187 struct udev_enumerate *udev_enumerate_new_from_devices(struct udev *udev, const char *subsystem, ...)
188 {
189         struct udev_enumerate *udev_enumerate;
190         va_list vargs;
191         const char *arg;
192         char base[UTIL_PATH_SIZE];
193         struct stat statbuf;
194         struct list_node subsystem_include_list;
195         struct list_node subsystem_exclude_list;
196         struct udev_list_entry *list_entry;
197
198         if (udev == NULL)
199                 return NULL;
200
201         udev_enumerate = enumerate_new(udev);
202         if (udev_enumerate == NULL)
203                 return NULL;
204
205         va_start(vargs, subsystem);
206         list_init(&subsystem_include_list);
207         list_init(&subsystem_exclude_list);
208         for (arg = subsystem; arg != NULL; arg = va_arg(vargs, const char *)) {
209                 if (arg[0] != '!')
210                         list_entry_add(udev, &subsystem_include_list, arg, NULL, 1, 0);
211                 else
212                         list_entry_add(udev, &subsystem_exclude_list, &arg[1], NULL, 1, 0);
213         }
214         va_end(vargs);
215
216         /* if we have /sys/subsystem/, forget all the old stuff */
217         util_strlcpy(base, udev_get_sys_path(udev), sizeof(base));
218         util_strlcat(base, "/subsystem", sizeof(base));
219         if (stat(base, &statbuf) == 0) {
220                 info(udev, "searching '/subsystem/*/devices/*' dir\n");
221                 devices_scan_subsystems(udev, "/subsystem", "/devices",
222                                         list_get_entry(&subsystem_include_list),
223                                         list_get_entry(&subsystem_exclude_list),
224                                         &udev_enumerate->devices_list);
225         } else {
226                 info(udev, "searching '/bus/*/devices/*' dir\n");
227                 devices_scan_subsystems(udev, "/bus", "/devices",
228                                         list_get_entry(&subsystem_include_list),
229                                         list_get_entry(&subsystem_exclude_list),
230                                         &udev_enumerate->devices_list);
231                 info(udev, "searching '/class/*' dir\n");
232                 devices_scan_subsystems(udev, "/class", NULL,
233                                         list_get_entry(&subsystem_include_list),
234                                         list_get_entry(&subsystem_exclude_list),
235                                         &udev_enumerate->devices_list);
236         }
237
238         list_cleanup(udev, &subsystem_include_list);
239         list_cleanup(udev, &subsystem_exclude_list);
240
241         /* sort delayed devices to the end of the list */
242         udev_list_entry_foreach(list_entry, list_get_entry(&udev_enumerate->devices_list)) {
243                 if (devices_delay(udev, udev_list_entry_get_name(list_entry)))
244                         list_entry_move_to_end(list_entry);
245         }
246         return udev_enumerate;
247 }
248
249 struct udev_enumerate *udev_enumerate_new_from_subsystems(struct udev *udev)
250 {
251         struct udev_enumerate *udev_enumerate;
252         char base[UTIL_PATH_SIZE];
253         struct stat statbuf;
254         const char *subsysdir;
255
256         if (udev == NULL)
257                 return NULL;
258
259         udev_enumerate = enumerate_new(udev);
260         if (udev_enumerate == NULL)
261                 return NULL;
262
263         util_strlcpy(base, udev_get_sys_path(udev), sizeof(base));
264         util_strlcat(base, "/subsystem", sizeof(base));
265         if (stat(base, &statbuf) == 0)
266                 subsysdir = "/subsystem";
267         else
268                 subsysdir = "/bus";
269         info(udev, "searching '%s/*' dir\n", subsysdir);
270         devices_scan_subsystem(udev, subsysdir, NULL, NULL, &udev_enumerate->devices_list);
271         info(udev, "searching '%s/*/drivers/*' dir\n", subsysdir);
272         devices_scan_subsystems(udev, subsysdir, "/drivers",
273                                 NULL, NULL,
274                                 &udev_enumerate->devices_list);
275         return udev_enumerate;
276 }