]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/libsystemd/sd-device/test-sd-device.c
Merge pull request #24054 from keszybz/initrd-no-reload
[thirdparty/systemd.git] / src / libsystemd / sd-device / test-sd-device.c
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3 #include <ctype.h>
4 #include <fcntl.h>
5 #include <unistd.h>
6
7 #include "device-enumerator-private.h"
8 #include "device-internal.h"
9 #include "device-private.h"
10 #include "device-util.h"
11 #include "errno-util.h"
12 #include "fd-util.h"
13 #include "hashmap.h"
14 #include "nulstr-util.h"
15 #include "path-util.h"
16 #include "rm-rf.h"
17 #include "string-util.h"
18 #include "tests.h"
19 #include "time-util.h"
20 #include "tmpfile-util.h"
21
22 static void test_sd_device_one(sd_device *d) {
23 _cleanup_(sd_device_unrefp) sd_device *dev = NULL;
24 const char *syspath, *sysname, *subsystem = NULL, *devname, *val;
25 bool is_block = false;
26 dev_t devnum;
27 usec_t usec;
28 int ifindex, r;
29
30 assert_se(sd_device_get_syspath(d, &syspath) >= 0);
31 assert_se(path_startswith(syspath, "/sys"));
32 assert_se(sd_device_get_sysname(d, &sysname) >= 0);
33
34 log_info("%s(%s)", __func__, syspath);
35
36 assert_se(sd_device_new_from_syspath(&dev, syspath) >= 0);
37 assert_se(sd_device_get_syspath(dev, &val) >= 0);
38 assert_se(streq(syspath, val));
39 dev = sd_device_unref(dev);
40
41 assert_se(sd_device_new_from_path(&dev, syspath) >= 0);
42 assert_se(sd_device_get_syspath(dev, &val) >= 0);
43 assert_se(streq(syspath, val));
44 dev = sd_device_unref(dev);
45
46 r = sd_device_get_ifindex(d, &ifindex);
47 if (r >= 0) {
48 assert_se(ifindex > 0);
49
50 r = sd_device_new_from_ifindex(&dev, ifindex);
51 if (r == -ENODEV)
52 log_device_warning_errno(d, r,
53 "Failed to create sd-device object from ifindex %i. "
54 "Maybe running on a non-host network namespace.", ifindex);
55 else {
56 assert_se(r >= 0);
57 assert_se(sd_device_get_syspath(dev, &val) >= 0);
58 assert_se(streq(syspath, val));
59 dev = sd_device_unref(dev);
60 }
61
62 /* This does not require the interface really exists on the network namespace.
63 * Hence, this should always succeed. */
64 assert_se(sd_device_new_from_ifname(&dev, sysname) >= 0);
65 assert_se(sd_device_get_syspath(dev, &val) >= 0);
66 assert_se(streq(syspath, val));
67 dev = sd_device_unref(dev);
68 } else
69 assert_se(r == -ENOENT);
70
71 r = sd_device_get_subsystem(d, &subsystem);
72 if (r < 0)
73 assert_se(r == -ENOENT);
74 else if (!streq(subsystem, "gpio")) { /* Unfortunately, there exist /sys/class/gpio and /sys/bus/gpio.
75 * Hence, sd_device_new_from_subsystem_sysname() and
76 * sd_device_new_from_device_id() may not work as expected. */
77 const char *name, *id;
78
79 if (streq(subsystem, "drivers"))
80 name = strjoina(d->driver_subsystem, ":", sysname);
81 else
82 name = sysname;
83 assert_se(sd_device_new_from_subsystem_sysname(&dev, subsystem, name) >= 0);
84 assert_se(sd_device_get_syspath(dev, &val) >= 0);
85 assert_se(streq(syspath, val));
86 dev = sd_device_unref(dev);
87
88 /* The device ID depends on subsystem. */
89 assert_se(device_get_device_id(d, &id) >= 0);
90 r = sd_device_new_from_device_id(&dev, id);
91 if (r == -ENODEV && ifindex > 0)
92 log_device_warning_errno(d, r,
93 "Failed to create sd-device object from device ID \"%s\". "
94 "Maybe running on a non-host network namespace.", id);
95 else {
96 assert_se(r >= 0);
97 assert_se(sd_device_get_syspath(dev, &val) >= 0);
98 assert_se(streq(syspath, val));
99 dev = sd_device_unref(dev);
100 }
101
102 /* These require udev database, and reading database requires device ID. */
103 r = sd_device_get_is_initialized(d);
104 if (r > 0) {
105 r = sd_device_get_usec_since_initialized(d, &usec);
106 assert_se((r >= 0 && usec > 0) || r == -ENODATA);
107 } else
108 assert(r == 0);
109
110 r = sd_device_get_property_value(d, "ID_NET_DRIVER", &val);
111 assert_se(r >= 0 || r == -ENOENT);
112 }
113
114 is_block = streq_ptr(subsystem, "block");
115
116 r = sd_device_get_devname(d, &devname);
117 if (r >= 0) {
118 r = sd_device_new_from_devname(&dev, devname);
119 if (r >= 0) {
120 assert_se(sd_device_get_syspath(dev, &val) >= 0);
121 assert_se(streq(syspath, val));
122 dev = sd_device_unref(dev);
123 } else
124 assert_se(r == -ENODEV || ERRNO_IS_PRIVILEGE(r));
125
126 r = sd_device_new_from_path(&dev, devname);
127 if (r >= 0) {
128 assert_se(sd_device_get_syspath(dev, &val) >= 0);
129 assert_se(streq(syspath, val));
130 dev = sd_device_unref(dev);
131
132 _cleanup_close_ int fd = -1;
133 fd = sd_device_open(d, O_CLOEXEC| O_NONBLOCK | (is_block ? O_RDONLY : O_NOCTTY | O_PATH));
134 assert_se(fd >= 0 || ERRNO_IS_PRIVILEGE(fd));
135 } else
136 assert_se(r == -ENODEV || ERRNO_IS_PRIVILEGE(r));
137 } else
138 assert_se(r == -ENOENT);
139
140 r = sd_device_get_devnum(d, &devnum);
141 if (r >= 0) {
142 _cleanup_free_ char *p = NULL;
143
144 assert_se(major(devnum) > 0);
145
146 assert_se(sd_device_new_from_devnum(&dev, is_block ? 'b' : 'c', devnum) >= 0);
147 assert_se(sd_device_get_syspath(dev, &val) >= 0);
148 assert_se(streq(syspath, val));
149 dev = sd_device_unref(dev);
150
151 assert_se(asprintf(&p, "/dev/%s/%u:%u", is_block ? "block" : "char", major(devnum), minor(devnum)) >= 0);
152 assert_se(sd_device_new_from_devname(&dev, p) >= 0);
153 assert_se(sd_device_get_syspath(dev, &val) >= 0);
154 assert_se(streq(syspath, val));
155 dev = sd_device_unref(dev);
156
157 assert_se(sd_device_new_from_path(&dev, p) >= 0);
158 assert_se(sd_device_get_syspath(dev, &val) >= 0);
159 assert_se(streq(syspath, val));
160 dev = sd_device_unref(dev);
161 } else
162 assert_se(r == -ENOENT);
163
164 assert_se(sd_device_get_devpath(d, &val) >= 0);
165
166 r = sd_device_get_devtype(d, &val);
167 assert_se(r >= 0 || r == -ENOENT);
168
169 r = sd_device_get_driver(d, &val);
170 assert_se(r >= 0 || r == -ENOENT);
171
172 r = sd_device_get_sysnum(d, &val);
173 if (r >= 0) {
174 assert_se(val > sysname);
175 assert_se(val < sysname + strlen(sysname));
176 assert_se(in_charset(val, DIGITS));
177 assert_se(!ascii_isdigit(val[-1]));
178 } else
179 assert_se(r == -ENOENT);
180
181 r = sd_device_get_sysattr_value(d, "name_assign_type", &val);
182 assert_se(r >= 0 || ERRNO_IS_PRIVILEGE(r) || IN_SET(r, -ENOENT, -EINVAL));
183 }
184
185 TEST(sd_device_enumerator_devices) {
186 _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL;
187 sd_device *d;
188
189 assert_se(sd_device_enumerator_new(&e) >= 0);
190 assert_se(sd_device_enumerator_allow_uninitialized(e) >= 0);
191 /* On some CI environments, it seems some loop block devices and corresponding bdi devices sometimes
192 * disappear during running this test. Let's exclude them here for stability. */
193 assert_se(sd_device_enumerator_add_match_subsystem(e, "bdi", false) >= 0);
194 assert_se(sd_device_enumerator_add_nomatch_sysname(e, "loop*") >= 0);
195 /* On CentOS CI, systemd-networkd-tests.py may be running when this test is invoked. The networkd
196 * test creates and removes many network interfaces, and may interfere with this test. */
197 assert_se(sd_device_enumerator_add_match_subsystem(e, "net", false) >= 0);
198 FOREACH_DEVICE(e, d)
199 test_sd_device_one(d);
200 }
201
202 TEST(sd_device_enumerator_subsystems) {
203 _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL;
204 sd_device *d;
205
206 assert_se(sd_device_enumerator_new(&e) >= 0);
207 assert_se(sd_device_enumerator_allow_uninitialized(e) >= 0);
208 FOREACH_SUBSYSTEM(e, d)
209 test_sd_device_one(d);
210 }
211
212 static void test_sd_device_enumerator_filter_subsystem_one(
213 const char *subsystem,
214 Hashmap *h,
215 unsigned *ret_n_new_dev,
216 unsigned *ret_n_removed_dev) {
217
218 _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL;
219 unsigned n_new_dev = 0, n_removed_dev = 0;
220 sd_device *d;
221
222 assert_se(sd_device_enumerator_new(&e) >= 0);
223 assert_se(sd_device_enumerator_add_match_subsystem(e, subsystem, true) >= 0);
224 assert_se(sd_device_enumerator_add_nomatch_sysname(e, "loop*") >= 0);
225
226 FOREACH_DEVICE(e, d) {
227 const char *syspath;
228 sd_device *t;
229
230 assert_se(sd_device_get_syspath(d, &syspath) >= 0);
231 t = hashmap_remove(h, syspath);
232
233 if (!t) {
234 log_warning("New device found: subsystem:%s syspath:%s", subsystem, syspath);
235 n_new_dev++;
236 }
237
238 assert_se(!sd_device_unref(t));
239 }
240
241 HASHMAP_FOREACH(d, h) {
242 const char *syspath;
243
244 assert_se(sd_device_get_syspath(d, &syspath) >= 0);
245 log_warning("Device removed: subsystem:%s syspath:%s", subsystem, syspath);
246 n_removed_dev++;
247
248 assert_se(!sd_device_unref(d));
249 }
250
251 hashmap_free(h);
252
253 *ret_n_new_dev = n_new_dev;
254 *ret_n_removed_dev = n_removed_dev;
255 }
256
257 TEST(sd_device_enumerator_filter_subsystem) {
258 _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL;
259 _cleanup_(hashmap_freep) Hashmap *subsystems;
260 unsigned n_new_dev = 0, n_removed_dev = 0;
261 sd_device *d;
262 Hashmap *h;
263 char *s;
264
265 assert_se(subsystems = hashmap_new(&string_hash_ops));
266 assert_se(sd_device_enumerator_new(&e) >= 0);
267 /* See comments in TEST(sd_device_enumerator_devices). */
268 assert_se(sd_device_enumerator_add_match_subsystem(e, "bdi", false) >= 0);
269 assert_se(sd_device_enumerator_add_nomatch_sysname(e, "loop*") >= 0);
270 assert_se(sd_device_enumerator_add_match_subsystem(e, "net", false) >= 0);
271
272 FOREACH_DEVICE(e, d) {
273 const char *syspath, *subsystem;
274 int r;
275
276 assert_se(sd_device_get_syspath(d, &syspath) >= 0);
277
278 r = sd_device_get_subsystem(d, &subsystem);
279 assert_se(r >= 0 || r == -ENOENT);
280 if (r < 0)
281 continue;
282
283 h = hashmap_get(subsystems, subsystem);
284 if (!h) {
285 char *str;
286 assert_se(str = strdup(subsystem));
287 assert_se(h = hashmap_new(&string_hash_ops));
288 assert_se(hashmap_put(subsystems, str, h) >= 0);
289 }
290
291 assert_se(hashmap_put(h, syspath, d) >= 0);
292 assert_se(sd_device_ref(d));
293
294 log_debug("Added subsystem:%s syspath:%s", subsystem, syspath);
295 }
296
297 while ((h = hashmap_steal_first_key_and_value(subsystems, (void**) &s))) {
298 unsigned n, m;
299
300 test_sd_device_enumerator_filter_subsystem_one(s, TAKE_PTR(h), &n, &m);
301 free(s);
302
303 n_new_dev += n;
304 n_removed_dev += m;
305 }
306
307 if (n_new_dev > 0)
308 log_warning("%u new devices are found in re-scan", n_new_dev);
309 if (n_removed_dev > 0)
310 log_warning("%u devices removed in re-scan", n_removed_dev);
311
312 /* Assume that not so many devices are plugged or unplugged. */
313 assert_se(n_new_dev + n_removed_dev <= 10);
314 }
315
316 TEST(sd_device_enumerator_add_match_sysattr) {
317 _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL;
318 sd_device *dev;
319 int ifindex;
320
321 assert_se(sd_device_enumerator_new(&e) >= 0);
322 assert_se(sd_device_enumerator_allow_uninitialized(e) >= 0);
323 assert_se(sd_device_enumerator_add_match_subsystem(e, "net", true) >= 0);
324 assert_se(sd_device_enumerator_add_match_sysattr(e, "ifindex", "1", true) >= 0);
325 assert_se(sd_device_enumerator_add_match_sysattr(e, "ifindex", "hoge", true) >= 0);
326 assert_se(sd_device_enumerator_add_match_sysattr(e, "ifindex", "foo", true) >= 0);
327 assert_se(sd_device_enumerator_add_match_sysattr(e, "ifindex", "bar", false) >= 0);
328 assert_se(sd_device_enumerator_add_match_sysattr(e, "ifindex", "baz", false) >= 0);
329
330 dev = sd_device_enumerator_get_device_first(e);
331 assert_se(dev);
332 assert_se(sd_device_get_ifindex(dev, &ifindex) >= 0);
333 assert_se(ifindex == 1);
334
335 assert_se(!sd_device_enumerator_get_device_next(e));
336 }
337
338 TEST(sd_device_enumerator_add_match_property) {
339 _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL;
340 sd_device *dev;
341 int ifindex;
342
343 assert_se(sd_device_enumerator_new(&e) >= 0);
344 assert_se(sd_device_enumerator_allow_uninitialized(e) >= 0);
345 assert_se(sd_device_enumerator_add_match_subsystem(e, "net", true) >= 0);
346 assert_se(sd_device_enumerator_add_match_sysattr(e, "ifindex", "1", true) >= 0);
347 assert_se(sd_device_enumerator_add_match_property(e, "IFINDE*", "1*") >= 0);
348 assert_se(sd_device_enumerator_add_match_property(e, "IFINDE*", "hoge") >= 0);
349 assert_se(sd_device_enumerator_add_match_property(e, "IFINDE*", NULL) >= 0);
350 assert_se(sd_device_enumerator_add_match_property(e, "AAAAA", "BBBB") >= 0);
351 assert_se(sd_device_enumerator_add_match_property(e, "FOOOO", NULL) >= 0);
352
353 dev = sd_device_enumerator_get_device_first(e);
354 assert_se(dev);
355 assert_se(sd_device_get_ifindex(dev, &ifindex) >= 0);
356 assert_se(ifindex == 1);
357 }
358
359 TEST(sd_device_new_from_nulstr) {
360 const char *devlinks =
361 "/dev/disk/by-partuuid/1290d63a-42cc-4c71-b87c-xxxxxxxxxxxx\0"
362 "/dev/disk/by-path/pci-0000:00:0f.0-scsi-0:0:0:0-part3\0"
363 "/dev/disk/by-label/Arch\\x20Linux\0"
364 "/dev/disk/by-uuid/a07b87e5-4af5-4a59-bde9-yyyyyyyyyyyy\0"
365 "/dev/disk/by-partlabel/Arch\\x20Linux\0"
366 "\0";
367
368 _cleanup_(sd_device_unrefp) sd_device *device = NULL, *from_nulstr = NULL;
369 _cleanup_free_ char *nulstr_copy = NULL;
370 const char *devlink, *nulstr;
371 size_t len;
372
373 assert_se(sd_device_new_from_syspath(&device, "/sys/class/net/lo") >= 0);
374
375 /* Yeah, of course, setting devlink to the loopback interface is nonsense. But this is just a
376 * test for generating and parsing nulstr. For issue #17772. */
377 NULSTR_FOREACH(devlink, devlinks) {
378 log_device_info(device, "setting devlink: %s", devlink);
379 assert_se(device_add_devlink(device, devlink) >= 0);
380 assert_se(set_contains(device->devlinks, devlink));
381 }
382
383 /* For issue #23799 */
384 assert_se(device_add_tag(device, "tag1", false) >= 0);
385 assert_se(device_add_tag(device, "tag2", false) >= 0);
386 assert_se(device_add_tag(device, "current-tag1", true) >= 0);
387 assert_se(device_add_tag(device, "current-tag2", true) >= 0);
388
389 /* These properties are necessary for device_new_from_nulstr(). See device_verify(). */
390 assert_se(device_add_property_internal(device, "SEQNUM", "1") >= 0);
391 assert_se(device_add_property_internal(device, "ACTION", "change") >= 0);
392
393 assert_se(device_get_properties_nulstr(device, &nulstr, &len) >= 0);
394 assert_se(nulstr_copy = newdup(char, nulstr, len));
395 assert_se(device_new_from_nulstr(&from_nulstr, nulstr_copy, len) >= 0);
396
397 assert_se(sd_device_has_tag(from_nulstr, "tag1") == 1);
398 assert_se(sd_device_has_tag(from_nulstr, "tag2") == 1);
399 assert_se(sd_device_has_tag(from_nulstr, "current-tag1") == 1);
400 assert_se(sd_device_has_tag(from_nulstr, "current-tag2") == 1);
401 assert_se(sd_device_has_current_tag(from_nulstr, "tag1") == 0);
402 assert_se(sd_device_has_current_tag(from_nulstr, "tag2") == 0);
403 assert_se(sd_device_has_current_tag(from_nulstr, "current-tag1") == 1);
404 assert_se(sd_device_has_current_tag(from_nulstr, "current-tag2") == 1);
405
406 NULSTR_FOREACH(devlink, devlinks) {
407 log_device_info(from_nulstr, "checking devlink: %s", devlink);
408 assert_se(set_contains(from_nulstr->devlinks, devlink));
409 }
410 }
411
412 TEST(sd_device_new_from_path) {
413 _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL;
414 _cleanup_(rm_rf_physical_and_freep) char *tmpdir = NULL;
415 sd_device *dev;
416 int r;
417
418 assert_se(mkdtemp_malloc("/tmp/test-sd-device.XXXXXXX", &tmpdir) >= 0);
419
420 assert_se(sd_device_enumerator_new(&e) >= 0);
421 assert_se(sd_device_enumerator_allow_uninitialized(e) >= 0);
422 assert_se(sd_device_enumerator_add_match_subsystem(e, "block", true) >= 0);
423 assert_se(sd_device_enumerator_add_nomatch_sysname(e, "loop*") >= 0);
424 assert_se(sd_device_enumerator_add_match_property(e, "DEVNAME", "*") >= 0);
425
426 FOREACH_DEVICE(e, dev) {
427 _cleanup_(sd_device_unrefp) sd_device *d = NULL;
428 const char *syspath, *devpath, *sysname, *s;
429 _cleanup_free_ char *path = NULL;
430
431 assert_se(sd_device_get_sysname(dev, &sysname) >= 0);
432
433 log_debug("%s(%s)", __func__, sysname);
434
435 assert_se(sd_device_get_syspath(dev, &syspath) >= 0);
436 assert_se(sd_device_new_from_path(&d, syspath) >= 0);
437 assert_se(sd_device_get_syspath(d, &s) >= 0);
438 assert_se(streq(s, syspath));
439 d = sd_device_unref(d);
440
441 assert_se(sd_device_get_devname(dev, &devpath) >= 0);
442 r = sd_device_new_from_path(&d, devpath);
443 if (r >= 0) {
444 assert_se(sd_device_get_syspath(d, &s) >= 0);
445 assert_se(streq(s, syspath));
446 d = sd_device_unref(d);
447 } else
448 assert_se(r == -ENODEV || ERRNO_IS_PRIVILEGE(r));
449
450 assert_se(path = path_join(tmpdir, sysname));
451 assert_se(symlink(syspath, path) >= 0);
452 assert_se(sd_device_new_from_path(&d, path) >= 0);
453 assert_se(sd_device_get_syspath(d, &s) >= 0);
454 assert_se(streq(s, syspath));
455 }
456 }
457
458 DEFINE_TEST_MAIN(LOG_INFO);