From b778cba4bfab53c567f6a81adb6413ab7d634c92 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 1 Oct 2020 22:17:31 +0200 Subject: [PATCH] sd-event: optionally, if an event source fails, exit the event loop Currently, if an event source callback returns an error, we'll disable the event source and continue. This adds a per-event source flag that if turned on goes further: the event loop is also exited, propagating the error code. This is inspired by some patterns repeatedly seen in #15206. The idea is that event sources that server the "primary" function of a program are marked like this, so that if they fail the failure is instantly propagated and terminates the program. --- src/libsystemd/libsystemd.sym | 2 + src/libsystemd/sd-event/event-source.h | 1 + src/libsystemd/sd-event/sd-event.c | 51 ++++++++++++++++++++++---- src/systemd/sd-event.h | 2 + 4 files changed, 48 insertions(+), 8 deletions(-) diff --git a/src/libsystemd/libsystemd.sym b/src/libsystemd/libsystemd.sym index c6dfcd6791..6e7f2eee53 100644 --- a/src/libsystemd/libsystemd.sym +++ b/src/libsystemd/libsystemd.sym @@ -726,6 +726,8 @@ LIBSYSTEMD_247 { global: sd_event_add_time_relative; sd_event_source_set_time_relative; + sd_event_source_get_exit_on_failure; + sd_event_source_set_exit_on_failure; sd_bus_error_has_names_sentinel; diff --git a/src/libsystemd/sd-event/event-source.h b/src/libsystemd/sd-event/event-source.h index 08eb9b6a61..a8a30d825e 100644 --- a/src/libsystemd/sd-event/event-source.h +++ b/src/libsystemd/sd-event/event-source.h @@ -60,6 +60,7 @@ struct sd_event_source { bool pending:1; bool dispatching:1; bool floating:1; + bool exit_on_failure:1; int64_t priority; unsigned pending_index; diff --git a/src/libsystemd/sd-event/sd-event.c b/src/libsystemd/sd-event/sd-event.c index 7dd43f2ddc..e891f62bb7 100644 --- a/src/libsystemd/sd-event/sd-event.c +++ b/src/libsystemd/sd-event/sd-event.c @@ -3183,16 +3183,21 @@ static int process_inotify(sd_event *e) { } static int source_dispatch(sd_event_source *s) { + _cleanup_(sd_event_unrefp) sd_event *saved_event = NULL; EventSourceType saved_type; int r = 0; assert(s); assert(s->pending || s->type == SOURCE_EXIT); - /* Save the event source type, here, so that we still know it after the event callback which might invalidate - * the event. */ + /* Save the event source type, here, so that we still know it after the event callback which might + * invalidate the event. */ saved_type = s->type; + /* Similar, store a reference to the event loop object, so that we can still access it after the + * callback might have invalidated/disconnected the event source. */ + saved_event = sd_event_ref(s->event); + if (!IN_SET(s->type, SOURCE_DEFER, SOURCE_EXIT)) { r = source_set_pending(s, false); if (r < 0) @@ -3299,9 +3304,15 @@ static int source_dispatch(sd_event_source *s) { s->dispatching = false; - if (r < 0) - log_debug_errno(r, "Event source %s (type %s) returned error, disabling: %m", - strna(s->description), event_source_type_to_string(saved_type)); + if (r < 0) { + log_debug_errno(r, "Event source %s (type %s) returned error, %s: %m", + strna(s->description), + event_source_type_to_string(saved_type), + s->exit_on_failure ? "exiting" : "disabling"); + + if (s->exit_on_failure) + (void) sd_event_exit(saved_event, r); + } if (s->n_ref == 0) source_free(s); @@ -3334,9 +3345,15 @@ static int event_prepare(sd_event *e) { r = s->prepare(s, s->userdata); s->dispatching = false; - if (r < 0) - log_debug_errno(r, "Prepare callback of event source %s (type %s) returned error, disabling: %m", - strna(s->description), event_source_type_to_string(s->type)); + if (r < 0) { + log_debug_errno(r, "Prepare callback of event source %s (type %s) returned error, %s: %m", + strna(s->description), + event_source_type_to_string(s->type), + s->exit_on_failure ? "exiting" : "disabling"); + + if (s->exit_on_failure) + (void) sd_event_exit(e, r); + } if (s->n_ref == 0) source_free(s); @@ -3974,3 +3991,21 @@ _public_ int sd_event_source_set_floating(sd_event_source *s, int b) { return 1; } + +_public_ int sd_event_source_get_exit_on_failure(sd_event_source *s) { + assert_return(s, -EINVAL); + assert_return(s->type != SOURCE_EXIT, -EDOM); + + return s->exit_on_failure; +} + +_public_ int sd_event_source_set_exit_on_failure(sd_event_source *s, int b) { + assert_return(s, -EINVAL); + assert_return(s->type != SOURCE_EXIT, -EDOM); + + if (s->exit_on_failure == !!b) + return 0; + + s->exit_on_failure = b; + return 1; +} diff --git a/src/systemd/sd-event.h b/src/systemd/sd-event.h index dc96bfa681..3a53c3d27d 100644 --- a/src/systemd/sd-event.h +++ b/src/systemd/sd-event.h @@ -160,6 +160,8 @@ int sd_event_source_set_destroy_callback(sd_event_source *s, sd_event_destroy_t int sd_event_source_get_destroy_callback(sd_event_source *s, sd_event_destroy_t *ret); int sd_event_source_get_floating(sd_event_source *s); int sd_event_source_set_floating(sd_event_source *s, int b); +int sd_event_source_get_exit_on_failure(sd_event_source *s); +int sd_event_source_set_exit_on_failure(sd_event_source *s, int b); /* Define helpers so that __attribute__((cleanup(sd_event_unrefp))) and similar may be used. */ _SD_DEFINE_POINTER_CLEANUP_FUNC(sd_event, sd_event_unref);