summaryrefslogtreecommitdiff
path: root/src/core/socket.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/core/socket.c')
-rw-r--r--src/core/socket.c62
1 files changed, 53 insertions, 9 deletions
diff --git a/src/core/socket.c b/src/core/socket.c
index 7eeed068bd..d4b409ef53 100644
--- a/src/core/socket.c
+++ b/src/core/socket.c
@@ -100,7 +100,8 @@ static void socket_init(Unit *u) {
s->control_command_id = _SOCKET_EXEC_COMMAND_INVALID;
- RATELIMIT_INIT(s->trigger_limit, 5*USEC_PER_SEC, 2500);
+ s->trigger_limit.interval = USEC_INFINITY;
+ s->trigger_limit.burst = (unsigned) -1;
}
static void socket_unwatch_control_pid(Socket *s) {
@@ -328,6 +329,25 @@ static int socket_add_extras(Socket *s) {
assert(s);
+ /* Pick defaults for the trigger limit, if nothing was explicitly configured. We pick a relatively high limit
+ * in Accept=yes mode, and a lower limit for Accept=no. Reason: in Accept=yes mode we are invoking accept()
+ * ourselves before the trigger limit can hit, thus incoming connections are taken off the socket queue quickly
+ * and reliably. This is different for Accept=no, where the spawned service has to take the incoming traffic
+ * off the queues, which it might not necessarily do. Moreover, while Accept=no services are supposed to
+ * process whatever is queued in one go, and thus should normally never have to be started frequently. This is
+ * different for Accept=yes where each connection is processed by a new service instance, and thus frequent
+ * service starts are typical. */
+
+ if (s->trigger_limit.interval == USEC_INFINITY)
+ s->trigger_limit.interval = 2 * USEC_PER_SEC;
+
+ if (s->trigger_limit.burst == (unsigned) -1) {
+ if (s->accept)
+ s->trigger_limit.burst = 200;
+ else
+ s->trigger_limit.burst = 20;
+ }
+
if (have_non_accept_socket(s)) {
if (!UNIT_DEREF(s->service)) {
@@ -620,8 +640,8 @@ static void socket_dump(Unit *u, FILE *f, const char *prefix) {
if (!isempty(s->user) || !isempty(s->group))
fprintf(f,
- "%sOwnerUser: %s\n"
- "%sOwnerGroup: %s\n",
+ "%sSocketUser: %s\n"
+ "%sSocketGroup: %s\n",
prefix, strna(s->user),
prefix, strna(s->group));
@@ -670,6 +690,12 @@ static void socket_dump(Unit *u, FILE *f, const char *prefix) {
fprintf(f, "%sListenFIFO: %s\n", prefix, p->path);
}
+ fprintf(f,
+ "%sTriggerLimitIntervalSec: %s\n"
+ "%sTriggerLimitBurst: %u\n",
+ prefix, format_timespan(time_string, FORMAT_TIMESPAN_MAX, s->trigger_limit.interval, USEC_PER_SEC),
+ prefix, s->trigger_limit.burst);
+
exec_context_dump(&s->exec_context, f, prefix);
kill_context_dump(&s->kill_context, f, prefix);
@@ -1271,11 +1297,13 @@ static int socket_open_fds(Socket *s) {
/* Apply the socket protocol */
switch(p->address.type) {
+
case SOCK_STREAM:
case SOCK_SEQPACKET:
if (p->socket->socket_protocol == IPPROTO_SCTP)
p->address.protocol = p->socket->socket_protocol;
break;
+
case SOCK_DGRAM:
if (p->socket->socket_protocol == IPPROTO_UDPLITE)
p->address.protocol = p->socket->socket_protocol;
@@ -1339,8 +1367,7 @@ static int socket_open_fds(Socket *s) {
}
break;
- case SOCKET_USB_FUNCTION:
- {
+ case SOCKET_USB_FUNCTION: {
_cleanup_free_ char *ep = NULL;
ep = path_make_absolute("ep0", p->path);
@@ -2057,6 +2084,7 @@ fail:
static int socket_start(Unit *u) {
Socket *s = SOCKET(u);
+ int r;
assert(s);
@@ -2101,6 +2129,12 @@ static int socket_start(Unit *u) {
assert(s->state == SOCKET_DEAD || s->state == SOCKET_FAILED);
+ r = unit_start_limit_test(u);
+ if (r < 0) {
+ socket_enter_dead(s, SOCKET_FAILURE_START_LIMIT_HIT);
+ return r;
+ }
+
s->result = SOCKET_SUCCESS;
s->reset_cpu_usage = true;
@@ -2735,17 +2769,26 @@ static void socket_trigger_notify(Unit *u, Unit *other) {
assert(u);
assert(other);
- /* Don't propagate state changes from the service if we are
- already down or accepting connections */
- if (!IN_SET(s->state, SOCKET_RUNNING, SOCKET_LISTENING) || s->accept)
+ /* Filter out invocations with bogus state */
+ if (other->load_state != UNIT_LOADED || other->type != UNIT_SERVICE)
+ return;
+
+ /* Don't propagate state changes from the service if we are already down */
+ if (!IN_SET(s->state, SOCKET_RUNNING, SOCKET_LISTENING))
return;
+ /* We don't care for the service state if we are in Accept=yes mode */
+ if (s->accept)
+ return;
+
+ /* Propagate start limit hit state */
if (other->start_limit_hit) {
socket_enter_stop_pre(s, SOCKET_FAILURE_SERVICE_START_LIMIT_HIT);
return;
}
- if (other->load_state != UNIT_LOADED || other->type != UNIT_SERVICE)
+ /* Don't propagate anything if there's still a job queued */
+ if (other->job)
return;
if (IN_SET(SERVICE(other)->state,
@@ -2818,6 +2861,7 @@ static const char* const socket_result_table[_SOCKET_RESULT_MAX] = {
[SOCKET_FAILURE_EXIT_CODE] = "exit-code",
[SOCKET_FAILURE_SIGNAL] = "signal",
[SOCKET_FAILURE_CORE_DUMP] = "core-dump",
+ [SOCKET_FAILURE_START_LIMIT_HIT] = "start-limit-hit",
[SOCKET_FAILURE_TRIGGER_LIMIT_HIT] = "trigger-limit-hit",
[SOCKET_FAILURE_SERVICE_START_LIMIT_HIT] = "service-start-limit-hit"
};