]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/libsystemd/sd-device/sd-device.c
json: avoid cleanup of unitialized variable
[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
TG
787 r = device_set_subsystem(device, "subsystem");
788 if (r < 0)
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) {
1191 /* use dev_t -- b259:131072, c254:0 */
1192 r = asprintf(&id, "%c%u:%u",
1193 streq(subsystem, "block") ? 'b' : 'c',
1194 major(devnum), minor(devnum));
1195 if (r < 0)
1196 return -errno;
1197 } else if (ifindex > 0) {
1198 /* use netdev ifindex -- n3 */
1199 r = asprintf(&id, "n%u", ifindex);
1200 if (r < 0)
1201 return -errno;
1202 } else {
1203 /* use $subsys:$sysname -- pci:0000:00:1f.2
1204 * sysname() has '!' translated, get it from devpath
1205 */
1206 const char *sysname;
1207
1208 sysname = basename(device->devpath);
1209 if (!sysname)
1210 return -EINVAL;
1211
1212 r = asprintf(&id, "+%s:%s", subsystem, sysname);
1213 if (r < 0)
1214 return -errno;
1215 }
1216
1217 device->id_filename = id;
1218 id = NULL;
1219 }
1220
1221 *ret = device->id_filename;
1222
1223 return 0;
1224}
1225
107f2e25 1226int device_read_db_aux(sd_device *device, bool force) {
57fa1d09
TG
1227 _cleanup_free_ char *db = NULL;
1228 char *path;
1229 const char *id, *value;
1230 char key;
1231 size_t db_len;
1232 unsigned i;
1233 int r;
1234
1235 enum {
1236 PRE_KEY,
1237 KEY,
1238 PRE_VALUE,
1239 VALUE,
1240 INVALID_LINE,
1241 } state = PRE_KEY;
1242
107f2e25 1243 if (device->db_loaded || (!force && device->sealed))
57fa1d09
TG
1244 return 0;
1245
7141e4f6
TG
1246 device->db_loaded = true;
1247
57fa1d09
TG
1248 r = device_get_id_filename(device, &id);
1249 if (r < 0)
1250 return r;
1251
1252 path = strjoina("/run/udev/data/", id);
1253
1254 r = read_full_file(path, &db, &db_len);
1255 if (r < 0) {
1256 if (r == -ENOENT)
1257 return 0;
1258 else {
1259 log_debug("sd-device: failed to read db '%s': %s", path, strerror(-r));
1260 return r;
1261 }
1262 }
1263
1264 /* devices with a database entry are initialized */
1265 device->is_initialized = true;;
1266
1267 for (i = 0; i < db_len; i++) {
1268 switch (state) {
1269 case PRE_KEY:
1270 if (!strchr(NEWLINE, db[i])) {
1271 key = db[i];
1272
1273 state = KEY;
1274 }
1275
1276 break;
1277 case KEY:
1278 if (db[i] != ':') {
1279 log_debug("sd-device: ignoring invalid db entry with key '%c'", key);
1280
1281 state = INVALID_LINE;
1282 } else {
1283 db[i] = '\0';
1284
1285 state = PRE_VALUE;
1286 }
1287
1288 break;
1289 case PRE_VALUE:
1290 value = &db[i];
1291
1292 state = VALUE;
1293
1294 break;
1295 case INVALID_LINE:
1296 if (strchr(NEWLINE, db[i]))
1297 state = PRE_KEY;
1298
1299 break;
1300 case VALUE:
1301 if (strchr(NEWLINE, db[i])) {
1302 db[i] = '\0';
1303 r = handle_db_line(device, key, value);
1304 if (r < 0)
1305 log_debug("sd-device: failed to handle db entry '%c:%s': %s", key, value, strerror(-r));
1306
1307 state = PRE_KEY;
1308 }
1309
1310 break;
1311 default:
1312 assert_not_reached("invalid state when parsing db");
1313 }
1314 }
1315
57fa1d09
TG
1316 return 0;
1317}
1318
107f2e25
TG
1319static int device_read_db(sd_device *device) {
1320 return device_read_db_aux(device, false);
1321}
1322
57fa1d09
TG
1323_public_ int sd_device_get_is_initialized(sd_device *device, int *initialized) {
1324 int r;
1325
1326 assert_return(device, -EINVAL);
1327 assert_return(initialized, -EINVAL);
1328
1329 r = device_read_db(device);
1330 if (r < 0)
1331 return r;
1332
1333 *initialized = device->is_initialized;
1334
1335 return 0;
1336}
1337
1338_public_ int sd_device_get_usec_since_initialized(sd_device *device, uint64_t *usec) {
1339 usec_t now_ts;
1340 int r;
1341
1342 assert_return(device, -EINVAL);
1343 assert_return(usec, -EINVAL);
1344
1345 r = device_read_db(device);
1346 if (r < 0)
1347 return r;
1348
1349 if (!device->is_initialized)
1350 return -EBUSY;
1351
1352 if (!device->usec_initialized)
1353 return -ENODATA;
1354
1355 now_ts = now(clock_boottime_or_monotonic());
1356
1357 if (now_ts < device->usec_initialized)
1358 return -EIO;
1359
1360 *usec = now_ts - device->usec_initialized;
1361
1362 return 0;
1363}
1364
1365_public_ const char *sd_device_get_tag_first(sd_device *device) {
1366 assert_return(device, NULL);
1367
1368 (void) device_read_db(device);
1369
1370 device->tags_iterator_generation = device->tags_generation;
1371 device->tags_iterator = ITERATOR_FIRST;
1372
1373 return set_iterate(device->tags, &device->tags_iterator);
1374}
1375
1376_public_ const char *sd_device_get_tag_next(sd_device *device) {
1377 assert_return(device, NULL);
1378
1379 (void) device_read_db(device);
1380
1381 if (device->tags_iterator_generation != device->tags_generation)
1382 return NULL;
1383
1384 return set_iterate(device->tags, &device->tags_iterator);
1385}
1386
1387_public_ const char *sd_device_get_devlink_first(sd_device *device) {
1388 assert_return(device, NULL);
1389
1390 (void) device_read_db(device);
1391
1392 device->devlinks_iterator_generation = device->devlinks_generation;
1393 device->devlinks_iterator = ITERATOR_FIRST;
1394
1395 return set_iterate(device->devlinks, &device->devlinks_iterator);
1396}
1397
1398_public_ const char *sd_device_get_devlink_next(sd_device *device) {
1399 assert_return(device, NULL);
1400
1401 (void) device_read_db(device);
1402
1403 if (device->devlinks_iterator_generation != device->devlinks_generation)
1404 return NULL;
1405
1406 return set_iterate(device->devlinks, &device->devlinks_iterator);
1407}
1408
1409static int device_properties_prepare(sd_device *device) {
1410 int r;
1411
1412 assert(device);
1413
1414 r = device_read_uevent_file(device);
1415 if (r < 0)
1416 return r;
1417
1418 r = device_read_db(device);
1419 if (r < 0)
1420 return r;
1421
1422 if (device->property_devlinks_outdated) {
1423 char *devlinks = NULL;
1424 const char *devlink;
1425
1426 devlink = sd_device_get_devlink_first(device);
1427 if (devlink)
1428 devlinks = strdupa(devlink);
1429
1430 while ((devlink = sd_device_get_devlink_next(device)))
1431 devlinks = strjoina(devlinks, " ", devlink);
1432
1433 r = device_add_property_internal(device, "DEVLINKS", devlinks);
1434 if (r < 0)
1435 return r;
1436
1437 device->property_devlinks_outdated = false;
1438 }
1439
1440 if (device->property_tags_outdated) {
1441 char *tags = NULL;
1442 const char *tag;
1443
1444 tag = sd_device_get_tag_first(device);
1445 if (tag)
1446 tags = strjoina(":", tag);
1447
1448 while ((tag = sd_device_get_tag_next(device)))
1449 tags = strjoina(tags, ":", tag);
1450
1451 tags = strjoina(tags, ":");
1452
1453 r = device_add_property_internal(device, "TAGS", tags);
1454 if (r < 0)
1455 return r;
1456
1457 device->property_tags_outdated = false;
1458 }
1459
1460 return 0;
1461}
1462
1463_public_ const char *sd_device_get_property_first(sd_device *device, const char **_value) {
1464 const char *key;
1465 const char *value;
1466 int r;
1467
1468 assert_return(device, NULL);
1469
1470 r = device_properties_prepare(device);
1471 if (r < 0)
1472 return NULL;
1473
1474 device->properties_iterator_generation = device->properties_generation;
1475 device->properties_iterator = ITERATOR_FIRST;
1476
1477 value = ordered_hashmap_iterate(device->properties, &device->properties_iterator, (const void**)&key);
1478
1479 if (_value)
1480 *_value = value;
1481
1482 return key;
1483}
1484
1485_public_ const char *sd_device_get_property_next(sd_device *device, const char **_value) {
1486 const char *key;
1487 const char *value;
1488 int r;
1489
1490 assert_return(device, NULL);
1491
1492 r = device_properties_prepare(device);
1493 if (r < 0)
1494 return NULL;
1495
1496 if (device->properties_iterator_generation != device->properties_generation)
1497 return NULL;
1498
1499 value = ordered_hashmap_iterate(device->properties, &device->properties_iterator, (const void**)&key);
1500
1501 if (_value)
1502 *_value = value;
1503
1504 return key;
1505}
1506
1507static int device_sysattrs_read_all(sd_device *device) {
1508 _cleanup_closedir_ DIR *dir = NULL;
1509 const char *syspath;
1510 struct dirent *dent;
1511 int r;
1512
1513 assert(device);
1514
1515 if (device->sysattrs_read)
1516 return 0;
1517
1518 r = sd_device_get_syspath(device, &syspath);
1519 if (r < 0)
1520 return r;
1521
1522 dir = opendir(syspath);
1523 if (!dir)
1524 return -errno;
1525
1526 r = set_ensure_allocated(&device->sysattrs, &string_hash_ops);
1527 if (r < 0)
1528 return r;
1529
1530 for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) {
1531 char *path;
1532 struct stat statbuf;
1533
1534 /* only handle symlinks and regular files */
1535 if (dent->d_type != DT_LNK && dent->d_type != DT_REG)
1536 continue;
1537
1538 path = strjoina(syspath, "/", dent->d_name);
1539
1540 if (lstat(path, &statbuf) != 0)
1541 continue;
1542
1543 if (!(statbuf.st_mode & S_IRUSR))
1544 continue;
1545
1546 r = set_put_strdup(device->sysattrs, dent->d_name);
1547 if (r < 0)
1548 return r;
1549 }
1550
1551 device->sysattrs_read = true;
1552
1553 return 0;
1554}
1555
1556_public_ const char *sd_device_get_sysattr_first(sd_device *device) {
1557 int r;
1558
1559 assert_return(device, NULL);
1560
1561 if (!device->sysattrs_read) {
1562 r = device_sysattrs_read_all(device);
1563 if (r < 0) {
1564 errno = -r;
1565 return NULL;
1566 }
1567 }
1568
1569 device->sysattrs_iterator = ITERATOR_FIRST;
1570
1571 return set_iterate(device->sysattrs, &device->sysattrs_iterator);
1572}
1573
1574_public_ const char *sd_device_get_sysattr_next(sd_device *device) {
1575 assert_return(device, NULL);
1576
1577 if (!device->sysattrs_read)
1578 return NULL;
1579
1580 return set_iterate(device->sysattrs, &device->sysattrs_iterator);
1581}
1582
1583_public_ int sd_device_has_tag(sd_device *device, const char *tag) {
1584 assert_return(device, -EINVAL);
1585 assert_return(tag, -EINVAL);
1586
1587 (void) device_read_db(device);
1588
1589 return !!set_contains(device->tags, tag);
1590}
1591
1592_public_ int sd_device_get_property_value(sd_device *device, const char *key, const char **_value) {
1593 char *value;
1594 int r;
1595
1596 assert_return(device, -EINVAL);
1597 assert_return(key, -EINVAL);
1598 assert_return(_value, -EINVAL);
1599
1600 r = device_properties_prepare(device);
1601 if (r < 0)
1602 return r;
1603
1604 value = ordered_hashmap_get(device->properties, key);
1605 if (!value)
1606 return -ENOENT;
1607
1608 *_value = value;
1609
1610 return 0;
1611}
1612
1613/* replaces the value if it already exists */
2fe29a46 1614static int device_add_sysattr_value(sd_device *device, const char *_key, char *value) {
57fa1d09 1615 _cleanup_free_ char *key = NULL;
2fe29a46 1616 _cleanup_free_ char *value_old = NULL;
57fa1d09
TG
1617 int r;
1618
1619 assert(device);
1620 assert(_key);
1621
1622 r = hashmap_ensure_allocated(&device->sysattr_values, &string_hash_ops);
1623 if (r < 0)
1624 return r;
1625
2fe29a46 1626 value_old = hashmap_remove2(device->sysattr_values, _key, (void **)&key);
57fa1d09
TG
1627 if (!key) {
1628 key = strdup(_key);
1629 if (!key)
1630 return -ENOMEM;
1631 }
1632
57fa1d09
TG
1633 r = hashmap_put(device->sysattr_values, key, value);
1634 if (r < 0)
1635 return r;
1636
1637 key = NULL;
57fa1d09
TG
1638
1639 return 0;
1640}
1641
1642static int device_get_sysattr_value(sd_device *device, const char *_key, const char **_value) {
1643 const char *key = NULL, *value;
1644
1645 assert(device);
1646 assert(_key);
1647
1648 value = hashmap_get2(device->sysattr_values, _key, (void **) &key);
1649 if (!key)
1650 return -ENOENT;
1651
1652 if (_value)
1653 *_value = value;
1654
1655 return 0;
1656}
1657
1658/* We cache all sysattr lookups. If an attribute does not exist, it is stored
1659 * with a NULL value in the cache, otherwise the returned string is stored */
1660_public_ int sd_device_get_sysattr_value(sd_device *device, const char *sysattr, const char **_value) {
1661 _cleanup_free_ char *value = NULL;
1662 const char *syspath, *cached_value = NULL;
1663 char *path;
1664 struct stat statbuf;
1665 int r;
1666
1667 assert_return(device, -EINVAL);
1668 assert_return(sysattr, -EINVAL);
1669
1670 /* look for possibly already cached result */
1671 r = device_get_sysattr_value(device, sysattr, &cached_value);
1672 if (r != -ENOENT) {
1673 if (r < 0)
1674 return r;
1675
1676 if (!cached_value)
1677 /* we looked up the sysattr before and it did not exist */
1678 return -ENOENT;
1679
1680 if (_value)
1681 *_value = cached_value;
1682
1683 return 0;
1684 }
1685
1686 r = sd_device_get_syspath(device, &syspath);
1687 if (r < 0)
1688 return r;
1689
1690 path = strjoina(syspath, "/", sysattr);
1691 r = lstat(path, &statbuf);
1692 if (r < 0) {
1693 /* remember that we could not access the sysattr */
1694 r = device_add_sysattr_value(device, sysattr, NULL);
1695 if (r < 0)
1696 return r;
1697
1698 return -ENOENT;
1699 } else if (S_ISLNK(statbuf.st_mode)) {
1700 /* Some core links return only the last element of the target path,
1701 * these are just values, the paths should not be exposed. */
1702 if (STR_IN_SET(sysattr, "driver", "subsystem", "module")) {
1703 r = readlink_value(path, &value);
1704 if (r < 0)
1705 return r;
1706 } else
1707 return -EINVAL;
1708 } else if (S_ISDIR(statbuf.st_mode)) {
1709 /* skip directories */
1710 return -EINVAL;
1711 } else if (!(statbuf.st_mode & S_IRUSR)) {
1712 /* skip non-readable files */
1713 return -EPERM;
1714 } else {
1715 size_t size;
1716
1717 /* read attribute value */
1718 r = read_full_file(path, &value, &size);
1719 if (r < 0)
1720 return r;
1721
1722 /* drop trailing newlines */
1723 while (size > 0 && value[--size] == '\n')
1724 value[size] = '\0';
1725 }
1726
1727 r = device_add_sysattr_value(device, sysattr, value);
1728 if (r < 0)
1729 return r;
1730
1731 *_value = value;
1732 value = NULL;
1733
1734 return 0;
1735}
1736
1737static void device_remove_sysattr_value(sd_device *device, const char *_key) {
1738 _cleanup_free_ char *key = NULL;
1739 _cleanup_free_ char *value = NULL;
1740
1741 assert(device);
1742 assert(_key);
1743
1744 value = hashmap_remove2(device->sysattr_values, _key, (void **) &key);
1745
1746 return;
1747}
1748
1749/* set the attribute and save it in the cache. If a NULL value is passed the
1750 * attribute is cleared from the cache */
2fe29a46 1751_public_ int sd_device_set_sysattr_value(sd_device *device, const char *sysattr, char *_value) {
57fa1d09 1752 _cleanup_close_ int fd = -1;
2fe29a46 1753 _cleanup_free_ char *value = NULL;
57fa1d09
TG
1754 const char *syspath;
1755 char *path;
1756 struct stat statbuf;
1757 size_t value_len = 0;
1758 ssize_t size;
1759 int r;
1760
1761 assert_return(device, -EINVAL);
1762 assert_return(sysattr, -EINVAL);
1763
2fe29a46 1764 if (!_value) {
57fa1d09
TG
1765 device_remove_sysattr_value(device, sysattr);
1766
1767 return 0;
1768 }
1769
1770 r = sd_device_get_syspath(device, &syspath);
1771 if (r < 0)
1772 return r;
1773
1774 path = strjoina(syspath, "/", sysattr);
1775 r = lstat(path, &statbuf);
1776 if (r < 0) {
2fe29a46
TG
1777 value = strdup("");
1778 if (!value)
1779 return -ENOMEM;
1780
1781 r = device_add_sysattr_value(device, sysattr, value);
57fa1d09
TG
1782 if (r < 0)
1783 return r;
1784
1785 return -ENXIO;
1786 }
1787
1788 if (S_ISLNK(statbuf.st_mode))
1789 return -EINVAL;
1790
1791 /* skip directories */
1792 if (S_ISDIR(statbuf.st_mode))
1793 return -EISDIR;
1794
1795 /* skip non-readable files */
1796 if ((statbuf.st_mode & S_IRUSR) == 0)
1797 return -EACCES;
1798
2fe29a46 1799 value_len = strlen(_value);
57fa1d09
TG
1800
1801 /* drop trailing newlines */
24eb4a30
DH
1802 while (value_len > 0 && _value[value_len - 1] == '\n')
1803 _value[--value_len] = '\0';
57fa1d09
TG
1804
1805 /* value length is limited to 4k */
1806 if (value_len > 4096)
1807 return -EINVAL;
1808
1809 fd = open(path, O_WRONLY | O_CLOEXEC);
1810 if (fd < 0)
1811 return -errno;
1812
2fe29a46
TG
1813 value = strdup(_value);
1814 if (!value)
1815 return -ENOMEM;
1816
57fa1d09
TG
1817 size = write(fd, value, value_len);
1818 if (size < 0)
1819 return -errno;
1820
1821 if ((size_t)size != value_len)
1822 return -EIO;
1823
1824 r = device_add_sysattr_value(device, sysattr, value);
1825 if (r < 0)
1826 return r;
1827
2fe29a46
TG
1828 value = NULL;
1829
57fa1d09
TG
1830 return 0;
1831}