summaryrefslogtreecommitdiffstats
path: root/src/udev/test-udev-rule-runner.c
diff options
context:
space:
mode:
authorYu Watanabe <watanabe.yu+github@gmail.com>2023-08-03 12:57:41 +0200
committerYu Watanabe <watanabe.yu+github@gmail.com>2023-08-03 13:37:16 +0200
commit09fa0a0721e093144550b06f2ab7cb4275212be5 (patch)
treee1ceafb7e39f9197922dc90937bcf200a594ced6 /src/udev/test-udev-rule-runner.c
parentmeson: use template to declare udev plugins (diff)
downloadsystemd-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.c178
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);