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