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