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