core: use socket cookie for naming per-connection service instances + set $SO_COOKIE env var to it (#37469)
This commit is contained in:
@@ -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
|
||||
|
||||
+12
-1
@@ -4409,7 +4409,18 @@ StandardInputData=V2XigLJyZSBubyBzdHJhbmdlcnMgdG8gbG92ZQpZb3Uga25vdyB0aGUgcnVsZX
|
||||
in case of an unnamed socket. <varname>$REMOTE_PORT</varname> is not set for <constant>AF_UNIX</constant>
|
||||
sockets.</para>
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v254"/></listitem>
|
||||
<xi:include href="version-info.xml" xpointer="v220"/></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><varname>$SO_COOKIE</varname></term>
|
||||
|
||||
<listitem><para>If this is a unit started via per-connection socket activation (i.e. via a socket
|
||||
unit with <varname>Accept=yes</varname>), this environment variable contains the Linux socket
|
||||
cookie, formatted as decimal integer. The socket cookie can otherwise be acquired via <citerefentry
|
||||
project='man-pages'><refentrytitle>getsockopt</refentrytitle><manvolnum>7</manvolnum></citerefentry>.</para>
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v258"/></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
|
||||
@@ -425,14 +425,19 @@
|
||||
above for a more detailed discussion of the naming rules of triggered services.</para>
|
||||
|
||||
<para>For IPv4 and IPv6 connections, the <varname>$REMOTE_ADDR</varname> environment variable will
|
||||
contain the remote IP address, and <varname>$REMOTE_PORT</varname> will contain the remote port. This
|
||||
is the same as the format used by CGI. For <constant>SOCK_RAW</constant>, the port is the IP
|
||||
protocol.</para>
|
||||
contain the remote IP address, and <varname>$REMOTE_PORT</varname> will contain the remote port
|
||||
number. These two variables correspond to those defined by the CGI interface for web services (see
|
||||
<ulink url="https://datatracker.ietf.org/doc/html/rfc3875">RFC 3875</ulink>).</para>
|
||||
|
||||
<para>For <constant>AF_UNIX</constant> socket connections, the <varname>$REMOTE_ADDR</varname>
|
||||
environment variable will contain either the remote socket's file system path starting with a slash
|
||||
(<literal>/</literal>) or its address in the abstract namespace starting with an at symbol
|
||||
(<literal>@</literal>). If the socket is unnamed, <varname>$REMOTE_ADDR</varname> will not be set.</para>
|
||||
(<literal>@</literal>). If the socket is unnamed, <varname>$REMOTE_ADDR</varname> will not be
|
||||
set.</para>
|
||||
|
||||
<para>The <varname>$SO_COOKIE</varname> environment variable is set to the Linux socket cookie,
|
||||
formatted as decimal integer. The socket cookie can otherwise be acquired via <citerefentry
|
||||
project='man-pages'><refentrytitle>getsockopt</refentrytitle><manvolnum>7</manvolnum></citerefentry>.</para>
|
||||
|
||||
<para>It is recommended to set <varname>CollectMode=inactive-or-failed</varname> for service
|
||||
instances activated via <varname>Accept=yes</varname>, to ensure that failed connection services are
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
+9
-1
@@ -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;
|
||||
|
||||
+25
-11
@@ -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;
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user