diff --git a/TODO b/TODO index f2d515ad78..3b3fe2e323 100644 --- a/TODO +++ b/TODO @@ -125,9 +125,6 @@ Deprecations and removals: Features: -* in pid1, name Accept=yes service instances after the socket cookie. Also, set - $SO_COOKIE env var or so for executed services. - * loginctl: show argv[] of "leader" process in tabular list-sessions output * loginctl: show "service identifier" in tabular list-sessions output, to make diff --git a/man/systemd.exec.xml b/man/systemd.exec.xml index 62c59308ef..a63700afb3 100644 --- a/man/systemd.exec.xml +++ b/man/systemd.exec.xml @@ -4409,7 +4409,18 @@ StandardInputData=V2XigLJyZSBubyBzdHJhbmdlcnMgdG8gbG92ZQpZb3Uga25vdyB0aGUgcnVsZX in case of an unnamed socket. $REMOTE_PORT is not set for AF_UNIX sockets. - + + + + + $SO_COOKIE + + If this is a unit started via per-connection socket activation (i.e. via a socket + unit with Accept=yes), this environment variable contains the Linux socket + cookie, formatted as decimal integer. The socket cookie can otherwise be acquired via getsockopt7. + + diff --git a/man/systemd.socket.xml b/man/systemd.socket.xml index f20c5fc2ef..39bd0dd626 100644 --- a/man/systemd.socket.xml +++ b/man/systemd.socket.xml @@ -425,14 +425,19 @@ above for a more detailed discussion of the naming rules of triggered services. For IPv4 and IPv6 connections, the $REMOTE_ADDR environment variable will - contain the remote IP address, and $REMOTE_PORT will contain the remote port. This - is the same as the format used by CGI. For SOCK_RAW, the port is the IP - protocol. + contain the remote IP address, and $REMOTE_PORT will contain the remote port + number. These two variables correspond to those defined by the CGI interface for web services (see + RFC 3875). For AF_UNIX socket connections, the $REMOTE_ADDR environment variable will contain either the remote socket's file system path starting with a slash (/) or its address in the abstract namespace starting with an at symbol - (@). If the socket is unnamed, $REMOTE_ADDR will not be set. + (@). If the socket is unnamed, $REMOTE_ADDR will not be + set. + + The $SO_COOKIE environment variable is set to the Linux socket cookie, + formatted as decimal integer. The socket cookie can otherwise be acquired via getsockopt7. It is recommended to set CollectMode=inactive-or-failed for service instances activated via Accept=yes, to ensure that failed connection services are diff --git a/src/basic/socket-util.c b/src/basic/socket-util.c index c5499d6e48..919884ba47 100644 --- a/src/basic/socket-util.c +++ b/src/basic/socket-util.c @@ -1959,3 +1959,18 @@ finalize: return 0; } + +int socket_get_cookie(int fd, uint64_t *ret) { + assert(fd >= 0); + + uint64_t cookie = 0; + socklen_t cookie_len = sizeof(cookie); + if (getsockopt(fd, SOL_SOCKET, SO_COOKIE, &cookie, &cookie_len) < 0) + return -errno; + + assert(cookie_len == sizeof(cookie)); + if (ret) + *ret = cookie; + + return 0; +} diff --git a/src/basic/socket-util.h b/src/basic/socket-util.h index 830b48e435..dea93dbde8 100644 --- a/src/basic/socket-util.h +++ b/src/basic/socket-util.h @@ -354,3 +354,5 @@ int socket_address_equal_unix(const char *a, const char *b); int vsock_get_local_cid(unsigned *ret); int netlink_socket_get_multicast_groups(int fd, size_t *ret_len, uint32_t **ret_groups); + +int socket_get_cookie(int fd, uint64_t *ret); diff --git a/src/core/service.c b/src/core/service.c index 141c85745a..46e8ff6239 100644 --- a/src/core/service.c +++ b/src/core/service.c @@ -1749,7 +1749,7 @@ static int service_spawn_internal( if (r < 0) return r; - our_env = new0(char*, 15); + our_env = new0(char*, 16); if (!our_env) return -ENOMEM; @@ -1821,6 +1821,14 @@ static int service_spawn_internal( our_env[n_env++] = t; } } + + uint64_t cookie; + if (socket_get_cookie(s->socket_fd, &cookie) >= 0) { + char *t; + if (asprintf(&t, "SO_COOKIE=%" PRIu64, cookie) < 0) + return -ENOMEM; + our_env[n_env++] = t; + } } Service *env_source = NULL; diff --git a/src/core/socket.c b/src/core/socket.c index af67ac3d06..1ce6bc1ba6 100644 --- a/src/core/socket.c +++ b/src/core/socket.c @@ -830,7 +830,11 @@ static void socket_dump(Unit *u, FILE *f, const char *prefix) { cgroup_context_dump(UNIT(s), f, prefix); } -static int instance_from_socket(int fd, unsigned nr, char **ret) { +static int instance_from_socket( + int fd, + unsigned nr, + char **ret) { + union sockaddr_union local, remote; socklen_t l; int r; @@ -846,6 +850,11 @@ static int instance_from_socket(int fd, unsigned nr, char **ret) { if (getpeername(fd, &remote.sa, &l) < 0) return -errno; + uint64_t cookie; + r = socket_get_cookie(fd, &cookie); + if (r < 0) + return r; + char *s; switch (local.sa.sa_family) { @@ -856,8 +865,9 @@ static int instance_from_socket(int fd, unsigned nr, char **ret) { b = be32toh(remote.in.sin_addr.s_addr); if (asprintf(&s, - "%u-%u.%u.%u.%u:%u-%u.%u.%u.%u:%u", + "%u-%" PRIu64 "-%u.%u.%u.%u:%u-%u.%u.%u.%u:%u", nr, + cookie, a >> 24, (a >> 16) & 0xFF, (a >> 8) & 0xFF, a & 0xFF, be16toh(local.in.sin_port), b >> 24, (b >> 16) & 0xFF, (b >> 8) & 0xFF, b & 0xFF, @@ -879,8 +889,9 @@ static int instance_from_socket(int fd, unsigned nr, char **ret) { *b = remote.in6.sin6_addr.s6_addr+12; if (asprintf(&s, - "%u-%u.%u.%u.%u:%u-%u.%u.%u.%u:%u", + "%u-%" PRIu64 "-%u.%u.%u.%u:%u-%u.%u.%u.%u:%u", nr, + cookie, a[0], a[1], a[2], a[3], be16toh(local.in6.sin6_port), b[0], b[1], b[2], b[3], @@ -888,8 +899,9 @@ static int instance_from_socket(int fd, unsigned nr, char **ret) { return -ENOMEM; } else { if (asprintf(&s, - "%u-%s:%u-%s:%u", + "%u-%" PRIu64 "-%s:%u-%s:%u", nr, + cookie, IN6_ADDR_TO_STRING(&local.in6.sin6_addr), be16toh(local.in6.sin6_port), IN6_ADDR_TO_STRING(&remote.in6.sin6_addr), @@ -909,19 +921,20 @@ static int instance_from_socket(int fd, unsigned nr, char **ret) { uint64_t pidfd_id; if (pidfd >= 0 && pidfd_get_inode_id(pidfd, &pidfd_id) >= 0) - r = asprintf(&s, "%u-" PID_FMT "_%" PRIu64 "-" UID_FMT, - nr, ucred.pid, pidfd_id, ucred.uid); + r = asprintf(&s, "%u-%" PRIu64 "-" PID_FMT "_%" PRIu64 "-" UID_FMT, + nr, cookie, ucred.pid, pidfd_id, ucred.uid); else - r = asprintf(&s, "%u-" PID_FMT "-" UID_FMT, - nr, ucred.pid, ucred.uid); + r = asprintf(&s, "%u-%" PRIu64 "-" PID_FMT "-" UID_FMT, + nr, cookie, ucred.pid, ucred.uid); if (r < 0) return -ENOMEM; } else if (r == -ENODATA) { /* This handles the case where somebody is connecting from another pid/uid namespace * (e.g. from outside of our container). */ if (asprintf(&s, - "%u-unknown", - nr) < 0) + "%u-%" PRIu64 "-unknown", + nr, + cookie) < 0) return -ENOMEM; } else return r; @@ -931,8 +944,9 @@ static int instance_from_socket(int fd, unsigned nr, char **ret) { case AF_VSOCK: if (asprintf(&s, - "%u-%u:%u-%u:%u", + "%u-%" PRIu64 "-%u:%u-%u:%u", nr, + cookie, local.vm.svm_cid, local.vm.svm_port, remote.vm.svm_cid, remote.vm.svm_port) < 0) return -ENOMEM; diff --git a/src/libsystemd/sd-netlink/test-netlink.c b/src/libsystemd/sd-netlink/test-netlink.c index 274719c296..252bb46b6e 100644 --- a/src/libsystemd/sd-netlink/test-netlink.c +++ b/src/libsystemd/sd-netlink/test-netlink.c @@ -717,9 +717,7 @@ TEST(sock_diag_unix) { ASSERT_OK_ERRNO(fstat(unix_fd, &st)); uint64_t cookie; - socklen_t cookie_len = sizeof(cookie); - ASSERT_OK_ERRNO(getsockopt(unix_fd, SOL_SOCKET, SO_COOKIE, &cookie, &cookie_len)); - ASSERT_EQ(cookie_len, sizeof(cookie)); + ASSERT_OK(socket_get_cookie(unix_fd, &cookie)); _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *message = NULL; ASSERT_OK(sd_sock_diag_message_new_unix(nl, &message, st.st_ino, cookie, UDIAG_SHOW_RQLEN)); diff --git a/src/shared/socket-netlink.c b/src/shared/socket-netlink.c index 1ecb2284fe..a810e1c1f3 100644 --- a/src/shared/socket-netlink.c +++ b/src/shared/socket-netlink.c @@ -504,12 +504,10 @@ int af_unix_get_qlen(int fd, uint32_t *ret) { if (r < 0) return r; - uint64_t cookie = 0; - socklen_t cookie_len = sizeof(cookie); - if (getsockopt(fd, SOL_SOCKET, SO_COOKIE, &cookie, &cookie_len) < 0) - return -errno; - - assert(cookie_len == sizeof(cookie)); + uint64_t cookie; + r = socket_get_cookie(fd, &cookie); + if (r < 0) + return r; _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *message = NULL; r = sd_sock_diag_message_new_unix(nl, &message, st.st_ino, cookie, UDIAG_SHOW_RQLEN);