]>
Commit | Line | Data |
---|---|---|
1 | /* SPDX-License-Identifier: LGPL-2.1-or-later */ | |
2 | ||
3 | #include <fcntl.h> | |
4 | #include <unistd.h> | |
5 | ||
6 | #include "sd-event.h" | |
7 | ||
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 "fs-util.h" | |
14 | #include "hashmap.h" | |
15 | #include "mkdir.h" | |
16 | #include "mount-util.h" | |
17 | #include "mountpoint-util.h" | |
18 | #include "nulstr-util.h" | |
19 | #include "path-util.h" | |
20 | #include "process-util.h" | |
21 | #include "rm-rf.h" | |
22 | #include "set.h" | |
23 | #include "stat-util.h" | |
24 | #include "string-util.h" | |
25 | #include "tests.h" | |
26 | #include "tmpfile-util.h" | |
27 | #include "udev-util.h" | |
28 | ||
29 | TEST(mdio_bus) { | |
30 | int r; | |
31 | ||
32 | /* For issue #37711 */ | |
33 | ||
34 | if (getuid() != 0) | |
35 | return (void) log_tests_skipped("not running as root"); | |
36 | ||
37 | ASSERT_OK(r = safe_fork("(mdio_bus)", FORK_CLOSE_ALL_FDS|FORK_DEATHSIG_SIGTERM|FORK_REOPEN_LOG|FORK_LOG|FORK_WAIT|FORK_NEW_MOUNTNS|FORK_MOUNTNS_SLAVE, NULL)); | |
38 | if (r == 0) { | |
39 | const char *syspath = "/sys/bus/mdio_bus/drivers/Qualcomm Atheros AR8031!AR8033"; | |
40 | const char *id = "+drivers:mdio_bus:Qualcomm Atheros AR8031!AR8033"; | |
41 | ||
42 | struct { | |
43 | int (*getter)(sd_device*, const char**); | |
44 | const char *val; | |
45 | } table[] = { | |
46 | { sd_device_get_syspath, syspath }, | |
47 | { sd_device_get_device_id, id }, | |
48 | { sd_device_get_subsystem, "drivers" }, | |
49 | { sd_device_get_driver_subsystem, "mdio_bus" }, | |
50 | { sd_device_get_sysname, "Qualcomm Atheros AR8031/AR8033" }, | |
51 | }; | |
52 | ||
53 | ASSERT_OK_ERRNO(setenv("SYSTEMD_DEVICE_VERIFY_SYSFS", "0", /* overwrite = */ false)); | |
54 | ASSERT_OK(mount_nofollow_verbose(LOG_ERR, "tmpfs", "/sys/bus/", "tmpfs", 0, NULL)); | |
55 | r = mkdir_p(syspath, 0755); | |
56 | if (ERRNO_IS_NEG_PRIVILEGE(r)) { | |
57 | log_tests_skipped("Lacking privileges to create %s", syspath); | |
58 | _exit(EXIT_SUCCESS); | |
59 | } | |
60 | ASSERT_OK(r); | |
61 | ||
62 | _cleanup_free_ char *uevent = path_join(syspath, "uevent"); | |
63 | ASSERT_NOT_NULL(uevent); | |
64 | ASSERT_OK(touch(uevent)); | |
65 | ||
66 | _cleanup_(sd_device_unrefp) sd_device *dev = NULL; | |
67 | ASSERT_OK(sd_device_new_from_syspath(&dev, syspath)); | |
68 | ||
69 | FOREACH_ELEMENT(t, table) { | |
70 | const char *v; | |
71 | ||
72 | ASSERT_OK(t->getter(dev, &v)); | |
73 | ASSERT_STREQ(v, t->val); | |
74 | } | |
75 | ||
76 | dev = sd_device_unref(dev); | |
77 | ASSERT_OK(sd_device_new_from_device_id(&dev, id)); | |
78 | ||
79 | FOREACH_ELEMENT(t, table) { | |
80 | const char *v; | |
81 | ||
82 | ASSERT_OK(t->getter(dev, &v)); | |
83 | ASSERT_STREQ(v, t->val); | |
84 | } | |
85 | ||
86 | _exit(EXIT_SUCCESS); | |
87 | } | |
88 | } | |
89 | ||
90 | static void test_sd_device_one(sd_device *d) { | |
91 | _cleanup_(sd_device_unrefp) sd_device *dev = NULL; | |
92 | const char *syspath, *sysname, *subsystem = NULL, *devname, *val; | |
93 | bool is_block = false; | |
94 | dev_t devnum; | |
95 | usec_t usec; | |
96 | int ifindex, r; | |
97 | ||
98 | ASSERT_OK(sd_device_get_syspath(d, &syspath)); | |
99 | ASSERT_NOT_NULL(path_startswith(syspath, "/sys")); | |
100 | ASSERT_OK(sd_device_get_sysname(d, &sysname)); | |
101 | ||
102 | log_info("%s(%s)", __func__, syspath); | |
103 | ||
104 | ASSERT_OK(sd_device_new_from_syspath(&dev, syspath)); | |
105 | ASSERT_OK(sd_device_get_syspath(dev, &val)); | |
106 | ASSERT_STREQ(syspath, val); | |
107 | ASSERT_NULL(dev = sd_device_unref(dev)); | |
108 | ||
109 | ASSERT_OK(sd_device_new_from_path(&dev, syspath)); | |
110 | ASSERT_OK(sd_device_get_syspath(dev, &val)); | |
111 | ASSERT_STREQ(syspath, val); | |
112 | ASSERT_NULL(dev = sd_device_unref(dev)); | |
113 | ||
114 | r = sd_device_get_ifindex(d, &ifindex); | |
115 | if (r < 0) | |
116 | ASSERT_ERROR(r, ENOENT); | |
117 | else { | |
118 | ASSERT_GT(ifindex, 0); | |
119 | ||
120 | const char *ifname; | |
121 | ASSERT_OK(device_get_ifname(d, &ifname)); | |
122 | ASSERT_NOT_NULL(endswith(syspath, ifname)); | |
123 | if (strchr(sysname, '/')) | |
124 | ASSERT_FALSE(streq(ifname, sysname)); | |
125 | else | |
126 | ASSERT_STREQ(ifname, sysname); | |
127 | ||
128 | r = sd_device_new_from_ifindex(&dev, ifindex); | |
129 | if (r < 0) { | |
130 | ASSERT_ERROR(r, ENODEV); | |
131 | log_device_warning_errno(d, r, | |
132 | "Failed to create sd-device object from ifindex %i. " | |
133 | "Maybe running on a non-host network namespace.", ifindex); | |
134 | } else { | |
135 | ASSERT_OK(sd_device_get_syspath(dev, &val)); | |
136 | ASSERT_STREQ(syspath, val); | |
137 | ASSERT_NULL(dev = sd_device_unref(dev)); | |
138 | } | |
139 | ||
140 | /* This does not require the interface really exists on the network namespace. | |
141 | * Hence, this should always succeed. */ | |
142 | ASSERT_OK(sd_device_new_from_ifname(&dev, sysname)); | |
143 | ASSERT_OK(sd_device_get_syspath(dev, &val)); | |
144 | ASSERT_STREQ(syspath, val); | |
145 | ASSERT_NULL(dev = sd_device_unref(dev)); | |
146 | } | |
147 | ||
148 | r = sd_device_get_subsystem(d, &subsystem); | |
149 | if (r < 0) | |
150 | ASSERT_ERROR(r, ENOENT); | |
151 | else { | |
152 | const char *name, *id; | |
153 | ||
154 | if (streq(subsystem, "drivers")) { | |
155 | const char *driver_subsystem; | |
156 | ASSERT_OK(sd_device_get_driver_subsystem(d, &driver_subsystem)); | |
157 | name = strjoina(driver_subsystem, ":", sysname); | |
158 | } else | |
159 | name = sysname; | |
160 | ||
161 | r = sd_device_new_from_subsystem_sysname(&dev, subsystem, name); | |
162 | if (r < 0) | |
163 | ASSERT_ERROR(r, ETOOMANYREFS); | |
164 | else { | |
165 | ASSERT_OK(sd_device_get_syspath(dev, &val)); | |
166 | ASSERT_STREQ(syspath, val); | |
167 | ASSERT_NULL(dev = sd_device_unref(dev)); | |
168 | } | |
169 | ||
170 | /* The device ID depends on subsystem. */ | |
171 | ASSERT_OK(sd_device_get_device_id(d, &id)); | |
172 | r = sd_device_new_from_device_id(&dev, id); | |
173 | if (r < 0) { | |
174 | if (r == -ENODEV && ifindex > 0) | |
175 | log_device_warning_errno(d, r, | |
176 | "Failed to create sd-device object from device ID \"%s\". " | |
177 | "Maybe running on a non-host network namespace.", id); | |
178 | else | |
179 | ASSERT_ERROR(r, ETOOMANYREFS); | |
180 | } else { | |
181 | ASSERT_OK(sd_device_get_syspath(dev, &val)); | |
182 | ASSERT_STREQ(syspath, val); | |
183 | ASSERT_NULL(dev = sd_device_unref(dev)); | |
184 | } | |
185 | ||
186 | /* These require udev database, and reading database requires device ID. */ | |
187 | ASSERT_OK(r = sd_device_get_is_initialized(d)); | |
188 | if (r > 0) { | |
189 | r = sd_device_get_usec_since_initialized(d, &usec); | |
190 | if (r < 0) | |
191 | ASSERT_ERROR(r, ENODATA); | |
192 | else | |
193 | ASSERT_GT(usec, 0U); | |
194 | } | |
195 | ||
196 | r = sd_device_get_property_value(d, "ID_NET_DRIVER", &val); | |
197 | if (r < 0) | |
198 | ASSERT_ERROR(r, ENOENT); | |
199 | } | |
200 | ||
201 | if (streq(subsystem, "drm")) { | |
202 | const char *edid_content; | |
203 | size_t edid_size = 0; | |
204 | ||
205 | r = sd_device_get_sysattr_value_with_size(d, "edid", &edid_content, &edid_size); | |
206 | if (r < 0) | |
207 | ASSERT_ERROR(r, ENOENT); | |
208 | ||
209 | /* at least 128 if monitor is connected, otherwise 0 */ | |
210 | ASSERT_TRUE(edid_size == 0 || edid_size >= 128); | |
211 | } | |
212 | ||
213 | is_block = streq_ptr(subsystem, "block"); | |
214 | ||
215 | r = sd_device_get_devname(d, &devname); | |
216 | if (r < 0) | |
217 | ASSERT_ERROR(r, ENOENT); | |
218 | else { | |
219 | r = sd_device_new_from_devname(&dev, devname); | |
220 | if (r < 0) | |
221 | ASSERT_TRUE(r == -ENODEV || ERRNO_IS_NEG_PRIVILEGE(r)); | |
222 | else { | |
223 | ASSERT_OK(sd_device_get_syspath(dev, &val)); | |
224 | ASSERT_STREQ(syspath, val); | |
225 | ASSERT_NULL(dev = sd_device_unref(dev)); | |
226 | } | |
227 | ||
228 | r = sd_device_new_from_path(&dev, devname); | |
229 | if (r < 0) | |
230 | ASSERT_TRUE(r == -ENODEV || ERRNO_IS_NEG_PRIVILEGE(r)); | |
231 | else { | |
232 | ASSERT_OK(sd_device_get_syspath(dev, &val)); | |
233 | ASSERT_STREQ(syspath, val); | |
234 | ASSERT_NULL(dev = sd_device_unref(dev)); | |
235 | ||
236 | _cleanup_close_ int fd = -EBADF; | |
237 | fd = sd_device_open(d, O_CLOEXEC| O_NONBLOCK | (is_block ? O_RDONLY : O_NOCTTY | O_PATH)); | |
238 | ASSERT_TRUE(fd >= 0 || ERRNO_IS_NEG_PRIVILEGE(fd)); | |
239 | } | |
240 | } | |
241 | ||
242 | r = sd_device_get_devnum(d, &devnum); | |
243 | if (r < 0) | |
244 | ASSERT_ERROR(r, ENOENT); | |
245 | else { | |
246 | _cleanup_free_ char *p = NULL; | |
247 | ||
248 | ASSERT_GT(major(devnum), 0U); | |
249 | ||
250 | ASSERT_OK(sd_device_new_from_devnum(&dev, is_block ? 'b' : 'c', devnum)); | |
251 | ASSERT_OK(sd_device_get_syspath(dev, &val)); | |
252 | ASSERT_STREQ(syspath, val); | |
253 | ASSERT_NULL(dev = sd_device_unref(dev)); | |
254 | ||
255 | ASSERT_OK(asprintf(&p, "/dev/%s/%u:%u", is_block ? "block" : "char", major(devnum), minor(devnum))); | |
256 | ASSERT_OK(sd_device_new_from_devname(&dev, p)); | |
257 | ASSERT_OK(sd_device_get_syspath(dev, &val)); | |
258 | ASSERT_STREQ(syspath, val); | |
259 | ASSERT_NULL(dev = sd_device_unref(dev)); | |
260 | ||
261 | ASSERT_OK(sd_device_new_from_path(&dev, p)); | |
262 | ASSERT_OK(sd_device_get_syspath(dev, &val)); | |
263 | ASSERT_STREQ(syspath, val); | |
264 | ASSERT_NULL(dev = sd_device_unref(dev)); | |
265 | } | |
266 | ||
267 | ASSERT_OK(sd_device_get_devpath(d, &val)); | |
268 | ||
269 | r = sd_device_get_devtype(d, NULL); | |
270 | if (r < 0) | |
271 | ASSERT_ERROR(r, ENOENT); | |
272 | ||
273 | r = sd_device_get_driver(d, NULL); | |
274 | if (r < 0) | |
275 | ASSERT_ERROR(r, ENOENT); | |
276 | ||
277 | r = sd_device_get_sysnum(d, &val); | |
278 | if (r < 0) | |
279 | ASSERT_ERROR(r, ENOENT); | |
280 | else { | |
281 | ASSERT_TRUE(val > sysname); | |
282 | ASSERT_TRUE(val < sysname + strlen(sysname)); | |
283 | ASSERT_TRUE(in_charset(val, DIGITS)); | |
284 | ASSERT_FALSE(ascii_isdigit(val[-1])); | |
285 | ||
286 | r = device_get_sysnum_unsigned(d, NULL); | |
287 | if (r < 0) | |
288 | ASSERT_ERROR(r, ERANGE); /* sysnum may be too large. */ | |
289 | } | |
290 | ||
291 | r = sd_device_get_sysattr_value(d, "nsid", NULL); | |
292 | if (r < 0) | |
293 | ASSERT_TRUE(ERRNO_IS_NEG_PRIVILEGE(r) || IN_SET(r, -ENOENT, -EINVAL)); | |
294 | else { | |
295 | unsigned x; | |
296 | ASSERT_OK(r = device_get_sysattr_unsigned(d, "nsid", &x)); | |
297 | ASSERT_EQ(x > 0, r > 0); | |
298 | } | |
299 | } | |
300 | ||
301 | static void exclude_problematic_devices(sd_device_enumerator *e) { | |
302 | /* On some CI environments, it seems some loop block devices and corresponding bdi devices sometimes | |
303 | * disappear during running this test. Let's exclude them here for stability. */ | |
304 | ASSERT_OK(sd_device_enumerator_add_match_subsystem(e, "bdi", false)); | |
305 | ASSERT_OK(sd_device_enumerator_add_nomatch_sysname(e, "loop*")); | |
306 | /* On CentOS CI, systemd-networkd-tests.py may be running when this test is invoked. The networkd | |
307 | * test creates and removes many network interfaces, and may interfere with this test. */ | |
308 | ASSERT_OK(sd_device_enumerator_add_match_subsystem(e, "net", false)); | |
309 | } | |
310 | ||
311 | TEST(sd_device_enumerator_devices) { | |
312 | _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL; | |
313 | ||
314 | ASSERT_OK(sd_device_enumerator_new(&e)); | |
315 | ASSERT_OK(sd_device_enumerator_allow_uninitialized(e)); | |
316 | exclude_problematic_devices(e); | |
317 | ||
318 | FOREACH_DEVICE(e, d) | |
319 | test_sd_device_one(d); | |
320 | } | |
321 | ||
322 | TEST(sd_device_enumerator_subsystems) { | |
323 | _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL; | |
324 | ||
325 | ASSERT_OK(sd_device_enumerator_new(&e)); | |
326 | ASSERT_OK(sd_device_enumerator_allow_uninitialized(e)); | |
327 | FOREACH_SUBSYSTEM(e, d) | |
328 | test_sd_device_one(d); | |
329 | } | |
330 | ||
331 | static void test_sd_device_enumerator_filter_subsystem_one( | |
332 | const char *subsystem, | |
333 | Hashmap *h, | |
334 | unsigned *ret_n_new_dev, | |
335 | unsigned *ret_n_removed_dev) { | |
336 | ||
337 | _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL; | |
338 | unsigned n_new_dev = 0, n_removed_dev = 0; | |
339 | sd_device *dev; | |
340 | ||
341 | ASSERT_OK(sd_device_enumerator_new(&e)); | |
342 | ASSERT_OK(sd_device_enumerator_add_match_subsystem(e, subsystem, true)); | |
343 | ASSERT_OK(sd_device_enumerator_add_nomatch_sysname(e, "loop*")); | |
344 | ||
345 | FOREACH_DEVICE(e, d) { | |
346 | const char *syspath; | |
347 | sd_device *t; | |
348 | ||
349 | ASSERT_OK(sd_device_get_syspath(d, &syspath)); | |
350 | t = hashmap_remove(h, syspath); | |
351 | ||
352 | if (!t) { | |
353 | log_warning("New device found: subsystem:%s syspath:%s", subsystem, syspath); | |
354 | n_new_dev++; | |
355 | } | |
356 | ||
357 | ASSERT_NULL(sd_device_unref(t)); | |
358 | } | |
359 | ||
360 | HASHMAP_FOREACH(dev, h) { | |
361 | const char *syspath; | |
362 | ||
363 | ASSERT_OK(sd_device_get_syspath(dev, &syspath)); | |
364 | log_warning("Device removed: subsystem:%s syspath:%s", subsystem, syspath); | |
365 | n_removed_dev++; | |
366 | ||
367 | ASSERT_NULL(sd_device_unref(dev)); | |
368 | } | |
369 | ||
370 | hashmap_free(h); | |
371 | ||
372 | *ret_n_new_dev = n_new_dev; | |
373 | *ret_n_removed_dev = n_removed_dev; | |
374 | } | |
375 | ||
376 | static bool test_sd_device_enumerator_filter_subsystem_trial(void) { | |
377 | _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL; | |
378 | _cleanup_hashmap_free_ Hashmap *subsystems = NULL; | |
379 | unsigned n_new_dev = 0, n_removed_dev = 0; | |
380 | Hashmap *h; | |
381 | char *s; | |
382 | ||
383 | ASSERT_NOT_NULL((subsystems = hashmap_new(&string_hash_ops))); | |
384 | ASSERT_OK(sd_device_enumerator_new(&e)); | |
385 | exclude_problematic_devices(e); | |
386 | ||
387 | FOREACH_DEVICE(e, d) { | |
388 | const char *syspath, *subsystem; | |
389 | int r; | |
390 | ||
391 | ASSERT_OK(sd_device_get_syspath(d, &syspath)); | |
392 | ||
393 | r = sd_device_get_subsystem(d, &subsystem); | |
394 | if (r < 0) { | |
395 | ASSERT_ERROR(r, ENOENT); | |
396 | continue; | |
397 | } | |
398 | ||
399 | h = hashmap_get(subsystems, subsystem); | |
400 | if (!h) { | |
401 | char *str; | |
402 | ASSERT_NOT_NULL((str = strdup(subsystem))); | |
403 | ASSERT_NOT_NULL((h = hashmap_new(&string_hash_ops))); | |
404 | ASSERT_OK(hashmap_put(subsystems, str, h)); | |
405 | } | |
406 | ||
407 | ASSERT_OK(hashmap_put(h, syspath, d)); | |
408 | ASSERT_NOT_NULL(sd_device_ref(d)); | |
409 | ||
410 | log_debug("Added subsystem:%s syspath:%s", subsystem, syspath); | |
411 | } | |
412 | ||
413 | while ((h = hashmap_steal_first_key_and_value(subsystems, (void**) &s))) { | |
414 | unsigned n, m; | |
415 | ||
416 | test_sd_device_enumerator_filter_subsystem_one(s, TAKE_PTR(h), &n, &m); | |
417 | free(s); | |
418 | ||
419 | n_new_dev += n; | |
420 | n_removed_dev += m; | |
421 | } | |
422 | ||
423 | if (n_new_dev > 0) | |
424 | log_warning("%u new devices are found in re-scan", n_new_dev); | |
425 | if (n_removed_dev > 0) | |
426 | log_warning("%u devices removed in re-scan", n_removed_dev); | |
427 | ||
428 | return n_new_dev + n_removed_dev == 0; | |
429 | } | |
430 | ||
431 | static bool test_sd_device_enumerator_filter_subsystem_trial_many(void) { | |
432 | for (unsigned i = 0; i < 20; i++) { | |
433 | log_debug("%s(): trial %u", __func__, i); | |
434 | if (test_sd_device_enumerator_filter_subsystem_trial()) | |
435 | return true; | |
436 | } | |
437 | ||
438 | return false; | |
439 | } | |
440 | ||
441 | static int on_inotify(sd_event_source *s, const struct inotify_event *event, void *userdata) { | |
442 | if (test_sd_device_enumerator_filter_subsystem_trial_many()) | |
443 | return sd_event_exit(sd_event_source_get_event(s), 0); | |
444 | ||
445 | return sd_event_exit(sd_event_source_get_event(s), -EBUSY); | |
446 | } | |
447 | ||
448 | TEST(sd_device_enumerator_filter_subsystem) { | |
449 | /* The test test_sd_device_enumerator_filter_subsystem_trial() is quite racy. Let's run the function | |
450 | * several times after the udev queue becomes empty. */ | |
451 | ||
452 | if (!udev_available() || (access("/run/udev", F_OK) < 0 && errno == ENOENT)) { | |
453 | ASSERT_TRUE(test_sd_device_enumerator_filter_subsystem_trial_many()); | |
454 | return; | |
455 | } | |
456 | ||
457 | _cleanup_(sd_event_unrefp) sd_event *event = NULL; | |
458 | ASSERT_OK(sd_event_default(&event)); | |
459 | ASSERT_OK(sd_event_add_inotify(event, NULL, "/run/udev" , IN_DELETE, on_inotify, NULL)); | |
460 | ||
461 | if (udev_queue_is_empty() == 0) { | |
462 | log_debug("udev queue is not empty, waiting for all queued events to be processed."); | |
463 | ASSERT_OK(sd_event_loop(event)); | |
464 | } else | |
465 | ASSERT_TRUE(test_sd_device_enumerator_filter_subsystem_trial_many()); | |
466 | } | |
467 | ||
468 | TEST(sd_device_enumerator_add_match_sysattr) { | |
469 | _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL; | |
470 | sd_device *dev; | |
471 | int ifindex; | |
472 | ||
473 | ASSERT_OK(sd_device_enumerator_new(&e)); | |
474 | ASSERT_OK(sd_device_enumerator_allow_uninitialized(e)); | |
475 | ASSERT_OK(sd_device_enumerator_add_match_subsystem(e, "net", true)); | |
476 | ASSERT_OK(sd_device_enumerator_add_match_sysattr(e, "ifindex", "1", true)); | |
477 | ASSERT_OK(sd_device_enumerator_add_match_sysattr(e, "ifindex", "hoge", true)); | |
478 | ASSERT_OK(sd_device_enumerator_add_match_sysattr(e, "ifindex", "foo", true)); | |
479 | ASSERT_OK(sd_device_enumerator_add_match_sysattr(e, "ifindex", "bar", false)); | |
480 | ASSERT_OK(sd_device_enumerator_add_match_sysattr(e, "ifindex", "baz", false)); | |
481 | ||
482 | ASSERT_NOT_NULL((dev = sd_device_enumerator_get_device_first(e))); | |
483 | ASSERT_OK(sd_device_get_ifindex(dev, &ifindex)); | |
484 | ASSERT_EQ(ifindex, 1); | |
485 | ||
486 | ASSERT_NULL(sd_device_enumerator_get_device_next(e)); | |
487 | } | |
488 | ||
489 | TEST(sd_device_enumerator_add_match_property) { | |
490 | _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL; | |
491 | sd_device *dev; | |
492 | int ifindex; | |
493 | ||
494 | ASSERT_OK(sd_device_enumerator_new(&e)); | |
495 | ASSERT_OK(sd_device_enumerator_allow_uninitialized(e)); | |
496 | ASSERT_OK(sd_device_enumerator_add_match_subsystem(e, "net", true)); | |
497 | ASSERT_OK(sd_device_enumerator_add_match_sysattr(e, "ifindex", "1", true)); | |
498 | ASSERT_OK(sd_device_enumerator_add_match_property(e, "IFINDE*", "1*")); | |
499 | ASSERT_OK(sd_device_enumerator_add_match_property(e, "IFINDE*", "hoge")); | |
500 | ASSERT_OK(sd_device_enumerator_add_match_property(e, "IFINDE*", NULL)); | |
501 | ASSERT_OK(sd_device_enumerator_add_match_property(e, "AAAAA", "BBBB")); | |
502 | ASSERT_OK(sd_device_enumerator_add_match_property(e, "FOOOO", NULL)); | |
503 | ||
504 | ASSERT_NOT_NULL((dev = sd_device_enumerator_get_device_first(e))); | |
505 | ASSERT_OK(sd_device_get_ifindex(dev, &ifindex)); | |
506 | ASSERT_EQ(ifindex, 1); | |
507 | } | |
508 | ||
509 | TEST(sd_device_enumerator_add_match_property_required) { | |
510 | _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL; | |
511 | sd_device *dev; | |
512 | int ifindex; | |
513 | ||
514 | ASSERT_OK(sd_device_enumerator_new(&e)); | |
515 | ASSERT_OK(sd_device_enumerator_allow_uninitialized(e)); | |
516 | ASSERT_OK(sd_device_enumerator_add_match_subsystem(e, "net", true)); | |
517 | ASSERT_OK(sd_device_enumerator_add_match_sysattr(e, "ifindex", "1", true)); | |
518 | ASSERT_OK(sd_device_enumerator_add_match_property_required(e, "IFINDE*", "1*")); | |
519 | ||
520 | /* Only one required match which should be satisfied. */ | |
521 | ASSERT_NOT_NULL((dev = sd_device_enumerator_get_device_first(e))); | |
522 | ASSERT_OK(sd_device_get_ifindex(dev, &ifindex)); | |
523 | ASSERT_EQ(ifindex, 1); | |
524 | ||
525 | /* Now let's add a bunch of garbage properties which should not be satisfied. */ | |
526 | ASSERT_OK(sd_device_enumerator_add_match_property_required(e, "IFINDE*", "hoge")); | |
527 | ASSERT_OK(sd_device_enumerator_add_match_property_required(e, "IFINDE*", NULL)); | |
528 | ASSERT_OK(sd_device_enumerator_add_match_property_required(e, "AAAAA", "BBBB")); | |
529 | ASSERT_OK(sd_device_enumerator_add_match_property_required(e, "FOOOO", NULL)); | |
530 | ||
531 | ASSERT_NULL(sd_device_enumerator_get_device_first(e)); | |
532 | } | |
533 | ||
534 | static void check_parent_match(sd_device_enumerator *e, sd_device *dev) { | |
535 | const char *syspath; | |
536 | bool found = false; | |
537 | ||
538 | ASSERT_OK(sd_device_get_syspath(dev, &syspath)); | |
539 | ||
540 | FOREACH_DEVICE(e, d) { | |
541 | const char *s; | |
542 | ||
543 | ASSERT_OK(sd_device_get_syspath(d, &s)); | |
544 | if (streq(s, syspath)) { | |
545 | found = true; | |
546 | break; | |
547 | } | |
548 | } | |
549 | ||
550 | if (!found) { | |
551 | log_device_debug(dev, "not enumerated, already removed??"); | |
552 | /* If the original device not found, then the device should be already removed. */ | |
553 | ASSERT_FAIL(access(syspath, F_OK)); | |
554 | ASSERT_EQ(errno, ENOENT); | |
555 | } | |
556 | } | |
557 | ||
558 | TEST(sd_device_enumerator_add_match_parent) { | |
559 | _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL; | |
560 | /* Some devices have thousands of children. Avoid spending too much time in the double loop below. */ | |
561 | unsigned iterations = 200; | |
562 | int r; | |
563 | ||
564 | ASSERT_OK(sd_device_enumerator_new(&e)); | |
565 | ASSERT_OK(sd_device_enumerator_allow_uninitialized(e)); | |
566 | exclude_problematic_devices(e); | |
567 | ||
568 | if (!slow_tests_enabled()) | |
569 | ASSERT_OK(sd_device_enumerator_add_match_subsystem(e, "block", true)); | |
570 | ||
571 | FOREACH_DEVICE(e, dev) { | |
572 | _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *p = NULL; | |
573 | const char *syspath; | |
574 | sd_device *parent; | |
575 | ||
576 | if (iterations-- == 0) | |
577 | break; | |
578 | ||
579 | ASSERT_OK(sd_device_get_syspath(dev, &syspath)); | |
580 | ||
581 | r = sd_device_get_parent(dev, &parent); | |
582 | if (r < 0) { | |
583 | ASSERT_TRUE(ERRNO_IS_NEG_DEVICE_ABSENT(r)); | |
584 | continue; | |
585 | } | |
586 | ||
587 | log_debug("> %s", syspath); | |
588 | ||
589 | ASSERT_OK(sd_device_enumerator_new(&p)); | |
590 | ASSERT_OK(sd_device_enumerator_allow_uninitialized(p)); | |
591 | ASSERT_OK(sd_device_enumerator_add_match_parent(p, parent)); | |
592 | ||
593 | check_parent_match(p, dev); | |
594 | ||
595 | /* If the device does not have subsystem, then it is not enumerated. */ | |
596 | r = sd_device_get_subsystem(parent, NULL); | |
597 | if (r < 0) { | |
598 | ASSERT_ERROR(r, ENOENT); | |
599 | continue; | |
600 | } | |
601 | check_parent_match(p, parent); | |
602 | } | |
603 | } | |
604 | ||
605 | TEST(sd_device_enumerator_add_all_parents) { | |
606 | _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL; | |
607 | int r; | |
608 | ||
609 | /* STEP 1: enumerate all block devices without all_parents() */ | |
610 | ASSERT_OK(sd_device_enumerator_new(&e)); | |
611 | ASSERT_OK(sd_device_enumerator_allow_uninitialized(e)); | |
612 | ||
613 | /* filter in only a subsystem */ | |
614 | ASSERT_OK(sd_device_enumerator_add_nomatch_sysname(e, "loop*")); | |
615 | ASSERT_OK(sd_device_enumerator_add_match_subsystem(e, "block", true)); | |
616 | ASSERT_OK(sd_device_enumerator_add_match_property(e, "DEVTYPE", "partition")); | |
617 | ||
618 | unsigned devices_count_with_parents = 0; | |
619 | unsigned devices_count_without_parents = 0; | |
620 | FOREACH_DEVICE(e, dev) { | |
621 | ASSERT_OK_POSITIVE(device_is_subsystem_devtype(dev, "block", "partition")); | |
622 | devices_count_without_parents++; | |
623 | } | |
624 | ||
625 | log_debug("found %u devices", devices_count_without_parents); | |
626 | ||
627 | /* STEP 2: enumerate again with all_parents() */ | |
628 | ASSERT_OK(sd_device_enumerator_add_all_parents(e)); | |
629 | ||
630 | unsigned not_filtered_parent_count = 0; | |
631 | FOREACH_DEVICE(e, dev) { | |
632 | ASSERT_OK(r = device_is_subsystem_devtype(dev, "block", "partition")); | |
633 | if (r == 0) | |
634 | not_filtered_parent_count++; | |
635 | devices_count_with_parents++; | |
636 | } | |
637 | log_debug("found %u devices out of %u that would have been excluded without all_parents()", | |
638 | not_filtered_parent_count, | |
639 | devices_count_with_parents); | |
640 | ASSERT_EQ(devices_count_with_parents, devices_count_without_parents + not_filtered_parent_count); | |
641 | } | |
642 | ||
643 | TEST(sd_device_get_child) { | |
644 | _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL; | |
645 | /* Some devices have thousands of children. Avoid spending too much time in the double loop below. */ | |
646 | unsigned iterations = 3000; | |
647 | int r; | |
648 | ||
649 | ASSERT_OK(sd_device_enumerator_new(&e)); | |
650 | ASSERT_OK(sd_device_enumerator_allow_uninitialized(e)); | |
651 | exclude_problematic_devices(e); | |
652 | ||
653 | if (!slow_tests_enabled()) | |
654 | ASSERT_OK(sd_device_enumerator_add_match_subsystem(e, "block", true)); | |
655 | ||
656 | FOREACH_DEVICE(e, dev) { | |
657 | const char *syspath, *parent_syspath, *expected_suffix, *suffix; | |
658 | sd_device *parent; | |
659 | bool found = false; | |
660 | ||
661 | ASSERT_OK(sd_device_get_syspath(dev, &syspath)); | |
662 | ||
663 | r = sd_device_get_parent(dev, &parent); | |
664 | if (r < 0) { | |
665 | ASSERT_TRUE(ERRNO_IS_NEG_DEVICE_ABSENT(r)); | |
666 | continue; | |
667 | } | |
668 | ||
669 | ASSERT_OK(sd_device_get_syspath(parent, &parent_syspath)); | |
670 | ASSERT_NOT_NULL((expected_suffix = path_startswith(syspath, parent_syspath))); | |
671 | ||
672 | log_debug("> %s", syspath); | |
673 | ||
674 | FOREACH_DEVICE_CHILD_WITH_SUFFIX(parent, child, suffix) { | |
675 | const char *s; | |
676 | ||
677 | if (iterations-- == 0) | |
678 | return; | |
679 | ||
680 | ASSERT_NOT_NULL(child); | |
681 | ASSERT_NOT_NULL(suffix); | |
682 | ||
683 | if (!streq(suffix, expected_suffix)) | |
684 | continue; | |
685 | ||
686 | ASSERT_OK(sd_device_get_syspath(child, &s)); | |
687 | ASSERT_STREQ(s, syspath); | |
688 | found = true; | |
689 | break; | |
690 | } | |
691 | ASSERT_TRUE(found); | |
692 | } | |
693 | } | |
694 | ||
695 | TEST(sd_device_new_from_nulstr) { | |
696 | const char *devlinks = | |
697 | "/dev/disk/by-partuuid/1290d63a-42cc-4c71-b87c-xxxxxxxxxxxx\0" | |
698 | "/dev/disk/by-path/pci-0000:00:0f.0-scsi-0:0:0:0-part3\0" | |
699 | "/dev/disk/by-label/Arch\\x20Linux\0" | |
700 | "/dev/disk/by-uuid/a07b87e5-4af5-4a59-bde9-yyyyyyyyyyyy\0" | |
701 | "/dev/disk/by-partlabel/Arch\\x20Linux\0" | |
702 | "\0"; | |
703 | ||
704 | _cleanup_(sd_device_unrefp) sd_device *device = NULL, *from_nulstr = NULL; | |
705 | _cleanup_free_ char *nulstr_copy = NULL; | |
706 | const char *nulstr; | |
707 | size_t len; | |
708 | ||
709 | ASSERT_OK(sd_device_new_from_syspath(&device, "/sys/class/net/lo")); | |
710 | ||
711 | /* Yeah, of course, setting devlink to the loopback interface is nonsense. But this is just a | |
712 | * test for generating and parsing nulstr. For issue #17772. */ | |
713 | NULSTR_FOREACH(devlink, devlinks) { | |
714 | log_device_info(device, "setting devlink: %s", devlink); | |
715 | ASSERT_OK(device_add_devlink(device, devlink)); | |
716 | ASSERT_TRUE(set_contains(device->devlinks, devlink)); | |
717 | } | |
718 | ||
719 | /* For issue #23799 */ | |
720 | ASSERT_OK(device_add_tag(device, "tag1", false)); | |
721 | ASSERT_OK(device_add_tag(device, "tag2", false)); | |
722 | ASSERT_OK(device_add_tag(device, "current-tag1", true)); | |
723 | ASSERT_OK(device_add_tag(device, "current-tag2", true)); | |
724 | ||
725 | /* These properties are necessary for device_new_from_nulstr(). See device_verify(). */ | |
726 | ASSERT_OK(device_add_property_internal(device, "SEQNUM", "1")); | |
727 | ASSERT_OK(device_add_property_internal(device, "ACTION", "change")); | |
728 | ||
729 | ASSERT_OK(device_get_properties_nulstr(device, &nulstr, &len)); | |
730 | ASSERT_NOT_NULL((nulstr_copy = newdup(char, nulstr, len))); | |
731 | ASSERT_OK(device_new_from_nulstr(&from_nulstr, nulstr_copy, len)); | |
732 | ||
733 | ASSERT_OK_POSITIVE(sd_device_has_tag(from_nulstr, "tag1")); | |
734 | ASSERT_OK_POSITIVE(sd_device_has_tag(from_nulstr, "tag2")); | |
735 | ASSERT_OK_POSITIVE(sd_device_has_tag(from_nulstr, "current-tag1")); | |
736 | ASSERT_OK_POSITIVE(sd_device_has_tag(from_nulstr, "current-tag2")); | |
737 | ASSERT_OK_ZERO(sd_device_has_current_tag(from_nulstr, "tag1")); | |
738 | ASSERT_OK_ZERO(sd_device_has_current_tag(from_nulstr, "tag2")); | |
739 | ASSERT_OK_POSITIVE(sd_device_has_current_tag(from_nulstr, "current-tag1")); | |
740 | ASSERT_OK_POSITIVE(sd_device_has_current_tag(from_nulstr, "current-tag2")); | |
741 | ||
742 | NULSTR_FOREACH(devlink, devlinks) { | |
743 | log_device_info(from_nulstr, "checking devlink: %s", devlink); | |
744 | ASSERT_TRUE(set_contains(from_nulstr->devlinks, devlink)); | |
745 | } | |
746 | } | |
747 | ||
748 | TEST(sd_device_new_from_path) { | |
749 | _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL; | |
750 | _cleanup_(rm_rf_physical_and_freep) char *tmpdir = NULL; | |
751 | int r; | |
752 | ||
753 | ASSERT_OK(mkdtemp_malloc("/tmp/test-sd-device.XXXXXXX", &tmpdir)); | |
754 | ||
755 | ASSERT_OK(sd_device_enumerator_new(&e)); | |
756 | ASSERT_OK(sd_device_enumerator_allow_uninitialized(e)); | |
757 | ASSERT_OK(sd_device_enumerator_add_match_subsystem(e, "block", true)); | |
758 | ASSERT_OK(sd_device_enumerator_add_nomatch_sysname(e, "loop*")); | |
759 | ASSERT_OK(sd_device_enumerator_add_match_property(e, "DEVNAME", "*")); | |
760 | ||
761 | FOREACH_DEVICE(e, dev) { | |
762 | _cleanup_(sd_device_unrefp) sd_device *d = NULL; | |
763 | const char *syspath, *devpath, *sysname, *s; | |
764 | _cleanup_free_ char *path = NULL; | |
765 | ||
766 | ASSERT_OK(sd_device_get_sysname(dev, &sysname)); | |
767 | ||
768 | log_debug("%s(%s)", __func__, sysname); | |
769 | ||
770 | ASSERT_OK(sd_device_get_syspath(dev, &syspath)); | |
771 | ASSERT_OK(sd_device_new_from_path(&d, syspath)); | |
772 | ASSERT_OK(sd_device_get_syspath(d, &s)); | |
773 | ASSERT_STREQ(s, syspath); | |
774 | ASSERT_NULL(d = sd_device_unref(d)); | |
775 | ||
776 | ASSERT_OK(sd_device_get_devname(dev, &devpath)); | |
777 | r = sd_device_new_from_path(&d, devpath); | |
778 | if (r < 0) | |
779 | ASSERT_TRUE(r == -ENODEV || ERRNO_IS_NEG_PRIVILEGE(r)); | |
780 | else { | |
781 | ASSERT_OK(sd_device_get_syspath(d, &s)); | |
782 | ASSERT_STREQ(s, syspath); | |
783 | ASSERT_NULL(d = sd_device_unref(d)); | |
784 | } | |
785 | ||
786 | ASSERT_NOT_NULL((path = path_join(tmpdir, sysname))); | |
787 | ASSERT_OK_ERRNO(symlink(syspath, path)); | |
788 | ASSERT_OK(sd_device_new_from_path(&d, path)); | |
789 | ASSERT_OK(sd_device_get_syspath(d, &s)); | |
790 | ASSERT_STREQ(s, syspath); | |
791 | } | |
792 | } | |
793 | ||
794 | static void test_devname_from_devnum_one(const char *path) { | |
795 | _cleanup_free_ char *resolved = NULL; | |
796 | struct stat st; | |
797 | ||
798 | log_debug("> %s", path); | |
799 | ||
800 | if (stat(path, &st) < 0) { | |
801 | log_notice("Path %s not found, skipping test", path); | |
802 | return; | |
803 | } | |
804 | ||
805 | ASSERT_OK(devname_from_devnum(st.st_mode, st.st_rdev, &resolved)); | |
806 | ASSERT_TRUE(path_equal(path, resolved)); | |
807 | ASSERT_NULL(resolved = mfree(resolved)); | |
808 | ASSERT_OK(devname_from_stat_rdev(&st, &resolved)); | |
809 | ASSERT_TRUE(path_equal(path, resolved)); | |
810 | } | |
811 | ||
812 | TEST(devname_from_devnum) { | |
813 | test_devname_from_devnum_one("/dev/null"); | |
814 | test_devname_from_devnum_one("/dev/zero"); | |
815 | test_devname_from_devnum_one("/dev/full"); | |
816 | test_devname_from_devnum_one("/dev/random"); | |
817 | test_devname_from_devnum_one("/dev/urandom"); | |
818 | test_devname_from_devnum_one("/dev/tty"); | |
819 | ||
820 | if (is_device_node("/run/systemd/inaccessible/blk") > 0) { | |
821 | test_devname_from_devnum_one("/run/systemd/inaccessible/chr"); | |
822 | test_devname_from_devnum_one("/run/systemd/inaccessible/blk"); | |
823 | } | |
824 | } | |
825 | ||
826 | static int intro(void) { | |
827 | if (path_is_mount_point("/sys") <= 0) | |
828 | return log_tests_skipped("/sys is not mounted"); | |
829 | ||
830 | return EXIT_SUCCESS; | |
831 | } | |
832 | ||
833 | DEFINE_TEST_MAIN_WITH_INTRO(LOG_INFO, intro); |