]>
Commit | Line | Data |
---|---|---|
eb1f0e66 KS |
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 | #include "../udev.h" | |
34 | ||
ba6929f6 | 35 | struct udev_device *device_init(struct udev *udev) |
eb1f0e66 KS |
36 | { |
37 | struct udev_device *udev_device; | |
38 | ||
ba6929f6 KS |
39 | if (udev == NULL) |
40 | return NULL; | |
41 | ||
eb1f0e66 KS |
42 | udev_device = malloc(sizeof(struct udev_device)); |
43 | if (udev_device == NULL) | |
44 | return NULL; | |
45 | memset(udev_device, 0x00, sizeof(struct udev_device)); | |
46 | udev_device->refcount = 1; | |
47 | udev_device->udev = udev; | |
ba6929f6 KS |
48 | INIT_LIST_HEAD(&udev_device->link_list); |
49 | INIT_LIST_HEAD(&udev_device->env_list); | |
50 | log_info(udev_device->udev, "udev_device: %p created\n", udev_device); | |
eb1f0e66 KS |
51 | return udev_device; |
52 | } | |
53 | ||
54 | /** | |
55 | * udev_device_new_from_devpath: | |
56 | * @udev: udev library context | |
57 | * @devpath: sys device path | |
58 | * | |
59 | * Create new udev device, and fill in information from the sysfs | |
60 | * device and the udev database entry. The devpath must not contain | |
61 | * the sysfs mount path, and must contain a leading '/'. | |
62 | * | |
63 | * The initial refcount is 1, and needs to be decremented to | |
64 | * release the ressources of the udev device. | |
65 | * | |
66 | * Returns: a new udev device, or #NULL, if it does not exist | |
67 | **/ | |
68 | struct udev_device *udev_device_new_from_devpath(struct udev *udev, const char *devpath) | |
69 | { | |
70 | char path[PATH_SIZE]; | |
71 | struct stat statbuf; | |
72 | struct udev_device *udev_device; | |
ba6929f6 KS |
73 | struct udevice *udevice; |
74 | struct name_entry *name_loop; | |
eb1f0e66 KS |
75 | int err; |
76 | ||
ba6929f6 KS |
77 | if (udev == NULL) |
78 | return NULL; | |
79 | if (devpath == NULL) | |
80 | return NULL; | |
81 | ||
eb1f0e66 KS |
82 | strlcpy(path, udev_get_sys_path(udev), sizeof(path)); |
83 | strlcat(path, devpath, sizeof(path)); | |
84 | if (stat(path, &statbuf) != 0) | |
85 | return NULL; | |
86 | if (!S_ISDIR(statbuf.st_mode)) | |
87 | return NULL; | |
88 | ||
89 | udev_device = device_init(udev); | |
90 | if (udev_device == NULL) | |
91 | return NULL; | |
92 | ||
ba6929f6 KS |
93 | udevice = udev_device_init(NULL); |
94 | if (udevice == NULL) { | |
eb1f0e66 KS |
95 | free(udev_device); |
96 | return NULL; | |
97 | } | |
eb1f0e66 | 98 | |
ba6929f6 | 99 | /* resolve possible symlink to real path */ |
eb1f0e66 KS |
100 | strlcpy(path, devpath, sizeof(path)); |
101 | sysfs_resolve_link(path, sizeof(path)); | |
ba6929f6 KS |
102 | udev_device->devpath = strdup(path); |
103 | log_info(udev, "device %p has devpath '%s'\n", udev_device, udev_device_get_devpath(udev_device)); | |
eb1f0e66 | 104 | |
ba6929f6 | 105 | err = udev_db_get_device(udevice, path); |
eb1f0e66 KS |
106 | if (err >= 0) |
107 | log_info(udev, "device %p filled with udev database data\n", udev_device); | |
ba6929f6 KS |
108 | |
109 | if (udevice->name[0] != '\0') | |
110 | asprintf(&udev_device->devname, "%s/%s", udev_get_dev_path(udev), udevice->name); | |
111 | ||
112 | list_for_each_entry(name_loop, &udevice->symlink_list, node) { | |
113 | char name[PATH_SIZE]; | |
114 | ||
115 | strlcpy(name, udev_get_dev_path(udev), sizeof(name)); | |
116 | strlcat(name, "/", sizeof(name)); | |
117 | strlcat(name, name_loop->name, sizeof(name)); | |
118 | name_list_add(&udev_device->link_list, name, 0); | |
119 | } | |
120 | ||
121 | list_for_each_entry(name_loop, &udevice->env_list, node) | |
122 | name_list_add(&udev_device->env_list, name_loop->name, 0); | |
123 | ||
124 | udev_device_cleanup(udevice); | |
eb1f0e66 KS |
125 | return udev_device; |
126 | } | |
127 | ||
128 | /** | |
129 | * udev_device_get_udev: | |
130 | * | |
131 | * Retrieve the udev library context the device was created with. | |
132 | * | |
133 | * Returns: the udev library context | |
134 | **/ | |
135 | struct udev *udev_device_get_udev(struct udev_device *udev_device) | |
136 | { | |
ba6929f6 KS |
137 | if (udev_device == NULL) |
138 | return NULL; | |
eb1f0e66 KS |
139 | return udev_device->udev; |
140 | } | |
141 | ||
142 | /** | |
143 | * udev_device_ref: | |
144 | * @udev_device: udev device | |
145 | * | |
146 | * Take a reference of a udev device. | |
147 | * | |
148 | * Returns: the passed udev device | |
149 | **/ | |
150 | struct udev_device *udev_device_ref(struct udev_device *udev_device) | |
151 | { | |
ba6929f6 KS |
152 | if (udev_device == NULL) |
153 | return NULL; | |
eb1f0e66 KS |
154 | udev_device->refcount++; |
155 | return udev_device; | |
156 | } | |
157 | ||
158 | /** | |
159 | * udev_device_unref: | |
160 | * @udev_device: udev device | |
161 | * | |
162 | * Drop a reference of a udev device. If the refcount reaches zero, | |
163 | * the ressources of the device will be released. | |
164 | * | |
165 | **/ | |
166 | void udev_device_unref(struct udev_device *udev_device) | |
167 | { | |
ba6929f6 KS |
168 | if (udev_device == NULL) |
169 | return; | |
eb1f0e66 KS |
170 | udev_device->refcount--; |
171 | if (udev_device->refcount > 0) | |
172 | return; | |
ba6929f6 KS |
173 | free(udev_device->devpath); |
174 | free(udev_device->devname); | |
175 | free(udev_device->subsystem); | |
176 | name_list_cleanup(&udev_device->link_list); | |
177 | name_list_cleanup(&udev_device->env_list); | |
178 | log_info(udev_device->udev, "udev_device: %p released\n", udev_device); | |
eb1f0e66 KS |
179 | free(udev_device); |
180 | } | |
181 | ||
182 | /** | |
183 | * udev_device_get_devpath: | |
184 | * @udev_device: udev device | |
185 | * | |
186 | * Retrieve the sys path of the udev device. The path does not contain | |
187 | * the sys mount point. | |
188 | * | |
189 | * Returns: the sys path of the udev device | |
190 | **/ | |
191 | const char *udev_device_get_devpath(struct udev_device *udev_device) | |
192 | { | |
ba6929f6 KS |
193 | if (udev_device == NULL) |
194 | return NULL; | |
195 | return udev_device->devpath; | |
eb1f0e66 KS |
196 | } |
197 | ||
198 | /** | |
199 | * udev_device_get_devname: | |
200 | * @udev_device: udev device | |
201 | * | |
202 | * Retrieve the device node file name belonging to the udev device. | |
ba6929f6 | 203 | * The path is an absolute path, and starts with the device directory. |
eb1f0e66 KS |
204 | * |
205 | * Returns: the device node file name of the udev device, or #NULL if no device node exists | |
206 | **/ | |
207 | const char *udev_device_get_devname(struct udev_device *udev_device) | |
208 | { | |
ba6929f6 | 209 | if (udev_device == NULL) |
eb1f0e66 | 210 | return NULL; |
ba6929f6 | 211 | return udev_device->devname; |
eb1f0e66 KS |
212 | } |
213 | ||
214 | /** | |
215 | * udev_device_get_subsystem: | |
216 | * @udev_device: udev device | |
217 | * | |
218 | * Retrieve the subsystem string of the udev device. The string does not | |
219 | * contain any "/". | |
220 | * | |
221 | * Returns: the subsystem name of the udev device, or #NULL if it can not be determined | |
222 | **/ | |
223 | const char *udev_device_get_subsystem(struct udev_device *udev_device) | |
224 | { | |
ba6929f6 KS |
225 | char subsystem[NAME_SIZE]; |
226 | ||
227 | if (udev_device == NULL) | |
228 | return NULL; | |
229 | if (udev_device->subsystem != NULL) | |
230 | return udev_device->subsystem; | |
231 | if (util_get_sys_subsystem(udev_device->udev, udev_device->devpath, subsystem, sizeof(subsystem)) < 2) | |
eb1f0e66 | 232 | return NULL; |
ba6929f6 KS |
233 | udev_device->subsystem = strdup(subsystem); |
234 | return udev_device->subsystem; | |
eb1f0e66 KS |
235 | } |
236 | ||
237 | /** | |
238 | * udev_device_get_devlinks: | |
239 | * @udev_device: udev device | |
240 | * @cb: function to be called for every device link found | |
241 | * @data: data to be passed to the function | |
242 | * | |
243 | * Retrieve the device links pointing to the device file of the | |
244 | * udev device. For every device link, the passed function will be | |
ba6929f6 KS |
245 | * called with the device link string. |
246 | * The path is an absolute path, and starts with the device directory. | |
247 | * If the function returns 1, remaning device links will be ignored. | |
eb1f0e66 KS |
248 | * |
249 | * Returns: the number of device links passed to the caller, or a negative value on error | |
250 | **/ | |
251 | int udev_device_get_devlinks(struct udev_device *udev_device, | |
252 | int (*cb)(struct udev_device *udev_device, const char *value, void *data), | |
253 | void *data) | |
254 | { | |
255 | struct name_entry *name_loop; | |
256 | int count = 0; | |
257 | ||
ba6929f6 KS |
258 | if (udev_device == NULL) |
259 | return -1; | |
260 | list_for_each_entry(name_loop, &udev_device->link_list, node) { | |
eb1f0e66 KS |
261 | count++; |
262 | if (cb(udev_device, name_loop->name, data) != 0) | |
263 | break; | |
264 | } | |
265 | return count; | |
266 | } | |
267 | ||
268 | /** | |
269 | * udev_device_get_properties: | |
270 | * @udev_device: udev device | |
271 | * @cb: function to be called for every property found | |
272 | * @data: data to be passed to the function | |
273 | * | |
274 | * Retrieve the property key/value pairs belonging to the | |
275 | * udev device. For every key/value pair, the passed function will be | |
276 | * called. If the function returns 1, remaning properties will be | |
277 | * ignored. | |
278 | * | |
279 | * Returns: the number of properties passed to the caller, or a negative value on error | |
280 | **/ | |
281 | int udev_device_get_properties(struct udev_device *udev_device, | |
282 | int (*cb)(struct udev_device *udev_device, const char *key, const char *value, void *data), | |
283 | void *data) | |
284 | { | |
285 | struct name_entry *name_loop; | |
286 | int count = 0; | |
287 | ||
ba6929f6 KS |
288 | if (udev_device == NULL) |
289 | return -1; | |
290 | list_for_each_entry(name_loop, &udev_device->env_list, node) { | |
eb1f0e66 KS |
291 | char name[PATH_SIZE]; |
292 | char *val; | |
293 | ||
294 | strncpy(name, name_loop->name, PATH_SIZE); | |
295 | name[PATH_SIZE-1] = '\0'; | |
296 | val = strchr(name, '='); | |
297 | if (val == NULL) | |
298 | continue; | |
299 | val[0] = '\0'; | |
300 | val = &val[1]; | |
ba6929f6 | 301 | count++; |
eb1f0e66 KS |
302 | if (cb(udev_device, name, val, data) != 0) |
303 | break; | |
304 | } | |
305 | return count; | |
306 | } |