diff options
author | Yu Watanabe <watanabe.yu+github@gmail.com> | 2023-08-03 12:57:41 +0200 |
---|---|---|
committer | Yu Watanabe <watanabe.yu+github@gmail.com> | 2023-08-03 13:37:16 +0200 |
commit | 09fa0a0721e093144550b06f2ab7cb4275212be5 (patch) | |
tree | e1ceafb7e39f9197922dc90937bcf200a594ced6 /src/udev/test-udev-rule-runner.c | |
parent | meson: use template to declare udev plugins (diff) | |
download | systemd-09fa0a0721e093144550b06f2ab7cb4275212be5.tar.xz systemd-09fa0a0721e093144550b06f2ab7cb4275212be5.zip |
test: rename udev-rule-runner -> test-udev-rule-runner
This partially revert 0454cf05d38d289474ca65c1917d414b2958f6b5.
The executable actually does not work with itself, but needs to be
combined with test-udev.py. But, even so, the executable is for testing.
In the next commit, test and normal executables are declared in the same
way, and naming of the executable becomes essential to classify them.
Let's rename the executable and prefix with 'test-'.
Diffstat (limited to 'src/udev/test-udev-rule-runner.c')
-rw-r--r-- | src/udev/test-udev-rule-runner.c | 178 |
1 files changed, 178 insertions, 0 deletions
diff --git a/src/udev/test-udev-rule-runner.c b/src/udev/test-udev-rule-runner.c new file mode 100644 index 0000000000..72296b3f0e --- /dev/null +++ b/src/udev/test-udev-rule-runner.c @@ -0,0 +1,178 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/*** + Copyright © 2003-2004 Greg Kroah-Hartman <greg@kroah.com> +***/ + +#include <errno.h> +#include <sched.h> +#include <stdio.h> +#include <stdlib.h> +#include <sys/mount.h> +#include <sys/signalfd.h> +#include <unistd.h> + +#include "device-private.h" +#include "fs-util.h" +#include "log.h" +#include "main-func.h" +#include "mkdir-label.h" +#include "mount-util.h" +#include "namespace-util.h" +#include "parse-util.h" +#include "selinux-util.h" +#include "signal-util.h" +#include "string-util.h" +#include "tests.h" +#include "udev-event.h" +#include "udev-spawn.h" +#include "version.h" + +static int device_new_from_synthetic_event(sd_device **ret, const char *syspath, const char *action) { + _cleanup_(sd_device_unrefp) sd_device *dev = NULL; + sd_device_action_t a; + int r; + + assert(ret); + assert(syspath); + assert(action); + + a = device_action_from_string(action); + if (a < 0) + return a; + + r = sd_device_new_from_syspath(&dev, syspath); + if (r < 0) + return r; + + r = device_read_uevent_file(dev); + if (r < 0) + return r; + + r = device_set_action(dev, a); + if (r < 0) + return r; + + *ret = TAKE_PTR(dev); + return 0; +} + +static int fake_filesystems(void) { + static const struct fakefs { + const char *src; + const char *target; + const char *error; + bool ignore_mount_error; + } fakefss[] = { + { "tmpfs/sys", "/sys", "Failed to mount test /sys", false }, + { "tmpfs/dev", "/dev", "Failed to mount test /dev", false }, + { "run", "/run", "Failed to mount test /run", false }, + { "run", "/etc/udev/rules.d", "Failed to mount empty /etc/udev/rules.d", true }, + { "run", UDEVLIBEXECDIR "/rules.d", "Failed to mount empty " UDEVLIBEXECDIR "/rules.d", true }, + }; + int r; + + r = detach_mount_namespace(); + if (r < 0) + return log_error_errno(r, "Failed to detach mount namespace: %m"); + + for (size_t i = 0; i < ELEMENTSOF(fakefss); i++) { + r = mount_nofollow_verbose(fakefss[i].ignore_mount_error ? LOG_NOTICE : LOG_ERR, + fakefss[i].src, fakefss[i].target, NULL, MS_BIND, NULL); + if (r < 0 && !fakefss[i].ignore_mount_error) + return r; + } + + return 0; +} + +static int run(int argc, char *argv[]) { + _cleanup_(udev_rules_freep) UdevRules *rules = NULL; + _cleanup_(udev_event_freep) UdevEvent *event = NULL; + _cleanup_(sd_device_unrefp) sd_device *dev = NULL; + const char *devpath, *devname, *action; + int r; + + test_setup_logging(LOG_INFO); + + if (!IN_SET(argc, 2, 3, 4)) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "This program needs between one and three arguments, %d given", argc - 1); + + r = fake_filesystems(); + if (r < 0) + return r; + + /* Let's make sure the test runs with selinux assumed disabled. */ +#if HAVE_SELINUX + fini_selinuxmnt(); +#endif + mac_selinux_retest(); + + if (argc == 2) { + if (!streq(argv[1], "check")) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "Unknown argument: %s", argv[1]); + + return 0; + } + + log_debug("version %s", GIT_VERSION); + + r = mac_init(); + if (r < 0) + return r; + + action = argv[1]; + devpath = argv[2]; + + if (argv[3]) { + unsigned us; + + r = safe_atou(argv[3], &us); + if (r < 0) + return log_error_errno(r, "Invalid delay '%s': %m", argv[3]); + usleep_safe(us); + } + + assert_se(udev_rules_load(&rules, RESOLVE_NAME_EARLY) == 0); + + const char *syspath = strjoina("/sys", devpath); + r = device_new_from_synthetic_event(&dev, syspath, action); + if (r < 0) + return log_debug_errno(r, "Failed to open device '%s'", devpath); + + assert_se(event = udev_event_new(dev, 0, NULL, log_get_max_level())); + + assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGTERM, SIGINT, SIGHUP, SIGCHLD, -1) >= 0); + + /* do what devtmpfs usually provides us */ + if (sd_device_get_devname(dev, &devname) >= 0) { + const char *subsystem; + mode_t mode = 0600; + + if (sd_device_get_subsystem(dev, &subsystem) >= 0 && streq(subsystem, "block")) + mode |= S_IFBLK; + else + mode |= S_IFCHR; + + if (!streq(action, "remove")) { + dev_t devnum = makedev(0, 0); + + (void) mkdir_parents_label(devname, 0755); + (void) sd_device_get_devnum(dev, &devnum); + if (mknod(devname, mode, devnum) < 0) + return log_error_errno(errno, "mknod() failed for '%s': %m", devname); + } else { + if (unlink(devname) < 0) + return log_error_errno(errno, "unlink('%s') failed: %m", devname); + (void) rmdir_parents(devname, "/dev"); + } + } + + udev_event_execute_rules(event, -1, 3 * USEC_PER_SEC, SIGKILL, NULL, rules); + udev_event_execute_run(event, 3 * USEC_PER_SEC, SIGKILL); + + return 0; +} + +DEFINE_MAIN_FUNCTION(run); |