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;
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) {
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) {
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) {
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);
* 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)) {
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),
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;
}
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";
"%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,
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)
p->n_auxiliary_fds = 0;
clear:
- for (size_t i = 0; i < n; ++i)
- free(ent[i]);
-
+ free_many((void**) ent, n);
return r;
}
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
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)
return true;
}
- return exec_needs_network_namespace(context);
+ return exec_needs_network_namespace(&s->exec_context);
}
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);
* 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) {
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;
(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;
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,
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;
}
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,
.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;
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? */
_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) {
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) {
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)
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
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) {
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) {
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) {
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;
}
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) {
goto refuse;
}
- if (cfd < 0) {
+ if (cfd < 0) { /* Accept=no case */
bool pending = false;
Unit *other;
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;
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;
}
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++;
/* 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));
cfd >= 0 ? "template" : "non-template",
bus_error_message(&error, r));
+fail:
socket_enter_stop_pre(s, SOCKET_FAILURE_RESOURCES);
}
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) {
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));
(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));
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;
}
}
-_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);
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);
}
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);
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;
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;
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;
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) {
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;
* 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);
.start = socket_start,
.stop = socket_stop,
- .kill = socket_kill,
.clean = socket_clean,
.can_clean = socket_can_clean,