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