By recording a copy of LISTEN_FDNAMES, we make it possible to learn mappings from file descriptor labels (e.g., as set by FileDescriptorName= in systemd.socket(5)).
This also makes it possible to invoke check_socket_activation() more than once and have it return the same value each time. This is one step toward addressing https://gitlab.com/qemu-project/qemu/-/issues/3011 Since we can't count on the buffer returned from getenv persisting (getenv is documented as non-re-entrant), we need to keep a copy of it around in case multiple subsystems want to interrogate it. This proposed implementation uses a static buffer, and breaks socket activation with a visible error_report if LISTEN_FDNAMES is too large. Another approach would be to g_strdup the value returned by getenv, which would have failure modes on heap exhaustion, and would introduce a memory leak as there's no clear opportunity to g_free the copy. Signed-off-by: Daniel Kahn Gillmor <d...@fifthhorseman.net> --- util/systemd.c | 33 +++++++++++++++++++++++++++++---- 1 file changed, 29 insertions(+), 4 deletions(-) diff --git a/util/systemd.c b/util/systemd.c index ced518f771..1eca2bd69f 100644 --- a/util/systemd.c +++ b/util/systemd.c @@ -16,37 +16,62 @@ #include "qemu/error-report.h" #ifndef _WIN32 +static char fdnames[256]; + unsigned int check_socket_activation(void) { + static unsigned int nr_fds = -1; const char *s; unsigned long pid; - unsigned long nr_fds; + unsigned long nr_fdsl; unsigned int i; int fd; int f; int err; + if (nr_fds != -1) { + return nr_fds; + } s = getenv("LISTEN_PID"); if (s == NULL) { + nr_fds = 0; return 0; } err = qemu_strtoul(s, NULL, 10, &pid); if (err) { + nr_fds = 0; return 0; } if (pid != getpid()) { + nr_fds = 0; return 0; } s = getenv("LISTEN_FDS"); if (s == NULL) { + nr_fds = 0; return 0; } - err = qemu_strtoul(s, NULL, 10, &nr_fds); + err = qemu_strtoul(s, NULL, 10, &nr_fdsl); if (err) { + nr_fds = 0; return 0; } - assert(nr_fds <= UINT_MAX); + assert(nr_fdsl <= UINT_MAX); + nr_fds = (unsigned int) nr_fdsl; + s = getenv("LISTEN_FDNAMES"); + if (s != NULL) { + size_t fdnames_len = strlen(s); + if (fdnames_len + 1 > sizeof(fdnames)) { + error_report("LISTEN_FDNAMES is larger than %ldu bytes, " + "ignoring socket activation.", + sizeof(fdnames)); + nr_fds = 0; + return 0; + } else { + memcpy(fdnames, s, fdnames_len + 1); + } + } /* So these are not passed to any child processes we might start. */ unsetenv("LISTEN_FDS"); @@ -69,7 +94,7 @@ unsigned int check_socket_activation(void) } } - return (unsigned int) nr_fds; + return nr_fds; } #else /* !_WIN32 */ -- 2.47.2