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