]>
Commit | Line | Data |
---|---|---|
eb1f0e66 KS |
1 | /* |
2 | * libudev - interface to udev device information | |
3 | * | |
065db052 | 4 | * Copyright (C) 2008-2009 Kay Sievers <kay.sievers@vrfy.org> |
eb1f0e66 | 5 | * |
4061ab9f KS |
6 | * This library is free software; you can redistribute it and/or |
7 | * modify it under the terms of the GNU Lesser General Public | |
8 | * License as published by the Free Software Foundation; either | |
9 | * version 2.1 of the License, or (at your option) any later version. | |
eb1f0e66 KS |
10 | */ |
11 | ||
eb1f0e66 KS |
12 | #include <stdio.h> |
13 | #include <stdlib.h> | |
14 | #include <stddef.h> | |
15 | #include <unistd.h> | |
16 | #include <errno.h> | |
17 | #include <string.h> | |
18 | #include <dirent.h> | |
93b0f384 | 19 | #include <fcntl.h> |
517814e7 | 20 | #include <ctype.h> |
eb1f0e66 KS |
21 | #include <sys/stat.h> |
22 | ||
23 | #include "libudev.h" | |
24 | #include "libudev-private.h" | |
eb1f0e66 | 25 | |
1e511322 KS |
26 | /** |
27 | * udev_device: | |
28 | * | |
29 | * Representation of a kernel sys device. | |
30 | */ | |
11d543c1 | 31 | struct udev_device { |
11d543c1 | 32 | struct udev *udev; |
b2d9e4f2 | 33 | struct udev_device *parent_device; |
11d543c1 | 34 | char *syspath; |
4ad3a37f | 35 | const char *devpath; |
517814e7 KS |
36 | char *sysname; |
37 | const char *sysnum; | |
99214844 | 38 | char *devnode; |
11d543c1 | 39 | char *subsystem; |
bf8b2ae1 | 40 | char *devtype; |
979ff016 | 41 | char *driver; |
c4f5f942 | 42 | char *action; |
c4f5f942 | 43 | char *devpath_old; |
cb14f454 | 44 | char *knodename; |
6493e655 | 45 | char **envp; |
c2654402 KS |
46 | char *monitor_buf; |
47 | size_t monitor_buf_len; | |
b99028c9 KS |
48 | struct udev_list_node devlinks_list; |
49 | struct udev_list_node properties_list; | |
50 | struct udev_list_node sysattr_list; | |
6bd1c78a | 51 | unsigned long long int seqnum; |
b99028c9 KS |
52 | int event_timeout; |
53 | int timeout; | |
6bd1c78a | 54 | int num_fake_partitions; |
e88a82b5 | 55 | int devlink_priority; |
b99028c9 KS |
56 | int refcount; |
57 | dev_t devnum; | |
d7ce7539 | 58 | int watch_handle; |
b99028c9 KS |
59 | unsigned int parent_set:1; |
60 | unsigned int subsystem_set:1; | |
bf8b2ae1 | 61 | unsigned int devtype_set:1; |
b99028c9 KS |
62 | unsigned int devlinks_uptodate:1; |
63 | unsigned int envp_uptodate:1; | |
64 | unsigned int driver_set:1; | |
65 | unsigned int info_loaded:1; | |
66 | unsigned int ignore_remove:1; | |
11d543c1 KS |
67 | }; |
68 | ||
9a997ecf | 69 | static size_t devpath_to_db_path(struct udev *udev, const char *devpath, char *filename, size_t len) |
e88a82b5 | 70 | { |
065db052 KS |
71 | char *s; |
72 | size_t l; | |
e88a82b5 | 73 | |
065db052 KS |
74 | s = filename; |
75 | l = util_strpcpyl(&s, len, udev_get_dev_path(udev), "/.udev/db/", NULL); | |
81469dae | 76 | return util_path_encode(devpath, s, l); |
e88a82b5 KS |
77 | } |
78 | ||
77b852f3 | 79 | int udev_device_read_db(struct udev_device *udev_device) |
e88a82b5 KS |
80 | { |
81 | struct stat stats; | |
3eb46ec6 KS |
82 | char filename[UTIL_PATH_SIZE]; |
83 | char line[UTIL_LINE_SIZE]; | |
e88a82b5 | 84 | FILE *f; |
e88a82b5 | 85 | |
9a997ecf | 86 | devpath_to_db_path(udev_device->udev, udev_device->devpath, filename, sizeof(filename)); |
e88a82b5 KS |
87 | |
88 | if (lstat(filename, &stats) != 0) { | |
86b57788 | 89 | dbg(udev_device->udev, "no db file to read %s: %m\n", filename); |
e88a82b5 KS |
90 | return -1; |
91 | } | |
92 | if ((stats.st_mode & S_IFMT) == S_IFLNK) { | |
3eb46ec6 | 93 | char target[UTIL_PATH_SIZE]; |
517814e7 | 94 | char devnode[UTIL_PATH_SIZE]; |
e88a82b5 | 95 | int target_len; |
1e75cda3 | 96 | char *next; |
e88a82b5 | 97 | |
e88a82b5 KS |
98 | target_len = readlink(filename, target, sizeof(target)); |
99 | if (target_len > 0) | |
100 | target[target_len] = '\0'; | |
101 | else { | |
86b57788 | 102 | dbg(udev_device->udev, "error reading db link %s: %m\n", filename); |
e88a82b5 KS |
103 | return -1; |
104 | } | |
1e75cda3 KS |
105 | |
106 | next = strchr(target, ' '); | |
107 | if (next != NULL) { | |
108 | next[0] = '\0'; | |
109 | next = &next[1]; | |
110 | } | |
065db052 | 111 | util_strscpyl(devnode, sizeof(devnode), udev_get_dev_path(udev_device->udev), "/", target, NULL); |
517814e7 | 112 | udev_device_set_devnode(udev_device, devnode); |
1e75cda3 | 113 | while (next != NULL) { |
517814e7 | 114 | char devlink[UTIL_PATH_SIZE]; |
1e75cda3 KS |
115 | const char *lnk; |
116 | ||
117 | lnk = next; | |
118 | next = strchr(next, ' '); | |
119 | if (next != NULL) { | |
120 | next[0] = '\0'; | |
121 | next = &next[1]; | |
122 | } | |
065db052 | 123 | util_strscpyl(devlink, sizeof(devlink), udev_get_dev_path(udev_device->udev), "/", lnk, NULL); |
517814e7 | 124 | udev_device_add_devlink(udev_device, devlink); |
1e75cda3 | 125 | } |
99214844 | 126 | info(udev_device->udev, "device %p filled with db symlink data '%s'\n", udev_device, udev_device->devnode); |
e88a82b5 KS |
127 | return 0; |
128 | } | |
129 | ||
130 | f = fopen(filename, "r"); | |
131 | if (f == NULL) { | |
86b57788 | 132 | dbg(udev_device->udev, "error reading db file %s: %m\n", filename); |
e88a82b5 KS |
133 | return -1; |
134 | } | |
135 | while (fgets(line, sizeof(line), f)) { | |
136 | ssize_t len; | |
137 | const char *val; | |
e88a82b5 KS |
138 | |
139 | len = strlen(line); | |
140 | if (len < 4) | |
141 | break; | |
142 | line[len-1] = '\0'; | |
143 | val = &line[2]; | |
e88a82b5 KS |
144 | switch(line[0]) { |
145 | case 'N': | |
065db052 | 146 | util_strscpyl(filename, sizeof(filename), udev_get_dev_path(udev_device->udev), "/", val, NULL); |
517814e7 | 147 | udev_device_set_devnode(udev_device, filename); |
e88a82b5 | 148 | break; |
e88a82b5 | 149 | case 'S': |
065db052 | 150 | util_strscpyl(filename, sizeof(filename), udev_get_dev_path(udev_device->udev), "/", val, NULL); |
8cd2e972 | 151 | udev_device_add_devlink(udev_device, filename); |
e88a82b5 KS |
152 | break; |
153 | case 'L': | |
8cd2e972 | 154 | udev_device_set_devlink_priority(udev_device, atoi(val)); |
e88a82b5 KS |
155 | break; |
156 | case 'T': | |
8cd2e972 | 157 | udev_device_set_event_timeout(udev_device, atoi(val)); |
e88a82b5 KS |
158 | break; |
159 | case 'A': | |
8cd2e972 | 160 | udev_device_set_num_fake_partitions(udev_device, atoi(val)); |
e88a82b5 KS |
161 | break; |
162 | case 'R': | |
8cd2e972 | 163 | udev_device_set_ignore_remove(udev_device, atoi(val)); |
e88a82b5 KS |
164 | break; |
165 | case 'E': | |
8cd2e972 | 166 | udev_device_add_property_from_string(udev_device, val); |
e88a82b5 | 167 | break; |
d7ce7539 SJR |
168 | case 'W': |
169 | udev_device_set_watch_handle(udev_device, atoi(val)); | |
170 | break; | |
e88a82b5 KS |
171 | } |
172 | } | |
173 | fclose(f); | |
174 | ||
cd42b50d | 175 | info(udev_device->udev, "device %p filled with db file data\n", udev_device); |
04f5d75f | 176 | return 0; |
e88a82b5 KS |
177 | } |
178 | ||
bd85566c | 179 | int udev_device_read_uevent_file(struct udev_device *udev_device) |
99214844 KS |
180 | { |
181 | char filename[UTIL_PATH_SIZE]; | |
182 | FILE *f; | |
183 | char line[UTIL_LINE_SIZE]; | |
184 | int maj = 0; | |
185 | int min = 0; | |
186 | ||
065db052 | 187 | util_strscpyl(filename, sizeof(filename), udev_device->syspath, "/uevent", NULL); |
99214844 KS |
188 | f = fopen(filename, "r"); |
189 | if (f == NULL) | |
190 | return -1; | |
191 | ||
192 | while (fgets(line, sizeof(line), f)) { | |
193 | char *pos; | |
194 | ||
195 | pos = strchr(line, '\n'); | |
196 | if (pos == NULL) | |
197 | continue; | |
198 | pos[0] = '\0'; | |
199 | ||
bf8b2ae1 MH |
200 | if (strncmp(line, "DEVTYPE=", 8) == 0) |
201 | udev_device_set_devtype(udev_device, &line[8]); | |
202 | else if (strncmp(line, "MAJOR=", 6) == 0) | |
99214844 KS |
203 | maj = strtoull(&line[6], NULL, 10); |
204 | else if (strncmp(line, "MINOR=", 6) == 0) | |
205 | min = strtoull(&line[6], NULL, 10); | |
cb14f454 KS |
206 | else if (strncmp(line, "DEVNAME=", 8) == 0) |
207 | udev_device_set_knodename(udev_device, &line[8]); | |
99214844 | 208 | |
8cd2e972 | 209 | udev_device_add_property_from_string(udev_device, line); |
99214844 KS |
210 | } |
211 | ||
212 | udev_device->devnum = makedev(maj, min); | |
213 | ||
214 | fclose(f); | |
215 | return 0; | |
216 | } | |
217 | ||
77b852f3 | 218 | static void device_load_info(struct udev_device *device) |
99214844 | 219 | { |
517814e7 | 220 | device->info_loaded = 1; |
bd85566c | 221 | udev_device_read_uevent_file(device); |
77b852f3 | 222 | udev_device_read_db(device); |
99214844 KS |
223 | } |
224 | ||
8cd2e972 | 225 | void udev_device_set_info_loaded(struct udev_device *device) |
99214844 KS |
226 | { |
227 | device->info_loaded = 1; | |
228 | } | |
229 | ||
a5710160 | 230 | struct udev_device *udev_device_new(struct udev *udev) |
eb1f0e66 KS |
231 | { |
232 | struct udev_device *udev_device; | |
ebacd6ec | 233 | struct udev_list_entry *list_entry; |
eb1f0e66 | 234 | |
ba6929f6 KS |
235 | if (udev == NULL) |
236 | return NULL; | |
237 | ||
b29a5e4a | 238 | udev_device = calloc(1, sizeof(struct udev_device)); |
eb1f0e66 KS |
239 | if (udev_device == NULL) |
240 | return NULL; | |
eb1f0e66 KS |
241 | udev_device->refcount = 1; |
242 | udev_device->udev = udev; | |
517814e7 | 243 | udev_list_init(&udev_device->devlinks_list); |
8cd2e972 | 244 | udev_list_init(&udev_device->properties_list); |
69239210 | 245 | udev_list_init(&udev_device->sysattr_list); |
979ff016 | 246 | udev_device->event_timeout = -1; |
d7ce7539 | 247 | udev_device->watch_handle = -1; |
ebacd6ec KS |
248 | /* copy global properties */ |
249 | udev_list_entry_foreach(list_entry, udev_get_properties_list_entry(udev)) | |
250 | udev_device_add_property(udev_device, | |
251 | udev_list_entry_get_name(list_entry), | |
252 | udev_list_entry_get_value(list_entry)); | |
86b57788 | 253 | dbg(udev_device->udev, "udev_device: %p created\n", udev_device); |
eb1f0e66 KS |
254 | return udev_device; |
255 | } | |
256 | ||
257 | /** | |
8753fadf | 258 | * udev_device_new_from_syspath: |
eb1f0e66 | 259 | * @udev: udev library context |
8753fadf | 260 | * @syspath: sys device path including sys directory |
eb1f0e66 | 261 | * |
8753fadf KS |
262 | * Create new udev device, and fill in information from the sys |
263 | * device and the udev database entry. The sypath is the absolute | |
264 | * path to the device, including the sys mount point. | |
eb1f0e66 KS |
265 | * |
266 | * The initial refcount is 1, and needs to be decremented to | |
be7de409 | 267 | * release the resources of the udev device. |
eb1f0e66 KS |
268 | * |
269 | * Returns: a new udev device, or #NULL, if it does not exist | |
270 | **/ | |
8753fadf | 271 | struct udev_device *udev_device_new_from_syspath(struct udev *udev, const char *syspath) |
eb1f0e66 | 272 | { |
b95f8a76 KS |
273 | size_t len; |
274 | const char *subdir; | |
3eb46ec6 | 275 | char path[UTIL_PATH_SIZE]; |
62b9dfb6 | 276 | char *pos; |
eb1f0e66 KS |
277 | struct stat statbuf; |
278 | struct udev_device *udev_device; | |
eb1f0e66 | 279 | |
ba6929f6 KS |
280 | if (udev == NULL) |
281 | return NULL; | |
8753fadf | 282 | if (syspath == NULL) |
ba6929f6 KS |
283 | return NULL; |
284 | ||
b95f8a76 KS |
285 | /* path starts in sys */ |
286 | len = strlen(udev_get_sys_path(udev)); | |
287 | if (strncmp(syspath, udev_get_sys_path(udev), len) != 0) { | |
288 | info(udev, "not in sys :%s\n", syspath); | |
eb1f0e66 | 289 | return NULL; |
4ad3a37f | 290 | } |
eb1f0e66 | 291 | |
b95f8a76 KS |
292 | /* path is not a root directory */ |
293 | subdir = &syspath[len+1]; | |
294 | pos = strrchr(subdir, '/'); | |
f454f670 | 295 | if (pos == NULL || pos[1] == '\0' || pos < &subdir[2]) { |
86b57788 | 296 | dbg(udev, "not a subdir :%s\n", syspath); |
eb1f0e66 | 297 | return NULL; |
b95f8a76 | 298 | } |
eb1f0e66 | 299 | |
ba6929f6 | 300 | /* resolve possible symlink to real path */ |
065db052 | 301 | util_strscpy(path, sizeof(path), syspath); |
b21b95d7 | 302 | util_resolve_sys_link(udev, path, sizeof(path)); |
b95f8a76 | 303 | |
62b9dfb6 KS |
304 | /* try to resolve the silly block layout if needed */ |
305 | if (strncmp(&path[len], "/block/", 7) == 0) { | |
306 | char block[UTIL_PATH_SIZE]; | |
307 | char part[UTIL_PATH_SIZE]; | |
308 | ||
065db052 | 309 | util_strscpy(block, sizeof(block), path); |
62b9dfb6 KS |
310 | pos = strrchr(block, '/'); |
311 | if (pos == NULL) | |
312 | return NULL; | |
065db052 | 313 | util_strscpy(part, sizeof(part), pos); |
62b9dfb6 | 314 | pos[0] = '\0'; |
065db052 KS |
315 | if (util_resolve_sys_link(udev, block, sizeof(block)) == 0) |
316 | util_strscpyl(path, sizeof(path), block, part, NULL); | |
62b9dfb6 KS |
317 | } |
318 | ||
b95f8a76 KS |
319 | /* path exists in sys */ |
320 | if (strncmp(&syspath[len], "/devices/", 9) == 0 || | |
321 | strncmp(&syspath[len], "/class/", 7) == 0 || | |
322 | strncmp(&syspath[len], "/block/", 7) == 0) { | |
323 | char file[UTIL_PATH_SIZE]; | |
324 | ||
325 | /* all "devices" require a "uevent" file */ | |
065db052 | 326 | util_strscpyl(file, sizeof(file), path, "/uevent", NULL); |
b95f8a76 | 327 | if (stat(file, &statbuf) != 0) { |
86b57788 | 328 | dbg(udev, "not a device: %s\n", syspath); |
b95f8a76 KS |
329 | return NULL; |
330 | } | |
331 | } else { | |
332 | /* everything else just needs to be a directory */ | |
333 | if (stat(path, &statbuf) != 0 || !S_ISDIR(statbuf.st_mode)) { | |
86b57788 | 334 | dbg(udev, "directory not found: %s\n", syspath); |
b95f8a76 KS |
335 | return NULL; |
336 | } | |
337 | } | |
338 | ||
a5710160 | 339 | udev_device = udev_device_new(udev); |
b95f8a76 KS |
340 | if (udev_device == NULL) |
341 | return NULL; | |
342 | ||
8cd2e972 | 343 | udev_device_set_syspath(udev_device, path); |
7d563a17 | 344 | info(udev, "device %p has devpath '%s'\n", udev_device, udev_device_get_devpath(udev_device)); |
eb1f0e66 | 345 | |
eb1f0e66 KS |
346 | return udev_device; |
347 | } | |
348 | ||
1e511322 KS |
349 | /** |
350 | * udev_device_new_from_devnum: | |
351 | * @udev: udev library context | |
352 | * @type: char or block device | |
353 | * @devnum: device major/minor number | |
354 | * | |
355 | * Create new udev device, and fill in information from the sys | |
356 | * device and the udev database entry. The device is looked up | |
357 | * by its major/minor number. Character and block device numbers | |
358 | * are not unique across the two types, they do not share the same | |
359 | * range of numbers. | |
360 | * | |
361 | * The initial refcount is 1, and needs to be decremented to | |
362 | * release the resources of the udev device. | |
363 | * | |
364 | * Returns: a new udev device, or #NULL, if it does not exist | |
365 | **/ | |
4c9dff47 KS |
366 | struct udev_device *udev_device_new_from_devnum(struct udev *udev, char type, dev_t devnum) |
367 | { | |
368 | char path[UTIL_PATH_SIZE]; | |
03198b93 | 369 | const char *type_str; |
438d4c3c | 370 | struct udev_enumerate *udev_enumerate; |
0de33a61 | 371 | struct udev_list_entry *list_entry; |
bf7ad0ea | 372 | struct udev_device *device = NULL; |
03198b93 KS |
373 | |
374 | if (type == 'b') | |
375 | type_str = "block"; | |
376 | else if (type == 'c') | |
377 | type_str = "char"; | |
378 | else | |
379 | return NULL; | |
4c9dff47 | 380 | |
b95f8a76 | 381 | /* /sys/dev/{block,char}/<maj>:<min> link */ |
03198b93 KS |
382 | snprintf(path, sizeof(path), "%s/dev/%s/%u:%u", udev_get_sys_path(udev), |
383 | type_str, major(devnum), minor(devnum)); | |
bf7ad0ea KS |
384 | if (util_resolve_sys_link(udev, path, sizeof(path)) == 0) |
385 | return udev_device_new_from_syspath(udev, path); | |
4c9dff47 | 386 | |
438d4c3c KS |
387 | udev_enumerate = udev_enumerate_new(udev); |
388 | if (udev_enumerate == NULL) | |
389 | return NULL; | |
390 | ||
bc8184ed KS |
391 | /* fallback to search sys devices for the major/minor */ |
392 | if (type == 'b') | |
c97f839e | 393 | udev_enumerate_add_match_subsystem(udev_enumerate, "block"); |
bc8184ed | 394 | else if (type == 'c') |
c97f839e KS |
395 | udev_enumerate_add_nomatch_subsystem(udev_enumerate, "block"); |
396 | udev_enumerate_scan_devices(udev_enumerate); | |
438d4c3c | 397 | udev_list_entry_foreach(list_entry, udev_enumerate_get_list_entry(udev_enumerate)) { |
bf7ad0ea KS |
398 | struct udev_device *device_loop; |
399 | ||
0de33a61 | 400 | device_loop = udev_device_new_from_syspath(udev, udev_list_entry_get_name(list_entry)); |
bf7ad0ea KS |
401 | if (device_loop != NULL) { |
402 | if (udev_device_get_devnum(device_loop) == devnum) { | |
c97f839e | 403 | if (type == 'b' && strcmp(udev_device_get_subsystem(device_loop), "block") != 0) |
bc8184ed | 404 | continue; |
c97f839e | 405 | if (type == 'c' && strcmp(udev_device_get_subsystem(device_loop), "block") == 0) |
bc8184ed | 406 | continue; |
bf7ad0ea KS |
407 | device = device_loop; |
408 | break; | |
409 | } | |
410 | udev_device_unref(device_loop); | |
411 | } | |
bf7ad0ea | 412 | } |
438d4c3c | 413 | udev_enumerate_unref(udev_enumerate); |
bf7ad0ea | 414 | return device; |
4c9dff47 KS |
415 | } |
416 | ||
1e511322 KS |
417 | /** |
418 | * udev_device_new_from_subsystem_sysname: | |
419 | * @udev: udev library context | |
420 | * @subsystem: the subsytem of the device | |
421 | * @sysname: the name of the device | |
422 | * | |
423 | * Create new udev device, and fill in information from the sys | |
424 | * device and the udev database entry. The device is looked up | |
425 | * by the subsytem and name string of the device, like "mem", | |
426 | * "zero", or "block", "sda". | |
427 | * | |
428 | * The initial refcount is 1, and needs to be decremented to | |
429 | * release the resources of the udev device. | |
430 | * | |
431 | * Returns: a new udev device, or #NULL, if it does not exist | |
432 | **/ | |
90d80c2e KS |
433 | struct udev_device *udev_device_new_from_subsystem_sysname(struct udev *udev, const char *subsystem, const char *sysname) |
434 | { | |
90d80c2e KS |
435 | char path_full[UTIL_PATH_SIZE]; |
436 | char *path; | |
065db052 | 437 | size_t l; |
90d80c2e KS |
438 | struct stat statbuf; |
439 | ||
065db052 KS |
440 | path = path_full; |
441 | l = util_strpcpyl(&path, sizeof(path_full), udev_get_sys_path(udev), NULL); | |
90d80c2e KS |
442 | |
443 | if (strcmp(subsystem, "subsystem") == 0) { | |
065db052 | 444 | util_strscpyl(path, l, "/subsystem/", sysname, NULL); |
90d80c2e KS |
445 | if (stat(path_full, &statbuf) == 0) |
446 | goto found; | |
447 | ||
065db052 | 448 | util_strscpyl(path, l, "/bus/", sysname, NULL); |
90d80c2e KS |
449 | if (stat(path_full, &statbuf) == 0) |
450 | goto found; | |
451 | ||
065db052 | 452 | util_strscpyl(path, l, "/class/", sysname, NULL); |
90d80c2e KS |
453 | if (stat(path_full, &statbuf) == 0) |
454 | goto found; | |
455 | goto out; | |
456 | } | |
457 | ||
458 | if (strcmp(subsystem, "module") == 0) { | |
065db052 | 459 | util_strscpyl(path, l, "/module/", sysname, NULL); |
90d80c2e KS |
460 | if (stat(path_full, &statbuf) == 0) |
461 | goto found; | |
462 | goto out; | |
463 | } | |
464 | ||
465 | if (strcmp(subsystem, "drivers") == 0) { | |
466 | char subsys[UTIL_NAME_SIZE]; | |
467 | char *driver; | |
468 | ||
065db052 | 469 | util_strscpy(subsys, sizeof(subsys), sysname); |
90d80c2e KS |
470 | driver = strchr(subsys, ':'); |
471 | if (driver != NULL) { | |
472 | driver[0] = '\0'; | |
473 | driver = &driver[1]; | |
065db052 KS |
474 | |
475 | util_strscpyl(path, l, "/subsystem/", subsys, "/drivers/", driver, NULL); | |
90d80c2e KS |
476 | if (stat(path_full, &statbuf) == 0) |
477 | goto found; | |
478 | ||
065db052 | 479 | util_strscpyl(path, l, "/bus/", subsys, "/drivers/", driver, NULL); |
90d80c2e KS |
480 | if (stat(path_full, &statbuf) == 0) |
481 | goto found; | |
482 | } | |
483 | goto out; | |
484 | } | |
485 | ||
065db052 | 486 | util_strscpyl(path, l, "/subsystem/", subsystem, "/devices/", sysname, NULL); |
90d80c2e KS |
487 | if (stat(path_full, &statbuf) == 0) |
488 | goto found; | |
489 | ||
065db052 | 490 | util_strscpyl(path, l, "/bus/", subsystem, "/devices/", sysname, NULL); |
90d80c2e KS |
491 | if (stat(path_full, &statbuf) == 0) |
492 | goto found; | |
493 | ||
065db052 | 494 | util_strscpyl(path, l, "/class/", subsystem, "/", sysname, NULL); |
90d80c2e KS |
495 | if (stat(path_full, &statbuf) == 0) |
496 | goto found; | |
497 | out: | |
498 | return NULL; | |
499 | found: | |
500 | return udev_device_new_from_syspath(udev, path_full); | |
501 | } | |
502 | ||
b2d9e4f2 | 503 | static struct udev_device *device_new_from_parent(struct udev_device *udev_device) |
4ad3a37f KS |
504 | { |
505 | struct udev_device *udev_device_parent = NULL; | |
506 | char path[UTIL_PATH_SIZE]; | |
b95f8a76 | 507 | const char *subdir; |
4ad3a37f | 508 | |
b95f8a76 KS |
509 | /* follow "device" link in deprecated sys layout */ |
510 | if (strncmp(udev_device->devpath, "/class/", 7) == 0 || | |
511 | strncmp(udev_device->devpath, "/block/", 7) == 0) { | |
065db052 | 512 | util_strscpyl(path, sizeof(path), udev_device->syspath, "/device", NULL); |
f454ecf7 | 513 | if (util_resolve_sys_link(udev_device->udev, path, sizeof(path)) == 0) { |
b95f8a76 | 514 | udev_device_parent = udev_device_new_from_syspath(udev_device->udev, path); |
f454ecf7 KS |
515 | if (udev_device_parent != NULL) |
516 | return udev_device_parent; | |
517 | } | |
b95f8a76 | 518 | } |
4ad3a37f | 519 | |
065db052 | 520 | util_strscpy(path, sizeof(path), udev_device->syspath); |
b95f8a76 | 521 | subdir = &path[strlen(udev_get_sys_path(udev_device->udev))+1]; |
4ad3a37f | 522 | while (1) { |
b95f8a76 KS |
523 | char *pos; |
524 | ||
525 | pos = strrchr(subdir, '/'); | |
526 | if (pos == NULL || pos < &subdir[2]) | |
4ad3a37f KS |
527 | break; |
528 | pos[0] = '\0'; | |
8753fadf | 529 | udev_device_parent = udev_device_new_from_syspath(udev_device->udev, path); |
4ad3a37f | 530 | if (udev_device_parent != NULL) |
0518da3b KS |
531 | return udev_device_parent; |
532 | } | |
0518da3b | 533 | return NULL; |
4ad3a37f KS |
534 | } |
535 | ||
1e511322 KS |
536 | /** |
537 | * udev_device_get_parent: | |
538 | * @udev_device: the device to start searching from | |
539 | * | |
540 | * Find the next parent device, and fill in information from the sys | |
541 | * device and the udev database entry. | |
542 | * | |
543 | * The returned the device is not referenced. It is attached to the | |
544 | * child device, and will be cleaned up when the child device | |
545 | * is cleaned up. | |
546 | * | |
547 | * It is not neccessarily just the upper level directory, empty or not | |
548 | * recognized sys directories are ignored. | |
549 | * | |
550 | * It can be called as many times as needed, without caring about | |
551 | * references. | |
552 | * | |
553 | * Returns: a new udev device, or #NULL, if it no parent exist. | |
554 | **/ | |
b2d9e4f2 KS |
555 | struct udev_device *udev_device_get_parent(struct udev_device *udev_device) |
556 | { | |
b95f8a76 KS |
557 | if (udev_device == NULL) |
558 | return NULL; | |
31f4b036 KS |
559 | if (!udev_device->parent_set) { |
560 | udev_device->parent_set = 1; | |
561 | udev_device->parent_device = device_new_from_parent(udev_device); | |
0518da3b | 562 | } |
31f4b036 | 563 | if (udev_device->parent_device != NULL) |
86b57788 | 564 | dbg(udev_device->udev, "returning existing parent %p\n", udev_device->parent_device); |
b2d9e4f2 KS |
565 | return udev_device->parent_device; |
566 | } | |
567 | ||
1e511322 KS |
568 | /** |
569 | * udev_device_get_parent_with_subsystem_devtype: | |
570 | * @udev_device: udev device to start searching from | |
571 | * @subsystem: the subsytem of the device | |
572 | * @devtype: the type (DEVTYPE) of the device | |
573 | * | |
574 | * Find the next parent device, with a matching subsystem and devtype | |
575 | * value, and fill in information from the sys device and the udev | |
576 | * database entry. | |
577 | * | |
578 | * The returned the device is not referenced. It is attached to the | |
579 | * child device, and will be cleaned up when the child device | |
580 | * is cleaned up. | |
581 | * | |
582 | * It can be called as many times as needed, without caring about | |
583 | * references. | |
584 | * | |
585 | * Returns: a new udev device, or #NULL, if no matching parent exists. | |
586 | **/ | |
883012d4 | 587 | struct udev_device *udev_device_get_parent_with_subsystem_devtype(struct udev_device *udev_device, const char *subsystem, const char *devtype) |
bf8b2ae1 MH |
588 | { |
589 | struct udev_device *parent; | |
590 | ||
883012d4 MH |
591 | if (subsystem == NULL) |
592 | return NULL; | |
593 | ||
bf8b2ae1 MH |
594 | parent = udev_device_get_parent(udev_device); |
595 | while (parent != NULL) { | |
883012d4 | 596 | const char *parent_subsystem; |
bf8b2ae1 MH |
597 | const char *parent_devtype; |
598 | ||
883012d4 MH |
599 | parent_subsystem = udev_device_get_subsystem(parent); |
600 | if (parent_subsystem != NULL && strcmp(parent_subsystem, subsystem) == 0) { | |
601 | if (devtype == NULL) | |
602 | break; | |
603 | parent_devtype = udev_device_get_devtype(parent); | |
604 | if (parent_devtype != NULL && strcmp(parent_devtype, devtype) == 0) | |
605 | break; | |
606 | } | |
bf8b2ae1 MH |
607 | parent = udev_device_get_parent(parent); |
608 | } | |
609 | return parent; | |
610 | } | |
611 | ||
eb1f0e66 KS |
612 | /** |
613 | * udev_device_get_udev: | |
7d8787b3 | 614 | * @udev_device: udev device |
eb1f0e66 KS |
615 | * |
616 | * Retrieve the udev library context the device was created with. | |
617 | * | |
618 | * Returns: the udev library context | |
619 | **/ | |
620 | struct udev *udev_device_get_udev(struct udev_device *udev_device) | |
621 | { | |
ba6929f6 KS |
622 | if (udev_device == NULL) |
623 | return NULL; | |
eb1f0e66 KS |
624 | return udev_device->udev; |
625 | } | |
626 | ||
627 | /** | |
628 | * udev_device_ref: | |
629 | * @udev_device: udev device | |
630 | * | |
631 | * Take a reference of a udev device. | |
632 | * | |
633 | * Returns: the passed udev device | |
634 | **/ | |
635 | struct udev_device *udev_device_ref(struct udev_device *udev_device) | |
636 | { | |
ba6929f6 KS |
637 | if (udev_device == NULL) |
638 | return NULL; | |
eb1f0e66 KS |
639 | udev_device->refcount++; |
640 | return udev_device; | |
641 | } | |
642 | ||
643 | /** | |
644 | * udev_device_unref: | |
645 | * @udev_device: udev device | |
646 | * | |
647 | * Drop a reference of a udev device. If the refcount reaches zero, | |
be7de409 | 648 | * the resources of the device will be released. |
eb1f0e66 KS |
649 | * |
650 | **/ | |
651 | void udev_device_unref(struct udev_device *udev_device) | |
652 | { | |
ba6929f6 KS |
653 | if (udev_device == NULL) |
654 | return; | |
eb1f0e66 KS |
655 | udev_device->refcount--; |
656 | if (udev_device->refcount > 0) | |
657 | return; | |
b2d9e4f2 KS |
658 | if (udev_device->parent_device != NULL) |
659 | udev_device_unref(udev_device->parent_device); | |
11d543c1 | 660 | free(udev_device->syspath); |
517814e7 | 661 | free(udev_device->sysname); |
99214844 | 662 | free(udev_device->devnode); |
ba6929f6 | 663 | free(udev_device->subsystem); |
bf8b2ae1 | 664 | free(udev_device->devtype); |
eb8837e1 KS |
665 | udev_list_cleanup_entries(udev_device->udev, &udev_device->devlinks_list); |
666 | udev_list_cleanup_entries(udev_device->udev, &udev_device->properties_list); | |
1c7047ea KS |
667 | free(udev_device->action); |
668 | free(udev_device->driver); | |
669 | free(udev_device->devpath_old); | |
cb14f454 | 670 | free(udev_device->knodename); |
69239210 | 671 | udev_list_cleanup_entries(udev_device->udev, &udev_device->sysattr_list); |
6493e655 | 672 | free(udev_device->envp); |
c2654402 | 673 | free(udev_device->monitor_buf); |
86b57788 | 674 | dbg(udev_device->udev, "udev_device: %p released\n", udev_device); |
eb1f0e66 KS |
675 | free(udev_device); |
676 | } | |
677 | ||
678 | /** | |
679 | * udev_device_get_devpath: | |
680 | * @udev_device: udev device | |
681 | * | |
11d543c1 KS |
682 | * Retrieve the kernel devpath value of the udev device. The path |
683 | * does not contain the sys mount point, and starts with a '/'. | |
eb1f0e66 | 684 | * |
11d543c1 | 685 | * Returns: the devpath of the udev device |
eb1f0e66 KS |
686 | **/ |
687 | const char *udev_device_get_devpath(struct udev_device *udev_device) | |
688 | { | |
ba6929f6 KS |
689 | if (udev_device == NULL) |
690 | return NULL; | |
691 | return udev_device->devpath; | |
eb1f0e66 KS |
692 | } |
693 | ||
11d543c1 KS |
694 | /** |
695 | * udev_device_get_syspath: | |
696 | * @udev_device: udev device | |
697 | * | |
698 | * Retrieve the sys path of the udev device. The path is an | |
699 | * absolute path and starts with the sys mount point. | |
700 | * | |
701 | * Returns: the sys path of the udev device | |
702 | **/ | |
703 | const char *udev_device_get_syspath(struct udev_device *udev_device) | |
704 | { | |
705 | if (udev_device == NULL) | |
706 | return NULL; | |
707 | return udev_device->syspath; | |
708 | } | |
709 | ||
1e511322 KS |
710 | /** |
711 | * udev_device_get_sysname: | |
712 | * @udev_device: udev device | |
713 | * | |
714 | * Returns: the sys name of the device device | |
715 | **/ | |
4ad3a37f KS |
716 | const char *udev_device_get_sysname(struct udev_device *udev_device) |
717 | { | |
718 | if (udev_device == NULL) | |
719 | return NULL; | |
720 | return udev_device->sysname; | |
721 | } | |
722 | ||
1e511322 KS |
723 | /** |
724 | * udev_device_get_sysnum: | |
725 | * @udev_device: udev device | |
726 | * | |
727 | * Returns: the trailing number of of the device name | |
728 | **/ | |
517814e7 KS |
729 | const char *udev_device_get_sysnum(struct udev_device *udev_device) |
730 | { | |
731 | if (udev_device == NULL) | |
732 | return NULL; | |
733 | return udev_device->sysnum; | |
734 | } | |
735 | ||
eb1f0e66 | 736 | /** |
fb762bb9 | 737 | * udev_device_get_devnode: |
eb1f0e66 KS |
738 | * @udev_device: udev device |
739 | * | |
740 | * Retrieve the device node file name belonging to the udev device. | |
ba6929f6 | 741 | * The path is an absolute path, and starts with the device directory. |
eb1f0e66 KS |
742 | * |
743 | * Returns: the device node file name of the udev device, or #NULL if no device node exists | |
744 | **/ | |
fb762bb9 | 745 | const char *udev_device_get_devnode(struct udev_device *udev_device) |
eb1f0e66 | 746 | { |
ba6929f6 | 747 | if (udev_device == NULL) |
eb1f0e66 | 748 | return NULL; |
99214844 | 749 | if (!udev_device->info_loaded) |
77b852f3 | 750 | device_load_info(udev_device); |
99214844 | 751 | return udev_device->devnode; |
eb1f0e66 KS |
752 | } |
753 | ||
754 | /** | |
755 | * udev_device_get_subsystem: | |
756 | * @udev_device: udev device | |
757 | * | |
758 | * Retrieve the subsystem string of the udev device. The string does not | |
759 | * contain any "/". | |
760 | * | |
761 | * Returns: the subsystem name of the udev device, or #NULL if it can not be determined | |
762 | **/ | |
763 | const char *udev_device_get_subsystem(struct udev_device *udev_device) | |
764 | { | |
17fcfb59 | 765 | char subsystem[UTIL_NAME_SIZE]; |
ba6929f6 KS |
766 | |
767 | if (udev_device == NULL) | |
768 | return NULL; | |
5c5cad79 KS |
769 | if (!udev_device->subsystem_set) { |
770 | udev_device->subsystem_set = 1; | |
771 | /* read "subsytem" link */ | |
772 | if (util_get_sys_subsystem(udev_device->udev, udev_device->syspath, subsystem, sizeof(subsystem)) > 0) { | |
773 | udev_device_set_subsystem(udev_device, subsystem); | |
774 | return udev_device->subsystem; | |
775 | } | |
776 | /* implicit names */ | |
777 | if (strncmp(udev_device->devpath, "/module/", 8) == 0) { | |
778 | udev_device_set_subsystem(udev_device, "module"); | |
779 | return udev_device->subsystem; | |
780 | } | |
781 | if (strstr(udev_device->devpath, "/drivers/") != NULL) { | |
782 | udev_device_set_subsystem(udev_device, "drivers"); | |
783 | return udev_device->subsystem; | |
784 | } | |
785 | if (strncmp(udev_device->devpath, "/subsystem/", 11) == 0 || | |
786 | strncmp(udev_device->devpath, "/class/", 7) == 0 || | |
787 | strncmp(udev_device->devpath, "/bus/", 5) == 0) { | |
788 | udev_device_set_subsystem(udev_device, "subsystem"); | |
789 | return udev_device->subsystem; | |
790 | } | |
0518da3b | 791 | } |
5c5cad79 | 792 | return udev_device->subsystem; |
eb1f0e66 KS |
793 | } |
794 | ||
bf8b2ae1 MH |
795 | /** |
796 | * udev_device_get_devtype: | |
797 | * @udev_device: udev device | |
798 | * | |
799 | * Retrieve the devtype string of the udev device. | |
800 | * | |
801 | * Returns: the devtype name of the udev device, or #NULL if it can not be determined | |
802 | **/ | |
803 | const char *udev_device_get_devtype(struct udev_device *udev_device) | |
804 | { | |
805 | if (udev_device == NULL) | |
806 | return NULL; | |
807 | if (!udev_device->devtype_set) { | |
808 | udev_device->devtype_set = 1; | |
b9251174 KS |
809 | if (!udev_device->info_loaded) |
810 | udev_device_read_uevent_file(udev_device); | |
bf8b2ae1 MH |
811 | } |
812 | return udev_device->devtype; | |
813 | } | |
814 | ||
eb1f0e66 | 815 | /** |
0de33a61 | 816 | * udev_device_get_devlinks_list_entry: |
eb1f0e66 | 817 | * @udev_device: udev device |
eb1f0e66 | 818 | * |
bf7ad0ea KS |
819 | * Retrieve the list of device links pointing to the device file of |
820 | * the udev device. The next list entry can be retrieved with | |
e345e267 | 821 | * udev_list_entry_next(), which returns #NULL if no more entries exist. |
bf7ad0ea | 822 | * The devlink path can be retrieved from the list entry by |
e345e267 | 823 | * udev_list_entry_get_name(). The path is an absolute path, and starts with |
bf7ad0ea | 824 | * the device directory. |
eb1f0e66 | 825 | * |
bf7ad0ea | 826 | * Returns: the first entry of the device node link list |
eb1f0e66 | 827 | **/ |
0de33a61 | 828 | struct udev_list_entry *udev_device_get_devlinks_list_entry(struct udev_device *udev_device) |
eb1f0e66 | 829 | { |
99214844 KS |
830 | if (udev_device == NULL) |
831 | return NULL; | |
832 | if (!udev_device->info_loaded) | |
77b852f3 | 833 | device_load_info(udev_device); |
517814e7 | 834 | return udev_list_get_entry(&udev_device->devlinks_list); |
eb1f0e66 KS |
835 | } |
836 | ||
979ff016 KS |
837 | void udev_device_cleanup_devlinks_list(struct udev_device *udev_device) |
838 | { | |
bd85566c | 839 | udev_device->devlinks_uptodate = 0; |
eb8837e1 | 840 | udev_list_cleanup_entries(udev_device->udev, &udev_device->devlinks_list); |
979ff016 KS |
841 | } |
842 | ||
eb1f0e66 | 843 | /** |
0de33a61 | 844 | * udev_device_get_properties_list_entry: |
eb1f0e66 | 845 | * @udev_device: udev device |
eb1f0e66 | 846 | * |
bf7ad0ea | 847 | * Retrieve the list of key/value device properties of the udev |
e345e267 | 848 | * device. The next list entry can be retrieved with udev_list_entry_next(), |
bf7ad0ea KS |
849 | * which returns #NULL if no more entries exist. The property name |
850 | * can be retrieved from the list entry by udev_list_get_name(), | |
851 | * the property value by udev_list_get_value(). | |
eb1f0e66 | 852 | * |
bf7ad0ea | 853 | * Returns: the first entry of the property list |
eb1f0e66 | 854 | **/ |
0de33a61 | 855 | struct udev_list_entry *udev_device_get_properties_list_entry(struct udev_device *udev_device) |
eb1f0e66 | 856 | { |
99214844 KS |
857 | if (udev_device == NULL) |
858 | return NULL; | |
859 | if (!udev_device->info_loaded) | |
77b852f3 | 860 | device_load_info(udev_device); |
bd85566c KS |
861 | if (!udev_device->devlinks_uptodate) { |
862 | char symlinks[UTIL_PATH_SIZE]; | |
863 | struct udev_list_entry *list_entry; | |
864 | ||
865 | udev_device->devlinks_uptodate = 1; | |
866 | list_entry = udev_device_get_devlinks_list_entry(udev_device); | |
867 | if (list_entry != NULL) { | |
065db052 KS |
868 | char *s; |
869 | size_t l; | |
870 | ||
871 | s = symlinks; | |
872 | l = util_strpcpyl(&s, sizeof(symlinks), udev_list_entry_get_name(list_entry), NULL); | |
873 | udev_list_entry_foreach(list_entry, udev_list_entry_get_next(list_entry)) | |
874 | l = util_strpcpyl(&s, l, " ", udev_list_entry_get_name(list_entry), NULL); | |
bd85566c KS |
875 | udev_device_add_property(udev_device, "DEVLINKS", symlinks); |
876 | } | |
877 | } | |
8cd2e972 | 878 | return udev_list_get_entry(&udev_device->properties_list); |
eb1f0e66 | 879 | } |
11d543c1 | 880 | |
1e511322 KS |
881 | /** |
882 | * udev_device_get_driver: | |
883 | * @udev_device: udev device | |
884 | * | |
885 | * Returns: the driver string or #NULL, if ther is no driver attached. | |
886 | **/ | |
c4f5f942 KS |
887 | const char *udev_device_get_driver(struct udev_device *udev_device) |
888 | { | |
17fcfb59 | 889 | char driver[UTIL_NAME_SIZE]; |
95d90c4f | 890 | |
c4f5f942 KS |
891 | if (udev_device == NULL) |
892 | return NULL; | |
5c5cad79 KS |
893 | if (!udev_device->driver_set) { |
894 | udev_device->driver_set = 1; | |
895 | if (util_get_sys_driver(udev_device->udev, udev_device->syspath, driver, sizeof(driver)) > 0) | |
896 | udev_device->driver = strdup(driver); | |
897 | } | |
c4f5f942 KS |
898 | return udev_device->driver; |
899 | } | |
900 | ||
1e511322 KS |
901 | /** |
902 | * udev_device_get_devnum: | |
903 | * @udev_device: udev device | |
904 | * | |
905 | * Returns: the device major/minor number. | |
906 | **/ | |
c4f5f942 KS |
907 | dev_t udev_device_get_devnum(struct udev_device *udev_device) |
908 | { | |
909 | if (udev_device == NULL) | |
910 | return makedev(0, 0); | |
99214844 | 911 | if (!udev_device->info_loaded) |
77b852f3 | 912 | device_load_info(udev_device); |
c4f5f942 KS |
913 | return udev_device->devnum; |
914 | } | |
915 | ||
1e511322 KS |
916 | /** |
917 | * udev_device_get_action: | |
918 | * @udev_device: udev device | |
919 | * | |
920 | * This is only valid if the device was received through a monitor. Devices read from | |
921 | * sys do not have an action string. Usual actions are: add, remove, change, online, | |
922 | * offline. | |
923 | * | |
924 | * Returns: the kernel action value, or #NULL if there is no action value available. | |
925 | **/ | |
c4f5f942 KS |
926 | const char *udev_device_get_action(struct udev_device *udev_device) |
927 | { | |
928 | if (udev_device == NULL) | |
929 | return NULL; | |
930 | return udev_device->action; | |
931 | } | |
932 | ||
1e511322 KS |
933 | /** |
934 | * udev_device_get_devnum: | |
935 | * @udev_device: udev device | |
936 | * | |
937 | * This is only valid if the device was received through a monitor. Devices read from | |
938 | * sys do not have a sequence number. | |
939 | * | |
940 | * Returns: the kernel event sequence number, or 0 if there is no sequence number available. | |
941 | **/ | |
37372bbc KS |
942 | unsigned long long int udev_device_get_seqnum(struct udev_device *udev_device) |
943 | { | |
944 | if (udev_device == NULL) | |
945 | return 0; | |
946 | return udev_device->seqnum; | |
947 | } | |
948 | ||
1e511322 KS |
949 | /** |
950 | * udev_device_get_sysattr_value: | |
951 | * @udev_device: udev device | |
952 | * @sysattr: attribute name | |
953 | * | |
954 | * The retrieved value is cached in the device. Repeated reads will return the same | |
955 | * value and not open the attribute again. | |
956 | * | |
957 | * Returns: the content of a sys attribute file, or #NULL if there is no sys attribute value. | |
958 | **/ | |
69239210 | 959 | const char *udev_device_get_sysattr_value(struct udev_device *udev_device, const char *sysattr) |
93b0f384 | 960 | { |
0de33a61 | 961 | struct udev_list_entry *list_entry; |
93b0f384 | 962 | char path[UTIL_PATH_SIZE]; |
affed87a | 963 | char value[4096]; |
93b0f384 KS |
964 | struct stat statbuf; |
965 | int fd; | |
966 | ssize_t size; | |
967 | const char *val = NULL; | |
968 | ||
517814e7 KS |
969 | if (udev_device == NULL) |
970 | return NULL; | |
69239210 | 971 | if (sysattr == NULL) |
517814e7 KS |
972 | return NULL; |
973 | ||
0518da3b | 974 | /* look for possibly already cached result */ |
69239210 KS |
975 | udev_list_entry_foreach(list_entry, udev_list_get_entry(&udev_device->sysattr_list)) { |
976 | if (strcmp(udev_list_entry_get_name(list_entry), sysattr) == 0) { | |
86b57788 KS |
977 | dbg(udev_device->udev, "got '%s' (%s) from cache\n", |
978 | sysattr, udev_list_entry_get_value(list_entry)); | |
0de33a61 | 979 | return udev_list_entry_get_value(list_entry); |
0518da3b KS |
980 | } |
981 | } | |
982 | ||
065db052 | 983 | util_strscpyl(path, sizeof(path), udev_device_get_syspath(udev_device), "/", sysattr, NULL); |
93b0f384 | 984 | if (lstat(path, &statbuf) != 0) { |
86b57788 | 985 | dbg(udev_device->udev, "no attribute '%s', keep negative entry\n", path); |
69239210 | 986 | udev_list_entry_add(udev_device->udev, &udev_device->sysattr_list, sysattr, NULL, 0, 0); |
93b0f384 KS |
987 | goto out; |
988 | } | |
989 | ||
990 | if (S_ISLNK(statbuf.st_mode)) { | |
93b0f384 KS |
991 | char target[UTIL_NAME_SIZE]; |
992 | int len; | |
993 | char *pos; | |
994 | ||
096e59ed KS |
995 | /* some core links return the last element of the target path */ |
996 | if (strcmp(sysattr, "driver") != 0 && | |
997 | strcmp(sysattr, "subsystem") != 0 && | |
998 | strcmp(sysattr, "module") != 0) | |
999 | goto out; | |
1000 | ||
93b0f384 KS |
1001 | len = readlink(path, target, sizeof(target)); |
1002 | if (len > 0) { | |
1003 | target[len] = '\0'; | |
1004 | pos = strrchr(target, '/'); | |
1005 | if (pos != NULL) { | |
1006 | pos = &pos[1]; | |
86b57788 | 1007 | dbg(udev_device->udev, "cache '%s' with link value '%s'\n", sysattr, pos); |
69239210 | 1008 | list_entry = udev_list_entry_add(udev_device->udev, &udev_device->sysattr_list, sysattr, pos, 0, 0); |
0de33a61 | 1009 | val = udev_list_entry_get_value(list_entry); |
93b0f384 KS |
1010 | } |
1011 | } | |
1012 | goto out; | |
1013 | } | |
1014 | ||
1015 | /* skip directories */ | |
1016 | if (S_ISDIR(statbuf.st_mode)) | |
1017 | goto out; | |
1018 | ||
1019 | /* skip non-readable files */ | |
1020 | if ((statbuf.st_mode & S_IRUSR) == 0) | |
1021 | goto out; | |
1022 | ||
1023 | /* read attribute value */ | |
1024 | fd = open(path, O_RDONLY); | |
1025 | if (fd < 0) { | |
86b57788 | 1026 | dbg(udev_device->udev, "attribute '%s' can not be opened\n", path); |
93b0f384 KS |
1027 | goto out; |
1028 | } | |
1029 | size = read(fd, value, sizeof(value)); | |
1030 | close(fd); | |
1031 | if (size < 0) | |
1032 | goto out; | |
1033 | if (size == sizeof(value)) | |
1034 | goto out; | |
1035 | ||
0518da3b | 1036 | /* got a valid value, store it in cache and return it */ |
93b0f384 KS |
1037 | value[size] = '\0'; |
1038 | util_remove_trailing_chars(value, '\n'); | |
86b57788 | 1039 | dbg(udev_device->udev, "'%s' has attribute value '%s'\n", path, value); |
69239210 | 1040 | list_entry = udev_list_entry_add(udev_device->udev, &udev_device->sysattr_list, sysattr, value, 0, 0); |
0de33a61 | 1041 | val = udev_list_entry_get_value(list_entry); |
93b0f384 KS |
1042 | out: |
1043 | return val; | |
1044 | } | |
517814e7 | 1045 | |
8cd2e972 | 1046 | int udev_device_set_syspath(struct udev_device *udev_device, const char *syspath) |
11d543c1 | 1047 | { |
8753fadf | 1048 | const char *pos; |
517814e7 | 1049 | size_t len; |
8753fadf | 1050 | |
8cd2e972 | 1051 | free(udev_device->syspath); |
8753fadf KS |
1052 | udev_device->syspath = strdup(syspath); |
1053 | if (udev_device->syspath == NULL) | |
11d543c1 KS |
1054 | return -ENOMEM; |
1055 | udev_device->devpath = &udev_device->syspath[strlen(udev_get_sys_path(udev_device->udev))]; | |
9a997ecf | 1056 | udev_device_add_property(udev_device, "DEVPATH", udev_device->devpath); |
517814e7 | 1057 | |
8753fadf KS |
1058 | pos = strrchr(udev_device->syspath, '/'); |
1059 | if (pos == NULL) | |
1060 | return -EINVAL; | |
517814e7 KS |
1061 | udev_device->sysname = strdup(&pos[1]); |
1062 | if (udev_device->sysname == NULL) | |
1063 | return -ENOMEM; | |
1064 | ||
1065 | /* some devices have '!' in their name, change that to '/' */ | |
1066 | len = 0; | |
1067 | while (udev_device->sysname[len] != '\0') { | |
1068 | if (udev_device->sysname[len] == '!') | |
1069 | udev_device->sysname[len] = '/'; | |
1070 | len++; | |
1071 | } | |
1072 | ||
1073 | /* trailing number */ | |
babcf3cb | 1074 | while (len > 0 && isdigit(udev_device->sysname[--len])) |
517814e7 | 1075 | udev_device->sysnum = &udev_device->sysname[len]; |
babcf3cb AJ |
1076 | |
1077 | /* sysname is completely numeric */ | |
1078 | if (len == 0) | |
1079 | udev_device->sysnum = NULL; | |
1080 | ||
11d543c1 KS |
1081 | return 0; |
1082 | } | |
1083 | ||
8cd2e972 | 1084 | int udev_device_set_subsystem(struct udev_device *udev_device, const char *subsystem) |
11d543c1 | 1085 | { |
979ff016 | 1086 | free(udev_device->subsystem); |
11d543c1 KS |
1087 | udev_device->subsystem = strdup(subsystem); |
1088 | if (udev_device->subsystem == NULL) | |
9a997ecf | 1089 | return -ENOMEM; |
5c5cad79 | 1090 | udev_device->subsystem_set = 1; |
9a997ecf | 1091 | udev_device_add_property(udev_device, "SUBSYSTEM", udev_device->subsystem); |
11d543c1 KS |
1092 | return 0; |
1093 | } | |
1094 | ||
bf8b2ae1 MH |
1095 | int udev_device_set_devtype(struct udev_device *udev_device, const char *devtype) |
1096 | { | |
1097 | free(udev_device->devtype); | |
1098 | udev_device->devtype = strdup(devtype); | |
1099 | if (udev_device->devtype == NULL) | |
1100 | return -ENOMEM; | |
1101 | udev_device->devtype_set = 1; | |
1102 | udev_device_add_property(udev_device, "DEVTYPE", udev_device->devtype); | |
1103 | return 0; | |
1104 | } | |
1105 | ||
8cd2e972 | 1106 | int udev_device_set_devnode(struct udev_device *udev_device, const char *devnode) |
11d543c1 | 1107 | { |
517814e7 | 1108 | free(udev_device->devnode); |
99214844 | 1109 | udev_device->devnode = strdup(devnode); |
bd85566c KS |
1110 | if (devnode == NULL) |
1111 | return 0; | |
99214844 | 1112 | if (udev_device->devnode == NULL) |
11d543c1 | 1113 | return -ENOMEM; |
979ff016 | 1114 | udev_device_add_property(udev_device, "DEVNAME", udev_device->devnode); |
11d543c1 KS |
1115 | return 0; |
1116 | } | |
1117 | ||
8cd2e972 | 1118 | int udev_device_add_devlink(struct udev_device *udev_device, const char *devlink) |
11d543c1 | 1119 | { |
bd85566c | 1120 | udev_device->devlinks_uptodate = 0; |
517814e7 | 1121 | if (udev_list_entry_add(udev_device->udev, &udev_device->devlinks_list, devlink, NULL, 1, 0) == NULL) |
11d543c1 KS |
1122 | return -ENOMEM; |
1123 | return 0; | |
1124 | } | |
1125 | ||
517814e7 | 1126 | struct udev_list_entry *udev_device_add_property(struct udev_device *udev_device, const char *key, const char *value) |
11d543c1 | 1127 | { |
979ff016 | 1128 | udev_device->envp_uptodate = 0; |
ebacd6ec KS |
1129 | if (value == NULL) { |
1130 | struct udev_list_entry *list_entry; | |
1131 | ||
1132 | list_entry = udev_device_get_properties_list_entry(udev_device); | |
1133 | list_entry = udev_list_entry_get_by_name(list_entry, key); | |
1134 | if (list_entry != NULL) | |
1e78dcbe | 1135 | udev_list_entry_delete(list_entry); |
ebacd6ec KS |
1136 | return NULL; |
1137 | } | |
517814e7 | 1138 | return udev_list_entry_add(udev_device->udev, &udev_device->properties_list, key, value, 1, 0); |
11d543c1 | 1139 | } |
c4f5f942 | 1140 | |
517814e7 | 1141 | struct udev_list_entry *udev_device_add_property_from_string(struct udev_device *udev_device, const char *property) |
0518da3b KS |
1142 | { |
1143 | char name[UTIL_PATH_SIZE]; | |
1144 | char *val; | |
1145 | ||
065db052 | 1146 | util_strscpy(name, sizeof(name), property); |
0518da3b KS |
1147 | val = strchr(name, '='); |
1148 | if (val == NULL) | |
517814e7 | 1149 | return NULL; |
0518da3b KS |
1150 | val[0] = '\0'; |
1151 | val = &val[1]; | |
1152 | if (val[0] == '\0') | |
1153 | val = NULL; | |
517814e7 | 1154 | return udev_device_add_property(udev_device, name, val); |
0518da3b KS |
1155 | } |
1156 | ||
1e511322 KS |
1157 | /** |
1158 | * udev_device_get_property_value: | |
1159 | * @udev_device: udev device | |
1160 | * @key: property name | |
1161 | * | |
1162 | * Returns: the value of a device property, or #NULL if there is no such property. | |
1163 | **/ | |
3d7b2831 KS |
1164 | const char *udev_device_get_property_value(struct udev_device *udev_device, const char *key) |
1165 | { | |
1166 | struct udev_list_entry *list_entry; | |
1167 | ||
1168 | if (udev_device == NULL) | |
1169 | return NULL; | |
1170 | if (key == NULL) | |
1171 | return NULL; | |
1172 | ||
1173 | list_entry = udev_device_get_properties_list_entry(udev_device); | |
1174 | list_entry = udev_list_entry_get_by_name(list_entry, key); | |
1175 | return udev_list_entry_get_value(list_entry); | |
1176 | } | |
1177 | ||
6493e655 KS |
1178 | #define ENVP_SIZE 128 |
1179 | #define MONITOR_BUF_SIZE 4096 | |
1180 | static int update_envp_monitor_buf(struct udev_device *udev_device) | |
979ff016 | 1181 | { |
6493e655 | 1182 | struct udev_list_entry *list_entry; |
065db052 KS |
1183 | char *s; |
1184 | size_t l; | |
6493e655 | 1185 | unsigned int i; |
979ff016 | 1186 | |
6493e655 KS |
1187 | /* monitor buffer of property strings */ |
1188 | free(udev_device->monitor_buf); | |
1189 | udev_device->monitor_buf_len = 0; | |
1190 | udev_device->monitor_buf = malloc(MONITOR_BUF_SIZE); | |
1191 | if (udev_device->monitor_buf == NULL) | |
1192 | return -ENOMEM; | |
be18918f | 1193 | |
6493e655 | 1194 | /* envp array, strings will point into monitor buffer */ |
427e20b2 KS |
1195 | if (udev_device->envp == NULL) |
1196 | udev_device->envp = malloc(sizeof(char *) * ENVP_SIZE); | |
6493e655 KS |
1197 | if (udev_device->envp == NULL) |
1198 | return -ENOMEM; | |
979ff016 | 1199 | |
6493e655 | 1200 | i = 0; |
065db052 KS |
1201 | s = udev_device->monitor_buf; |
1202 | l = MONITOR_BUF_SIZE; | |
6493e655 KS |
1203 | udev_list_entry_foreach(list_entry, udev_device_get_properties_list_entry(udev_device)) { |
1204 | /* add string to envp array */ | |
065db052 | 1205 | udev_device->envp[i++] = s; |
6493e655 KS |
1206 | if (i+1 >= ENVP_SIZE) |
1207 | return -EINVAL; | |
c2654402 | 1208 | |
6493e655 | 1209 | /* add property string to monitor buffer */ |
065db052 KS |
1210 | l = util_strpcpyl(&s, l, udev_list_entry_get_name(list_entry), "=", |
1211 | udev_list_entry_get_value(list_entry), NULL); | |
1212 | if (l == 0) | |
c2654402 | 1213 | return -EINVAL; |
065db052 | 1214 | s++; |
c2654402 | 1215 | } |
6493e655 | 1216 | udev_device->envp[i] = NULL; |
065db052 | 1217 | udev_device->monitor_buf_len = s - udev_device->monitor_buf; |
6493e655 | 1218 | udev_device->envp_uptodate = 1; |
065db052 KS |
1219 | dbg(udev_device->udev, "filled envp/monitor buffer, %u properties, %zu bytes\n", |
1220 | i, udev_device->monitor_buf_len); | |
6493e655 KS |
1221 | return 0; |
1222 | } | |
1223 | ||
1224 | char **udev_device_get_properties_envp(struct udev_device *udev_device) | |
1225 | { | |
1226 | if (!udev_device->envp_uptodate) | |
c6243a41 | 1227 | if (update_envp_monitor_buf(udev_device) != 0) |
6493e655 KS |
1228 | return NULL; |
1229 | return udev_device->envp; | |
1230 | } | |
1231 | ||
1232 | ssize_t udev_device_get_properties_monitor_buf(struct udev_device *udev_device, const char **buf) | |
1233 | { | |
1234 | if (!udev_device->envp_uptodate) | |
c6243a41 | 1235 | if (update_envp_monitor_buf(udev_device) != 0) |
6493e655 | 1236 | return -EINVAL; |
c2654402 | 1237 | *buf = udev_device->monitor_buf; |
c2654402 KS |
1238 | return udev_device->monitor_buf_len; |
1239 | } | |
1240 | ||
8cd2e972 | 1241 | int udev_device_set_action(struct udev_device *udev_device, const char *action) |
c4f5f942 | 1242 | { |
517814e7 | 1243 | free(udev_device->action); |
c4f5f942 KS |
1244 | udev_device->action = strdup(action); |
1245 | if (udev_device->action == NULL) | |
1246 | return -ENOMEM; | |
31f4b036 | 1247 | udev_device_add_property(udev_device, "ACTION", udev_device->action); |
c4f5f942 KS |
1248 | return 0; |
1249 | } | |
1250 | ||
8cd2e972 | 1251 | int udev_device_set_driver(struct udev_device *udev_device, const char *driver) |
c4f5f942 | 1252 | { |
979ff016 | 1253 | free(udev_device->driver); |
c4f5f942 KS |
1254 | udev_device->driver = strdup(driver); |
1255 | if (udev_device->driver == NULL) | |
1256 | return -ENOMEM; | |
5c5cad79 | 1257 | udev_device->driver_set = 1; |
31f4b036 | 1258 | udev_device_add_property(udev_device, "DRIVER", udev_device->driver); |
c4f5f942 KS |
1259 | return 0; |
1260 | } | |
1261 | ||
8cd2e972 | 1262 | const char *udev_device_get_devpath_old(struct udev_device *udev_device) |
c4f5f942 | 1263 | { |
c4f5f942 KS |
1264 | return udev_device->devpath_old; |
1265 | } | |
1266 | ||
8cd2e972 | 1267 | int udev_device_set_devpath_old(struct udev_device *udev_device, const char *devpath_old) |
c4f5f942 KS |
1268 | { |
1269 | udev_device->devpath_old = strdup(devpath_old); | |
1270 | if (udev_device->devpath_old == NULL) | |
1271 | return -ENOMEM; | |
31f4b036 | 1272 | udev_device_add_property(udev_device, "DEVPATH_OLD", udev_device->devpath_old); |
c4f5f942 KS |
1273 | return 0; |
1274 | } | |
1275 | ||
cb14f454 KS |
1276 | const char *udev_device_get_knodename(struct udev_device *udev_device) |
1277 | { | |
1278 | return udev_device->knodename; | |
1279 | } | |
1280 | ||
1281 | int udev_device_set_knodename(struct udev_device *udev_device, const char *knodename) | |
1282 | { | |
1283 | udev_device->knodename = strdup(knodename); | |
1284 | if (udev_device->knodename == NULL) | |
1285 | return -ENOMEM; | |
1286 | return 0; | |
1287 | } | |
1288 | ||
8cd2e972 | 1289 | int udev_device_get_timeout(struct udev_device *udev_device) |
c4f5f942 | 1290 | { |
c4f5f942 KS |
1291 | return udev_device->timeout; |
1292 | } | |
1293 | ||
8cd2e972 | 1294 | int udev_device_set_timeout(struct udev_device *udev_device, int timeout) |
c4f5f942 KS |
1295 | { |
1296 | udev_device->timeout = timeout; | |
1297 | return 0; | |
1298 | } | |
8cd2e972 | 1299 | int udev_device_get_event_timeout(struct udev_device *udev_device) |
99214844 KS |
1300 | { |
1301 | if (!udev_device->info_loaded) | |
77b852f3 | 1302 | device_load_info(udev_device); |
99214844 KS |
1303 | return udev_device->event_timeout; |
1304 | } | |
1305 | ||
8cd2e972 | 1306 | int udev_device_set_event_timeout(struct udev_device *udev_device, int event_timeout) |
99214844 KS |
1307 | { |
1308 | udev_device->event_timeout = event_timeout; | |
1309 | return 0; | |
1310 | } | |
c4f5f942 | 1311 | |
8cd2e972 | 1312 | int udev_device_set_seqnum(struct udev_device *udev_device, unsigned long long int seqnum) |
37372bbc | 1313 | { |
31f4b036 KS |
1314 | char num[32]; |
1315 | ||
37372bbc | 1316 | udev_device->seqnum = seqnum; |
31f4b036 KS |
1317 | snprintf(num, sizeof(num), "%llu", seqnum); |
1318 | udev_device_add_property(udev_device, "SEQNUM", num); | |
37372bbc KS |
1319 | return 0; |
1320 | } | |
1321 | ||
8cd2e972 | 1322 | int udev_device_set_devnum(struct udev_device *udev_device, dev_t devnum) |
c4f5f942 | 1323 | { |
31f4b036 KS |
1324 | char num[32]; |
1325 | ||
c4f5f942 | 1326 | udev_device->devnum = devnum; |
31f4b036 KS |
1327 | |
1328 | snprintf(num, sizeof(num), "%u", major(devnum)); | |
1329 | udev_device_add_property(udev_device, "MAJOR", num); | |
1330 | snprintf(num, sizeof(num), "%u", minor(devnum)); | |
1331 | udev_device_add_property(udev_device, "MINOR", num); | |
c4f5f942 KS |
1332 | return 0; |
1333 | } | |
6bd1c78a | 1334 | |
8cd2e972 | 1335 | int udev_device_get_num_fake_partitions(struct udev_device *udev_device) |
6bd1c78a | 1336 | { |
99214844 | 1337 | if (!udev_device->info_loaded) |
77b852f3 | 1338 | device_load_info(udev_device); |
6bd1c78a KS |
1339 | return udev_device->num_fake_partitions; |
1340 | } | |
1341 | ||
8cd2e972 | 1342 | int udev_device_set_num_fake_partitions(struct udev_device *udev_device, int num) |
6bd1c78a KS |
1343 | { |
1344 | udev_device->num_fake_partitions = num; | |
e88a82b5 | 1345 | return 0; |
6bd1c78a KS |
1346 | } |
1347 | ||
8cd2e972 | 1348 | int udev_device_get_devlink_priority(struct udev_device *udev_device) |
6bd1c78a | 1349 | { |
99214844 | 1350 | if (!udev_device->info_loaded) |
77b852f3 | 1351 | device_load_info(udev_device); |
e88a82b5 | 1352 | return udev_device->devlink_priority; |
6bd1c78a KS |
1353 | } |
1354 | ||
8cd2e972 | 1355 | int udev_device_set_devlink_priority(struct udev_device *udev_device, int prio) |
6bd1c78a | 1356 | { |
e88a82b5 KS |
1357 | udev_device->devlink_priority = prio; |
1358 | return 0; | |
6bd1c78a KS |
1359 | } |
1360 | ||
8cd2e972 | 1361 | int udev_device_get_ignore_remove(struct udev_device *udev_device) |
6bd1c78a | 1362 | { |
99214844 | 1363 | if (!udev_device->info_loaded) |
77b852f3 | 1364 | device_load_info(udev_device); |
6bd1c78a KS |
1365 | return udev_device->ignore_remove; |
1366 | } | |
1367 | ||
8cd2e972 | 1368 | int udev_device_set_ignore_remove(struct udev_device *udev_device, int ignore) |
6bd1c78a | 1369 | { |
e88a82b5 KS |
1370 | udev_device->ignore_remove = ignore; |
1371 | return 0; | |
6bd1c78a | 1372 | } |
d7ce7539 SJR |
1373 | |
1374 | int udev_device_get_watch_handle(struct udev_device *udev_device) | |
1375 | { | |
1376 | if (!udev_device->info_loaded) | |
1377 | device_load_info(udev_device); | |
1378 | return udev_device->watch_handle; | |
1379 | } | |
1380 | ||
1381 | int udev_device_set_watch_handle(struct udev_device *udev_device, int handle) | |
1382 | { | |
1383 | udev_device->watch_handle = handle; | |
1384 | return 0; | |
1385 | } |