chiark / gitweb /
c7fb683c1084354e37c78bb9f5c397993dfe7de0
[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         util_strlcat(path, "/", sizeof(path));
82         util_strlcat(path, subsystem, sizeof(path));
83         if (subdir != NULL)
84                 util_strlcat(path, subdir, sizeof(path));
85         dir = opendir(path);
86         if (dir == NULL)
87                 return -1;
88         for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) {
89                 char syspath[UTIL_PATH_SIZE];
90
91                 if (dent->d_name[0] == '.')
92                         continue;
93                 util_strlcpy(syspath, path, sizeof(syspath));
94                 util_strlcat(syspath, "/", sizeof(syspath));
95                 util_strlcat(syspath, dent->d_name, sizeof(syspath));
96                 util_resolve_sys_link(udev, syspath, sizeof(syspath));
97                 list_entry_add(udev, devices_list, syspath, NULL, 1, 1);
98         }
99         closedir(dir);
100         return 0;
101 }
102
103 static int devices_scan_subsystems(struct udev *udev,
104                                    const char *basedir, const char *subdir,
105                                    struct udev_list_entry *subsystem_include_list,
106                                    struct udev_list_entry *subsystem_exclude_list,
107                                    struct list_node *devices_list)
108 {
109         if (subsystem_include_list != NULL) {
110                 struct udev_list_entry *list_entry;
111
112                 /* if list of subsystems to scan is given, just use this list */
113                 udev_list_entry_foreach(list_entry, subsystem_include_list)
114                         devices_scan_subsystem(udev, basedir, udev_list_entry_get_name(list_entry), subdir, devices_list);
115         } else {
116                 char path[UTIL_PATH_SIZE];
117                 DIR *dir;
118                 struct dirent *dent;
119
120                 /* if no list of subsystems to scan is given, scan all, and possible exclude some subsystems */
121                 util_strlcpy(path, udev_get_sys_path(udev), sizeof(path));
122                 util_strlcat(path, basedir, sizeof(path));
123                 dir = opendir(path);
124                 if (dir == NULL)
125                         return -1;
126                 for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) {
127                         if (dent->d_name[0] == '.')
128                                 continue;
129                         if (udev_list_entry_get_by_name(subsystem_exclude_list, dent->d_name) != NULL)
130                                         continue;
131                         devices_scan_subsystem(udev, basedir, dent->d_name, subdir, devices_list);
132                 }
133                 closedir(dir);
134         }
135         return 0;
136 }
137
138 static int devices_delay(struct udev *udev, const char *syspath)
139 {
140         static const char *delay_device_list[] = {
141                 "/block/md",
142                 "/block/dm-",
143                 NULL
144         };
145         size_t len;
146         int i;
147
148         len = strlen(udev_get_sys_path(udev));
149
150         for (i = 0; delay_device_list[i] != NULL; i++) {
151                 if (strstr(&syspath[len], delay_device_list[i]) != NULL) {
152                         info(udev, "delaying: %s\n", syspath);
153                         return 1;
154                 }
155         }
156         return 0;
157 }
158
159 /**
160  * udev_enumerate_new_from_devices:
161  * @udev: udev library context
162  * @subsystem: the list of names of subsystems to look for devices
163  *
164  * Returns: an enumeration context
165  **/
166 struct udev_enumerate *udev_enumerate_new_from_devices(struct udev *udev, const char *subsystem, ...)
167 {
168         struct udev_enumerate *udev_enumerate;
169         va_list vargs;
170         const char *arg;
171         char base[UTIL_PATH_SIZE];
172         struct stat statbuf;
173         struct list_node subsystem_include_list;
174         struct list_node subsystem_exclude_list;
175         struct udev_list_entry *list_entry;
176
177         if (udev == NULL)
178                 return NULL;
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
188         va_start(vargs, subsystem);
189         list_init(&subsystem_include_list);
190         list_init(&subsystem_exclude_list);
191         for (arg = subsystem; arg != NULL; arg = va_arg(vargs, const char *)) {
192                 if (arg[0] != '!')
193                         list_entry_add(udev, &subsystem_include_list, arg, NULL, 1, 0);
194                 else
195                         list_entry_add(udev, &subsystem_exclude_list, &arg[1], NULL, 1, 0);
196         }
197         va_end(vargs);
198
199         /* if we have /sys/subsystem/, forget all the old stuff */
200         util_strlcpy(base, udev_get_sys_path(udev), sizeof(base));
201         util_strlcat(base, "/subsystem", sizeof(base));
202         if (stat(base, &statbuf) == 0) {
203                 info(udev, "searching 'subsystem/*/devices/*' dir\n");
204                 devices_scan_subsystems(udev, "/subsystem", "/devices",
205                                         list_get_entry(&subsystem_include_list),
206                                         list_get_entry(&subsystem_exclude_list),
207                                         &udev_enumerate->devices_list);
208         } else {
209                 info(udev, "searching 'bus/*/devices/*' dir\n");
210                 devices_scan_subsystems(udev, "/bus", "/devices",
211                                         list_get_entry(&subsystem_include_list),
212                                         list_get_entry(&subsystem_exclude_list),
213                                         &udev_enumerate->devices_list);
214                 info(udev, "searching 'class/*' dir\n");
215                 devices_scan_subsystems(udev, "/class", NULL,
216                                         list_get_entry(&subsystem_include_list),
217                                         list_get_entry(&subsystem_exclude_list),
218                                         &udev_enumerate->devices_list);
219         }
220
221         list_cleanup(udev, &subsystem_include_list);
222         list_cleanup(udev, &subsystem_exclude_list);
223
224         /* sort delayed devices to the end of the list */
225         udev_list_entry_foreach(list_entry, list_get_entry(&udev_enumerate->devices_list)) {
226                 if (devices_delay(udev, udev_list_entry_get_name(list_entry)))
227                         list_entry_move_to_end(list_entry);
228         }
229         return udev_enumerate;
230 }
231
232 struct udev_enumerate *udev_enumerate_new_from_subsystems(struct udev *udev)
233 {
234         return NULL;
235 }