mirror of
https://github.com/systemd/systemd.git
synced 2024-11-28 12:53:36 +08:00
socket-proxy: Support exit-on-idle
This adds the --exit-idle-time argument that causes systemd-socket-proxyd to exit when there has been an idle period. An open connection prevents the idle period from starting, even if there is no activity on that connection. When combined with another service that uses StopWhenUnneeded=, the proxy exiting can trigger a resource-intensive process to exit. So although the proxy may consume minimal resources, significant resources can be saved indirectly. Fixes #2106
This commit is contained in:
parent
c28904dae0
commit
9e12d5bf63
@ -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>
|
||||
|
@ -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,9 +589,13 @@ 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();
|
||||
|
||||
@ -545,11 +603,14 @@ static int help(void) {
|
||||
"%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;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user