]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
test: add test for pidfd support in sd-event
authorLennart Poettering <lennart@poettering.net>
Wed, 30 Oct 2019 16:42:31 +0000 (17:42 +0100)
committerLennart Poettering <lennart@poettering.net>
Wed, 4 Dec 2019 09:35:39 +0000 (10:35 +0100)
src/libsystemd/sd-event/test-event.c

index 954b93ada0cf8b314a6ebe021184cc1bd0892cfc..54d293ca4619153ca9fd5434783597c1604e38bc 100644 (file)
@@ -9,6 +9,7 @@
 #include "fs-util.h"
 #include "log.h"
 #include "macro.h"
+#include "missing_syscall.h"
 #include "parse-util.h"
 #include "path-util.h"
 #include "process-util.h"
@@ -62,6 +63,11 @@ static int child_handler(sd_event_source *s, const siginfo_t *si, void *userdata
         assert_se(s);
         assert_se(si);
 
+        assert_se(si->si_uid == getuid());
+        assert_se(si->si_signo == SIGCHLD);
+        assert_se(si->si_code == CLD_EXITED);
+        assert_se(si->si_status == 78);
+
         log_info("got child on %c", PTR_TO_INT(userdata));
 
         assert_se(userdata == INT_TO_PTR('f'));
@@ -75,6 +81,7 @@ static int child_handler(sd_event_source *s, const siginfo_t *si, void *userdata
 static int signal_handler(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata) {
         sd_event_source *p = NULL;
         pid_t pid;
+        siginfo_t plain_si;
 
         assert_se(s);
         assert_se(si);
@@ -83,16 +90,41 @@ static int signal_handler(sd_event_source *s, const struct signalfd_siginfo *si,
 
         assert_se(userdata == INT_TO_PTR('e'));
 
-        assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGCHLD, -1) >= 0);
+        assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGCHLD, SIGUSR2, -1) >= 0);
 
         pid = fork();
         assert_se(pid >= 0);
 
-        if (pid == 0)
-                _exit(EXIT_SUCCESS);
+        if (pid == 0) {
+                sigset_t ss;
+
+                assert_se(sigemptyset(&ss) >= 0);
+                assert_se(sigaddset(&ss, SIGUSR2) >= 0);
+
+                zero(plain_si);
+                assert_se(sigwaitinfo(&ss, &plain_si) >= 0);
+
+                assert_se(plain_si.si_signo == SIGUSR2);
+                assert_se(plain_si.si_value.sival_int == 4711);
+
+                _exit(78);
+        }
 
         assert_se(sd_event_add_child(sd_event_source_get_event(s), &p, pid, WEXITED, child_handler, INT_TO_PTR('f')) >= 0);
         assert_se(sd_event_source_set_enabled(p, SD_EVENT_ONESHOT) >= 0);
+        assert_se(sd_event_source_set_child_process_own(p, true) >= 0);
+
+        /* We can't use structured initialization here, since the structure contains various unions and these
+         * fields lie in overlapping (carefully aligned) unions that LLVM is allergic to allow assignments
+         * to */
+        zero(plain_si);
+        plain_si.si_signo = SIGUSR2;
+        plain_si.si_code = SI_QUEUE;
+        plain_si.si_pid = getpid();
+        plain_si.si_uid = getuid();
+        plain_si.si_value.sival_int = 4711;
+
+        assert_se(sd_event_source_send_child_signal(p, SIGUSR2, &plain_si, 0) >= 0);
 
         sd_event_source_unref(s);
 
@@ -119,7 +151,7 @@ static int defer_handler(sd_event_source *s, void *userdata) {
         return 1;
 }
 
