]> git.ipfire.org Git - thirdparty/systemd.git/blobdiff - src/libsystemd/sd-event/sd-event.c
tree-wide: generate EBADF when we get invalid fds
[thirdparty/systemd.git] / src / libsystemd / sd-event / sd-event.c
index c5f062b3e00409bd4f2d20dc67cfc4ae2e87a4b7..0e33ced342bf4040de3e56898465cf0266665617 100644 (file)
@@ -22,7 +22,6 @@
 #include <sys/epoll.h>
 #include <sys/timerfd.h>
 #include <sys/wait.h>
-#include <pthread.h>
 
 #include "sd-id128.h"
 #include "sd-daemon.h"
 #include "missing.h"
 #include "set.h"
 #include "list.h"
+#include "signal-util.h"
 
 #include "sd-event.h"
 
-#define EPOLL_QUEUE_MAX 512U
 #define DEFAULT_ACCURACY_USEC (250 * USEC_PER_MSEC)
 
 typedef enum EventSourceType {
@@ -66,7 +65,7 @@ struct sd_event_source {
         void *userdata;
         sd_event_handler_t prepare;
 
-        char *name;
+        char *description;
 
         EventSourceType type:5;
         int enabled:3;
@@ -463,27 +462,28 @@ _public_ sd_event* sd_event_unref(sd_event *e) {
 static bool event_pid_changed(sd_event *e) {
         assert(e);
 
-        /* We don't support people creating am event loop and keeping
+        /* We don't support people creating an event loop and keeping
          * it around over a fork(). Let's complain. */
 
         return e->original_pid != getpid();
 }
 
-static int source_io_unregister(sd_event_source *s) {
+static void source_io_unregister(sd_event_source *s) {
         int r;
 
         assert(s);
         assert(s->type == SOURCE_IO);
 
+        if (event_pid_changed(s->event))
+                return;
+
         if (!s->io.registered)
-                return 0;
+                return;
 
         r = epoll_ctl(s->event->epoll_fd, EPOLL_CTL_DEL, s->io.fd, NULL);
-        if (r < 0)
-                return -errno;
+        assert_log(r >= 0);
 
         s->io.registered = false;
-        return 0;
 }
 
 static int source_io_register(
@@ -598,6 +598,39 @@ static bool need_signal(sd_event *e, int signal) {
                 e->n_enabled_child_sources > 0);
 }
 
+static int event_update_signal_fd(sd_event *e) {
+        struct epoll_event ev = {};
+        bool add_to_epoll;
+        int r;
+
+        assert(e);
+
+        if (event_pid_changed(e))
+                return 0;
+
+        add_to_epoll = e->signal_fd < 0;
+
+        r = signalfd(e->signal_fd, &e->sigset, SFD_NONBLOCK|SFD_CLOEXEC);
+        if (r < 0)
+                return -errno;
+
+        e->signal_fd = r;
+
+        if (!add_to_epoll)
+                return 0;
+
+        ev.events = EPOLLIN;
+        ev.data.ptr = INT_TO_PTR(SOURCE_SIGNAL);
+
+        r = epoll_ctl(e->epoll_fd, EPOLL_CTL_ADD, e->signal_fd, &ev);
+        if (r < 0) {
+                e->signal_fd = safe_close(e->signal_fd);
+                return -errno;
+        }
+
+        return 0;
+}
+
 static void source_disconnect(sd_event_source *s) {
         sd_event *event;
 
@@ -640,6 +673,10 @@ static void source_disconnect(sd_event_source *s) {
                         /* If the signal was on and now it is off... */
                         if (s->enabled != SD_EVENT_OFF && !need_signal(s->event, s->signal.sig)) {
                                 assert_se(sigdelset(&s->event->sigset, s->signal.sig) == 0);
+
+                                (void) event_update_signal_fd(s->event);
+                                /* If disabling failed, we might get a spurious event,
+                                 * but otherwise nothing bad should happen. */
                         }
                 }
 
@@ -654,6 +691,10 @@ static void source_disconnect(sd_event_source *s) {
                                 /* We know the signal was on, if it is off now... */
                                 if (!need_signal(s->event, SIGCHLD)) {
                                         assert_se(sigdelset(&s->event->sigset, SIGCHLD) == 0);
+
+                                        (void) event_update_signal_fd(s->event);
+                                        /* If disabling failed, we might get a spurious event,
+                                         * but otherwise nothing bad should happen. */
                                 }
                         }
 
@@ -699,7 +740,7 @@ static void source_free(sd_event_source *s) {
         assert(s);
 
         source_disconnect(s);
-        free(s->name);
+        free(s->description);
         free(s);
 }
 
@@ -775,7 +816,7 @@ _public_ int sd_event_add_io(
         int r;
 
         assert_return(e, -EINVAL);
-        assert_return(fd >= 0, -EINVAL);
+        assert_return(fd >= 0, -EBADF);
         assert_return(!(events & ~(EPOLLIN|EPOLLOUT|EPOLLRDHUP|EPOLLPRI|EPOLLERR|EPOLLHUP|EPOLLET)), -EINVAL);
         assert_return(callback, -EINVAL);
         assert_return(e->state != SD_EVENT_FINISHED, -ESTALE);
@@ -853,6 +894,12 @@ static int event_setup_timer_fd(
         return 0;
 }
 
+static int time_exit_callback(sd_event_source *s, uint64_t usec, void *userdata) {
+        assert(s);
+
+        return sd_event_exit(sd_event_source_get_event(s), PTR_TO_INT(userdata));
+}
+
 _public_ int sd_event_add_time(
                 sd_event *e,
                 sd_event_source **ret,
@@ -870,12 +917,14 @@ _public_ int sd_event_add_time(
         assert_return(e, -EINVAL);
         assert_return(usec != (uint64_t) -1, -EINVAL);
         assert_return(accuracy != (uint64_t) -1, -EINVAL);
-        assert_return(callback, -EINVAL);
         assert_return(e->state != SD_EVENT_FINISHED, -ESTALE);
         assert_return(!event_pid_changed(e), -ECHILD);
 
+        if (!callback)
+                callback = time_exit_callback;
+
         type = clock_to_event_source_type(clock);
-        assert_return(type >= 0, -ENOTSUP);
+        assert_return(type >= 0, -EOPNOTSUPP);
 
         d = event_get_clock_data(e, type);
         assert(d);
@@ -929,36 +978,6 @@ fail:
         return r;
 }
 
-static int event_update_signal_fd(sd_event *e) {
-        struct epoll_event ev = {};
-        bool add_to_epoll;
-        int r;
-
-        assert(e);
-
-        add_to_epoll = e->signal_fd < 0;
-
-        r = signalfd(e->signal_fd, &e->sigset, SFD_NONBLOCK|SFD_CLOEXEC);
-        if (r < 0)
-                return -errno;
-
-        e->signal_fd = r;
-
-        if (!add_to_epoll)
-                return 0;
-
-        ev.events = EPOLLIN;
-        ev.data.ptr = INT_TO_PTR(SOURCE_SIGNAL);
-
-        r = epoll_ctl(e->epoll_fd, EPOLL_CTL_ADD, e->signal_fd, &ev);
-        if (r < 0) {
-                e->signal_fd = safe_close(e->signal_fd);
-                return -errno;
-        }
-
-        return 0;
-}
-
 static int signal_exit_callback(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata) {
         assert(s);
 
@@ -1023,6 +1042,9 @@ _public_ int sd_event_add_signal(
                 }
         }
 
+        /* Use the signal name as description for the event source by default */
+        (void) sd_event_source_set_description(s, signal_to_string(sig));
+
         if (ret)
                 *ret = s;
 
@@ -1245,18 +1267,20 @@ _public_ sd_event_source* sd_event_source_unref(sd_event_source *s) {
         return NULL;
 }
 
-_public_ int sd_event_source_set_name(sd_event_source *s, const char *name) {
+_public_ int sd_event_source_set_description(sd_event_source *s, const char *description) {
         assert_return(s, -EINVAL);
+        assert_return(!event_pid_changed(s->event), -ECHILD);
 
-        return free_and_strdup(&s->name, name);
+        return free_and_strdup(&s->description, description);
 }
 
-_public_ int sd_event_source_get_name(sd_event_source *s, const char **name) {
+_public_ int sd_event_source_get_description(sd_event_source *s, const char **description) {
         assert_return(s, -EINVAL);
-        assert_return(name, -EINVAL);
-
-        *name = s->name;
+        assert_return(description, -EINVAL);
+        assert_return(s->description, -ENXIO);
+        assert_return(!event_pid_changed(s->event), -ECHILD);
 
+        *description = s->description;
         return 0;
 }
 
@@ -1287,7 +1311,7 @@ _public_ int sd_event_source_set_io_fd(sd_event_source *s, int fd) {
         int r;
 
         assert_return(s, -EINVAL);
-        assert_return(fd >= 0, -EINVAL);
+        assert_return(fd >= 0, -EBADF);
         assert_return(s->type == SOURCE_IO, -EDOM);
         assert_return(!event_pid_changed(s->event), -ECHILD);
 
@@ -1431,10 +1455,7 @@ _public_ int sd_event_source_set_enabled(sd_event_source *s, int m) {
                 switch (s->type) {
 
                 case SOURCE_IO:
-                        r = source_io_unregister(s);
-                        if (r < 0)
-                                return r;
-
+                        source_io_unregister(s);
                         s->enabled = m;
                         break;
 
@@ -2144,10 +2165,10 @@ static int source_dispatch(sd_event_source *s) {
         s->dispatching = false;
 
         if (r < 0) {
-                if (s->name)
-                        log_debug("Event source '%s' returned error, disabling: %s", s->name, strerror(-r));
+                if (s->description)
+                        log_debug_errno(r, "Event source '%s' returned error, disabling: %m", s->description);
                 else
-                        log_debug("Event source %p returned error, disabling: %s", s, strerror(-r));
+                        log_debug_errno(r, "Event source %p returned error, disabling: %m", s);
         }
 
         if (s->n_ref == 0)
@@ -2182,10 +2203,10 @@ static int event_prepare(sd_event *e) {
                 s->dispatching = false;
 
                 if (r < 0) {
-                        if (s->name)
-                                log_debug("Prepare callback of event source '%s' returned error, disabling: %s", s->name, strerror(-r));
+                        if (s->description)
+                                log_debug_errno(r, "Prepare callback of event source '%s' returned error, disabling: %m", s->description);
                         else
-                                log_debug("Prepare callback of event source %p returned error, disabling: %s", s, strerror(-r));
+                                log_debug_errno(r, "Prepare callback of event source %p returned error, disabling: %m", s);
                 }
 
                 if (s->n_ref == 0)
@@ -2215,7 +2236,7 @@ static int dispatch_exit(sd_event *e) {
 
         r = source_dispatch(p);
 
-        e->state = SD_EVENT_PASSIVE;
+        e->state = SD_EVENT_INITIAL;
         sd_event_unref(e);
 
         return r;
@@ -2284,7 +2305,7 @@ _public_ int sd_event_prepare(sd_event *e) {
         assert_return(e, -EINVAL);
         assert_return(!event_pid_changed(e), -ECHILD);
         assert_return(e->state != SD_EVENT_FINISHED, -ESTALE);
-        assert_return(e->state == SD_EVENT_PASSIVE, -EBUSY);
+        assert_return(e->state == SD_EVENT_INITIAL, -EBUSY);
 
         if (e->exit_requested)
                 goto pending;
@@ -2318,15 +2339,15 @@ _public_ int sd_event_prepare(sd_event *e) {
         if (event_next_pending(e) || e->need_process_child)
                 goto pending;
 
-        e->state = SD_EVENT_PREPARED;
+        e->state = SD_EVENT_ARMED;
 
         return 0;
 
 pending:
-        e->state = SD_EVENT_PREPARED;
+        e->state = SD_EVENT_ARMED;
         r = sd_event_wait(e, 0);
         if (r == 0)
-                e->state = SD_EVENT_PREPARED;
+                e->state = SD_EVENT_ARMED;
 
         return r;
 }
@@ -2339,14 +2360,14 @@ _public_ int sd_event_wait(sd_event *e, uint64_t timeout) {
         assert_return(e, -EINVAL);
         assert_return(!event_pid_changed(e), -ECHILD);
         assert_return(e->state != SD_EVENT_FINISHED, -ESTALE);
-        assert_return(e->state == SD_EVENT_PREPARED, -EBUSY);
+        assert_return(e->state == SD_EVENT_ARMED, -EBUSY);
 
         if (e->exit_requested) {
                 e->state = SD_EVENT_PENDING;
                 return 1;
         }
 
-        ev_queue_max = CLAMP(e->n_sources, 1U, EPOLL_QUEUE_MAX);
+        ev_queue_max = MAX(e->n_sources, 1u);
         ev_queue = newa(struct epoll_event, ev_queue_max);
 
         m = epoll_wait(e->epoll_fd, ev_queue, ev_queue_max,
@@ -2358,7 +2379,6 @@ _public_ int sd_event_wait(sd_event *e, uint64_t timeout) {
                 }
 
                 r = -errno;
-
                 goto finish;
         }
 
@@ -2427,7 +2447,7 @@ _public_ int sd_event_wait(sd_event *e, uint64_t timeout) {
         r = 0;
 
 finish:
-        e->state = SD_EVENT_PASSIVE;
+        e->state = SD_EVENT_INITIAL;
 
         return r;
 }
@@ -2450,14 +2470,14 @@ _public_ int sd_event_dispatch(sd_event *e) {
 
                 e->state = SD_EVENT_RUNNING;
                 r = source_dispatch(p);
-                e->state = SD_EVENT_PASSIVE;
+                e->state = SD_EVENT_INITIAL;
 
                 sd_event_unref(e);
 
                 return r;
         }
 
-        e->state = SD_EVENT_PASSIVE;
+        e->state = SD_EVENT_INITIAL;
 
         return 1;
 }
@@ -2468,19 +2488,23 @@ _public_ int sd_event_run(sd_event *e, uint64_t timeout) {
         assert_return(e, -EINVAL);
         assert_return(!event_pid_changed(e), -ECHILD);
         assert_return(e->state != SD_EVENT_FINISHED, -ESTALE);
-        assert_return(e->state == SD_EVENT_PASSIVE, -EBUSY);
+        assert_return(e->state == SD_EVENT_INITIAL, -EBUSY);
 
         r = sd_event_prepare(e);
-        if (r > 0)
-                return sd_event_dispatch(e);
-        else if (r < 0)
-                return r;
+        if (r == 0)
+                /* There was nothing? Then wait... */
+                r = sd_event_wait(e, timeout);
 
-        r = sd_event_wait(e, timeout);
-        if (r > 0)
-                return sd_event_dispatch(e);
-        else
-                return r;
+        if (r > 0) {
+                /* There's something now, then let's dispatch it */
+                r = sd_event_dispatch(e);
+                if (r < 0)
+                        return r;
+
+                return 1;
+        }
+
+        return r;
 }
 
 _public_ int sd_event_loop(sd_event *e) {
@@ -2488,7 +2512,7 @@ _public_ int sd_event_loop(sd_event *e) {
 
         assert_return(e, -EINVAL);
         assert_return(!event_pid_changed(e), -ECHILD);
-        assert_return(e->state == SD_EVENT_PASSIVE, -EBUSY);
+        assert_return(e->state == SD_EVENT_INITIAL, -EBUSY);
 
         sd_event_ref(e);
 
@@ -2548,9 +2572,12 @@ _public_ int sd_event_now(sd_event *e, clockid_t clock, uint64_t *usec) {
         assert_return(usec, -EINVAL);
         assert_return(!event_pid_changed(e), -ECHILD);
 
-        /* If we haven't run yet, just get the actual time */
-        if (!dual_timestamp_is_set(&e->timestamp))
-                return -ENODATA;
+        if (!dual_timestamp_is_set(&e->timestamp)) {
+                /* Implicitly fall back to now() if we never ran
+                 * before and thus have no cached time. */
+                *usec = now(clock);
+                return 1;
+        }
 
         switch (clock) {