diff options
-rw-r--r-- | man/systemd-socket-proxyd.xml | 10 | ||||
-rw-r--r-- | src/socket-proxy/socket-proxyd.c | 81 |
2 files changed, 85 insertions, 6 deletions
diff --git a/man/systemd-socket-proxyd.xml b/man/systemd-socket-proxyd.xml index a72ac1bbc6..58b26aad87 100644 --- a/man/systemd-socket-proxyd.xml +++ b/man/systemd-socket-proxyd.xml @@ -67,6 +67,13 @@ <listitem><para>Sets the maximum number of simultaneous connections, defaults to 256. If the limit of concurrent connections is reached further connections will be refused.</para></listitem> </varlistentry> + <varlistentry> + <term><option>--exit-idle-time=</option></term> + + <listitem><para>Sets the time before exiting when there are no connections, defaults to + <constant>infinity</constant>. Takes a unit-less value in seconds, or a time span value such + as <literal>5min 20s</literal>.</para></listitem> + </varlistentry> </variablelist> </refsect1> <refsect1> @@ -115,6 +122,9 @@ server { <programlisting><![CDATA[# systemctl enable --now proxy-to-nginx.socket $ curl http://localhost:80/]]></programlisting> </example> + <para>If <filename>nginx.service</filename> has <varname>StopWhenUnneeded=</varname> set, then + passing <option>--exit-idle-time=</option> to <command>systemd-socket-proxyd</command> allows + both services to stop during idle periods.</para> </refsect2> <refsect2> <title>Namespace Example</title> diff --git a/src/socket-proxy/socket-proxyd.c b/src/socket-proxy/socket-proxyd.c index 2ee6fc2f0a..7b548c5076 100644 --- a/src/socket-proxy/socket-proxyd.c +++ b/src/socket-proxy/socket-proxyd.c @@ -31,10 +31,12 @@ static unsigned arg_connections_max = 256; static const char *arg_remote_host = NULL; +static usec_t arg_exit_idle_time = USEC_INFINITY; typedef struct Context { sd_event *event; sd_resolve *resolve; + sd_event_source *idle_time; Set *listen; Set *connections; @@ -75,6 +77,51 @@ static void connection_free(Connection *c) { free(c); } +static int idle_time_cb(sd_event_source *s, uint64_t usec, void *userdata) { + Context *c = userdata; + int r; + + if (!set_isempty(c->connections)) { + log_warning("Idle timer fired even though there are connections, ignoring"); + return 0; + } + + r = sd_event_exit(c->event, 0); + if (r < 0) { + log_warning_errno(r, "Error while stopping event loop, ignoring: %m"); + return 0; + } + return 0; +} + +static int connection_release(Connection *c) { + int r; + Context *context = c->context; + usec_t idle_instant; + + connection_free(c); + + if (arg_exit_idle_time < USEC_INFINITY && set_isempty(context->connections)) { + idle_instant = usec_add(now(CLOCK_MONOTONIC), arg_exit_idle_time); + if (context->idle_time) { + r = sd_event_source_set_time(context->idle_time, idle_instant); + 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(context->event, &context->idle_time, CLOCK_MONOTONIC, + idle_instant, 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); @@ -83,6 +130,7 @@ static void context_clear(Context *context) { sd_event_unref(context->event); sd_resolve_unref(context->resolve); + sd_event_source_unref(context->idle_time); } static int connection_create_pipes(Connection *c, int buffer[static 2], size_t *sz) { @@ -206,7 +254,7 @@ static int traffic_cb(sd_event_source *s, int fd, uint32_t revents, void *userda return 1; quit: - connection_free(c); + connection_release(c); return 0; /* ignore errors, continue serving */ } @@ -269,7 +317,7 @@ static int connection_complete(Connection *c) { return 0; fail: - connection_free(c); + connection_release(c); return 0; /* ignore errors, continue serving */ } @@ -299,7 +347,7 @@ static int connect_cb(sd_event_source *s, int fd, uint32_t revents, void *userda return connection_complete(c); fail: - connection_free(c); + connection_release(c); return 0; /* ignore errors, continue serving */ } @@ -343,7 +391,7 @@ static int connection_start(Connection *c, struct sockaddr *sa, socklen_t salen) return 0; fail: - connection_free(c); + connection_release(c); return 0; /* ignore errors, continue serving */ } @@ -361,7 +409,7 @@ static int resolve_handler(sd_resolve_query *q, int ret, const struct addrinfo * return connection_start(c, ai->ai_addr, ai->ai_addrlen); fail: - connection_free(c); + connection_release(c); return 0; /* ignore errors, continue serving */ } @@ -409,7 +457,7 @@ static int resolve_remote(Connection *c) { return 0; fail: - connection_free(c); + connection_release(c); return 0; /* ignore errors, continue serving */ } @@ -426,6 +474,12 @@ static int add_connection_socket(Context *context, int fd) { return 0; } + if (context->idle_time) { + 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"); + } + r = set_ensure_allocated(&context->connections, NULL); if (r < 0) { log_oom(); @@ -535,21 +589,28 @@ static int add_listen_socket(Context *context, int fd) { static int help(void) { _cleanup_free_ char *link = NULL; + _cleanup_free_ char *time_link = NULL; int r; r = terminal_urlify_man("systemd-socket-proxyd", "8", &link); if (r < 0) return log_oom(); + r = terminal_urlify_man("systemd.time", "7", &time_link); + if (r < 0) + return log_oom(); printf("%1$s [HOST:PORT]\n" "%1$s [SOCKET]\n\n" "Bidirectionally proxy local sockets to another (possibly remote) socket.\n\n" " -c --connections-max= Set the maximum number of connections to be accepted\n" + " --exit-idle-time= Exit when without a connection for this duration. See\n" + " the %3$s for time span format\n" " -h --help Show this help\n" " --version Show package version\n" "\nSee the %2$s for details.\n" , program_invocation_short_name , link + , time_link ); return 0; @@ -559,11 +620,13 @@ static int parse_argv(int argc, char *argv[]) { enum { ARG_VERSION = 0x100, + ARG_EXIT_IDLE, ARG_IGNORE_ENV }; static const struct option options[] = { { "connections-max", required_argument, NULL, 'c' }, + { "exit-idle-time", required_argument, NULL, ARG_EXIT_IDLE }, { "help", no_argument, NULL, 'h' }, { "version", no_argument, NULL, ARG_VERSION }, {} @@ -597,6 +660,12 @@ static int parse_argv(int argc, char *argv[]) { break; + case ARG_EXIT_IDLE: + r = parse_sec(optarg, &arg_exit_idle_time); + if (r < 0) + return log_error_errno(r, "Failed to parse --exit-idle-time= argument: %s", optarg); + break; + case '?': return -EINVAL; |