X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=elogind.git;a=blobdiff_plain;f=src%2Fcore%2Fsocket.c;h=34ce1b1ffd54c6f84da894447aad23295884921f;hp=35531edb751e3cd0c5635b1791f7bfcb6849c5eb;hb=cf8bd44339b00330fdbc91041d6731ba8aba9fec;hpb=e66cf1a3f94fff48a572f6dbd19b43c9bcf7b8c7 diff --git a/src/core/socket.c b/src/core/socket.c index 35531edb7..34ce1b1ff 100644 --- a/src/core/socket.c +++ b/src/core/socket.c @@ -29,8 +29,10 @@ #include #include #include -#ifdef HAVE_XATTR -#include +#include + +#ifdef HAVE_SELINUX +#include #endif #include "sd-event.h" @@ -57,6 +59,7 @@ static const UnitActiveState state_translation_table[_SOCKET_STATE_MAX] = { [SOCKET_DEAD] = UNIT_INACTIVE, [SOCKET_START_PRE] = UNIT_ACTIVATING, + [SOCKET_START_CHOWN] = UNIT_ACTIVATING, [SOCKET_START_POST] = UNIT_ACTIVATING, [SOCKET_LISTENING] = UNIT_ACTIVE, [SOCKET_RUNNING] = UNIT_ACTIVE, @@ -90,13 +93,8 @@ static void socket_init(Unit *u) { s->ip_ttl = -1; s->mark = -1; - exec_context_init(&s->exec_context); s->exec_context.std_output = u->manager->default_std_output; s->exec_context.std_error = u->manager->default_std_error; - kill_context_init(&s->kill_context); - cgroup_context_init(&s->cgroup_context); - - unit_cgroup_context_init_defaults(u, &s->cgroup_context); s->control_command_id = _SOCKET_EXEC_COMMAND_INVALID; } @@ -121,9 +119,7 @@ void socket_free_ports(Socket *s) { sd_event_source_unref(p->event_source); - if (p->fd >= 0) - close_nointr_nofail(p->fd); - + safe_close(p->fd); free(p->path); free(p); } @@ -136,8 +132,6 @@ static void socket_done(Unit *u) { socket_free_ports(s); - cgroup_context_done(&s->cgroup_context); - exec_context_done(&s->exec_context); s->exec_runtime = exec_runtime_unref(s->exec_runtime); exec_command_free_array(s->exec_command, _SOCKET_EXEC_COMMAND_MAX); s->control_command = NULL; @@ -156,6 +150,11 @@ static void socket_done(Unit *u) { free(s->smack_ip_in); free(s->smack_ip_out); + strv_free(s->symlinks); + + free(s->user); + free(s->group); + s->timer_event_source = sd_event_source_unref(s->timer_event_source); } @@ -177,12 +176,16 @@ static int socket_arm_timer(Socket *s) { return sd_event_source_set_enabled(s->timer_event_source, SD_EVENT_ONESHOT); } - return sd_event_add_monotonic(UNIT(s)->manager->event, &s->timer_event_source, now(CLOCK_MONOTONIC) + s->timeout_usec, 0, socket_dispatch_timer, s); + return sd_event_add_time( + UNIT(s)->manager->event, + &s->timer_event_source, + CLOCK_MONOTONIC, + now(CLOCK_MONOTONIC) + s->timeout_usec, 0, + socket_dispatch_timer, s); } -static int socket_instantiate_service(Socket *s) { - _cleanup_free_ char *prefix = NULL; - _cleanup_free_ char *name = NULL; +int socket_instantiate_service(Socket *s) { + _cleanup_free_ char *prefix = NULL, *name = NULL; int r; Unit *u; @@ -193,31 +196,20 @@ static int socket_instantiate_service(Socket *s) { * here. For Accept=no this is mostly a NOP since the service * is figured out at load time anyway. */ - if (UNIT_DEREF(s->service)) + if (UNIT_DEREF(s->service) || !s->accept) return 0; - assert(s->accept); - - if (!(prefix = unit_name_to_prefix(UNIT(s)->id))) + prefix = unit_name_to_prefix(UNIT(s)->id); + if (!prefix) return -ENOMEM; - r = asprintf(&name, "%s@%u.service", prefix, s->n_accepted); - - if (r < 0) + if (asprintf(&name, "%s@%u.service", prefix, s->n_accepted) < 0) return -ENOMEM; r = manager_load_unit(UNIT(s)->manager, name, NULL, NULL, &u); - if (r < 0) return r; -#ifdef HAVE_SYSV_COMPAT - if (SERVICE(u)->is_sysv) { - log_error("Using SysV services for socket activation is not supported. Refusing."); - return -ENOENT; - } -#endif - u->no_gc = true; unit_ref_set(&s->service, u); @@ -340,7 +332,7 @@ static int socket_add_extras(Socket *s) { if (r < 0) return r; - r = unit_exec_context_patch_defaults(u, &s->exec_context); + r = unit_patch_contexts(u); if (r < 0) return r; @@ -349,7 +341,7 @@ static int socket_add_extras(Socket *s) { if (r < 0) return r; - r = unit_add_default_slice(u); + r = unit_add_default_slice(u, &s->cgroup_context); if (r < 0) return r; } @@ -363,6 +355,39 @@ static int socket_add_extras(Socket *s) { return 0; } +static const char *socket_find_symlink_target(Socket *s) { + const char *found = NULL; + SocketPort *p; + + LIST_FOREACH(port, p, s->ports) { + const char *f = NULL; + + switch (p->type) { + + case SOCKET_FIFO: + f = p->path; + break; + + case SOCKET_SOCKET: + if (p->address.sockaddr.un.sun_path[0] != 0) + f = p->address.sockaddr.un.sun_path; + break; + + default: + break; + } + + if (f) { + if (found) + return NULL; + + found = f; + } + } + + return found; +} + static int socket_verify(Socket *s) { assert(s); @@ -395,6 +420,11 @@ static int socket_verify(Socket *s) { return -EINVAL; } + if (!strv_isempty(s->symlinks) && !socket_find_symlink_target(s)) { + log_error_unit(UNIT(s)->id, "%s has symlinks set but none or more than one node in the file system. Refusing.", UNIT(s)->id); + return -EINVAL; + } + return 0; } @@ -436,6 +466,7 @@ _const_ static const char* listen_lookup(int family, int type) { } static void socket_dump(Unit *u, FILE *f, const char *prefix) { + char time_string[FORMAT_TIMESPAN_MAX]; SocketExecCommand c; Socket *s = SOCKET(u); SocketPort *p; @@ -454,12 +485,15 @@ static void socket_dump(Unit *u, FILE *f, const char *prefix) { "%sSocketMode: %04o\n" "%sDirectoryMode: %04o\n" "%sKeepAlive: %s\n" + "%sNoDelay: %s\n" "%sFreeBind: %s\n" "%sTransparent: %s\n" "%sBroadcast: %s\n" "%sPassCredentials: %s\n" "%sPassSecurity: %s\n" - "%sTCPCongestion: %s\n", + "%sTCPCongestion: %s\n" + "%sRemoveOnStop: %s\n" + "%sSELinuxLabelViaNet: %s\n", prefix, socket_state_to_string(s->state), prefix, socket_result_to_string(s->result), prefix, socket_address_bind_ipv6_only_to_string(s->bind_ipv6_only), @@ -467,17 +501,20 @@ static void socket_dump(Unit *u, FILE *f, const char *prefix) { prefix, s->socket_mode, prefix, s->directory_mode, prefix, yes_no(s->keep_alive), + prefix, yes_no(s->no_delay), prefix, yes_no(s->free_bind), prefix, yes_no(s->transparent), prefix, yes_no(s->broadcast), prefix, yes_no(s->pass_cred), prefix, yes_no(s->pass_sec), - prefix, strna(s->tcp_congestion)); + prefix, strna(s->tcp_congestion), + prefix, yes_no(s->remove_on_stop), + prefix, yes_no(s->selinux_label_via_net)); if (s->control_pid > 0) fprintf(f, - "%sControl PID: %lu\n", - prefix, (unsigned long) s->control_pid); + "%sControl PID: "PID_FMT"\n", + prefix, s->control_pid); if (s->bind_to_device) fprintf(f, @@ -558,6 +595,36 @@ static void socket_dump(Unit *u, FILE *f, const char *prefix) { "%sSmackLabelIPOut: %s\n", prefix, s->smack_ip_out); + if (!isempty(s->user) || !isempty(s->group)) + fprintf(f, + "%sOwnerUser: %s\n" + "%sOwnerGroup: %s\n", + prefix, strna(s->user), + prefix, strna(s->group)); + + if(s->keep_alive_time) + fprintf(f, + "%sKeepAliveTime: %s\n", + prefix, format_timespan(time_string, FORMAT_TIMESPAN_MAX, + s->keep_alive_time, USEC_PER_SEC)); + + if(s->keep_alive_interval) + fprintf(f, + "%sKeepAliveInterval: %s\n", + prefix, format_timespan(time_string, FORMAT_TIMESPAN_MAX, + s->keep_alive_interval, USEC_PER_SEC)); + + if(s->keep_alive_cnt) + fprintf(f, + "%sKeepAliveProbes: %u\n", + prefix, s->keep_alive_cnt); + + if(s->defer_accept) + fprintf(f, + "%sDeferAccept: %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) { @@ -597,13 +664,7 @@ static void socket_dump(Unit *u, FILE *f, const char *prefix) { static int instance_from_socket(int fd, unsigned nr, char **instance) { socklen_t l; char *r; - union { - struct sockaddr sa; - struct sockaddr_un un; - struct sockaddr_in in; - struct sockaddr_in6 in6; - struct sockaddr_storage storage; - } local, remote; + union sockaddr_union local, remote; assert(fd >= 0); assert(instance); @@ -675,16 +736,22 @@ static int instance_from_socket(int fd, unsigned nr, char **instance) { int k; k = getpeercred(fd, &ucred); - if (k < 0) + if (k >= 0) { + if (asprintf(&r, + "%u-"PID_FMT"-"UID_FMT, + nr, ucred.pid, ucred.uid) < 0) + return -ENOMEM; + } else if (k == -ENODATA) { + /* This handles the case where somebody is + * connecting from another pid/uid namespace + * (e.g. from outside of our container). */ + if (asprintf(&r, + "%u-unknown", + nr) < 0) + return -ENOMEM; + } else return k; - if (asprintf(&r, - "%u-%lu-%lu", - nr, - (unsigned long) ucred.pid, - (unsigned long) ucred.uid) < 0) - return -ENOMEM; - break; } @@ -698,6 +765,7 @@ static int instance_from_socket(int fd, unsigned nr, char **instance) { static void socket_close_fds(Socket *s) { SocketPort *p; + char **i; assert(s); @@ -708,18 +776,40 @@ static void socket_close_fds(Socket *s) { if (p->fd < 0) continue; - close_nointr_nofail(p->fd); + p->fd = safe_close(p->fd); - /* One little note: we should never delete any sockets - * in the file system here! After all some other - * process we spawned might still have a reference of - * this fd and wants to continue to use it. Therefore - * we delete sockets in the file system before we - * create a new one, not after we stopped using - * one! */ + /* One little note: we should normally not delete any + * sockets in the file system here! After all some + * other process we spawned might still have a + * reference of this fd and wants to continue to use + * it. Therefore we delete sockets in the file system + * before we create a new one, not after we stopped + * using one! */ - p->fd = -1; + if (s->remove_on_stop) { + switch (p->type) { + + case SOCKET_FIFO: + unlink(p->path); + break; + + case SOCKET_MQUEUE: + mq_unlink(p->path); + break; + + case SOCKET_SOCKET: + socket_address_unlink(&p->address); + break; + + default: + break; + } + } } + + if (s->remove_on_stop) + STRV_FOREACH(i, s->symlinks) + unlink(*i); } static void socket_apply_socket_options(Socket *s, int fd) { @@ -732,6 +822,36 @@ static void socket_apply_socket_options(Socket *s, int fd) { log_warning_unit(UNIT(s)->id, "SO_KEEPALIVE failed: %m"); } + if (s->keep_alive_time) { + int value = s->keep_alive_time / USEC_PER_SEC; + if (setsockopt(fd, SOL_TCP, TCP_KEEPIDLE, &value, sizeof(value)) < 0) + log_warning_unit(UNIT(s)->id, "TCP_KEEPIDLE failed: %m"); + } + + if (s->keep_alive_interval) { + int value = s->keep_alive_interval / USEC_PER_SEC; + if (setsockopt(fd, SOL_TCP, TCP_KEEPINTVL, &value, sizeof(value)) < 0) + log_warning_unit(UNIT(s)->id, "TCP_KEEPINTVL failed: %m"); + } + + if (s->keep_alive_cnt) { + int value = s->keep_alive_cnt; + if (setsockopt(fd, SOL_SOCKET, TCP_KEEPCNT, &value, sizeof(value)) < 0) + log_warning_unit(UNIT(s)->id, "TCP_KEEPCNT failed: %m"); + } + + if (s->defer_accept) { + int value = s->defer_accept / USEC_PER_SEC; + if (setsockopt(fd, SOL_TCP, TCP_DEFER_ACCEPT, &value, sizeof(value)) < 0) + log_warning_unit(UNIT(s)->id, "TCP_DEFER_ACCEPT failed: %m"); + } + + if (s->no_delay) { + int b = s->no_delay; + if (setsockopt(fd, SOL_TCP, TCP_NODELAY, &b, sizeof(b)) < 0) + log_warning_unit(UNIT(s)->id, "TCP_NODELAY failed: %m"); + } + if (s->broadcast) { int one = 1; if (setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &one, sizeof(one)) < 0) @@ -888,9 +1008,7 @@ static int fifo_address_create( fail: label_context_clear(); - - if (fd >= 0) - close_nointr_nofail(fd); + safe_close(fd); return r; } @@ -905,7 +1023,8 @@ static int special_address_create( assert(path); assert(_fd); - if ((fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NONBLOCK|O_NOFOLLOW)) < 0) { + fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NONBLOCK|O_NOFOLLOW); + if (fd < 0) { r = -errno; goto fail; } @@ -925,8 +1044,7 @@ static int special_address_create( return 0; fail: - if (fd >= 0) - close_nointr_nofail(fd); + safe_close(fd); return r; } @@ -959,7 +1077,6 @@ static int mq_address_create( /* Include the original umask in our mask */ umask(~mq_mode | old_mask); - fd = mq_open(path, O_RDONLY|O_CLOEXEC|O_NONBLOCK|O_CREAT, mq_mode, attr); umask(old_mask); @@ -985,12 +1102,26 @@ static int mq_address_create( return 0; fail: - if (fd >= 0) - close_nointr_nofail(fd); - + safe_close(fd); return r; } +static int socket_symlink(Socket *s) { + const char *p; + char **i; + + assert(s); + + p = socket_find_symlink_target(s); + if (!p) + return 0; + + STRV_FOREACH(i, s->symlinks) + symlink(p, *i); + + return 0; +} + static int socket_open_fds(Socket *s) { SocketPort *p; int r; @@ -1005,20 +1136,25 @@ static int socket_open_fds(Socket *s) { continue; if (p->type == SOCKET_SOCKET) { - +#ifdef HAVE_SELINUX + if (!know_label && s->selinux_label_via_net) { + r = getcon(&label); + if (r < 0) + return r; + know_label = true; + } +#endif if (!know_label) { - if ((r = socket_instantiate_service(s)) < 0) + r = socket_instantiate_service(s); + if (r < 0) return r; if (UNIT_ISSET(s->service) && SERVICE(UNIT_DEREF(s->service))->exec_command[SERVICE_EXEC_START]) { r = label_get_create_label_from_exe(SERVICE(UNIT_DEREF(s->service))->exec_command[SERVICE_EXEC_START]->path, &label); - - if (r < 0) { - if (r != -EPERM) - return r; - } + if (r < 0 && r != -EPERM) + return r; } know_label = true; @@ -1040,6 +1176,7 @@ static int socket_open_fds(Socket *s) { p->fd = r; socket_apply_socket_options(s, p->fd); + socket_symlink(s); } else if (p->type == SOCKET_SPECIAL) { @@ -1060,6 +1197,8 @@ static int socket_open_fds(Socket *s) { goto rollback; socket_apply_fifo_options(s, p->fd); + socket_symlink(s); + } else if (p->type == SOCKET_MQUEUE) { r = mq_address_create( @@ -1093,11 +1232,12 @@ static void socket_unwatch_fds(Socket *s) { if (p->fd < 0) continue; - if (p->event_source) { - r = sd_event_source_set_enabled(p->event_source, SD_EVENT_OFF); - if (r < 0) - log_debug_unit(UNIT(s)->id, "Failed to disable event source."); - } + if (!p->event_source) + continue; + + r = sd_event_source_set_enabled(p->event_source, SD_EVENT_OFF); + if (r < 0) + log_debug_unit(UNIT(s)->id, "Failed to disable event source."); } } @@ -1136,14 +1276,16 @@ static void socket_set_state(Socket *s, SocketState state) { old_state = s->state; s->state = state; - if (state != SOCKET_START_PRE && - state != SOCKET_START_POST && - state != SOCKET_STOP_PRE && - state != SOCKET_STOP_PRE_SIGTERM && - state != SOCKET_STOP_PRE_SIGKILL && - state != SOCKET_STOP_POST && - state != SOCKET_FINAL_SIGTERM && - state != SOCKET_FINAL_SIGKILL) { + if (!IN_SET(state, + SOCKET_START_PRE, + SOCKET_START_CHOWN, + SOCKET_START_POST, + SOCKET_STOP_PRE, + SOCKET_STOP_PRE_SIGTERM, + SOCKET_STOP_PRE_SIGKILL, + SOCKET_STOP_POST, + SOCKET_FINAL_SIGTERM, + SOCKET_FINAL_SIGKILL)) { s->timer_event_source = sd_event_source_unref(s->timer_event_source); socket_unwatch_control_pid(s); @@ -1154,12 +1296,14 @@ static void socket_set_state(Socket *s, SocketState state) { if (state != SOCKET_LISTENING) socket_unwatch_fds(s); - if (state != SOCKET_START_POST && - state != SOCKET_LISTENING && - state != SOCKET_RUNNING && - state != SOCKET_STOP_PRE && - state != SOCKET_STOP_PRE_SIGTERM && - state != SOCKET_STOP_PRE_SIGKILL) + if (!IN_SET(state, + SOCKET_START_CHOWN, + SOCKET_START_POST, + SOCKET_LISTENING, + SOCKET_RUNNING, + SOCKET_STOP_PRE, + SOCKET_STOP_PRE_SIGTERM, + SOCKET_STOP_PRE_SIGKILL)) socket_close_fds(s); if (state != old_state) @@ -1179,14 +1323,16 @@ static int socket_coldplug(Unit *u) { if (s->deserialized_state == s->state) return 0; - if (s->deserialized_state == SOCKET_START_PRE || - s->deserialized_state == SOCKET_START_POST || - s->deserialized_state == SOCKET_STOP_PRE || - s->deserialized_state == SOCKET_STOP_PRE_SIGTERM || - s->deserialized_state == SOCKET_STOP_PRE_SIGKILL || - s->deserialized_state == SOCKET_STOP_POST || - s->deserialized_state == SOCKET_FINAL_SIGTERM || - s->deserialized_state == SOCKET_FINAL_SIGKILL) { + if (IN_SET(s->deserialized_state, + SOCKET_START_PRE, + SOCKET_START_CHOWN, + SOCKET_START_POST, + SOCKET_STOP_PRE, + SOCKET_STOP_PRE_SIGTERM, + SOCKET_STOP_PRE_SIGKILL, + SOCKET_STOP_POST, + SOCKET_FINAL_SIGTERM, + SOCKET_FINAL_SIGKILL)) { if (s->control_pid <= 0) return -EBADMSG; @@ -1200,12 +1346,14 @@ static int socket_coldplug(Unit *u) { return r; } - if (s->deserialized_state == SOCKET_START_POST || - s->deserialized_state == SOCKET_LISTENING || - s->deserialized_state == SOCKET_RUNNING || - s->deserialized_state == SOCKET_STOP_PRE || - s->deserialized_state == SOCKET_STOP_PRE_SIGTERM || - s->deserialized_state == SOCKET_STOP_PRE_SIGKILL) { + if (IN_SET(s->deserialized_state, + SOCKET_START_CHOWN, + SOCKET_START_POST, + SOCKET_LISTENING, + SOCKET_RUNNING, + SOCKET_STOP_PRE, + SOCKET_STOP_PRE_SIGTERM, + SOCKET_STOP_PRE_SIGKILL)) { r = socket_open_fds(s); if (r < 0) return r; @@ -1222,9 +1370,9 @@ static int socket_coldplug(Unit *u) { } static int socket_spawn(Socket *s, ExecCommand *c, pid_t *_pid) { + _cleanup_free_ char **argv = NULL; pid_t pid; int r; - char **argv; assert(s); assert(c); @@ -1261,8 +1409,6 @@ static int socket_spawn(Socket *s, ExecCommand *c, pid_t *_pid) { NULL, s->exec_runtime, &pid); - - strv_free(argv); if (r < 0) goto fail; @@ -1272,12 +1418,94 @@ static int socket_spawn(Socket *s, ExecCommand *c, pid_t *_pid) { goto fail; *_pid = pid; - return 0; fail: s->timer_event_source = sd_event_source_unref(s->timer_event_source); + return r; +} + +static int socket_chown(Socket *s, pid_t *_pid) { + pid_t pid; + int r; + r = socket_arm_timer(s); + if (r < 0) + goto fail; + + /* We have to resolve the user names out-of-process, hence + * let's fork here. It's messy, but well, what can we do? */ + + pid = fork(); + if (pid < 0) + return -errno; + + if (pid == 0) { + SocketPort *p; + uid_t uid = (uid_t) -1; + gid_t gid = (gid_t) -1; + int ret; + + default_signals(SIGNALS_CRASH_HANDLER, SIGNALS_IGNORE, -1); + ignore_signals(SIGPIPE, -1); + log_forget_fds(); + + if (!isempty(s->user)) { + const char *user = s->user; + + r = get_user_creds(&user, &uid, &gid, NULL, NULL); + if (r < 0) { + ret = EXIT_USER; + goto fail_child; + } + } + + if (!isempty(s->group)) { + const char *group = s->group; + + r = get_group_creds(&group, &gid); + if (r < 0) { + ret = EXIT_GROUP; + goto fail_child; + } + } + + LIST_FOREACH(port, p, s->ports) { + const char *path = NULL; + + if (p->type == SOCKET_SOCKET) + path = socket_address_get_path(&p->address); + else if (p->type == SOCKET_FIFO) + path = p->path; + + if (!path) + continue; + + if (chown(path, uid, gid) < 0) { + r = -errno; + ret = EXIT_CHOWN; + goto fail_child; + } + } + + _exit(0); + + fail_child: + log_open(); + log_error("Failed to chown socket at step %s: %s", exit_status_to_string(ret, EXIT_STATUS_SYSTEMD), strerror(-r)); + + _exit(ret); + } + + r = unit_watch_pid(UNIT(s), pid); + if (r < 0) + goto fail; + + *_pid = pid; + return 0; + +fail: + s->timer_event_source = sd_event_source_unref(s->timer_event_source); return r; } @@ -1305,11 +1533,12 @@ static void socket_enter_stop_post(Socket *s, SocketResult f) { s->result = f; socket_unwatch_control_pid(s); - s->control_command_id = SOCKET_EXEC_STOP_POST; + s->control_command = s->exec_command[SOCKET_EXEC_STOP_POST]; - if ((s->control_command = s->exec_command[SOCKET_EXEC_STOP_POST])) { - if ((r = socket_spawn(s, s->control_command, &s->control_pid)) < 0) + if (s->control_command) { + r = socket_spawn(s, s->control_command, &s->control_pid); + if (r < 0) goto fail; socket_set_state(s, SOCKET_STOP_POST); @@ -1377,11 +1606,12 @@ static void socket_enter_stop_pre(Socket *s, SocketResult f) { s->result = f; socket_unwatch_control_pid(s); - s->control_command_id = SOCKET_EXEC_STOP_PRE; + s->control_command = s->exec_command[SOCKET_EXEC_STOP_PRE]; - if ((s->control_command = s->exec_command[SOCKET_EXEC_STOP_PRE])) { - if ((r = socket_spawn(s, s->control_command, &s->control_pid)) < 0) + if (s->control_command) { + r = socket_spawn(s, s->control_command, &s->control_pid); + if (r < 0) goto fail; socket_set_state(s, SOCKET_STOP_PRE); @@ -1416,26 +1646,53 @@ static void socket_enter_start_post(Socket *s) { int r; assert(s); + socket_unwatch_control_pid(s); + s->control_command_id = SOCKET_EXEC_START_POST; + s->control_command = s->exec_command[SOCKET_EXEC_START_POST]; + + if (s->control_command) { + r = socket_spawn(s, s->control_command, &s->control_pid); + if (r < 0) { + log_warning_unit(UNIT(s)->id, "%s failed to run 'start-post' task: %s", UNIT(s)->id, strerror(-r)); + goto fail; + } + + 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) { + int r; + + assert(s); + r = socket_open_fds(s); if (r < 0) { log_warning_unit(UNIT(s)->id, "%s failed to listen on sockets: %s", UNIT(s)->id, strerror(-r)); goto fail; } - socket_unwatch_control_pid(s); + if (!isempty(s->user) || !isempty(s->group)) { - s->control_command_id = SOCKET_EXEC_START_POST; + socket_unwatch_control_pid(s); + s->control_command_id = SOCKET_EXEC_START_CHOWN; + s->control_command = NULL; - if ((s->control_command = s->exec_command[SOCKET_EXEC_START_POST])) { - r = socket_spawn(s, s->control_command, &s->control_pid); + r = socket_chown(s, &s->control_pid); if (r < 0) { - log_warning_unit(UNIT(s)->id, "%s failed to run 'start-post' task: %s", UNIT(s)->id, strerror(-r)); + log_warning_unit(UNIT(s)->id, "%s failed to fork 'start-chown' task: %s", UNIT(s)->id, strerror(-r)); goto fail; } - socket_set_state(s, SOCKET_START_POST); + socket_set_state(s, SOCKET_START_CHOWN); } else - socket_enter_listening(s); + socket_enter_start_post(s); return; @@ -1448,22 +1705,23 @@ static void socket_enter_start_pre(Socket *s) { assert(s); socket_unwatch_control_pid(s); - s->control_command_id = SOCKET_EXEC_START_PRE; + s->control_command = s->exec_command[SOCKET_EXEC_START_PRE]; - if ((s->control_command = s->exec_command[SOCKET_EXEC_START_PRE])) { + if (s->control_command) { r = socket_spawn(s, s->control_command, &s->control_pid); - if (r < 0) + if (r < 0) { + log_warning_unit(UNIT(s)->id, "%s failed to run 'start-pre' task: %s", UNIT(s)->id, strerror(-r)); goto fail; + } socket_set_state(s, SOCKET_START_PRE); } else - socket_enter_start_post(s); + socket_enter_start_chown(s); return; fail: - log_warning_unit(UNIT(s)->id, "%s failed to run 'start-pre' task: %s", UNIT(s)->id, strerror(-r)); socket_enter_dead(s, SOCKET_FAILURE_RESOURCES); } @@ -1480,7 +1738,7 @@ static void socket_enter_running(Socket *s, int cfd) { log_debug_unit(UNIT(s)->id, "Suppressing connection request on %s since unit stop is scheduled.", UNIT(s)->id); if (cfd >= 0) - close_nointr_nofail(cfd); + safe_close(cfd); else { /* Flush all sockets by closing and reopening them */ socket_close_fds(s); @@ -1516,6 +1774,12 @@ static void socket_enter_running(Socket *s, int cfd) { } if (!pending) { + if (!UNIT_ISSET(s->service)) { + log_error_unit(UNIT(s)->id, "%s: service to activate vanished, refusing activation.", UNIT(s)->id); + r = -ENOENT; + goto fail; + } + r = manager_add_job(UNIT(s)->manager, JOB_START, UNIT_DEREF(s->service), JOB_REPLACE, true, &error, NULL); if (r < 0) goto fail; @@ -1528,7 +1792,7 @@ static void socket_enter_running(Socket *s, int cfd) { if (s->n_connections >= s->max_connections) { log_warning_unit(UNIT(s)->id, "%s: Too many incoming connections (%u)", UNIT(s)->id, s->n_connections); - close_nointr_nofail(cfd); + safe_close(cfd); return; } @@ -1543,7 +1807,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. */ - close_nointr_nofail(cfd); + safe_close(cfd); return; } @@ -1578,6 +1842,9 @@ static void socket_enter_running(Socket *s, int cfd) { cfd = -1; s->n_connections ++; + if (s->selinux_label_via_net) + service->exec_context.selinux_label_via_net = true; + r = manager_add_job(UNIT(s)->manager, JOB_START, UNIT(service), JOB_REPLACE, true, &error, NULL); if (r < 0) goto fail; @@ -1594,9 +1861,7 @@ fail: bus_error_message(&error, r)); socket_enter_stop_pre(s, SOCKET_FAILURE_RESOURCES); - - if (cfd >= 0) - close_nointr_nofail(cfd); + safe_close(cfd); } static void socket_run_next(Socket *s) { @@ -1634,16 +1899,20 @@ static int socket_start(Unit *u) { /* We cannot fulfill this request right now, try again later * please! */ - if (s->state == SOCKET_STOP_PRE || - s->state == SOCKET_STOP_PRE_SIGKILL || - s->state == SOCKET_STOP_PRE_SIGTERM || - s->state == SOCKET_STOP_POST || - s->state == SOCKET_FINAL_SIGTERM || - s->state == SOCKET_FINAL_SIGKILL) + if (IN_SET(s->state, + SOCKET_STOP_PRE, + SOCKET_STOP_PRE_SIGKILL, + SOCKET_STOP_PRE_SIGTERM, + SOCKET_STOP_POST, + SOCKET_FINAL_SIGTERM, + SOCKET_FINAL_SIGKILL)) return -EAGAIN; - if (s->state == SOCKET_START_PRE || - s->state == SOCKET_START_POST) + /* Already on it! */ + if (IN_SET(s->state, + SOCKET_START_PRE, + SOCKET_START_CHOWN, + SOCKET_START_POST)) return 0; /* Cannot run this without the service being around */ @@ -1665,14 +1934,6 @@ static int socket_start(Unit *u) { log_error_unit(u->id, "Socket service %s already active, refusing.", UNIT(service)->id); return -EBUSY; } - -#ifdef HAVE_SYSV_COMPAT - if (service->is_sysv) { - log_error_unit(u->id, - "Using SysV services for socket activation is not supported. Refusing."); - return -ENOENT; - } -#endif } assert(s->state == SOCKET_DEAD || s->state == SOCKET_FAILED); @@ -1689,18 +1950,21 @@ static int socket_stop(Unit *u) { assert(s); /* Already on it */ - if (s->state == SOCKET_STOP_PRE || - s->state == SOCKET_STOP_PRE_SIGTERM || - s->state == SOCKET_STOP_PRE_SIGKILL || - s->state == SOCKET_STOP_POST || - s->state == SOCKET_FINAL_SIGTERM || - s->state == SOCKET_FINAL_SIGKILL) + if (IN_SET(s->state, + SOCKET_STOP_PRE, + SOCKET_STOP_PRE_SIGTERM, + SOCKET_STOP_PRE_SIGKILL, + SOCKET_STOP_POST, + SOCKET_FINAL_SIGTERM, + SOCKET_FINAL_SIGKILL)) return 0; /* If there's already something running we go directly into * kill mode. */ - if (s->state == SOCKET_START_PRE || - s->state == SOCKET_START_POST) { + if (IN_SET(s->state, + SOCKET_START_PRE, + SOCKET_START_CHOWN, + SOCKET_START_POST)) { socket_enter_signal(s, SOCKET_STOP_PRE_SIGTERM, SOCKET_SUCCESS); return -EAGAIN; } @@ -1725,7 +1989,7 @@ static int socket_serialize(Unit *u, FILE *f, FDSet *fds) { unit_serialize_item_format(u, f, "n-accepted", "%u", s->n_accepted); if (s->control_pid > 0) - unit_serialize_item_format(u, f, "control-pid", "%lu", (unsigned long) s->control_pid); + unit_serialize_item_format(u, f, "control-pid", PID_FMT, s->control_pid); if (s->control_command_id >= 0) unit_serialize_item(u, f, "control-command", socket_exec_command_to_string(s->control_command_id)); @@ -1827,8 +2091,7 @@ static int socket_deserialize_item(Unit *u, const char *key, const char *value, break; if (p) { - if (p->fd >= 0) - close_nointr_nofail(p->fd); + safe_close(p->fd); p->fd = fdset_remove(fds, fd); } } @@ -1847,8 +2110,7 @@ static int socket_deserialize_item(Unit *u, const char *key, const char *value, break; if (p) { - if (p->fd >= 0) - close_nointr_nofail(p->fd); + safe_close(p->fd); p->fd = fdset_remove(fds, fd); } } @@ -1867,8 +2129,7 @@ static int socket_deserialize_item(Unit *u, const char *key, const char *value, break; if (p) { - if (p->fd >= 0) - close_nointr_nofail(p->fd); + safe_close(p->fd); p->fd = fdset_remove(fds, fd); } } @@ -1886,8 +2147,7 @@ static int socket_deserialize_item(Unit *u, const char *key, const char *value, break; if (p) { - if (p->fd >= 0) - close_nointr_nofail(p->fd); + safe_close(p->fd); p->fd = fdset_remove(fds, fd); } } @@ -1905,8 +2165,7 @@ static int socket_deserialize_item(Unit *u, const char *key, const char *value, break; if (p) { - if (p->fd >= 0) - close_nointr_nofail(p->fd); + safe_close(p->fd); p->fd = fdset_remove(fds, fd); } } @@ -2081,7 +2340,7 @@ static void socket_sigchld_event(Unit *u, pid_t pid, int code, int status) { else if (code == CLD_DUMPED) f = SOCKET_FAILURE_CORE_DUMP; else - assert_not_reached("Unknown code"); + assert_not_reached("Unknown sigchld code"); if (s->control_command) { exec_status_exit(&s->control_command->exec_status, &s->exec_context, pid, code, status); @@ -2121,11 +2380,18 @@ static void socket_sigchld_event(Unit *u, pid_t pid, int code, int status) { case SOCKET_START_PRE: if (f == SOCKET_SUCCESS) - socket_enter_start_post(s); + socket_enter_start_chown(s); else socket_enter_signal(s, SOCKET_FINAL_SIGTERM, f); break; + case SOCKET_START_CHOWN: + if (f == SOCKET_SUCCESS) + socket_enter_start_post(s); + else + socket_enter_stop_pre(s, f); + break; + case SOCKET_START_POST: if (f == SOCKET_SUCCESS) socket_enter_listening(s); @@ -2163,65 +2429,53 @@ static int socket_dispatch_timer(sd_event_source *source, usec_t usec, void *use switch (s->state) { case SOCKET_START_PRE: - log_warning_unit(UNIT(s)->id, - "%s starting timed out. Terminating.", UNIT(s)->id); + log_warning_unit(UNIT(s)->id, "%s starting timed out. Terminating.", UNIT(s)->id); socket_enter_signal(s, SOCKET_FINAL_SIGTERM, SOCKET_FAILURE_TIMEOUT); break; + case SOCKET_START_CHOWN: case SOCKET_START_POST: - log_warning_unit(UNIT(s)->id, - "%s starting timed out. Stopping.", UNIT(s)->id); + log_warning_unit(UNIT(s)->id, "%s starting timed out. Stopping.", UNIT(s)->id); socket_enter_stop_pre(s, SOCKET_FAILURE_TIMEOUT); break; case SOCKET_STOP_PRE: - log_warning_unit(UNIT(s)->id, - "%s stopping timed out. Terminating.", UNIT(s)->id); + log_warning_unit(UNIT(s)->id, "%s stopping timed out. Terminating.", UNIT(s)->id); socket_enter_signal(s, SOCKET_STOP_PRE_SIGTERM, SOCKET_FAILURE_TIMEOUT); break; case SOCKET_STOP_PRE_SIGTERM: if (s->kill_context.send_sigkill) { - log_warning_unit(UNIT(s)->id, - "%s stopping timed out. Killing.", UNIT(s)->id); + log_warning_unit(UNIT(s)->id, "%s stopping timed out. Killing.", UNIT(s)->id); socket_enter_signal(s, SOCKET_STOP_PRE_SIGKILL, SOCKET_FAILURE_TIMEOUT); } else { - log_warning_unit(UNIT(s)->id, - "%s stopping timed out. Skipping SIGKILL. Ignoring.", - UNIT(s)->id); + log_warning_unit(UNIT(s)->id, "%s stopping timed out. Skipping SIGKILL. Ignoring.", UNIT(s)->id); socket_enter_stop_post(s, SOCKET_FAILURE_TIMEOUT); } break; case SOCKET_STOP_PRE_SIGKILL: - log_warning_unit(UNIT(s)->id, - "%s still around after SIGKILL. Ignoring.", UNIT(s)->id); + log_warning_unit(UNIT(s)->id, "%s still around after SIGKILL. Ignoring.", UNIT(s)->id); socket_enter_stop_post(s, SOCKET_FAILURE_TIMEOUT); break; case SOCKET_STOP_POST: - log_warning_unit(UNIT(s)->id, - "%s stopping timed out (2). Terminating.", UNIT(s)->id); + log_warning_unit(UNIT(s)->id, "%s stopping timed out (2). Terminating.", UNIT(s)->id); socket_enter_signal(s, SOCKET_FINAL_SIGTERM, SOCKET_FAILURE_TIMEOUT); break; case SOCKET_FINAL_SIGTERM: if (s->kill_context.send_sigkill) { - log_warning_unit(UNIT(s)->id, - "%s stopping timed out (2). Killing.", UNIT(s)->id); + log_warning_unit(UNIT(s)->id, "%s stopping timed out (2). Killing.", UNIT(s)->id); socket_enter_signal(s, SOCKET_FINAL_SIGKILL, SOCKET_FAILURE_TIMEOUT); } else { - log_warning_unit(UNIT(s)->id, - "%s stopping timed out (2). Skipping SIGKILL. Ignoring.", - UNIT(s)->id); + log_warning_unit(UNIT(s)->id, "%s stopping timed out (2). Skipping SIGKILL. Ignoring.", UNIT(s)->id); socket_enter_dead(s, SOCKET_FAILURE_TIMEOUT); } break; case SOCKET_FINAL_SIGKILL: - log_warning_unit(UNIT(s)->id, - "%s still around after SIGKILL (2). Entering failed mode.", - UNIT(s)->id); + log_warning_unit(UNIT(s)->id, "%s still around after SIGKILL (2). Entering failed mode.", UNIT(s)->id); socket_enter_dead(s, SOCKET_FAILURE_TIMEOUT); break; @@ -2370,6 +2624,7 @@ static int socket_get_timeout(Unit *u, uint64_t *timeout) { static const char* const socket_state_table[_SOCKET_STATE_MAX] = { [SOCKET_DEAD] = "dead", [SOCKET_START_PRE] = "start-pre", + [SOCKET_START_CHOWN] = "start-chown", [SOCKET_START_POST] = "start-post", [SOCKET_LISTENING] = "listening", [SOCKET_RUNNING] = "running", @@ -2386,6 +2641,7 @@ DEFINE_STRING_TABLE_LOOKUP(socket_state, SocketState); static const char* const socket_exec_command_table[_SOCKET_EXEC_COMMAND_MAX] = { [SOCKET_EXEC_START_PRE] = "StartPre", + [SOCKET_EXEC_START_CHOWN] = "StartChown", [SOCKET_EXEC_START_POST] = "StartPost", [SOCKET_EXEC_STOP_PRE] = "StopPre", [SOCKET_EXEC_STOP_POST] = "StopPost"