]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/libsystemd/sd-device/sd-device.c
tree-wide: use mdash instead of a two minuses
[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) {
4afd3348 46 _cleanup_(sd_device_unrefp) sd_device *device = NULL;
57fa1d09
TG
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) {
313cefa1 139 device->properties_generation++;
57fa1d09
TG
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) {
4afd3348 225 _cleanup_(sd_device_unrefp) sd_device *device = NULL;
57fa1d09
TG
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
313cefa1 312 len++;
a9ec9f29
KS
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;
97c94b98 497 const char *syspath, *key = NULL, *value = NULL, *major = NULL, *minor = NULL;
57fa1d09
TG
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 {
4afd3348 627 _cleanup_(sd_device_unrefp) sd_device *device = NULL;
57fa1d09
TG
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';
313cefa1 672 sysname++;
57fa1d09
TG
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;
313cefa1 974 pos++;
57fa1d09
TG
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
313cefa1 989 len++;
57fa1d09
TG
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
313cefa1 1069 device->tags_generation++;
57fa1d09
TG
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
313cefa1 1089 device->devlinks_generation++;
57fa1d09
TG
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
ccddd104 1215 /* use dev_t — b259:131072, c254:0 */
57fa1d09
TG
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 1221 } else if (ifindex > 0) {
ccddd104 1222 /* use netdev ifindex — n3 */
57fa1d09
TG
1223 r = asprintf(&id, "n%u", ifindex);
1224 if (r < 0)
53fae771 1225 return -ENOMEM;
57fa1d09 1226 } else {
ccddd104 1227 /* use $subsys:$sysname — pci:0000:00:1f.2
57fa1d09
TG
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
bccfe92e 1400 (void) set_iterate(device->tags, &device->tags_iterator, &v);
8927b1da 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
bccfe92e 1414 (void) set_iterate(device->tags, &device->tags_iterator, &v);
8927b1da 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
bccfe92e 1428 (void) set_iterate(device->devlinks, &device->devlinks_iterator, &v);
8927b1da 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
bccfe92e 1442 (void) set_iterate(device->devlinks, &device->devlinks_iterator, &v);
8927b1da 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) {
1d88a271
MP
1460 _cleanup_free_ char *devlinks = NULL;
1461 size_t devlinks_allocated = 0, devlinks_len = 0;
57fa1d09
TG
1462 const char *devlink;
1463
1d88a271
MP
1464 for (devlink = sd_device_get_devlink_first(device); devlink; devlink = sd_device_get_devlink_next(device)) {
1465 char *e;
57fa1d09 1466
1d88a271
MP
1467 if (!GREEDY_REALLOC(devlinks, devlinks_allocated, devlinks_len + strlen(devlink) + 2))
1468 return -ENOMEM;
1469 if (devlinks_len > 0)
1470 stpcpy(devlinks + devlinks_len++, " ");
1471 e = stpcpy(devlinks + devlinks_len, devlink);
1472 devlinks_len = e - devlinks;
1473 }
57fa1d09
TG
1474
1475 r = device_add_property_internal(device, "DEVLINKS", devlinks);
1476 if (r < 0)
1477 return r;
1478
1479 device->property_devlinks_outdated = false;
1480 }
1481
1482 if (device->property_tags_outdated) {
1d88a271
MP
1483 _cleanup_free_ char *tags = NULL;
1484 size_t tags_allocated = 0, tags_len = 0;
57fa1d09
TG
1485 const char *tag;
1486
1d88a271
MP
1487 if (!GREEDY_REALLOC(tags, tags_allocated, 2))
1488 return -ENOMEM;
1489 stpcpy(tags, ":");
1490 tags_len++;
57fa1d09 1491
1d88a271
MP
1492 for (tag = sd_device_get_tag_first(device); tag; tag = sd_device_get_tag_next(device)) {
1493 char *e;
57fa1d09 1494
817ec8cc 1495 if (!GREEDY_REALLOC(tags, tags_allocated, tags_len + strlen(tag) + 2))
1d88a271
MP
1496 return -ENOMEM;
1497 e = stpcpy(stpcpy(tags + tags_len, tag), ":");
1498 tags_len = e - tags;
1499 }
57fa1d09
TG
1500
1501 r = device_add_property_internal(device, "TAGS", tags);
1502 if (r < 0)
1503 return r;
1504
1505 device->property_tags_outdated = false;
1506 }
1507
1508 return 0;
1509}
1510
1511_public_ const char *sd_device_get_property_first(sd_device *device, const char **_value) {
1512 const char *key;
1513 const char *value;
1514 int r;
1515
1516 assert_return(device, NULL);
1517
1518 r = device_properties_prepare(device);
1519 if (r < 0)
1520 return NULL;
1521
1522 device->properties_iterator_generation = device->properties_generation;
1523 device->properties_iterator = ITERATOR_FIRST;
1524
8927b1da 1525 ordered_hashmap_iterate(device->properties, &device->properties_iterator, (void**)&value, (const void**)&key);
57fa1d09
TG
1526
1527 if (_value)
1528 *_value = value;
1529
1530 return key;
1531}
1532
1533_public_ const char *sd_device_get_property_next(sd_device *device, const char **_value) {
1534 const char *key;
1535 const char *value;
1536 int r;
1537
1538 assert_return(device, NULL);
1539
1540 r = device_properties_prepare(device);
1541 if (r < 0)
1542 return NULL;
1543
1544 if (device->properties_iterator_generation != device->properties_generation)
1545 return NULL;
1546
8927b1da 1547 ordered_hashmap_iterate(device->properties, &device->properties_iterator, (void**)&value, (const void**)&key);
57fa1d09
TG
1548
1549 if (_value)
1550 *_value = value;
1551
1552 return key;
1553}
1554
1555static int device_sysattrs_read_all(sd_device *device) {
1556 _cleanup_closedir_ DIR *dir = NULL;
1557 const char *syspath;
1558 struct dirent *dent;
1559 int r;
1560
1561 assert(device);
1562
1563 if (device->sysattrs_read)
1564 return 0;
1565
1566 r = sd_device_get_syspath(device, &syspath);
1567 if (r < 0)
1568 return r;
1569
1570 dir = opendir(syspath);
1571 if (!dir)
1572 return -errno;
1573
1574 r = set_ensure_allocated(&device->sysattrs, &string_hash_ops);
1575 if (r < 0)
1576 return r;
1577
1578 for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) {
1579 char *path;
1580 struct stat statbuf;
1581
1582 /* only handle symlinks and regular files */
1583 if (dent->d_type != DT_LNK && dent->d_type != DT_REG)
1584 continue;
1585
1586 path = strjoina(syspath, "/", dent->d_name);
1587
1588 if (lstat(path, &statbuf) != 0)
1589 continue;
1590
1591 if (!(statbuf.st_mode & S_IRUSR))
1592 continue;
1593
1594 r = set_put_strdup(device->sysattrs, dent->d_name);
1595 if (r < 0)
1596 return r;
1597 }
1598
1599 device->sysattrs_read = true;
1600
1601 return 0;
1602}
1603
1604_public_ const char *sd_device_get_sysattr_first(sd_device *device) {
8927b1da 1605 void *v;
57fa1d09
TG
1606 int r;
1607
1608 assert_return(device, NULL);
1609
1610 if (!device->sysattrs_read) {
1611 r = device_sysattrs_read_all(device);
1612 if (r < 0) {
1613 errno = -r;
1614 return NULL;
1615 }
1616 }
1617
1618 device->sysattrs_iterator = ITERATOR_FIRST;
1619
bccfe92e 1620 (void) set_iterate(device->sysattrs, &device->sysattrs_iterator, &v);
8927b1da 1621 return v;
57fa1d09
TG
1622}
1623
1624_public_ const char *sd_device_get_sysattr_next(sd_device *device) {
8927b1da
DH
1625 void *v;
1626
57fa1d09
TG
1627 assert_return(device, NULL);
1628
1629 if (!device->sysattrs_read)
1630 return NULL;
1631
bccfe92e 1632 (void) set_iterate(device->sysattrs, &device->sysattrs_iterator, &v);
8927b1da 1633 return v;
57fa1d09
TG
1634}
1635
1636_public_ int sd_device_has_tag(sd_device *device, const char *tag) {
1637 assert_return(device, -EINVAL);
1638 assert_return(tag, -EINVAL);
1639
1640 (void) device_read_db(device);
1641
1642 return !!set_contains(device->tags, tag);
1643}
1644
1645_public_ int sd_device_get_property_value(sd_device *device, const char *key, const char **_value) {
1646 char *value;
1647 int r;
1648
1649 assert_return(device, -EINVAL);
1650 assert_return(key, -EINVAL);
1651 assert_return(_value, -EINVAL);
1652
1653 r = device_properties_prepare(device);
1654 if (r < 0)
1655 return r;
1656
1657 value = ordered_hashmap_get(device->properties, key);
1658 if (!value)
1659 return -ENOENT;
1660
1661 *_value = value;
1662
1663 return 0;
1664}
1665
1666/* replaces the value if it already exists */
2fe29a46 1667static int device_add_sysattr_value(sd_device *device, const char *_key, char *value) {
57fa1d09 1668 _cleanup_free_ char *key = NULL;
2fe29a46 1669 _cleanup_free_ char *value_old = NULL;
57fa1d09
TG
1670 int r;
1671
1672 assert(device);
1673 assert(_key);
1674
1675 r = hashmap_ensure_allocated(&device->sysattr_values, &string_hash_ops);
1676 if (r < 0)
1677 return r;
1678
2fe29a46 1679 value_old = hashmap_remove2(device->sysattr_values, _key, (void **)&key);
57fa1d09
TG
1680 if (!key) {
1681 key = strdup(_key);
1682 if (!key)
1683 return -ENOMEM;
1684 }
1685
57fa1d09
TG
1686 r = hashmap_put(device->sysattr_values, key, value);
1687 if (r < 0)
1688 return r;
1689
1690 key = NULL;
57fa1d09
TG
1691
1692 return 0;
1693}
1694
1695static int device_get_sysattr_value(sd_device *device, const char *_key, const char **_value) {
1696 const char *key = NULL, *value;
1697
1698 assert(device);
1699 assert(_key);
1700
1701 value = hashmap_get2(device->sysattr_values, _key, (void **) &key);
1702 if (!key)
1703 return -ENOENT;
1704
1705 if (_value)
1706 *_value = value;
1707
1708 return 0;
1709}
1710
1711/* We cache all sysattr lookups. If an attribute does not exist, it is stored
1712 * with a NULL value in the cache, otherwise the returned string is stored */
1713_public_ int sd_device_get_sysattr_value(sd_device *device, const char *sysattr, const char **_value) {
1714 _cleanup_free_ char *value = NULL;
1715 const char *syspath, *cached_value = NULL;
1716 char *path;
1717 struct stat statbuf;
1718 int r;
1719
1720 assert_return(device, -EINVAL);
1721 assert_return(sysattr, -EINVAL);
1722
1723 /* look for possibly already cached result */
1724 r = device_get_sysattr_value(device, sysattr, &cached_value);
1725 if (r != -ENOENT) {
1726 if (r < 0)
1727 return r;
1728
1729 if (!cached_value)
1730 /* we looked up the sysattr before and it did not exist */
1731 return -ENOENT;
1732
1733 if (_value)
1734 *_value = cached_value;
1735
1736 return 0;
1737 }
1738
1739 r = sd_device_get_syspath(device, &syspath);
1740 if (r < 0)
1741 return r;
1742
1743 path = strjoina(syspath, "/", sysattr);
1744 r = lstat(path, &statbuf);
1745 if (r < 0) {
1746 /* remember that we could not access the sysattr */
1747 r = device_add_sysattr_value(device, sysattr, NULL);
1748 if (r < 0)
1749 return r;
1750
1751 return -ENOENT;
1752 } else if (S_ISLNK(statbuf.st_mode)) {
1753 /* Some core links return only the last element of the target path,
1754 * these are just values, the paths should not be exposed. */
1755 if (STR_IN_SET(sysattr, "driver", "subsystem", "module")) {
1756 r = readlink_value(path, &value);
1757 if (r < 0)
1758 return r;
1759 } else
1760 return -EINVAL;
1761 } else if (S_ISDIR(statbuf.st_mode)) {
1762 /* skip directories */
1763 return -EINVAL;
1764 } else if (!(statbuf.st_mode & S_IRUSR)) {
1765 /* skip non-readable files */
1766 return -EPERM;
1767 } else {
1768 size_t size;
1769
1770 /* read attribute value */
1771 r = read_full_file(path, &value, &size);
1772 if (r < 0)
1773 return r;
1774
1775 /* drop trailing newlines */
1776 while (size > 0 && value[--size] == '\n')
1777 value[size] = '\0';
1778 }
1779
1780 r = device_add_sysattr_value(device, sysattr, value);
1781 if (r < 0)
1782 return r;
1783
1784 *_value = value;
1785 value = NULL;
1786
1787 return 0;
1788}
1789
1790static void device_remove_sysattr_value(sd_device *device, const char *_key) {
1791 _cleanup_free_ char *key = NULL;
1792 _cleanup_free_ char *value = NULL;
1793
1794 assert(device);
1795 assert(_key);
1796
1797 value = hashmap_remove2(device->sysattr_values, _key, (void **) &key);
1798
1799 return;
1800}
1801
1802/* set the attribute and save it in the cache. If a NULL value is passed the
1803 * attribute is cleared from the cache */
2fe29a46 1804_public_ int sd_device_set_sysattr_value(sd_device *device, const char *sysattr, char *_value) {
57fa1d09 1805 _cleanup_close_ int fd = -1;
2fe29a46 1806 _cleanup_free_ char *value = NULL;
57fa1d09
TG
1807 const char *syspath;
1808 char *path;
1809 struct stat statbuf;
1810 size_t value_len = 0;
1811 ssize_t size;
1812 int r;
1813
1814 assert_return(device, -EINVAL);
1815 assert_return(sysattr, -EINVAL);
1816
2fe29a46 1817 if (!_value) {
57fa1d09
TG
1818 device_remove_sysattr_value(device, sysattr);
1819
1820 return 0;
1821 }
1822
1823 r = sd_device_get_syspath(device, &syspath);
1824 if (r < 0)
1825 return r;
1826
1827 path = strjoina(syspath, "/", sysattr);
1828 r = lstat(path, &statbuf);
1829 if (r < 0) {
2fe29a46
TG
1830 value = strdup("");
1831 if (!value)
1832 return -ENOMEM;
1833
1834 r = device_add_sysattr_value(device, sysattr, value);
57fa1d09
TG
1835 if (r < 0)
1836 return r;
1837
1838 return -ENXIO;
1839 }
1840
1841 if (S_ISLNK(statbuf.st_mode))
1842 return -EINVAL;
1843
1844 /* skip directories */
1845 if (S_ISDIR(statbuf.st_mode))
1846 return -EISDIR;
1847
1848 /* skip non-readable files */
1849 if ((statbuf.st_mode & S_IRUSR) == 0)
1850 return -EACCES;
1851
2fe29a46 1852 value_len = strlen(_value);
57fa1d09
TG
1853
1854 /* drop trailing newlines */
24eb4a30
DH
1855 while (value_len > 0 && _value[value_len - 1] == '\n')
1856 _value[--value_len] = '\0';
57fa1d09
TG
1857
1858 /* value length is limited to 4k */
1859 if (value_len > 4096)
1860 return -EINVAL;
1861
1862 fd = open(path, O_WRONLY | O_CLOEXEC);
1863 if (fd < 0)
1864 return -errno;
1865
2fe29a46
TG
1866 value = strdup(_value);
1867 if (!value)
1868 return -ENOMEM;
1869
57fa1d09
TG
1870 size = write(fd, value, value_len);
1871 if (size < 0)
1872 return -errno;
1873
1874 if ((size_t)size != value_len)
1875 return -EIO;
1876
1877 r = device_add_sysattr_value(device, sysattr, value);
1878 if (r < 0)
1879 return r;
1880
2fe29a46
TG
1881 value = NULL;
1882
57fa1d09
TG
1883 return 0;
1884}