~ensc


#22 support systemd socket activation 8 months ago

Ticket created by ~ensc on ~kennylevinsen/seatd

It would be nice when seatd supports systemd's socket activation. E.g. the setup of the listening socket is done by systemd and seatd just uses the given fd. This will solve #20 implicitly because the the underlying problem (wait until service accepts requests) disappears.

I tested it with

From 4e0440268ef13a329bb329c3b1d4454f2a58aaef Mon Sep 17 00:00:00 2001
From: Enrico Scholz <enrico.scholz@sigma-chemnitz.de>
Date: Thu, 2 May 2024 16:47:34 +0200
Subject: [PATCH 1/2] sd-listen: implement sd_listen() function

From: Enrico Scholz <enrico.scholz@sigma-chemnitz.de>

Based on systemd sources with manual implementation of the safe_ato*()
functions.

Upstream-Status: Pending

Signed-off-by: Enrico Scholz <enrico.scholz@sigma-chemnitz.de>
---
 meson.build       |   1 +
 seatd/sd-listen.c | 159 ++++++++++++++++++++++++++++++++++++++++++++++
 seatd/sd-listen.h |   8 +++
 3 files changed, 168 insertions(+)
 create mode 100644 seatd/sd-listen.c
 create mode 100644 seatd/sd-listen.h

diff --git a/meson.build b/meson.build
index 516d7d2..43de87c 100644
--- a/meson.build
+++ b/meson.build
@@ -119,6 +119,7 @@ server_files = [
 	'seatd/seat.c',
 	'seatd/client.c',
 	'seatd/server.c',
+	'seatd/sd-listen.c',
 ]
 
 with_seatd = get_option('libseat-seatd') == 'enabled'
diff --git a/seatd/sd-listen.c b/seatd/sd-listen.c
new file mode 100644
index 0000000..c1c2884
--- /dev/null
+++ b/seatd/sd-listen.c
@@ -0,0 +1,159 @@
+#include "sd-listen.h"
+
+#include <stdlib.h>
+#include <errno.h>
+#include <stdbool.h>
+#include <limits.h>
+#include <unistd.h>
+#include <sys/fcntl.h>
+
+#define variable_is_signed(_var)		\
+	_Generic(_var,				\
+		 signed char: true,		\
+		 unsigned char: false,		\
+		 int: true,			\
+		 short: true,			\
+		 long: true,			\
+		 long long: true,		\
+		 unsigned short: false,		\
+		 unsigned int: false,		\
+		 unsigned long: false,		\
+		 unsigned long long: false)
+
+#define integer_safe_assign(_dst, _src) __extension__			\
+	({								\
+		int _safe_assign_res = 1;				\
+		if ((__typeof__(_src))((__typeof__(_dst))(_src)) != (_src)) \
+			_safe_assign_res = 0;				\
+		else							\
+			(_dst) = (__typeof__(_dst))(_src);		\
+		_safe_assign_res;					\
+	})
+
+#define safe_atoX(_s, _res_ptr, _fn, _fn_type)				\
+	do {								\
+		char const	*s = (_s);				\
+		_fn_type	res;					\
+		char		*e;					\
+									\
+		errno = 0;						\
+		res = _fn(s, &e, 0);					\
+		if (errno > 0) {					\
+			return -1;					\
+		} else if (*s == '\0' || *e != '\0') {			\
+			errno = EINVAL;					\
+			return -1;					\
+		} else if (!variable_is_signed(res) && *s == '-') {	\
+			errno = EINVAL;					\
+			return -1;					\
+		} else if (!integer_safe_assign(*(_res_ptr), res)) {	\
+			errno = ERANGE;					\
+			return -1;					\
+		} else {						\
+			return 0;					\
+		}							\
+	} while (0)
+
+static int safe_atoi(char const *str, int *ret)
+{
+	safe_atoX(str, ret, strtol, long);
+}
+
+static int safe_atolu(char const *str, unsigned long *ret)
+{
+	safe_atoX(str, ret, strtoul, unsigned long);
+}
+
+static int parse_pid(char const *s, pid_t * ret)
+{
+	unsigned long	pid;
+	int		rc;
+
+	rc = safe_atolu(s, &pid);
+	if (rc < 0) {
+		return rc;
+	} else if (!integer_safe_assign(*ret, pid)) {
+		errno = EINVAL;
+		return -1;
+	} else {
+		return 0;
+	}
+}
+
+static int fd_cloexec(int fd, bool ena)
+{
+	int	flags = fcntl(fd, F_GETFD, 0);
+
+	if (flags < 0)
+		return flags;
+
+	if (ena)
+		flags |= FD_CLOEXEC;
+	else
+		flags &= ~FD_CLOEXEC;
+
+	return fcntl(fd, F_SETFD, flags);
+}
+
+static void unsetenv_all(bool unset_environment)
+{
+
+        if (!unset_environment)
+                return;
+
+        unsetenv("LISTEN_PID");
+        unsetenv("LISTEN_FDS");
+        unsetenv("LISTEN_FDNAMES");
+}
+
+int sd_listen_fds(int unset_environment)
+{
+        const char *e;
+        int n, r, fd;
+        pid_t pid;
+
+        e = getenv("LISTEN_PID");
+        if (!e) {
+                r = 0;
+                goto finish;
+        }
+
+        r = parse_pid(e, &pid);
+        if (r < 0)
+                goto finish;
+
+        /* Is this for us? */
+        if (getpid() != pid) {
+                r = 0;
+                goto finish;
+        }
+
+        e = getenv("LISTEN_FDS");
+        if (!e) {
+                r = 0;
+                goto finish;
+        }
+
+        r = safe_atoi(e, &n);
+        if (r < 0)
+                goto finish;
+
+	_Static_assert(SD_LISTEN_FDS_START < INT_MAX,
+		       "SD_LISTEN_FDS_START out of range");
+        if (n <= 0 || n > INT_MAX - SD_LISTEN_FDS_START) {
+                r = -EINVAL;
+                goto finish;
+        }
+
+        for (fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + n; fd ++) {
+                r = fd_cloexec(fd, true);
+                if (r < 0)
+                        goto finish;
+        }
+
+        r = n;
+
+finish:
+        unsetenv_all(unset_environment);
+        return r;
+}
diff --git a/seatd/sd-listen.h b/seatd/sd-listen.h
new file mode 100644
index 0000000..3ee2f8b
--- /dev/null
+++ b/seatd/sd-listen.h
@@ -0,0 +1,8 @@
+#ifndef H_SEATD_SD_LISTEN_H
+#define H_SEATD_SD_LISTEN_H
+
+#define SD_LISTEN_FDS_START 3
+
+int sd_listen_fds(int unset_environment);
+
+#endif	/* H_SEATD_SD_LISTEN_H */
-- 
2.44.0
From e19fc72390aaf98f712ed61553a0dbb2387923a9 Mon Sep 17 00:00:00 2001
From: Enrico Scholz <enrico.scholz@sigma-chemnitz.de>
Date: Thu, 2 May 2024 16:48:32 +0200
Subject: [PATCH 2/2] seatd: use systemd socket activation

