]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/libsystemd/sd-device/device-private.c
Merge pull request #11827 from keszybz/pkgconfig-variables
[thirdparty/systemd.git] / src / libsystemd / sd-device / device-private.c
CommitLineData
53e1b683 1/* SPDX-License-Identifier: LGPL-2.1+ */
57fa1d09
TG
2
3#include <ctype.h>
57fa1d09 4#include <net/if.h>
07630cea 5#include <sys/types.h>
57fa1d09
TG
6
7#include "sd-device.h"
8
b5efdb8a 9#include "alloc-util.h"
57fa1d09
TG
10#include "device-internal.h"
11#include "device-private.h"
07630cea 12#include "device-util.h"
3ffd4af2 13#include "fd-util.h"
07630cea 14#include "fileio.h"
f4f15635 15#include "fs-util.h"
07630cea
LP
16#include "hashmap.h"
17#include "macro.h"
18#include "mkdir.h"
6bedfcbb 19#include "parse-util.h"
07630cea
LP
20#include "path-util.h"
21#include "refcnt.h"
22#include "set.h"
8b43440b 23#include "string-table.h"
07630cea
LP
24#include "string-util.h"
25#include "strv.h"
26#include "strxcpyx.h"
e4de7287 27#include "tmpfile-util.h"
ee104e11 28#include "user-util.h"
07630cea 29#include "util.h"
57fa1d09
TG
30
31int device_add_property(sd_device *device, const char *key, const char *value) {
32 int r;
33
34 assert(device);
35 assert(key);
36
37 r = device_add_property_aux(device, key, value, false);
38 if (r < 0)
39 return r;
40
41 if (key[0] != '.') {
42 r = device_add_property_aux(device, key, value, true);
43 if (r < 0)
44 return r;
45 }
46
47 return 0;
48}
49
57fa1d09
TG
50void device_set_devlink_priority(sd_device *device, int priority) {
51 assert(device);
52
53 device->devlink_priority = priority;
54}
55
56void device_set_is_initialized(sd_device *device) {
57 assert(device);
58
59 device->is_initialized = true;
60}
61
62int device_ensure_usec_initialized(sd_device *device, sd_device *device_old) {
dc5042c0 63 usec_t when;
57fa1d09
TG
64
65 assert(device);
66
67 if (device_old && device_old->usec_initialized > 0)
dc5042c0 68 when = device_old->usec_initialized;
57fa1d09 69 else
dc5042c0 70 when = now(CLOCK_MONOTONIC);
57fa1d09 71
dc5042c0 72 return device_set_usec_initialized(device, when);
57fa1d09
TG
73}
74
57fa1d09
TG
75uint64_t device_get_properties_generation(sd_device *device) {
76 assert(device);
77
78 return device->properties_generation;
79}
80
81uint64_t device_get_tags_generation(sd_device *device) {
82 assert(device);
83
84 return device->tags_generation;
85}
86
87uint64_t device_get_devlinks_generation(sd_device *device) {
88 assert(device);
89
90 return device->devlinks_generation;
91}
92
93int device_get_devnode_mode(sd_device *device, mode_t *mode) {
94 int r;
95
96 assert(device);
57fa1d09
TG
97
98 r = device_read_db(device);
99 if (r < 0)
100 return r;
101
dcfbde3a
YW
102 if (device->devmode == (mode_t) -1)
103 return -ENOENT;
104
78ffb476
YW
105 if (mode)
106 *mode = device->devmode;
57fa1d09
TG
107
108 return 0;
109}
110
111int device_get_devnode_uid(sd_device *device, uid_t *uid) {
112 int r;
113
114 assert(device);
57fa1d09
TG
115
116 r = device_read_db(device);
117 if (r < 0)
118 return r;
119
dcfbde3a
YW
120 if (device->devuid == (uid_t) -1)
121 return -ENOENT;
122
78ffb476
YW
123 if (uid)
124 *uid = device->devuid;
57fa1d09
TG
125
126 return 0;
127}
128
129static int device_set_devuid(sd_device *device, const char *uid) {
130 unsigned u;
131 int r;
132
133 assert(device);
134 assert(uid);
135
136 r = safe_atou(uid, &u);
137 if (r < 0)
138 return r;
139
140 r = device_add_property_internal(device, "DEVUID", uid);
141 if (r < 0)
142 return r;
143
144 device->devuid = u;
145
146 return 0;
147}
148
149int device_get_devnode_gid(sd_device *device, gid_t *gid) {
150 int r;
151
152 assert(device);
57fa1d09
TG
153
154 r = device_read_db(device);
155 if (r < 0)
156 return r;
157
dcfbde3a
YW
158 if (device->devgid == (gid_t) -1)
159 return -ENOENT;
160
78ffb476
YW
161 if (gid)
162 *gid = device->devgid;
57fa1d09
TG
163
164 return 0;
165}
166
167static int device_set_devgid(sd_device *device, const char *gid) {
168 unsigned g;
169 int r;
170
171 assert(device);
172 assert(gid);
173
174 r = safe_atou(gid, &g);
175 if (r < 0)
176 return r;
177
178 r = device_add_property_internal(device, "DEVGID", gid);
179 if (r < 0)
180 return r;
181
182 device->devgid = g;
183
184 return 0;
185}
186
401cb614 187static int device_amend(sd_device *device, const char *key, const char *value) {
57fa1d09
TG
188 int r;
189
190 assert(device);
191 assert(key);
192 assert(value);
193
194 if (streq(key, "DEVPATH")) {
195 char *path;
196
197 path = strjoina("/sys", value);
198
199 /* the caller must verify or trust this data (e.g., if it comes from the kernel) */
200 r = device_set_syspath(device, path, false);
201 if (r < 0)
c7d54dae 202 return log_device_debug_errno(device, r, "sd-device: Failed to set syspath to '%s': %m", path);
57fa1d09
TG
203 } else if (streq(key, "SUBSYSTEM")) {
204 r = device_set_subsystem(device, value);
205 if (r < 0)
c7d54dae 206 return log_device_debug_errno(device, r, "sd-device: Failed to set subsystem to '%s': %m", value);
57fa1d09
TG
207 } else if (streq(key, "DEVTYPE")) {
208 r = device_set_devtype(device, value);
209 if (r < 0)
c7d54dae 210 return log_device_debug_errno(device, r, "sd-device: Failed to set devtype to '%s': %m", value);
57fa1d09
TG
211 } else if (streq(key, "DEVNAME")) {
212 r = device_set_devname(device, value);
213 if (r < 0)
c7d54dae 214 return log_device_debug_errno(device, r, "sd-device: Failed to set devname to '%s': %m", value);
57fa1d09 215 } else if (streq(key, "USEC_INITIALIZED")) {
dc5042c0
ZJS
216 usec_t t;
217
218 r = safe_atou64(value, &t);
219 if (r < 0)
220 return log_device_debug_errno(device, r, "sd-device: Failed to parse timestamp '%s': %m", value);
221
222 r = device_set_usec_initialized(device, t);
57fa1d09 223 if (r < 0)
c7d54dae 224 return log_device_debug_errno(device, r, "sd-device: Failed to set usec-initialized to '%s': %m", value);
57fa1d09
TG
225 } else if (streq(key, "DRIVER")) {
226 r = device_set_driver(device, value);
227 if (r < 0)
c7d54dae 228 return log_device_debug_errno(device, r, "sd-device: Failed to set driver to '%s': %m", value);
57fa1d09
TG
229 } else if (streq(key, "IFINDEX")) {
230 r = device_set_ifindex(device, value);
231 if (r < 0)
c7d54dae 232 return log_device_debug_errno(device, r, "sd-device: Failed to set ifindex to '%s': %m", value);
57fa1d09
TG
233 } else if (streq(key, "DEVMODE")) {
234 r = device_set_devmode(device, value);
235 if (r < 0)
c7d54dae 236 return log_device_debug_errno(device, r, "sd-device: Failed to set devmode to '%s': %m", value);
57fa1d09
TG
237 } else if (streq(key, "DEVUID")) {
238 r = device_set_devuid(device, value);
239 if (r < 0)
c7d54dae 240 return log_device_debug_errno(device, r, "sd-device: Failed to set devuid to '%s': %m", value);
57fa1d09
TG
241 } else if (streq(key, "DEVGID")) {
242 r = device_set_devgid(device, value);
243 if (r < 0)
c7d54dae 244 return log_device_debug_errno(device, r, "sd-device: Failed to set devgid to '%s': %m", value);
57fa1d09 245 } else if (streq(key, "DEVLINKS")) {
4df4fd11
TG
246 const char *word, *state;
247 size_t l;
57fa1d09 248
4df4fd11 249 FOREACH_WORD(word, l, value, state) {
de9b34b6 250 char devlink[l + 1];
57fa1d09 251
de9b34b6
TG
252 strncpy(devlink, word, l);
253 devlink[l] = '\0';
57fa1d09 254
4df4fd11 255 r = device_add_devlink(device, devlink);
57fa1d09 256 if (r < 0)
c7d54dae 257 return log_device_debug_errno(device, r, "sd-device: Failed to add devlink '%s': %m", devlink);
57fa1d09
TG
258 }
259 } else if (streq(key, "TAGS")) {
4df4fd11
TG
260 const char *word, *state;
261 size_t l;
57fa1d09 262
4df4fd11 263 FOREACH_WORD_SEPARATOR(word, l, value, ":", state) {
de9b34b6 264 char tag[l + 1];
57fa1d09 265
db2f8a2e 266 (void) strncpy(tag, word, l);
de9b34b6 267 tag[l] = '\0';
57fa1d09 268
4df4fd11 269 r = device_add_tag(device, tag);
57fa1d09 270 if (r < 0)
c7d54dae 271 return log_device_debug_errno(device, r, "sd-device: Failed to add tag '%s': %m", tag);
57fa1d09
TG
272 }
273 } else {
274 r = device_add_property_internal(device, key, value);
275 if (r < 0)
c7d54dae 276 return log_device_debug_errno(device, r, "sd-device: Failed to add property '%s=%s': %m", key, value);
57fa1d09
TG
277 }
278
279 return 0;
280}
281
282static const char* const device_action_table[_DEVICE_ACTION_MAX] = {
283 [DEVICE_ACTION_ADD] = "add",
284 [DEVICE_ACTION_REMOVE] = "remove",
285 [DEVICE_ACTION_CHANGE] = "change",
286 [DEVICE_ACTION_MOVE] = "move",
287 [DEVICE_ACTION_ONLINE] = "online",
288 [DEVICE_ACTION_OFFLINE] = "offline",
9a39e1ce
LP
289 [DEVICE_ACTION_BIND] = "bind",
290 [DEVICE_ACTION_UNBIND] = "unbind",
57fa1d09
TG
291};
292
293DEFINE_STRING_TABLE_LOOKUP(device_action, DeviceAction);
294
295static int device_append(sd_device *device, char *key, const char **_major, const char **_minor, uint64_t *_seqnum,
296 DeviceAction *_action) {
297 DeviceAction action = _DEVICE_ACTION_INVALID;
298 uint64_t seqnum = 0;
299 const char *major = NULL, *minor = NULL;
300 char *value;
301 int r;
302
303 assert(device);
304 assert(key);
305 assert(_major);
306 assert(_minor);
307 assert(_seqnum);
308 assert(_action);
309
310 value = strchr(key, '=');
311 if (!value) {
c7d54dae 312 log_device_debug(device, "sd-device: Not a key-value pair: '%s'", key);
57fa1d09
TG
313 return -EINVAL;
314 }
315
316 *value = '\0';
317
318 value++;
319
320 if (streq(key, "MAJOR"))
321 major = value;
322 else if (streq(key, "MINOR"))
323 minor = value;
324 else {
325 if (streq(key, "ACTION")) {
326 action = device_action_from_string(value);
327 if (action == _DEVICE_ACTION_INVALID)
328 return -EINVAL;
329 } else if (streq(key, "SEQNUM")) {
330 r = safe_atou64(value, &seqnum);
331 if (r < 0)
332 return r;
333 else if (seqnum == 0)
334 /* kernel only sends seqnum > 0 */
335 return -EINVAL;
336 }
337
401cb614 338 r = device_amend(device, key, value);
57fa1d09
TG
339 if (r < 0)
340 return r;
341 }
342
343 if (major != 0)
344 *_major = major;
345
346 if (minor != 0)
347 *_minor = minor;
348
349 if (action != _DEVICE_ACTION_INVALID)
350 *_action = action;
351
352 if (seqnum > 0)
353 *_seqnum = seqnum;
354
355 return 0;
356}
357
358void device_seal(sd_device *device) {
359 assert(device);
360
361 device->sealed = true;
362}
363
364static int device_verify(sd_device *device, DeviceAction action, uint64_t seqnum) {
365 assert(device);
366
367 if (!device->devpath || !device->subsystem || action == _DEVICE_ACTION_INVALID || seqnum == 0) {
c7d54dae 368 log_device_debug(device, "sd-device: Device created from strv or nulstr lacks devpath, subsystem, action or seqnum.");
57fa1d09
TG
369 return -EINVAL;
370 }
371
372 device->sealed = true;
373
374 return 0;
375}
376
377int device_new_from_strv(sd_device **ret, char **strv) {
4afd3348 378 _cleanup_(sd_device_unrefp) sd_device *device = NULL;
57fa1d09
TG
379 char **key;
380 const char *major = NULL, *minor = NULL;
381 DeviceAction action = _DEVICE_ACTION_INVALID;
8d578a2e 382 uint64_t seqnum = 0;
57fa1d09
TG
383 int r;
384
385 assert(ret);
386 assert(strv);
387
388 r = device_new_aux(&device);
389 if (r < 0)
390 return r;
391
392 STRV_FOREACH(key, strv) {
393 r = device_append(device, *key, &major, &minor, &seqnum, &action);
394 if (r < 0)
395 return r;
396 }
397
398 if (major) {
399 r = device_set_devnum(device, major, minor);
400 if (r < 0)
c7d54dae 401 return log_device_debug_errno(device, r, "sd-device: Failed to set devnum %s:%s: %m", major, minor);
57fa1d09
TG
402 }
403
404 r = device_verify(device, action, seqnum);
405 if (r < 0)
406 return r;
407
1cc6c93a 408 *ret = TAKE_PTR(device);
57fa1d09
TG
409
410 return 0;
411}
412
413int device_new_from_nulstr(sd_device **ret, uint8_t *nulstr, size_t len) {
4afd3348 414 _cleanup_(sd_device_unrefp) sd_device *device = NULL;
57fa1d09
TG
415 const char *major = NULL, *minor = NULL;
416 DeviceAction action = _DEVICE_ACTION_INVALID;
8d578a2e 417 uint64_t seqnum = 0;
57fa1d09
TG
418 unsigned i = 0;
419 int r;
420
421 assert(ret);
422 assert(nulstr);
423 assert(len);
424
425 r = device_new_aux(&device);
426 if (r < 0)
427 return r;
428
429 while (i < len) {
430 char *key;
431 const char *end;
432
433 key = (char*)&nulstr[i];
434 end = memchr(key, '\0', len - i);
435 if (!end) {
c7d54dae 436 log_device_debug(device, "sd-device: Failed to parse nulstr");
57fa1d09
TG
437 return -EINVAL;
438 }
439 i += end - key + 1;
440
441 r = device_append(device, key, &major, &minor, &seqnum, &action);
442 if (r < 0)
443 return r;
444 }
445
446 if (major) {
447 r = device_set_devnum(device, major, minor);
448 if (r < 0)
c7d54dae 449 return log_device_debug_errno(device, r, "sd-device: Failed to set devnum %s:%s: %m", major, minor);
57fa1d09
TG
450 }
451
452 r = device_verify(device, action, seqnum);
453 if (r < 0)
454 return r;
455
1cc6c93a 456 *ret = TAKE_PTR(device);
57fa1d09
TG
457
458 return 0;
459}
460
461static int device_update_properties_bufs(sd_device *device) {
462 const char *val, *prop;
ccc1002a
TG
463 _cleanup_free_ char **buf_strv = NULL;
464 _cleanup_free_ uint8_t *buf_nulstr = NULL;
d854ba50 465 size_t allocated_nulstr = 0;
ccc1002a 466 size_t nulstr_len = 0, num = 0, i = 0;
57fa1d09
TG
467
468 assert(device);
469
aa20f49a
TG
470 if (!device->properties_buf_outdated)
471 return 0;
472
57fa1d09
TG
473 FOREACH_DEVICE_PROPERTY(device, prop, val) {
474 size_t len = 0;
475
476 len = strlen(prop) + 1 + strlen(val);
477
478 buf_nulstr = GREEDY_REALLOC0(buf_nulstr, allocated_nulstr, nulstr_len + len + 2);
479 if (!buf_nulstr)
480 return -ENOMEM;
481
57fa1d09
TG
482 strscpyl((char *)buf_nulstr + nulstr_len, len + 1, prop, "=", val, NULL);
483 nulstr_len += len + 1;
d854ba50 484 ++num;
57fa1d09
TG
485 }
486
ccc1002a
TG
487 /* build buf_strv from buf_nulstr */
488 buf_strv = new0(char *, num + 1);
489 if (!buf_strv)
490 return -ENOMEM;
d854ba50 491
d854ba50 492 NULSTR_FOREACH(val, (char*) buf_nulstr) {
ccc1002a 493 buf_strv[i] = (char *) val;
d854ba50
MP
494 assert(i < num);
495 i++;
496 }
57fa1d09 497
f9ecfd3b 498 free_and_replace(device->properties_nulstr, buf_nulstr);
ccc1002a 499 device->properties_nulstr_len = nulstr_len;
f9ecfd3b 500 free_and_replace(device->properties_strv, buf_strv);
ccc1002a 501
57fa1d09
TG
502 device->properties_buf_outdated = false;
503
504 return 0;
505}
506
507int device_get_properties_nulstr(sd_device *device, const uint8_t **nulstr, size_t *len) {
508 int r;
509
510 assert(device);
511 assert(nulstr);
512 assert(len);
513
aa20f49a
TG
514 r = device_update_properties_bufs(device);
515 if (r < 0)
516 return r;
57fa1d09
TG
517
518 *nulstr = device->properties_nulstr;
519 *len = device->properties_nulstr_len;
520
521 return 0;
522}
523
524int device_get_properties_strv(sd_device *device, char ***strv) {
525 int r;
526
527 assert(device);
528 assert(strv);
529
530 r = device_update_properties_bufs(device);
531 if (r < 0)
532 return r;
533
534 *strv = device->properties_strv;
535
536 return 0;
537}
538
539int device_get_devlink_priority(sd_device *device, int *priority) {
540 int r;
541
542 assert(device);
543 assert(priority);
544
545 r = device_read_db(device);
546 if (r < 0)
547 return r;
548
549 *priority = device->devlink_priority;
550
551 return 0;
552}
553
554int device_get_watch_handle(sd_device *device, int *handle) {
555 int r;
556
557 assert(device);
57fa1d09
TG
558
559 r = device_read_db(device);
560 if (r < 0)
561 return r;
562
dcfbde3a
YW
563 if (device->watch_handle < 0)
564 return -ENOENT;
565
78ffb476
YW
566 if (handle)
567 *handle = device->watch_handle;
57fa1d09
TG
568
569 return 0;
570}
571
572void device_set_watch_handle(sd_device *device, int handle) {
573 assert(device);
574
575 device->watch_handle = handle;
576}
577
578int device_rename(sd_device *device, const char *name) {
579 _cleanup_free_ char *dirname = NULL;
580 char *new_syspath;
581 const char *interface;
582 int r;
583
584 assert(device);
585 assert(name);
586
587 dirname = dirname_malloc(device->syspath);
588 if (!dirname)
589 return -ENOMEM;
590
591 new_syspath = strjoina(dirname, "/", name);
592
593 /* the user must trust that the new name is correct */
594 r = device_set_syspath(device, new_syspath, false);
595 if (r < 0)
596 return r;
597
598 r = sd_device_get_property_value(device, "INTERFACE", &interface);
599 if (r >= 0) {
f7b1c8d1
EV
600 /* like DEVPATH_OLD, INTERFACE_OLD is not saved to the db, but only stays around for the current event */
601 r = device_add_property_internal(device, "INTERFACE_OLD", interface);
57fa1d09
TG
602 if (r < 0)
603 return r;
604
f7b1c8d1 605 r = device_add_property_internal(device, "INTERFACE", name);
57fa1d09
TG
606 if (r < 0)
607 return r;
608 } else if (r != -ENOENT)
609 return r;
610
611 return 0;
612}
613
614int device_shallow_clone(sd_device *old_device, sd_device **new_device) {
4afd3348 615 _cleanup_(sd_device_unrefp) sd_device *ret = NULL;
57fa1d09
TG
616 int r;
617
618 assert(old_device);
619 assert(new_device);
620
621 r = device_new_aux(&ret);
622 if (r < 0)
623 return r;
624
625 r = device_set_syspath(ret, old_device->syspath, false);
626 if (r < 0)
627 return r;
628
629 r = device_set_subsystem(ret, old_device->subsystem);
630 if (r < 0)
631 return r;
632
633 ret->devnum = old_device->devnum;
634
1cc6c93a 635 *new_device = TAKE_PTR(ret);
57fa1d09
TG
636
637 return 0;
638}
639
640int device_clone_with_db(sd_device *old_device, sd_device **new_device) {
4afd3348 641 _cleanup_(sd_device_unrefp) sd_device *ret = NULL;
57fa1d09
TG
642 int r;
643
644 assert(old_device);
645 assert(new_device);
646
647 r = device_shallow_clone(old_device, &ret);
648 if (r < 0)
649 return r;
650
651 r = device_read_db(ret);
652 if (r < 0)
653 return r;
654
655 ret->sealed = true;
656
1cc6c93a 657 *new_device = TAKE_PTR(ret);
57fa1d09
TG
658
659 return 0;
660}
661
662int device_new_from_synthetic_event(sd_device **new_device, const char *syspath, const char *action) {
4afd3348 663 _cleanup_(sd_device_unrefp) sd_device *ret = NULL;
57fa1d09
TG
664 int r;
665
666 assert(new_device);
667 assert(syspath);
668 assert(action);
669
670 r = sd_device_new_from_syspath(&ret, syspath);
671 if (r < 0)
672 return r;
673
674 r = device_read_uevent_file(ret);
675 if (r < 0)
676 return r;
677
678 r = device_add_property_internal(ret, "ACTION", action);
679 if (r < 0)
680 return r;
681
1cc6c93a 682 *new_device = TAKE_PTR(ret);
57fa1d09
TG
683
684 return 0;
685}
686
ad5944d7
YW
687int device_new_from_stat_rdev(sd_device **ret, const struct stat *st) {
688 char type;
689
690 assert(ret);
691 assert(st);
692
693 if (S_ISBLK(st->st_mode))
694 type = 'b';
695 else if (S_ISCHR(st->st_mode))
696 type = 'c';
697 else
698 return -ENOTTY;
699
700 return sd_device_new_from_devnum(ret, type, st->st_rdev);
701}
702
57fa1d09
TG
703int device_copy_properties(sd_device *device_dst, sd_device *device_src) {
704 const char *property, *value;
a3ce8136 705 Iterator i;
57fa1d09
TG
706 int r;
707
708 assert(device_dst);
709 assert(device_src);
710
a3ce8136
YW
711 r = device_properties_prepare(device_src);
712 if (r < 0)
713 return r;
714
5ce41697 715 ORDERED_HASHMAP_FOREACH_KEY(value, property, device_src->properties_db, i) {
a3ce8136
YW
716 r = device_add_property_aux(device_dst, property, value, true);
717 if (r < 0)
718 return r;
719 }
720
5ce41697 721 ORDERED_HASHMAP_FOREACH_KEY(value, property, device_src->properties, i) {
a3ce8136 722 r = device_add_property_aux(device_dst, property, value, false);
57fa1d09
TG
723 if (r < 0)
724 return r;
725 }
726
727 return 0;
728}
729
730void device_cleanup_tags(sd_device *device) {
731 assert(device);
732
733 set_free_free(device->tags);
734 device->tags = NULL;
735 device->property_tags_outdated = true;
313cefa1 736 device->tags_generation++;
57fa1d09
TG
737}
738
739void device_cleanup_devlinks(sd_device *device) {
740 assert(device);
741
742 set_free_free(device->devlinks);
743 device->devlinks = NULL;
744 device->property_devlinks_outdated = true;
313cefa1 745 device->devlinks_generation++;
57fa1d09
TG
746}
747
748void device_remove_tag(sd_device *device, const char *tag) {
749 assert(device);
750 assert(tag);
751
752 free(set_remove(device->tags, tag));
753 device->property_tags_outdated = true;
313cefa1 754 device->tags_generation++;
57fa1d09
TG
755}
756
757static int device_tag(sd_device *device, const char *tag, bool add) {
758 const char *id;
759 char *path;
760 int r;
761
762 assert(device);
763 assert(tag);
764
765 r = device_get_id_filename(device, &id);
766 if (r < 0)
767 return r;
768
769 path = strjoina("/run/udev/tags/", tag, "/", id);
770
771 if (add) {
772 r = touch_file(path, true, USEC_INFINITY, UID_INVALID, GID_INVALID, 0444);
773 if (r < 0)
774 return r;
775 } else {
776 r = unlink(path);
777 if (r < 0 && errno != ENOENT)
778 return -errno;
779 }
780
781 return 0;
782}
783
784int device_tag_index(sd_device *device, sd_device *device_old, bool add) {
785 const char *tag;
786 int r = 0, k;
787
788 if (add && device_old) {
789 /* delete possible left-over tags */
790 FOREACH_DEVICE_TAG(device_old, tag) {
791 if (!sd_device_has_tag(device, tag)) {
792 k = device_tag(device_old, tag, false);
793 if (r >= 0 && k < 0)
794 r = k;
795 }
796 }
797 }
798
799 FOREACH_DEVICE_TAG(device, tag) {
800 k = device_tag(device, tag, add);
801 if (r >= 0 && k < 0)
802 r = k;
803 }
804
805 return r;
806}
807
808static bool device_has_info(sd_device *device) {
809 assert(device);
810
811 if (!set_isempty(device->devlinks))
812 return true;
813
814 if (device->devlink_priority != 0)
815 return true;
816
817 if (!ordered_hashmap_isempty(device->properties_db))
818 return true;
819
820 if (!set_isempty(device->tags))
821 return true;
822
823 if (device->watch_handle >= 0)
824 return true;
825
826 return false;
827}
828
829void device_set_db_persist(sd_device *device) {
830 assert(device);
831
832 device->db_persist = true;
833}
834
835int device_update_db(sd_device *device) {
836 const char *id;
837 char *path;
838 _cleanup_fclose_ FILE *f = NULL;
839 _cleanup_free_ char *path_tmp = NULL;
840 bool has_info;
841 int r;
842
843 assert(device);
844
845 has_info = device_has_info(device);
846
847 r = device_get_id_filename(device, &id);
848 if (r < 0)
849 return r;
850
851 path = strjoina("/run/udev/data/", id);
852
853 /* do not store anything for otherwise empty devices */
854 if (!has_info && major(device->devnum) == 0 && device->ifindex == 0) {
855 r = unlink(path);
856 if (r < 0 && errno != ENOENT)
857 return -errno;
858
859 return 0;
860 }
861
862 /* write a database file */
863 r = mkdir_parents(path, 0755);
864 if (r < 0)
865 return r;
866
867 r = fopen_temporary(path, &f, &path_tmp);
868 if (r < 0)
869 return r;
870
871 /*
872 * set 'sticky' bit to indicate that we should not clean the
873 * database when we transition from initramfs to the real root
874 */
875 if (device->db_persist) {
876 r = fchmod(fileno(f), 01644);
877 if (r < 0) {
878 r = -errno;
879 goto fail;
880 }
881 } else {
882 r = fchmod(fileno(f), 0644);
883 if (r < 0) {
884 r = -errno;
885 goto fail;
886 }
887 }
888
889 if (has_info) {
890 const char *property, *value, *tag;
891 Iterator i;
892
893 if (major(device->devnum) > 0) {
894 const char *devlink;
895
896 FOREACH_DEVICE_DEVLINK(device, devlink)
fbd0b64f 897 fprintf(f, "S:%s\n", devlink + STRLEN("/dev/"));
57fa1d09
TG
898
899 if (device->devlink_priority != 0)
900 fprintf(f, "L:%i\n", device->devlink_priority);
901
902 if (device->watch_handle >= 0)
903 fprintf(f, "W:%i\n", device->watch_handle);
904 }
905
906 if (device->usec_initialized > 0)
907 fprintf(f, "I:"USEC_FMT"\n", device->usec_initialized);
908
909 ORDERED_HASHMAP_FOREACH_KEY(value, property, device->properties_db, i)
910 fprintf(f, "E:%s=%s\n", property, value);
911
912 FOREACH_DEVICE_TAG(device, tag)
913 fprintf(f, "G:%s\n", tag);
914 }
915
916 r = fflush_and_check(f);
917 if (r < 0)
918 goto fail;
919
920 r = rename(path_tmp, path);
921 if (r < 0) {
922 r = -errno;
923 goto fail;
924 }
925
c7d54dae
YW
926 log_device_debug(device, "sd-device: Created %s file '%s' for '%s'", has_info ? "db" : "empty",
927 path, device->devpath);
57fa1d09
TG
928
929 return 0;
930
931fail:
dacd6cee
LP
932 (void) unlink(path);
933 (void) unlink(path_tmp);
57fa1d09 934
c7d54dae 935 return log_device_debug_errno(device, r, "sd-device: Failed to create %s file '%s' for '%s'", has_info ? "db" : "empty", path, device->devpath);
57fa1d09
TG
936}
937
938int device_delete_db(sd_device *device) {
939 const char *id;
940 char *path;
941 int r;
942
943 assert(device);
944
945 r = device_get_id_filename(device, &id);
946 if (r < 0)
947 return r;
948
949 path = strjoina("/run/udev/data/", id);
950
951 r = unlink(path);
952 if (r < 0 && errno != ENOENT)
953 return -errno;
954
955 return 0;
956}