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