Line data Source code
1 : /* SPDX-License-Identifier: LGPL-2.1+ */
2 :
3 : #include <ctype.h>
4 : #include <net/if.h>
5 : #include <sys/ioctl.h>
6 : #include <sys/types.h>
7 :
8 : #include "sd-device.h"
9 :
10 : #include "alloc-util.h"
11 : #include "device-internal.h"
12 : #include "device-private.h"
13 : #include "device-util.h"
14 : #include "dirent-util.h"
15 : #include "fd-util.h"
16 : #include "fileio.h"
17 : #include "fs-util.h"
18 : #include "hashmap.h"
19 : #include "macro.h"
20 : #include "parse-util.h"
21 : #include "path-util.h"
22 : #include "set.h"
23 : #include "socket-util.h"
24 : #include "stat-util.h"
25 : #include "stdio-util.h"
26 : #include "string-util.h"
27 : #include "strv.h"
28 : #include "strxcpyx.h"
29 : #include "util.h"
30 :
31 7956 : int device_new_aux(sd_device **ret) {
32 : sd_device *device;
33 :
34 7956 : assert(ret);
35 :
36 7956 : device = new(sd_device, 1);
37 7956 : if (!device)
38 0 : return -ENOMEM;
39 :
40 7956 : *device = (sd_device) {
41 : .n_ref = 1,
42 : .watch_handle = -1,
43 : .devmode = (mode_t) -1,
44 : .devuid = (uid_t) -1,
45 : .devgid = (gid_t) -1,
46 : .action = _DEVICE_ACTION_INVALID,
47 : };
48 :
49 7956 : *ret = device;
50 7956 : return 0;
51 : }
52 :
53 7956 : static sd_device *device_free(sd_device *device) {
54 7956 : assert(device);
55 :
56 7956 : sd_device_unref(device->parent);
57 7956 : free(device->syspath);
58 7956 : free(device->sysname);
59 7956 : free(device->devtype);
60 7956 : free(device->devname);
61 7956 : free(device->subsystem);
62 7956 : free(device->driver_subsystem);
63 7956 : free(device->driver);
64 7956 : free(device->id_filename);
65 7956 : free(device->properties_strv);
66 7956 : free(device->properties_nulstr);
67 :
68 7956 : ordered_hashmap_free_free_free(device->properties);
69 7956 : ordered_hashmap_free_free_free(device->properties_db);
70 7956 : hashmap_free_free_free(device->sysattr_values);
71 7956 : set_free_free(device->sysattrs);
72 7956 : set_free_free(device->tags);
73 7956 : set_free_free(device->devlinks);
74 :
75 7956 : return mfree(device);
76 : }
77 :
78 31494 : DEFINE_PUBLIC_TRIVIAL_REF_UNREF_FUNC(sd_device, sd_device, device_free);
79 :
80 57307 : int device_add_property_aux(sd_device *device, const char *_key, const char *_value, bool db) {
81 : OrderedHashmap **properties;
82 :
83 57307 : assert(device);
84 57307 : assert(_key);
85 :
86 57307 : if (db)
87 13123 : properties = &device->properties_db;
88 : else
89 44184 : properties = &device->properties;
90 :
91 57307 : if (_value) {
92 56979 : _cleanup_free_ char *key = NULL, *value = NULL, *old_key = NULL, *old_value = NULL;
93 : int r;
94 :
95 56979 : r = ordered_hashmap_ensure_allocated(properties, &string_hash_ops);
96 56979 : if (r < 0)
97 0 : return r;
98 :
99 56979 : key = strdup(_key);
100 56979 : if (!key)
101 0 : return -ENOMEM;
102 :
103 56979 : value = strdup(_value);
104 56979 : if (!value)
105 0 : return -ENOMEM;
106 :
107 56979 : old_value = ordered_hashmap_get2(*properties, key, (void**) &old_key);
108 :
109 56979 : r = ordered_hashmap_replace(*properties, key, value);
110 56979 : if (r < 0)
111 0 : return r;
112 :
113 56979 : key = NULL;
114 56979 : value = NULL;
115 : } else {
116 328 : _cleanup_free_ char *key = NULL;
117 656 : _cleanup_free_ char *value = NULL;
118 :
119 328 : value = ordered_hashmap_remove2(*properties, _key, (void**) &key);
120 : }
121 :
122 57307 : if (!db) {
123 44184 : device->properties_generation++;
124 44184 : device->properties_buf_outdated = true;
125 : }
126 :
127 57307 : return 0;
128 : }
129 :
130 31061 : int device_add_property_internal(sd_device *device, const char *key, const char *value) {
131 31061 : return device_add_property_aux(device, key, value, false);
132 : }
133 :
134 7956 : int device_set_syspath(sd_device *device, const char *_syspath, bool verify) {
135 7956 : _cleanup_free_ char *syspath = NULL;
136 : const char *devpath;
137 : int r;
138 :
139 7956 : assert(device);
140 7956 : assert(_syspath);
141 :
142 : /* must be a subdirectory of /sys */
143 7956 : if (!path_startswith(_syspath, "/sys/"))
144 0 : return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
145 : "sd-device: Syspath '%s' is not a subdirectory of /sys",
146 : _syspath);
147 :
148 7956 : if (verify) {
149 7956 : r = chase_symlinks(_syspath, NULL, 0, &syspath);
150 7956 : if (r == -ENOENT)
151 0 : return -ENODEV; /* the device does not exist (any more?) */
152 7956 : if (r < 0)
153 0 : return log_debug_errno(r, "sd-device: Failed to get target of '%s': %m", _syspath);
154 :
155 7956 : if (!path_startswith(syspath, "/sys")) {
156 0 : _cleanup_free_ char *real_sys = NULL, *new_syspath = NULL;
157 : char *p;
158 :
159 : /* /sys is a symlink to somewhere sysfs is mounted on? In that case, we convert the path to real sysfs to "/sys". */
160 0 : r = chase_symlinks("/sys", NULL, 0, &real_sys);
161 0 : if (r < 0)
162 0 : return log_debug_errno(r, "sd-device: Failed to chase symlink /sys: %m");
163 :
164 0 : p = path_startswith(syspath, real_sys);
165 0 : if (!p)
166 0 : return log_debug_errno(SYNTHETIC_ERRNO(ENODEV),
167 : "sd-device: Canonicalized path '%s' does not starts with sysfs mount point '%s'",
168 : syspath, real_sys);
169 :
170 0 : new_syspath = path_join("/sys", p);
171 0 : if (!new_syspath)
172 0 : return -ENOMEM;
173 :
174 0 : free_and_replace(syspath, new_syspath);
175 0 : path_simplify(syspath, false);
176 : }
177 :
178 7956 : if (path_startswith(syspath, "/sys/devices/")) {
179 : char *path;
180 :
181 : /* all 'devices' require an 'uevent' file */
182 33810 : path = strjoina(syspath, "/uevent");
183 6762 : r = access(path, F_OK);
184 6762 : if (r < 0) {
185 3 : if (errno == ENOENT)
186 : /* this is not a valid device */
187 3 : return -ENODEV;
188 :
189 0 : return log_debug_errno(errno, "sd-device: %s does not have an uevent file: %m", syspath);
190 : }
191 : } else {
192 : /* everything else just needs to be a directory */
193 1194 : if (!is_dir(syspath, false))
194 11 : return -ENODEV;
195 : }
196 : } else {
197 0 : syspath = strdup(_syspath);
198 0 : if (!syspath)
199 0 : return -ENOMEM;
200 : }
201 :
202 7942 : devpath = syspath + STRLEN("/sys");
203 :
204 7942 : if (devpath[0] == '\0')
205 : /* '/sys' alone is not a valid device path */
206 0 : return -ENODEV;
207 :
208 7942 : r = device_add_property_internal(device, "DEVPATH", devpath);
209 7942 : if (r < 0)
210 0 : return r;
211 :
212 7942 : free_and_replace(device->syspath, syspath);
213 7942 : device->devpath = devpath;
214 7942 : return 0;
215 : }
216 :
217 7956 : _public_ int sd_device_new_from_syspath(sd_device **ret, const char *syspath) {
218 7956 : _cleanup_(sd_device_unrefp) sd_device *device = NULL;
219 : int r;
220 :
221 7956 : assert_return(ret, -EINVAL);
222 7956 : assert_return(syspath, -EINVAL);
223 :
224 7956 : r = device_new_aux(&device);
225 7956 : if (r < 0)
226 0 : return r;
227 :
228 7956 : r = device_set_syspath(device, syspath, true);
229 7956 : if (r < 0)
230 14 : return r;
231 :
232 7942 : *ret = TAKE_PTR(device);
233 7942 : return 0;
234 : }
235 :
236 606 : _public_ int sd_device_new_from_devnum(sd_device **ret, char type, dev_t devnum) {
237 : char *syspath;
238 : char id[DECIMAL_STR_MAX(unsigned) * 2 + 1];
239 :
240 606 : assert_return(ret, -EINVAL);
241 606 : assert_return(IN_SET(type, 'b', 'c'), -EINVAL);
242 :
243 : /* use /sys/dev/{block,char}/<maj>:<min> link */
244 606 : xsprintf(id, "%u:%u", major(devnum), minor(devnum));
245 :
246 5454 : syspath = strjoina("/sys/dev/", (type == 'b' ? "block" : "char"), "/", id);
247 :
248 606 : return sd_device_new_from_syspath(ret, syspath);
249 : }
250 :
251 151 : _public_ int sd_device_new_from_subsystem_sysname(sd_device **ret, const char *subsystem, const char *sysname) {
252 : char *name, *syspath;
253 151 : size_t len = 0;
254 :
255 151 : assert_return(ret, -EINVAL);
256 151 : assert_return(subsystem, -EINVAL);
257 151 : assert_return(sysname, -EINVAL);
258 :
259 151 : if (streq(subsystem, "subsystem")) {
260 5 : syspath = strjoina("/sys/subsystem/", sysname);
261 1 : if (access(syspath, F_OK) >= 0)
262 0 : return sd_device_new_from_syspath(ret, syspath);
263 :
264 5 : syspath = strjoina("/sys/bus/", sysname);
265 1 : if (access(syspath, F_OK) >= 0)
266 1 : return sd_device_new_from_syspath(ret, syspath);
267 :
268 0 : syspath = strjoina("/sys/class/", sysname);
269 0 : if (access(syspath, F_OK) >= 0)
270 0 : return sd_device_new_from_syspath(ret, syspath);
271 150 : } else if (streq(subsystem, "module")) {
272 115 : syspath = strjoina("/sys/module/", sysname);
273 23 : if (access(syspath, F_OK) >= 0)
274 23 : return sd_device_new_from_syspath(ret, syspath);
275 127 : } else if (streq(subsystem, "drivers")) {
276 : char subsys[PATH_MAX];
277 : char *driver;
278 :
279 1 : strscpy(subsys, sizeof(subsys), sysname);
280 1 : driver = strchr(subsys, ':');
281 1 : if (driver) {
282 1 : driver[0] = '\0';
283 1 : driver++;
284 :
285 9 : syspath = strjoina("/sys/subsystem/", subsys, "/drivers/", driver);
286 1 : if (access(syspath, F_OK) >= 0)
287 1 : return sd_device_new_from_syspath(ret, syspath);
288 :
289 9 : syspath = strjoina("/sys/bus/", subsys, "/drivers/", driver);
290 1 : if (access(syspath, F_OK) >= 0)
291 1 : return sd_device_new_from_syspath(ret, syspath);
292 : }
293 : }
294 :
295 : /* translate sysname back to sysfs filename */
296 126 : name = strdupa(sysname);
297 1129 : while (name[len] != '\0') {
298 1003 : if (name[len] == '/')
299 0 : name[len] = '!';
300 :
301 1003 : len++;
302 : }
303 :
304 1134 : syspath = strjoina("/sys/subsystem/", subsystem, "/devices/", name);
305 126 : if (access(syspath, F_OK) >= 0)
306 0 : return sd_device_new_from_syspath(ret, syspath);
307 :
308 1134 : syspath = strjoina("/sys/bus/", subsystem, "/devices/", name);
309 126 : if (access(syspath, F_OK) >= 0)
310 0 : return sd_device_new_from_syspath(ret, syspath);
311 :
312 1134 : syspath = strjoina("/sys/class/", subsystem, "/", name);
313 126 : if (access(syspath, F_OK) >= 0)
314 125 : return sd_device_new_from_syspath(ret, syspath);
315 :
316 9 : syspath = strjoina("/sys/firmware/", subsystem, "/", sysname);
317 1 : if (access(syspath, F_OK) >= 0)
318 0 : return sd_device_new_from_syspath(ret, syspath);
319 :
320 1 : return -ENODEV;
321 : }
322 :
323 749 : int device_set_devtype(sd_device *device, const char *_devtype) {
324 749 : _cleanup_free_ char *devtype = NULL;
325 : int r;
326 :
327 749 : assert(device);
328 749 : assert(_devtype);
329 :
330 749 : devtype = strdup(_devtype);
331 749 : if (!devtype)
332 0 : return -ENOMEM;
333 :
334 749 : r = device_add_property_internal(device, "DEVTYPE", devtype);
335 749 : if (r < 0)
336 0 : return r;
337 :
338 749 : free_and_replace(device->devtype, devtype);
339 :
340 749 : return 0;
341 : }
342 :
343 106 : int device_set_ifindex(sd_device *device, const char *_ifindex) {
344 : int ifindex, r;
345 :
346 106 : assert(device);
347 106 : assert(_ifindex);
348 :
349 106 : r = parse_ifindex(_ifindex, &ifindex);
350 106 : if (r < 0)
351 0 : return r;
352 :
353 106 : r = device_add_property_internal(device, "IFINDEX", _ifindex);
354 106 : if (r < 0)
355 0 : return r;
356 :
357 106 : device->ifindex = ifindex;
358 :
359 106 : return 0;
360 : }
361 :
362 2102 : int device_set_devname(sd_device *device, const char *_devname) {
363 2102 : _cleanup_free_ char *devname = NULL;
364 : int r;
365 :
366 2102 : assert(device);
367 2102 : assert(_devname);
368 :
369 2102 : if (_devname[0] != '/') {
370 2102 : r = asprintf(&devname, "/dev/%s", _devname);
371 2102 : if (r < 0)
372 0 : return -ENOMEM;
373 : } else {
374 0 : devname = strdup(_devname);
375 0 : if (!devname)
376 0 : return -ENOMEM;
377 : }
378 :
379 2102 : r = device_add_property_internal(device, "DEVNAME", devname);
380 2102 : if (r < 0)
381 0 : return r;
382 :
383 2102 : free_and_replace(device->devname, devname);
384 :
385 2102 : return 0;
386 : }
387 :
388 63 : int device_set_devmode(sd_device *device, const char *_devmode) {
389 : unsigned devmode;
390 : int r;
391 :
392 63 : assert(device);
393 63 : assert(_devmode);
394 :
395 63 : r = safe_atou(_devmode, &devmode);
396 63 : if (r < 0)
397 0 : return r;
398 :
399 63 : if (devmode > 07777)
400 0 : return -EINVAL;
401 :
402 63 : r = device_add_property_internal(device, "DEVMODE", _devmode);
403 63 : if (r < 0)
404 0 : return r;
405 :
406 63 : device->devmode = devmode;
407 :
408 63 : return 0;
409 : }
410 :
411 2102 : int device_set_devnum(sd_device *device, const char *major, const char *minor) {
412 2102 : unsigned maj = 0, min = 0;
413 : int r;
414 :
415 2102 : assert(device);
416 2102 : assert(major);
417 :
418 2102 : r = safe_atou(major, &maj);
419 2102 : if (r < 0)
420 0 : return r;
421 2102 : if (!maj)
422 0 : return 0;
423 :
424 2102 : if (minor) {
425 2102 : r = safe_atou(minor, &min);
426 2102 : if (r < 0)
427 0 : return r;
428 : }
429 :
430 2102 : r = device_add_property_internal(device, "MAJOR", major);
431 2102 : if (r < 0)
432 0 : return r;
433 :
434 2102 : if (minor) {
435 2102 : r = device_add_property_internal(device, "MINOR", minor);
436 2102 : if (r < 0)
437 0 : return r;
438 : }
439 :
440 2102 : device->devnum = makedev(maj, min);
441 :
442 2102 : return 0;
443 : }
444 :
445 11647 : static int handle_uevent_line(sd_device *device, const char *key, const char *value, const char **major, const char **minor) {
446 : int r;
447 :
448 11647 : assert(device);
449 11647 : assert(key);
450 11647 : assert(value);
451 11647 : assert(major);
452 11647 : assert(minor);
453 :
454 11647 : if (streq(key, "DEVTYPE")) {
455 749 : r = device_set_devtype(device, value);
456 749 : if (r < 0)
457 0 : return r;
458 10898 : } else if (streq(key, "IFINDEX")) {
459 106 : r = device_set_ifindex(device, value);
460 106 : if (r < 0)
461 0 : return r;
462 10792 : } else if (streq(key, "DEVNAME")) {
463 2102 : r = device_set_devname(device, value);
464 2102 : if (r < 0)
465 0 : return r;
466 8690 : } else if (streq(key, "DEVMODE")) {
467 63 : r = device_set_devmode(device, value);
468 63 : if (r < 0)
469 0 : return r;
470 8627 : } else if (streq(key, "MAJOR"))
471 2102 : *major = value;
472 6525 : else if (streq(key, "MINOR"))
473 2102 : *minor = value;
474 : else {
475 4423 : r = device_add_property_internal(device, key, value);
476 4423 : if (r < 0)
477 0 : return r;
478 : }
479 :
480 11647 : return 0;
481 : }
482 :
483 31660 : int device_read_uevent_file(sd_device *device) {
484 31660 : _cleanup_free_ char *uevent = NULL;
485 31660 : const char *syspath, *key = NULL, *value = NULL, *major = NULL, *minor = NULL;
486 : char *path;
487 : size_t uevent_len;
488 : unsigned i;
489 : int r;
490 :
491 : enum {
492 : PRE_KEY,
493 : KEY,
494 : PRE_VALUE,
495 : VALUE,
496 : INVALID_LINE,
497 31660 : } state = PRE_KEY;
498 :
499 31660 : assert(device);
500 :
501 31660 : if (device->uevent_loaded || device->sealed)
502 25644 : return 0;
503 :
504 6016 : r = sd_device_get_syspath(device, &syspath);
505 6016 : if (r < 0)
506 0 : return r;
507 :
508 30080 : path = strjoina(syspath, "/uevent");
509 :
510 6016 : r = read_full_file(path, &uevent, &uevent_len);
511 6016 : if (r == -EACCES) {
512 : /* empty uevent files may be write-only */
513 797 : device->uevent_loaded = true;
514 797 : return 0;
515 : }
516 5219 : if (r == -ENOENT)
517 : /* some devices may not have uevent files, see set_syspath() */
518 0 : return 0;
519 5219 : if (r < 0)
520 0 : return log_device_debug_errno(device, r, "sd-device: Failed to read uevent file '%s': %m", path);
521 :
522 5219 : device->uevent_loaded = true;
523 :
524 218516 : for (i = 0; i < uevent_len; i++)
525 213297 : switch (state) {
526 11683 : case PRE_KEY:
527 11683 : if (!strchr(NEWLINE, uevent[i])) {
528 11647 : key = &uevent[i];
529 :
530 11647 : state = KEY;
531 : }
532 :
533 11683 : break;
534 77255 : case KEY:
535 77255 : if (uevent[i] == '=') {
536 11647 : uevent[i] = '\0';
537 :
538 11647 : state = PRE_VALUE;
539 65608 : } else if (strchr(NEWLINE, uevent[i])) {
540 0 : uevent[i] = '\0';
541 0 : log_device_debug(device, "sd-device: Invalid uevent line '%s', ignoring", key);
542 :
543 0 : state = PRE_KEY;
544 : }
545 :
546 77255 : break;
547 11647 : case PRE_VALUE:
548 11647 : value = &uevent[i];
549 11647 : state = VALUE;
550 :
551 : _fallthrough_; /* to handle empty property */
552 124359 : case VALUE:
553 124359 : if (strchr(NEWLINE, uevent[i])) {
554 11647 : uevent[i] = '\0';
555 :
556 11647 : r = handle_uevent_line(device, key, value, &major, &minor);
557 11647 : if (r < 0)
558 0 : log_device_debug_errno(device, r, "sd-device: Failed to handle uevent entry '%s=%s', ignoring: %m", key, value);
559 :
560 11647 : state = PRE_KEY;
561 : }
562 :
563 124359 : break;
564 0 : default:
565 0 : assert_not_reached("Invalid state when parsing uevent file");
566 : }
567 :
568 5219 : if (major) {
569 2102 : r = device_set_devnum(device, major, minor);
570 2102 : if (r < 0)
571 0 : log_device_debug_errno(device, r, "sd-device: Failed to set 'MAJOR=%s' or 'MINOR=%s' from '%s', ignoring: %m", major, minor, path);
572 : }
573 :
574 5219 : return 0;
575 : }
576 :
577 5822 : _public_ int sd_device_get_ifindex(sd_device *device, int *ifindex) {
578 : int r;
579 :
580 5822 : assert_return(device, -EINVAL);
581 :
582 5822 : r = device_read_uevent_file(device);
583 5822 : if (r < 0)
584 0 : return r;
585 :
586 5822 : if (device->ifindex <= 0)
587 5648 : return -ENOENT;
588 :
589 174 : if (ifindex)
590 174 : *ifindex = device->ifindex;
591 :
592 174 : return 0;
593 : }
594 :
595 618 : _public_ int sd_device_new_from_device_id(sd_device **ret, const char *id) {
596 : int r;
597 :
598 618 : assert_return(ret, -EINVAL);
599 618 : assert_return(id, -EINVAL);
600 :
601 618 : switch (id[0]) {
602 473 : case 'b':
603 : case 'c': {
604 : dev_t devt;
605 :
606 473 : if (isempty(id))
607 0 : return -EINVAL;
608 :
609 473 : r = parse_dev(id + 1, &devt);
610 473 : if (r < 0)
611 0 : return r;
612 :
613 473 : return sd_device_new_from_devnum(ret, id[0], devt);
614 : }
615 :
616 68 : case 'n': {
617 68 : _cleanup_(sd_device_unrefp) sd_device *device = NULL;
618 68 : _cleanup_close_ int sk = -1;
619 68 : struct ifreq ifr = {};
620 : int ifindex;
621 :
622 68 : r = parse_ifindex(&id[1], &ifr.ifr_ifindex);
623 68 : if (r < 0)
624 0 : return r;
625 :
626 68 : sk = socket_ioctl_fd();
627 68 : if (sk < 0)
628 0 : return sk;
629 :
630 68 : r = ioctl(sk, SIOCGIFNAME, &ifr);
631 68 : if (r < 0)
632 7 : return -errno;
633 :
634 61 : r = sd_device_new_from_subsystem_sysname(&device, "net", ifr.ifr_name);
635 61 : if (r < 0)
636 0 : return r;
637 :
638 61 : r = sd_device_get_ifindex(device, &ifindex);
639 61 : if (r < 0)
640 0 : return r;
641 :
642 : /* this is racey, so we might end up with the wrong device */
643 61 : if (ifr.ifr_ifindex != ifindex)
644 0 : return -ENODEV;
645 :
646 61 : *ret = TAKE_PTR(device);
647 61 : return 0;
648 : }
649 :
650 77 : case '+': {
651 : char subsys[PATH_MAX];
652 : char *sysname;
653 :
654 77 : (void) strscpy(subsys, sizeof(subsys), id + 1);
655 77 : sysname = strchr(subsys, ':');
656 77 : if (!sysname)
657 0 : return -EINVAL;
658 :
659 77 : sysname[0] = '\0';
660 77 : sysname++;
661 :
662 77 : return sd_device_new_from_subsystem_sysname(ret, subsys, sysname);
663 : }
664 :
665 0 : default:
666 0 : return -EINVAL;
667 : }
668 : }
669 :
670 27642 : _public_ int sd_device_get_syspath(sd_device *device, const char **ret) {
671 27642 : assert_return(device, -EINVAL);
672 27642 : assert_return(ret, -EINVAL);
673 :
674 27642 : assert(path_startswith(device->syspath, "/sys/"));
675 :
676 27642 : *ret = device->syspath;
677 :
678 27642 : return 0;
679 : }
680 :
681 1 : static int device_new_from_child(sd_device **ret, sd_device *child) {
682 1 : _cleanup_free_ char *path = NULL;
683 : const char *subdir, *syspath;
684 : int r;
685 :
686 1 : assert(ret);
687 1 : assert(child);
688 :
689 1 : r = sd_device_get_syspath(child, &syspath);
690 1 : if (r < 0)
691 0 : return r;
692 :
693 1 : path = strdup(syspath);
694 1 : if (!path)
695 0 : return -ENOMEM;
696 1 : subdir = path + STRLEN("/sys");
697 :
698 3 : for (;;) {
699 : char *pos;
700 :
701 4 : pos = strrchr(subdir, '/');
702 4 : if (!pos || pos < subdir + 2)
703 : break;
704 :
705 3 : *pos = '\0';
706 :
707 3 : r = sd_device_new_from_syspath(ret, path);
708 3 : if (r < 0)
709 3 : continue;
710 :
711 0 : return 0;
712 : }
713 :
714 1 : return -ENODEV;
715 : }
716 :
717 1 : _public_ int sd_device_get_parent(sd_device *child, sd_device **ret) {
718 :
719 1 : assert_return(ret, -EINVAL);
720 1 : assert_return(child, -EINVAL);
721 :
722 1 : if (!child->parent_set) {
723 1 : child->parent_set = true;
724 :
725 1 : (void) device_new_from_child(&child->parent, child);
726 : }
727 :
728 1 : if (!child->parent)
729 1 : return -ENOENT;
730 :
731 0 : *ret = child->parent;
732 0 : return 0;
733 : }
734 :
735 7859 : int device_set_subsystem(sd_device *device, const char *_subsystem) {
736 7859 : _cleanup_free_ char *subsystem = NULL;
737 : int r;
738 :
739 7859 : assert(device);
740 7859 : assert(_subsystem);
741 :
742 7859 : subsystem = strdup(_subsystem);
743 7859 : if (!subsystem)
744 0 : return -ENOMEM;
745 :
746 7859 : r = device_add_property_internal(device, "SUBSYSTEM", subsystem);
747 7859 : if (r < 0)
748 0 : return r;
749 :
750 7859 : device->subsystem_set = true;
751 7859 : return free_and_replace(device->subsystem, subsystem);
752 : }
753 :
754 412 : static int device_set_drivers_subsystem(sd_device *device, const char *_subsystem) {
755 412 : _cleanup_free_ char *subsystem = NULL;
756 : int r;
757 :
758 412 : assert(device);
759 412 : assert(_subsystem);
760 412 : assert(*_subsystem);
761 :
762 412 : subsystem = strdup(_subsystem);
763 412 : if (!subsystem)
764 0 : return -ENOMEM;
765 :
766 412 : r = device_set_subsystem(device, "drivers");
767 412 : if (r < 0)
768 0 : return r;
769 :
770 412 : return free_and_replace(device->driver_subsystem, subsystem);
771 : }
772 :
773 10318 : _public_ int sd_device_get_subsystem(sd_device *device, const char **ret) {
774 10318 : const char *syspath, *drivers = NULL;
775 : int r;
776 :
777 10318 : assert_return(ret, -EINVAL);
778 10318 : assert_return(device, -EINVAL);
779 :
780 10318 : r = sd_device_get_syspath(device, &syspath);
781 10318 : if (r < 0)
782 0 : return r;
783 :
784 10318 : if (!device->subsystem_set) {
785 7859 : _cleanup_free_ char *subsystem = NULL;
786 : char *path;
787 :
788 : /* read 'subsystem' link */
789 39295 : path = strjoina(syspath, "/subsystem");
790 7859 : r = readlink_value(path, &subsystem);
791 7859 : if (r >= 0)
792 6676 : r = device_set_subsystem(device, subsystem);
793 : /* use implicit names */
794 1183 : else if (path_startswith(device->devpath, "/module/"))
795 644 : r = device_set_subsystem(device, "module");
796 666 : else if (!(drivers = strstr(syspath, "/drivers/")) &&
797 127 : PATH_STARTSWITH_SET(device->devpath, "/subsystem/",
798 : "/class/",
799 : "/bus/"))
800 127 : r = device_set_subsystem(device, "subsystem");
801 7859 : if (r < 0 && r != -ENOENT)
802 0 : return log_device_debug_errno(device, r, "sd-device: Failed to set subsystem for %s: %m", device->devpath);
803 :
804 7859 : device->subsystem_set = true;
805 2459 : } else if (!device->driver_subsystem_set)
806 0 : drivers = strstr(syspath, "/drivers/");
807 :
808 10318 : if (!device->driver_subsystem_set) {
809 7859 : if (drivers) {
810 412 : _cleanup_free_ char *subpath = NULL;
811 :
812 412 : subpath = strndup(syspath, drivers - syspath);
813 412 : if (!subpath)
814 0 : r = -ENOMEM;
815 : else {
816 : const char *subsys;
817 :
818 412 : subsys = strrchr(subpath, '/');
819 412 : if (!subsys)
820 0 : r = -EINVAL;
821 : else
822 412 : r = device_set_drivers_subsystem(device, subsys + 1);
823 : }
824 412 : if (r < 0 && r != -ENOENT)
825 0 : return log_device_debug_errno(device, r, "sd-device: Failed to set subsystem for driver %s: %m", device->devpath);
826 : }
827 :
828 7859 : device->driver_subsystem_set = true;
829 : }
830 :
831 10318 : if (!device->subsystem)
832 0 : return -ENOENT;
833 :
834 10318 : *ret = device->subsystem;
835 10318 : return 0;
836 : }
837 :
838 1126 : _public_ int sd_device_get_devtype(sd_device *device, const char **devtype) {
839 : int r;
840 :
841 1126 : assert(devtype);
842 1126 : assert(device);
843 :
844 1126 : r = device_read_uevent_file(device);
845 1126 : if (r < 0)
846 0 : return r;
847 :
848 1126 : if (!device->devtype)
849 1045 : return -ENOENT;
850 :
851 81 : *devtype = device->devtype;
852 :
853 81 : return 0;
854 : }
855 :
856 0 : _public_ int sd_device_get_parent_with_subsystem_devtype(sd_device *child, const char *subsystem, const char *devtype, sd_device **ret) {
857 0 : sd_device *parent = NULL;
858 : int r;
859 :
860 0 : assert_return(child, -EINVAL);
861 0 : assert_return(subsystem, -EINVAL);
862 :
863 0 : r = sd_device_get_parent(child, &parent);
864 0 : while (r >= 0) {
865 0 : const char *parent_subsystem = NULL;
866 0 : const char *parent_devtype = NULL;
867 :
868 0 : (void) sd_device_get_subsystem(parent, &parent_subsystem);
869 0 : if (streq_ptr(parent_subsystem, subsystem)) {
870 0 : if (!devtype)
871 0 : break;
872 :
873 0 : (void) sd_device_get_devtype(parent, &parent_devtype);
874 0 : if (streq_ptr(parent_devtype, devtype))
875 0 : break;
876 : }
877 0 : r = sd_device_get_parent(parent, &parent);
878 : }
879 :
880 0 : if (r < 0)
881 0 : return r;
882 :
883 0 : *ret = parent;
884 0 : return 0;
885 : }
886 :
887 8376 : _public_ int sd_device_get_devnum(sd_device *device, dev_t *devnum) {
888 : int r;
889 :
890 8376 : assert_return(device, -EINVAL);
891 :
892 8376 : r = device_read_uevent_file(device);
893 8376 : if (r < 0)
894 0 : return r;
895 :
896 8376 : if (major(device->devnum) <= 0)
897 5651 : return -ENOENT;
898 :
899 2725 : if (devnum)
900 2725 : *devnum = device->devnum;
901 :
902 2725 : return 0;
903 : }
904 :
905 96 : int device_set_driver(sd_device *device, const char *_driver) {
906 96 : _cleanup_free_ char *driver = NULL;
907 : int r;
908 :
909 96 : assert(device);
910 96 : assert(_driver);
911 :
912 96 : driver = strdup(_driver);
913 96 : if (!driver)
914 0 : return -ENOMEM;
915 :
916 96 : r = device_add_property_internal(device, "DRIVER", driver);
917 96 : if (r < 0)
918 0 : return r;
919 :
920 96 : device->driver_set = true;
921 96 : return free_and_replace(device->driver, driver);
922 : }
923 :
924 1124 : _public_ int sd_device_get_driver(sd_device *device, const char **ret) {
925 1124 : assert_return(device, -EINVAL);
926 1124 : assert_return(ret, -EINVAL);
927 :
928 1124 : if (!device->driver_set) {
929 1123 : _cleanup_free_ char *driver = NULL;
930 : const char *syspath;
931 : char *path;
932 : int r;
933 :
934 1123 : r = sd_device_get_syspath(device, &syspath);
935 1123 : if (r < 0)
936 0 : return r;
937 :
938 5615 : path = strjoina(syspath, "/driver");
939 1123 : r = readlink_value(path, &driver);
940 1123 : if (r >= 0) {
941 96 : r = device_set_driver(device, driver);
942 96 : if (r < 0)
943 0 : return log_device_debug_errno(device, r, "sd-device: Failed to set driver for %s: %m", device->devpath);
944 1027 : } else if (r == -ENOENT)
945 1027 : device->driver_set = true;
946 : else
947 0 : return log_device_debug_errno(device, r, "sd-device: Failed to set driver for %s: %m", device->devpath);
948 : }
949 :
950 1124 : if (!device->driver)
951 1028 : return -ENOENT;
952 :
953 96 : *ret = device->driver;
954 96 : return 0;
955 : }
956 :
957 80828 : _public_ int sd_device_get_devpath(sd_device *device, const char **devpath) {
958 80828 : assert_return(device, -EINVAL);
959 80828 : assert_return(devpath, -EINVAL);
960 :
961 80828 : assert(device->devpath);
962 80828 : assert(device->devpath[0] == '/');
963 :
964 80828 : *devpath = device->devpath;
965 80828 : return 0;
966 : }
967 :
968 1773 : _public_ int sd_device_get_devname(sd_device *device, const char **devname) {
969 : int r;
970 :
971 1773 : assert_return(device, -EINVAL);
972 1773 : assert_return(devname, -EINVAL);
973 :
974 1773 : r = device_read_uevent_file(device);
975 1773 : if (r < 0)
976 0 : return r;
977 :
978 1773 : if (!device->devname)
979 1007 : return -ENOENT;
980 :
981 766 : assert(path_startswith(device->devname, "/dev/"));
982 :
983 766 : *devname = device->devname;
984 766 : return 0;
985 : }
986 :
987 1728 : static int device_set_sysname(sd_device *device) {
988 1728 : _cleanup_free_ char *sysname = NULL;
989 1728 : const char *sysnum = NULL;
990 : const char *pos;
991 1728 : size_t len = 0;
992 :
993 1728 : if (!device->devpath)
994 0 : return -EINVAL;
995 :
996 1728 : pos = strrchr(device->devpath, '/');
997 1728 : if (!pos)
998 0 : return -EINVAL;
999 1728 : pos++;
1000 :
1001 : /* devpath is not a root directory */
1002 1728 : if (*pos == '\0' || pos <= device->devpath)
1003 0 : return -EINVAL;
1004 :
1005 1728 : sysname = strdup(pos);
1006 1728 : if (!sysname)
1007 0 : return -ENOMEM;
1008 :
1009 : /* some devices have '!' in their name, change that to '/' */
1010 15520 : while (sysname[len] != '\0') {
1011 13792 : if (sysname[len] == '!')
1012 0 : sysname[len] = '/';
1013 :
1014 13792 : len++;
1015 : }
1016 :
1017 : /* trailing number */
1018 3533 : while (len > 0 && isdigit(sysname[--len]))
1019 1805 : sysnum = &sysname[len];
1020 :
1021 1728 : if (len == 0)
1022 7 : sysnum = NULL;
1023 :
1024 1728 : device->sysname_set = true;
1025 1728 : device->sysnum = sysnum;
1026 1728 : return free_and_replace(device->sysname, sysname);
1027 : }
1028 :
1029 1729 : _public_ int sd_device_get_sysname(sd_device *device, const char **ret) {
1030 : int r;
1031 :
1032 1729 : assert_return(device, -EINVAL);
1033 1729 : assert_return(ret, -EINVAL);
1034 :
1035 1729 : if (!device->sysname_set) {
1036 1728 : r = device_set_sysname(device);
1037 1728 : if (r < 0)
1038 0 : return r;
1039 : }
1040 :
1041 1729 : assert_return(device->sysname, -ENOENT);
1042 :
1043 1729 : *ret = device->sysname;
1044 1729 : return 0;
1045 : }
1046 :
1047 1124 : _public_ int sd_device_get_sysnum(sd_device *device, const char **ret) {
1048 : int r;
1049 :
1050 1124 : assert_return(device, -EINVAL);
1051 1124 : assert_return(ret, -EINVAL);
1052 :
1053 1124 : if (!device->sysname_set) {
1054 0 : r = device_set_sysname(device);
1055 0 : if (r < 0)
1056 0 : return r;
1057 : }
1058 :
1059 1124 : if (!device->sysnum)
1060 498 : return -ENOENT;
1061 :
1062 626 : *ret = device->sysnum;
1063 626 : return 0;
1064 : }
1065 :
1066 1559 : static bool is_valid_tag(const char *tag) {
1067 1559 : assert(tag);
1068 :
1069 1559 : return !strchr(tag, ':') && !strchr(tag, ' ');
1070 : }
1071 :
1072 1559 : int device_add_tag(sd_device *device, const char *tag) {
1073 : int r;
1074 :
1075 1559 : assert(device);
1076 1559 : assert(tag);
1077 :
1078 1559 : if (!is_valid_tag(tag))
1079 0 : return -EINVAL;
1080 :
1081 1559 : r = set_ensure_allocated(&device->tags, &string_hash_ops);
1082 1559 : if (r < 0)
1083 0 : return r;
1084 :
1085 1559 : r = set_put_strdup(device->tags, tag);
1086 1559 : if (r < 0)
1087 0 : return r;
1088 :
1089 1559 : device->tags_generation++;
1090 1559 : device->property_tags_outdated = true;
1091 :
1092 1559 : return 0;
1093 : }
1094 :
1095 948 : int device_add_devlink(sd_device *device, const char *devlink) {
1096 : int r;
1097 :
1098 948 : assert(device);
1099 948 : assert(devlink);
1100 :
1101 948 : r = set_ensure_allocated(&device->devlinks, &string_hash_ops);
1102 948 : if (r < 0)
1103 0 : return r;
1104 :
1105 948 : r = set_put_strdup(device->devlinks, devlink);
1106 948 : if (r < 0)
1107 0 : return r;
1108 :
1109 948 : device->devlinks_generation++;
1110 948 : device->property_devlinks_outdated = true;
1111 :
1112 948 : return 0;
1113 : }
1114 :
1115 13123 : static int device_add_property_internal_from_string(sd_device *device, const char *str) {
1116 13123 : _cleanup_free_ char *key = NULL;
1117 : char *value;
1118 : int r;
1119 :
1120 13123 : assert(device);
1121 13123 : assert(str);
1122 :
1123 13123 : key = strdup(str);
1124 13123 : if (!key)
1125 0 : return -ENOMEM;
1126 :
1127 13123 : value = strchr(key, '=');
1128 13123 : if (!value)
1129 0 : return -EINVAL;
1130 :
1131 13123 : *value = '\0';
1132 :
1133 13123 : if (isempty(++value))
1134 164 : value = NULL;
1135 :
1136 : /* Add the property to both sd_device::properties and sd_device::properties_db,
1137 : * as this is called by only handle_db_line(). */
1138 13123 : r = device_add_property_aux(device, key, value, false);
1139 13123 : if (r < 0)
1140 0 : return r;
1141 :
1142 13123 : return device_add_property_aux(device, key, value, true);
1143 : }
1144 :
1145 2448 : int device_set_usec_initialized(sd_device *device, usec_t when) {
1146 : char s[DECIMAL_STR_MAX(usec_t)];
1147 : int r;
1148 :
1149 2448 : assert(device);
1150 :
1151 2448 : xsprintf(s, USEC_FMT, when);
1152 :
1153 2448 : r = device_add_property_internal(device, "USEC_INITIALIZED", s);
1154 2448 : if (r < 0)
1155 0 : return r;
1156 :
1157 2448 : device->usec_initialized = when;
1158 2448 : return 0;
1159 : }
1160 :
1161 18310 : static int handle_db_line(sd_device *device, char key, const char *value) {
1162 : char *path;
1163 : int r;
1164 :
1165 18310 : assert(device);
1166 18310 : assert(value);
1167 :
1168 18310 : switch (key) {
1169 1559 : case 'G':
1170 1559 : r = device_add_tag(device, value);
1171 1559 : if (r < 0)
1172 0 : return r;
1173 :
1174 1559 : break;
1175 948 : case 'S':
1176 4740 : path = strjoina("/dev/", value);
1177 948 : r = device_add_devlink(device, path);
1178 948 : if (r < 0)
1179 0 : return r;
1180 :
1181 948 : break;
1182 13123 : case 'E':
1183 13123 : r = device_add_property_internal_from_string(device, value);
1184 13123 : if (r < 0)
1185 0 : return r;
1186 :
1187 13123 : break;
1188 2448 : case 'I': {
1189 : usec_t t;
1190 :
1191 2448 : r = safe_atou64(value, &t);
1192 2448 : if (r < 0)
1193 0 : return r;
1194 :
1195 2448 : r = device_set_usec_initialized(device, t);
1196 2448 : if (r < 0)
1197 0 : return r;
1198 :
1199 2448 : break;
1200 : }
1201 6 : case 'L':
1202 6 : r = safe_atoi(value, &device->devlink_priority);
1203 6 : if (r < 0)
1204 0 : return r;
1205 :
1206 6 : break;
1207 226 : case 'W':
1208 226 : r = safe_atoi(value, &device->watch_handle);
1209 226 : if (r < 0)
1210 0 : return r;
1211 :
1212 226 : break;
1213 0 : default:
1214 0 : log_device_debug(device, "sd-device: Unknown key '%c' in device db, ignoring", key);
1215 : }
1216 :
1217 18310 : return 0;
1218 : }
1219 :
1220 8818 : int device_get_id_filename(sd_device *device, const char **ret) {
1221 8818 : assert(device);
1222 8818 : assert(ret);
1223 :
1224 8818 : if (!device->id_filename) {
1225 5950 : _cleanup_free_ char *id = NULL;
1226 : const char *subsystem;
1227 : dev_t devnum;
1228 : int ifindex, r;
1229 :
1230 5950 : r = sd_device_get_subsystem(device, &subsystem);
1231 5950 : if (r < 0)
1232 0 : return r;
1233 :
1234 5950 : if (sd_device_get_devnum(device, &devnum) >= 0) {
1235 2036 : assert(subsystem);
1236 :
1237 : /* use dev_t — b259:131072, c254:0 */
1238 2036 : r = asprintf(&id, "%c%u:%u",
1239 2036 : streq(subsystem, "block") ? 'b' : 'c',
1240 : major(devnum), minor(devnum));
1241 2036 : if (r < 0)
1242 0 : return -ENOMEM;
1243 3914 : } else if (sd_device_get_ifindex(device, &ifindex) >= 0) {
1244 : /* use netdev ifindex — n3 */
1245 106 : r = asprintf(&id, "n%u", (unsigned) ifindex);
1246 106 : if (r < 0)
1247 0 : return -ENOMEM;
1248 : } else {
1249 : /* use $subsys:$sysname — pci:0000:00:1f.2
1250 : * sysname() has '!' translated, get it from devpath
1251 : */
1252 : const char *sysname;
1253 :
1254 3808 : sysname = basename(device->devpath);
1255 3808 : if (!sysname)
1256 0 : return -EINVAL;
1257 :
1258 3808 : if (!subsystem)
1259 0 : return -EINVAL;
1260 :
1261 3808 : if (streq(subsystem, "drivers")) {
1262 : /* the 'drivers' pseudo-subsystem is special, and needs the real subsystem
1263 : * encoded as well */
1264 275 : r = asprintf(&id, "+drivers:%s:%s", device->driver_subsystem, sysname);
1265 275 : if (r < 0)
1266 0 : return -ENOMEM;
1267 : } else {
1268 3533 : r = asprintf(&id, "+%s:%s", subsystem, sysname);
1269 3533 : if (r < 0)
1270 0 : return -ENOMEM;
1271 : }
1272 : }
1273 :
1274 5950 : device->id_filename = TAKE_PTR(id);
1275 : }
1276 :
1277 8818 : *ret = device->id_filename;
1278 8818 : return 0;
1279 : }
1280 :
1281 8818 : int device_read_db_internal_filename(sd_device *device, const char *filename) {
1282 8818 : _cleanup_free_ char *db = NULL;
1283 : const char *value;
1284 : size_t db_len, i;
1285 : char key;
1286 : int r;
1287 :
1288 : enum {
1289 : PRE_KEY,
1290 : KEY,
1291 : PRE_VALUE,
1292 : VALUE,
1293 : INVALID_LINE,
1294 8818 : } state = PRE_KEY;
1295 :
1296 8818 : assert(device);
1297 8818 : assert(filename);
1298 :
1299 8818 : r = read_full_file(filename, &db, &db_len);
1300 8818 : if (r < 0) {
1301 5853 : if (r == -ENOENT)
1302 5853 : return 0;
1303 :
1304 0 : return log_device_debug_errno(device, r, "sd-device: Failed to read db '%s': %m", filename);
1305 : }
1306 :
1307 : /* devices with a database entry are initialized */
1308 2965 : device->is_initialized = true;
1309 :
1310 2965 : device->db_loaded = true;
1311 :
1312 519009 : for (i = 0; i < db_len; i++) {
1313 516044 : switch (state) {
1314 18310 : case PRE_KEY:
1315 18310 : if (!strchr(NEWLINE, db[i])) {
1316 18310 : key = db[i];
1317 :
1318 18310 : state = KEY;
1319 : }
1320 :
1321 18310 : break;
1322 18310 : case KEY:
1323 18310 : if (db[i] != ':') {
1324 0 : log_device_debug(device, "sd-device: Invalid db entry with key '%c', ignoring", key);
1325 :
1326 0 : state = INVALID_LINE;
1327 : } else {
1328 18310 : db[i] = '\0';
1329 :
1330 18310 : state = PRE_VALUE;
1331 : }
1332 :
1333 18310 : break;
1334 18310 : case PRE_VALUE:
1335 18310 : value = &db[i];
1336 :
1337 18310 : state = VALUE;
1338 :
1339 18310 : break;
1340 0 : case INVALID_LINE:
1341 0 : if (strchr(NEWLINE, db[i]))
1342 0 : state = PRE_KEY;
1343 :
1344 0 : break;
1345 461114 : case VALUE:
1346 461114 : if (strchr(NEWLINE, db[i])) {
1347 18310 : db[i] = '\0';
1348 18310 : r = handle_db_line(device, key, value);
1349 18310 : if (r < 0)
1350 0 : log_device_debug_errno(device, r, "sd-device: Failed to handle db entry '%c:%s', ignoring: %m", key, value);
1351 :
1352 18310 : state = PRE_KEY;
1353 : }
1354 :
1355 461114 : break;
1356 0 : default:
1357 0 : return log_device_debug_errno(device, SYNTHETIC_ERRNO(EINVAL), "sd-device: invalid db syntax.");
1358 : }
1359 : }
1360 :
1361 2965 : return 0;
1362 : }
1363 :
1364 24920 : int device_read_db_internal(sd_device *device, bool force) {
1365 : const char *id, *path;
1366 : int r;
1367 :
1368 24920 : assert(device);
1369 :
1370 24920 : if (device->db_loaded || (!force && device->sealed))
1371 16102 : return 0;
1372 :
1373 8818 : r = device_get_id_filename(device, &id);
1374 8818 : if (r < 0)
1375 0 : return r;
1376 :
1377 44090 : path = strjoina("/run/udev/data/", id);
1378 :
1379 8818 : return device_read_db_internal_filename(device, path);
1380 : }
1381 :
1382 6385 : _public_ int sd_device_get_is_initialized(sd_device *device) {
1383 : int r;
1384 :
1385 6385 : assert_return(device, -EINVAL);
1386 :
1387 6385 : r = device_read_db(device);
1388 6385 : if (r < 0)
1389 0 : return r;
1390 :
1391 6385 : return device->is_initialized;
1392 : }
1393 :
1394 373 : _public_ int sd_device_get_usec_since_initialized(sd_device *device, uint64_t *usec) {
1395 : usec_t now_ts;
1396 : int r;
1397 :
1398 373 : assert_return(device, -EINVAL);
1399 373 : assert_return(usec, -EINVAL);
1400 :
1401 373 : r = device_read_db(device);
1402 373 : if (r < 0)
1403 0 : return r;
1404 :
1405 373 : if (!device->is_initialized)
1406 0 : return -EBUSY;
1407 :
1408 373 : if (!device->usec_initialized)
1409 79 : return -ENODATA;
1410 :
1411 294 : now_ts = now(clock_boottime_or_monotonic());
1412 :
1413 294 : if (now_ts < device->usec_initialized)
1414 0 : return -EIO;
1415 :
1416 294 : *usec = now_ts - device->usec_initialized;
1417 294 : return 0;
1418 : }
1419 :
1420 905 : _public_ const char *sd_device_get_tag_first(sd_device *device) {
1421 : void *v;
1422 :
1423 905 : assert_return(device, NULL);
1424 :
1425 905 : (void) device_read_db(device);
1426 :
1427 905 : device->tags_iterator_generation = device->tags_generation;
1428 905 : device->tags_iterator = ITERATOR_FIRST;
1429 :
1430 905 : (void) set_iterate(device->tags, &device->tags_iterator, &v);
1431 905 : return v;
1432 : }
1433 :
1434 981 : _public_ const char *sd_device_get_tag_next(sd_device *device) {
1435 : void *v;
1436 :
1437 981 : assert_return(device, NULL);
1438 :
1439 981 : (void) device_read_db(device);
1440 :
1441 981 : if (device->tags_iterator_generation != device->tags_generation)
1442 0 : return NULL;
1443 :
1444 981 : (void) set_iterate(device->tags, &device->tags_iterator, &v);
1445 981 : return v;
1446 : }
1447 :
1448 622 : _public_ const char *sd_device_get_devlink_first(sd_device *device) {
1449 : void *v;
1450 :
1451 622 : assert_return(device, NULL);
1452 :
1453 622 : (void) device_read_db(device);
1454 :
1455 622 : device->devlinks_iterator_generation = device->devlinks_generation;
1456 622 : device->devlinks_iterator = ITERATOR_FIRST;
1457 :
1458 622 : (void) set_iterate(device->devlinks, &device->devlinks_iterator, &v);
1459 622 : return v;
1460 : }
1461 :
1462 1091 : _public_ const char *sd_device_get_devlink_next(sd_device *device) {
1463 : void *v;
1464 :
1465 1091 : assert_return(device, NULL);
1466 :
1467 1091 : (void) device_read_db(device);
1468 :
1469 1091 : if (device->devlinks_iterator_generation != device->devlinks_generation)
1470 0 : return NULL;
1471 :
1472 1091 : (void) set_iterate(device->devlinks, &device->devlinks_iterator, &v);
1473 1091 : return v;
1474 : }
1475 :
1476 14563 : int device_properties_prepare(sd_device *device) {
1477 : int r;
1478 :
1479 14563 : assert(device);
1480 :
1481 14563 : r = device_read_uevent_file(device);
1482 14563 : if (r < 0)
1483 0 : return r;
1484 :
1485 14563 : r = device_read_db(device);
1486 14563 : if (r < 0)
1487 0 : return r;
1488 :
1489 14563 : if (device->property_devlinks_outdated) {
1490 164 : _cleanup_free_ char *devlinks = NULL;
1491 164 : size_t devlinks_allocated = 0, devlinks_len = 0;
1492 : const char *devlink;
1493 :
1494 845 : for (devlink = sd_device_get_devlink_first(device); devlink; devlink = sd_device_get_devlink_next(device)) {
1495 : char *e;
1496 :
1497 681 : if (!GREEDY_REALLOC(devlinks, devlinks_allocated, devlinks_len + strlen(devlink) + 2))
1498 0 : return -ENOMEM;
1499 681 : if (devlinks_len > 0)
1500 517 : stpcpy(devlinks + devlinks_len++, " ");
1501 681 : e = stpcpy(devlinks + devlinks_len, devlink);
1502 681 : devlinks_len = e - devlinks;
1503 : }
1504 :
1505 164 : r = device_add_property_internal(device, "DEVLINKS", devlinks);
1506 164 : if (r < 0)
1507 0 : return r;
1508 :
1509 164 : device->property_devlinks_outdated = false;
1510 : }
1511 :
1512 14563 : if (device->property_tags_outdated) {
1513 905 : _cleanup_free_ char *tags = NULL;
1514 905 : size_t tags_allocated = 0, tags_len = 0;
1515 : const char *tag;
1516 :
1517 905 : if (!GREEDY_REALLOC(tags, tags_allocated, 2))
1518 0 : return -ENOMEM;
1519 905 : stpcpy(tags, ":");
1520 905 : tags_len++;
1521 :
1522 1886 : for (tag = sd_device_get_tag_first(device); tag; tag = sd_device_get_tag_next(device)) {
1523 : char *e;
1524 :
1525 981 : if (!GREEDY_REALLOC(tags, tags_allocated, tags_len + strlen(tag) + 2))
1526 0 : return -ENOMEM;
1527 981 : e = stpcpy(stpcpy(tags + tags_len, tag), ":");
1528 981 : tags_len = e - tags;
1529 : }
1530 :
1531 905 : r = device_add_property_internal(device, "TAGS", tags);
1532 905 : if (r < 0)
1533 0 : return r;
1534 :
1535 905 : device->property_tags_outdated = false;
1536 : }
1537 :
1538 14563 : return 0;
1539 : }
1540 :
1541 751 : _public_ const char *sd_device_get_property_first(sd_device *device, const char **_value) {
1542 : const char *key;
1543 : int r;
1544 :
1545 751 : assert_return(device, NULL);
1546 :
1547 751 : r = device_properties_prepare(device);
1548 751 : if (r < 0)
1549 0 : return NULL;
1550 :
1551 751 : device->properties_iterator_generation = device->properties_generation;
1552 751 : device->properties_iterator = ITERATOR_FIRST;
1553 :
1554 751 : (void) ordered_hashmap_iterate(device->properties, &device->properties_iterator, (void**)_value, (const void**)&key);
1555 751 : return key;
1556 : }
1557 :
1558 4714 : _public_ const char *sd_device_get_property_next(sd_device *device, const char **_value) {
1559 : const char *key;
1560 : int r;
1561 :
1562 4714 : assert_return(device, NULL);
1563 :
1564 4714 : r = device_properties_prepare(device);
1565 4714 : if (r < 0)
1566 0 : return NULL;
1567 :
1568 4714 : if (device->properties_iterator_generation != device->properties_generation)
1569 0 : return NULL;
1570 :
1571 4714 : (void) ordered_hashmap_iterate(device->properties, &device->properties_iterator, (void**)_value, (const void**)&key);
1572 4714 : return key;
1573 : }
1574 :
1575 0 : static int device_sysattrs_read_all(sd_device *device) {
1576 0 : _cleanup_closedir_ DIR *dir = NULL;
1577 : const char *syspath;
1578 : struct dirent *dent;
1579 : int r;
1580 :
1581 0 : assert(device);
1582 :
1583 0 : if (device->sysattrs_read)
1584 0 : return 0;
1585 :
1586 0 : r = sd_device_get_syspath(device, &syspath);
1587 0 : if (r < 0)
1588 0 : return r;
1589 :
1590 0 : dir = opendir(syspath);
1591 0 : if (!dir)
1592 0 : return -errno;
1593 :
1594 0 : r = set_ensure_allocated(&device->sysattrs, &string_hash_ops);
1595 0 : if (r < 0)
1596 0 : return r;
1597 :
1598 0 : FOREACH_DIRENT_ALL(dent, dir, return -errno) {
1599 0 : _cleanup_free_ char *path = NULL;
1600 : struct stat statbuf;
1601 :
1602 : /* only handle symlinks and regular files */
1603 0 : if (!IN_SET(dent->d_type, DT_LNK, DT_REG))
1604 0 : continue;
1605 :
1606 0 : path = path_join(syspath, dent->d_name);
1607 0 : if (!path)
1608 0 : return -ENOMEM;
1609 :
1610 0 : if (lstat(path, &statbuf) != 0)
1611 0 : continue;
1612 :
1613 0 : if (!(statbuf.st_mode & S_IRUSR))
1614 0 : continue;
1615 :
1616 0 : r = set_put_strdup(device->sysattrs, dent->d_name);
1617 0 : if (r < 0)
1618 0 : return r;
1619 : }
1620 :
1621 0 : device->sysattrs_read = true;
1622 :
1623 0 : return 0;
1624 : }
1625 :
1626 0 : _public_ const char *sd_device_get_sysattr_first(sd_device *device) {
1627 : void *v;
1628 : int r;
1629 :
1630 0 : assert_return(device, NULL);
1631 :
1632 0 : if (!device->sysattrs_read) {
1633 0 : r = device_sysattrs_read_all(device);
1634 0 : if (r < 0) {
1635 0 : errno = -r;
1636 0 : return NULL;
1637 : }
1638 : }
1639 :
1640 0 : device->sysattrs_iterator = ITERATOR_FIRST;
1641 :
1642 0 : (void) set_iterate(device->sysattrs, &device->sysattrs_iterator, &v);
1643 0 : return v;
1644 : }
1645 :
1646 0 : _public_ const char *sd_device_get_sysattr_next(sd_device *device) {
1647 : void *v;
1648 :
1649 0 : assert_return(device, NULL);
1650 :
1651 0 : if (!device->sysattrs_read)
1652 0 : return NULL;
1653 :
1654 0 : (void) set_iterate(device->sysattrs, &device->sysattrs_iterator, &v);
1655 0 : return v;
1656 : }
1657 :
1658 0 : _public_ int sd_device_has_tag(sd_device *device, const char *tag) {
1659 0 : assert_return(device, -EINVAL);
1660 0 : assert_return(tag, -EINVAL);
1661 :
1662 0 : (void) device_read_db(device);
1663 :
1664 0 : return !!set_contains(device->tags, tag);
1665 : }
1666 :
1667 9098 : _public_ int sd_device_get_property_value(sd_device *device, const char *key, const char **_value) {
1668 : char *value;
1669 : int r;
1670 :
1671 9098 : assert_return(device, -EINVAL);
1672 9098 : assert_return(key, -EINVAL);
1673 :
1674 9098 : r = device_properties_prepare(device);
1675 9098 : if (r < 0)
1676 0 : return r;
1677 :
1678 9098 : value = ordered_hashmap_get(device->properties, key);
1679 9098 : if (!value)
1680 8406 : return -ENOENT;
1681 :
1682 692 : if (_value)
1683 692 : *_value = value;
1684 692 : return 0;
1685 : }
1686 :
1687 : /* replaces the value if it already exists */
1688 1124 : static int device_add_sysattr_value(sd_device *device, const char *_key, char *value) {
1689 1124 : _cleanup_free_ char *key = NULL;
1690 1124 : _cleanup_free_ char *value_old = NULL;
1691 : int r;
1692 :
1693 1124 : assert(device);
1694 1124 : assert(_key);
1695 :
1696 1124 : r = hashmap_ensure_allocated(&device->sysattr_values, &string_hash_ops);
1697 1124 : if (r < 0)
1698 0 : return r;
1699 :
1700 1124 : value_old = hashmap_remove2(device->sysattr_values, _key, (void **)&key);
1701 1124 : if (!key) {
1702 1124 : key = strdup(_key);
1703 1124 : if (!key)
1704 0 : return -ENOMEM;
1705 : }
1706 :
1707 1124 : r = hashmap_put(device->sysattr_values, key, value);
1708 1124 : if (r < 0)
1709 0 : return r;
1710 1124 : TAKE_PTR(key);
1711 :
1712 1124 : return 0;
1713 : }
1714 :
1715 1128 : static int device_get_sysattr_value(sd_device *device, const char *_key, const char **_value) {
1716 1128 : const char *key = NULL, *value;
1717 :
1718 1128 : assert(device);
1719 1128 : assert(_key);
1720 :
1721 1128 : value = hashmap_get2(device->sysattr_values, _key, (void **) &key);
1722 1128 : if (!key)
1723 1127 : return -ENOENT;
1724 :
1725 1 : if (_value)
1726 1 : *_value = value;
1727 1 : return 0;
1728 : }
1729 :
1730 : /* We cache all sysattr lookups. If an attribute does not exist, it is stored
1731 : * with a NULL value in the cache, otherwise the returned string is stored */
1732 1128 : _public_ int sd_device_get_sysattr_value(sd_device *device, const char *sysattr, const char **_value) {
1733 1128 : _cleanup_free_ char *value = NULL;
1734 1128 : const char *path, *syspath, *cached_value = NULL;
1735 : struct stat statbuf;
1736 : int r;
1737 :
1738 1128 : assert_return(device, -EINVAL);
1739 1128 : assert_return(sysattr, -EINVAL);
1740 :
1741 : /* look for possibly already cached result */
1742 1128 : r = device_get_sysattr_value(device, sysattr, &cached_value);
1743 1128 : if (r != -ENOENT) {
1744 1 : if (r < 0)
1745 0 : return r;
1746 :
1747 1 : if (!cached_value)
1748 : /* we looked up the sysattr before and it did not exist */
1749 0 : return -ENOENT;
1750 :
1751 1 : if (_value)
1752 1 : *_value = cached_value;
1753 :
1754 1 : return 0;
1755 : }
1756 :
1757 1127 : r = sd_device_get_syspath(device, &syspath);
1758 1127 : if (r < 0)
1759 0 : return r;
1760 :
1761 1127 : path = prefix_roota(syspath, sysattr);
1762 1127 : r = lstat(path, &statbuf);
1763 1127 : if (r < 0) {
1764 : /* remember that we could not access the sysattr */
1765 1115 : r = device_add_sysattr_value(device, sysattr, NULL);
1766 1115 : if (r < 0)
1767 0 : return r;
1768 :
1769 1115 : return -ENOENT;
1770 12 : } else if (S_ISLNK(statbuf.st_mode)) {
1771 : /* Some core links return only the last element of the target path,
1772 : * these are just values, the paths should not be exposed. */
1773 0 : if (STR_IN_SET(sysattr, "driver", "subsystem", "module")) {
1774 0 : r = readlink_value(path, &value);
1775 0 : if (r < 0)
1776 0 : return r;
1777 : } else
1778 0 : return -EINVAL;
1779 12 : } else if (S_ISDIR(statbuf.st_mode)) {
1780 : /* skip directories */
1781 0 : return -EINVAL;
1782 12 : } else if (!(statbuf.st_mode & S_IRUSR)) {
1783 : /* skip non-readable files */
1784 0 : return -EPERM;
1785 : } else {
1786 : size_t size;
1787 :
1788 : /* read attribute value */
1789 12 : r = read_full_file(path, &value, &size);
1790 12 : if (r < 0)
1791 3 : return r;
1792 :
1793 : /* drop trailing newlines */
1794 18 : while (size > 0 && value[--size] == '\n')
1795 9 : value[size] = '\0';
1796 : }
1797 :
1798 9 : r = device_add_sysattr_value(device, sysattr, value);
1799 9 : if (r < 0)
1800 0 : return r;
1801 :
1802 9 : *_value = TAKE_PTR(value);
1803 :
1804 9 : return 0;
1805 : }
1806 :
1807 0 : static void device_remove_sysattr_value(sd_device *device, const char *_key) {
1808 0 : _cleanup_free_ char *key = NULL;
1809 :
1810 0 : assert(device);
1811 0 : assert(_key);
1812 :
1813 0 : free(hashmap_remove2(device->sysattr_values, _key, (void **) &key));
1814 0 : }
1815 :
1816 : /* set the attribute and save it in the cache. If a NULL value is passed the
1817 : * attribute is cleared from the cache */
1818 0 : _public_ int sd_device_set_sysattr_value(sd_device *device, const char *sysattr, const char *_value) {
1819 0 : _cleanup_free_ char *value = NULL;
1820 : const char *syspath, *path;
1821 : size_t len;
1822 : int r;
1823 :
1824 0 : assert_return(device, -EINVAL);
1825 0 : assert_return(sysattr, -EINVAL);
1826 :
1827 0 : if (!_value) {
1828 0 : device_remove_sysattr_value(device, sysattr);
1829 0 : return 0;
1830 : }
1831 :
1832 0 : r = sd_device_get_syspath(device, &syspath);
1833 0 : if (r < 0)
1834 0 : return r;
1835 :
1836 0 : path = prefix_roota(syspath, sysattr);
1837 :
1838 0 : len = strlen(_value);
1839 :
1840 : /* drop trailing newlines */
1841 0 : while (len > 0 && _value[len - 1] == '\n')
1842 0 : len --;
1843 :
1844 : /* value length is limited to 4k */
1845 0 : if (len > 4096)
1846 0 : return -EINVAL;
1847 :
1848 0 : value = strndup(_value, len);
1849 0 : if (!value)
1850 0 : return -ENOMEM;
1851 :
1852 0 : r = write_string_file(path, value, WRITE_STRING_FILE_DISABLE_BUFFER | WRITE_STRING_FILE_NOFOLLOW);
1853 0 : if (r < 0) {
1854 0 : if (r == -ELOOP)
1855 0 : return -EINVAL;
1856 0 : if (r == -EISDIR)
1857 0 : return r;
1858 :
1859 0 : r = free_and_strdup(&value, "");
1860 0 : if (r < 0)
1861 0 : return r;
1862 :
1863 0 : r = device_add_sysattr_value(device, sysattr, value);
1864 0 : if (r < 0)
1865 0 : return r;
1866 0 : TAKE_PTR(value);
1867 :
1868 0 : return -ENXIO;
1869 : }
1870 :
1871 0 : r = device_add_sysattr_value(device, sysattr, value);
1872 0 : if (r < 0)
1873 0 : return r;
1874 0 : TAKE_PTR(value);
1875 :
1876 0 : return 0;
1877 : }
|