chiark / gitweb /
b8e4807c478cffd79f7607a286cfd94270e4a713
[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 "config.h"
21
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <stddef.h>
25 #include <unistd.h>
26 #include <errno.h>
27 #include <string.h>
28 #include <dirent.h>
29 #include <sys/stat.h>
30
31 #include "libudev.h"
32 #include "libudev-private.h"
33
34 static int devices_scan_subsystem(struct udev *udev,
35                                   const char *basedir, const char *subsystem, const char *subdir,
36                                   struct list_head *device_list)
37 {
38         char path[UTIL_PATH_SIZE];
39         DIR *dir;
40         struct dirent *dent;
41
42         util_strlcpy(path, udev_get_sys_path(udev), sizeof(path));
43         util_strlcat(path, basedir, sizeof(path));
44         util_strlcat(path, "/", sizeof(path));
45         util_strlcat(path, subsystem, sizeof(path));
46         if (subdir != NULL)
47                 util_strlcat(path, subdir, sizeof(path));
48         dir = opendir(path);
49         if (dir == NULL)
50                 return -1;
51         for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) {
52                 char syspath[UTIL_PATH_SIZE];
53
54                 if (dent->d_name[0] == '.')
55                         continue;
56                 util_strlcpy(syspath, path, sizeof(syspath));
57                 util_strlcat(syspath, "/", sizeof(syspath));
58                 util_strlcat(syspath, dent->d_name, sizeof(syspath));
59                 util_resolve_sys_link(udev, syspath, sizeof(syspath));
60                 util_name_list_add(udev, device_list, syspath, NULL, 1);
61         }
62         closedir(dir);
63         return 0;
64 }
65
66 static int devices_scan_subsystems(struct udev *udev,
67                                    const char *basedir, const char *subsystem, const char *subdir,
68                                    struct list_head *device_list)
69 {
70         char path[UTIL_PATH_SIZE];
71         DIR *dir;
72         struct dirent *dent;
73
74         if (subsystem != NULL)
75                 return devices_scan_subsystem(udev, basedir, subsystem, subdir, device_list);
76
77         util_strlcpy(path, udev_get_sys_path(udev), sizeof(path));
78         util_strlcat(path, basedir, sizeof(path));
79         dir = opendir(path);
80         if (dir == NULL)
81                 return -1;
82         for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) {
83                 if (dent->d_name[0] == '.')
84                         continue;
85                 devices_scan_subsystem(udev, basedir, dent->d_name, subdir, device_list);
86         }
87         closedir(dir);
88         return 0;
89 }
90
91 static int devices_delay(struct udev *udev, const char *syspath)
92 {
93         static const char *delay_device_list[] = {
94                 "/block/md",
95                 "/block/dm-",
96                 NULL
97         };
98         size_t len;
99         int i;
100
101         len = strlen(udev_get_sys_path(udev));
102
103         for (i = 0; delay_device_list[i] != NULL; i++) {
104                 if (strstr(&syspath[len], delay_device_list[i]) != NULL) {
105                         info(udev, "delaying: %s\n", syspath);
106                         return 1;
107                 }
108         }
109         return 0;
110 }
111
112 /**
113  * udev_enumerate_devices:
114  * @udev: udev library context
115  * @subsystem: the subsystem to enumerate
116  * @cb: function to be called for every device found
117  * @data: data to be passed to the function
118  *
119  * Returns: the number of devices passed to the caller, or a negative value on error
120  **/
121 int udev_enumerate_devices(struct udev *udev, const char *subsystem,
122                            int (*cb)(struct udev_device *udev_device, void *data),
123                            void *data)
124 {
125         char base[UTIL_PATH_SIZE];
126         struct stat statbuf;
127         struct list_head device_list;
128         struct util_name_entry *loop_device;
129         struct util_name_entry *tmp_device;
130         int cb_rc = 0;
131         int count = 0;
132
133         INIT_LIST_HEAD(&device_list);
134
135         /* if we have /sys/subsystem/, forget all the old stuff */
136         util_strlcpy(base, udev_get_sys_path(udev), sizeof(base));
137         util_strlcat(base, "/subsystem", sizeof(base));
138         if (stat(base, &statbuf) == 0) {
139                 devices_scan_subsystems(udev, "/subsystem", subsystem, "/devices", &device_list);
140         } else {
141                 devices_scan_subsystems(udev, "/bus", subsystem, "/devices", &device_list);
142                 devices_scan_subsystems(udev, "/class", subsystem, NULL, &device_list);
143         }
144
145         list_for_each_entry_safe(loop_device, tmp_device, &device_list, node) {
146                 if (devices_delay(udev, loop_device->name))
147                         continue;
148                 if (cb_rc == 0) {
149                         struct udev_device *device;
150
151                         device = udev_device_new_from_syspath(udev, loop_device->name);
152                         if (device != NULL) {
153                                 cb_rc = cb(device, data);
154                                 count++;
155                                 udev_device_unref(device);
156                         }
157                 }
158                 list_del(&loop_device->node);
159                 free(loop_device->name);
160                 free(loop_device);
161         }
162
163         /* handle remaining delayed devices */
164         list_for_each_entry_safe(loop_device, tmp_device, &device_list, node) {
165                 if (cb_rc == 0) {
166                         struct udev_device *device;
167
168                         device = udev_device_new_from_syspath(udev, loop_device->name);
169                         if (device != NULL) {
170                                 cb_rc = cb(device, data);
171                                 count++;
172                                 udev_device_unref(device);
173                         }
174                 }
175                 list_del(&loop_device->node);
176                 free(loop_device->name);
177                 free(loop_device);
178         }
179
180         return count;
181 }