chiark / gitweb /
80df8bd72d8d1adc5976200f5a34910f19c8b574
[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_head 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_list *udev_enumerate_get_devices_list(struct udev_enumerate *udev_enumerate)
58 {
59         if (udev_enumerate == NULL)
60                 return NULL;
61         return list_get_entry(&udev_enumerate->devices_list);
62 }
63
64 static int devices_scan_subsystem(struct udev *udev,
65                                   const char *basedir, const char *subsystem, const char *subdir,
66                                   struct list_head *device_list)
67 {
68         char path[UTIL_PATH_SIZE];
69         DIR *dir;
70         struct dirent *dent;
71
72         util_strlcpy(path, udev_get_sys_path(udev), sizeof(path));
73         util_strlcat(path, basedir, sizeof(path));
74         util_strlcat(path, "/", sizeof(path));
75         util_strlcat(path, subsystem, sizeof(path));
76         if (subdir != NULL)
77                 util_strlcat(path, subdir, sizeof(path));
78         dir = opendir(path);
79         if (dir == NULL)
80                 return -1;
81         for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) {
82                 char syspath[UTIL_PATH_SIZE];
83
84                 if (dent->d_name[0] == '.')
85                         continue;
86                 util_strlcpy(syspath, path, sizeof(syspath));
87                 util_strlcat(syspath, "/", sizeof(syspath));
88                 util_strlcat(syspath, dent->d_name, sizeof(syspath));
89                 util_resolve_sys_link(udev, syspath, sizeof(syspath));
90                 list_insert(udev, device_list, syspath, NULL, 1);
91         }
92         closedir(dir);
93         return 0;
94 }
95
96 static int devices_scan_subsystems(struct udev *udev,
97                                    const char *basedir, const char *subsystem, const char *subdir,
98                                    struct list_head *device_list)
99 {
100         char path[UTIL_PATH_SIZE];
101         DIR *dir;
102         struct dirent *dent;
103
104         if (subsystem != NULL)
105                 return devices_scan_subsystem(udev, basedir, subsystem, subdir, device_list);
106
107         util_strlcpy(path, udev_get_sys_path(udev), sizeof(path));
108         util_strlcat(path, basedir, sizeof(path));
109         dir = opendir(path);
110         if (dir == NULL)
111                 return -1;
112         for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) {
113                 if (dent->d_name[0] == '.')
114                         continue;
115                 devices_scan_subsystem(udev, basedir, dent->d_name, subdir, device_list);
116         }
117         closedir(dir);
118         return 0;
119 }
120
121 static int devices_delay(struct udev *udev, const char *syspath)
122 {
123         static const char *delay_device_list[] = {
124                 "/block/md",
125                 "/block/dm-",
126                 NULL
127         };
128         size_t len;
129         int i;
130
131         len = strlen(udev_get_sys_path(udev));
132
133         for (i = 0; delay_device_list[i] != NULL; i++) {
134                 if (strstr(&syspath[len], delay_device_list[i]) != NULL) {
135                         info(udev, "delaying: %s\n", syspath);
136                         return 1;
137                 }
138         }
139         return 0;
140 }
141
142 /**
143  * udev_enumerate_new_from_subsystems:
144  * @udev: udev library context
145  * @subsystem: the subsystem to enumerate
146  *
147  * Returns: an enumeration context
148  **/
149 struct udev_enumerate *udev_enumerate_new_from_subsystems(struct udev *udev, const char *subsystem)
150 {
151         struct udev_enumerate *udev_enumerate;
152         char base[UTIL_PATH_SIZE];
153         struct stat statbuf;
154         struct udev_list *list;
155
156         if (udev == NULL)
157                 return NULL;
158
159         udev_enumerate = malloc(sizeof(struct udev_enumerate));
160         if (udev_enumerate == NULL)
161                 return NULL;
162         memset(udev_enumerate, 0x00, (sizeof(struct udev_enumerate)));
163         udev_enumerate->refcount = 1;
164         udev_enumerate->udev = udev;
165         INIT_LIST_HEAD(&udev_enumerate->devices_list);
166
167         /* if we have /sys/subsystem/, forget all the old stuff */
168         util_strlcpy(base, udev_get_sys_path(udev), sizeof(base));
169         util_strlcat(base, "/subsystem", sizeof(base));
170         if (stat(base, &statbuf) == 0) {
171                 devices_scan_subsystems(udev, "/subsystem", subsystem, "/devices", &udev_enumerate->devices_list);
172         } else {
173                 devices_scan_subsystems(udev, "/bus", subsystem, "/devices", &udev_enumerate->devices_list);
174                 devices_scan_subsystems(udev, "/class", subsystem, NULL, &udev_enumerate->devices_list);
175         }
176
177         /* sort delayed devices to the end of the list */
178         list = list_get_entry(&udev_enumerate->devices_list);
179         while (list != NULL) {
180                 if (devices_delay(udev, udev_list_get_name(list)))
181                         list_move_to_end(list, &udev_enumerate->devices_list);
182                 list = udev_list_get_next(list);
183         }
184         return udev_enumerate;
185 }