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.c171
1 files changed, 78 insertions, 93 deletions
diff --git a/src/core/socket.c b/src/core/socket.c
index 41988788b8..56d32225c4 100644
--- a/src/core/socket.c
+++ b/src/core/socket.c
@@ -1,22 +1,4 @@
/* 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>
@@ -454,32 +436,32 @@ static int socket_verify(Socket *s) {
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;
@@ -629,8 +611,7 @@ int socket_acquire_peer(Socket *s, int fd, SocketPeer **p) {
remote->socket = s;
- *p = remote;
- remote = NULL;
+ *p = TAKE_PTR(remote);
return 1;
}
@@ -800,27 +781,28 @@ static void socket_dump(Unit *u, FILE *f, const char *prefix) {
"%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)
@@ -829,15 +811,20 @@ static void socket_dump(Unit *u, FILE *f, const char *prefix) {
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,
@@ -1047,43 +1034,43 @@ static void socket_apply_socket_options(Socket *s, int fd) {
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");
}
}
@@ -1114,7 +1101,6 @@ static void socket_apply_socket_options(Socket *s, int fd) {
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");
@@ -1204,7 +1190,7 @@ static int fifo_address_create(
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);
@@ -1238,10 +1224,7 @@ static int fifo_address_create(
goto fail;
}
- r = fd;
- fd = -1;
-
- return r;
+ return TAKE_FD(fd);
fail:
mac_selinux_create_file_clear();
@@ -1251,7 +1234,6 @@ fail:
static int special_address_create(const char *path, bool writable) {
_cleanup_close_ int fd = -1;
struct stat st;
- int r;
assert(path);
@@ -1266,16 +1248,12 @@ static int special_address_create(const char *path, bool writable) {
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);
@@ -1290,10 +1268,7 @@ static int usbffs_address_create(const char *path) {
if (!S_ISREG(st.st_mode))
return -EEXIST;
- r = fd;
- fd = -1;
-
- return r;
+ return TAKE_FD(fd);
}
static int mq_address_create(
@@ -1306,7 +1281,6 @@ static int mq_address_create(
struct stat st;
mode_t old_mask;
struct mq_attr _attr, *attr = NULL;
- int r;
assert(path);
@@ -1320,7 +1294,7 @@ static int mq_address_create(
}
/* 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);
@@ -1338,10 +1312,7 @@ static int mq_address_create(
st.st_gid != getgid())
return -EEXIST;
- r = fd;
- fd = -1;
-
- return r;
+ return TAKE_FD(fd);
}
static int socket_symlink(Socket *s) {
@@ -1395,13 +1366,14 @@ static int usbffs_select_ep(const struct dirent *d) {
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;
@@ -1416,15 +1388,13 @@ static int usbffs_dispatch_eps(SocketPort *p) {
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]);
}
@@ -1439,7 +1409,9 @@ fail:
}
static int socket_determine_selinux_label(Socket *s, char **ret) {
+ Service *service;
ExecCommand *c;
+ _cleanup_free_ char *path = NULL;
int r;
assert(s);
@@ -1461,11 +1433,16 @@ static int socket_determine_selinux_label(Socket *s, char **ret) {
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;
}
@@ -1813,7 +1790,7 @@ static void socket_set_state(Socket *s, SocketState state) {
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) {
@@ -2261,18 +2238,17 @@ static void socket_enter_running(Socket *s, int cfd) {
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) {
@@ -2310,15 +2286,13 @@ static void socket_enter_running(Socket *s, int cfd) {
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;
@@ -2327,8 +2301,7 @@ static void socket_enter_running(Socket *s, int cfd) {
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;
}
}
@@ -2344,8 +2317,7 @@ static void socket_enter_running(Socket *s, int cfd) {
/* 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);
@@ -2373,8 +2345,7 @@ static void socket_enter_running(Socket *s, int cfd) {
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) {
@@ -2390,6 +2361,11 @@ static void socket_enter_running(Socket *s, int cfd) {
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",
@@ -2533,6 +2509,7 @@ static int socket_serialize(Unit *u, FILE *f, FDSet *fds) {
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);
@@ -2613,6 +2590,13 @@ static int socket_deserialize_item(Unit *u, const char *key, const char *value,
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;
@@ -3120,8 +3104,9 @@ static int socket_dispatch_timer(sd_event_source *source, usec_t usec, void *use
}
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);
@@ -3144,7 +3129,7 @@ int socket_collect_fds(Socket *s, int **fds) {
return -ENOMEM;
LIST_FOREACH(port, p, s->ports) {
- int i;
+ size_t i;
if (p->fd >= 0)
rfds[k++] = p->fd;
@@ -3155,7 +3140,7 @@ int socket_collect_fds(Socket *s, int **fds) {
assert(k == n);
*fds = rfds;
- return n;
+ return (int) n;
}
static void socket_reset_failed(Unit *u) {