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