X-Git-Url: http://git.ipfire.org/?a=blobdiff_plain;f=src%2Fcore%2Fsocket.c;h=e5b2e8ae762ac2bddc8618323372b00609d6b508;hb=495e75ed5c8cce933947dae10a4a1b5f8067e432;hp=a6477de5a027d9a10d01e45de1e3056ba7ad6a27;hpb=1f74369c2eb32cf2ec5b39c25d73cead313705ca;p=thirdparty%2Fsystemd.git diff --git a/src/core/socket.c b/src/core/socket.c index a6477de5a02..e5b2e8ae762 100644 --- a/src/core/socket.c +++ b/src/core/socket.c @@ -26,7 +26,7 @@ #include "in-addr-util.h" #include "io-util.h" #include "ip-protocol-list.h" -#include "label.h" +#include "label-util.h" #include "log.h" #include "mkdir-label.h" #include "parse-util.h" @@ -82,8 +82,8 @@ static void socket_init(Unit *u) { assert(u); assert(u->load_state == UNIT_STUB); - s->backlog = SOMAXCONN; - s->timeout_usec = u->manager->default_timeout_start_usec; + s->backlog = SOMAXCONN_DELUXE; + s->timeout_usec = u->manager->defaults.timeout_start_usec; s->directory_mode = 0755; s->socket_mode = 0666; @@ -94,22 +94,27 @@ static void socket_init(Unit *u) { s->ip_ttl = -1; s->mark = -1; - s->exec_context.std_output = u->manager->default_std_output; - s->exec_context.std_error = u->manager->default_std_error; + s->exec_context.std_output = u->manager->defaults.std_output; + s->exec_context.std_error = u->manager->defaults.std_error; + s->control_pid = PIDREF_NULL; s->control_command_id = _SOCKET_EXEC_COMMAND_INVALID; s->trigger_limit.interval = USEC_INFINITY; s->trigger_limit.burst = UINT_MAX; + + s->poll_limit_interval = USEC_INFINITY; + s->poll_limit_burst = UINT_MAX; } static void socket_unwatch_control_pid(Socket *s) { assert(s); - if (s->control_pid <= 0) + if (!pidref_is_set(&s->control_pid)) return; - unit_unwatch_pid(UNIT(s), TAKE_PID(s->control_pid)); + unit_unwatch_pidref(UNIT(s), &s->control_pid); + pidref_done(&s->control_pid); } static void socket_cleanup_fd_list(SocketPort *p) { @@ -120,21 +125,23 @@ static void socket_cleanup_fd_list(SocketPort *p) { p->n_auxiliary_fds = 0; } -void socket_free_ports(Socket *s) { - SocketPort *p; +SocketPort *socket_port_free(SocketPort *p) { + if (!p) + return NULL; - assert(s); + sd_event_source_unref(p->event_source); - while ((p = s->ports)) { - LIST_REMOVE(port, s->ports, p); + socket_cleanup_fd_list(p); + safe_close(p->fd); + free(p->path); - sd_event_source_unref(p->event_source); + return mfree(p); +} - socket_cleanup_fd_list(p); - safe_close(p->fd); - free(p->path); - free(p); - } +void socket_free_ports(Socket *s) { + assert(s); + + LIST_CLEAR(port, s->ports, socket_port_free); } static void socket_done(Unit *u) { @@ -175,34 +182,10 @@ static void socket_done(Unit *u) { s->timer_event_source = sd_event_source_disable_unref(s->timer_event_source); } -static int socket_arm_timer(Socket *s, usec_t usec) { - int r; - +static int socket_arm_timer(Socket *s, bool relative, usec_t usec) { assert(s); - if (s->timer_event_source) { - r = sd_event_source_set_time(s->timer_event_source, usec); - if (r < 0) - return r; - - return sd_event_source_set_enabled(s->timer_event_source, SD_EVENT_ONESHOT); - } - - if (usec == USEC_INFINITY) - return 0; - - r = sd_event_add_time( - UNIT(s)->manager->event, - &s->timer_event_source, - CLOCK_MONOTONIC, - usec, 0, - socket_dispatch_timer, s); - if (r < 0) - return r; - - (void) sd_event_source_set_description(s->timer_event_source, "socket-timer"); - - return 0; + return unit_arm_timer(UNIT(s), &s->timer_event_source, relative, usec, socket_dispatch_timer); } static bool have_non_accept_socket(Socket *s) { @@ -276,14 +259,10 @@ static int socket_add_default_dependencies(Socket *s) { return r; } - r = unit_add_two_dependencies_by_name(UNIT(s), UNIT_BEFORE, UNIT_CONFLICTS, SPECIAL_SHUTDOWN_TARGET, true, UNIT_DEPENDENCY_DEFAULT); - if (r < 0) - return r; - - return exec_context_add_default_dependencies(UNIT(s), &s->exec_context); + return unit_add_two_dependencies_by_name(UNIT(s), UNIT_BEFORE, UNIT_CONFLICTS, SPECIAL_SHUTDOWN_TARGET, true, UNIT_DEPENDENCY_DEFAULT); } -_pure_ static bool socket_has_exec(Socket *s) { +static bool socket_has_exec(Socket *s) { unsigned i; assert(s); @@ -307,17 +286,20 @@ static int socket_add_extras(Socket *s) { * 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. */ + * service starts are typical. + * + * For the poll limit we follow a similar rule, but use 3/4th of the trigger limit parameters, to + * trigger this earlier. */ if (s->trigger_limit.interval == USEC_INFINITY) s->trigger_limit.interval = 2 * USEC_PER_SEC; + if (s->trigger_limit.burst == UINT_MAX) + s->trigger_limit.burst = s->accept ? 200 : 20; - if (s->trigger_limit.burst == UINT_MAX) { - if (s->accept) - s->trigger_limit.burst = 200; - else - s->trigger_limit.burst = 20; - } + if (s->poll_limit_interval == USEC_INFINITY) + s->poll_limit_interval = 2 * USEC_PER_SEC; + if (s->poll_limit_burst == UINT_MAX) + s->poll_limit_burst = s->accept ? 150 : 15; if (have_non_accept_socket(s)) { @@ -500,7 +482,7 @@ static SocketPeer *socket_peer_free(SocketPeer *p) { DEFINE_TRIVIAL_REF_UNREF_FUNC(SocketPeer, socket_peer, socket_peer_free); -int socket_acquire_peer(Socket *s, int fd, SocketPeer **p) { +int socket_acquire_peer(Socket *s, int fd, SocketPeer **ret) { _cleanup_(socket_peer_unrefp) SocketPeer *remote = NULL; SocketPeer sa = { .peer_salen = sizeof(union sockaddr_union), @@ -509,22 +491,19 @@ int socket_acquire_peer(Socket *s, int fd, SocketPeer **p) { assert(fd >= 0); assert(s); + assert(ret); if (getpeername(fd, &sa.peer.sa, &sa.peer_salen) < 0) - return log_unit_error_errno(UNIT(s), errno, "getpeername failed: %m"); + return log_unit_error_errno(UNIT(s), errno, "getpeername() failed: %m"); if (!IN_SET(sa.peer.sa.sa_family, AF_INET, AF_INET6, AF_VSOCK)) { - *p = NULL; + *ret = NULL; return 0; } - r = set_ensure_allocated(&s->peers_by_address, &peer_address_hash_ops); - if (r < 0) - return r; - i = set_get(s->peers_by_address, &sa); if (i) { - *p = socket_peer_ref(i); + *ret = socket_peer_ref(i); return 1; } @@ -535,17 +514,17 @@ int socket_acquire_peer(Socket *s, int fd, SocketPeer **p) { remote->peer = sa.peer; remote->peer_salen = sa.peer_salen; - r = set_put(s->peers_by_address, remote); + r = set_ensure_put(&s->peers_by_address, &peer_address_hash_ops, remote); if (r < 0) - return r; + return log_unit_error_errno(UNIT(s), r, "Failed to insert peer info into hash table: %m"); remote->socket = s; - *p = TAKE_PTR(remote); + *ret = TAKE_PTR(remote); return 1; } -_const_ static const char* listen_lookup(int family, int type) { +static const char* listen_lookup(int family, int type) { if (family == AF_NETLINK) return "ListenNetlink"; @@ -618,10 +597,10 @@ static void socket_dump(Unit *u, FILE *f, const char *prefix) { "%sTimestamping: %s\n", prefix, socket_timestamping_to_string(s->timestamping)); - if (s->control_pid > 0) + if (pidref_is_set(&s->control_pid)) fprintf(f, "%sControl PID: "PID_FMT"\n", - prefix, s->control_pid); + prefix, s->control_pid.pid); if (s->bind_to_device) fprintf(f, @@ -767,9 +746,13 @@ static void socket_dump(Unit *u, FILE *f, const char *prefix) { fprintf(f, "%sTriggerLimitIntervalSec: %s\n" - "%sTriggerLimitBurst: %u\n", + "%sTriggerLimitBurst: %u\n" + "%sPollLimitIntervalSec: %s\n" + "%sPollLimitBurst: %u\n", prefix, FORMAT_TIMESPAN(s->trigger_limit.interval, USEC_PER_SEC), - prefix, s->trigger_limit.burst); + prefix, s->trigger_limit.burst, + prefix, FORMAT_TIMESPAN(s->poll_limit_interval, USEC_PER_SEC), + prefix, s->poll_limit_burst); str = ip_protocol_to_name(s->socket_protocol); if (str) @@ -1352,9 +1335,7 @@ fail: p->n_auxiliary_fds = 0; clear: - for (size_t i = 0; i < n; ++i) - free(ent[i]); - + free_many((void**) ent, n); return r; } @@ -1383,14 +1364,15 @@ int socket_load_service_unit(Socket *s, int cfd, Unit **ret) { if (cfd >= 0) { r = instance_from_socket(cfd, s->n_accepted, &instance); - if (ERRNO_IS_DISCONNECT(r)) - /* ENOTCONN is legitimate if TCP RST was received. Other socket families might return - * different errors. This connection is over, but the socket unit lives on. */ - return log_unit_debug_errno(UNIT(s), r, - "Got %s on incoming socket, assuming aborted connection attempt, ignoring.", - errno_to_name(r)); - if (r < 0) + if (r < 0) { + if (ERRNO_IS_DISCONNECT(r)) + /* ENOTCONN is legitimate if TCP RST was received. Other socket families might return + * different errors. This connection is over, but the socket unit lives on. */ + return log_unit_debug_errno(UNIT(s), r, + "Got %s on incoming socket, assuming aborted connection attempt, ignoring.", + errno_to_name(r)); return r; + } } /* For accepting sockets, we don't know how the instance will be called until we get a connection and @@ -1480,14 +1462,22 @@ static int socket_address_listen_do( log_unit_error_errno(u, error, fmt, strna(_t)); \ }) -static int fork_needed(const SocketAddress *address, const ExecContext *context) { +static int fork_needed(const SocketAddress *address, Socket *s) { int r; assert(address); - assert(context); + assert(s); /* Check if we need to do the cgroup or netns stuff. If not we can do things much simpler. */ + /* If there are any NFTSet= directives with cgroup source, we need the cgroup */ + Unit *u = UNIT(s); + CGroupContext *c = unit_get_cgroup_context(u); + if (c) + FOREACH_ARRAY(nft_set, c->nft_set_context.sets, c->nft_set_context.n_sets) + if (nft_set->source == NFT_SET_SOURCE_CGROUP) + return true; + if (IN_SET(address->sockaddr.sa.sa_family, AF_INET, AF_INET6)) { r = bpf_firewall_supported(); if (r < 0) @@ -1496,7 +1486,7 @@ static int fork_needed(const SocketAddress *address, const ExecContext *context) return true; } - return exec_needs_network_namespace(context); + return exec_needs_network_namespace(&s->exec_context); } static int socket_address_listen_in_cgroup( @@ -1504,9 +1494,9 @@ static int socket_address_listen_in_cgroup( const SocketAddress *address, const char *label) { + _cleanup_(pidref_done) PidRef pid = PIDREF_NULL; _cleanup_close_pair_ int pair[2] = PIPE_EBADF; int fd, r; - pid_t pid; assert(s); assert(address); @@ -1516,7 +1506,7 @@ static int socket_address_listen_in_cgroup( * the socket is actually properly attached to the unit's cgroup for the purpose of BPF filtering and * such. */ - r = fork_needed(address, &s->exec_context); + r = fork_needed(address, s); if (r < 0) return r; if (r == 0) { @@ -1598,7 +1588,7 @@ static int socket_address_listen_in_cgroup( fd = receive_one_fd(pair[0], 0); /* We synchronously wait for the helper, as it shouldn't be slow */ - r = wait_for_terminate_and_check("(sd-listen)", pid, WAIT_LOG_ABNORMAL); + r = wait_for_terminate_and_check("(sd-listen)", pid.pid, WAIT_LOG_ABNORMAL); if (r < 0) { safe_close(fd); return r; @@ -1762,6 +1752,10 @@ static int socket_watch_fds(Socket *s) { (void) sd_event_source_set_description(p->event_source, "socket-port-io"); } + + r = sd_event_source_set_ratelimit(p->event_source, s->poll_limit_interval, s->poll_limit_burst); + if (r < 0) + log_unit_debug_errno(UNIT(s), r, "Failed to set poll limit on I/O event source, ignoring: %m"); } return 0; @@ -1857,8 +1851,8 @@ static int socket_coldplug(Unit *u) { if (s->deserialized_state == s->state) return 0; - if (s->control_pid > 0 && - pid_is_unwaited(s->control_pid) && + if (pidref_is_set(&s->control_pid) && + pid_is_unwaited(s->control_pid.pid) && IN_SET(s->deserialized_state, SOCKET_START_PRE, SOCKET_START_CHOWN, @@ -1871,11 +1865,11 @@ static int socket_coldplug(Unit *u) { SOCKET_FINAL_SIGKILL, SOCKET_CLEANING)) { - r = unit_watch_pid(UNIT(s), s->control_pid, false); + r = unit_watch_pidref(UNIT(s), &s->control_pid, /* exclusive= */ false); if (r < 0) return r; - r = socket_arm_timer(s, usec_add(u->state_change_timestamp.monotonic, s->timeout_usec)); + r = socket_arm_timer(s, /* relative= */ false, usec_add(u->state_change_timestamp.monotonic, s->timeout_usec)); if (r < 0) return r; } @@ -1917,7 +1911,7 @@ static int socket_coldplug(Unit *u) { return 0; } -static int socket_spawn(Socket *s, ExecCommand *c, pid_t *_pid) { +static int socket_spawn(Socket *s, ExecCommand *c, PidRef *ret_pid) { _cleanup_(exec_params_clear) ExecParameters exec_params = { .flags = EXEC_APPLY_SANDBOXING|EXEC_APPLY_CHROOT|EXEC_APPLY_TTY_STDIN, @@ -1926,18 +1920,19 @@ static int socket_spawn(Socket *s, ExecCommand *c, pid_t *_pid) { .stderr_fd = -EBADF, .exec_fd = -EBADF, }; + _cleanup_(pidref_done) PidRef pidref = PIDREF_NULL; pid_t pid; int r; assert(s); assert(c); - assert(_pid); + assert(ret_pid); r = unit_prepare_exec(UNIT(s)); if (r < 0) return r; - r = socket_arm_timer(s, usec_add(now(CLOCK_MONOTONIC), s->timeout_usec)); + r = socket_arm_timer(s, /* relative= */ true, s->timeout_usec); if (r < 0) return r; @@ -1955,22 +1950,27 @@ static int socket_spawn(Socket *s, ExecCommand *c, pid_t *_pid) { if (r < 0) return r; - r = unit_watch_pid(UNIT(s), pid, true); + r = pidref_set_pid(&pidref, pid); if (r < 0) return r; - *_pid = pid; + r = unit_watch_pidref(UNIT(s), &pidref, /* exclusive= */ true); + if (r < 0) + return r; + *ret_pid = TAKE_PIDREF(pidref); return 0; } -static int socket_chown(Socket *s, pid_t *_pid) { - pid_t pid; +static int socket_chown(Socket *s, PidRef *ret_pid) { + _cleanup_(pidref_done) PidRef pid = PIDREF_NULL; int r; - r = socket_arm_timer(s, usec_add(now(CLOCK_MONOTONIC), s->timeout_usec)); + assert(s); + + r = socket_arm_timer(s, /* relative= */ true, s->timeout_usec); if (r < 0) - goto fail; + return r; /* We have to resolve the user names out-of-process, hence * let's fork here. It's messy, but well, what can we do? */ @@ -2024,16 +2024,12 @@ static int socket_chown(Socket *s, pid_t *_pid) { _exit(EXIT_SUCCESS); } - r = unit_watch_pid(UNIT(s), pid, true); + r = unit_watch_pidref(UNIT(s), &pid, /* exclusive= */ true); if (r < 0) - goto fail; + return r; - *_pid = pid; + *ret_pid = TAKE_PIDREF(pid); return 0; - -fail: - s->timer_event_source = sd_event_source_disable_unref(s->timer_event_source); - return r; } static void socket_enter_dead(Socket *s, SocketResult f) { @@ -2072,19 +2068,18 @@ static void socket_enter_stop_post(Socket *s, SocketResult f) { s->control_command = s->exec_command[SOCKET_EXEC_STOP_POST]; if (s->control_command) { + pidref_done(&s->control_pid); + r = socket_spawn(s, s->control_command, &s->control_pid); - if (r < 0) - goto fail; + if (r < 0) { + log_unit_warning_errno(UNIT(s), r, "Failed to spawn 'stop-post' task: %m"); + socket_enter_signal(s, SOCKET_FINAL_SIGTERM, SOCKET_FAILURE_RESOURCES); + return; + } socket_set_state(s, SOCKET_STOP_POST); } else socket_enter_signal(s, SOCKET_FINAL_SIGTERM, SOCKET_SUCCESS); - - return; - -fail: - log_unit_warning_errno(UNIT(s), r, "Failed to run 'stop-post' task: %m"); - socket_enter_signal(s, SOCKET_FINAL_SIGTERM, SOCKET_FAILURE_RESOURCES); } static int state_to_kill_operation(Socket *s, SocketState state) { @@ -2109,16 +2104,20 @@ static void socket_enter_signal(Socket *s, SocketState state, SocketResult f) { UNIT(s), &s->kill_context, state_to_kill_operation(s, state), - -1, - s->control_pid, - false); - if (r < 0) + /* main_pid= */ NULL, + &s->control_pid, + /* main_pid_alien= */ false); + if (r < 0) { + log_unit_warning_errno(UNIT(s), r, "Failed to kill processes: %m"); goto fail; + } if (r > 0) { - r = socket_arm_timer(s, usec_add(now(CLOCK_MONOTONIC), s->timeout_usec)); - if (r < 0) + r = socket_arm_timer(s, /* relative= */ true, s->timeout_usec); + if (r < 0) { + log_unit_warning_errno(UNIT(s), r, "Failed to install timer: %m"); goto fail; + } socket_set_state(s, state); } else if (state == SOCKET_STOP_PRE_SIGTERM) @@ -2133,8 +2132,6 @@ static void socket_enter_signal(Socket *s, SocketState state, SocketResult f) { return; fail: - log_unit_warning_errno(UNIT(s), r, "Failed to kill processes: %m"); - if (IN_SET(state, SOCKET_STOP_PRE_SIGTERM, SOCKET_STOP_PRE_SIGKILL)) socket_enter_stop_post(s, SOCKET_FAILURE_RESOURCES); else @@ -2153,19 +2150,18 @@ static void socket_enter_stop_pre(Socket *s, SocketResult f) { s->control_command = s->exec_command[SOCKET_EXEC_STOP_PRE]; if (s->control_command) { + pidref_done(&s->control_pid); + r = socket_spawn(s, s->control_command, &s->control_pid); - if (r < 0) - goto fail; + if (r < 0) { + log_unit_warning_errno(UNIT(s), r, "Failed to spawn 'stop-pre' task: %m"); + socket_enter_stop_post(s, SOCKET_FAILURE_RESOURCES); + return; + } socket_set_state(s, SOCKET_STOP_PRE); } else socket_enter_stop_post(s, SOCKET_SUCCESS); - - return; - -fail: - log_unit_warning_errno(UNIT(s), r, "Failed to run 'stop-pre' task: %m"); - socket_enter_stop_post(s, SOCKET_FAILURE_RESOURCES); } static void socket_enter_listening(Socket *s) { @@ -2180,14 +2176,11 @@ static void socket_enter_listening(Socket *s) { r = socket_watch_fds(s); if (r < 0) { log_unit_warning_errno(UNIT(s), r, "Failed to watch sockets: %m"); - goto fail; + socket_enter_stop_pre(s, SOCKET_FAILURE_RESOURCES); + return; } socket_set_state(s, SOCKET_LISTENING); - return; - -fail: - socket_enter_stop_pre(s, SOCKET_FAILURE_RESOURCES); } static void socket_enter_start_post(Socket *s) { @@ -2199,20 +2192,18 @@ static void socket_enter_start_post(Socket *s) { s->control_command = s->exec_command[SOCKET_EXEC_START_POST]; if (s->control_command) { + pidref_done(&s->control_pid); + r = socket_spawn(s, s->control_command, &s->control_pid); if (r < 0) { - log_unit_warning_errno(UNIT(s), r, "Failed to run 'start-post' task: %m"); - goto fail; + log_unit_warning_errno(UNIT(s), r, "Failed to spawn 'start-post' task: %m"); + socket_enter_stop_pre(s, SOCKET_FAILURE_RESOURCES); + return; } socket_set_state(s, SOCKET_START_POST); } else socket_enter_listening(s); - - return; - -fail: - socket_enter_stop_pre(s, SOCKET_FAILURE_RESOURCES); } static void socket_enter_start_chown(Socket *s) { @@ -2234,7 +2225,7 @@ static void socket_enter_start_chown(Socket *s) { r = socket_chown(s, &s->control_pid); if (r < 0) { - log_unit_warning_errno(UNIT(s), r, "Failed to fork 'start-chown' task: %m"); + log_unit_warning_errno(UNIT(s), r, "Failed to spawn 'start-chown' task: %m"); goto fail; } @@ -2260,20 +2251,18 @@ static void socket_enter_start_pre(Socket *s) { s->control_command = s->exec_command[SOCKET_EXEC_START_PRE]; if (s->control_command) { + pidref_done(&s->control_pid); + r = socket_spawn(s, s->control_command, &s->control_pid); if (r < 0) { - log_unit_warning_errno(UNIT(s), r, "Failed to run 'start-pre' task: %m"); - goto fail; + log_unit_warning_errno(UNIT(s), r, "Failed to spawn 'start-pre' task: %m"); + socket_enter_dead(s, SOCKET_FAILURE_RESOURCES); + return; } socket_set_state(s, SOCKET_START_PRE); } else socket_enter_start_chown(s); - - return; - -fail: - socket_enter_dead(s, SOCKET_FAILURE_RESOURCES); } static void flush_ports(Socket *s) { @@ -2319,7 +2308,7 @@ static void socket_enter_running(Socket *s, int cfd_in) { goto refuse; } - if (cfd < 0) { + if (cfd < 0) { /* Accept=no case */ bool pending = false; Unit *other; @@ -2332,18 +2321,18 @@ static void socket_enter_running(Socket *s, int cfd_in) { if (!pending) { if (!UNIT_ISSET(s->service)) { - r = log_unit_error_errno(UNIT(s), SYNTHETIC_ERRNO(ENOENT), - "Service to activate vanished, refusing activation."); + r = log_unit_warning_errno(UNIT(s), SYNTHETIC_ERRNO(ENOENT), + "Service to activate vanished, refusing activation."); goto fail; } r = manager_add_job(UNIT(s)->manager, JOB_START, UNIT_DEREF(s->service), JOB_REPLACE, NULL, &error, NULL); if (r < 0) - goto fail; + goto queue_error; } socket_set_state(s, SOCKET_RUNNING); - } else { + } else { /* Accept=yes case */ _cleanup_(socket_peer_unrefp) SocketPeer *p = NULL; Unit *service; @@ -2355,10 +2344,12 @@ static void socket_enter_running(Socket *s, int cfd_in) { if (s->max_connections_per_source > 0) { r = socket_acquire_peer(s, cfd, &p); - if (ERRNO_IS_DISCONNECT(r)) - return; - if (r < 0) /* We didn't have enough resources to acquire peer information, let's fail. */ + if (r < 0) { + if (ERRNO_IS_DISCONNECT(r)) + return; + /* We didn't have enough resources to acquire peer information, let's fail. */ goto fail; + } if (r > 0 && p->n_ref > s->max_connections_per_source) { _cleanup_free_ char *t = NULL; @@ -2372,23 +2363,31 @@ static void socket_enter_running(Socket *s, int cfd_in) { } r = socket_load_service_unit(s, cfd, &service); - if (ERRNO_IS_DISCONNECT(r)) - return; - if (r < 0) + if (r < 0) { + if (ERRNO_IS_DISCONNECT(r)) + return; + + log_unit_warning_errno(UNIT(s), r, "Failed to load connection service unit: %m"); goto fail; + } r = unit_add_two_dependencies(UNIT(s), UNIT_BEFORE, UNIT_TRIGGERS, service, false, UNIT_DEPENDENCY_IMPLICIT); - if (r < 0) + if (r < 0) { + log_unit_warning_errno(UNIT(s), r, "Failed to add Before=/Triggers= dependencies on connection unit: %m"); goto fail; + } s->n_accepted++; r = service_set_socket_fd(SERVICE(service), cfd, s, p, s->selinux_context_from_net); - if (ERRNO_IS_DISCONNECT(r)) - return; - if (r < 0) + if (r < 0) { + if (ERRNO_IS_DISCONNECT(r)) + return; + + log_unit_warning_errno(UNIT(s), r, "Failed to set socket on service: %m"); goto fail; + } TAKE_FD(cfd); /* We passed ownership of the fd to the service now. Forget it here. */ s->n_connections++; @@ -2398,21 +2397,20 @@ static void socket_enter_running(Socket *s, int cfd_in) { /* We failed to activate the new service, but it still exists. Let's make sure the * service closes and forgets the connection fd again, immediately. */ service_release_socket_fd(SERVICE(service)); - goto fail; + goto queue_error; } /* Notify clients about changed counters */ unit_add_to_dbus_queue(UNIT(s)); } - TAKE_FD(cfd); return; refuse: s->n_refused++; return; -fail: +queue_error: if (ERRNO_IS_RESOURCE(r)) log_unit_warning(UNIT(s), "Failed to queue service startup job: %s", bus_error_message(&error, r)); @@ -2421,6 +2419,7 @@ fail: cfd >= 0 ? "template" : "non-template", bus_error_message(&error, r)); +fail: socket_enter_stop_pre(s, SOCKET_FAILURE_RESOURCES); } @@ -2435,21 +2434,19 @@ static void socket_run_next(Socket *s) { s->control_command = s->control_command->command_next; - r = socket_spawn(s, s->control_command, &s->control_pid); - if (r < 0) - goto fail; - - return; + pidref_done(&s->control_pid); -fail: - log_unit_warning_errno(UNIT(s), r, "Failed to run next task: %m"); + r = socket_spawn(s, s->control_command, &s->control_pid); + if (r < 0) { + log_unit_warning_errno(UNIT(s), r, "Failed to spawn next task: %m"); - if (s->state == SOCKET_START_POST) - socket_enter_stop_pre(s, SOCKET_FAILURE_RESOURCES); - else if (s->state == SOCKET_STOP_POST) - socket_enter_dead(s, SOCKET_FAILURE_RESOURCES); - else - socket_enter_signal(s, SOCKET_FINAL_SIGTERM, SOCKET_FAILURE_RESOURCES); + if (s->state == SOCKET_START_POST) + socket_enter_stop_pre(s, SOCKET_FAILURE_RESOURCES); + else if (s->state == SOCKET_STOP_POST) + socket_enter_dead(s, SOCKET_FAILURE_RESOURCES); + else + socket_enter_signal(s, SOCKET_FINAL_SIGTERM, SOCKET_FAILURE_RESOURCES); + } } static int socket_start(Unit *u) { @@ -2484,12 +2481,16 @@ static int socket_start(Unit *u) { service = SERVICE(UNIT_DEREF(s->service)); if (UNIT(service)->load_state != UNIT_LOADED) - return log_unit_error_errno(u, SYNTHETIC_ERRNO(ENOENT), "Socket service %s not loaded, refusing.", UNIT(service)->id); + return log_unit_error_errno(u, SYNTHETIC_ERRNO(ENOENT), + "Socket service %s not loaded, refusing.", UNIT(service)->id); /* If the service is already active we cannot start the * socket */ - if (!IN_SET(service->state, SERVICE_DEAD, SERVICE_FAILED, SERVICE_DEAD_BEFORE_AUTO_RESTART, SERVICE_FAILED_BEFORE_AUTO_RESTART, SERVICE_AUTO_RESTART)) - return log_unit_error_errno(u, SYNTHETIC_ERRNO(EBUSY), "Socket service %s already active, refusing.", UNIT(service)->id); + if (!IN_SET(service->state, + SERVICE_DEAD, SERVICE_DEAD_BEFORE_AUTO_RESTART, SERVICE_FAILED, SERVICE_FAILED_BEFORE_AUTO_RESTART, + SERVICE_AUTO_RESTART, SERVICE_AUTO_RESTART_QUEUED)) + return log_unit_error_errno(u, SYNTHETIC_ERRNO(EBUSY), + "Socket service %s already active, refusing.", UNIT(service)->id); } assert(IN_SET(s->state, SOCKET_DEAD, SOCKET_FAILED)); @@ -2557,8 +2558,8 @@ static int socket_serialize(Unit *u, FILE *f, FDSet *fds) { (void) serialize_item_format(f, "n-accepted", "%u", s->n_accepted); (void) serialize_item_format(f, "n-refused", "%u", s->n_refused); - if (s->control_pid > 0) - (void) serialize_item_format(f, "control-pid", PID_FMT, s->control_pid); + if (pidref_is_set(&s->control_pid)) + (void) serialize_item_format(f, "control-pid", PID_FMT, s->control_pid.pid); if (s->control_command_id >= 0) (void) serialize_item(f, "control-command", socket_exec_command_to_string(s->control_command_id)); @@ -2639,12 +2640,10 @@ static int socket_deserialize_item(Unit *u, const char *key, const char *value, else s->n_refused += k; } else if (streq(key, "control-pid")) { - pid_t pid; - - if (parse_pid(value, &pid) < 0) - log_unit_debug(u, "Failed to parse control-pid value: %s", value); - else - s->control_pid = pid; + pidref_done(&s->control_pid); + r = pidref_set_pidstr(&s->control_pid, value); + if (r < 0) + log_debug_errno(r, "Failed to pin control PID '%s', ignoring: %m", value); } else if (streq(key, "control-command")) { SocketExecCommand id; @@ -2863,13 +2862,13 @@ static void socket_distribute_fds(Unit *u, FDSet *fds) { } } -_pure_ static UnitActiveState socket_active_state(Unit *u) { +static UnitActiveState socket_active_state(Unit *u) { assert(u); return state_translation_table[SOCKET(u)->state]; } -_pure_ static const char *socket_sub_state_to_string(Unit *u) { +static const char *socket_sub_state_to_string(Unit *u) { assert(u); return socket_state_to_string(SOCKET(u)->state); @@ -2937,7 +2936,7 @@ SocketType socket_port_type_from_string(const char *s) { return _SOCKET_TYPE_INVALID; } -_pure_ static bool socket_may_gc(Unit *u) { +static bool socket_may_gc(Unit *u) { Socket *s = SOCKET(u); assert(u); @@ -2960,9 +2959,9 @@ static int socket_accept_do(Socket *s, int fd) { } static int socket_accept_in_cgroup(Socket *s, SocketPort *p, int fd) { + _cleanup_(pidref_done) PidRef pid = PIDREF_NULL; _cleanup_close_pair_ int pair[2] = PIPE_EBADF; int cfd, r; - pid_t pid; assert(s); assert(p); @@ -3012,7 +3011,7 @@ static int socket_accept_in_cgroup(Socket *s, SocketPort *p, int fd) { cfd = receive_one_fd(pair[0], 0); /* We synchronously wait for the helper, as it shouldn't be slow */ - r = wait_for_terminate_and_check("(sd-accept)", pid, WAIT_LOG_ABNORMAL); + r = wait_for_terminate_and_check("(sd-accept)", pid.pid, WAIT_LOG_ABNORMAL); if (r < 0) { safe_close(cfd); return r; @@ -3084,10 +3083,10 @@ static void socket_sigchld_event(Unit *u, pid_t pid, int code, int status) { assert(s); assert(pid >= 0); - if (pid != s->control_pid) + if (pid != s->control_pid.pid) return; - s->control_pid = 0; + pidref_done(&s->control_pid); if (is_clean_exit(code, status, EXIT_CLEAN_COMMAND, NULL)) f = SOCKET_SUCCESS; @@ -3353,17 +3352,13 @@ static void socket_trigger_notify(Unit *u, Unit *other) { if (IN_SET(SERVICE(other)->state, SERVICE_DEAD, SERVICE_DEAD_BEFORE_AUTO_RESTART, SERVICE_FAILED, SERVICE_FAILED_BEFORE_AUTO_RESTART, SERVICE_FINAL_SIGTERM, SERVICE_FINAL_SIGKILL, - SERVICE_AUTO_RESTART)) + SERVICE_AUTO_RESTART, SERVICE_AUTO_RESTART_QUEUED)) socket_enter_listening(s); if (SERVICE(other)->state == SERVICE_RUNNING) socket_set_state(s, SOCKET_RUNNING); } -static int socket_kill(Unit *u, KillWho who, int signo, int code, int value, sd_bus_error *error) { - return unit_kill_common(u, who, signo, code, value, -1, SOCKET(u)->control_pid, error); -} - static int socket_get_timeout(Unit *u, usec_t *timeout) { Socket *s = SOCKET(u); usec_t t; @@ -3392,12 +3387,8 @@ char *socket_fdname(Socket *s) { return s->fdname ?: UNIT(s)->id; } -static int socket_control_pid(Unit *u) { - Socket *s = SOCKET(u); - - assert(s); - - return s->control_pid; +static PidRef *socket_control_pid(Unit *u) { + return &ASSERT_PTR(SOCKET(u))->control_pid; } static int socket_clean(Unit *u, ExecCleanMask mask) { @@ -3423,20 +3414,22 @@ static int socket_clean(Unit *u, ExecCleanMask mask) { s->control_command = NULL; s->control_command_id = _SOCKET_EXEC_COMMAND_INVALID; - r = socket_arm_timer(s, usec_add(now(CLOCK_MONOTONIC), s->exec_context.timeout_clean_usec)); - if (r < 0) + r = socket_arm_timer(s, /* relative= */ true, s->exec_context.timeout_clean_usec); + if (r < 0) { + log_unit_warning_errno(u, r, "Failed to install timer: %m"); goto fail; + } r = unit_fork_and_watch_rm_rf(u, l, &s->control_pid); - if (r < 0) + if (r < 0) { + log_unit_warning_errno(u, r, "Failed to spawn cleaning task: %m"); goto fail; + } socket_set_state(s, SOCKET_CLEANING); - return 0; fail: - log_unit_warning_errno(u, r, "Failed to initiate cleaning: %m"); s->clean_result = SOCKET_FAILURE_RESOURCES; s->timer_event_source = sd_event_source_disable_unref(s->timer_event_source); return r; @@ -3512,7 +3505,7 @@ SocketTimestamping socket_timestamping_from_string_harder(const char *p) { * too. */ if (streq(p, "nsec")) return SOCKET_TIMESTAMPING_NS; - if (STR_IN_SET(p, "usec", "µs")) + if (STR_IN_SET(p, "usec", "µs", "μs")) /* Accept both small greek letter mu + micro sign unicode codepoints */ return SOCKET_TIMESTAMPING_US; r = parse_boolean(p); @@ -3550,7 +3543,6 @@ const UnitVTable socket_vtable = { .start = socket_start, .stop = socket_stop, - .kill = socket_kill, .clean = socket_clean, .can_clean = socket_can_clean,