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