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