From 29206d4619843252c2e04f20dc03c246547600a2 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 14 Jul 2016 12:37:28 +0200 Subject: core: add a concept of "dynamic" user ids, that are allocated as long as a service is running This adds a new boolean setting DynamicUser= to service files. If set, a new user will be allocated dynamically when the unit is started, and released when it is stopped. The user ID is allocated from the range 61184..65519. The user will not be added to /etc/passwd (but an NSS module to be added later should make it show up in getent passwd). For now, care should be taken that the service writes no files to disk, since this might result in files owned by UIDs that might get assigned dynamically to a different service later on. Later patches will tighten sandboxing in order to ensure that this cannot happen, except for a few selected directories. A simple way to test this is: systemd-run -p DynamicUser=1 /bin/sleep 99999 --- src/core/socket.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) (limited to 'src/core/socket.c') diff --git a/src/core/socket.c b/src/core/socket.c index e098055885..1ce41a1f07 100644 --- a/src/core/socket.c +++ b/src/core/socket.c @@ -150,6 +150,8 @@ static void socket_done(Unit *u) { exec_command_free_array(s->exec_command, _SOCKET_EXEC_COMMAND_MAX); s->control_command = NULL; + dynamic_creds_unref(&s->dynamic_creds); + socket_unwatch_control_pid(s); unit_ref_unset(&s->service); @@ -1602,6 +1604,9 @@ static int socket_coldplug(Unit *u) { return r; } + if (!IN_SET(s->deserialized_state, SOCKET_DEAD, SOCKET_FAILED)) + (void) unit_setup_dynamic_creds(u); + socket_set_state(s, s->deserialized_state); return 0; } @@ -1633,6 +1638,10 @@ static int socket_spawn(Socket *s, ExecCommand *c, pid_t *_pid) { if (r < 0) return r; + r = unit_setup_dynamic_creds(UNIT(s)); + if (r < 0) + return r; + r = socket_arm_timer(s, usec_add(now(CLOCK_MONOTONIC), s->timeout_usec)); if (r < 0) return r; @@ -1654,6 +1663,7 @@ static int socket_spawn(Socket *s, ExecCommand *c, pid_t *_pid) { &s->exec_context, &exec_params, s->exec_runtime, + &s->dynamic_creds, &pid); if (r < 0) return r; @@ -1757,12 +1767,14 @@ static void socket_enter_dead(Socket *s, SocketResult f) { if (f != SOCKET_SUCCESS) s->result = f; + 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); exec_context_destroy_runtime_directory(&s->exec_context, manager_get_runtime_prefix(UNIT(s)->manager)); - socket_set_state(s, s->result != SOCKET_SUCCESS ? SOCKET_FAILED : SOCKET_DEAD); + dynamic_creds_destroy(&s->dynamic_creds); } static void socket_enter_signal(Socket *s, SocketState state, SocketResult f); @@ -2930,6 +2942,7 @@ const UnitVTable socket_vtable = { .cgroup_context_offset = offsetof(Socket, cgroup_context), .kill_context_offset = offsetof(Socket, kill_context), .exec_runtime_offset = offsetof(Socket, exec_runtime), + .dynamic_creds_offset = offsetof(Socket, dynamic_creds), .sections = "Unit\0" -- cgit v1.2.3-54-g00ecf From 9d565427647a874b7eda64ef0cb41ea74a96659b Mon Sep 17 00:00:00 2001 From: Susant Sahani Date: Tue, 2 Aug 2016 23:18:23 +0530 Subject: socket: add support to control no. of connections from one source (#3607) Introduce MaxConnectionsPerSource= that is number of concurrent connections allowed per IP. RFE: 1939 --- man/systemd.socket.xml | 8 ++ src/core/dbus-socket.c | 1 + src/core/load-fragment-gperf.gperf.m4 | 1 + src/core/service.c | 1 + src/core/service.h | 1 + src/core/socket.c | 185 ++++++++++++++++++++++++++++++++++ src/core/socket.h | 16 +++ 7 files changed, 213 insertions(+) (limited to 'src/core/socket.c') diff --git a/man/systemd.socket.xml b/man/systemd.socket.xml index 5bf54d8ef3..26e5d3ce7b 100644 --- a/man/systemd.socket.xml +++ b/man/systemd.socket.xml @@ -443,6 +443,14 @@ + MaxConnectionsPerSource= + The maximum number of connections for a service per source IP address. + This is is very similar to the MaxConnections= directive + above. Disabled by default. + + + + KeepAlive= Takes a boolean argument. If true, the TCP/IP stack will send a keep alive message after 2h (depending on diff --git a/src/core/dbus-socket.c b/src/core/dbus-socket.c index 961340608d..9a071a1355 100644 --- a/src/core/dbus-socket.c +++ b/src/core/dbus-socket.c @@ -137,6 +137,7 @@ const sd_bus_vtable bus_socket_vtable[] = { SD_BUS_PROPERTY("Symlinks", "as", NULL, offsetof(Socket, symlinks), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("Mark", "i", bus_property_get_int, offsetof(Socket, mark), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("MaxConnections", "u", bus_property_get_unsigned, offsetof(Socket, max_connections), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("MaxConnectionsPerSource", "u", bus_property_get_unsigned, offsetof(Socket, max_connections_per_source), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("MessageQueueMaxMessages", "x", bus_property_get_long, offsetof(Socket, mq_maxmsg), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("MessageQueueMessageSize", "x", bus_property_get_long, offsetof(Socket, mq_msgsize), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("ReusePort", "b", bus_property_get_bool, offsetof(Socket, reuse_port), SD_BUS_VTABLE_PROPERTY_CONST), diff --git a/src/core/load-fragment-gperf.gperf.m4 b/src/core/load-fragment-gperf.gperf.m4 index c9cdbe8ba7..396f847213 100644 --- a/src/core/load-fragment-gperf.gperf.m4 +++ b/src/core/load-fragment-gperf.gperf.m4 @@ -293,6 +293,7 @@ Socket.DirectoryMode, config_parse_mode, 0, Socket.Accept, config_parse_bool, 0, offsetof(Socket, accept) Socket.Writable, config_parse_bool, 0, offsetof(Socket, writable) Socket.MaxConnections, config_parse_unsigned, 0, offsetof(Socket, max_connections) +Socket.MaxConnectionsPerSource, config_parse_unsigned, 0, offsetof(Socket, max_connections_per_source) Socket.KeepAlive, config_parse_bool, 0, offsetof(Socket, keep_alive) Socket.KeepAliveTimeSec, config_parse_sec, 0, offsetof(Socket, keep_alive_time) Socket.KeepAliveIntervalSec, config_parse_sec, 0, offsetof(Socket, keep_alive_interval) diff --git a/src/core/service.c b/src/core/service.c index 4d59d78ecb..eb125cb999 100644 --- a/src/core/service.c +++ b/src/core/service.c @@ -342,6 +342,7 @@ static void service_done(Unit *u) { s->bus_name_owner = mfree(s->bus_name_owner); service_close_socket_fd(s); + s->peer = socket_peer_unref(s->peer); unit_ref_unset(&s->accept_socket); diff --git a/src/core/service.h b/src/core/service.h index 8e56e1acb9..888007cc0b 100644 --- a/src/core/service.h +++ b/src/core/service.h @@ -152,6 +152,7 @@ struct Service { pid_t main_pid, control_pid; int socket_fd; + SocketPeer *peer; bool socket_fd_selinux_context_net; bool permissions_start_only; diff --git a/src/core/socket.c b/src/core/socket.c index 1ce41a1f07..ff55885fb3 100644 --- a/src/core/socket.c +++ b/src/core/socket.c @@ -57,6 +57,7 @@ #include "unit-printf.h" #include "unit.h" #include "user-util.h" +#include "in-addr-util.h" static const UnitActiveState state_translation_table[_SOCKET_STATE_MAX] = { [SOCKET_DEAD] = UNIT_INACTIVE, @@ -77,6 +78,9 @@ static const UnitActiveState state_translation_table[_SOCKET_STATE_MAX] = { static int socket_dispatch_io(sd_event_source *source, int fd, uint32_t revents, void *userdata); static int socket_dispatch_timer(sd_event_source *source, usec_t usec, void *userdata); +SocketPeer *socket_peer_new(void); +int socket_find_peer(Socket *s, int fd, SocketPeer **p); + static void socket_init(Unit *u) { Socket *s = SOCKET(u); @@ -141,11 +145,17 @@ void socket_free_ports(Socket *s) { static void socket_done(Unit *u) { Socket *s = SOCKET(u); + SocketPeer *p; assert(s); socket_free_ports(s); + while ((p = hashmap_steal_first(s->peers_by_address))) + p->socket = NULL; + + s->peers_by_address = hashmap_free(s->peers_by_address); + s->exec_runtime = exec_runtime_unref(s->exec_runtime); exec_command_free_array(s->exec_command, _SOCKET_EXEC_COMMAND_MAX); s->control_command = NULL; @@ -468,6 +478,40 @@ static int socket_verify(Socket *s) { return 0; } +static void peer_address_hash_func(const void *p, struct siphash *state) { + const SocketPeer *s = p; + + assert(s); + + if (s->peer.sa.sa_family == AF_INET) + siphash24_compress(&s->peer.in.sin_addr, sizeof(s->peer.in.sin_addr), state); + else if (s->peer.sa.sa_family == AF_INET6) + siphash24_compress(&s->peer.in6.sin6_addr, sizeof(s->peer.in6.sin6_addr), state); +} + +static int peer_address_compare_func(const void *a, const void *b) { + const SocketPeer *x = a, *y = b; + + if (x->peer.sa.sa_family < y->peer.sa.sa_family) + return -1; + if (x->peer.sa.sa_family > y->peer.sa.sa_family) + return 1; + + switch(x->peer.sa.sa_family) { + case AF_INET: + return memcmp(&x->peer.in.sin_addr, &y->peer.in.sin_addr, sizeof(x->peer.in.sin_addr)); + case AF_INET6: + return memcmp(&x->peer.in6.sin6_addr, &y->peer.in6.sin6_addr, sizeof(x->peer.in6.sin6_addr)); + } + + return -1; +} + +const struct hash_ops peer_address_hash_ops = { + .hash = peer_address_hash_func, + .compare = peer_address_compare_func +}; + static int socket_load(Unit *u) { Socket *s = SOCKET(u); int r; @@ -475,6 +519,10 @@ static int socket_load(Unit *u) { assert(u); assert(u->load_state == UNIT_STUB); + r = hashmap_ensure_allocated(&s->peers_by_address, &peer_address_hash_ops); + if (r < 0) + return r; + r = unit_load_fragment_and_dropin(u); if (r < 0) return r; @@ -2050,6 +2098,7 @@ static void socket_enter_running(Socket *s, int cfd) { socket_set_state(s, SOCKET_RUNNING); } else { _cleanup_free_ char *prefix = NULL, *instance = NULL, *name = NULL; + _cleanup_(socket_peer_unrefp) SocketPeer *p = NULL; Service *service; if (s->n_connections >= s->max_connections) { @@ -2058,6 +2107,21 @@ static void socket_enter_running(Socket *s, int cfd) { return; } + if (s->max_connections_per_source > 0) { + r = socket_find_peer(s, cfd, &p); + if (r < 0) { + safe_close(cfd); + return; + } + + if (p->n_ref > s->max_connections_per_source) { + log_unit_warning(UNIT(s), "Too many incoming connections (%u) from source, refusing connection attempt.", p->n_ref); + safe_close(cfd); + p = NULL; + return; + } + } + r = socket_instantiate_service(s); if (r < 0) goto fail; @@ -2099,6 +2163,11 @@ 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++; + if (s->max_connections_per_source > 0) { + service->peer = socket_peer_ref(p); + p = NULL; + } + r = manager_add_job(UNIT(s)->manager, JOB_START, UNIT(service), JOB_REPLACE, &error, NULL); if (r < 0) { /* We failed to activate the new service, but it still exists. Let's make sure the service @@ -2244,7 +2313,9 @@ static int socket_stop(Unit *u) { static int socket_serialize(Unit *u, FILE *f, FDSet *fds) { Socket *s = SOCKET(u); + SocketPeer *k; SocketPort *p; + Iterator i; int r; assert(u); @@ -2295,6 +2366,16 @@ static int socket_serialize(Unit *u, FILE *f, FDSet *fds) { } } + HASHMAP_FOREACH(k, s->peers_by_address, i) { + _cleanup_free_ char *t = NULL; + + r = sockaddr_pretty(&k->peer.sa, FAMILY_ADDRESS_SIZE(k->peer.sa.sa_family), true, true, &t); + if (r < 0) + return r; + + unit_serialize_item_format(u, f, "peer", "%u %s", k->n_ref, t); + } + return 0; } @@ -2458,6 +2539,33 @@ static int socket_deserialize_item(Unit *u, const char *key, const char *value, } } + } else if (streq(key, "peer")) { + _cleanup_(socket_peer_unrefp) SocketPeer *p; + int n_ref, skip = 0; + SocketAddress a; + int r; + + if (sscanf(value, "%u %n", &n_ref, &skip) < 1 || n_ref < 1) + log_unit_debug(u, "Failed to parse socket peer value: %s", value); + else { + r = socket_address_parse(&a, value+skip); + if (r < 0) + return r; + + p = socket_peer_new(); + if (!p) + return log_oom(); + + p->n_ref = n_ref; + memcpy(&p->peer, &a.sockaddr, sizeof(a.sockaddr)); + p->socket = s; + + r = hashmap_put(s->peers_by_address, p, p); + if (r < 0) + return r; + + p = NULL; + } } else log_unit_debug(UNIT(s), "Unknown serialization key: %s", key); @@ -2554,6 +2662,83 @@ _pure_ static bool socket_check_gc(Unit *u) { return s->n_connections > 0; } +SocketPeer *socket_peer_new(void) { + SocketPeer *p; + + p = new0(SocketPeer, 1); + if (!p) + return NULL; + + p->n_ref = 1; + + return p; +} + +SocketPeer *socket_peer_ref(SocketPeer *p) { + if (!p) + return NULL; + + assert(p->n_ref > 0); + p->n_ref++; + + return p; +} + +SocketPeer *socket_peer_unref(SocketPeer *p) { + if (!p) + return NULL; + + assert(p->n_ref > 0); + + p->n_ref--; + + if (p->n_ref > 0) + return NULL; + + if (p->socket) + (void) hashmap_remove(p->socket->peers_by_address, p); + + free(p); + + return NULL; +} + +int socket_find_peer(Socket *s, int fd, SocketPeer **p) { + _cleanup_free_ SocketPeer *remote = NULL; + SocketPeer sa, *i; + socklen_t salen = sizeof(sa.peer); + int r; + + assert(fd >= 0); + assert(s); + + r = getpeername(fd, &sa.peer.sa, &salen); + if (r < 0) + return log_error_errno(errno, "getpeername failed: %m"); + + i = hashmap_get(s->peers_by_address, &sa); + if (i) { + *p = i; + return 1; + } + + remote = socket_peer_new(); + if (!remote) + return log_oom(); + + memcpy(&remote->peer, &sa.peer, sizeof(union sockaddr_union)); + remote->socket = s; + + r = hashmap_put(s->peers_by_address, remote, remote); + if (r < 0) + return r; + + *p = remote; + remote = NULL; + + return 0; +} + static int socket_dispatch_io(sd_event_source *source, int fd, uint32_t revents, void *userdata) { SocketPort *p = userdata; int cfd = -1; diff --git a/src/core/socket.h b/src/core/socket.h index 6c32d67bef..2fe38ef2aa 100644 --- a/src/core/socket.h +++ b/src/core/socket.h @@ -20,6 +20,7 @@ ***/ typedef struct Socket Socket; +typedef struct SocketPeer SocketPeer; #include "mount.h" #include "service.h" @@ -79,9 +80,12 @@ struct Socket { LIST_HEAD(SocketPort, ports); + Hashmap *peers_by_address; + unsigned n_accepted; unsigned n_connections; unsigned max_connections; + unsigned max_connections_per_source; unsigned backlog; unsigned keep_alive_cnt; @@ -164,6 +168,18 @@ struct Socket { RateLimit trigger_limit; }; +struct SocketPeer { + unsigned n_ref; + + Socket *socket; + union sockaddr_union peer; +}; + +SocketPeer *socket_peer_ref(SocketPeer *p); +SocketPeer *socket_peer_unref(SocketPeer *p); + +DEFINE_TRIVIAL_CLEANUP_FUNC(SocketPeer*, socket_peer_unref); + /* Called from the service code when collecting fds */ int socket_collect_fds(Socket *s, int **fds); -- cgit v1.2.3-54-g00ecf From c39f1ce24ddb1aa683991c5099dcc2afbfcbc57c Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 26 Jul 2016 17:40:35 +0200 Subject: core: turn various execution flags into a proper flags parameter The ExecParameters structure contains a number of bit-flags, that were so far exposed as bool:1, change this to a proper, single binary bit flag field. This makes things a bit more expressive, and is helpful as we add more flags, since these booleans are passed around in various callers, for example service_spawn(), whose signature can be made much shorter now. Not all bit booleans from ExecParameters are moved into the flags field for now, but this can be added later. --- src/core/execute.c | 21 ++++++++----- src/core/execute.h | 17 ++++++++--- src/core/mount.c | 12 +++----- src/core/service.c | 90 +++++++++++++++++++----------------------------------- src/core/socket.c | 12 +++----- src/core/swap.c | 12 +++----- 6 files changed, 72 insertions(+), 92 deletions(-) (limited to 'src/core/socket.c') diff --git a/src/core/execute.c b/src/core/execute.c index 77a75245cb..bc0fd27402 100644 --- a/src/core/execute.c +++ b/src/core/execute.c @@ -427,7 +427,7 @@ static int setup_input( return STDIN_FILENO; } - i = fixup_input(context->std_input, socket_fd, params->apply_tty_stdin); + i = fixup_input(context->std_input, socket_fd, params->flags & EXEC_APPLY_TTY_STDIN); switch (i) { @@ -502,7 +502,7 @@ static int setup_output( return STDERR_FILENO; } - i = fixup_input(context->std_input, socket_fd, params->apply_tty_stdin); + i = fixup_input(context->std_input, socket_fd, params->flags & EXEC_APPLY_TTY_STDIN); o = fixup_output(context->std_output, socket_fd); if (fileno == STDERR_FILENO) { @@ -1675,7 +1675,7 @@ static int exec_child( exec_context_tty_reset(context, params); - if (params->confirm_spawn) { + if (params->flags & EXEC_CONFIRM_SPAWN) { char response; r = ask_for_confirmation(&response, argv); @@ -1940,7 +1940,7 @@ static int exec_child( umask(context->umask); - if (params->apply_permissions && !command->privileged) { + if ((params->flags & EXEC_APPLY_PERMISSIONS) && !command->privileged) { r = enforce_groups(context, username, gid); if (r < 0) { *exit_status = EXIT_GROUP; @@ -2010,7 +2010,7 @@ static int exec_child( } r = setup_namespace( - params->apply_chroot ? context->root_directory : NULL, + (params->flags & EXEC_APPLY_CHROOT) ? context->root_directory : NULL, context->read_write_paths, context->read_only_paths, context->inaccessible_paths, @@ -2041,7 +2041,7 @@ static int exec_child( else wd = "/"; - if (params->apply_chroot) { + if (params->flags & EXEC_APPLY_CHROOT) { if (!needs_mount_namespace && context->root_directory) if (chroot(context->root_directory) < 0) { *exit_status = EXIT_CHROOT; @@ -2065,7 +2065,12 @@ static int exec_child( } #ifdef HAVE_SELINUX - if (params->apply_permissions && mac_selinux_use() && params->selinux_context_net && socket_fd >= 0 && !command->privileged) { + if ((params->flags & EXEC_APPLY_PERMISSIONS) && + mac_selinux_use() && + params->selinux_context_net && + socket_fd >= 0 && + !command->privileged) { + r = mac_selinux_get_child_mls_label(socket_fd, command->path, context->selinux_context, &mac_selinux_context_net); if (r < 0) { *exit_status = EXIT_SELINUX_CONTEXT; @@ -2090,7 +2095,7 @@ static int exec_child( return r; } - if (params->apply_permissions && !command->privileged) { + if ((params->flags & EXEC_APPLY_PERMISSIONS) && !command->privileged) { bool use_address_families = context->address_families_whitelist || !set_isempty(context->address_families); diff --git a/src/core/execute.h b/src/core/execute.h index 48cc18fbb3..77418ea2ad 100644 --- a/src/core/execute.h +++ b/src/core/execute.h @@ -208,6 +208,17 @@ struct ExecContext { bool no_new_privileges_set:1; }; +typedef enum ExecFlags { + EXEC_CONFIRM_SPAWN = 1U << 0, + EXEC_APPLY_PERMISSIONS = 1U << 1, + EXEC_APPLY_CHROOT = 1U << 2, + EXEC_APPLY_TTY_STDIN = 1U << 3, + + /* The following are not usec by execute.c, but by consumers internally */ + EXEC_PASS_FDS = 1U << 4, + EXEC_IS_CONTROL = 1U << 5, +} ExecFlags; + struct ExecParameters { char **argv; char **environment; @@ -216,11 +227,7 @@ struct ExecParameters { char **fd_names; unsigned n_fds; - bool apply_permissions:1; - bool apply_chroot:1; - bool apply_tty_stdin:1; - - bool confirm_spawn:1; + ExecFlags flags; bool selinux_context_net:1; bool cgroup_delegate:1; diff --git a/src/core/mount.c b/src/core/mount.c index afb20af9e2..3f6ac7fcf9 100644 --- a/src/core/mount.c +++ b/src/core/mount.c @@ -701,12 +701,10 @@ static int mount_spawn(Mount *m, ExecCommand *c, pid_t *_pid) { pid_t pid; int r; ExecParameters exec_params = { - .apply_permissions = true, - .apply_chroot = true, - .apply_tty_stdin = true, - .stdin_fd = -1, - .stdout_fd = -1, - .stderr_fd = -1, + .flags = EXEC_APPLY_PERMISSIONS|EXEC_APPLY_CHROOT|EXEC_APPLY_TTY_STDIN, + .stdin_fd = -1, + .stdout_fd = -1, + .stderr_fd = -1, }; assert(m); @@ -732,7 +730,7 @@ static int mount_spawn(Mount *m, ExecCommand *c, pid_t *_pid) { return r; exec_params.environment = UNIT(m)->manager->environment; - exec_params.confirm_spawn = UNIT(m)->manager->confirm_spawn; + exec_params.flags |= UNIT(m)->manager->confirm_spawn ? EXEC_CONFIRM_SPAWN : 0; exec_params.cgroup_supported = UNIT(m)->manager->cgroup_supported; exec_params.cgroup_path = UNIT(m)->cgroup_path; exec_params.cgroup_delegate = m->cgroup_context.delegate; diff --git a/src/core/service.c b/src/core/service.c index eb125cb999..b4db7d17ed 100644 --- a/src/core/service.c +++ b/src/core/service.c @@ -1152,11 +1152,7 @@ static int service_spawn( Service *s, ExecCommand *c, usec_t timeout, - bool pass_fds, - bool apply_permissions, - bool apply_chroot, - bool apply_tty_stdin, - bool is_control, + ExecFlags flags, pid_t *_pid) { _cleanup_strv_free_ char **argv = NULL, **final_env = NULL, **our_env = NULL, **fd_names = NULL; @@ -1166,12 +1162,10 @@ static int service_spawn( pid_t pid; ExecParameters exec_params = { - .apply_permissions = apply_permissions, - .apply_chroot = apply_chroot, - .apply_tty_stdin = apply_tty_stdin, - .stdin_fd = -1, - .stdout_fd = -1, - .stderr_fd = -1, + .flags = flags, + .stdin_fd = -1, + .stdout_fd = -1, + .stderr_fd = -1, }; int r; @@ -1194,7 +1188,7 @@ static int service_spawn( if (r < 0) return r; - if (pass_fds || + if ((flags & EXEC_PASS_FDS) || s->exec_context.std_input == EXEC_INPUT_SOCKET || s->exec_context.std_output == EXEC_OUTPUT_SOCKET || s->exec_context.std_error == EXEC_OUTPUT_SOCKET) { @@ -1218,7 +1212,7 @@ static int service_spawn( if (!our_env) return -ENOMEM; - if (is_control ? s->notify_access == NOTIFY_ALL : s->notify_access != NOTIFY_NONE) + if ((flags & EXEC_IS_CONTROL) ? s->notify_access == NOTIFY_ALL : s->notify_access != NOTIFY_NONE) if (asprintf(our_env + n_env++, "NOTIFY_SOCKET=%s", UNIT(s)->manager->notify_socket) < 0) return -ENOMEM; @@ -1226,7 +1220,7 @@ static int service_spawn( if (asprintf(our_env + n_env++, "MAINPID="PID_FMT, s->main_pid) < 0) return -ENOMEM; - if (!MANAGER_IS_SYSTEM(UNIT(s)->manager)) + if (MANAGER_IS_USER(UNIT(s)->manager)) if (asprintf(our_env + n_env++, "MANAGERPID="PID_FMT, getpid()) < 0) return -ENOMEM; @@ -1266,18 +1260,18 @@ static int service_spawn( if (!final_env) return -ENOMEM; - if (is_control && UNIT(s)->cgroup_path) { + if ((flags & EXEC_IS_CONTROL) && UNIT(s)->cgroup_path) { path = strjoina(UNIT(s)->cgroup_path, "/control"); (void) cg_create(SYSTEMD_CGROUP_CONTROLLER, path); } else path = UNIT(s)->cgroup_path; exec_params.argv = argv; + exec_params.environment = final_env; exec_params.fds = fds; exec_params.fd_names = fd_names; exec_params.n_fds = n_fds; - exec_params.environment = final_env; - exec_params.confirm_spawn = UNIT(s)->manager->confirm_spawn; + exec_params.flags |= UNIT(s)->manager->confirm_spawn ? EXEC_CONFIRM_SPAWN : 0; exec_params.cgroup_supported = UNIT(s)->manager->cgroup_supported; exec_params.cgroup_path = path; exec_params.cgroup_delegate = s->cgroup_context.delegate; @@ -1465,11 +1459,9 @@ static void service_enter_stop_post(Service *s, ServiceResult f) { r = service_spawn(s, s->control_command, s->timeout_stop_usec, - false, - !s->permissions_start_only, - !s->root_directory_start_only, - true, - true, + (s->permissions_start_only ? 0 : EXEC_APPLY_PERMISSIONS) | + (s->root_directory_start_only ? 0 : EXEC_APPLY_CHROOT) | + EXEC_APPLY_TTY_STDIN | EXEC_IS_CONTROL, &s->control_pid); if (r < 0) goto fail; @@ -1580,11 +1572,9 @@ static void service_enter_stop(Service *s, ServiceResult f) { r = service_spawn(s, s->control_command, s->timeout_stop_usec, - false, - !s->permissions_start_only, - !s->root_directory_start_only, - false, - true, + (s->permissions_start_only ? 0 : EXEC_APPLY_PERMISSIONS) | + (s->root_directory_start_only ? 0 : EXEC_APPLY_CHROOT) | + EXEC_IS_CONTROL, &s->control_pid); if (r < 0) goto fail; @@ -1661,11 +1651,9 @@ static void service_enter_start_post(Service *s) { r = service_spawn(s, s->control_command, s->timeout_start_usec, - false, - !s->permissions_start_only, - !s->root_directory_start_only, - false, - true, + (s->permissions_start_only ? 0 : EXEC_APPLY_PERMISSIONS)| + (s->root_directory_start_only ? 0 : EXEC_APPLY_CHROOT)| + EXEC_IS_CONTROL, &s->control_pid); if (r < 0) goto fail; @@ -1735,11 +1723,7 @@ static void service_enter_start(Service *s) { r = service_spawn(s, c, timeout, - true, - true, - true, - true, - false, + EXEC_PASS_FDS|EXEC_APPLY_PERMISSIONS|EXEC_APPLY_CHROOT|EXEC_APPLY_TTY_STDIN, &pid); if (r < 0) goto fail; @@ -1798,11 +1782,9 @@ static void service_enter_start_pre(Service *s) { r = service_spawn(s, s->control_command, s->timeout_start_usec, - false, - !s->permissions_start_only, - !s->root_directory_start_only, - true, - true, + (s->permissions_start_only ? 0 : EXEC_APPLY_PERMISSIONS) | + (s->root_directory_start_only ? 0: EXEC_APPLY_CHROOT) | + EXEC_IS_CONTROL|EXEC_APPLY_TTY_STDIN, &s->control_pid); if (r < 0) goto fail; @@ -1877,11 +1859,9 @@ static void service_enter_reload(Service *s) { r = service_spawn(s, s->control_command, s->timeout_start_usec, - false, - !s->permissions_start_only, - !s->root_directory_start_only, - false, - true, + (s->permissions_start_only ? 0 : EXEC_APPLY_PERMISSIONS) | + (s->root_directory_start_only ? 0 : EXEC_APPLY_CHROOT) | + EXEC_IS_CONTROL, &s->control_pid); if (r < 0) goto fail; @@ -1919,12 +1899,10 @@ static void service_run_next_control(Service *s) { r = service_spawn(s, s->control_command, timeout, - false, - !s->permissions_start_only, - !s->root_directory_start_only, - s->control_command_id == SERVICE_EXEC_START_PRE || - s->control_command_id == SERVICE_EXEC_STOP_POST, - true, + (s->permissions_start_only ? 0 : EXEC_APPLY_PERMISSIONS) | + (s->root_directory_start_only ? 0 : EXEC_APPLY_CHROOT) | + (IN_SET(s->control_command_id, SERVICE_EXEC_START_PRE, SERVICE_EXEC_STOP_POST) ? EXEC_APPLY_TTY_STDIN : 0)| + EXEC_IS_CONTROL, &s->control_pid); if (r < 0) goto fail; @@ -1962,11 +1940,7 @@ static void service_run_next_main(Service *s) { r = service_spawn(s, s->main_command, s->timeout_start_usec, - true, - true, - true, - true, - false, + EXEC_PASS_FDS|EXEC_APPLY_PERMISSIONS|EXEC_APPLY_CHROOT|EXEC_APPLY_TTY_STDIN, &pid); if (r < 0) goto fail; diff --git a/src/core/socket.c b/src/core/socket.c index ff55885fb3..82363e2157 100644 --- a/src/core/socket.c +++ b/src/core/socket.c @@ -1664,12 +1664,10 @@ static int socket_spawn(Socket *s, ExecCommand *c, pid_t *_pid) { pid_t pid; int r; ExecParameters exec_params = { - .apply_permissions = true, - .apply_chroot = true, - .apply_tty_stdin = true, - .stdin_fd = -1, - .stdout_fd = -1, - .stderr_fd = -1, + .flags = EXEC_APPLY_PERMISSIONS|EXEC_APPLY_CHROOT|EXEC_APPLY_TTY_STDIN, + .stdin_fd = -1, + .stdout_fd = -1, + .stderr_fd = -1, }; assert(s); @@ -1700,7 +1698,7 @@ static int socket_spawn(Socket *s, ExecCommand *c, pid_t *_pid) { exec_params.argv = argv; exec_params.environment = UNIT(s)->manager->environment; - exec_params.confirm_spawn = UNIT(s)->manager->confirm_spawn; + exec_params.flags |= UNIT(s)->manager->confirm_spawn ? EXEC_CONFIRM_SPAWN : 0; exec_params.cgroup_supported = UNIT(s)->manager->cgroup_supported; exec_params.cgroup_path = UNIT(s)->cgroup_path; exec_params.cgroup_delegate = s->cgroup_context.delegate; diff --git a/src/core/swap.c b/src/core/swap.c index 66a318d01f..0ba4c4d881 100644 --- a/src/core/swap.c +++ b/src/core/swap.c @@ -611,12 +611,10 @@ static int swap_spawn(Swap *s, ExecCommand *c, pid_t *_pid) { pid_t pid; int r; ExecParameters exec_params = { - .apply_permissions = true, - .apply_chroot = true, - .apply_tty_stdin = true, - .stdin_fd = -1, - .stdout_fd = -1, - .stderr_fd = -1, + .flags = EXEC_APPLY_PERMISSIONS|EXEC_APPLY_CHROOT|EXEC_APPLY_TTY_STDIN, + .stdin_fd = -1, + .stdout_fd = -1, + .stderr_fd = -1, }; assert(s); @@ -642,7 +640,7 @@ static int swap_spawn(Swap *s, ExecCommand *c, pid_t *_pid) { goto fail; exec_params.environment = UNIT(s)->manager->environment; - exec_params.confirm_spawn = UNIT(s)->manager->confirm_spawn; + exec_params.flags |= UNIT(s)->manager->confirm_spawn ? EXEC_CONFIRM_SPAWN : 0; exec_params.cgroup_supported = UNIT(s)->manager->cgroup_supported; exec_params.cgroup_path = UNIT(s)->cgroup_path; exec_params.cgroup_delegate = s->cgroup_context.delegate; -- cgit v1.2.3-54-g00ecf From a0fef983ab200db4e2b151beb06c9cf8fef6c5ab Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 4 Aug 2016 21:14:27 +0200 Subject: core: remember first unit failure, not last unit failure Previously, the result value of a unit was overriden with each failure that took place, so that the result always reported the last failure that took place. With this commit this is changed, so that the first failure taking place is stored instead. This should normally not matter much as multiple failures are sufficiently uncommon. However, it improves one behaviour: if we send SIGABRT to a service due to a watchdog timeout, then this currently would be reported as "coredump" failure, rather than the "watchodg" failure it really is. Hence, in order to report information about the type of the failure, and not about the effect of it, let's change this from all unit type to store the first, not the last failure. This addresses the issue pointed out here: https://github.com/systemd/systemd/pull/3818#discussion_r73433520 --- src/core/automount.c | 2 +- src/core/busname.c | 6 +++--- src/core/mount.c | 8 ++++---- src/core/path.c | 2 +- src/core/scope.c | 4 ++-- src/core/service.c | 14 +++++++------- src/core/socket.c | 10 +++++----- src/core/swap.c | 8 ++++---- src/core/timer.c | 2 +- 9 files changed, 28 insertions(+), 28 deletions(-) (limited to 'src/core/socket.c') diff --git a/src/core/automount.c b/src/core/automount.c index 20a73c76f9..00295cf769 100644 --- a/src/core/automount.c +++ b/src/core/automount.c @@ -301,7 +301,7 @@ static void automount_dump(Unit *u, FILE *f, const char *prefix) { static void automount_enter_dead(Automount *a, AutomountResult f) { assert(a); - if (f != AUTOMOUNT_SUCCESS) + if (a->result == AUTOMOUNT_SUCCESS) a->result = f; automount_set_state(a, a->result != AUTOMOUNT_SUCCESS ? AUTOMOUNT_FAILED : AUTOMOUNT_DEAD); diff --git a/src/core/busname.c b/src/core/busname.c index 730be2ee14..7952cd31aa 100644 --- a/src/core/busname.c +++ b/src/core/busname.c @@ -442,7 +442,7 @@ fail: static void busname_enter_dead(BusName *n, BusNameResult f) { assert(n); - if (f != BUSNAME_SUCCESS) + if (n->result == BUSNAME_SUCCESS) n->result = f; busname_set_state(n, n->result != BUSNAME_SUCCESS ? BUSNAME_FAILED : BUSNAME_DEAD); @@ -454,7 +454,7 @@ static void busname_enter_signal(BusName *n, BusNameState state, BusNameResult f assert(n); - if (f != BUSNAME_SUCCESS) + if (n->result == BUSNAME_SUCCESS) n->result = f; kill_context_init(&kill_context); @@ -882,7 +882,7 @@ static void busname_sigchld_event(Unit *u, pid_t pid, int code, int status) { log_unit_full(u, f == BUSNAME_SUCCESS ? LOG_DEBUG : LOG_NOTICE, 0, "Control process exited, code=%s status=%i", sigchld_code_to_string(code), status); - if (f != BUSNAME_SUCCESS) + if (n->result == BUSNAME_SUCCESS) n->result = f; switch (n->state) { diff --git a/src/core/mount.c b/src/core/mount.c index 3f6ac7fcf9..f3ccf6d48a 100644 --- a/src/core/mount.c +++ b/src/core/mount.c @@ -759,7 +759,7 @@ static int mount_spawn(Mount *m, ExecCommand *c, pid_t *_pid) { static void mount_enter_dead(Mount *m, MountResult f) { assert(m); - if (f != MOUNT_SUCCESS) + if (m->result == MOUNT_SUCCESS) m->result = f; mount_set_state(m, m->result != MOUNT_SUCCESS ? MOUNT_FAILED : MOUNT_DEAD); @@ -775,7 +775,7 @@ static void mount_enter_dead(Mount *m, MountResult f) { static void mount_enter_mounted(Mount *m, MountResult f) { assert(m); - if (f != MOUNT_SUCCESS) + if (m->result == MOUNT_SUCCESS) m->result = f; mount_set_state(m, MOUNT_MOUNTED); @@ -786,7 +786,7 @@ static void mount_enter_signal(Mount *m, MountState state, MountResult f) { assert(m); - if (f != MOUNT_SUCCESS) + if (m->result == MOUNT_SUCCESS) m->result = f; r = unit_kill_context( @@ -1158,7 +1158,7 @@ static void mount_sigchld_event(Unit *u, pid_t pid, int code, int status) { else assert_not_reached("Unknown code"); - if (f != MOUNT_SUCCESS) + if (m->result == MOUNT_SUCCESS) m->result = f; if (m->control_command) { diff --git a/src/core/path.c b/src/core/path.c index 0dd0d375d8..10f9b06974 100644 --- a/src/core/path.c +++ b/src/core/path.c @@ -454,7 +454,7 @@ static int path_coldplug(Unit *u) { static void path_enter_dead(Path *p, PathResult f) { assert(p); - if (f != PATH_SUCCESS) + if (p->result == PATH_SUCCESS) p->result = f; path_set_state(p, p->result != PATH_SUCCESS ? PATH_FAILED : PATH_DEAD); diff --git a/src/core/scope.c b/src/core/scope.c index b45e238974..b278aed3d6 100644 --- a/src/core/scope.c +++ b/src/core/scope.c @@ -221,7 +221,7 @@ static void scope_dump(Unit *u, FILE *f, const char *prefix) { static void scope_enter_dead(Scope *s, ScopeResult f) { assert(s); - if (f != SCOPE_SUCCESS) + if (s->result == SCOPE_SUCCESS) s->result = f; scope_set_state(s, s->result != SCOPE_SUCCESS ? SCOPE_FAILED : SCOPE_DEAD); @@ -233,7 +233,7 @@ static void scope_enter_signal(Scope *s, ScopeState state, ScopeResult f) { assert(s); - if (f != SCOPE_SUCCESS) + if (s->result == SCOPE_SUCCESS) s->result = f; unit_watch_all_pids(UNIT(s)); diff --git a/src/core/service.c b/src/core/service.c index 0cbea52276..a6793b813b 100644 --- a/src/core/service.c +++ b/src/core/service.c @@ -1423,7 +1423,7 @@ static void service_enter_dead(Service *s, ServiceResult f, bool allow_restart) int r; assert(s); - if (f != SERVICE_SUCCESS) + if (s->result == SERVICE_SUCCESS) s->result = f; service_set_state(s, s->result != SERVICE_SUCCESS ? SERVICE_FAILED : SERVICE_DEAD); @@ -1472,7 +1472,7 @@ static void service_enter_stop_post(Service *s, ServiceResult f) { int r; assert(s); - if (f != SERVICE_SUCCESS) + if (s->result == SERVICE_SUCCESS) s->result = f; service_unwatch_control_pid(s); @@ -1525,7 +1525,7 @@ static void service_enter_signal(Service *s, ServiceState state, ServiceResult f assert(s); - if (f != SERVICE_SUCCESS) + if (s->result == SERVICE_SUCCESS) s->result = f; unit_watch_all_pids(UNIT(s)); @@ -1583,7 +1583,7 @@ static void service_enter_stop(Service *s, ServiceResult f) { assert(s); - if (f != SERVICE_SUCCESS) + if (s->result == SERVICE_SUCCESS) s->result = f; service_unwatch_control_pid(s); @@ -1635,7 +1635,7 @@ static bool service_good(Service *s) { static void service_enter_running(Service *s, ServiceResult f) { assert(s); - if (f != SERVICE_SUCCESS) + if (s->result == SERVICE_SUCCESS) s->result = f; service_unwatch_control_pid(s); @@ -2609,7 +2609,7 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) { "EXIT_STATUS=%i", status, NULL); - if (f != SERVICE_SUCCESS) + if (s->result == SERVICE_SUCCESS) s->result = f; if (s->main_command && @@ -2690,7 +2690,7 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) { "Control process exited, code=%s status=%i", sigchld_code_to_string(code), status); - if (f != SERVICE_SUCCESS) + if (s->result == SERVICE_SUCCESS) s->result = f; /* Immediately get rid of the cgroup, so that the diff --git a/src/core/socket.c b/src/core/socket.c index 82363e2157..c919e89b7d 100644 --- a/src/core/socket.c +++ b/src/core/socket.c @@ -1810,7 +1810,7 @@ fail: static void socket_enter_dead(Socket *s, SocketResult f) { assert(s); - if (f != SOCKET_SUCCESS) + if (s->result == SOCKET_SUCCESS) s->result = f; socket_set_state(s, s->result != SOCKET_SUCCESS ? SOCKET_FAILED : SOCKET_DEAD); @@ -1829,7 +1829,7 @@ static void socket_enter_stop_post(Socket *s, SocketResult f) { int r; assert(s); - if (f != SOCKET_SUCCESS) + if (s->result == SOCKET_SUCCESS) s->result = f; socket_unwatch_control_pid(s); @@ -1857,7 +1857,7 @@ static void socket_enter_signal(Socket *s, SocketState state, SocketResult f) { assert(s); - if (f != SOCKET_SUCCESS) + if (s->result == SOCKET_SUCCESS) s->result = f; r = unit_kill_context( @@ -1901,7 +1901,7 @@ static void socket_enter_stop_pre(Socket *s, SocketResult f) { int r; assert(s); - if (f != SOCKET_SUCCESS) + if (s->result == SOCKET_SUCCESS) s->result = f; socket_unwatch_control_pid(s); @@ -2822,7 +2822,7 @@ static void socket_sigchld_event(Unit *u, pid_t pid, int code, int status) { "Control process exited, code=%s status=%i", sigchld_code_to_string(code), status); - if (f != SOCKET_SUCCESS) + if (s->result == SOCKET_SUCCESS) s->result = f; if (s->control_command && diff --git a/src/core/swap.c b/src/core/swap.c index 0ba4c4d881..2c802da3b5 100644 --- a/src/core/swap.c +++ b/src/core/swap.c @@ -673,7 +673,7 @@ fail: static void swap_enter_dead(Swap *s, SwapResult f) { assert(s); - if (f != SWAP_SUCCESS) + if (s->result == SWAP_SUCCESS) s->result = f; swap_set_state(s, s->result != SWAP_SUCCESS ? SWAP_FAILED : SWAP_DEAD); @@ -689,7 +689,7 @@ static void swap_enter_dead(Swap *s, SwapResult f) { static void swap_enter_active(Swap *s, SwapResult f) { assert(s); - if (f != SWAP_SUCCESS) + if (s->result == SWAP_SUCCESS) s->result = f; swap_set_state(s, SWAP_ACTIVE); @@ -700,7 +700,7 @@ static void swap_enter_signal(Swap *s, SwapState state, SwapResult f) { assert(s); - if (f != SWAP_SUCCESS) + if (s->result == SWAP_SUCCESS) s->result = f; r = unit_kill_context( @@ -997,7 +997,7 @@ static void swap_sigchld_event(Unit *u, pid_t pid, int code, int status) { else assert_not_reached("Unknown code"); - if (f != SWAP_SUCCESS) + if (s->result == SWAP_SUCCESS) s->result = f; if (s->control_command) { diff --git a/src/core/timer.c b/src/core/timer.c index 3206296f09..e2b43f02f8 100644 --- a/src/core/timer.c +++ b/src/core/timer.c @@ -291,7 +291,7 @@ static int timer_coldplug(Unit *u) { static void timer_enter_dead(Timer *t, TimerResult f) { assert(t); - if (f != TIMER_SUCCESS) + if (t->result == TIMER_SUCCESS) t->result = f; timer_set_state(t, t->result != TIMER_SUCCESS ? TIMER_FAILED : TIMER_DEAD); -- cgit v1.2.3-54-g00ecf From 9a73653c3ed819c28a293a5df2da8bdac84dfdb1 Mon Sep 17 00:00:00 2001 From: Zbigniew Jędrzejewski-Szmek Date: Thu, 4 Aug 2016 20:55:45 -0400 Subject: systemd: convert peers_by_address to a set --- src/core/socket.c | 16 ++++++++-------- src/core/socket.h | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) (limited to 'src/core/socket.c') diff --git a/src/core/socket.c b/src/core/socket.c index ff55885fb3..d3b9a75547 100644 --- a/src/core/socket.c +++ b/src/core/socket.c @@ -151,10 +151,10 @@ static void socket_done(Unit *u) { socket_free_ports(s); - while ((p = hashmap_steal_first(s->peers_by_address))) + while ((p = set_steal_first(s->peers_by_address))) p->socket = NULL; - s->peers_by_address = hashmap_free(s->peers_by_address); + s->peers_by_address = set_free(s->peers_by_address); s->exec_runtime = exec_runtime_unref(s->exec_runtime); exec_command_free_array(s->exec_command, _SOCKET_EXEC_COMMAND_MAX); @@ -519,7 +519,7 @@ static int socket_load(Unit *u) { assert(u); assert(u->load_state == UNIT_STUB); - r = hashmap_ensure_allocated(&s->peers_by_address, &peer_address_hash_ops); + r = set_ensure_allocated(&s->peers_by_address, &peer_address_hash_ops); if (r < 0) return r; @@ -2366,7 +2366,7 @@ static int socket_serialize(Unit *u, FILE *f, FDSet *fds) { } } - HASHMAP_FOREACH(k, s->peers_by_address, i) { + SET_FOREACH(k, s->peers_by_address, i) { _cleanup_free_ char *t = NULL; r = sockaddr_pretty(&k->peer.sa, FAMILY_ADDRESS_SIZE(k->peer.sa.sa_family), true, true, &t); @@ -2560,7 +2560,7 @@ static int socket_deserialize_item(Unit *u, const char *key, const char *value, memcpy(&p->peer, &a.sockaddr, sizeof(a.sockaddr)); p->socket = s; - r = hashmap_put(s->peers_by_address, p, p); + r = set_put(s->peers_by_address, p); if (r < 0) return r; @@ -2696,7 +2696,7 @@ SocketPeer *socket_peer_unref(SocketPeer *p) { return NULL; if (p->socket) - (void) hashmap_remove(p->socket->peers_by_address, p); + set_remove(p->socket->peers_by_address, p); free(p); @@ -2716,7 +2716,7 @@ int socket_find_peer(Socket *s, int fd, SocketPeer **p) { if (r < 0) return log_error_errno(errno, "getpeername failed: %m"); - i = hashmap_get(s->peers_by_address, &sa); + i = set_get(s->peers_by_address, &sa); if (i) { *p = i; return 1; @@ -2729,7 +2729,7 @@ int socket_find_peer(Socket *s, int fd, SocketPeer **p) { memcpy(&remote->peer, &sa.peer, sizeof(union sockaddr_union)); remote->socket = s; - r = hashmap_put(s->peers_by_address, remote, remote); + r = set_put(s->peers_by_address, remote); if (r < 0) return r; diff --git a/src/core/socket.h b/src/core/socket.h index 2fe38ef2aa..edbe9df6b1 100644 --- a/src/core/socket.h +++ b/src/core/socket.h @@ -80,7 +80,7 @@ struct Socket { LIST_HEAD(SocketPort, ports); - Hashmap *peers_by_address; + Set *peers_by_address; unsigned n_accepted; unsigned n_connections; -- cgit v1.2.3-54-g00ecf From 166cf510c2ba5aed15dcb807ec263ea2201dcb28 Mon Sep 17 00:00:00 2001 From: Zbigniew Jędrzejewski-Szmek Date: Thu, 4 Aug 2016 21:42:23 -0400 Subject: core/socket: rework SocketPeer refcounting Make functions and definitions that don't need to be shared local to socket.c. --- src/core/socket.c | 194 ++++++++++++++++++++++++++++-------------------------- src/core/socket.h | 7 -- 2 files changed, 100 insertions(+), 101 deletions(-) (limited to 'src/core/socket.c') diff --git a/src/core/socket.c b/src/core/socket.c index d3b9a75547..972d494dbc 100644 --- a/src/core/socket.c +++ b/src/core/socket.c @@ -59,6 +59,13 @@ #include "user-util.h" #include "in-addr-util.h" +struct SocketPeer { + unsigned n_ref; + + Socket *socket; + union sockaddr_union peer; +}; + static const UnitActiveState state_translation_table[_SOCKET_STATE_MAX] = { [SOCKET_DEAD] = UNIT_INACTIVE, [SOCKET_START_PRE] = UNIT_ACTIVATING, @@ -78,9 +85,6 @@ static const UnitActiveState state_translation_table[_SOCKET_STATE_MAX] = { static int socket_dispatch_io(sd_event_source *source, int fd, uint32_t revents, void *userdata); static int socket_dispatch_timer(sd_event_source *source, usec_t usec, void *userdata); -SocketPeer *socket_peer_new(void); -int socket_find_peer(Socket *s, int fd, SocketPeer **p); - static void socket_init(Unit *u) { Socket *s = SOCKET(u); @@ -482,10 +486,11 @@ static void peer_address_hash_func(const void *p, struct siphash *state) { const SocketPeer *s = p; assert(s); + assert(IN_SET(s->peer.sa.sa_family, AF_INET, AF_INET6)); if (s->peer.sa.sa_family == AF_INET) siphash24_compress(&s->peer.in.sin_addr, sizeof(s->peer.in.sin_addr), state); - else if (s->peer.sa.sa_family == AF_INET6) + else siphash24_compress(&s->peer.in6.sin6_addr, sizeof(s->peer.in6.sin6_addr), state); } @@ -503,8 +508,7 @@ static int peer_address_compare_func(const void *a, const void *b) { case AF_INET6: return memcmp(&x->peer.in6.sin6_addr, &y->peer.in6.sin6_addr, sizeof(x->peer.in6.sin6_addr)); } - - return -1; + assert_not_reached("Black sheep in the family!"); } const struct hash_ops peer_address_hash_ops = { @@ -537,6 +541,87 @@ static int socket_load(Unit *u) { return socket_verify(s); } +static SocketPeer *socket_peer_new(void) { + SocketPeer *p; + + p = new0(SocketPeer, 1); + if (!p) + return NULL; + + p->n_ref = 1; + + return p; +} + +SocketPeer *socket_peer_ref(SocketPeer *p) { + if (!p) + return NULL; + + assert(p->n_ref > 0); + p->n_ref++; + + return p; +} + +SocketPeer *socket_peer_unref(SocketPeer *p) { + if (!p) + return NULL; + + assert(p->n_ref > 0); + + p->n_ref--; + + if (p->n_ref > 0) + return NULL; + + if (p->socket) + set_remove(p->socket->peers_by_address, p); + + return mfree(p); +} + +static int socket_acquire_peer(Socket *s, int fd, SocketPeer **p) { + _cleanup_(socket_peer_unrefp) SocketPeer *remote = NULL; + SocketPeer sa = {}, *i; + socklen_t salen = sizeof(sa.peer); + int r; + + assert(fd >= 0); + assert(s); + + r = getpeername(fd, &sa.peer.sa, &salen); + if (r < 0) + return log_error_errno(errno, "getpeername failed: %m"); + + if (!IN_SET(sa.peer.sa.sa_family, AF_INET, AF_INET6)) { + *p = NULL; + return 0; + } + + i = set_get(s->peers_by_address, &sa); + if (i) { + *p = socket_peer_ref(i); + return 1; + } + + remote = socket_peer_new(); + if (!remote) + return log_oom(); + + remote->peer = sa.peer; + + r = set_put(s->peers_by_address, remote); + if (r < 0) + return r; + + remote->socket = s; + + *p = remote; + remote = NULL; + + return 1; +} + _const_ static const char* listen_lookup(int family, int type) { if (family == AF_NETLINK) @@ -2102,22 +2187,22 @@ static void socket_enter_running(Socket *s, int cfd) { Service *service; if (s->n_connections >= s->max_connections) { - log_unit_warning(UNIT(s), "Too many incoming connections (%u), refusing connection attempt.", s->n_connections); + log_unit_warning(UNIT(s), "Too many incoming connections (%u), refusing connection attempt.", + s->n_connections); safe_close(cfd); return; } if (s->max_connections_per_source > 0) { - r = socket_find_peer(s, cfd, &p); + r = socket_acquire_peer(s, cfd, &p); if (r < 0) { safe_close(cfd); return; - } - - if (p->n_ref > s->max_connections_per_source) { - log_unit_warning(UNIT(s), "Too many incoming connections (%u) from source, refusing connection attempt.", p->n_ref); + } else if (r > 0 && p->n_ref > s->max_connections_per_source) { + log_unit_warning(UNIT(s), + "Too many incoming connections (%u) from source, refusing connection attempt.", + p->n_ref); safe_close(cfd); - p = NULL; return; } } @@ -2163,10 +2248,8 @@ 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++; - if (s->max_connections_per_source > 0) { - service->peer = socket_peer_ref(p); - p = NULL; - } + service->peer = p; /* Pass ownership of the peer reference */ + p = NULL; r = manager_add_job(UNIT(s)->manager, JOB_START, UNIT(service), JOB_REPLACE, &error, NULL); if (r < 0) { @@ -2662,83 +2745,6 @@ _pure_ static bool socket_check_gc(Unit *u) { return s->n_connections > 0; } -SocketPeer *socket_peer_new(void) { - SocketPeer *p; - - p = new0(SocketPeer, 1); - if (!p) - return NULL; - - p->n_ref = 1; - - return p; -} - -SocketPeer *socket_peer_ref(SocketPeer *p) { - if (!p) - return NULL; - - assert(p->n_ref > 0); - p->n_ref++; - - return p; -} - -SocketPeer *socket_peer_unref(SocketPeer *p) { - if (!p) - return NULL; - - assert(p->n_ref > 0); - - p->n_ref--; - - if (p->n_ref > 0) - return NULL; - - if (p->socket) - set_remove(p->socket->peers_by_address, p); - - free(p); - - return NULL; -} - -int socket_find_peer(Socket *s, int fd, SocketPeer **p) { - _cleanup_free_ SocketPeer *remote = NULL; - SocketPeer sa, *i; - socklen_t salen = sizeof(sa.peer); - int r; - - assert(fd >= 0); - assert(s); - - r = getpeername(fd, &sa.peer.sa, &salen); - if (r < 0) - return log_error_errno(errno, "getpeername failed: %m"); - - i = set_get(s->peers_by_address, &sa); - if (i) { - *p = i; - return 1; - } - - remote = socket_peer_new(); - if (!remote) - return log_oom(); - - memcpy(&remote->peer, &sa.peer, sizeof(union sockaddr_union)); - remote->socket = s; - - r = set_put(s->peers_by_address, remote); - if (r < 0) - return r; - - *p = remote; - remote = NULL; - - return 0; -} - static int socket_dispatch_io(sd_event_source *source, int fd, uint32_t revents, void *userdata) { SocketPort *p = userdata; int cfd = -1; diff --git a/src/core/socket.h b/src/core/socket.h index edbe9df6b1..6a78fd322d 100644 --- a/src/core/socket.h +++ b/src/core/socket.h @@ -168,13 +168,6 @@ struct Socket { RateLimit trigger_limit; }; -struct SocketPeer { - unsigned n_ref; - - Socket *socket; - union sockaddr_union peer; -}; - SocketPeer *socket_peer_ref(SocketPeer *p); SocketPeer *socket_peer_unref(SocketPeer *p); -- cgit v1.2.3-54-g00ecf From 3ebcd323bdeeb55bb963fe5e9d97a87f96fd8879 Mon Sep 17 00:00:00 2001 From: Zbigniew Jędrzejewski-Szmek Date: Thu, 4 Aug 2016 23:42:27 -0400 Subject: systemd: do not serialize peer, bump count when deserializing socket instead --- src/core/service.c | 14 ++++++++++++++ src/core/socket.c | 42 +----------------------------------------- src/core/socket.h | 1 + 3 files changed, 16 insertions(+), 41 deletions(-) (limited to 'src/core/socket.c') diff --git a/src/core/service.c b/src/core/service.c index 1629e1ce44..3c9455a5f8 100644 --- a/src/core/service.c +++ b/src/core/service.c @@ -1041,6 +1041,20 @@ static int service_coldplug(Unit *u) { if (!IN_SET(s->deserialized_state, SERVICE_DEAD, SERVICE_FAILED, SERVICE_AUTO_RESTART)) (void) unit_setup_dynamic_creds(u); + if (UNIT_ISSET(s->accept_socket)) { + Socket* socket = SOCKET(UNIT_DEREF(s->accept_socket)); + + if (socket->max_connections_per_source > 0) { + SocketPeer *peer; + + /* Make a best-effort attempt at bumping the connection count */ + if (socket_acquire_peer(socket, s->socket_fd, &peer) > 0) { + socket_peer_unref(s->peer); + s->peer = peer; + } + } + } + service_set_state(s, s->deserialized_state); return 0; } diff --git a/src/core/socket.c b/src/core/socket.c index 972d494dbc..0d77694251 100644 --- a/src/core/socket.c +++ b/src/core/socket.c @@ -580,7 +580,7 @@ SocketPeer *socket_peer_unref(SocketPeer *p) { return mfree(p); } -static int socket_acquire_peer(Socket *s, int fd, SocketPeer **p) { +int socket_acquire_peer(Socket *s, int fd, SocketPeer **p) { _cleanup_(socket_peer_unrefp) SocketPeer *remote = NULL; SocketPeer sa = {}, *i; socklen_t salen = sizeof(sa.peer); @@ -2396,9 +2396,7 @@ static int socket_stop(Unit *u) { static int socket_serialize(Unit *u, FILE *f, FDSet *fds) { Socket *s = SOCKET(u); - SocketPeer *k; SocketPort *p; - Iterator i; int r; assert(u); @@ -2449,16 +2447,6 @@ static int socket_serialize(Unit *u, FILE *f, FDSet *fds) { } } - SET_FOREACH(k, s->peers_by_address, i) { - _cleanup_free_ char *t = NULL; - - r = sockaddr_pretty(&k->peer.sa, FAMILY_ADDRESS_SIZE(k->peer.sa.sa_family), true, true, &t); - if (r < 0) - return r; - - unit_serialize_item_format(u, f, "peer", "%u %s", k->n_ref, t); - } - return 0; } @@ -2574,7 +2562,6 @@ static int socket_deserialize_item(Unit *u, const char *key, const char *value, if (sscanf(value, "%i %i %n", &fd, &type, &skip) < 2 || fd < 0 || type < 0 || !fdset_contains(fds, fd)) log_unit_debug(u, "Failed to parse socket value: %s", value); else { - LIST_FOREACH(port, p, s->ports) if (socket_address_is(&p->address, value+skip, type)) break; @@ -2622,33 +2609,6 @@ static int socket_deserialize_item(Unit *u, const char *key, const char *value, } } - } else if (streq(key, "peer")) { - _cleanup_(socket_peer_unrefp) SocketPeer *p; - int n_ref, skip = 0; - SocketAddress a; - int r; - - if (sscanf(value, "%u %n", &n_ref, &skip) < 1 || n_ref < 1) - log_unit_debug(u, "Failed to parse socket peer value: %s", value); - else { - r = socket_address_parse(&a, value+skip); - if (r < 0) - return r; - - p = socket_peer_new(); - if (!p) - return log_oom(); - - p->n_ref = n_ref; - memcpy(&p->peer, &a.sockaddr, sizeof(a.sockaddr)); - p->socket = s; - - r = set_put(s->peers_by_address, p); - if (r < 0) - return r; - - p = NULL; - } } else log_unit_debug(UNIT(s), "Unknown serialization key: %s", key); diff --git a/src/core/socket.h b/src/core/socket.h index 6a78fd322d..89f4664510 100644 --- a/src/core/socket.h +++ b/src/core/socket.h @@ -170,6 +170,7 @@ struct Socket { SocketPeer *socket_peer_ref(SocketPeer *p); SocketPeer *socket_peer_unref(SocketPeer *p); +int socket_acquire_peer(Socket *s, int fd, SocketPeer **p); DEFINE_TRIVIAL_CLEANUP_FUNC(SocketPeer*, socket_peer_unref); -- cgit v1.2.3-54-g00ecf From ea8f50f8085ce9385676385006030e8886cabaa6 Mon Sep 17 00:00:00 2001 From: Zbigniew Jędrzejewski-Szmek Date: Thu, 4 Aug 2016 23:51:47 -0400 Subject: core/socket: include remote address in the message when dropping connection Without the address the message is not very useful. Aug 04 23:52:21 rawhide systemd[1]: testlimit.socket: Too many incoming connections (4) from source ::1, dropping connection. --- src/core/socket.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) (limited to 'src/core/socket.c') diff --git a/src/core/socket.c b/src/core/socket.c index 0d77694251..b8a2f7fff8 100644 --- a/src/core/socket.c +++ b/src/core/socket.c @@ -2187,7 +2187,7 @@ static void socket_enter_running(Socket *s, int cfd) { Service *service; if (s->n_connections >= s->max_connections) { - log_unit_warning(UNIT(s), "Too many incoming connections (%u), refusing connection attempt.", + log_unit_warning(UNIT(s), "Too many incoming connections (%u), dropping connection.", s->n_connections); safe_close(cfd); return; @@ -2199,9 +2199,13 @@ static void socket_enter_running(Socket *s, int cfd) { safe_close(cfd); return; } else if (r > 0 && p->n_ref > s->max_connections_per_source) { + _cleanup_free_ char *t = NULL; + + sockaddr_pretty(&p->peer.sa, FAMILY_ADDRESS_SIZE(p->peer.sa.sa_family), true, false, &t); + log_unit_warning(UNIT(s), - "Too many incoming connections (%u) from source, refusing connection attempt.", - p->n_ref); + "Too many incoming connections (%u) from source %s, dropping connection.", + p->n_ref, strnull(t)); safe_close(cfd); return; } -- cgit v1.2.3-54-g00ecf From 80a58668d989c2316bcf1079b3e98ae526e633fa Mon Sep 17 00:00:00 2001 From: Zbigniew Jędrzejewski-Szmek Date: Fri, 5 Aug 2016 08:24:00 -0400 Subject: socket: add helper function to remove code duplication --- src/core/socket.c | 82 ++++++++++++++++++++----------------------------------- 1 file changed, 29 insertions(+), 53 deletions(-) (limited to 'src/core/socket.c') diff --git a/src/core/socket.c b/src/core/socket.c index b8a2f7fff8..7f3f154a16 100644 --- a/src/core/socket.c +++ b/src/core/socket.c @@ -2454,6 +2454,11 @@ static int socket_serialize(Unit *u, FILE *f, FDSet *fds) { return 0; } +static void socket_port_take_fd(SocketPort *p, FDSet *fds, int fd) { + safe_close(p->fd); + p->fd = fdset_remove(fds, fd); +} + static int socket_deserialize_item(Unit *u, const char *key, const char *value, FDSet *fds) { Socket *s = SOCKET(u); @@ -2508,18 +2513,13 @@ static int socket_deserialize_item(Unit *u, const char *key, const char *value, if (sscanf(value, "%i %n", &fd, &skip) < 1 || fd < 0 || !fdset_contains(fds, fd)) log_unit_debug(u, "Failed to parse fifo value: %s", value); - else { - + else LIST_FOREACH(port, p, s->ports) if (p->type == SOCKET_FIFO && - path_equal_or_files_same(p->path, value+skip)) + path_equal_or_files_same(p->path, value+skip)) { + socket_port_take_fd(p, fds, fd); break; - - if (p) { - safe_close(p->fd); - p->fd = fdset_remove(fds, fd); - } - } + } } else if (streq(key, "special")) { int fd, skip = 0; @@ -2527,18 +2527,13 @@ static int socket_deserialize_item(Unit *u, const char *key, const char *value, if (sscanf(value, "%i %n", &fd, &skip) < 1 || fd < 0 || !fdset_contains(fds, fd)) log_unit_debug(u, "Failed to parse special value: %s", value); - else { - + else LIST_FOREACH(port, p, s->ports) if (p->type == SOCKET_SPECIAL && - path_equal_or_files_same(p->path, value+skip)) + path_equal_or_files_same(p->path, value+skip)) { + socket_port_take_fd(p, fds, fd); break; - - if (p) { - safe_close(p->fd); - p->fd = fdset_remove(fds, fd); - } - } + } } else if (streq(key, "mqueue")) { int fd, skip = 0; @@ -2546,18 +2541,13 @@ static int socket_deserialize_item(Unit *u, const char *key, const char *value, if (sscanf(value, "%i %n", &fd, &skip) < 1 || fd < 0 || !fdset_contains(fds, fd)) log_unit_debug(u, "Failed to parse mqueue value: %s", value); - else { - + else LIST_FOREACH(port, p, s->ports) if (p->type == SOCKET_MQUEUE && - streq(p->path, value+skip)) + streq(p->path, value+skip)) { + socket_port_take_fd(p, fds, fd); break; - - if (p) { - safe_close(p->fd); - p->fd = fdset_remove(fds, fd); - } - } + } } else if (streq(key, "socket")) { int fd, type, skip = 0; @@ -2565,16 +2555,12 @@ static int socket_deserialize_item(Unit *u, const char *key, const char *value, if (sscanf(value, "%i %i %n", &fd, &type, &skip) < 2 || fd < 0 || type < 0 || !fdset_contains(fds, fd)) log_unit_debug(u, "Failed to parse socket value: %s", value); - else { + else LIST_FOREACH(port, p, s->ports) - if (socket_address_is(&p->address, value+skip, type)) + if (socket_address_is(&p->address, value+skip, type)) { + socket_port_take_fd(p, fds, fd); break; - - if (p) { - safe_close(p->fd); - p->fd = fdset_remove(fds, fd); - } - } + } } else if (streq(key, "netlink")) { int fd, skip = 0; @@ -2582,17 +2568,12 @@ static int socket_deserialize_item(Unit *u, const char *key, const char *value, if (sscanf(value, "%i %n", &fd, &skip) < 1 || fd < 0 || !fdset_contains(fds, fd)) log_unit_debug(u, "Failed to parse socket value: %s", value); - else { - + else LIST_FOREACH(port, p, s->ports) - if (socket_address_is_netlink(&p->address, value+skip)) + if (socket_address_is_netlink(&p->address, value+skip)) { + socket_port_take_fd(p, fds, fd); break; - - if (p) { - safe_close(p->fd); - p->fd = fdset_remove(fds, fd); - } - } + } } else if (streq(key, "ffs")) { int fd, skip = 0; @@ -2600,18 +2581,13 @@ static int socket_deserialize_item(Unit *u, const char *key, const char *value, if (sscanf(value, "%i %n", &fd, &skip) < 1 || fd < 0 || !fdset_contains(fds, fd)) log_unit_debug(u, "Failed to parse ffs value: %s", value); - else { - + else LIST_FOREACH(port, p, s->ports) if (p->type == SOCKET_USB_FUNCTION && - path_equal_or_files_same(p->path, value+skip)) + path_equal_or_files_same(p->path, value+skip)) { + socket_port_take_fd(p, fds, fd); break; - - if (p) { - safe_close(p->fd); - p->fd = fdset_remove(fds, fd); - } - } + } } else log_unit_debug(UNIT(s), "Unknown serialization key: %s", key); -- cgit v1.2.3-54-g00ecf