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