diff --git a/src/libsystemd/sd-event/event-util.c b/src/libsystemd/sd-event/event-util.c index d4e986fc49..d7f15882c8 100644 --- a/src/libsystemd/sd-event/event-util.c +++ b/src/libsystemd/sd-event/event-util.c @@ -12,12 +12,10 @@ #define SI_FLAG_FORWARD (INT32_C(1) << 30) #define SI_FLAG_POSITIVE (INT32_C(1) << 29) -DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR( +DEFINE_HASH_OPS_WITH_VALUE_DESTRUCTOR( event_source_hash_ops, - sd_event_source, - (void (*)(const sd_event_source*, struct siphash*)) trivial_hash_func, - (int (*)(const sd_event_source*, const sd_event_source*)) trivial_compare_func, - sd_event_source_disable_unref); + void, trivial_hash_func, trivial_compare_func, + sd_event_source, sd_event_source_disable_unref); int event_reset_time( sd_event *e, diff --git a/src/socket-proxy/socket-proxyd.c b/src/socket-proxy/socket-proxyd.c index 3a0160d17f..a9ec68cda6 100644 --- a/src/socket-proxy/socket-proxyd.c +++ b/src/socket-proxy/socket-proxyd.c @@ -16,6 +16,7 @@ #include "alloc-util.h" #include "build.h" #include "daemon-util.h" +#include "event-util.h" #include "errno-util.h" #include "fd-util.h" #include "log.h" @@ -58,8 +59,9 @@ typedef struct Connection { sd_resolve_query *resolve_query; } Connection; -static void connection_free(Connection *c) { - assert(c); +static Connection* connection_free(Connection *c) { + if (!c) + return NULL; if (c->context) set_remove(c->context->connections, c); @@ -75,7 +77,25 @@ static void connection_free(Connection *c) { sd_resolve_query_unref(c->resolve_query); - free(c); + return mfree(c); +} + +DEFINE_TRIVIAL_CLEANUP_FUNC(Connection*, connection_free); + +DEFINE_PRIVATE_HASH_OPS_WITH_VALUE_DESTRUCTOR( + connection_hash_ops, + void, trivial_hash_func, trivial_compare_func, + Connection, connection_free); + +static void context_done(Context *context) { + assert(context); + + set_free(context->listen); + set_free(context->connections); + + sd_event_unref(context->event); + sd_resolve_unref(context->resolve); + sd_event_source_unref(context->idle_time); } static int idle_time_cb(sd_event_source *s, uint64_t usec, void *userdata) { @@ -95,42 +115,26 @@ static int idle_time_cb(sd_event_source *s, uint64_t usec, void *userdata) { return 0; } -static int connection_release(Connection *c) { - Context *context = ASSERT_PTR(ASSERT_PTR(c)->context); +static void context_reset_timer(Context *context) { int r; - connection_free(c); - - if (arg_exit_idle_time < USEC_INFINITY && set_isempty(context->connections)) { - if (context->idle_time) { - r = sd_event_source_set_time_relative(context->idle_time, arg_exit_idle_time); - if (r < 0) - return log_error_errno(r, "Error while setting idle time: %m"); - - r = sd_event_source_set_enabled(context->idle_time, SD_EVENT_ONESHOT); - if (r < 0) - return log_error_errno(r, "Error while enabling idle time: %m"); - } else { - r = sd_event_add_time_relative( - context->event, &context->idle_time, CLOCK_MONOTONIC, - arg_exit_idle_time, 0, idle_time_cb, context); - if (r < 0) - return log_error_errno(r, "Failed to create idle timer: %m"); - } - } - - return 0; -} - -static void context_clear(Context *context) { assert(context); - set_free_with_destructor(context->listen, sd_event_source_unref); - set_free_with_destructor(context->connections, connection_free); + if (arg_exit_idle_time < USEC_INFINITY && set_isempty(context->connections)) { + r = event_reset_time_relative( + context->event, &context->idle_time, CLOCK_MONOTONIC, + arg_exit_idle_time, 0, idle_time_cb, context, + SD_EVENT_PRIORITY_NORMAL, "idle-timer", /* force = */ true); + if (r < 0) + log_warning_errno(r, "Failed to reset idle timer, ignoring: %m"); + } +} - sd_event_unref(context->event); - sd_resolve_unref(context->resolve); - sd_event_source_unref(context->idle_time); +static void connection_release(Connection *c) { + assert(c); + + connection_free(c); + context_reset_timer(c->context); } static int connection_create_pipes(Connection *c, int buffer[static 2], size_t *sz) { @@ -303,34 +307,29 @@ static int connection_complete(Connection *c) { r = connection_create_pipes(c, c->server_to_client_buffer, &c->server_to_client_buffer_size); if (r < 0) - goto fail; + return r; r = connection_create_pipes(c, c->client_to_server_buffer, &c->client_to_server_buffer_size); if (r < 0) - goto fail; + return r; r = connection_enable_event_sources(c); if (r < 0) - goto fail; + return r; return 0; - -fail: - connection_release(c); - return 0; /* ignore errors, continue serving */ } static int connect_cb(sd_event_source *s, int fd, uint32_t revents, void *userdata) { Connection *c = ASSERT_PTR(userdata); socklen_t solen; - int error, r; + int error; assert(s); assert(fd >= 0); solen = sizeof(error); - r = getsockopt(fd, SOL_SOCKET, SO_ERROR, &error, &solen); - if (r < 0) { + if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &error, &solen) < 0) { log_error_errno(errno, "Failed to issue SO_ERROR: %m"); goto fail; } @@ -342,7 +341,10 @@ static int connect_cb(sd_event_source *s, int fd, uint32_t revents, void *userda c->client_event_source = sd_event_source_unref(c->client_event_source); - return connection_complete(c); + if (connection_complete(c) < 0) + goto fail; + + return 0; fail: connection_release(c); @@ -357,40 +359,26 @@ static int connection_start(Connection *c, struct sockaddr *sa, socklen_t salen) assert(salen); c->client_fd = socket(sa->sa_family, SOCK_STREAM|SOCK_NONBLOCK|SOCK_CLOEXEC, 0); - if (c->client_fd < 0) { - log_error_errno(errno, "Failed to get remote socket: %m"); - goto fail; - } + if (c->client_fd < 0) + return log_error_errno(errno, "Failed to get remote socket: %m"); r = connect(c->client_fd, sa, salen); if (r < 0) { - if (errno == EINPROGRESS) { - r = sd_event_add_io(c->context->event, &c->client_event_source, c->client_fd, EPOLLOUT, connect_cb, c); - if (r < 0) { - log_error_errno(r, "Failed to add connection socket: %m"); - goto fail; - } + if (errno != EINPROGRESS) + return log_error_errno(errno, "Failed to connect to remote host: %m"); - r = sd_event_source_set_enabled(c->client_event_source, SD_EVENT_ONESHOT); - if (r < 0) { - log_error_errno(r, "Failed to enable oneshot event source: %m"); - goto fail; - } - } else { - log_error_errno(errno, "Failed to connect to remote host: %m"); - goto fail; - } - } else { - r = connection_complete(c); + r = sd_event_add_io(c->context->event, &c->client_event_source, c->client_fd, EPOLLOUT, connect_cb, c); if (r < 0) - goto fail; + return log_error_errno(r, "Failed to add connection socket: %m"); + + r = sd_event_source_set_enabled(c->client_event_source, SD_EVENT_ONESHOT); + if (r < 0) + return log_error_errno(r, "Failed to enable oneshot event source: %m"); + + return 0; } - return 0; - -fail: - connection_release(c); - return 0; /* ignore errors, continue serving */ + return connection_complete(c); } static int resolve_handler(sd_resolve_query *q, int ret, const struct addrinfo *ai, Connection *c) { @@ -404,7 +392,10 @@ static int resolve_handler(sd_resolve_query *q, int ret, const struct addrinfo * c->resolve_query = sd_resolve_query_unref(c->resolve_query); - return connection_start(c, ai->ai_addr, ai->ai_addrlen); + if (connection_start(c, ai->ai_addr, ai->ai_addrlen) < 0) + goto fail; + + return 0; fail: connection_release(c); @@ -426,10 +417,8 @@ static int resolve_remote(Connection *c) { int sa_len; r = sockaddr_un_set_path(&sa.un, arg_remote_host); - if (r < 0) { - log_error_errno(r, "Specified address doesn't fit in an AF_UNIX address, refusing: %m"); - goto fail; - } + if (r < 0) + return log_error_errno(r, "Specified address doesn't fit in an AF_UNIX address, refusing: %m"); sa_len = r; return connection_start(c, &sa.sa, sa_len); @@ -447,82 +436,73 @@ static int resolve_remote(Connection *c) { log_debug("Looking up address info for %s:%s", node, service); r = resolve_getaddrinfo(c->context->resolve, &c->resolve_query, node, service, &hints, resolve_handler, NULL, c); - if (r < 0) { - log_error_errno(r, "Failed to resolve remote host: %m"); - goto fail; - } + if (r < 0) + return log_error_errno(r, "Failed to resolve remote host: %m"); return 0; - -fail: - connection_release(c); - return 0; /* ignore errors, continue serving */ } -static int add_connection_socket(Context *context, int fd) { - Connection *c; +static int context_add_connection(Context *context, int fd) { int r; assert(context); - assert(fd >= 0); - if (set_size(context->connections) > arg_connections_max) { - log_warning("Hit connection limit, refusing connection."); - safe_close(fd); - return 0; + _cleanup_close_ int nfd = accept4(fd, NULL, NULL, SOCK_NONBLOCK|SOCK_CLOEXEC); + if (nfd < 0) { + if (!ERRNO_IS_ACCEPT_AGAIN(errno)) + log_warning_errno(errno, "Failed to accept() socket, ignoring: %m"); + + return -errno; } + if (DEBUG_LOGGING) { + _cleanup_free_ char *peer = NULL; + (void) getpeername_pretty(nfd, true, &peer); + log_debug("New connection from %s", strna(peer)); + } + + if (set_size(context->connections) > arg_connections_max) + return log_warning_errno(SYNTHETIC_ERRNO(EBUSY), "Hit connection limit, refusing connection."); + r = sd_event_source_set_enabled(context->idle_time, SD_EVENT_OFF); if (r < 0) log_warning_errno(r, "Unable to disable idle timer, continuing: %m"); - c = new(Connection, 1); - if (!c) { - log_oom(); - return 0; - } + _cleanup_(connection_freep) Connection *c = new(Connection, 1); + if (!c) + return log_oom(); *c = (Connection) { - .context = context, - .server_fd = fd, - .client_fd = -EBADF, - .server_to_client_buffer = EBADF_PAIR, - .client_to_server_buffer = EBADF_PAIR, + .server_fd = TAKE_FD(nfd), + .client_fd = -EBADF, + .server_to_client_buffer = EBADF_PAIR, + .client_to_server_buffer = EBADF_PAIR, }; - r = set_ensure_put(&context->connections, NULL, c); - if (r < 0) { - free(c); - log_oom(); - return 0; - } + r = set_ensure_put(&context->connections, &connection_hash_ops, c); + if (r < 0) + return log_oom(); - return resolve_remote(c); + c->context = context; + + r = resolve_remote(c); + if (r < 0) + return r; + + TAKE_PTR(c); + return 0; } static int accept_cb(sd_event_source *s, int fd, uint32_t revents, void *userdata) { - _cleanup_free_ char *peer = NULL; Context *context = ASSERT_PTR(userdata); - int nfd = -EBADF, r; + int r; assert(s); assert(fd >= 0); assert(revents & EPOLLIN); - nfd = accept4(fd, NULL, NULL, SOCK_NONBLOCK|SOCK_CLOEXEC); - if (nfd < 0) { - if (!ERRNO_IS_ACCEPT_AGAIN(errno)) - log_warning_errno(errno, "Failed to accept() socket: %m"); - } else { - (void) getpeername_pretty(nfd, true, &peer); - log_debug("New connection from %s", strna(peer)); - - r = add_connection_socket(context, nfd); - if (r < 0) { - log_warning_errno(r, "Failed to accept connection, ignoring: %m"); - safe_close(nfd); - } - } + if (context_add_connection(context, fd) < 0) + context_reset_timer(context); r = sd_event_source_set_enabled(s, SD_EVENT_ONESHOT); if (r < 0) @@ -532,7 +512,6 @@ static int accept_cb(sd_event_source *s, int fd, uint32_t revents, void *userdat } static int add_listen_socket(Context *context, int fd) { - sd_event_source *source; int r; assert(context); @@ -549,16 +528,11 @@ static int add_listen_socket(Context *context, int fd) { if (r < 0) return log_error_errno(r, "Failed to mark file descriptor non-blocking: %m"); + _cleanup_(sd_event_source_unrefp) sd_event_source *source = NULL; r = sd_event_add_io(context->event, &source, fd, EPOLLIN, accept_cb, context); if (r < 0) return log_error_errno(r, "Failed to add event source: %m"); - r = set_ensure_put(&context->listen, NULL, source); - if (r < 0) { - sd_event_source_unref(source); - return log_error_errno(r, "Failed to add source to set: %m"); - } - r = sd_event_source_set_exit_on_failure(source, true); if (r < 0) return log_error_errno(r, "Failed to enable exit-on-failure logic: %m"); @@ -569,6 +543,10 @@ static int add_listen_socket(Context *context, int fd) { if (r < 0) return log_error_errno(r, "Failed to enable oneshot mode: %m"); + r = set_ensure_consume(&context->listen, &event_source_hash_ops, TAKE_PTR(source)); + if (r < 0) + return log_error_errno(r, "Failed to add source to set: %m"); + return 0; } @@ -672,7 +650,7 @@ static int parse_argv(int argc, char *argv[]) { } static int run(int argc, char *argv[]) { - _cleanup_(context_clear) Context context = {}; + _cleanup_(context_done) Context context = {}; _unused_ _cleanup_(notify_on_cleanup) const char *notify_stop = NULL; int r, n, fd;