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