summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--man/systemd-socket-proxyd.xml10
-rw-r--r--src/socket-proxy/socket-proxyd.c81
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;