]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/libsystemd/sd-device/sd-device.c
Revert "rules: allow users to access frame buffer devices" (#3333)
[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
57fa1d09
TG
564 case VALUE:
565 if (strchr(NEWLINE, uevent[i])) {
566 uevent[i] = '\0';
567
568 r = handle_uevent_line(device, key, value, &major, &minor);
569 if (r < 0)
19c29853 570 log_debug_errno(r, "sd-device: failed to handle uevent entry '%s=%s': %m", key, value);
57fa1d09
TG
571
572 state = PRE_KEY;
573 }
574
575 break;
576 default:
577 assert_not_reached("invalid state when parsing uevent file");
578 }
579 }
580
581 if (major) {
582 r = device_set_devnum(device, major, minor);
583 if (r < 0)
23446f01 584 log_debug_errno(r, "sd-device: could not set 'MAJOR=%s' or 'MINOR=%s' from '%s': %m", major, minor, path);
57fa1d09
TG
585 }
586
57fa1d09
TG
587 return 0;
588}
589
590_public_ int sd_device_get_ifindex(sd_device *device, int *ifindex) {
591 int r;
592
593 assert_return(device, -EINVAL);
594 assert_return(ifindex, -EINVAL);
595
596 r = device_read_uevent_file(device);
597 if (r < 0)
598 return r;
599
600 *ifindex = device->ifindex;
601
602 return 0;
603}
604
605_public_ int sd_device_new_from_device_id(sd_device **ret, const char *id) {
606 int r;
607
608 assert_return(ret, -EINVAL);
609 assert_return(id, -EINVAL);
610
611 switch (id[0]) {
612 case 'b':
613 case 'c':
614 {
615 char type;
616 int maj, min;
617
618 r = sscanf(id, "%c%i:%i", &type, &maj, &min);
619 if (r != 3)
620 return -EINVAL;
621
622 return sd_device_new_from_devnum(ret, type, makedev(maj, min));
623 }
624 case 'n':
625 {
4afd3348 626 _cleanup_(sd_device_unrefp) sd_device *device = NULL;
57fa1d09
TG
627 _cleanup_close_ int sk = -1;
628 struct ifreq ifr = {};
629 int ifindex;
630
6ad623a3 631 r = parse_ifindex(&id[1], &ifr.ifr_ifindex);
57fa1d09
TG
632 if (r < 0)
633 return r;
57fa1d09
TG
634
635 sk = socket(PF_INET, SOCK_DGRAM, 0);
636 if (sk < 0)
637 return -errno;
638
639 r = ioctl(sk, SIOCGIFNAME, &ifr);
640 if (r < 0)
641 return -errno;
642
643 r = sd_device_new_from_subsystem_sysname(&device, "net", ifr.ifr_name);
644 if (r < 0)
645 return r;
646
647 r = sd_device_get_ifindex(device, &ifindex);
648 if (r < 0)
649 return r;
650
08232a02 651 /* this is racey, so we might end up with the wrong device */
57fa1d09
TG
652 if (ifr.ifr_ifindex != ifindex)
653 return -ENODEV;
654
655 *ret = device;
656 device = NULL;
657
658 return 0;
659 }
660 case '+':
661 {
662 char subsys[PATH_MAX];
663 char *sysname;
664
665 (void)strscpy(subsys, sizeof(subsys), id + 1);
666 sysname = strchr(subsys, ':');
667 if (!sysname)
668 return -EINVAL;
669
670 sysname[0] = '\0';
313cefa1 671 sysname++;
57fa1d09
TG
672
673 return sd_device_new_from_subsystem_sysname(ret, subsys, sysname);
674 }
675 default:
676 return -EINVAL;
677 }
678}
679
680_public_ int sd_device_get_syspath(sd_device *device, const char **ret) {
681 assert_return(device, -EINVAL);
682 assert_return(ret, -EINVAL);
683
684 assert(path_startswith(device->syspath, "/sys/"));
685
686 *ret = device->syspath;
687
688 return 0;
689}
690
691static int device_new_from_child(sd_device **ret, sd_device *child) {
692 _cleanup_free_ char *path = NULL;
693 const char *subdir, *syspath;
694 int r;
695
696 assert(ret);
697 assert(child);
698
699 r = sd_device_get_syspath(child, &syspath);
700 if (r < 0)
701 return r;
702
703 path = strdup(syspath);
704 if (!path)
705 return -ENOMEM;
706 subdir = path + strlen("/sys");
707
708 for (;;) {
709 char *pos;
710
711 pos = strrchr(subdir, '/');
712 if (!pos || pos < subdir + 2)
713 break;
714
715 *pos = '\0';
716
717 r = sd_device_new_from_syspath(ret, path);
718 if (r < 0)
719 continue;
720
721 return 0;
722 }
723
08232a02 724 return -ENODEV;
57fa1d09
TG
725}
726
727_public_ int sd_device_get_parent(sd_device *child, sd_device **ret) {
728
729 assert_return(ret, -EINVAL);
730 assert_return(child, -EINVAL);
731
732 if (!child->parent_set) {
733 child->parent_set = true;
734
735 (void)device_new_from_child(&child->parent, child);
736 }
737
738 if (!child->parent)
739 return -ENOENT;
740
741 *ret = child->parent;
742
743 return 0;
744}
745
746int device_set_subsystem(sd_device *device, const char *_subsystem) {
747 _cleanup_free_ char *subsystem = NULL;
748 int r;
749
750 assert(device);
751 assert(_subsystem);
752
753 subsystem = strdup(_subsystem);
754 if (!subsystem)
755 return -ENOMEM;
756
757 r = device_add_property_internal(device, "SUBSYSTEM", subsystem);
758 if (r < 0)
759 return r;
760
761 free(device->subsystem);
762 device->subsystem = subsystem;
763 subsystem = NULL;
764
765 device->subsystem_set = true;
766
767 return 0;
768}
769
770_public_ int sd_device_get_subsystem(sd_device *device, const char **ret) {
771 assert_return(ret, -EINVAL);
772 assert_return(device, -EINVAL);
773
774 if (!device->subsystem_set) {
775 _cleanup_free_ char *subsystem = NULL;
776 const char *syspath;
777 char *path;
778 int r;
779
780 /* read 'subsystem' link */
781 r = sd_device_get_syspath(device, &syspath);
782 if (r < 0)
783 return r;
784
785 path = strjoina(syspath, "/subsystem");
786 r = readlink_value(path, &subsystem);
787 if (r >= 0)
788 r = device_set_subsystem(device, subsystem);
789 /* use implicit names */
790 else if (path_startswith(device->devpath, "/module/"))
791 r = device_set_subsystem(device, "module");
792 else if (strstr(device->devpath, "/drivers/"))
793 r = device_set_subsystem(device, "drivers");
794 else if (path_startswith(device->devpath, "/subsystem/") ||
795 path_startswith(device->devpath, "/class/") ||
85091685 796 path_startswith(device->devpath, "/bus/"))
57fa1d09 797 r = device_set_subsystem(device, "subsystem");
4189708a 798 if (r < 0 && r != -ENOENT)
5a917c06 799 return log_debug_errno(r, "sd-device: could not set subsystem for %s: %m", device->devpath);
57fa1d09
TG
800
801 device->subsystem_set = true;
802 }
803
bf4c113e
DH
804 if (!device->subsystem)
805 return -ENOENT;
806
57fa1d09
TG
807 *ret = device->subsystem;
808
809 return 0;
810}
811
812_public_ int sd_device_get_devtype(sd_device *device, const char **devtype) {
813 int r;
814
815 assert(devtype);
816 assert(device);
817
818 r = device_read_uevent_file(device);
819 if (r < 0)
820 return r;
821
822 *devtype = device->devtype;
823
824 return 0;
825}
826
827_public_ int sd_device_get_parent_with_subsystem_devtype(sd_device *child, const char *subsystem, const char *devtype, sd_device **ret) {
828 sd_device *parent = NULL;
829 int r;
830
831 assert_return(child, -EINVAL);
832 assert_return(subsystem, -EINVAL);
833
834 r = sd_device_get_parent(child, &parent);
835 while (r >= 0) {
836 const char *parent_subsystem = NULL;
837 const char *parent_devtype = NULL;
838
839 (void)sd_device_get_subsystem(parent, &parent_subsystem);
840 if (streq_ptr(parent_subsystem, subsystem)) {
841 if (!devtype)
842 break;
843
844 (void)sd_device_get_devtype(parent, &parent_devtype);
845 if (streq_ptr(parent_devtype, devtype))
846 break;
847 }
848 r = sd_device_get_parent(parent, &parent);
849 }
850
851 if (r < 0)
852 return r;
853
854 *ret = parent;
855
856 return 0;
857}
858
859_public_ int sd_device_get_devnum(sd_device *device, dev_t *devnum) {
860 int r;
861
862 assert_return(device, -EINVAL);
863 assert_return(devnum, -EINVAL);
864
865 r = device_read_uevent_file(device);
866 if (r < 0)
867 return r;
868
869 *devnum = device->devnum;
870
871 return 0;
872}
873
874int device_set_driver(sd_device *device, const char *_driver) {
875 _cleanup_free_ char *driver = NULL;
876 int r;
877
878 assert(device);
879 assert(_driver);
880
881 driver = strdup(_driver);
882 if (!driver)
883 return -ENOMEM;
884
885 r = device_add_property_internal(device, "DRIVER", driver);
886 if (r < 0)
887 return r;
888
889 free(device->driver);
890 device->driver = driver;
891 driver = NULL;
892
893 device->driver_set = true;
894
895 return 0;
896}
897
898_public_ int sd_device_get_driver(sd_device *device, const char **ret) {
899 assert_return(device, -EINVAL);
900 assert_return(ret, -EINVAL);
901
902 if (!device->driver_set) {
903 _cleanup_free_ char *driver = NULL;
904 const char *syspath;
905 char *path;
906 int r;
907
908 r = sd_device_get_syspath(device, &syspath);
909 if (r < 0)
910 return r;
911
912 path = strjoina(syspath, "/driver");
913 r = readlink_value(path, &driver);
914 if (r >= 0) {
915 r = device_set_driver(device, driver);
916 if (r < 0)
7283a80d
TG
917 return log_debug_errno(r, "sd-device: could not set driver for %s: %m", device->devpath);
918 } else if (r == -ENOENT)
919 device->driver_set = true;
920 else
921 return log_debug_errno(r, "sd-device: could not set driver for %s: %m", device->devpath);
57fa1d09
TG
922 }
923
bf4c113e
DH
924 if (!device->driver)
925 return -ENOENT;
926
57fa1d09
TG
927 *ret = device->driver;
928
929 return 0;
930}
931
932_public_ int sd_device_get_devpath(sd_device *device, const char **devpath) {
933 assert_return(device, -EINVAL);
934 assert_return(devpath, -EINVAL);
935
936 assert(device->devpath);
937 assert(device->devpath[0] == '/');
938
939 *devpath = device->devpath;
940
941 return 0;
942}
943
944_public_ int sd_device_get_devname(sd_device *device, const char **devname) {
945 int r;
946
947 assert_return(device, -EINVAL);
948 assert_return(devname, -EINVAL);
949
950 r = device_read_uevent_file(device);
951 if (r < 0)
952 return r;
953
954 if (!device->devname)
955 return -ENOENT;
956
957 assert(path_startswith(device->devname, "/dev/"));
958
959 *devname = device->devname;
960
961 return 0;
962}
963
964static int device_set_sysname(sd_device *device) {
965 _cleanup_free_ char *sysname = NULL;
afcac065 966 const char *sysnum = NULL;
57fa1d09
TG
967 const char *pos;
968 size_t len = 0;
969
970 pos = strrchr(device->devpath, '/');
971 if (!pos)
972 return -EINVAL;
313cefa1 973 pos++;
57fa1d09
TG
974
975 /* devpath is not a root directory */
976 if (*pos == '\0' || pos <= device->devpath)
977 return -EINVAL;
978
979 sysname = strdup(pos);
980 if (!sysname)
981 return -ENOMEM;
982
983 /* some devices have '!' in their name, change that to '/' */
984 while (sysname[len] != '\0') {
985 if (sysname[len] == '!')
986 sysname[len] = '/';
987
313cefa1 988 len++;
57fa1d09
TG
989 }
990
991 /* trailing number */
992 while (len > 0 && isdigit(sysname[--len]))
993 sysnum = &sysname[len];
994
995 if (len == 0)
996 sysnum = NULL;
997
998 free(device->sysname);
999 device->sysname = sysname;
1000 sysname = NULL;
1001
1002 device->sysnum = sysnum;
1003
1004 device->sysname_set = true;
1005
1006 return 0;
1007}
1008
1009_public_ int sd_device_get_sysname(sd_device *device, const char **ret) {
1010 int r;
1011
1012 assert_return(device, -EINVAL);
1013 assert_return(ret, -EINVAL);
1014
1015 if (!device->sysname_set) {
1016 r = device_set_sysname(device);
1017 if (r < 0)
1018 return r;
1019 }
1020
bf4c113e
DH
1021 assert_return(device->sysname, -ENOENT);
1022
57fa1d09
TG
1023 *ret = device->sysname;
1024
1025 return 0;
1026}
1027
1028_public_ int sd_device_get_sysnum(sd_device *device, const char **ret) {
1029 int r;
1030
1031 assert_return(device, -EINVAL);
1032 assert_return(ret, -EINVAL);
1033
1034 if (!device->sysname_set) {
1035 r = device_set_sysname(device);
1036 if (r < 0)
1037 return r;
1038 }
1039
1040 *ret = device->sysnum;
1041
1042 return 0;
1043}
1044
1045static bool is_valid_tag(const char *tag) {
1046 assert(tag);
1047
1048 return !strchr(tag, ':') && !strchr(tag, ' ');
1049}
1050
1051int device_add_tag(sd_device *device, const char *tag) {
1052 int r;
1053
1054 assert(device);
1055 assert(tag);
1056
1057 if (!is_valid_tag(tag))
1058 return -EINVAL;
1059
1060 r = set_ensure_allocated(&device->tags, &string_hash_ops);
1061 if (r < 0)
1062 return r;
1063
1064 r = set_put_strdup(device->tags, tag);
1065 if (r < 0)
1066 return r;
1067
313cefa1 1068 device->tags_generation++;
57fa1d09
TG
1069 device->property_tags_outdated = true;
1070
1071 return 0;
1072}
1073
1074int device_add_devlink(sd_device *device, const char *devlink) {
1075 int r;
1076
1077 assert(device);
1078 assert(devlink);
1079
1080 r = set_ensure_allocated(&device->devlinks, &string_hash_ops);
1081 if (r < 0)
1082 return r;
1083
1084 r = set_put_strdup(device->devlinks, devlink);
1085 if (r < 0)
1086 return r;
1087
313cefa1 1088 device->devlinks_generation++;
57fa1d09
TG
1089 device->property_devlinks_outdated = true;
1090
1091 return 0;
1092}
1093
1094static int device_add_property_internal_from_string(sd_device *device, const char *str) {
1095 _cleanup_free_ char *key = NULL;
1096 char *value;
1097
1098 assert(device);
1099 assert(str);
1100
1101 key = strdup(str);
1102 if (!key)
1103 return -ENOMEM;
1104
1105 value = strchr(key, '=');
1106 if (!value)
1107 return -EINVAL;
1108
1109 *value = '\0';
1110
1111 if (isempty(++value))
1112 value = NULL;
1113
1114 return device_add_property_internal(device, key, value);
1115}
1116
1117int device_set_usec_initialized(sd_device *device, const char *initialized) {
1118 uint64_t usec_initialized;
1119 int r;
1120
1121 assert(device);
1122 assert(initialized);
1123
1124 r = safe_atou64(initialized, &usec_initialized);
1125 if (r < 0)
1126 return r;
1127
1128 r = device_add_property_internal(device, "USEC_INITIALIZED", initialized);
1129 if (r < 0)
1130 return r;
1131
1132 device->usec_initialized = usec_initialized;
1133
1134 return 0;
1135}
1136
1137static int handle_db_line(sd_device *device, char key, const char *value) {
1138 char *path;
1139 int r;
1140
1141 assert(device);
1142 assert(value);
1143
1144 switch (key) {
1145 case 'G':
1146 r = device_add_tag(device, value);
1147 if (r < 0)
1148 return r;
1149
1150 break;
1151 case 'S':
1152 path = strjoina("/dev/", value);
1153 r = device_add_devlink(device, path);
1154 if (r < 0)
1155 return r;
1156
1157 break;
1158 case 'E':
1159 r = device_add_property_internal_from_string(device, value);
1160 if (r < 0)
1161 return r;
1162
1163 break;
1164 case 'I':
1165 r = device_set_usec_initialized(device, value);
1166 if (r < 0)
1167 return r;
1168
1169 break;
1170 case 'L':
1171 r = safe_atoi(value, &device->devlink_priority);
1172 if (r < 0)
1173 return r;
1174
1175 break;
1176 case 'W':
1177 r = safe_atoi(value, &device->watch_handle);
1178 if (r < 0)
1179 return r;
1180
1181 break;
1182 default:
1183 log_debug("device db: unknown key '%c'", key);
1184 }
1185
1186 return 0;
1187}
1188
1189int device_get_id_filename(sd_device *device, const char **ret) {
1190 assert(device);
1191 assert(ret);
1192
1193 if (!device->id_filename) {
1194 _cleanup_free_ char *id = NULL;
1195 const char *subsystem;
1196 dev_t devnum;
1197 int ifindex, r;
1198
1199 r = sd_device_get_subsystem(device, &subsystem);
1200 if (r < 0)
1201 return r;
1202
1203 r = sd_device_get_devnum(device, &devnum);
1204 if (r < 0)
1205 return r;
1206
1207 r = sd_device_get_ifindex(device, &ifindex);
1208 if (r < 0)
1209 return r;
1210
1211 if (major(devnum) > 0) {
4189708a
TG
1212 assert(subsystem);
1213
ccddd104 1214 /* use dev_t — b259:131072, c254:0 */
57fa1d09
TG
1215 r = asprintf(&id, "%c%u:%u",
1216 streq(subsystem, "block") ? 'b' : 'c',
1217 major(devnum), minor(devnum));
1218 if (r < 0)
53fae771 1219 return -ENOMEM;
57fa1d09 1220 } else if (ifindex > 0) {
ccddd104 1221 /* use netdev ifindex — n3 */
57fa1d09
TG
1222 r = asprintf(&id, "n%u", ifindex);
1223 if (r < 0)
53fae771 1224 return -ENOMEM;
57fa1d09 1225 } else {
ccddd104 1226 /* use $subsys:$sysname — pci:0000:00:1f.2
57fa1d09
TG
1227 * sysname() has '!' translated, get it from devpath
1228 */
1229 const char *sysname;
1230
1231 sysname = basename(device->devpath);
1232 if (!sysname)
4189708a
TG
1233 return -EINVAL;
1234
1235 if (!subsystem)
57fa1d09
TG
1236 return -EINVAL;
1237
1238 r = asprintf(&id, "+%s:%s", subsystem, sysname);
1239 if (r < 0)
53fae771 1240 return -ENOMEM;
57fa1d09
TG
1241 }
1242
1243 device->id_filename = id;
1244 id = NULL;
1245 }
1246
1247 *ret = device->id_filename;
1248
1249 return 0;
1250}
1251
107f2e25 1252int device_read_db_aux(sd_device *device, bool force) {
57fa1d09
TG
1253 _cleanup_free_ char *db = NULL;
1254 char *path;
1255 const char *id, *value;
1256 char key;
1257 size_t db_len;
1258 unsigned i;
1259 int r;
1260
1261 enum {
1262 PRE_KEY,
1263 KEY,
1264 PRE_VALUE,
1265 VALUE,
1266 INVALID_LINE,
1267 } state = PRE_KEY;
1268
107f2e25 1269 if (device->db_loaded || (!force && device->sealed))
57fa1d09
TG
1270 return 0;
1271
7141e4f6
TG
1272 device->db_loaded = true;
1273
57fa1d09
TG
1274 r = device_get_id_filename(device, &id);
1275 if (r < 0)
1276 return r;
1277
1278 path = strjoina("/run/udev/data/", id);
1279
1280 r = read_full_file(path, &db, &db_len);
1281 if (r < 0) {
1282 if (r == -ENOENT)
1283 return 0;
e53fc357
LP
1284 else
1285 return log_debug_errno(r, "sd-device: failed to read db '%s': %m", path);
57fa1d09
TG
1286 }
1287
1288 /* devices with a database entry are initialized */
7e518afa 1289 device->is_initialized = true;
57fa1d09
TG
1290
1291 for (i = 0; i < db_len; i++) {
1292 switch (state) {
1293 case PRE_KEY:
1294 if (!strchr(NEWLINE, db[i])) {
1295 key = db[i];
1296
1297 state = KEY;
1298 }
1299
1300 break;
1301 case KEY:
1302 if (db[i] != ':') {
1303 log_debug("sd-device: ignoring invalid db entry with key '%c'", key);
1304
1305 state = INVALID_LINE;
1306 } else {
1307 db[i] = '\0';
1308
1309 state = PRE_VALUE;
1310 }
1311
1312 break;
1313 case PRE_VALUE:
1314 value = &db[i];
1315
1316 state = VALUE;
1317
1318 break;
1319 case INVALID_LINE:
1320 if (strchr(NEWLINE, db[i]))
1321 state = PRE_KEY;
1322
1323 break;
1324 case VALUE:
1325 if (strchr(NEWLINE, db[i])) {
1326 db[i] = '\0';
1327 r = handle_db_line(device, key, value);
1328 if (r < 0)
19c29853 1329 log_debug_errno(r, "sd-device: failed to handle db entry '%c:%s': %m", key, value);
57fa1d09
TG
1330
1331 state = PRE_KEY;
1332 }
1333
1334 break;
1335 default:
1336 assert_not_reached("invalid state when parsing db");
1337 }
1338 }
1339
57fa1d09
TG
1340 return 0;
1341}
1342
107f2e25
TG
1343static int device_read_db(sd_device *device) {
1344 return device_read_db_aux(device, false);
1345}
1346
57fa1d09
TG
1347_public_ int sd_device_get_is_initialized(sd_device *device, int *initialized) {
1348 int r;
1349
1350 assert_return(device, -EINVAL);
1351 assert_return(initialized, -EINVAL);
1352
1353 r = device_read_db(device);
1354 if (r < 0)
1355 return r;
1356
1357 *initialized = device->is_initialized;
1358
1359 return 0;
1360}
1361
1362_public_ int sd_device_get_usec_since_initialized(sd_device *device, uint64_t *usec) {
1363 usec_t now_ts;
1364 int r;
1365
1366 assert_return(device, -EINVAL);
1367 assert_return(usec, -EINVAL);
1368
1369 r = device_read_db(device);
1370 if (r < 0)
1371 return r;
1372
1373 if (!device->is_initialized)
1374 return -EBUSY;
1375
1376 if (!device->usec_initialized)
1377 return -ENODATA;
1378
1379 now_ts = now(clock_boottime_or_monotonic());
1380
1381 if (now_ts < device->usec_initialized)
1382 return -EIO;
1383
1384 *usec = now_ts - device->usec_initialized;
1385
1386 return 0;
1387}
1388
1389_public_ const char *sd_device_get_tag_first(sd_device *device) {
8927b1da
DH
1390 void *v;
1391
57fa1d09
TG
1392 assert_return(device, NULL);
1393
1394 (void) device_read_db(device);
1395
1396 device->tags_iterator_generation = device->tags_generation;
1397 device->tags_iterator = ITERATOR_FIRST;
1398
bccfe92e 1399 (void) set_iterate(device->tags, &device->tags_iterator, &v);
8927b1da 1400 return v;
57fa1d09
TG
1401}
1402
1403_public_ const char *sd_device_get_tag_next(sd_device *device) {
8927b1da
DH
1404 void *v;
1405
57fa1d09
TG
1406 assert_return(device, NULL);
1407
1408 (void) device_read_db(device);
1409
1410 if (device->tags_iterator_generation != device->tags_generation)
1411 return NULL;
1412
bccfe92e 1413 (void) set_iterate(device->tags, &device->tags_iterator, &v);
8927b1da 1414 return v;
57fa1d09
TG
1415}
1416
1417_public_ const char *sd_device_get_devlink_first(sd_device *device) {
8927b1da
DH
1418 void *v;
1419
57fa1d09
TG
1420 assert_return(device, NULL);
1421
1422 (void) device_read_db(device);
1423
1424 device->devlinks_iterator_generation = device->devlinks_generation;
1425 device->devlinks_iterator = ITERATOR_FIRST;
1426
bccfe92e 1427 (void) set_iterate(device->devlinks, &device->devlinks_iterator, &v);
8927b1da 1428 return v;
57fa1d09
TG
1429}
1430
1431_public_ const char *sd_device_get_devlink_next(sd_device *device) {
8927b1da
DH
1432 void *v;
1433
57fa1d09
TG
1434 assert_return(device, NULL);
1435
1436 (void) device_read_db(device);
1437
1438 if (device->devlinks_iterator_generation != device->devlinks_generation)
1439 return NULL;
1440
bccfe92e 1441 (void) set_iterate(device->devlinks, &device->devlinks_iterator, &v);
8927b1da 1442 return v;
57fa1d09
TG
1443}
1444
1445static int device_properties_prepare(sd_device *device) {
1446 int r;
1447
1448 assert(device);
1449
1450 r = device_read_uevent_file(device);
1451 if (r < 0)
1452 return r;
1453
1454 r = device_read_db(device);
1455 if (r < 0)
1456 return r;
1457
1458 if (device->property_devlinks_outdated) {
1d88a271
MP
1459 _cleanup_free_ char *devlinks = NULL;
1460 size_t devlinks_allocated = 0, devlinks_len = 0;
57fa1d09
TG
1461 const char *devlink;
1462
1d88a271
MP
1463 for (devlink = sd_device_get_devlink_first(device); devlink; devlink = sd_device_get_devlink_next(device)) {
1464 char *e;
57fa1d09 1465
1d88a271
MP
1466 if (!GREEDY_REALLOC(devlinks, devlinks_allocated, devlinks_len + strlen(devlink) + 2))
1467 return -ENOMEM;
1468 if (devlinks_len > 0)
1469 stpcpy(devlinks + devlinks_len++, " ");
1470 e = stpcpy(devlinks + devlinks_len, devlink);
1471 devlinks_len = e - devlinks;
1472 }
57fa1d09
TG
1473
1474 r = device_add_property_internal(device, "DEVLINKS", devlinks);
1475 if (r < 0)
1476 return r;
1477
1478 device->property_devlinks_outdated = false;
1479 }
1480
1481 if (device->property_tags_outdated) {
1d88a271
MP
1482 _cleanup_free_ char *tags = NULL;
1483 size_t tags_allocated = 0, tags_len = 0;
57fa1d09
TG
1484 const char *tag;
1485
1d88a271
MP
1486 if (!GREEDY_REALLOC(tags, tags_allocated, 2))
1487 return -ENOMEM;
1488 stpcpy(tags, ":");
1489 tags_len++;
57fa1d09 1490
1d88a271
MP
1491 for (tag = sd_device_get_tag_first(device); tag; tag = sd_device_get_tag_next(device)) {
1492 char *e;
57fa1d09 1493
817ec8cc 1494 if (!GREEDY_REALLOC(tags, tags_allocated, tags_len + strlen(tag) + 2))
1d88a271
MP
1495 return -ENOMEM;
1496 e = stpcpy(stpcpy(tags + tags_len, tag), ":");
1497 tags_len = e - tags;
1498 }
57fa1d09
TG
1499
1500 r = device_add_property_internal(device, "TAGS", tags);
1501 if (r < 0)
1502 return r;
1503
1504 device->property_tags_outdated = false;
1505 }
1506
1507 return 0;
1508}
1509
1510_public_ const char *sd_device_get_property_first(sd_device *device, const char **_value) {
1511 const char *key;
1512 const char *value;
1513 int r;
1514
1515 assert_return(device, NULL);
1516
1517 r = device_properties_prepare(device);
1518 if (r < 0)
1519 return NULL;
1520
1521 device->properties_iterator_generation = device->properties_generation;
1522 device->properties_iterator = ITERATOR_FIRST;
1523
8927b1da 1524 ordered_hashmap_iterate(device->properties, &device->properties_iterator, (void**)&value, (const void**)&key);
57fa1d09
TG
1525
1526 if (_value)
1527 *_value = value;
1528
1529 return key;
1530}
1531
1532_public_ const char *sd_device_get_property_next(sd_device *device, const char **_value) {
1533 const char *key;
1534 const char *value;
1535 int r;
1536
1537 assert_return(device, NULL);
1538
1539 r = device_properties_prepare(device);
1540 if (r < 0)
1541 return NULL;
1542
1543 if (device->properties_iterator_generation != device->properties_generation)
1544 return NULL;
1545
8927b1da 1546 ordered_hashmap_iterate(device->properties, &device->properties_iterator, (void**)&value, (const void**)&key);
57fa1d09
TG
1547
1548 if (_value)
1549 *_value = value;
1550
1551 return key;
1552}
1553
1554static int device_sysattrs_read_all(sd_device *device) {
1555 _cleanup_closedir_ DIR *dir = NULL;
1556 const char *syspath;
1557 struct dirent *dent;
1558 int r;
1559
1560 assert(device);
1561
1562 if (device->sysattrs_read)
1563 return 0;
1564
1565 r = sd_device_get_syspath(device, &syspath);
1566 if (r < 0)
1567 return r;
1568
1569 dir = opendir(syspath);
1570 if (!dir)
1571 return -errno;
1572
1573 r = set_ensure_allocated(&device->sysattrs, &string_hash_ops);
1574 if (r < 0)
1575 return r;
1576
1577 for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) {
1578 char *path;
1579 struct stat statbuf;
1580
1581 /* only handle symlinks and regular files */
1582 if (dent->d_type != DT_LNK && dent->d_type != DT_REG)
1583 continue;
1584
1585 path = strjoina(syspath, "/", dent->d_name);
1586
1587 if (lstat(path, &statbuf) != 0)
1588 continue;
1589
1590 if (!(statbuf.st_mode & S_IRUSR))
1591 continue;
1592
1593 r = set_put_strdup(device->sysattrs, dent->d_name);
1594 if (r < 0)
1595 return r;
1596 }
1597
1598 device->sysattrs_read = true;
1599
1600 return 0;
1601}
1602
1603_public_ const char *sd_device_get_sysattr_first(sd_device *device) {
8927b1da 1604 void *v;
57fa1d09
TG
1605 int r;
1606
1607 assert_return(device, NULL);
1608
1609 if (!device->sysattrs_read) {
1610 r = device_sysattrs_read_all(device);
1611 if (r < 0) {
1612 errno = -r;
1613 return NULL;
1614 }
1615 }
1616
1617 device->sysattrs_iterator = ITERATOR_FIRST;
1618
bccfe92e 1619 (void) set_iterate(device->sysattrs, &device->sysattrs_iterator, &v);
8927b1da 1620 return v;
57fa1d09
TG
1621}
1622
1623_public_ const char *sd_device_get_sysattr_next(sd_device *device) {
8927b1da
DH
1624 void *v;
1625
57fa1d09
TG
1626 assert_return(device, NULL);
1627
1628 if (!device->sysattrs_read)
1629 return NULL;
1630
bccfe92e 1631 (void) set_iterate(device->sysattrs, &device->sysattrs_iterator, &v);
8927b1da 1632 return v;
57fa1d09
TG
1633}
1634
1635_public_ int sd_device_has_tag(sd_device *device, const char *tag) {
1636 assert_return(device, -EINVAL);
1637 assert_return(tag, -EINVAL);
1638
1639 (void) device_read_db(device);
1640
1641 return !!set_contains(device->tags, tag);
1642}
1643
1644_public_ int sd_device_get_property_value(sd_device *device, const char *key, const char **_value) {
1645 char *value;
1646 int r;
1647
1648 assert_return(device, -EINVAL);
1649 assert_return(key, -EINVAL);
1650 assert_return(_value, -EINVAL);
1651
1652 r = device_properties_prepare(device);
1653 if (r < 0)
1654 return r;
1655
1656 value = ordered_hashmap_get(device->properties, key);
1657 if (!value)
1658 return -ENOENT;
1659
1660 *_value = value;
1661
1662 return 0;
1663}
1664
1665/* replaces the value if it already exists */
2fe29a46 1666static int device_add_sysattr_value(sd_device *device, const char *_key, char *value) {
57fa1d09 1667 _cleanup_free_ char *key = NULL;
2fe29a46 1668 _cleanup_free_ char *value_old = NULL;
57fa1d09
TG
1669 int r;
1670
1671 assert(device);
1672 assert(_key);
1673
1674 r = hashmap_ensure_allocated(&device->sysattr_values, &string_hash_ops);
1675 if (r < 0)
1676 return r;
1677
2fe29a46 1678 value_old = hashmap_remove2(device->sysattr_values, _key, (void **)&key);
57fa1d09
TG
1679 if (!key) {
1680 key = strdup(_key);
1681 if (!key)
1682 return -ENOMEM;
1683 }
1684
57fa1d09
TG
1685 r = hashmap_put(device->sysattr_values, key, value);
1686 if (r < 0)
1687 return r;
1688
1689 key = NULL;
57fa1d09
TG
1690
1691 return 0;
1692}
1693
1694static int device_get_sysattr_value(sd_device *device, const char *_key, const char **_value) {
1695 const char *key = NULL, *value;
1696
1697 assert(device);
1698 assert(_key);
1699
1700 value = hashmap_get2(device->sysattr_values, _key, (void **) &key);
1701 if (!key)
1702 return -ENOENT;
1703
1704 if (_value)
1705 *_value = value;
1706
1707 return 0;
1708}
1709
1710/* We cache all sysattr lookups. If an attribute does not exist, it is stored
1711 * with a NULL value in the cache, otherwise the returned string is stored */
1712_public_ int sd_device_get_sysattr_value(sd_device *device, const char *sysattr, const char **_value) {
1713 _cleanup_free_ char *value = NULL;
1714 const char *syspath, *cached_value = NULL;
1715 char *path;
1716 struct stat statbuf;
1717 int r;
1718
1719 assert_return(device, -EINVAL);
1720 assert_return(sysattr, -EINVAL);
1721
1722 /* look for possibly already cached result */
1723 r = device_get_sysattr_value(device, sysattr, &cached_value);
1724 if (r != -ENOENT) {
1725 if (r < 0)
1726 return r;
1727
1728 if (!cached_value)
1729 /* we looked up the sysattr before and it did not exist */
1730 return -ENOENT;
1731
1732 if (_value)
1733 *_value = cached_value;
1734
1735 return 0;
1736 }
1737
1738 r = sd_device_get_syspath(device, &syspath);
1739 if (r < 0)
1740 return r;
1741
1742 path = strjoina(syspath, "/", sysattr);
1743 r = lstat(path, &statbuf);
1744 if (r < 0) {
1745 /* remember that we could not access the sysattr */
1746 r = device_add_sysattr_value(device, sysattr, NULL);
1747 if (r < 0)
1748 return r;
1749
1750 return -ENOENT;
1751 } else if (S_ISLNK(statbuf.st_mode)) {
1752 /* Some core links return only the last element of the target path,
1753 * these are just values, the paths should not be exposed. */
1754 if (STR_IN_SET(sysattr, "driver", "subsystem", "module")) {
1755 r = readlink_value(path, &value);
1756 if (r < 0)
1757 return r;
1758 } else
1759 return -EINVAL;
1760 } else if (S_ISDIR(statbuf.st_mode)) {
1761 /* skip directories */
1762 return -EINVAL;
1763 } else if (!(statbuf.st_mode & S_IRUSR)) {
1764 /* skip non-readable files */
1765 return -EPERM;
1766 } else {
1767 size_t size;
1768
1769 /* read attribute value */
1770 r = read_full_file(path, &value, &size);
1771 if (r < 0)
1772 return r;
1773
1774 /* drop trailing newlines */
1775 while (size > 0 && value[--size] == '\n')
1776 value[size] = '\0';
1777 }
1778
1779 r = device_add_sysattr_value(device, sysattr, value);
1780 if (r < 0)
1781 return r;
1782
1783 *_value = value;
1784 value = NULL;
1785
1786 return 0;
1787}
1788
1789static void device_remove_sysattr_value(sd_device *device, const char *_key) {
1790 _cleanup_free_ char *key = NULL;
1791 _cleanup_free_ char *value = NULL;
1792
1793 assert(device);
1794 assert(_key);
1795
1796 value = hashmap_remove2(device->sysattr_values, _key, (void **) &key);
1797
1798 return;
1799}
1800
1801/* set the attribute and save it in the cache. If a NULL value is passed the
1802 * attribute is cleared from the cache */
2fe29a46 1803_public_ int sd_device_set_sysattr_value(sd_device *device, const char *sysattr, char *_value) {
57fa1d09 1804 _cleanup_close_ int fd = -1;
2fe29a46 1805 _cleanup_free_ char *value = NULL;
57fa1d09
TG
1806 const char *syspath;
1807 char *path;
1808 struct stat statbuf;
1809 size_t value_len = 0;
1810 ssize_t size;
1811 int r;
1812
1813 assert_return(device, -EINVAL);
1814 assert_return(sysattr, -EINVAL);
1815
2fe29a46 1816 if (!_value) {
57fa1d09
TG
1817 device_remove_sysattr_value(device, sysattr);
1818
1819 return 0;
1820 }
1821
1822 r = sd_device_get_syspath(device, &syspath);
1823 if (r < 0)
1824 return r;
1825
1826 path = strjoina(syspath, "/", sysattr);
1827 r = lstat(path, &statbuf);
1828 if (r < 0) {
2fe29a46
TG
1829 value = strdup("");
1830 if (!value)
1831 return -ENOMEM;
1832
1833 r = device_add_sysattr_value(device, sysattr, value);
57fa1d09
TG
1834 if (r < 0)
1835 return r;
1836
1837 return -ENXIO;
1838 }
1839
1840 if (S_ISLNK(statbuf.st_mode))
1841 return -EINVAL;
1842
1843 /* skip directories */
1844 if (S_ISDIR(statbuf.st_mode))
1845 return -EISDIR;
1846
1847 /* skip non-readable files */
1848 if ((statbuf.st_mode & S_IRUSR) == 0)
1849 return -EACCES;
1850
2fe29a46 1851 value_len = strlen(_value);
57fa1d09
TG
1852
1853 /* drop trailing newlines */
24eb4a30
DH
1854 while (value_len > 0 && _value[value_len - 1] == '\n')
1855 _value[--value_len] = '\0';
57fa1d09
TG
1856
1857 /* value length is limited to 4k */
1858 if (value_len > 4096)
1859 return -EINVAL;
1860
1861 fd = open(path, O_WRONLY | O_CLOEXEC);
1862 if (fd < 0)
1863 return -errno;
1864
2fe29a46
TG
1865 value = strdup(_value);
1866 if (!value)
1867 return -ENOMEM;
1868
57fa1d09
TG
1869 size = write(fd, value, value_len);
1870 if (size < 0)
1871 return -errno;
1872
1873 if ((size_t)size != value_len)
1874 return -EIO;
1875
1876 r = device_add_sysattr_value(device, sysattr, value);
1877 if (r < 0)
1878 return r;
1879
2fe29a46
TG
1880 value = NULL;
1881
57fa1d09
TG
1882 return 0;
1883}