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