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