-static bool do_quit = false;
+static bool do_quit;
 
 static int time_handler(sd_event_source *s, uint64_t usec, void *userdata) {
         log_info("got timer on %c", PTR_TO_INT(userdata));
@@ -161,7 +193,7 @@ static int post_handler(sd_event_source *s, void *userdata) {
         return 2;
 }
 
-static void test_basic(void) {
+static void test_basic(bool with_pidfd) {
         sd_event *e = NULL;
         sd_event_source *w = NULL, *x = NULL, *y = NULL, *z = NULL, *q = NULL, *t = NULL;
         static const char ch = 'x';
@@ -169,6 +201,8 @@ static void test_basic(void) {
         uint64_t event_now;
         int64_t priority;
 
+        assert_se(setenv("SYSTEMD_PIDFD", yes_no(with_pidfd), 1) >= 0);
+
         assert_se(pipe(a) >= 0);
         assert_se(pipe(b) >= 0);
         assert_se(pipe(d) >= 0);
@@ -201,6 +235,8 @@ static void test_basic(void) {
 
         assert_se(sd_event_add_io(e, &x, a[0], EPOLLIN, io_handler, INT_TO_PTR('a')) >= 0);
         assert_se(sd_event_add_io(e, &y, b[0], EPOLLIN, io_handler, INT_TO_PTR('b')) >= 0);
+
+        do_quit = false;
         assert_se(sd_event_add_time(e, &z, CLOCK_MONOTONIC, 0, 0, time_handler, INT_TO_PTR('c')) >= 0);
         assert_se(sd_event_add_exit(e, &q, exit_handler, INT_TO_PTR('g')) >= 0);
 
@@ -258,6 +294,8 @@ static void test_basic(void) {
         safe_close_pair(b);
         safe_close_pair(d);
         safe_close_pair(k);
+
+        assert_se(unsetenv("SYSTEMD_PIDFD") >= 0);
 }
 
 static void test_sd_event_now(void) {
@@ -482,15 +520,89 @@ static void test_inotify(unsigned n_create_events) {
         sd_event_unref(e);
 }
 
+static int pidfd_handler(sd_event_source *s, const siginfo_t *si, void *userdata) {
+        assert_se(s);
+        assert_se(si);
+
+        assert_se(si->si_uid == getuid());
+        assert_se(si->si_signo == SIGCHLD);
+        assert_se(si->si_code == CLD_EXITED);
+        assert_se(si->si_status == 66);
+
+        log_info("got pidfd on %c", PTR_TO_INT(userdata));
+
+        assert_se(userdata == INT_TO_PTR('p'));
+
+        assert_se(sd_event_exit(sd_event_source_get_event(s), 0) >= 0);
+        sd_event_source_unref(s);
+
+        return 0;
+}
+
+static void test_pidfd(void) {
+        sd_event_source *s = NULL, *t = NULL;
+        sd_event *e = NULL;
+        int pidfd;
+        pid_t pid, pid2;
+
+        assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGCHLD, -1) >= 0);
+
+        pid = fork();
+        if (pid == 0) {
+                /* child */
+                _exit(66);
+        }
+
+        assert_se(pid > 1);
+
+        pidfd = pidfd_open(pid, 0);
+        if (pidfd < 0) {
+                /* No pidfd_open() supported or blocked? */
+                assert_se(ERRNO_IS_NOT_SUPPORTED(errno) || ERRNO_IS_PRIVILEGE(errno));
+                (void) wait_for_terminate(pid, NULL);
+                return;
+        }
+
+        pid2 = fork();
+        if (pid2 == 0)
+                freeze();
+
+        assert_se(pid > 2);
+
+        assert_se(sd_event_default(&e) >= 0);
+        assert_se(sd_event_add_child_pidfd(e, &s, pidfd, WEXITED, pidfd_handler, INT_TO_PTR('p')) >= 0);
+        assert_se(sd_event_source_set_child_pidfd_own(s, true) >= 0);
+
+        /* This one should never trigger, since our second child lives forever */
+        assert_se(sd_event_add_child(e, &t, pid2, WEXITED, pidfd_handler, INT_TO_PTR('q')) >= 0);
+        assert_se(sd_event_source_set_child_process_own(t, true) >= 0);
+
+        assert_se(sd_event_loop(e) >= 0);
+
+        /* Child should still be alive */
+        assert_se(kill(pid2, 0) >= 0);
+
+        t = sd_event_source_unref(t);
+
+        /* Child should now be dead, since we dropped the ref */
+        assert_se(kill(pid2, 0) < 0 && errno == ESRCH);
+
+        sd_event_unref(e);
+}
+
 int main(int argc, char *argv[]) {
         test_setup_logging(LOG_INFO);
 
-        test_basic();
+        test_basic(true);   /* test with pidfd */
+        test_basic(false);  /* test without pidfd */
+
         test_sd_event_now();
         test_rtqueue();
 
         test_inotify(100); /* should work without overflow */
         test_inotify(33000); /* should trigger a q overflow */
 
+        test_pidfd();
+
         return 0;
 }