]> git.ipfire.org Git - thirdparty/systemd.git/blobdiff - src/libsystemd/sd-event/test-event.c
tree-wide: use -EBADF also in pipe initializers
[thirdparty/systemd.git] / src / libsystemd / sd-event / test-event.c
index 41745338bfd4b61da484889d318142c823161855..7cc4cc9e28382b125c52d8dc41d15a239c708506 100644 (file)
@@ -5,6 +5,7 @@
 #include "sd-event.h"
 
 #include "alloc-util.h"
+#include "exec-util.h"
 #include "fd-util.h"
 #include "fs-util.h"
 #include "log.h"
 #include "parse-util.h"
 #include "path-util.h"
 #include "process-util.h"
+#include "random-util.h"
 #include "rm-rf.h"
 #include "signal-util.h"
 #include "stdio-util.h"
 #include "string-util.h"
 #include "tests.h"
 #include "tmpfile-util.h"
-#include "util.h"
 
 static int prepare_handler(sd_event_source *s, void *userdata) {
         log_info("preparing %c", PTR_TO_INT(userdata));
@@ -53,7 +54,7 @@ static int io_handler(sd_event_source *s, int fd, uint32_t revents, void *userda
                 else
                         assert_se(sd_event_source_set_enabled(s, SD_EVENT_OFF) >= 0);
         } else
-                assert_not_reached("Yuck!");
+                assert_not_reached();
 
         return 1;
 }
@@ -168,7 +169,7 @@ static int time_handler(sd_event_source *s, uint64_t usec, void *userdata) {
                         got_c = true;
                 }
         } else
-                assert_not_reached("Huh?");
+                assert_not_reached();
 
         return 2;
 }
@@ -193,14 +194,17 @@ static int post_handler(sd_event_source *s, void *userdata) {
         return 2;
 }
 
