/* SPDX-License-Identifier: LGPL-2.1+ */
-/***
- This file is part of systemd.
-
- Copyright 2010 Lennart Poettering
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
#include <arpa/inet.h>
#include <errno.h>
s->peers_by_address = set_free(s->peers_by_address);
- s->exec_runtime = exec_runtime_unref(s->exec_runtime);
+ s->exec_runtime = exec_runtime_unref(s->exec_runtime, false);
exec_command_free_array(s->exec_command, _SOCKET_EXEC_COMMAND_MAX);
s->control_command = NULL;
if (r < 0)
return r;
- unit_ref_set(&s->service, u);
+ unit_ref_set(&s->service, UNIT(s), u);
return unit_add_two_dependencies(UNIT(s), UNIT_BEFORE, UNIT_TRIGGERS, u, false, UNIT_DEPENDENCY_IMPLICIT);
}
if (r < 0)
return r;
- unit_ref_set(&s->service, x);
+ unit_ref_set(&s->service, u, x);
}
r = unit_add_two_dependencies(u, UNIT_BEFORE, UNIT_TRIGGERS, UNIT_DEREF(s->service), true, UNIT_DEPENDENCY_IMPLICIT);
if (!s->ports) {
log_unit_error(UNIT(s), "Unit has no Listen setting (ListenStream=, ListenDatagram=, ListenFIFO=, ...). Refusing.");
- return -EINVAL;
+ return -ENOEXEC;
}
if (s->accept && have_non_accept_socket(s)) {
log_unit_error(UNIT(s), "Unit configured for accepting sockets, but sockets are non-accepting. Refusing.");
- return -EINVAL;
+ return -ENOEXEC;
}
if (s->accept && s->max_connections <= 0) {
log_unit_error(UNIT(s), "MaxConnection= setting too small. Refusing.");
- return -EINVAL;
+ return -ENOEXEC;
}
if (s->accept && UNIT_DEREF(s->service)) {
log_unit_error(UNIT(s), "Explicit service configuration for accepting socket units not supported. Refusing.");
- return -EINVAL;
+ return -ENOEXEC;
}
if (s->exec_context.pam_name && s->kill_context.kill_mode != KILL_CONTROL_GROUP) {
log_unit_error(UNIT(s), "Unit has PAM enabled. Kill mode must be set to 'control-group'. Refusing.");
- return -EINVAL;
+ return -ENOEXEC;
}
if (!strv_isempty(s->symlinks) && !socket_find_symlink_target(s)) {
log_unit_error(UNIT(s), "Unit has symlinks set but none or more than one node in the file system. Refusing.");
- return -EINVAL;
+ return -ENOEXEC;
}
return 0;
remote->socket = s;
- *p = remote;
- remote = NULL;
+ *p = TAKE_PTR(remote);
return 1;
}
"%sKeepAliveTimeSec: %s\n",
prefix, format_timespan(time_string, FORMAT_TIMESPAN_MAX, s->keep_alive_time, USEC_PER_SEC));
- if (s->keep_alive_interval)
+ if (s->keep_alive_interval > 0)
fprintf(f,
"%sKeepAliveIntervalSec: %s\n",
prefix, format_timespan(time_string, FORMAT_TIMESPAN_MAX, s->keep_alive_interval, USEC_PER_SEC));
- if (s->keep_alive_cnt)
+ if (s->keep_alive_cnt > 0)
fprintf(f,
"%sKeepAliveProbes: %u\n",
prefix, s->keep_alive_cnt);
- if (s->defer_accept)
+ if (s->defer_accept > 0)
fprintf(f,
"%sDeferAcceptSec: %s\n",
prefix, format_timespan(time_string, FORMAT_TIMESPAN_MAX, s->defer_accept, USEC_PER_SEC));
LIST_FOREACH(port, p, s->ports) {
- if (p->type == SOCKET_SOCKET) {
+ switch (p->type) {
+ case SOCKET_SOCKET: {
+ _cleanup_free_ char *k = NULL;
const char *t;
int r;
- char *k = NULL;
r = socket_address_print(&p->address, &k);
if (r < 0)
t = k;
fprintf(f, "%s%s: %s\n", prefix, listen_lookup(socket_address_family(&p->address), p->address.type), t);
- free(k);
- } else if (p->type == SOCKET_SPECIAL)
+ break;
+ }
+ case SOCKET_SPECIAL:
fprintf(f, "%sListenSpecial: %s\n", prefix, p->path);
- else if (p->type == SOCKET_USB_FUNCTION)
+ break;
+ case SOCKET_USB_FUNCTION:
fprintf(f, "%sListenUSBFunction: %s\n", prefix, p->path);
- else if (p->type == SOCKET_MQUEUE)
+ break;
+ case SOCKET_MQUEUE:
fprintf(f, "%sListenMessageQueue: %s\n", prefix, p->path);
- else
+ break;
+ default:
fprintf(f, "%sListenFIFO: %s\n", prefix, p->path);
+ }
}
fprintf(f,
assert(fd >= 0);
if (s->keep_alive) {
- int b = s->keep_alive;
- if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &b, sizeof(b)) < 0)
+ int one = 1;
+ if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &one, sizeof(one)) < 0)
log_unit_warning_errno(UNIT(s), errno, "SO_KEEPALIVE failed: %m");
}
- if (s->keep_alive_time) {
+ if (s->keep_alive_time > 0) {
int value = s->keep_alive_time / USEC_PER_SEC;
if (setsockopt(fd, SOL_TCP, TCP_KEEPIDLE, &value, sizeof(value)) < 0)
log_unit_warning_errno(UNIT(s), errno, "TCP_KEEPIDLE failed: %m");
}
- if (s->keep_alive_interval) {
+ if (s->keep_alive_interval > 0) {
int value = s->keep_alive_interval / USEC_PER_SEC;
if (setsockopt(fd, SOL_TCP, TCP_KEEPINTVL, &value, sizeof(value)) < 0)
log_unit_warning_errno(UNIT(s), errno, "TCP_KEEPINTVL failed: %m");
}
- if (s->keep_alive_cnt) {
+ if (s->keep_alive_cnt > 0) {
int value = s->keep_alive_cnt;
if (setsockopt(fd, SOL_TCP, TCP_KEEPCNT, &value, sizeof(value)) < 0)
log_unit_warning_errno(UNIT(s), errno, "TCP_KEEPCNT failed: %m");
}
- if (s->defer_accept) {
+ if (s->defer_accept > 0) {
int value = s->defer_accept / USEC_PER_SEC;
if (setsockopt(fd, SOL_TCP, TCP_DEFER_ACCEPT, &value, sizeof(value)) < 0)
log_unit_warning_errno(UNIT(s), errno, "TCP_DEFER_ACCEPT failed: %m");
}
if (s->no_delay) {
- int b = s->no_delay;
+ int one = 1;
if (s->socket_protocol == IPPROTO_SCTP) {
- if (setsockopt(fd, SOL_SCTP, SCTP_NODELAY, &b, sizeof(b)) < 0)
+ if (setsockopt(fd, SOL_SCTP, SCTP_NODELAY, &one, sizeof(one)) < 0)
log_unit_warning_errno(UNIT(s), errno, "SCTP_NODELAY failed: %m");
} else {
- if (setsockopt(fd, SOL_TCP, TCP_NODELAY, &b, sizeof(b)) < 0)
+ if (setsockopt(fd, SOL_TCP, TCP_NODELAY, &one, sizeof(one)) < 0)
log_unit_warning_errno(UNIT(s), errno, "TCP_NODELAY failed: %m");
}
}
int value = (int) s->receive_buffer;
/* We first try with SO_RCVBUFFORCE, in case we have the perms for that */
-
if (setsockopt(fd, SOL_SOCKET, SO_RCVBUFFORCE, &value, sizeof(value)) < 0)
if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &value, sizeof(value)) < 0)
log_unit_warning_errno(UNIT(s), errno, "SO_RCVBUF failed: %m");
return r;
/* Enforce the right access mode for the fifo */
- old_mask = umask(~ socket_mode);
+ old_mask = umask(~socket_mode);
/* Include the original umask in our mask */
(void) umask(~socket_mode | old_mask);
goto fail;
}
- r = fd;
- fd = -1;
-
- return r;
+ return TAKE_FD(fd);
fail:
mac_selinux_create_file_clear();
static int special_address_create(const char *path, bool writable) {
_cleanup_close_ int fd = -1;
struct stat st;
- int r;
assert(path);
if (!S_ISREG(st.st_mode) && !S_ISCHR(st.st_mode))
return -EEXIST;
- r = fd;
- fd = -1;
-
- return r;
+ return TAKE_FD(fd);
}
static int usbffs_address_create(const char *path) {
_cleanup_close_ int fd = -1;
struct stat st;
- int r;
assert(path);
if (!S_ISREG(st.st_mode))
return -EEXIST;
- r = fd;
- fd = -1;
-
- return r;
+ return TAKE_FD(fd);
}
static int mq_address_create(
struct stat st;
mode_t old_mask;
struct mq_attr _attr, *attr = NULL;
- int r;
assert(path);
}
/* Enforce the right access mode for the mq */
- old_mask = umask(~ mq_mode);
+ old_mask = umask(~mq_mode);
/* Include the original umask in our mask */
(void) umask(~mq_mode | old_mask);
st.st_gid != getgid())
return -EEXIST;
- r = fd;
- fd = -1;
-
- return r;
+ return TAKE_FD(fd);
}
static int socket_symlink(Socket *s) {
static int usbffs_dispatch_eps(SocketPort *p) {
_cleanup_free_ struct dirent **ent = NULL;
- int r, i, n, k;
+ size_t n, k, i;
+ int r;
r = scandir(p->path, &ent, usbffs_select_ep, alphasort);
if (r < 0)
return -errno;
- n = r;
+ n = (size_t) r;
p->auxiliary_fds = new(int, n);
if (!p->auxiliary_fds)
return -ENOMEM;
if (!ep)
return -ENOMEM;
- path_kill_slashes(ep);
+ path_simplify(ep, false);
r = usbffs_address_create(ep);
if (r < 0)
goto fail;
- p->auxiliary_fds[k] = r;
-
- ++k;
+ p->auxiliary_fds[k++] = r;
free(ent[i]);
}
}
static int socket_determine_selinux_label(Socket *s, char **ret) {
+ Service *service;
ExecCommand *c;
+ _cleanup_free_ char *path = NULL;
int r;
assert(s);
if (!UNIT_ISSET(s->service))
goto no_label;
- c = SERVICE(UNIT_DEREF(s->service))->exec_command[SERVICE_EXEC_START];
+ service = SERVICE(UNIT_DEREF(s->service));
+ c = service->exec_command[SERVICE_EXEC_START];
if (!c)
goto no_label;
- r = mac_selinux_get_create_label_from_exe(c->path, ret);
+ r = chase_symlinks(c->path, service->exec_context.root_directory, CHASE_PREFIX_ROOT, &path);
+ if (r < 0)
+ goto no_label;
+
+ r = mac_selinux_get_create_label_from_exe(path, ret);
if (IN_SET(r, -EPERM, -EOPNOTSUPP))
goto no_label;
}
r = bpf_firewall_supported();
if (r < 0)
return r;
- if (r == 0) /* If BPF firewalling isn't supported anyway — there's no point in this forking complexity */
+ if (r == BPF_FIREWALL_UNSUPPORTED) /* If BPF firewalling isn't supported anyway — there's no point in this forking complexity */
goto shortcut;
if (socketpair(AF_UNIX, SOCK_SEQPACKET|SOCK_CLOEXEC, 0, pair) < 0)
return log_unit_error_errno(UNIT(s), errno, "Failed to create communication channel: %m");
- r = unit_fork_helper_process(UNIT(s), &pid);
+ r = unit_fork_helper_process(UNIT(s), "(sd-listen)", &pid);
if (r < 0)
return log_unit_error_errno(UNIT(s), r, "Failed to fork off listener stub process: %m");
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_warn("listen-cgroup-helper", pid, false);
+ r = wait_for_terminate_and_check("(sd-listen)", pid, WAIT_LOG_ABNORMAL);
if (r < 0) {
safe_close(fd);
return r;
if (state != old_state)
log_unit_debug(UNIT(s), "Changed %s -> %s", socket_state_to_string(old_state), socket_state_to_string(state));
- unit_notify(UNIT(s), state_translation_table[old_state], state_translation_table[state], true);
+ unit_notify(UNIT(s), state_translation_table[old_state], state_translation_table[state], 0);
}
static int socket_coldplug(Unit *u) {
return r;
}
- if (!IN_SET(s->deserialized_state, SOCKET_DEAD, SOCKET_FAILED))
+ if (!IN_SET(s->deserialized_state, SOCKET_DEAD, SOCKET_FAILED)) {
(void) unit_setup_dynamic_creds(u);
+ (void) unit_setup_exec_runtime(u);
+ }
socket_set_state(s, s->deserialized_state);
return 0;
if (r < 0)
return r;
- manager_set_exec_params(UNIT(s)->manager, &exec_params);
unit_set_exec_params(UNIT(s), &exec_params);
exec_params.argv = c->argv;
/* We have to resolve the user names out-of-process, hence
* let's fork here. It's messy, but well, what can we do? */
- r = unit_fork_helper_process(UNIT(s), &pid);
+ r = unit_fork_helper_process(UNIT(s), "(sd-chown)", &pid);
if (r < 0)
return r;
if (r == 0) {
socket_set_state(s, s->result != SOCKET_SUCCESS ? SOCKET_FAILED : SOCKET_DEAD);
- exec_runtime_destroy(s->exec_runtime);
- s->exec_runtime = exec_runtime_unref(s->exec_runtime);
+ s->exec_runtime = exec_runtime_unref(s->exec_runtime, true);
exec_context_destroy_runtime_directory(&s->exec_context, UNIT(s)->manager->prefix[EXEC_DIRECTORY_RUNTIME]);
log_unit_debug(UNIT(s), "Suppressing connection request since unit stop is scheduled.");
if (cfd >= 0)
- cfd = safe_close(cfd);
+ goto refuse;
else
flush_ports(s);
return;
}
- if (!ratelimit_test(&s->trigger_limit)) {
- safe_close(cfd);
+ if (!ratelimit_below(&s->trigger_limit)) {
log_unit_warning(UNIT(s), "Trigger limit hit, refusing further activation.");
socket_enter_stop_pre(s, SOCKET_FAILURE_TRIGGER_LIMIT_HIT);
- return;
+ goto refuse;
}
if (cfd < 0) {
if (s->n_connections >= s->max_connections) {
log_unit_warning(UNIT(s), "Too many incoming connections (%u), dropping connection.",
s->n_connections);
- safe_close(cfd);
- return;
+ goto refuse;
}
if (s->max_connections_per_source > 0) {
r = socket_acquire_peer(s, cfd, &p);
if (r < 0) {
- safe_close(cfd);
- return;
+ goto refuse;
} else if (r > 0 && p->n_ref > s->max_connections_per_source) {
_cleanup_free_ char *t = NULL;
log_unit_warning(UNIT(s),
"Too many incoming connections (%u) from source %s, dropping connection.",
p->n_ref, strnull(t));
- safe_close(cfd);
- return;
+ goto refuse;
}
}
/* ENOTCONN is legitimate if TCP RST was received.
* This connection is over, but the socket unit lives on. */
log_unit_debug(UNIT(s), "Got ENOTCONN on incoming socket, assuming aborted connection attempt, ignoring.");
- safe_close(cfd);
- return;
+ goto refuse;
}
r = unit_name_to_prefix(UNIT(s)->id, &prefix);
cfd = -1; /* We passed ownership of the fd to the service now. Forget it here. */
s->n_connections++;
- service->peer = p; /* Pass ownership of the peer reference */
- p = NULL;
+ service->peer = TAKE_PTR(p); /* Pass ownership of the peer reference */
r = manager_add_job(UNIT(s)->manager, JOB_START, UNIT(service), JOB_REPLACE, &error, NULL);
if (r < 0) {
return;
+refuse:
+ s->n_refused++;
+ safe_close(cfd);
+ return;
+
fail:
log_unit_warning(UNIT(s), "Failed to queue service startup job (Maybe the service file is missing or not a %s unit?): %s",
cfd >= 0 ? "template" : "non-template",
unit_serialize_item(u, f, "state", socket_state_to_string(s->state));
unit_serialize_item(u, f, "result", socket_result_to_string(s->result));
unit_serialize_item_format(u, f, "n-accepted", "%u", s->n_accepted);
+ unit_serialize_item_format(u, f, "n-refused", "%u", s->n_refused);
if (s->control_pid > 0)
unit_serialize_item_format(u, f, "control-pid", PID_FMT, s->control_pid);
log_unit_debug(u, "Failed to parse n-accepted value: %s", value);
else
s->n_accepted += k;
+ } else if (streq(key, "n-refused")) {
+ unsigned k;
+
+ if (safe_atou(value, &k) < 0)
+ log_unit_debug(u, "Failed to parse n-refused value: %s", value);
+ else
+ s->n_refused += k;
} else if (streq(key, "control-pid")) {
pid_t pid;
return _SOCKET_TYPE_INVALID;
}
-_pure_ static bool socket_check_gc(Unit *u) {
+_pure_ static bool socket_may_gc(Unit *u) {
Socket *s = SOCKET(u);
assert(u);
- return s->n_connections > 0;
+ return s->n_connections == 0;
}
static int socket_accept_do(Socket *s, int fd) {
r = bpf_firewall_supported();
if (r < 0)
return r;
- if (r == 0)
+ if (r == BPF_FIREWALL_UNSUPPORTED)
goto shortcut;
if (socketpair(AF_UNIX, SOCK_SEQPACKET|SOCK_CLOEXEC, 0, pair) < 0)
return log_unit_error_errno(UNIT(s), errno, "Failed to create communication channel: %m");
- r = unit_fork_helper_process(UNIT(s), &pid);
+ r = unit_fork_helper_process(UNIT(s), "(sd-accept)", &pid);
if (r < 0)
return log_unit_error_errno(UNIT(s), r, "Failed to fork off accept stub process: %m");
if (r == 0) {
cfd = receive_one_fd(pair[0], 0);
/* We synchronously wait for the helper, as it shouldn't be slow */
- r = wait_for_terminate_and_warn("accept-cgroup-helper", pid, false);
+ r = wait_for_terminate_and_check("(sd-accept)", pid, WAIT_LOG_ABNORMAL);
if (r < 0) {
safe_close(cfd);
return r;
}
int socket_collect_fds(Socket *s, int **fds) {
- int *rfds, k = 0, n = 0;
+ size_t k = 0, n = 0;
SocketPort *p;
+ int *rfds;
assert(s);
assert(fds);
return -ENOMEM;
LIST_FOREACH(port, p, s->ports) {
- int i;
+ size_t i;
if (p->fd >= 0)
rfds[k++] = p->fd;
assert(k == n);
*fds = rfds;
- return n;
+ return (int) n;
}
static void socket_reset_failed(Unit *u) {
.active_state = socket_active_state,
.sub_state_to_string = socket_sub_state_to_string,
- .check_gc = socket_check_gc,
+ .may_gc = socket_may_gc,
.sigchld_event = socket_sigchld_event,