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