diff options
-rw-r--r-- | src/analyze/analyze-verify.c | 24 | ||||
-rw-r--r-- | src/core/socket.c | 142 | ||||
-rw-r--r-- | src/core/socket.h | 2 |
3 files changed, 82 insertions, 86 deletions
diff --git a/src/analyze/analyze-verify.c b/src/analyze/analyze-verify.c index 8275360adc..30cb79d509 100644 --- a/src/analyze/analyze-verify.c +++ b/src/analyze/analyze-verify.c @@ -94,6 +94,7 @@ static int generate_path(char **var, char **filenames) { } static int verify_socket(Unit *u) { + Unit *service; int r; assert(u); @@ -101,26 +102,15 @@ static int verify_socket(Unit *u) { if (u->type != UNIT_SOCKET) return 0; - /* Cannot run this without the service being around */ - - /* This makes sure instance is created if necessary. */ - r = socket_instantiate_service(SOCKET(u)); + r = socket_load_service_unit(SOCKET(u), -1, &service); if (r < 0) - return log_unit_error_errno(u, r, "Socket cannot be started, failed to create instance: %m"); - - /* This checks both type of sockets */ - if (UNIT_ISSET(SOCKET(u)->service)) { - Service *service; - - service = SERVICE(UNIT_DEREF(SOCKET(u)->service)); - log_unit_debug(u, "Using %s", UNIT(service)->id); + return log_unit_error_errno(u, r, "service unit for the socket cannot be loaded: %m"); - if (UNIT(service)->load_state != UNIT_LOADED) { - log_unit_error(u, "Service %s not loaded, %s cannot be started.", UNIT(service)->id, u->id); - return -ENOENT; - } - } + if (service->load_state != UNIT_LOADED) + return log_unit_error_errno(u, SYNTHETIC_ERRNO(ENOENT), + "service %s not loaded, socket cannot be started.", service->id); + log_unit_debug(u, "using service unit %s.", service->id); return 0; } diff --git a/src/core/socket.c b/src/core/socket.c index 218b4b245d..b9ccde8970 100644 --- a/src/core/socket.c +++ b/src/core/socket.c @@ -205,38 +205,25 @@ static int socket_arm_timer(Socket *s, usec_t usec) { return 0; } -int socket_instantiate_service(Socket *s) { - _cleanup_free_ char *prefix = NULL, *name = NULL; +static int socket_instantiate_service(Socket *s, int cfd) { + Unit *service; int r; - Unit *u; assert(s); + assert(cfd >= 0); - /* This fills in s->service if it isn't filled in yet. For - * Accept=yes sockets we create the next connection service - * here. For Accept=no this is mostly a NOP since the service - * is figured out at load time anyway. */ + /* This fills in s->service if it isn't filled in yet. For Accept=yes sockets we create the next + * connection service here. For Accept=no this is mostly a NOP since the service is figured out at + * load time anyway. */ - if (UNIT_DEREF(s->service)) - return 0; - - if (!s->accept) - return 0; - - r = unit_name_to_prefix(UNIT(s)->id, &prefix); - if (r < 0) - return r; - - if (asprintf(&name, "%s@%u.service", prefix, s->n_accepted) < 0) - return -ENOMEM; - - r = manager_load_unit(UNIT(s)->manager, name, NULL, NULL, &u); + r = socket_load_service_unit(s, cfd, &service); if (r < 0) return r; - unit_ref_set(&s->service, UNIT(s), u); + unit_ref_set(&s->service, UNIT(s), service); - return unit_add_two_dependencies(UNIT(s), UNIT_BEFORE, UNIT_TRIGGERS, u, false, UNIT_DEPENDENCY_IMPLICIT); + return unit_add_two_dependencies(UNIT(s), UNIT_BEFORE, UNIT_TRIGGERS, service, + false, UNIT_DEPENDENCY_IMPLICIT); } static bool have_non_accept_socket(Socket *s) { @@ -1398,37 +1385,81 @@ clear: return r; } +int socket_load_service_unit(Socket *s, int cfd, Unit **ret) { + /* Figure out what the unit that will be used to handle the connections on the socket looks like. + * + * If cfd < 0, then we don't have a connection yet. In case of Accept=yes sockets, use a fake + * instance name. + */ + + if (UNIT_ISSET(s->service)) { + *ret = UNIT_DEREF(s->service); + return 0; + } + + if (!s->accept) + return -ENODATA; + + /* Build the instance name and load the unit */ + _cleanup_free_ char *prefix = NULL, *instance = NULL, *name = NULL; + int r; + + r = unit_name_to_prefix(UNIT(s)->id, &prefix); + if (r < 0) + return r; + + if (cfd >= 0) { + r = instance_from_socket(cfd, s->n_accepted, &instance); + if (r == -ENOTCONN) + /* ENOTCONN is legitimate if TCP RST was received. + * This connection is over, but the socket unit lives on. */ + return log_unit_debug_errno(UNIT(s), r, + "Got ENOTCONN on incoming socket, assuming aborted connection attempt, ignoring."); + if (r < 0) + return r; + } + + /* For accepting sockets, we don't know how the instance will be called until we get a connection and + * can figure out what the peer name is. So let's use "internal" as the instance to make it clear + * that this is not an actual peer name. We use "unknown" when we cannot figure out the peer. */ + r = unit_name_build(prefix, instance ?: "internal", ".service", &name); + if (r < 0) + return r; + + return manager_load_unit(UNIT(s)->manager, name, NULL, NULL, ret); +} + static int socket_determine_selinux_label(Socket *s, char **ret) { - Service *service; - ExecCommand *c; - _cleanup_free_ char *path = NULL; int r; assert(s); assert(ret); if (s->selinux_context_from_net) { - /* If this is requested, get label from the network label */ + /* If this is requested, get the label from the network label */ r = mac_selinux_get_our_label(ret); if (r == -EOPNOTSUPP) goto no_label; } else { - /* Otherwise, get it from the executable we are about to start */ - r = socket_instantiate_service(s); - if (r < 0) - return r; + /* Otherwise, get it from the executable we are about to start. */ + + Unit *service; + ExecCommand *c; + _cleanup_free_ char *path = NULL; - if (!UNIT_ISSET(s->service)) + r = socket_load_service_unit(s, -1, &service); + if (r == -ENODATA) goto no_label; + if (r < 0) + return r; - service = SERVICE(UNIT_DEREF(s->service)); - c = service->exec_command[SERVICE_EXEC_START]; + c = SERVICE(service)->exec_command[SERVICE_EXEC_START]; if (!c) goto no_label; - r = chase_symlinks(c->path, service->exec_context.root_directory, CHASE_PREFIX_ROOT, &path, NULL); + r = chase_symlinks(c->path, SERVICE(service)->exec_context.root_directory, CHASE_PREFIX_ROOT, &path, NULL); if (r < 0) goto no_label; @@ -1614,8 +1645,8 @@ static int socket_open_fds(Socket *_s) { case SOCKET_SOCKET: if (!know_label) { - /* Figure out label, if we don't it know yet. We do it once, for the first socket where - * we need this and remember it for the rest. */ + /* Figure out the label, if we don't it know yet. We do it once for the first + * socket where we need this and remember it for the rest. */ r = socket_determine_selinux_label(s, &label); if (r < 0) @@ -2332,7 +2363,6 @@ 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; @@ -2344,9 +2374,9 @@ static void socket_enter_running(Socket *s, int cfd) { if (s->max_connections_per_source > 0) { r = socket_acquire_peer(s, cfd, &p); - if (r < 0) { + if (r < 0) goto refuse; - } else if (r > 0 && p->n_ref > s->max_connections_per_source) { + if (r > 0 && p->n_ref > s->max_connections_per_source) { _cleanup_free_ char *t = NULL; (void) sockaddr_pretty(&p->peer.sa, p->peer_salen, true, false, &t); @@ -2358,30 +2388,7 @@ static void socket_enter_running(Socket *s, int cfd) { } } - r = socket_instantiate_service(s); - if (r < 0) - goto fail; - - r = instance_from_socket(cfd, s->n_accepted, &instance); - if (r < 0) { - if (r != -ENOTCONN) - goto fail; - - /* 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."); - goto refuse; - } - - r = unit_name_to_prefix(UNIT(s)->id, &prefix); - if (r < 0) - goto fail; - - r = unit_name_build(prefix, instance, ".service", &name); - if (r < 0) - goto fail; - - r = unit_add_name(UNIT_DEREF(s->service), name); + r = socket_instantiate_service(s, cfd); if (r < 0) goto fail; @@ -2389,21 +2396,20 @@ static void socket_enter_running(Socket *s, int cfd) { unit_ref_unset(&s->service); s->n_accepted++; - unit_choose_id(UNIT(service), name); r = service_set_socket_fd(service, cfd, s, s->selinux_context_from_net); if (r < 0) goto fail; - cfd = -1; /* We passed ownership of the fd to the service now. Forget it here. */ + TAKE_FD(cfd); /* We passed ownership of the fd to the service now. Forget it here. */ s->n_connections++; service->peer = TAKE_PTR(p); /* Pass ownership of the peer reference */ r = manager_add_job(UNIT(s)->manager, JOB_START, UNIT(service), JOB_REPLACE, NULL, &error, NULL); if (r < 0) { - /* We failed to activate the new service, but it still exists. Let's make sure the service - * closes and forgets the connection fd again, immediately. */ + /* We failed to activate the new service, but it still exists. Let's make sure the + * service closes and forgets the connection fd again, immediately. */ service_close_socket_fd(service); goto fail; } diff --git a/src/core/socket.h b/src/core/socket.h index 9e0be15ba8..848eeb9c67 100644 --- a/src/core/socket.h +++ b/src/core/socket.h @@ -165,7 +165,7 @@ void socket_connection_unref(Socket *s); void socket_free_ports(Socket *s); -int socket_instantiate_service(Socket *s); +int socket_load_service_unit(Socket *s, int cfd, Unit **ret); char *socket_fdname(Socket *s); |