-static void test_basic(bool with_pidfd) {
+static void test_basic_one(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';
-        int a[2] = { -1, -1 }, b[2] = { -1, -1}, d[2] = { -1, -1}, k[2] = { -1, -1 };
+        int a[2] = { -EBADF, -EBADF }, b[2] = { -EBADF, -EBADF },
+            d[2] = { -EBADF, -EBADF }, k[2] = { -EBADF, -EBADF };
         uint64_t event_now;
         int64_t priority;
 
+        log_info("/* %s(pidfd=%s) */", __func__, yes_no(with_pidfd));
+
         assert_se(setenv("SYSTEMD_PIDFD", yes_no(with_pidfd), 1) >= 0);
 
         assert_se(pipe(a) >= 0);
@@ -217,20 +221,19 @@ static void test_basic(bool with_pidfd) {
         got_unref = false;
         assert_se(sd_event_add_io(e, &t, k[0], EPOLLIN, unref_handler, NULL) >= 0);
         assert_se(write(k[1], &ch, 1) == 1);
-        assert_se(sd_event_run(e, (uint64_t) -1) >= 1);
+        assert_se(sd_event_run(e, UINT64_MAX) >= 1);
         assert_se(got_unref);
 
         got_a = false, got_b = false, got_c = false, got_d = 0;
 
-        /* Add a oneshot handler, trigger it, reenable it, and trigger
-         * it again. */
+        /* Add a oneshot handler, trigger it, reenable it, and trigger it again. */
         assert_se(sd_event_add_io(e, &w, d[0], EPOLLIN, io_handler, INT_TO_PTR('d')) >= 0);
         assert_se(sd_event_source_set_enabled(w, SD_EVENT_ONESHOT) >= 0);
         assert_se(write(d[1], &ch, 1) >= 0);
-        assert_se(sd_event_run(e, (uint64_t) -1) >= 1);
+        assert_se(sd_event_run(e, UINT64_MAX) >= 1);
         assert_se(got_d == 1);
         assert_se(write(d[1], &ch, 1) >= 0);
-        assert_se(sd_event_run(e, (uint64_t) -1) >= 1);
+        assert_se(sd_event_run(e, UINT64_MAX) >= 1);
         assert_se(got_d == 2);
 
         assert_se(sd_event_add_io(e, &x, a[0], EPOLLIN, io_handler, INT_TO_PTR('a')) >= 0);
@@ -258,15 +261,15 @@ static void test_basic(bool with_pidfd) {
 
         assert_se(!got_a && !got_b && !got_c);
 
-        assert_se(sd_event_run(e, (uint64_t) -1) >= 1);
+        assert_se(sd_event_run(e, UINT64_MAX) >= 1);
 
         assert_se(!got_a && got_b && !got_c);
 
-        assert_se(sd_event_run(e, (uint64_t) -1) >= 1);
+        assert_se(sd_event_run(e, UINT64_MAX) >= 1);
 
         assert_se(!got_a && got_b && got_c);
 
-        assert_se(sd_event_run(e, (uint64_t) -1) >= 1);
+        assert_se(sd_event_run(e, UINT64_MAX) >= 1);
 
         assert_se(got_a && got_b && got_c);
 
@@ -298,7 +301,12 @@ static void test_basic(bool with_pidfd) {
         assert_se(unsetenv("SYSTEMD_PIDFD") >= 0);
 }
 
-static void test_sd_event_now(void) {
+TEST(basic) {
+        test_basic_one(true);   /* test with pidfd */
+        test_basic_one(false);  /* test without pidfd */
+}
+
+TEST(sd_event_now) {
         _cleanup_(sd_event_unrefp) sd_event *e = NULL;
         uint64_t event_now;
 
@@ -306,10 +314,8 @@ static void test_sd_event_now(void) {
         assert_se(sd_event_now(e, CLOCK_MONOTONIC, &event_now) > 0);
         assert_se(sd_event_now(e, CLOCK_REALTIME, &event_now) > 0);
         assert_se(sd_event_now(e, CLOCK_REALTIME_ALARM, &event_now) > 0);
-        if (clock_boottime_supported()) {
-                assert_se(sd_event_now(e, CLOCK_BOOTTIME, &event_now) > 0);
-                assert_se(sd_event_now(e, CLOCK_BOOTTIME_ALARM, &event_now) > 0);
-        }
+        assert_se(sd_event_now(e, CLOCK_BOOTTIME, &event_now) > 0);
+        assert_se(sd_event_now(e, CLOCK_BOOTTIME_ALARM, &event_now) > 0);
         assert_se(sd_event_now(e, -1, &event_now) == -EOPNOTSUPP);
         assert_se(sd_event_now(e, 900 /* arbitrary big number */, &event_now) == -EOPNOTSUPP);
 
@@ -318,10 +324,8 @@ static void test_sd_event_now(void) {
         assert_se(sd_event_now(e, CLOCK_MONOTONIC, &event_now) == 0);
         assert_se(sd_event_now(e, CLOCK_REALTIME, &event_now) == 0);
         assert_se(sd_event_now(e, CLOCK_REALTIME_ALARM, &event_now) == 0);
-        if (clock_boottime_supported()) {
-                assert_se(sd_event_now(e, CLOCK_BOOTTIME, &event_now) == 0);
-                assert_se(sd_event_now(e, CLOCK_BOOTTIME_ALARM, &event_now) == 0);
-        }
+        assert_se(sd_event_now(e, CLOCK_BOOTTIME, &event_now) == 0);
+        assert_se(sd_event_now(e, CLOCK_BOOTTIME_ALARM, &event_now) == 0);
         assert_se(sd_event_now(e, -1, &event_now) == -EOPNOTSUPP);
         assert_se(sd_event_now(e, 900 /* arbitrary big number */, &event_now) == -EOPNOTSUPP);
 }
@@ -335,7 +339,7 @@ static int rtqueue_handler(sd_event_source *s, const struct signalfd_siginfo *si
         return 0;
 }
 
-static void test_rtqueue(void) {
+TEST(rtqueue) {
         sd_event_source *u = NULL, *v = NULL, *s = NULL;
         sd_event *e = NULL;
 
@@ -357,19 +361,19 @@ static void test_rtqueue(void) {
         assert_se(n_rtqueue == 0);
         assert_se(last_rtqueue_sigval == 0);
 
-        assert_se(sd_event_run(e, (uint64_t) -1) >= 1);
+        assert_se(sd_event_run(e, UINT64_MAX) >= 1);
         assert_se(n_rtqueue == 1);
         assert_se(last_rtqueue_sigval == 2); /* first SIGRTMIN+3 */
 
-        assert_se(sd_event_run(e, (uint64_t) -1) >= 1);
+        assert_se(sd_event_run(e, UINT64_MAX) >= 1);
         assert_se(n_rtqueue == 2);
         assert_se(last_rtqueue_sigval == 4); /* second SIGRTMIN+3 */
 
-        assert_se(sd_event_run(e, (uint64_t) -1) >= 1);
+        assert_se(sd_event_run(e, UINT64_MAX) >= 1);
         assert_se(n_rtqueue == 3);
         assert_se(last_rtqueue_sigval == 3); /* first SIGUSR2 */
 
-        assert_se(sd_event_run(e, (uint64_t) -1) >= 1);
+        assert_se(sd_event_run(e, UINT64_MAX) >= 1);
         assert_se(n_rtqueue == 4);
         assert_se(last_rtqueue_sigval == 1); /* SIGRTMIN+2 */
 
@@ -396,8 +400,8 @@ struct inotify_context {
 static void maybe_exit(sd_event_source *s, struct inotify_context *c) {
         unsigned n;
 
-        assert(s);
-        assert(c);
+        assert_se(s);
+        assert_se(c);
 
         if (!c->delete_self_handler_called)
                 return;
@@ -431,13 +435,12 @@ static int inotify_handler(sd_event_source *s, const struct inotify_event *ev, v
                 log_info("inotify-handler <%s>: overflow", description);
                 c->create_overflow |= bit;
         } else if (ev->mask & IN_CREATE) {
-                unsigned i;
-
-                log_debug("inotify-handler <%s>: create on %s", description, ev->name);
+                if (streq(ev->name, "sub"))
+                        log_debug("inotify-handler <%s>: create on %s", description, ev->name);
+                else {
+                        unsigned i;
 
-                if (!streq(ev->name, "sub")) {
                         assert_se(safe_atou(ev->name, &i) >= 0);
-
                         assert_se(i < c->n_create_events);
                         c->create_called[i] |= bit;
                 }
@@ -445,7 +448,7 @@ static int inotify_handler(sd_event_source *s, const struct inotify_event *ev, v
                 log_info("inotify-handler <%s>: delete of %s", description, ev->name);
                 assert_se(streq(ev->name, "sub"));
         } else
-                assert_not_reached("unexpected inotify event");
+                assert_not_reached();
 
         maybe_exit(s, c);
         return 1;
@@ -463,13 +466,13 @@ static int delete_self_handler(sd_event_source *s, const struct inotify_event *e
         } else if (ev->mask & IN_IGNORED) {
                 log_info("delete-self-handler: ignore");
         } else
-                assert_not_reached("unexpected inotify event (delete-self)");
+                assert_not_reached();
 
         maybe_exit(s, c);
         return 1;
 }
 
-static void test_inotify(unsigned n_create_events) {
+static void test_inotify_one(unsigned n_create_events) {
         _cleanup_(rm_rf_physical_and_freep) char *p = NULL;
         sd_event_source *a = NULL, *b = NULL, *c = NULL, *d = NULL;
         struct inotify_context context = {
@@ -479,6 +482,8 @@ static void test_inotify(unsigned n_create_events) {
         const char *q;
         unsigned i;
 
+        log_info("/* %s(%u) */", __func__, n_create_events);
+
         assert_se(sd_event_default(&e) >= 0);
 
         assert_se(mkdtemp_malloc("/tmp/test-inotify-XXXXXX", &p) >= 0);
@@ -520,6 +525,11 @@ static void test_inotify(unsigned n_create_events) {
         sd_event_unref(e);
 }
 
+TEST(inotify) {
+        test_inotify_one(100); /* should work without overflow */
+        test_inotify_one(33000); /* should trigger a q overflow */
+}
+
 static int pidfd_handler(sd_event_source *s, const siginfo_t *si, void *userdata) {
         assert_se(s);
         assert_se(si);
@@ -539,7 +549,7 @@ static int pidfd_handler(sd_event_source *s, const siginfo_t *si, void *userdata
         return 0;
 }
 
-static void test_pidfd(void) {
+TEST(pidfd) {
         sd_event_source *s = NULL, *t = NULL;
         sd_event *e = NULL;
         int pidfd;
@@ -612,7 +622,12 @@ static int ratelimit_time_handler(sd_event_source *s, uint64_t usec, void *userd
         return 0;
 }
 
-static void test_ratelimit(void) {
+static int expired = -1;
+static int ratelimit_expired(sd_event_source *s, void *userdata) {
+        return ++expired;
+}
+
+TEST(ratelimit) {
         _cleanup_close_pair_ int p[2] = {-1, -1};
         _cleanup_(sd_event_unrefp) sd_event *e = NULL;
         _cleanup_(sd_event_source_unrefp) sd_event_source *s = NULL;
@@ -639,7 +654,7 @@ static void test_ratelimit(void) {
 
         assert_se(sd_event_source_is_ratelimited(s) == 0);
         assert_se(count == 10);
-        log_info("ratelimit_io_handler: called %d times, event source not ratelimited", count);
+        log_info("ratelimit_io_handler: called %u times, event source not ratelimited", count);
 
         assert_se(sd_event_source_set_ratelimit(s, 0, 0) >= 0);
         assert_se(sd_event_source_set_ratelimit(s, 1 * USEC_PER_SEC, 5) >= 0);
@@ -650,7 +665,7 @@ static void test_ratelimit(void) {
                 assert_se(sd_event_run(e, UINT64_MAX) >= 0);
                 assert_se(usleep(10) >= 0);
         }
-        log_info("ratelimit_io_handler: called %d times, event source got ratelimited", count);
+        log_info("ratelimit_io_handler: called %u times, event source got ratelimited", count);
         assert_se(count < 10);
 
         s = sd_event_source_unref(s);
@@ -664,38 +679,133 @@ static void test_ratelimit(void) {
                 assert_se(sd_event_run(e, UINT64_MAX) >= 0);
         } while (!sd_event_source_is_ratelimited(s));
 
-        log_info("ratelimit_time_handler: called %d times, event source got ratelimited", count);
+        log_info("ratelimit_time_handler: called %u times, event source got ratelimited", count);
         assert_se(count == 10);
 
-        /* In order to get rid of active rate limit client needs to disable it explicitely */
+        /* In order to get rid of active rate limit client needs to disable it explicitly */
         assert_se(sd_event_source_set_ratelimit(s, 0, 0) >= 0);
         assert_se(!sd_event_source_is_ratelimited(s));
 
         assert_se(sd_event_source_set_ratelimit(s, 1 * USEC_PER_SEC, 10) >= 0);
 
+        /* Set callback that will be invoked when we leave rate limited state. */
+        assert_se(sd_event_source_set_ratelimit_expire_callback(s, ratelimit_expired) >= 0);
+
         do {
                 assert_se(sd_event_run(e, UINT64_MAX) >= 0);
         } while (!sd_event_source_is_ratelimited(s));
 
         log_info("ratelimit_time_handler: called 10 more times, event source got ratelimited");
         assert_se(count == 20);
+
+        /* Dispatch the event loop once more and check that ratelimit expiration callback got called */
+        assert_se(sd_event_run(e, UINT64_MAX) >= 0);
+        assert_se(expired == 0);
 }
 
-int main(int argc, char *argv[]) {
-        test_setup_logging(LOG_DEBUG);
+TEST(simple_timeout) {
+        _cleanup_(sd_event_unrefp) sd_event *e = NULL;
+        usec_t f, t, some_time;
 
-        test_basic(true);   /* test with pidfd */
-        test_basic(false);  /* test without pidfd */
+        some_time = random_u64_range(2 * USEC_PER_SEC);
 
-        test_sd_event_now();
-        test_rtqueue();
+        assert_se(sd_event_default(&e) >= 0);
 
-        test_inotify(100); /* should work without overflow */
-        test_inotify(33000); /* should trigger a q overflow */
+        assert_se(sd_event_prepare(e) == 0);
 
-        test_pidfd();
+        f = now(CLOCK_MONOTONIC);
+        assert_se(sd_event_wait(e, some_time) >= 0);
+        t = now(CLOCK_MONOTONIC);
 
-        test_ratelimit();
+        /* The event loop may sleep longer than the specified time (timer accuracy, scheduling latencies, …),
+         * but never shorter. Let's check that. */
+        assert_se(t >= usec_add(f, some_time));
+}
 
-        return 0;
+static int inotify_self_destroy_handler(sd_event_source *s, const struct inotify_event *ev, void *userdata) {
+        sd_event_source **p = userdata;
+
+        assert_se(ev);
+        assert_se(p);
+        assert_se(*p == s);
+
+        assert_se(FLAGS_SET(ev->mask, IN_ATTRIB));
+
+        assert_se(sd_event_exit(sd_event_source_get_event(s), 0) >= 0);
+
+        *p = sd_event_source_unref(*p); /* here's what we actually intend to test: we destroy the event
+                                         * source from inside the event source handler */
+        return 1;
+}
+
+TEST(inotify_self_destroy) {
+        _cleanup_(sd_event_source_unrefp) sd_event_source *s = NULL;
+        _cleanup_(sd_event_unrefp) sd_event *e = NULL;
+        char path[] = "/tmp/inotifyXXXXXX";
+        _cleanup_close_ int fd = -EBADF;
+
+        /* Tests that destroying an inotify event source from its own handler is safe */
+
+        assert_se(sd_event_default(&e) >= 0);
+
+        fd = mkostemp_safe(path);
+        assert_se(fd >= 0);
+        assert_se(sd_event_add_inotify_fd(e, &s, fd, IN_ATTRIB, inotify_self_destroy_handler, &s) >= 0);
+        fd = safe_close(fd);
+        assert_se(unlink(path) >= 0); /* This will trigger IN_ATTRIB because link count goes to zero */
+        assert_se(sd_event_loop(e) >= 0);
 }
+
+struct inotify_process_buffered_data_context {
+        const char *path[2];
+        unsigned i;
+};
+
+static int inotify_process_buffered_data_handler(sd_event_source *s, const struct inotify_event *ev, void *userdata) {
+        struct inotify_process_buffered_data_context *c = ASSERT_PTR(userdata);
+        const char *description;
+
+        assert_se(sd_event_source_get_description(s, &description) >= 0);
+
+        assert_se(c->i < 2);
+        assert_se(streq(c->path[c->i], description));
+        c->i++;
+
+        return 1;
+}
+
+TEST(inotify_process_buffered_data) {
+        _cleanup_(rm_rf_physical_and_freep) char *p = NULL, *q = NULL;
+        _cleanup_(sd_event_source_unrefp) sd_event_source *a = NULL, *b = NULL;
+        _cleanup_(sd_event_unrefp) sd_event *e = NULL;
+        _cleanup_free_ char *z = NULL;
+
+        /* For issue #23826 */
+
+        assert_se(sd_event_default(&e) >= 0);
+
+        assert_se(mkdtemp_malloc("/tmp/test-inotify-XXXXXX", &p) >= 0);
+        assert_se(mkdtemp_malloc("/tmp/test-inotify-XXXXXX", &q) >= 0);
+
+        struct inotify_process_buffered_data_context context = {
+                .path = { p, q },
+        };
+
+        assert_se(sd_event_add_inotify(e, &a, p, IN_CREATE, inotify_process_buffered_data_handler, &context) >= 0);
+        assert_se(sd_event_add_inotify(e, &b, q, IN_CREATE, inotify_process_buffered_data_handler, &context) >= 0);
+
+        assert_se(z = path_join(p, "aaa"));
+        assert_se(touch(z) >= 0);
+        z = mfree(z);
+        assert_se(z = path_join(q, "bbb"));
+        assert_se(touch(z) >= 0);
+        z = mfree(z);
+
+        assert_se(sd_event_run(e, 10 * USEC_PER_SEC) > 0);
+        assert_se(sd_event_prepare(e) > 0); /* issue #23826: this was 0. */
+        assert_se(sd_event_dispatch(e) > 0);
+        assert_se(sd_event_prepare(e) == 0);
+        assert_se(sd_event_wait(e, 0) == 0);
+}
+
+DEFINE_TEST_MAIN(LOG_DEBUG);