chiark / gitweb /
vol_id: fix lib logging glue
[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 static int devices_call(struct udev *udev, const char *syspath,
113                         int (*cb)(struct udev *udev,
114                                   const char *syspath, const char *subsystem, const char *name,
115                                   void *data),
116                         void *data,
117                         int *cb_rc)
118 {
119         char subsystem[UTIL_PATH_SIZE];
120         const char *name;
121
122         name = strrchr(syspath, '/');
123         if (name == NULL)
124                 return -1;
125         name++;
126
127         if (util_get_sys_subsystem(udev, syspath, subsystem, sizeof(subsystem)) < 2)
128                 return -1;
129         *cb_rc = cb(udev, syspath, subsystem, name, data);
130         return 0;
131 }
132
133 /**
134  * udev_enumerate_devices:
135  * @udev_device: udev device
136  * @cb: function to be called for every property found
137  * @data: data to be passed to the function
138  *
139  * Retrieve the property key/value pairs belonging to the
140  * udev device. For every key/value pair, the passed function will be
141  * called. If the function returns 1, remaning properties will be
142  * ignored.
143  *
144  * Returns: the number of properties passed to the caller, or a negative value on error
145  **/
146 int udev_enumerate_devices(struct udev *udev, const char *subsystem,
147                            int (*cb)(struct udev *udev,
148                                      const char *syspath, const char *subsystem, const char *name, void *data),
149                            void *data)
150 {
151         char base[UTIL_PATH_SIZE];
152         struct stat statbuf;
153         struct list_head device_list;
154         struct util_name_entry *loop_device;
155         struct util_name_entry *tmp_device;
156         int cb_rc = 0;
157         int count = 0;
158
159         INIT_LIST_HEAD(&device_list);
160
161         /* if we have /sys/subsystem/, forget all the old stuff */
162         util_strlcpy(base, udev_get_sys_path(udev), sizeof(base));
163         util_strlcat(base, "/subsystem", sizeof(base));
164         if (stat(base, &statbuf) == 0) {
165                 devices_scan_subsystems(udev, "/subsystem", subsystem, "/devices", &device_list);
166         } else {
167                 devices_scan_subsystems(udev, "/bus", subsystem, "/devices", &device_list);
168                 devices_scan_subsystems(udev, "/class", subsystem, NULL, &device_list);
169         }
170
171         list_for_each_entry_safe(loop_device, tmp_device, &device_list, node) {
172                 if (devices_delay(udev, loop_device->name))
173                         continue;
174                 if (cb_rc == 0)
175                         if (devices_call(udev, loop_device->name, cb, data, &cb_rc) == 0)
176                                 count++;
177                 list_del(&loop_device->node);
178                 free(loop_device->name);
179                 free(loop_device);
180         }
181
182         /* handle remaining delayed devices */
183         list_for_each_entry_safe(loop_device, tmp_device, &device_list, node) {
184                 if (cb_rc == 0)
185                         if (devices_call(udev, loop_device->name, cb, data, &cb_rc) == 0)
186                                 count++;
187                 list_del(&loop_device->node);
188                 free(loop_device->name);
189                 free(loop_device);
190         }
191
192         return count;
193 }