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


Reply via email to