]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/libsystemd/sd-device/sd-device.c
libsystemd: use _NEG_ macros, adjust some comments
[thirdparty/systemd.git] / src / libsystemd / sd-device / sd-device.c
CommitLineData
db9ecf05 1/* SPDX-License-Identifier: LGPL-2.1-or-later */
57fa1d09
TG
2
3#include <ctype.h>
57fa1d09 4#include <net/if.h>
ef118d00 5#include <sys/ioctl.h>
07630cea 6#include <sys/types.h>
57fa1d09 7
07630cea
LP
8#include "sd-device.h"
9
b5efdb8a 10#include "alloc-util.h"
f461a28d 11#include "chase.h"
07630cea
LP
12#include "device-internal.h"
13#include "device-private.h"
14#include "device-util.h"
7176f06c 15#include "devnum-util.h"
8fb3f009 16#include "dirent-util.h"
a7910612 17#include "env-util.h"
3ffd4af2 18#include "fd-util.h"
57fa1d09 19#include "fileio.h"
bd44a727 20#include "format-util.h"
f4f15635 21#include "fs-util.h"
57fa1d09 22#include "hashmap.h"
b485fd93 23#include "id128-util.h"
07630cea 24#include "macro.h"
a7910612 25#include "missing_magic.h"
bd44a727 26#include "netlink-util.h"
6bedfcbb 27#include "parse-util.h"
07630cea 28#include "path-util.h"
57fa1d09 29#include "set.h"
429b4350 30#include "socket-util.h"
dc5042c0 31#include "stdio-util.h"
07630cea 32#include "string-util.h"
57fa1d09 33#include "strv.h"
07630cea 34#include "strxcpyx.h"
f5fbe71d 35#include "user-util.h"
57fa1d09
TG
36
37int device_new_aux(sd_device **ret) {
2cfb1978 38 sd_device *device;
57fa1d09
TG
39
40 assert(ret);
41
6116d2b2 42 device = new(sd_device, 1);
57fa1d09
TG
43 if (!device)
44 return -ENOMEM;
45
6116d2b2
YW
46 *device = (sd_device) {
47 .n_ref = 1,
f5fbe71d
YW
48 .devmode = MODE_INVALID,
49 .devuid = UID_INVALID,
50 .devgid = GID_INVALID,
a1130022 51 .action = _SD_DEVICE_ACTION_INVALID,
6116d2b2 52 };
57fa1d09
TG
53
54 *ret = device;
57fa1d09
TG
55 return 0;
56}
57
8301aa0b
YW
58static sd_device *device_free(sd_device *device) {
59 assert(device);
57fa1d09 60
8301aa0b
YW
61 sd_device_unref(device->parent);
62 free(device->syspath);
63 free(device->sysname);
64 free(device->devtype);
65 free(device->devname);
66 free(device->subsystem);
67 free(device->driver_subsystem);
68 free(device->driver);
fe732381 69 free(device->device_id);
8301aa0b
YW
70 free(device->properties_strv);
71 free(device->properties_nulstr);
72
dd75bbee
YW
73 ordered_hashmap_free(device->properties);
74 ordered_hashmap_free(device->properties_db);
61c0972d 75 hashmap_free(device->sysattr_values);
be327321 76 set_free(device->sysattrs);
e77b146f
LP
77 set_free(device->all_tags);
78 set_free(device->current_tags);
be327321 79 set_free(device->devlinks);
ec9b4f2b 80 hashmap_free(device->children);
8301aa0b
YW
81
82 return mfree(device);
57fa1d09
TG
83}
84
8301aa0b 85DEFINE_PUBLIC_TRIVIAL_REF_UNREF_FUNC(sd_device, sd_device, device_free);
57fa1d09 86
4f1ef7f9 87int device_add_property_aux(sd_device *device, const char *key, const char *value, bool db) {
57fa1d09
TG
88 OrderedHashmap **properties;
89
90 assert(device);
4f1ef7f9 91 assert(key);
57fa1d09
TG
92
93 if (db)
94 properties = &device->properties_db;
95 else
96 properties = &device->properties;
97
4f1ef7f9 98 if (value) {
d7ac0952
FS
99 _unused_ _cleanup_free_ char *old_value = NULL;
100 _cleanup_free_ char *new_key = NULL, *new_value = NULL, *old_key = NULL;
57fa1d09
TG
101 int r;
102
dd75bbee 103 r = ordered_hashmap_ensure_allocated(properties, &string_hash_ops_free_free);
57fa1d09
TG
104 if (r < 0)
105 return r;
106
4f1ef7f9
YW
107 new_key = strdup(key);
108 if (!new_key)
57fa1d09
TG
109 return -ENOMEM;
110
4f1ef7f9
YW
111 new_value = strdup(value);
112 if (!new_value)
57fa1d09
TG
113 return -ENOMEM;
114
115 old_value = ordered_hashmap_get2(*properties, key, (void**) &old_key);
116
327379f5 117 /* ordered_hashmap_replace() does not fail when the hashmap already has the entry. */
4f1ef7f9 118 r = ordered_hashmap_replace(*properties, new_key, new_value);
57fa1d09
TG
119 if (r < 0)
120 return r;
121
4f1ef7f9
YW
122 TAKE_PTR(new_key);
123 TAKE_PTR(new_value);
57fa1d09 124 } else {
d7ac0952
FS
125 _unused_ _cleanup_free_ char *old_value = NULL;
126 _cleanup_free_ char *old_key = NULL;
57fa1d09 127
4f1ef7f9 128 old_value = ordered_hashmap_remove2(*properties, key, (void**) &old_key);
57fa1d09
TG
129 }
130
131 if (!db) {
313cefa1 132 device->properties_generation++;
57fa1d09
TG
133 device->properties_buf_outdated = true;
134 }
135
136 return 0;
137}
138
57fa1d09
TG
139int device_set_syspath(sd_device *device, const char *_syspath, bool verify) {
140 _cleanup_free_ char *syspath = NULL;
141 const char *devpath;
142 int r;
143
144 assert(device);
145 assert(_syspath);
146
57fa1d09 147 if (verify) {
254d1313 148 _cleanup_close_ int fd = -EBADF;
1793bb61 149
95c250be
YW
150 /* The input path maybe a symlink located outside of /sys. Let's try to chase the symlink at first.
151 * The primary usecase is that e.g. /proc/device-tree is a symlink to /sys/firmware/devicetree/base.
152 * By chasing symlinks in the path at first, we can call sd_device_new_from_path() with such path. */
f461a28d 153 r = chase(_syspath, NULL, 0, &syspath, &fd);
08232a02 154 if (r == -ENOENT)
d7cb60da
YW
155 /* the device does not exist (any more?) */
156 return log_debug_errno(SYNTHETIC_ERRNO(ENODEV),
157 "sd-device: Failed to chase symlinks in \"%s\".", _syspath);
d89e0dc8 158 if (r < 0)
c7d54dae 159 return log_debug_errno(r, "sd-device: Failed to get target of '%s': %m", _syspath);
57fa1d09 160
2e1ec12e
YW
161 if (!path_startswith(syspath, "/sys")) {
162 _cleanup_free_ char *real_sys = NULL, *new_syspath = NULL;
163 char *p;
164
165 /* /sys is a symlink to somewhere sysfs is mounted on? In that case, we convert the path to real sysfs to "/sys". */
f461a28d 166 r = chase("/sys", NULL, 0, &real_sys, NULL);
2e1ec12e 167 if (r < 0)
c7d54dae 168 return log_debug_errno(r, "sd-device: Failed to chase symlink /sys: %m");
2e1ec12e
YW
169
170 p = path_startswith(syspath, real_sys);
baaa35ad
ZJS
171 if (!p)
172 return log_debug_errno(SYNTHETIC_ERRNO(ENODEV),
173 "sd-device: Canonicalized path '%s' does not starts with sysfs mount point '%s'",
174 syspath, real_sys);
2e1ec12e 175
657ee2d8 176 new_syspath = path_join("/sys", p);
2e1ec12e 177 if (!new_syspath)
d7cb60da 178 return log_oom_debug();
2e1ec12e
YW
179
180 free_and_replace(syspath, new_syspath);
4ff361cc 181 path_simplify(syspath);
2e1ec12e
YW
182 }
183
1793bb61
LP
184 if (path_startswith(syspath, "/sys/devices/")) {
185 /* For proper devices, stricter rules apply: they must have a 'uevent' file,
186 * otherwise we won't allow them */
57fa1d09 187
1793bb61 188 if (faccessat(fd, "uevent", F_OK, 0) < 0) {
52d62901 189 if (errno == ENOENT)
1793bb61
LP
190 /* This is not a valid device. Note, this condition is quite often
191 * satisfied when enumerating devices or finding a parent device.
29fafedd
YW
192 * Hence, use log_trace_errno() here. */
193 return log_trace_errno(SYNTHETIC_ERRNO(ENODEV),
1793bb61 194 "sd-device: the uevent file \"%s/uevent\" does not exist.", syspath);
5cf0ee31
LP
195 if (errno == ENOTDIR)
196 /* Not actually a directory. */
197 return log_debug_errno(SYNTHETIC_ERRNO(ENODEV),
198 "sd-device: the syspath \"%s\" is not a directory.", syspath);
52d62901 199
1793bb61 200 return log_debug_errno(errno, "sd-device: cannot find uevent file for %s: %m", syspath);
57fa1d09
TG
201 }
202 } else {
1793bb61
LP
203 struct stat st;
204
205 /* For everything else lax rules apply: they just need to be a directory */
206
207 if (fstat(fd, &st) < 0)
208 return log_debug_errno(errno, "sd-device: failed to check if syspath \"%s\" is a directory: %m", syspath);
209 if (!S_ISDIR(st.st_mode))
d7cb60da
YW
210 return log_debug_errno(SYNTHETIC_ERRNO(ENODEV),
211 "sd-device: the syspath \"%s\" is not a directory.", syspath);
57fa1d09 212 }
a7910612
LP
213
214 /* Only operate on sysfs, i.e. refuse going down into /sys/fs/cgroup/ or similar places where
215 * things are not arranged as kobjects in kernel, and hence don't necessarily have
216 * kobject/attribute structure. */
217 r = getenv_bool_secure("SYSTEMD_DEVICE_VERIFY_SYSFS");
218 if (r < 0 && r != -ENXIO)
219 log_debug_errno(r, "Failed to parse $SYSTEMD_DEVICE_VERIFY_SYSFS value: %m");
220 if (r != 0) {
221 r = fd_is_fs_type(fd, SYSFS_MAGIC);
222 if (r < 0)
223 return log_debug_errno(r, "sd-device: failed to check if syspath \"%s\" is backed by sysfs.", syspath);
224 if (r == 0)
225 return log_debug_errno(SYNTHETIC_ERRNO(ENODEV),
226 "sd-device: the syspath \"%s\" is outside of sysfs, refusing.", syspath);
227 }
57fa1d09 228 } else {
95c250be
YW
229 /* must be a subdirectory of /sys */
230 if (!path_startswith(_syspath, "/sys/"))
231 return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
232 "sd-device: Syspath '%s' is not a subdirectory of /sys",
233 _syspath);
234
57fa1d09
TG
235 syspath = strdup(_syspath);
236 if (!syspath)
d7cb60da 237 return log_oom_debug();
57fa1d09 238
4d960d0b
LP
239 path_simplify(syspath);
240 }
57fa1d09 241
4d960d0b 242 assert_se(devpath = startswith(syspath, "/sys"));
9dcde103 243 if (devpath[0] != '/')
d7cb60da 244 return log_debug_errno(SYNTHETIC_ERRNO(ENODEV), "sd-device: \"/sys\" alone is not a valid device path.");
c4f885e7 245
57fa1d09
TG
246 r = device_add_property_internal(device, "DEVPATH", devpath);
247 if (r < 0)
d7cb60da 248 return log_debug_errno(r, "sd-device: Failed to add \"DEVPATH\" property for device \"%s\": %m", syspath);
57fa1d09 249
f9ecfd3b 250 free_and_replace(device->syspath, syspath);
57fa1d09 251 device->devpath = devpath;
9a26098e
YW
252
253 /* Unset sysname and sysnum, they will be assigned when requested. */
254 device->sysnum = NULL;
255 device->sysname = mfree(device->sysname);
57fa1d09
TG
256 return 0;
257}
258
95c250be 259static int device_new_from_syspath(sd_device **ret, const char *syspath, bool strict) {
4afd3348 260 _cleanup_(sd_device_unrefp) sd_device *device = NULL;
57fa1d09
TG
261 int r;
262
263 assert_return(ret, -EINVAL);
264 assert_return(syspath, -EINVAL);
265
95c250be
YW
266 if (strict && !path_startswith(syspath, "/sys/"))
267 return -EINVAL;
268
57fa1d09
TG
269 r = device_new_aux(&device);
270 if (r < 0)
271 return r;
272
be247835 273 r = device_set_syspath(device, syspath, /* verify= */ true);
57fa1d09
TG
274 if (r < 0)
275 return r;
276
1cc6c93a 277 *ret = TAKE_PTR(device);
57fa1d09
TG
278 return 0;
279}
280
95c250be
YW
281_public_ int sd_device_new_from_syspath(sd_device **ret, const char *syspath) {
282 return device_new_from_syspath(ret, syspath, /* strict = */ true);
283}
284
f79fdea6 285int device_new_from_mode_and_devnum(sd_device **ret, mode_t mode, dev_t devnum) {
a46f9cd0
YW
286 _cleanup_(sd_device_unrefp) sd_device *dev = NULL;
287 _cleanup_free_ char *syspath = NULL;
23d3dfc3 288 const char *t, *subsystem = NULL;
a46f9cd0
YW
289 dev_t n;
290 int r;
57fa1d09 291
a46f9cd0
YW
292 assert(ret);
293
294 if (S_ISCHR(mode))
295 t = "char";
296 else if (S_ISBLK(mode))
297 t = "block";
298 else
299 return -ENOTTY;
300
301 if (major(devnum) == 0)
302 return -ENODEV;
303
67458536 304 if (asprintf(&syspath, "/sys/dev/%s/" DEVNUM_FORMAT_STR, t, DEVNUM_FORMAT_VAL(devnum)) < 0)
a46f9cd0 305 return -ENOMEM;
57fa1d09 306
a46f9cd0
YW
307 r = sd_device_new_from_syspath(&dev, syspath);
308 if (r < 0)
309 return r;
f1ad2c92 310
a46f9cd0
YW
311 r = sd_device_get_devnum(dev, &n);
312 if (r == -ENOENT)
313 return -ENXIO;
314 if (r < 0)
315 return r;
316 if (n != devnum)
317 return -ENXIO;
57fa1d09 318
a46f9cd0
YW
319 r = sd_device_get_subsystem(dev, &subsystem);
320 if (r < 0 && r != -ENOENT)
321 return r;
23d3dfc3 322 if (streq_ptr(subsystem, "block") != !!S_ISBLK(mode))
a46f9cd0 323 return -ENXIO;
57fa1d09 324
a46f9cd0
YW
325 *ret = TAKE_PTR(dev);
326 return 0;
327}
328
329_public_ int sd_device_new_from_devnum(sd_device **ret, char type, dev_t devnum) {
330 assert_return(ret, -EINVAL);
331 assert_return(IN_SET(type, 'b', 'c'), -EINVAL);
332
333 return device_new_from_mode_and_devnum(ret, type == 'b' ? S_IFBLK : S_IFCHR, devnum);
57fa1d09
TG
334}
335
bd44a727
YW
336static int device_new_from_main_ifname(sd_device **ret, const char *ifname) {
337 const char *syspath;
338
339 assert(ret);
340 assert(ifname);
341
342 syspath = strjoina("/sys/class/net/", ifname);
343 return sd_device_new_from_syspath(ret, syspath);
344}
345
346_public_ int sd_device_new_from_ifname(sd_device **ret, const char *ifname) {
347 _cleanup_free_ char *main_name = NULL;
348 int r;
349
350 assert_return(ret, -EINVAL);
351 assert_return(ifname, -EINVAL);
352
353 r = parse_ifindex(ifname);
354 if (r > 0)
355 return sd_device_new_from_ifindex(ret, r);
356
357 if (ifname_valid(ifname)) {
358 r = device_new_from_main_ifname(ret, ifname);
359 if (r >= 0)
360 return r;
361 }
362
363 r = rtnl_resolve_link_alternative_name(NULL, ifname, &main_name);
364 if (r < 0)
365 return r;
366
367 return device_new_from_main_ifname(ret, main_name);
368}
369
370_public_ int sd_device_new_from_ifindex(sd_device **ret, int ifindex) {
a46f9cd0 371 _cleanup_(sd_device_unrefp) sd_device *dev = NULL;
01afd0f7 372 char ifname[IF_NAMESIZE];
a46f9cd0 373 int r, i;
bd44a727
YW
374
375 assert_return(ret, -EINVAL);
376 assert_return(ifindex > 0, -EINVAL);
377
01afd0f7 378 if (format_ifname(ifindex, ifname) < 0)
bd44a727
YW
379 return -ENODEV;
380
a46f9cd0
YW
381 r = device_new_from_main_ifname(&dev, ifname);
382 if (r < 0)
383 return r;
384
385 r = sd_device_get_ifindex(dev, &i);
386 if (r == -ENOENT)
387 return -ENXIO;
388 if (r < 0)
389 return r;
390 if (i != ifindex)
391 return -ENXIO;
392
393 *ret = TAKE_PTR(dev);
394 return 0;
bd44a727
YW
395}
396
f5e77597
LP
397static int device_strjoin_new(
398 const char *a,
399 const char *b,
400 const char *c,
401 const char *d,
402 sd_device **ret) {
403
404 const char *p;
405 int r;
406
407 p = strjoina(a, b, c, d);
408 if (access(p, F_OK) < 0)
409 return IN_SET(errno, ENOENT, ENAMETOOLONG) ? 0 : -errno; /* If this sysfs is too long then it doesn't exist either */
410
411 r = sd_device_new_from_syspath(ret, p);
412 if (r < 0)
413 return r;
414
415 return 1;
416}
417
418_public_ int sd_device_new_from_subsystem_sysname(
419 sd_device **ret,
420 const char *subsystem,
421 const char *sysname) {
422
f5e77597
LP
423 char *name;
424 int r;
57fa1d09
TG
425
426 assert_return(ret, -EINVAL);
f5e77597
LP
427 assert_return(path_is_normalized(subsystem), -EINVAL);
428 assert_return(path_is_normalized(sysname), -EINVAL);
57fa1d09 429
4d38294b
YW
430 /* translate sysname back to sysfs filename */
431 name = strdupa_safe(sysname);
432 string_replace_char(name, '/', '!');
433
57fa1d09 434 if (streq(subsystem, "subsystem")) {
a43129b0 435 FOREACH_STRING(s, "/sys/bus/", "/sys/class/") {
4d38294b 436 r = device_strjoin_new(s, name, NULL, NULL, ret);
f5e77597
LP
437 if (r < 0)
438 return r;
439 if (r > 0)
440 return 0;
441 }
e17c95af 442
4d38294b
YW
443 } else if (streq(subsystem, "module")) {
444 r = device_strjoin_new("/sys/module/", name, NULL, NULL, ret);
f5e77597
LP
445 if (r < 0)
446 return r;
447 if (r > 0)
448 return 0;
e17c95af 449
57fa1d09 450 } else if (streq(subsystem, "drivers")) {
f5e77597 451 const char *sep;
57fa1d09 452
4d38294b 453 sep = strchr(name, ':');
52a89a5f 454 if (sep && sep[1] != '\0') { /* Require ":" and something non-empty after that. */
57fa1d09 455
4d38294b 456 const char *subsys = memdupa_suffix0(name, sep - name);
f5e77597
LP
457 sep++;
458
0b859c97
LP
459 if (streq(sep, "drivers")) /* If the sysname is "drivers", then it's the drivers directory itself that is meant. */
460 r = device_strjoin_new("/sys/bus/", subsys, "/drivers", NULL, ret);
461 else
462 r = device_strjoin_new("/sys/bus/", subsys, "/drivers/", sep, ret);
a43129b0
YW
463 if (r < 0)
464 return r;
465 if (r > 0)
466 return 0;
21d6220f
TG
467 }
468 }
a9ec9f29 469
a43129b0
YW
470 r = device_strjoin_new("/sys/bus/", subsystem, "/devices/", name, ret);
471 if (r < 0)
472 return r;
473 if (r > 0)
474 return 0;
57fa1d09 475
f5e77597
LP
476 r = device_strjoin_new("/sys/class/", subsystem, "/", name, ret);
477 if (r < 0)
478 return r;
479 if (r > 0)
480 return 0;
57fa1d09 481
4d38294b 482 r = device_strjoin_new("/sys/firmware/", subsystem, "/", name, ret);
f5e77597
LP
483 if (r < 0)
484 return r;
485 if (r > 0)
486 return 0;
a918b673 487
08232a02 488 return -ENODEV;
57fa1d09
TG
489}
490
a1130022 491_public_ int sd_device_new_from_stat_rdev(sd_device **ret, const struct stat *st) {
a1130022
LP
492 assert_return(ret, -EINVAL);
493 assert_return(st, -EINVAL);
494
a46f9cd0 495 return device_new_from_mode_and_devnum(ret, st->st_mode, st->st_rdev);
a1130022
LP
496}
497
e418f965
YW
498_public_ int sd_device_new_from_devname(sd_device **ret, const char *devname) {
499 struct stat st;
a46f9cd0
YW
500 dev_t devnum;
501 mode_t mode;
e418f965
YW
502
503 assert_return(ret, -EINVAL);
504 assert_return(devname, -EINVAL);
505
506 /* This function actually accepts both devlinks and devnames, i.e. both symlinks and device
507 * nodes below /dev/. */
508
509 /* Also ignore when the specified path is "/dev". */
510 if (isempty(path_startswith(devname, "/dev")))
511 return -EINVAL;
512
a46f9cd0 513 if (device_path_parse_major_minor(devname, &mode, &devnum) >= 0)
e418f965 514 /* Let's shortcut when "/dev/block/maj:min" or "/dev/char/maj:min" is specified.
a46f9cd0 515 * In that case, we can directly convert the path to syspath, hence it is not necessary
e418f965 516 * that the specified path exists. So, this works fine without udevd being running. */
a46f9cd0 517 return device_new_from_mode_and_devnum(ret, mode, devnum);
e418f965
YW
518
519 if (stat(devname, &st) < 0)
520 return ERRNO_IS_DEVICE_ABSENT(errno) ? -ENODEV : -errno;
521
522 return sd_device_new_from_stat_rdev(ret, &st);
523}
524
525_public_ int sd_device_new_from_path(sd_device **ret, const char *path) {
526 assert_return(ret, -EINVAL);
527 assert_return(path, -EINVAL);
528
529 if (path_startswith(path, "/dev"))
530 return sd_device_new_from_devname(ret, path);
531
95c250be 532 return device_new_from_syspath(ret, path, /* strict = */ false);
e418f965
YW
533}
534
fe20121a
ZJS
535int device_set_devtype(sd_device *device, const char *devtype) {
536 _cleanup_free_ char *t = NULL;
57fa1d09
TG
537 int r;
538
539 assert(device);
fe20121a 540 assert(devtype);
57fa1d09 541
fe20121a
ZJS
542 t = strdup(devtype);
543 if (!t)
57fa1d09
TG
544 return -ENOMEM;
545
fe20121a 546 r = device_add_property_internal(device, "DEVTYPE", t);
57fa1d09
TG
547 if (r < 0)
548 return r;
549
fe20121a 550 return free_and_replace(device->devtype, t);
57fa1d09
TG
551}
552
597da51b
ZJS
553int device_set_ifindex(sd_device *device, const char *name) {
554 int r, ifindex;
57fa1d09
TG
555
556 assert(device);
597da51b 557 assert(name);
57fa1d09 558
597da51b
ZJS
559 ifindex = parse_ifindex(name);
560 if (ifindex < 0)
561 return ifindex;
57fa1d09 562
597da51b 563 r = device_add_property_internal(device, "IFINDEX", name);
57fa1d09
TG
564 if (r < 0)
565 return r;
566
567 device->ifindex = ifindex;
568
569 return 0;
570}
571
70e45108
YW
572static int mangle_devname(const char *p, char **ret) {
573 char *q;
574
575 assert(p);
576 assert(ret);
577
578 if (!path_is_safe(p))
579 return -EINVAL;
580
581 /* When the path is absolute, it must start with "/dev/", but ignore "/dev/" itself. */
582 if (path_is_absolute(p)) {
583 if (isempty(path_startswith(p, "/dev/")))
584 return -EINVAL;
585
586 q = strdup(p);
587 } else
588 q = path_join("/dev/", p);
589 if (!q)
590 return -ENOMEM;
591
592 path_simplify(q);
593
594 *ret = q;
595 return 0;
596}
597
fe20121a
ZJS
598int device_set_devname(sd_device *device, const char *devname) {
599 _cleanup_free_ char *t = NULL;
57fa1d09
TG
600 int r;
601
602 assert(device);
fe20121a 603 assert(devname);
57fa1d09 604
70e45108
YW
605 r = mangle_devname(devname, &t);
606 if (r < 0)
607 return r;
57fa1d09 608
fe20121a 609 r = device_add_property_internal(device, "DEVNAME", t);
57fa1d09
TG
610 if (r < 0)
611 return r;
612
fe20121a 613 return free_and_replace(device->devname, t);
57fa1d09
TG
614}
615
616int device_set_devmode(sd_device *device, const char *_devmode) {
617 unsigned devmode;
618 int r;
619
620 assert(device);
621 assert(_devmode);
622
623 r = safe_atou(_devmode, &devmode);
624 if (r < 0)
625 return r;
626
627 if (devmode > 07777)
628 return -EINVAL;
629
630 r = device_add_property_internal(device, "DEVMODE", _devmode);
631 if (r < 0)
632 return r;
633
634 device->devmode = devmode;
635
636 return 0;
637}
638
639int device_set_devnum(sd_device *device, const char *major, const char *minor) {
49f23693 640 unsigned maj, min = 0;
57fa1d09
TG
641 int r;
642
643 assert(device);
644 assert(major);
645
646 r = safe_atou(major, &maj);
647 if (r < 0)
648 return r;
49f23693 649 if (maj == 0)
57fa1d09 650 return 0;
9d41c62f
LP
651 if (!DEVICE_MAJOR_VALID(maj))
652 return -EINVAL;
57fa1d09
TG
653
654 if (minor) {
655 r = safe_atou(minor, &min);
656 if (r < 0)
657 return r;
9d41c62f
LP
658 if (!DEVICE_MINOR_VALID(min))
659 return -EINVAL;
57fa1d09
TG
660 }
661
662 r = device_add_property_internal(device, "MAJOR", major);
663 if (r < 0)
664 return r;
665
666 if (minor) {
667 r = device_add_property_internal(device, "MINOR", minor);
668 if (r < 0)
669 return r;
670 }
671
672 device->devnum = makedev(maj, min);
673
674 return 0;
675}
676
1ce0d040
LP
677int device_set_diskseq(sd_device *device, const char *str) {
678 uint64_t diskseq;
679 int r;
680
681 assert(device);
682 assert(str);
683
684 r = safe_atou64(str, &diskseq);
685 if (r < 0)
686 return r;
687 if (diskseq == 0)
688 return -EINVAL;
689
690 r = device_add_property_internal(device, "DISKSEQ", str);
691 if (r < 0)
692 return r;
693
694 device->diskseq = diskseq;
695
696 return 0;
697}
698
699static int handle_uevent_line(
700 sd_device *device,
701 const char *key,
702 const char *value,
703 const char **major,
704 const char **minor) {
57fa1d09
TG
705
706 assert(device);
707 assert(key);
708 assert(value);
709 assert(major);
710 assert(minor);
711
08405125
YW
712 if (streq(key, "DEVTYPE"))
713 return device_set_devtype(device, value);
714 if (streq(key, "IFINDEX"))
715 return device_set_ifindex(device, value);
716 if (streq(key, "DEVNAME"))
717 return device_set_devname(device, value);
718 if (streq(key, "DEVMODE"))
719 return device_set_devmode(device, value);
720 if (streq(key, "DISKSEQ"))
721 return device_set_diskseq(device, value);
722 if (streq(key, "MAJOR"))
57fa1d09
TG
723 *major = value;
724 else if (streq(key, "MINOR"))
725 *minor = value;
08405125
YW
726 else
727 return device_add_property_internal(device, key, value);
57fa1d09
TG
728
729 return 0;
730}
731
732int device_read_uevent_file(sd_device *device) {
733 _cleanup_free_ char *uevent = NULL;
97c94b98 734 const char *syspath, *key = NULL, *value = NULL, *major = NULL, *minor = NULL;
57fa1d09
TG
735 char *path;
736 size_t uevent_len;
57fa1d09
TG
737 int r;
738
739 enum {
740 PRE_KEY,
741 KEY,
742 PRE_VALUE,
743 VALUE,
744 INVALID_LINE,
745 } state = PRE_KEY;
746
747 assert(device);
748
749 if (device->uevent_loaded || device->sealed)
750 return 0;
751
752 r = sd_device_get_syspath(device, &syspath);
753 if (r < 0)
754 return r;
755
c7d6ebb1
YW
756 device->uevent_loaded = true;
757
57fa1d09
TG
758 path = strjoina(syspath, "/uevent");
759
627055ce 760 r = read_full_virtual_file(path, &uevent, &uevent_len);
c5ed77b2 761 if (r == -EACCES || ERRNO_IS_NEG_DEVICE_ABSENT(r))
17761fb3
YW
762 /* The uevent files may be write-only, the device may be already removed, or the device
763 * may not have the uevent file. */
c5ed77b2
ZJS
764 return 0;
765 if (r < 0)
c7d54dae 766 return log_device_debug_errno(device, r, "sd-device: Failed to read uevent file '%s': %m", path);
57fa1d09 767
462035d5 768 for (size_t i = 0; i < uevent_len; i++)
57fa1d09
TG
769 switch (state) {
770 case PRE_KEY:
771 if (!strchr(NEWLINE, uevent[i])) {
772 key = &uevent[i];
773
774 state = KEY;
775 }
776
777 break;
778 case KEY:
779 if (uevent[i] == '=') {
780 uevent[i] = '\0';
781
782 state = PRE_VALUE;
783 } else if (strchr(NEWLINE, uevent[i])) {
784 uevent[i] = '\0';
c7d54dae 785 log_device_debug(device, "sd-device: Invalid uevent line '%s', ignoring", key);
57fa1d09
TG
786
787 state = PRE_KEY;
788 }
789
790 break;
791 case PRE_VALUE:
792 value = &uevent[i];
57fa1d09
TG
793 state = VALUE;
794
4831981d 795 _fallthrough_; /* to handle empty property */
57fa1d09
TG
796 case VALUE:
797 if (strchr(NEWLINE, uevent[i])) {
798 uevent[i] = '\0';
799
800 r = handle_uevent_line(device, key, value, &major, &minor);
801 if (r < 0)
c7d54dae 802 log_device_debug_errno(device, r, "sd-device: Failed to handle uevent entry '%s=%s', ignoring: %m", key, value);
57fa1d09
TG
803
804 state = PRE_KEY;
805 }
806
807 break;
808 default:
04499a70 809 assert_not_reached();
57fa1d09 810 }
57fa1d09
TG
811
812 if (major) {
813 r = device_set_devnum(device, major, minor);
814 if (r < 0)
9a81972b 815 log_device_debug_errno(device, r, "sd-device: Failed to set 'MAJOR=%s' or 'MINOR=%s' from '%s', ignoring: %m", major, strna(minor), path);
57fa1d09
TG
816 }
817
57fa1d09
TG
818 return 0;
819}
820
821_public_ int sd_device_get_ifindex(sd_device *device, int *ifindex) {
822 int r;
823
824 assert_return(device, -EINVAL);
57fa1d09
TG
825
826 r = device_read_uevent_file(device);
827 if (r < 0)
828 return r;
829
1dfa9607 830 if (device->ifindex <= 0)
dcfbde3a
YW
831 return -ENOENT;
832
78ffb476
YW
833 if (ifindex)
834 *ifindex = device->ifindex;
57fa1d09
TG
835
836 return 0;
837}
838
839_public_ int sd_device_new_from_device_id(sd_device **ret, const char *id) {
840 int r;
841
842 assert_return(ret, -EINVAL);
843 assert_return(id, -EINVAL);
844
845 switch (id[0]) {
846 case 'b':
3a47c40d
LP
847 case 'c': {
848 dev_t devt;
57fa1d09 849
3a47c40d 850 if (isempty(id))
57fa1d09
TG
851 return -EINVAL;
852
7176f06c 853 r = parse_devnum(id + 1, &devt);
3a47c40d
LP
854 if (r < 0)
855 return r;
856
857 return sd_device_new_from_devnum(ret, id[0], devt);
57fa1d09 858 }
2cfb1978
ZJS
859
860 case 'n': {
57fa1d09
TG
861 int ifindex;
862
bd44a727
YW
863 ifindex = parse_ifindex(id + 1);
864 if (ifindex < 0)
865 return ifindex;
57fa1d09 866
bd44a727 867 return sd_device_new_from_ifindex(ret, ifindex);
57fa1d09 868 }
2cfb1978
ZJS
869
870 case '+': {
3e2d0c6a 871 const char *subsys, *sep;
57fa1d09 872
3e2d0c6a
ZJS
873 sep = strchr(id + 1, ':');
874 if (!sep || sep - id - 1 > NAME_MAX)
57fa1d09
TG
875 return -EINVAL;
876
3e2d0c6a 877 subsys = memdupa_suffix0(id + 1, sep - id - 1);
57fa1d09 878
3e2d0c6a 879 return sd_device_new_from_subsystem_sysname(ret, subsys, sep + 1);
57fa1d09 880 }
2cfb1978 881
57fa1d09
TG
882 default:
883 return -EINVAL;
884 }
885}
886
887_public_ int sd_device_get_syspath(sd_device *device, const char **ret) {
888 assert_return(device, -EINVAL);
57fa1d09
TG
889
890 assert(path_startswith(device->syspath, "/sys/"));
891
45e093a9
YW
892 if (ret)
893 *ret = device->syspath;
57fa1d09
TG
894
895 return 0;
896}
897
ec9b4f2b
YW
898DEFINE_PRIVATE_HASH_OPS_FULL(
899 device_by_path_hash_ops,
900 char, path_hash_func, path_compare, free,
901 sd_device, sd_device_unref);
902
903static int device_enumerate_children_internal(sd_device *device, const char *subdir, Set **stack, Hashmap **children) {
904 _cleanup_closedir_ DIR *dir = NULL;
905 int r;
906
907 assert(device);
908 assert(stack);
909 assert(children);
910
911 r = device_opendir(device, subdir, &dir);
912 if (r < 0)
913 return r;
914
915 FOREACH_DIRENT_ALL(de, dir, return -errno) {
916 _cleanup_(sd_device_unrefp) sd_device *child = NULL;
917 _cleanup_free_ char *p = NULL;
918
919 if (dot_or_dot_dot(de->d_name))
920 continue;
921
922 if (!IN_SET(de->d_type, DT_LNK, DT_DIR))
923 continue;
924
925 if (subdir)
926 p = path_join(subdir, de->d_name);
927 else
928 p = strdup(de->d_name);
929 if (!p)
930 return -ENOMEM;
931
932 /* Try to create child device. */
933 r = sd_device_new_child(&child, device, p);
934 if (r >= 0) {
935 /* OK, this is a child device, saving it. */
936 r = hashmap_ensure_put(children, &device_by_path_hash_ops, p, child);
937 if (r < 0)
938 return r;
939
940 TAKE_PTR(p);
941 TAKE_PTR(child);
942 } else if (r == -ENODEV) {
943 /* This is not a child device. Push the sub-directory into stack, and read it later. */
944
945 if (de->d_type == DT_LNK)
946 /* Do not follow symlinks, otherwise, we will enter an infinite loop, e.g.,
947 * /sys/class/block/nvme0n1/subsystem/nvme0n1/subsystem/nvme0n1/subsystem/… */
948 continue;
949
950 r = set_ensure_consume(stack, &path_hash_ops_free, TAKE_PTR(p));
951 if (r < 0)
952 return r;
953 } else
954 return r;
955 }
956
957 return 0;
958}
959
960static int device_enumerate_children(sd_device *device) {
961 _cleanup_hashmap_free_ Hashmap *children = NULL;
962 _cleanup_set_free_ Set *stack = NULL;
963 int r;
964
965 assert(device);
966
967 if (device->children_enumerated)
968 return 0; /* Already enumerated. */
969
970 r = device_enumerate_children_internal(device, NULL, &stack, &children);
971 if (r < 0)
972 return r;
973
974 for (;;) {
975 _cleanup_free_ char *subdir = NULL;
976
977 subdir = set_steal_first(stack);
978 if (!subdir)
979 break;
980
981 r = device_enumerate_children_internal(device, subdir, &stack, &children);
982 if (r < 0)
983 return r;
984 }
985
986 device->children_enumerated = true;
987 device->children = TAKE_PTR(children);
988 return 1; /* Enumerated. */
989}
990
991_public_ sd_device *sd_device_get_child_first(sd_device *device, const char **ret_suffix) {
992 int r;
993
994 assert(device);
995
996 r = device_enumerate_children(device);
997 if (r < 0) {
998 log_device_debug_errno(device, r, "sd-device: failed to enumerate child devices: %m");
999 if (ret_suffix)
1000 *ret_suffix = NULL;
1001 return NULL;
1002 }
1003
1004 device->children_iterator = ITERATOR_FIRST;
1005
1006 return sd_device_get_child_next(device, ret_suffix);
1007}
1008
1009_public_ sd_device *sd_device_get_child_next(sd_device *device, const char **ret_suffix) {
1010 sd_device *child;
1011
1012 assert(device);
1013
5000cea8 1014 (void) hashmap_iterate(device->children, &device->children_iterator, (void**) &child, (const void**) ret_suffix);
ec9b4f2b
YW
1015 return child;
1016}
1017
68a52f59
YW
1018_public_ int sd_device_new_child(sd_device **ret, sd_device *device, const char *suffix) {
1019 _cleanup_free_ char *path = NULL;
ec9b4f2b 1020 sd_device *child;
68a52f59
YW
1021 const char *s;
1022 int r;
1023
1024 assert_return(ret, -EINVAL);
1025 assert_return(device, -EINVAL);
1026 assert_return(suffix, -EINVAL);
1027
10a2f90d 1028 if (!path_is_safe(suffix))
68a52f59
YW
1029 return -EINVAL;
1030
ec9b4f2b
YW
1031 /* If we have already enumerated children, try to find the child from the cache. */
1032 child = hashmap_get(device->children, suffix);
1033 if (child) {
1034 *ret = sd_device_ref(child);
1035 return 0;
1036 }
1037
68a52f59
YW
1038 r = sd_device_get_syspath(device, &s);
1039 if (r < 0)
1040 return r;
1041
1042 path = path_join(s, suffix);
1043 if (!path)
1044 return -ENOMEM;
1045
1046 return sd_device_new_from_syspath(ret, path);
1047}
1048
57fa1d09
TG
1049static int device_new_from_child(sd_device **ret, sd_device *child) {
1050 _cleanup_free_ char *path = NULL;
07c90f02 1051 const char *syspath;
57fa1d09
TG
1052 int r;
1053
1054 assert(ret);
1055 assert(child);
1056
1057 r = sd_device_get_syspath(child, &syspath);
1058 if (r < 0)
1059 return r;
1060
57fa1d09 1061 for (;;) {
07c90f02 1062 _cleanup_free_ char *p = NULL;
57fa1d09 1063
07c90f02
YW
1064 r = path_extract_directory(path ?: syspath, &p);
1065 if (r < 0)
1066 return r;
57fa1d09 1067
07c90f02
YW
1068 if (path_equal(p, "/sys"))
1069 return -ENODEV;
57fa1d09 1070
07c90f02
YW
1071 r = sd_device_new_from_syspath(ret, p);
1072 if (r != -ENODEV)
1073 return r;
57fa1d09 1074
07c90f02 1075 free_and_replace(path, p);
57fa1d09 1076 }
57fa1d09
TG
1077}
1078
1079_public_ int sd_device_get_parent(sd_device *child, sd_device **ret) {
df88f02b
YW
1080 int r;
1081
57fa1d09
TG
1082 assert_return(child, -EINVAL);
1083
1084 if (!child->parent_set) {
df88f02b
YW
1085 r = device_new_from_child(&child->parent, child);
1086 if (r < 0 && r != -ENODEV)
1087 return r;
57fa1d09 1088
df88f02b 1089 child->parent_set = true;
57fa1d09
TG
1090 }
1091
1092 if (!child->parent)
1093 return -ENOENT;
1094
45e093a9
YW
1095 if (ret)
1096 *ret = child->parent;
57fa1d09
TG
1097 return 0;
1098}
1099
df49a732
ZJS
1100int device_set_subsystem(sd_device *device, const char *subsystem) {
1101 _cleanup_free_ char *s = NULL;
57fa1d09
TG
1102 int r;
1103
1104 assert(device);
57fa1d09 1105
df49a732
ZJS
1106 if (subsystem) {
1107 s = strdup(subsystem);
1108 if (!s)
1109 return -ENOMEM;
1110 }
57fa1d09 1111
df49a732 1112 r = device_add_property_internal(device, "SUBSYSTEM", s);
57fa1d09
TG
1113 if (r < 0)
1114 return r;
1115
57fa1d09 1116 device->subsystem_set = true;
df49a732 1117 return free_and_replace(device->subsystem, s);
57fa1d09
TG
1118}
1119
e5ca293f 1120int device_set_drivers_subsystem(sd_device *device) {
de7e983e 1121 _cleanup_free_ char *subsystem = NULL;
13659527 1122 const char *devpath, *drivers, *p;
de7e983e
TG
1123 int r;
1124
1125 assert(device);
de7e983e 1126
13659527 1127 r = sd_device_get_devpath(device, &devpath);
4956f220
YW
1128 if (r < 0)
1129 return r;
1130
13659527 1131 drivers = strstr(devpath, "/drivers/");
0b859c97
LP
1132 if (!drivers)
1133 drivers = endswith(devpath, "/drivers");
4956f220
YW
1134 if (!drivers)
1135 return -EINVAL;
1136
be247835
LP
1137 /* Find the path component immediately before the "/drivers/" string */
1138 r = path_find_last_component(devpath, /* accept_dot_dot= */ false, &drivers, &p);
13659527
YW
1139 if (r < 0)
1140 return r;
1141 if (r == 0)
4956f220
YW
1142 return -EINVAL;
1143
13659527 1144 subsystem = strndup(p, r);
de7e983e
TG
1145 if (!subsystem)
1146 return -ENOMEM;
1147
1148 r = device_set_subsystem(device, "drivers");
1149 if (r < 0)
1150 return r;
1151
2cfb1978 1152 return free_and_replace(device->driver_subsystem, subsystem);
de7e983e
TG
1153}
1154
57fa1d09 1155_public_ int sd_device_get_subsystem(sd_device *device, const char **ret) {
de7e983e
TG
1156 int r;
1157
57fa1d09
TG
1158 assert_return(device, -EINVAL);
1159
1160 if (!device->subsystem_set) {
1161 _cleanup_free_ char *subsystem = NULL;
4956f220 1162 const char *syspath;
57fa1d09 1163 char *path;
57fa1d09 1164
4956f220
YW
1165 r = sd_device_get_syspath(device, &syspath);
1166 if (r < 0)
1167 return r;
1168
57fa1d09 1169 /* read 'subsystem' link */
57fa1d09
TG
1170 path = strjoina(syspath, "/subsystem");
1171 r = readlink_value(path, &subsystem);
4956f220
YW
1172 if (r < 0 && r != -ENOENT)
1173 return log_device_debug_errno(device, r,
1174 "sd-device: Failed to read subsystem for %s: %m",
1175 device->devpath);
1176
1177 if (subsystem)
57fa1d09
TG
1178 r = device_set_subsystem(device, subsystem);
1179 /* use implicit names */
0b859c97 1180 else if (!isempty(path_startswith(device->devpath, "/module/")))
57fa1d09 1181 r = device_set_subsystem(device, "module");
0b859c97 1182 else if (strstr(syspath, "/drivers/") || endswith(syspath, "/drivers"))
4956f220 1183 r = device_set_drivers_subsystem(device);
0b859c97 1184 else if (!isempty(PATH_STARTSWITH_SET(device->devpath, "/class/", "/bus/")))
57fa1d09 1185 r = device_set_subsystem(device, "subsystem");
4956f220
YW
1186 else {
1187 device->subsystem_set = true;
1188 r = 0;
de7e983e 1189 }
4956f220
YW
1190 if (r < 0)
1191 return log_device_debug_errno(device, r,
1192 "sd-device: Failed to set subsystem for %s: %m",
1193 device->devpath);
57fa1d09
TG
1194 }
1195
bf4c113e
DH
1196 if (!device->subsystem)
1197 return -ENOENT;
1198
45e093a9
YW
1199 if (ret)
1200 *ret = device->subsystem;
57fa1d09
TG
1201 return 0;
1202}
1203
1204_public_ int sd_device_get_devtype(sd_device *device, const char **devtype) {
1205 int r;
1206
730b76bd 1207 assert_return(device, -EINVAL);
57fa1d09
TG
1208
1209 r = device_read_uevent_file(device);
1210 if (r < 0)
1211 return r;
1212
dcfbde3a
YW
1213 if (!device->devtype)
1214 return -ENOENT;
1215
730b76bd
ZJS
1216 if (devtype)
1217 *devtype = device->devtype;
57fa1d09 1218
730b76bd 1219 return !!device->devtype;
57fa1d09
TG
1220}
1221
1222_public_ int sd_device_get_parent_with_subsystem_devtype(sd_device *child, const char *subsystem, const char *devtype, sd_device **ret) {
1223 sd_device *parent = NULL;
1224 int r;
1225
1226 assert_return(child, -EINVAL);
1227 assert_return(subsystem, -EINVAL);
1228
1229 r = sd_device_get_parent(child, &parent);
1230 while (r >= 0) {
1231 const char *parent_subsystem = NULL;
57fa1d09 1232
db2f8a2e 1233 (void) sd_device_get_subsystem(parent, &parent_subsystem);
57fa1d09 1234 if (streq_ptr(parent_subsystem, subsystem)) {
45e093a9
YW
1235 const char *parent_devtype = NULL;
1236
57fa1d09
TG
1237 if (!devtype)
1238 break;
1239
db2f8a2e 1240 (void) sd_device_get_devtype(parent, &parent_devtype);
57fa1d09
TG
1241 if (streq_ptr(parent_devtype, devtype))
1242 break;
1243 }
1244 r = sd_device_get_parent(parent, &parent);
1245 }
1246
1247 if (r < 0)
1248 return r;
1249
45e093a9
YW
1250 if (ret)
1251 *ret = parent;
57fa1d09
TG
1252 return 0;
1253}
1254
1255_public_ int sd_device_get_devnum(sd_device *device, dev_t *devnum) {
1256 int r;
1257
1258 assert_return(device, -EINVAL);
57fa1d09
TG
1259
1260 r = device_read_uevent_file(device);
1261 if (r < 0)
1262 return r;
1263
dcfbde3a
YW
1264 if (major(device->devnum) <= 0)
1265 return -ENOENT;
1266
78ffb476
YW
1267 if (devnum)
1268 *devnum = device->devnum;
57fa1d09
TG
1269
1270 return 0;
1271}
1272
ca0176fa
ZJS
1273int device_set_driver(sd_device *device, const char *driver) {
1274 _cleanup_free_ char *d = NULL;
57fa1d09
TG
1275 int r;
1276
1277 assert(device);
57fa1d09 1278
ca0176fa
ZJS
1279 if (driver) {
1280 d = strdup(driver);
1281 if (!d)
1282 return -ENOMEM;
1283 }
57fa1d09 1284
ca0176fa 1285 r = device_add_property_internal(device, "DRIVER", d);
57fa1d09
TG
1286 if (r < 0)
1287 return r;
1288
57fa1d09 1289 device->driver_set = true;
ca0176fa 1290 return free_and_replace(device->driver, d);
57fa1d09
TG
1291}
1292
1293_public_ int sd_device_get_driver(sd_device *device, const char **ret) {
1294 assert_return(device, -EINVAL);
57fa1d09
TG
1295
1296 if (!device->driver_set) {
1297 _cleanup_free_ char *driver = NULL;
1298 const char *syspath;
1299 char *path;
1300 int r;
1301
1302 r = sd_device_get_syspath(device, &syspath);
1303 if (r < 0)
1304 return r;
1305
1306 path = strjoina(syspath, "/driver");
1307 r = readlink_value(path, &driver);
ca0176fa
ZJS
1308 if (r < 0 && r != -ENOENT)
1309 return log_device_debug_errno(device, r,
1310 "sd-device: readlink(\"%s\") failed: %m", path);
1311
1312 r = device_set_driver(device, driver);
1313 if (r < 0)
1314 return log_device_debug_errno(device, r,
1315 "sd-device: Failed to set driver \"%s\": %m", driver);
57fa1d09
TG
1316 }
1317
bf4c113e
DH
1318 if (!device->driver)
1319 return -ENOENT;
1320
45e093a9
YW
1321 if (ret)
1322 *ret = device->driver;
57fa1d09
TG
1323 return 0;
1324}
1325
d4df6ce2 1326_public_ int sd_device_get_devpath(sd_device *device, const char **ret) {
57fa1d09 1327 assert_return(device, -EINVAL);
57fa1d09
TG
1328
1329 assert(device->devpath);
1330 assert(device->devpath[0] == '/');
1331
d4df6ce2
LP
1332 if (ret)
1333 *ret = device->devpath;
1334
57fa1d09
TG
1335 return 0;
1336}
1337
1338_public_ int sd_device_get_devname(sd_device *device, const char **devname) {
1339 int r;
1340
1341 assert_return(device, -EINVAL);
57fa1d09
TG
1342
1343 r = device_read_uevent_file(device);
1344 if (r < 0)
1345 return r;
1346
1347 if (!device->devname)
1348 return -ENOENT;
1349
70e45108 1350 assert(!isempty(path_startswith(device->devname, "/dev/")));
57fa1d09 1351
45e093a9
YW
1352 if (devname)
1353 *devname = device->devname;
57fa1d09
TG
1354 return 0;
1355}
1356
6e25642f 1357static int device_set_sysname_and_sysnum(sd_device *device) {
57fa1d09 1358 _cleanup_free_ char *sysname = NULL;
9dbfcaf2 1359 size_t len, n;
5fa9d220 1360 int r;
57fa1d09 1361
c58e0ce6 1362 assert(device);
57fa1d09 1363
c58e0ce6
YW
1364 r = path_extract_filename(device->devpath, &sysname);
1365 if (r < 0)
1366 return r;
e4e1353c
YW
1367 if (r == O_DIRECTORY)
1368 return -EINVAL;
57fa1d09
TG
1369
1370 /* some devices have '!' in their name, change that to '/' */
9dbfcaf2 1371 string_replace_char(sysname, '!', '/');
57fa1d09 1372
9dbfcaf2
YW
1373 n = strspn_from_end(sysname, DIGITS);
1374 len = strlen(sysname);
1375 assert(n <= len);
1376 if (n == len)
1377 n = 0; /* Do not set sysnum for number only sysname. */
57fa1d09 1378
9dbfcaf2 1379 device->sysnum = n > 0 ? sysname + len - n : NULL;
2cfb1978 1380 return free_and_replace(device->sysname, sysname);
57fa1d09
TG
1381}
1382
1383_public_ int sd_device_get_sysname(sd_device *device, const char **ret) {
1384 int r;
1385
1386 assert_return(device, -EINVAL);
57fa1d09 1387
6e25642f
YW
1388 if (!device->sysname) {
1389 r = device_set_sysname_and_sysnum(device);
57fa1d09
TG
1390 if (r < 0)
1391 return r;
1392 }
1393
45e093a9
YW
1394 if (ret)
1395 *ret = device->sysname;
57fa1d09
TG
1396 return 0;
1397}
1398
1399_public_ int sd_device_get_sysnum(sd_device *device, const char **ret) {
1400 int r;
1401
1402 assert_return(device, -EINVAL);
57fa1d09 1403
6e25642f
YW
1404 if (!device->sysname) {
1405 r = device_set_sysname_and_sysnum(device);
57fa1d09
TG
1406 if (r < 0)
1407 return r;
1408 }
1409
dcfbde3a
YW
1410 if (!device->sysnum)
1411 return -ENOENT;
1412
45e093a9
YW
1413 if (ret)
1414 *ret = device->sysnum;
57fa1d09
TG
1415 return 0;
1416}
1417
a1130022
LP
1418_public_ int sd_device_get_action(sd_device *device, sd_device_action_t *ret) {
1419 assert_return(device, -EINVAL);
1420
1421 if (device->action < 0)
1422 return -ENOENT;
1423
1424 if (ret)
1425 *ret = device->action;
1426
1427 return 0;
1428}
1429
1430_public_ int sd_device_get_seqnum(sd_device *device, uint64_t *ret) {
1431 assert_return(device, -EINVAL);
1432
1433 if (device->seqnum == 0)
1434 return -ENOENT;
1435
1436 if (ret)
1437 *ret = device->seqnum;
1438
1439 return 0;
1440}
1441
122adcb2 1442_public_ int sd_device_get_diskseq(sd_device *device, uint64_t *ret) {
becbcca5
YW
1443 int r;
1444
122adcb2
LB
1445 assert_return(device, -EINVAL);
1446
becbcca5
YW
1447 r = device_read_uevent_file(device);
1448 if (r < 0)
1449 return r;
1450
122adcb2
LB
1451 if (device->diskseq == 0)
1452 return -ENOENT;
1453
1454 if (ret)
1455 *ret = device->diskseq;
1456
1457 return 0;
1458}
1459
57fa1d09
TG
1460static bool is_valid_tag(const char *tag) {
1461 assert(tag);
1462
0b4c70b4 1463 return in_charset(tag, ALPHANUMERICAL "-_") && filename_is_valid(tag);
57fa1d09
TG
1464}
1465
e77b146f
LP
1466int device_add_tag(sd_device *device, const char *tag, bool both) {
1467 int r, added;
57fa1d09
TG
1468
1469 assert(device);
1470 assert(tag);
1471
1472 if (!is_valid_tag(tag))
1473 return -EINVAL;
1474
e77b146f
LP
1475 /* Definitely add to the "all" list of tags (i.e. the sticky list) */
1476 added = set_put_strdup(&device->all_tags, tag);
1477 if (added < 0)
1478 return added;
1479
1480 /* And optionally, also add it to the current list of tags */
1481 if (both) {
1482 r = set_put_strdup(&device->current_tags, tag);
1483 if (r < 0) {
1484 if (added > 0)
1485 (void) set_remove(device->all_tags, tag);
1486
1487 return r;
1488 }
1489 }
57fa1d09 1490
313cefa1 1491 device->tags_generation++;
57fa1d09
TG
1492 device->property_tags_outdated = true;
1493
1494 return 0;
1495}
1496
1497int device_add_devlink(sd_device *device, const char *devlink) {
2c5f119c 1498 char *p;
57fa1d09
TG
1499 int r;
1500
1501 assert(device);
1502 assert(devlink);
1503
70e45108
YW
1504 r = mangle_devname(devlink, &p);
1505 if (r < 0)
1506 return r;
2c5f119c
YW
1507
1508 r = set_ensure_consume(&device->devlinks, &path_hash_ops_free, p);
57fa1d09
TG
1509 if (r < 0)
1510 return r;
1511
313cefa1 1512 device->devlinks_generation++;
57fa1d09
TG
1513 device->property_devlinks_outdated = true;
1514
2c5f119c 1515 return r; /* return 1 when newly added, 0 when already exists */
57fa1d09
TG
1516}
1517
2c5f119c
YW
1518int device_remove_devlink(sd_device *device, const char *devlink) {
1519 _cleanup_free_ char *p = NULL, *s = NULL;
70e45108 1520 int r;
aeefa4d2
FB
1521
1522 assert(device);
1523 assert(devlink);
1524
70e45108
YW
1525 r = mangle_devname(devlink, &p);
1526 if (r < 0)
1527 return r;
2c5f119c
YW
1528
1529 s = set_remove(device->devlinks, p);
aeefa4d2 1530 if (!s)
2c5f119c 1531 return 0; /* does not exist */
aeefa4d2
FB
1532
1533 device->devlinks_generation++;
1534 device->property_devlinks_outdated = true;
2c5f119c 1535 return 1; /* removed */
aeefa4d2
FB
1536}
1537
b881ce16
YW
1538bool device_has_devlink(sd_device *device, const char *devlink) {
1539 assert(device);
1540 assert(devlink);
1541
1542 return set_contains(device->devlinks, devlink);
1543}
1544
57fa1d09
TG
1545static int device_add_property_internal_from_string(sd_device *device, const char *str) {
1546 _cleanup_free_ char *key = NULL;
1547 char *value;
03dd7b7d 1548 int r;
57fa1d09
TG
1549
1550 assert(device);
1551 assert(str);
1552
1553 key = strdup(str);
1554 if (!key)
1555 return -ENOMEM;
1556
1557 value = strchr(key, '=');
1558 if (!value)
1559 return -EINVAL;
1560
1561 *value = '\0';
1562
1563 if (isempty(++value))
1564 value = NULL;
1565
03dd7b7d
YW
1566 /* Add the property to both sd_device::properties and sd_device::properties_db,
1567 * as this is called by only handle_db_line(). */
1568 r = device_add_property_aux(device, key, value, false);
1569 if (r < 0)
1570 return r;
1571
1572 return device_add_property_aux(device, key, value, true);
57fa1d09
TG
1573}
1574
dc5042c0
ZJS
1575int device_set_usec_initialized(sd_device *device, usec_t when) {
1576 char s[DECIMAL_STR_MAX(usec_t)];
57fa1d09
TG
1577 int r;
1578
1579 assert(device);
57fa1d09 1580
dc5042c0 1581 xsprintf(s, USEC_FMT, when);
57fa1d09 1582
dc5042c0 1583 r = device_add_property_internal(device, "USEC_INITIALIZED", s);
57fa1d09
TG
1584 if (r < 0)
1585 return r;
1586
dc5042c0 1587 device->usec_initialized = when;
57fa1d09
TG
1588 return 0;
1589}
1590
1591static int handle_db_line(sd_device *device, char key, const char *value) {
57fa1d09
TG
1592 int r;
1593
1594 assert(device);
1595 assert(value);
1596
1597 switch (key) {
e77b146f
LP
1598 case 'G': /* Any tag */
1599 case 'Q': /* Current tag */
08405125 1600 return device_add_tag(device, value, key == 'Q');
57fa1d09 1601
08405125
YW
1602 case 'S': {
1603 const char *path;
57fa1d09 1604
08405125
YW
1605 path = strjoina("/dev/", value);
1606 return device_add_devlink(device, path);
1607 }
57fa1d09 1608 case 'E':
08405125 1609 return device_add_property_internal_from_string(device, value);
57fa1d09 1610
dc5042c0
ZJS
1611 case 'I': {
1612 usec_t t;
1613
1614 r = safe_atou64(value, &t);
1615 if (r < 0)
1616 return r;
1617
08405125 1618 return device_set_usec_initialized(device, t);
dc5042c0 1619 }
57fa1d09 1620 case 'L':
08405125 1621 return safe_atoi(value, &device->devlink_priority);
57fa1d09 1622
57fa1d09 1623 case 'W':
e7f781e4
YW
1624 /* Deprecated. Previously, watch handle is both saved in database and /run/udev/watch.
1625 * However, the handle saved in database may not be updated when the handle is updated
1626 * or removed. Moreover, it is not necessary to store the handle within the database,
1627 * as its value becomes meaningless when udevd is restarted. */
08405125
YW
1628 return 0;
1629
58b30ada 1630 case 'V':
08405125 1631 return safe_atou(value, &device->database_version);
58b30ada 1632
57fa1d09 1633 default:
c7d54dae 1634 log_device_debug(device, "sd-device: Unknown key '%c' in device db, ignoring", key);
08405125 1635 return 0;
57fa1d09 1636 }
57fa1d09
TG
1637}
1638
fe732381 1639int device_get_device_id(sd_device *device, const char **ret) {
57fa1d09
TG
1640 assert(device);
1641 assert(ret);
1642
fe732381 1643 if (!device->device_id) {
57fa1d09
TG
1644 _cleanup_free_ char *id = NULL;
1645 const char *subsystem;
1646 dev_t devnum;
1647 int ifindex, r;
1648
1649 r = sd_device_get_subsystem(device, &subsystem);
1650 if (r < 0)
1651 return r;
1652
dcfbde3a 1653 if (sd_device_get_devnum(device, &devnum) >= 0) {
ccddd104 1654 /* use dev_t — b259:131072, c254:0 */
67458536 1655 if (asprintf(&id, "%c" DEVNUM_FORMAT_STR,
57fa1d09 1656 streq(subsystem, "block") ? 'b' : 'c',
67458536 1657 DEVNUM_FORMAT_VAL(devnum)) < 0)
53fae771 1658 return -ENOMEM;
1dfa9607 1659 } else if (sd_device_get_ifindex(device, &ifindex) >= 0) {
ccddd104 1660 /* use netdev ifindex — n3 */
ce1d08ba 1661 if (asprintf(&id, "n%u", (unsigned) ifindex) < 0)
53fae771 1662 return -ENOMEM;
57fa1d09 1663 } else {
ce1d08ba 1664 _cleanup_free_ char *sysname = NULL;
4189708a 1665
ce1d08ba
YW
1666 /* use $subsys:$sysname — pci:0000:00:1f.2
1667 * sd_device_get_sysname() has '!' translated, get it from devpath */
1668 r = path_extract_filename(device->devpath, &sysname);
1669 if (r < 0)
1670 return r;
e4e1353c
YW
1671 if (r == O_DIRECTORY)
1672 return -EINVAL;
57fa1d09 1673
ce1d08ba
YW
1674 if (streq(subsystem, "drivers")) {
1675 /* the 'drivers' pseudo-subsystem is special, and needs the real
1676 * subsystem encoded as well */
1677 assert(device->driver_subsystem);
fe20121a 1678 id = strjoin("+drivers:", device->driver_subsystem, ":", sysname);
ce1d08ba 1679 } else
fe20121a
ZJS
1680 id = strjoin("+", subsystem, ":", sysname);
1681 if (!id)
1682 return -ENOMEM;
57fa1d09
TG
1683 }
1684
db2bad43
YW
1685 if (!filename_is_valid(id))
1686 return -EINVAL;
1687
fe732381 1688 device->device_id = TAKE_PTR(id);
57fa1d09
TG
1689 }
1690
fe732381 1691 *ret = device->device_id;
57fa1d09
TG
1692 return 0;
1693}
1694
b07d0f2a 1695int device_read_db_internal_filename(sd_device *device, const char *filename) {
57fa1d09 1696 _cleanup_free_ char *db = NULL;
b07d0f2a 1697 const char *value;
462035d5 1698 size_t db_len;
376ee2c3 1699 char key = '\0'; /* Unnecessary initialization to appease gcc-12.0.0-0.4.fc36 */
57fa1d09
TG
1700 int r;
1701
1702 enum {
1703 PRE_KEY,
1704 KEY,
1705 PRE_VALUE,
1706 VALUE,
1707 INVALID_LINE,
1708 } state = PRE_KEY;
1709
ebcc52fa 1710 assert(device);
b07d0f2a 1711 assert(filename);
ebcc52fa 1712
b07d0f2a 1713 r = read_full_file(filename, &db, &db_len);
57fa1d09
TG
1714 if (r < 0) {
1715 if (r == -ENOENT)
1716 return 0;
b07d0f2a
YW
1717
1718 return log_device_debug_errno(device, r, "sd-device: Failed to read db '%s': %m", filename);
57fa1d09
TG
1719 }
1720
1721 /* devices with a database entry are initialized */
7e518afa 1722 device->is_initialized = true;
57fa1d09 1723
cd53c8f9
ZJS
1724 device->db_loaded = true;
1725
e47a3af4 1726 for (size_t i = 0; i < db_len; i++)
57fa1d09
TG
1727 switch (state) {
1728 case PRE_KEY:
1729 if (!strchr(NEWLINE, db[i])) {
1730 key = db[i];
1731
1732 state = KEY;
1733 }
1734
1735 break;
1736 case KEY:
1737 if (db[i] != ':') {
c7d54dae 1738 log_device_debug(device, "sd-device: Invalid db entry with key '%c', ignoring", key);
57fa1d09
TG
1739
1740 state = INVALID_LINE;
1741 } else {
1742 db[i] = '\0';
1743
1744 state = PRE_VALUE;
1745 }
1746
1747 break;
1748 case PRE_VALUE:
1749 value = &db[i];
1750
1751 state = VALUE;
1752
1753 break;
1754 case INVALID_LINE:
1755 if (strchr(NEWLINE, db[i]))
1756 state = PRE_KEY;
1757
1758 break;
1759 case VALUE:
1760 if (strchr(NEWLINE, db[i])) {
1761 db[i] = '\0';
1762 r = handle_db_line(device, key, value);
1763 if (r < 0)
462035d5
ZJS
1764 log_device_debug_errno(device, r, "sd-device: Failed to handle db entry '%c:%s', ignoring: %m",
1765 key, value);
57fa1d09
TG
1766
1767 state = PRE_KEY;
1768 }
1769
1770 break;
1771 default:
b07d0f2a 1772 return log_device_debug_errno(device, SYNTHETIC_ERRNO(EINVAL), "sd-device: invalid db syntax.");
57fa1d09 1773 }
57fa1d09 1774
57fa1d09
TG
1775 return 0;
1776}
1777
b07d0f2a
YW
1778int device_read_db_internal(sd_device *device, bool force) {
1779 const char *id, *path;
1780 int r;
1781
1782 assert(device);
1783
1784 if (device->db_loaded || (!force && device->sealed))
1785 return 0;
1786
fe732381 1787 r = device_get_device_id(device, &id);
b07d0f2a
YW
1788 if (r < 0)
1789 return r;
1790
1791 path = strjoina("/run/udev/data/", id);
1792
1793 return device_read_db_internal_filename(device, path);
1794}
1795
5a937ea2 1796_public_ int sd_device_get_is_initialized(sd_device *device) {
57fa1d09
TG
1797 int r;
1798
1799 assert_return(device, -EINVAL);
57fa1d09
TG
1800
1801 r = device_read_db(device);
591c186f
YW
1802 if (r == -ENOENT)
1803 /* The device may be already removed or renamed. */
1804 return false;
57fa1d09
TG
1805 if (r < 0)
1806 return r;
1807
5a937ea2 1808 return device->is_initialized;
57fa1d09
TG
1809}
1810
8626b43b
LP
1811_public_ int sd_device_get_usec_initialized(sd_device *device, uint64_t *ret) {
1812 int r;
1813
1814 assert_return(device, -EINVAL);
1815
4eb5f1db 1816 r = sd_device_get_is_initialized(device);
8626b43b
LP
1817 if (r < 0)
1818 return r;
4eb5f1db 1819 if (r == 0)
8626b43b
LP
1820 return -EBUSY;
1821
1822 if (device->usec_initialized == 0)
1823 return -ENODATA;
1824
1825 if (ret)
1826 *ret = device->usec_initialized;
1827
1828 return 0;
1829}
1830
4eb5f1db
YW
1831_public_ int sd_device_get_usec_since_initialized(sd_device *device, uint64_t *ret) {
1832 usec_t now_ts, ts;
57fa1d09
TG
1833 int r;
1834
1835 assert_return(device, -EINVAL);
57fa1d09 1836
4eb5f1db 1837 r = sd_device_get_usec_initialized(device, &ts);
57fa1d09
TG
1838 if (r < 0)
1839 return r;
1840
02ef01ad 1841 now_ts = now(CLOCK_MONOTONIC);
57fa1d09 1842
4eb5f1db 1843 if (now_ts < ts)
57fa1d09
TG
1844 return -EIO;
1845
4eb5f1db
YW
1846 if (ret)
1847 *ret = usec_sub_unsigned(now_ts, ts);
1848
57fa1d09
TG
1849 return 0;
1850}
1851
1852_public_ const char *sd_device_get_tag_first(sd_device *device) {
8927b1da
DH
1853 void *v;
1854
57fa1d09
TG
1855 assert_return(device, NULL);
1856
1857 (void) device_read_db(device);
1858
e77b146f
LP
1859 device->all_tags_iterator_generation = device->tags_generation;
1860 device->all_tags_iterator = ITERATOR_FIRST;
57fa1d09 1861
e77b146f 1862 (void) set_iterate(device->all_tags, &device->all_tags_iterator, &v);
8927b1da 1863 return v;
57fa1d09
TG
1864}
1865
1866_public_ const char *sd_device_get_tag_next(sd_device *device) {
8927b1da
DH
1867 void *v;
1868
57fa1d09
TG
1869 assert_return(device, NULL);
1870
1871 (void) device_read_db(device);
1872
e77b146f
LP
1873 if (device->all_tags_iterator_generation != device->tags_generation)
1874 return NULL;
1875
1876 (void) set_iterate(device->all_tags, &device->all_tags_iterator, &v);
1877 return v;
1878}
1879
6ece7cd2
YW
1880static bool device_database_supports_current_tags(sd_device *device) {
1881 assert(device);
1882
1883 (void) device_read_db(device);
1884
1885 /* The current tags (saved in Q field) feature is implemented in database version 1.
1886 * If the database version is 0, then the tags (NOT current tags, saved in G field) are not
1887 * sticky. Thus, we can safely bypass the operations for the current tags (Q) to tags (G). */
1888
1889 return device->database_version >= 1;
1890}
1891
e77b146f
LP
1892_public_ const char *sd_device_get_current_tag_first(sd_device *device) {
1893 void *v;
1894
1895 assert_return(device, NULL);
1896
6ece7cd2
YW
1897 if (!device_database_supports_current_tags(device))
1898 return sd_device_get_tag_first(device);
1899
e77b146f
LP
1900 (void) device_read_db(device);
1901
1902 device->current_tags_iterator_generation = device->tags_generation;
1903 device->current_tags_iterator = ITERATOR_FIRST;
1904
1905 (void) set_iterate(device->current_tags, &device->current_tags_iterator, &v);
1906 return v;
1907}
1908
1909_public_ const char *sd_device_get_current_tag_next(sd_device *device) {
1910 void *v;
1911
1912 assert_return(device, NULL);
1913
6ece7cd2
YW
1914 if (!device_database_supports_current_tags(device))
1915 return sd_device_get_tag_next(device);
1916
e77b146f
LP
1917 (void) device_read_db(device);
1918
1919 if (device->current_tags_iterator_generation != device->tags_generation)
57fa1d09
TG
1920 return NULL;
1921
e77b146f 1922 (void) set_iterate(device->current_tags, &device->current_tags_iterator, &v);
8927b1da 1923 return v;
57fa1d09
TG
1924}
1925
1926_public_ const char *sd_device_get_devlink_first(sd_device *device) {
8927b1da
DH
1927 void *v;
1928
57fa1d09
TG
1929 assert_return(device, NULL);
1930
1931 (void) device_read_db(device);
1932
1933 device->devlinks_iterator_generation = device->devlinks_generation;
1934 device->devlinks_iterator = ITERATOR_FIRST;
1935
bccfe92e 1936 (void) set_iterate(device->devlinks, &device->devlinks_iterator, &v);
8927b1da 1937 return v;
57fa1d09
TG
1938}
1939
1940_public_ const char *sd_device_get_devlink_next(sd_device *device) {
8927b1da
DH
1941 void *v;
1942
57fa1d09
TG
1943 assert_return(device, NULL);
1944
1945 (void) device_read_db(device);
1946
1947 if (device->devlinks_iterator_generation != device->devlinks_generation)
1948 return NULL;
1949
bccfe92e 1950 (void) set_iterate(device->devlinks, &device->devlinks_iterator, &v);
8927b1da 1951 return v;
57fa1d09
TG
1952}
1953
a3ce8136 1954int device_properties_prepare(sd_device *device) {
57fa1d09
TG
1955 int r;
1956
1957 assert(device);
1958
1959 r = device_read_uevent_file(device);
1960 if (r < 0)
1961 return r;
1962
1963 r = device_read_db(device);
1964 if (r < 0)
1965 return r;
1966
1967 if (device->property_devlinks_outdated) {
1d88a271 1968 _cleanup_free_ char *devlinks = NULL;
57fa1d09 1969
8d80f275 1970 r = set_strjoin(device->devlinks, " ", false, &devlinks);
57fa1d09
TG
1971 if (r < 0)
1972 return r;
1973
6f3ac0d5
YW
1974 if (!isempty(devlinks)) {
1975 r = device_add_property_internal(device, "DEVLINKS", devlinks);
1976 if (r < 0)
1977 return r;
1978 }
1979
57fa1d09
TG
1980 device->property_devlinks_outdated = false;
1981 }
1982
1983 if (device->property_tags_outdated) {
1d88a271 1984 _cleanup_free_ char *tags = NULL;
57fa1d09 1985
8d80f275 1986 r = set_strjoin(device->all_tags, ":", true, &tags);
6f3ac0d5
YW
1987 if (r < 0)
1988 return r;
57fa1d09 1989
6f3ac0d5 1990 if (!isempty(tags)) {
e77b146f
LP
1991 r = device_add_property_internal(device, "TAGS", tags);
1992 if (r < 0)
1993 return r;
1d88a271 1994 }
57fa1d09 1995
6f3ac0d5 1996 tags = mfree(tags);
8d80f275 1997 r = set_strjoin(device->current_tags, ":", true, &tags);
6f3ac0d5
YW
1998 if (r < 0)
1999 return r;
e77b146f 2000
6f3ac0d5 2001 if (!isempty(tags)) {
e77b146f
LP
2002 r = device_add_property_internal(device, "CURRENT_TAGS", tags);
2003 if (r < 0)
2004 return r;
2005 }
57fa1d09
TG
2006
2007 device->property_tags_outdated = false;
2008 }
2009
2010 return 0;
2011}
2012
2013_public_ const char *sd_device_get_property_first(sd_device *device, const char **_value) {
2014 const char *key;
57fa1d09
TG
2015 int r;
2016
2017 assert_return(device, NULL);
2018
2019 r = device_properties_prepare(device);
2020 if (r < 0)
2021 return NULL;
2022
2023 device->properties_iterator_generation = device->properties_generation;
2024 device->properties_iterator = ITERATOR_FIRST;
2025
7b9103a6 2026 (void) ordered_hashmap_iterate(device->properties, &device->properties_iterator, (void**)_value, (const void**)&key);
57fa1d09
TG
2027 return key;
2028}
2029
2030_public_ const char *sd_device_get_property_next(sd_device *device, const char **_value) {
2031 const char *key;
57fa1d09
TG
2032 int r;
2033
2034 assert_return(device, NULL);
2035
2036 r = device_properties_prepare(device);
2037 if (r < 0)
2038 return NULL;
2039
2040 if (device->properties_iterator_generation != device->properties_generation)
2041 return NULL;
2042
7b9103a6 2043 (void) ordered_hashmap_iterate(device->properties, &device->properties_iterator, (void**)_value, (const void**)&key);
57fa1d09
TG
2044 return key;
2045}
2046
4bc9d816 2047static int device_sysattrs_read_all_internal(sd_device *device, const char *subdir, Set **stack) {
57fa1d09 2048 _cleanup_closedir_ DIR *dir = NULL;
57fa1d09
TG
2049 int r;
2050
4bc9d816
YW
2051 assert(device);
2052 assert(stack);
2053
62ccd11d
YW
2054 r = device_opendir(device, subdir, &dir);
2055 if (r == -ENOENT && subdir)
2056 return 0; /* Maybe, this is a child device, and is already removed. */
57fa1d09
TG
2057 if (r < 0)
2058 return r;
2059
5b304c70
YW
2060 if (subdir) {
2061 if (faccessat(dirfd(dir), "uevent", F_OK, 0) >= 0)
2062 return 0; /* this is a child device, skipping */
2063 if (errno != ENOENT) {
2064 log_device_debug_errno(device, errno,
2065 "sd-device: Failed to access %s/uevent, ignoring sub-directory %s: %m",
2066 subdir, subdir);
2067 return 0;
2068 }
2069 }
57fa1d09 2070
c7f0d9e5 2071 FOREACH_DIRENT_ALL(de, dir, return -errno) {
65c0f14b 2072 _cleanup_free_ char *p = NULL;
57fa1d09
TG
2073 struct stat statbuf;
2074
c7f0d9e5 2075 if (dot_or_dot_dot(de->d_name))
57fa1d09
TG
2076 continue;
2077
fadcc122 2078 /* only handle symlinks, regular files, and directories */
c7f0d9e5 2079 if (!IN_SET(de->d_type, DT_LNK, DT_REG, DT_DIR))
fadcc122
YW
2080 continue;
2081
2082 if (subdir) {
c7f0d9e5 2083 p = path_join(subdir, de->d_name);
fadcc122
YW
2084 if (!p)
2085 return -ENOMEM;
2086 }
2087
c7f0d9e5 2088 if (de->d_type == DT_DIR) {
4bc9d816
YW
2089 /* push the sub-directory into the stack, and read it later. */
2090 if (p)
2091 r = set_ensure_consume(stack, &path_hash_ops_free, TAKE_PTR(p));
2092 else
2093 r = set_put_strdup_full(stack, &path_hash_ops_free, de->d_name);
fadcc122
YW
2094 if (r < 0)
2095 return r;
2096
2097 continue;
2098 }
2099
65c0f14b 2100 if (fstatat(dirfd(dir), de->d_name, &statbuf, AT_SYMLINK_NOFOLLOW) < 0)
57fa1d09
TG
2101 continue;
2102
ab218d0b 2103 if ((statbuf.st_mode & (S_IRUSR | S_IWUSR)) == 0)
57fa1d09
TG
2104 continue;
2105
bc5e8ebb
YW
2106 if (p)
2107 r = set_ensure_consume(&device->sysattrs, &path_hash_ops_free, TAKE_PTR(p));
2108 else
2109 r = set_put_strdup_full(&device->sysattrs, &path_hash_ops_free, de->d_name);
57fa1d09
TG
2110 if (r < 0)
2111 return r;
2112 }
2113
fadcc122
YW
2114 return 0;
2115}
2116
2117static int device_sysattrs_read_all(sd_device *device) {
4bc9d816 2118 _cleanup_set_free_ Set *stack = NULL;
fadcc122
YW
2119 int r;
2120
2121 assert(device);
2122
2123 if (device->sysattrs_read)
2124 return 0;
2125
4bc9d816 2126 r = device_sysattrs_read_all_internal(device, NULL, &stack);
fadcc122
YW
2127 if (r < 0)
2128 return r;
2129
4bc9d816
YW
2130 for (;;) {
2131 _cleanup_free_ char *subdir = NULL;
2132
2133 subdir = set_steal_first(stack);
2134 if (!subdir)
2135 break;
2136
2137 r = device_sysattrs_read_all_internal(device, subdir, &stack);
2138 if (r < 0)
2139 return r;
2140 }
2141
57fa1d09
TG
2142 device->sysattrs_read = true;
2143
2144 return 0;
2145}
2146
2147_public_ const char *sd_device_get_sysattr_first(sd_device *device) {
8927b1da 2148 void *v;
57fa1d09
TG
2149 int r;
2150
2151 assert_return(device, NULL);
2152
2153 if (!device->sysattrs_read) {
2154 r = device_sysattrs_read_all(device);
2155 if (r < 0) {
2156 errno = -r;
2157 return NULL;
2158 }
2159 }
2160
2161 device->sysattrs_iterator = ITERATOR_FIRST;
2162
bccfe92e 2163 (void) set_iterate(device->sysattrs, &device->sysattrs_iterator, &v);
8927b1da 2164 return v;
57fa1d09
TG
2165}
2166
2167_public_ const char *sd_device_get_sysattr_next(sd_device *device) {
8927b1da
DH
2168 void *v;
2169
57fa1d09
TG
2170 assert_return(device, NULL);
2171
2172 if (!device->sysattrs_read)
2173 return NULL;
2174
bccfe92e 2175 (void) set_iterate(device->sysattrs, &device->sysattrs_iterator, &v);
8927b1da 2176 return v;
57fa1d09
TG
2177}
2178
2179_public_ int sd_device_has_tag(sd_device *device, const char *tag) {
2180 assert_return(device, -EINVAL);
2181 assert_return(tag, -EINVAL);
2182
2183 (void) device_read_db(device);
2184
e77b146f
LP
2185 return set_contains(device->all_tags, tag);
2186}
2187
2188_public_ int sd_device_has_current_tag(sd_device *device, const char *tag) {
2189 assert_return(device, -EINVAL);
2190 assert_return(tag, -EINVAL);
2191
6ece7cd2
YW
2192 if (!device_database_supports_current_tags(device))
2193 return sd_device_has_tag(device, tag);
2194
e77b146f
LP
2195 (void) device_read_db(device);
2196
2197 return set_contains(device->current_tags, tag);
57fa1d09
TG
2198}
2199
45e093a9
YW
2200_public_ int sd_device_get_property_value(sd_device *device, const char *key, const char **ret_value) {
2201 const char *value;
57fa1d09
TG
2202 int r;
2203
2204 assert_return(device, -EINVAL);
2205 assert_return(key, -EINVAL);
57fa1d09
TG
2206
2207 r = device_properties_prepare(device);
2208 if (r < 0)
2209 return r;
2210
2211 value = ordered_hashmap_get(device->properties, key);
2212 if (!value)
2213 return -ENOENT;
2214
45e093a9
YW
2215 if (ret_value)
2216 *ret_value = value;
57fa1d09
TG
2217 return 0;
2218}
2219
15345fc6
YW
2220int device_get_property_bool(sd_device *device, const char *key) {
2221 const char *value;
2222 int r;
2223
2224 assert(device);
2225 assert(key);
2226
2227 r = sd_device_get_property_value(device, key, &value);
2228 if (r < 0)
2229 return r;
2230
2231 return parse_boolean(value);
2232}
2233
eedfef0f
YW
2234int device_get_property_int(sd_device *device, const char *key, int *ret) {
2235 const char *value;
2236 int r, v;
2237
2238 assert(device);
2239 assert(key);
2240
2241 r = sd_device_get_property_value(device, key, &value);
2242 if (r < 0)
2243 return r;
2244
2245 r = safe_atoi(value, &v);
2246 if (r < 0)
2247 return r;
2248
2249 if (ret)
2250 *ret = v;
2251 return 0;
2252}
2253
b485fd93
LP
2254_public_ int sd_device_get_trigger_uuid(sd_device *device, sd_id128_t *ret) {
2255 const char *s;
2256 sd_id128_t id;
2257 int r;
2258
2259 assert_return(device, -EINVAL);
2260
2261 /* Retrieves the UUID attached to a uevent when triggering it from userspace via
2262 * sd_device_trigger_with_uuid() or an equivalent interface. Returns -ENOENT if the record is not
2263 * caused by a synthetic event and -ENODATA if it was but no UUID was specified */
2264
2265 r = sd_device_get_property_value(device, "SYNTH_UUID", &s);
2266 if (r < 0)
2267 return r;
2268
2269 if (streq(s, "0")) /* SYNTH_UUID=0 is set whenever a device is triggered by userspace without specifying a UUID */
2270 return -ENODATA;
2271
2272 r = sd_id128_from_string(s, &id);
2273 if (r < 0)
2274 return r;
2275
2276 if (ret)
2277 *ret = id;
2278
2279 return 0;
2280}
2281
5eb83e90
YW
2282void device_clear_sysattr_cache(sd_device *device) {
2283 device->sysattr_values = hashmap_free(device->sysattr_values);
2284}
2285
18b11379 2286int device_cache_sysattr_value(sd_device *device, const char *key, char *value) {
d7ac0952
FS
2287 _unused_ _cleanup_free_ char *old_value = NULL;
2288 _cleanup_free_ char *new_key = NULL;
57fa1d09
TG
2289 int r;
2290
2291 assert(device);
61c0972d 2292 assert(key);
57fa1d09 2293
61c0972d
YW
2294 /* This takes the reference of the input value. The input value may be NULL.
2295 * This replaces the value if it already exists. */
57fa1d09 2296
66efcbb1 2297 /* First, remove the old cache entry. So, we do not need to clear cache on error. */
61c0972d
YW
2298 old_value = hashmap_remove2(device->sysattr_values, key, (void **) &new_key);
2299 if (!new_key) {
2300 new_key = strdup(key);
2301 if (!new_key)
57fa1d09
TG
2302 return -ENOMEM;
2303 }
2304
8f5a8d7c 2305 r = hashmap_ensure_put(&device->sysattr_values, &path_hash_ops_free_free, new_key, value);
57fa1d09
TG
2306 if (r < 0)
2307 return r;
61c0972d
YW
2308
2309 TAKE_PTR(new_key);
57fa1d09
TG
2310
2311 return 0;
2312}
2313
18b11379 2314int device_get_cached_sysattr_value(sd_device *device, const char *key, const char **ret_value) {
b63dd5aa 2315 const char *k = NULL, *value;
57fa1d09
TG
2316
2317 assert(device);
b63dd5aa 2318 assert(key);
57fa1d09 2319
b63dd5aa
YW
2320 value = hashmap_get2(device->sysattr_values, key, (void **) &k);
2321 if (!k)
2322 return -ESTALE; /* We have not read the attribute. */
2323 if (!value)
2324 return -ENOENT; /* We have looked up the attribute before and it did not exist. */
2325 if (ret_value)
2326 *ret_value = value;
57fa1d09
TG
2327 return 0;
2328}
2329
2330/* We cache all sysattr lookups. If an attribute does not exist, it is stored
2331 * with a NULL value in the cache, otherwise the returned string is stored */
32a739af 2332_public_ int sd_device_get_sysattr_value(sd_device *device, const char *sysattr, const char **ret_value) {
8e7e4a73
LP
2333 _cleanup_free_ char *value = NULL, *path = NULL;
2334 const char *syspath;
57fa1d09
TG
2335 struct stat statbuf;
2336 int r;
2337
2338 assert_return(device, -EINVAL);
2339 assert_return(sysattr, -EINVAL);
2340
2341 /* look for possibly already cached result */
b63dd5aa
YW
2342 r = device_get_cached_sysattr_value(device, sysattr, ret_value);
2343 if (r != -ESTALE)
2344 return r;
57fa1d09
TG
2345
2346 r = sd_device_get_syspath(device, &syspath);
2347 if (r < 0)
2348 return r;
2349
8e7e4a73
LP
2350 path = path_join(syspath, sysattr);
2351 if (!path)
2352 return -ENOMEM;
2353
e2e40e9a 2354 if (lstat(path, &statbuf) < 0) {
acfc2a1d
YW
2355 int k;
2356
e2e40e9a
LP
2357 r = -errno;
2358
57fa1d09 2359 /* remember that we could not access the sysattr */
acfc2a1d
YW
2360 k = device_cache_sysattr_value(device, sysattr, NULL);
2361 if (k < 0)
2362 log_device_debug_errno(device, k,
2363 "sd-device: failed to cache attribute '%s' with NULL, ignoring: %m",
2364 sysattr);
57fa1d09 2365
acfc2a1d 2366 return r;
57fa1d09
TG
2367 } else if (S_ISLNK(statbuf.st_mode)) {
2368 /* Some core links return only the last element of the target path,
2369 * these are just values, the paths should not be exposed. */
2370 if (STR_IN_SET(sysattr, "driver", "subsystem", "module")) {
2371 r = readlink_value(path, &value);
2372 if (r < 0)
2373 return r;
2374 } else
2375 return -EINVAL;
163e0a88 2376 } else if (S_ISDIR(statbuf.st_mode))
57fa1d09 2377 /* skip directories */
163e0a88
YW
2378 return -EISDIR;
2379 else if (!(statbuf.st_mode & S_IRUSR))
57fa1d09
TG
2380 /* skip non-readable files */
2381 return -EPERM;
163e0a88 2382 else {
70160c6e
YW
2383 size_t size;
2384
2385 /* Read attribute value, Some attributes contain embedded '\0'. So, it is necessary to
2386 * also get the size of the result. See issue #20025. */
2387 r = read_full_virtual_file(path, &value, &size);
57fa1d09
TG
2388 if (r < 0)
2389 return r;
2390
2391 /* drop trailing newlines */
70160c6e
YW
2392 while (size > 0 && strchr(NEWLINE, value[--size]))
2393 value[size] = '\0';
57fa1d09
TG
2394 }
2395
acfc2a1d
YW
2396 /* Unfortunately, we need to return 'const char*' instead of 'char*'. Hence, failure in caching
2397 * sysattr value is critical unlike the other places. */
2a0ebc05 2398 r = device_cache_sysattr_value(device, sysattr, value);
acfc2a1d
YW
2399 if (r < 0) {
2400 log_device_debug_errno(device, r,
2401 "sd-device: failed to cache attribute '%s' with '%s'%s: %m",
2402 sysattr, value, ret_value ? "" : ", ignoring");
2403 if (ret_value)
2404 return r;
57fa1d09 2405
eb18e7b7
YW
2406 return 0;
2407 }
2408
2409 if (ret_value)
2410 *ret_value = value;
2411
2412 TAKE_PTR(value);
57fa1d09
TG
2413 return 0;
2414}
2415
bb1bc2fc
YW
2416int device_get_sysattr_int(sd_device *device, const char *sysattr, int *ret_value) {
2417 const char *value;
2418 int r;
2419
2420 r = sd_device_get_sysattr_value(device, sysattr, &value);
2421 if (r < 0)
2422 return r;
2423
2424 int v;
2425 r = safe_atoi(value, &v);
2426 if (r < 0)
2427 return log_device_debug_errno(device, r, "Failed to parse '%s' attribute: %m", sysattr);
2428
2429 if (ret_value)
2430 *ret_value = v;
2431 /* We return "true" if the value is positive. */
2432 return v > 0;
2433}
2434
48a511cf
ZJS
2435int device_get_sysattr_unsigned(sd_device *device, const char *sysattr, unsigned *ret_value) {
2436 const char *value;
2437 int r;
2438
2439 r = sd_device_get_sysattr_value(device, sysattr, &value);
2440 if (r < 0)
2441 return r;
2442
2443 unsigned v;
2444 r = safe_atou(value, &v);
2445 if (r < 0)
2446 return log_device_debug_errno(device, r, "Failed to parse '%s' attribute: %m", sysattr);
2447
2448 if (ret_value)
2449 *ret_value = v;
2450 /* We return "true" if the value is positive. */
2451 return v > 0;
2452}
2453
2829fca2
YW
2454int device_get_sysattr_bool(sd_device *device, const char *sysattr) {
2455 const char *value;
2456 int r;
2457
2458 assert(device);
2459 assert(sysattr);
2460
2461 r = sd_device_get_sysattr_value(device, sysattr, &value);
2462 if (r < 0)
2463 return r;
2464
2465 return parse_boolean(value);
2466}
2467
2a0ebc05 2468static void device_remove_cached_sysattr_value(sd_device *device, const char *_key) {
57fa1d09 2469 _cleanup_free_ char *key = NULL;
57fa1d09
TG
2470
2471 assert(device);
2472 assert(_key);
2473
2cfb1978 2474 free(hashmap_remove2(device->sysattr_values, _key, (void **) &key));
57fa1d09
TG
2475}
2476
8aae9a66 2477_public_ int sd_device_set_sysattr_value(sd_device *device, const char *sysattr, const char *_value) {
8e7e4a73
LP
2478 _cleanup_free_ char *value = NULL, *path = NULL;
2479 const char *syspath;
7cadbe09 2480 size_t len;
57fa1d09
TG
2481 int r;
2482
2483 assert_return(device, -EINVAL);
2484 assert_return(sysattr, -EINVAL);
2485
2f7a2e39
YW
2486 /* Set the attribute and save it in the cache. */
2487
2fe29a46 2488 if (!_value) {
2f7a2e39 2489 /* If input value is NULL, then clear cache and not write anything. */
2a0ebc05 2490 device_remove_cached_sysattr_value(device, sysattr);
57fa1d09
TG
2491 return 0;
2492 }
2493
2494 r = sd_device_get_syspath(device, &syspath);
2495 if (r < 0)
2496 return r;
2497
8e7e4a73
LP
2498 path = path_join(syspath, sysattr);
2499 if (!path)
2500 return -ENOMEM;
2fa4861a 2501
2fa4861a 2502 len = strlen(_value);
57fa1d09
TG
2503
2504 /* drop trailing newlines */
2a394d0b 2505 while (len > 0 && strchr(NEWLINE, _value[len - 1]))
2fa4861a 2506 len --;
57fa1d09
TG
2507
2508 /* value length is limited to 4k */
2fa4861a 2509 if (len > 4096)
57fa1d09
TG
2510 return -EINVAL;
2511
2fa4861a 2512 value = strndup(_value, len);
2fe29a46
TG
2513 if (!value)
2514 return -ENOMEM;
2515
7cadbe09
YW
2516 r = write_string_file(path, value, WRITE_STRING_FILE_DISABLE_BUFFER | WRITE_STRING_FILE_NOFOLLOW);
2517 if (r < 0) {
23ff570b
YW
2518 /* On failure, clear cache entry, as we do not know how it fails. */
2519 device_remove_cached_sysattr_value(device, sysattr);
2520 return r;
7cadbe09 2521 }
57fa1d09 2522
297d1370
YW
2523 /* Do not cache action string written into uevent file. */
2524 if (streq(sysattr, "uevent"))
2525 return 0;
2526
2a0ebc05 2527 r = device_cache_sysattr_value(device, sysattr, value);
57fa1d09 2528 if (r < 0)
acfc2a1d
YW
2529 log_device_debug_errno(device, r,
2530 "sd-device: failed to cache attribute '%s' with '%s', ignoring: %m",
2531 sysattr, value);
2532 else
2533 TAKE_PTR(value);
2fe29a46 2534
57fa1d09
TG
2535 return 0;
2536}
ea2bc257
YW
2537
2538_public_ int sd_device_set_sysattr_valuef(sd_device *device, const char *sysattr, const char *format, ...) {
2539 _cleanup_free_ char *value = NULL;
2540 va_list ap;
2541 int r;
2542
2543 assert_return(device, -EINVAL);
2544 assert_return(sysattr, -EINVAL);
2545
2546 if (!format) {
2a0ebc05 2547 device_remove_cached_sysattr_value(device, sysattr);
ea2bc257
YW
2548 return 0;
2549 }
2550
2551 va_start(ap, format);
2552 r = vasprintf(&value, format, ap);
2553 va_end(ap);
2554
2555 if (r < 0)
2556 return -ENOMEM;
2557
2558 return sd_device_set_sysattr_value(device, sysattr, value);
2559}
f4d36efa
YW
2560
2561_public_ int sd_device_trigger(sd_device *device, sd_device_action_t action) {
2562 const char *s;
2563
2564 assert_return(device, -EINVAL);
2565
2566 s = device_action_to_string(action);
2567 if (!s)
2568 return -EINVAL;
2569
b485fd93 2570 /* This uses the simple no-UUID interface of kernel < 4.13 */
f4d36efa
YW
2571 return sd_device_set_sysattr_value(device, "uevent", s);
2572}
b485fd93
LP
2573
2574_public_ int sd_device_trigger_with_uuid(
2575 sd_device *device,
2576 sd_device_action_t action,
2577 sd_id128_t *ret_uuid) {
2578
b485fd93
LP
2579 const char *s, *j;
2580 sd_id128_t u;
2581 int r;
2582
2583 assert_return(device, -EINVAL);
2584
48008c1c 2585 /* If no one wants to know the UUID, use the simple interface from pre-4.13 times */
b485fd93
LP
2586 if (!ret_uuid)
2587 return sd_device_trigger(device, action);
2588
2589 s = device_action_to_string(action);
2590 if (!s)
2591 return -EINVAL;
2592
2593 r = sd_id128_randomize(&u);
2594 if (r < 0)
2595 return r;
2596
b7416360 2597 j = strjoina(s, " ", SD_ID128_TO_UUID_STRING(u));
b485fd93
LP
2598
2599 r = sd_device_set_sysattr_value(device, "uevent", j);
2600 if (r < 0)
2601 return r;
2602
2603 *ret_uuid = u;
2604 return 0;
2605}
f9a726ab
YW
2606
2607_public_ int sd_device_open(sd_device *device, int flags) {
254d1313 2608 _cleanup_close_ int fd = -EBADF, fd2 = -EBADF;
15345fc6 2609 const char *devname, *subsystem = NULL;
f9a726ab
YW
2610 uint64_t q, diskseq = 0;
2611 struct stat st;
2612 dev_t devnum;
2613 int r;
2614
2615 assert_return(device, -EINVAL);
2616 assert_return(FLAGS_SET(flags, O_PATH) || !FLAGS_SET(flags, O_NOFOLLOW), -EINVAL);
2617
2618 r = sd_device_get_devname(device, &devname);
2619 if (r == -ENOENT)
2620 return -ENOEXEC;
2621 if (r < 0)
2622 return r;
2623
2624 r = sd_device_get_devnum(device, &devnum);
2625 if (r == -ENOENT)
2626 return -ENOEXEC;
2627 if (r < 0)
2628 return r;
2629
2630 r = sd_device_get_subsystem(device, &subsystem);
2631 if (r < 0 && r != -ENOENT)
2632 return r;
2633
f9a726ab
YW
2634 fd = open(devname, FLAGS_SET(flags, O_PATH) ? flags : O_CLOEXEC|O_NOFOLLOW|O_PATH);
2635 if (fd < 0)
2636 return -errno;
2637
2638 if (fstat(fd, &st) < 0)
2639 return -errno;
2640
2641 if (st.st_rdev != devnum)
2642 return -ENXIO;
2643
2644 if (streq_ptr(subsystem, "block") ? !S_ISBLK(st.st_mode) : !S_ISCHR(st.st_mode))
2645 return -ENXIO;
2646
2647 /* If flags has O_PATH, then we cannot check diskseq. Let's return earlier. */
2648 if (FLAGS_SET(flags, O_PATH))
2649 return TAKE_FD(fd);
2650
42899bce
YW
2651 /* If the device is not initialized, then we cannot determine if we should check diskseq through
2652 * ID_IGNORE_DISKSEQ property. Let's skip to check diskseq in that case. */
2653 r = sd_device_get_is_initialized(device);
2654 if (r < 0)
03b894fc 2655 return r;
42899bce
YW
2656 if (r > 0) {
2657 r = device_get_property_bool(device, "ID_IGNORE_DISKSEQ");
03b894fc
YW
2658 if (r < 0 && r != -ENOENT)
2659 return r;
42899bce
YW
2660 if (r <= 0) {
2661 r = sd_device_get_diskseq(device, &diskseq);
2662 if (r < 0 && r != -ENOENT)
2663 return r;
2664 }
03b894fc
YW
2665 }
2666
83b94cf1 2667 fd2 = fd_reopen(fd, flags);
f9a726ab 2668 if (fd2 < 0)
83b94cf1 2669 return fd2;
f9a726ab
YW
2670
2671 if (diskseq == 0)
2672 return TAKE_FD(fd2);
2673
2674 r = fd_get_diskseq(fd2, &q);
2675 if (r < 0)
2676 return r;
2677
2678 if (q != diskseq)
2679 return -ENXIO;
2680
2681 return TAKE_FD(fd2);
2682}
db3049b6
YW
2683
2684int device_opendir(sd_device *device, const char *subdir, DIR **ret) {
2685 _cleanup_closedir_ DIR *d = NULL;
2686 _cleanup_free_ char *path = NULL;
2687 const char *syspath;
2688 int r;
2689
2690 assert(device);
2691 assert(ret);
2692
2693 r = sd_device_get_syspath(device, &syspath);
2694 if (r < 0)
2695 return r;
2696
2697 if (subdir) {
2698 if (!path_is_safe(subdir))
2699 return -EINVAL;
2700
2701 path = path_join(syspath, subdir);
2702 if (!path)
2703 return -ENOMEM;
2704 }
2705
2706 d = opendir(path ?: syspath);
2707 if (!d)
2708 return -errno;
2709
2710 *ret = TAKE_PTR(d);
2711 return 0;
2712}