From: Enrico Scholz <enrico.scholz@sigma-chemnitz.de>

Upstream-Status: Pending

Signed-off-by: Enrico Scholz <enrico.scholz@sigma-chemnitz.de>
---
 seatd/seatd.c | 8 +++++++-
 1 file changed, 7 insertions(+), 1 deletion(-)

Index: git/seatd/seatd.c
===================================================================
--- git.orig/seatd/seatd.c
+++ git/seatd/seatd.c
@@ -15,6 +15,8 @@
 #include "poller.h"
 #include "server.h"
 
+#include "sd-listen.h"
+
 #define LISTEN_BACKLOG 16
 
 static int open_socket(const char *path, int uid, int gid) {
@@ -152,8 +154,10 @@ int main(int argc, char *argv[]) {
 	log_init();
 	libseat_set_log_level(level);
 
+	bool systemd_socket = sd_listen_fds(1) > 0;
+
 	struct stat st;
-	if (lstat(SEATD_DEFAULTPATH, &st) == 0) {
+	if (!systemd_socket && lstat(SEATD_DEFAULTPATH, &st) == 0) {
 		if (!S_ISSOCK(st.st_mode)) {
 			log_errorf("Non-socket file found at socket path %s, refusing to start",
 				   SEATD_DEFAULTPATH);
@@ -179,7 +183,11 @@ int main(int argc, char *argv[]) {
 	}
 
 	int ret = 1;
-	int socket_fd = open_socket(SEATD_DEFAULTPATH, uid, gid);
+	int socket_fd = systemd_socket ? SD_LISTEN_FDS_START : -1;
+
+	if (socket_fd < 0)
+		socket_fd = open_socket(SEATD_DEFAULTPATH, uid, gid);
+
 	if (socket_fd == -1) {
 		log_error("Could not create server socket");
 		goto error_server;
@@ -210,7 +218,7 @@ int main(int argc, char *argv[]) {
 	ret = 0;
 
 error_socket:
-	if (unlink(SEATD_DEFAULTPATH) == -1) {
+	if (!systemd_socket && unlink(SEATD_DEFAULTPATH) == -1) {
 		log_errorf("Could not remove socket: %s", strerror(errno));
 	}
 error_server:

and

## seatd.service
[Unit]

[Service]
Type=simple
ExecStart=/usr/bin/seatd
## seatd.socket
[Unit]
Description=seatd socket

[Socket]
ListenStream=/run/seatd.sock
SocketMode=0770
SocketGroup=video
RemoveOnStop=yes

[Install]
WantedBy=sockets.target