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