From 57efceeb02bf72e372a3005a369218adebd855ff Mon Sep 17 00:00:00 2001 From: Hugo Osvaldo Barrera Date: Mon, 2 Feb 2026 22:16:11 +0100 Subject: [PATCH] Implement socket activation without libsystemd Socket activation uses sd_listen_fds from libsystemd, and can only be compiled on systems with systemd. This is an issue for Alpine / postmarketOS, where upstream has no systemd package, but downstream depends on upstream's pipewire package and wants to rely on socket activation. This also prevents using socket-activation on other non-systemd distributions, including non-Linux. Implement equivalent functionality without a dependency on libsystemd. --- src/modules/meson.build | 4 -- src/modules/module-protocol-native.c | 15 ++--- src/modules/module-protocol-pulse/server.c | 22 ++----- src/modules/network-utils.h | 71 ++++++++++++++++++++++ 4 files changed, 82 insertions(+), 30 deletions(-) diff --git a/src/modules/meson.build b/src/modules/meson.build index 7ad232ac8..6b215f3a0 100644 --- a/src/modules/meson.build +++ b/src/modules/meson.build @@ -276,10 +276,6 @@ pipewire_module_link_factory = shared_library('pipewire-module-link-factory', pipewire_module_protocol_deps = [mathlib, dl_lib, pipewire_dep] -if systemd_dep.found() - pipewire_module_protocol_deps += systemd_dep -endif - if selinux_dep.found() pipewire_module_protocol_deps += selinux_dep endif diff --git a/src/modules/module-protocol-native.c b/src/modules/module-protocol-native.c index 96e99f35e..2be92a847 100644 --- a/src/modules/module-protocol-native.c +++ b/src/modules/module-protocol-native.c @@ -34,10 +34,6 @@ #include #include -#ifdef HAVE_SYSTEMD -#include -#endif - #ifdef HAVE_SELINUX #include #endif @@ -45,6 +41,7 @@ #include #include +#include "network-utils.h" #include "pipewire/private.h" #include "modules/module-protocol-native/connection.h" @@ -909,13 +906,12 @@ static int add_socket(struct pw_protocol *protocol, struct server *s, struct soc int fd = -1, res; bool activated = false; -#ifdef HAVE_SYSTEMD { - int i, n = sd_listen_fds(0); + int i, n = listen_fd(); for (i = 0; i < n; ++i) { - if (sd_is_socket_unix(SD_LISTEN_FDS_START + i, SOCK_STREAM, - 1, s->addr.sun_path, 0) > 0) { - fd = SD_LISTEN_FDS_START + i; + if (is_socket_unix(LISTEN_FDS_START + i, SOCK_STREAM, + s->addr.sun_path) > 0) { + fd = LISTEN_FDS_START + i; activated = true; pw_log_info("server %p: Found socket activation socket for '%s'", s, s->addr.sun_path); @@ -923,7 +919,6 @@ static int add_socket(struct pw_protocol *protocol, struct server *s, struct soc } } } -#endif if (fd < 0) { struct stat socket_stat; diff --git a/src/modules/module-protocol-pulse/server.c b/src/modules/module-protocol-pulse/server.c index 4e744e33f..aeab710b0 100644 --- a/src/modules/module-protocol-pulse/server.c +++ b/src/modules/module-protocol-pulse/server.c @@ -21,9 +21,6 @@ #include #include -#ifdef HAVE_SYSTEMD -#include -#endif #include #include @@ -577,26 +574,19 @@ static bool is_stale_socket(int fd, const struct sockaddr_un *addr_un) return false; } -#ifdef HAVE_SYSTEMD -static int check_systemd_activation(const char *path) +static int check_socket_activation(const char *path) { - const int n = sd_listen_fds(0); + const int n = listen_fd(); for (int i = 0; i < n; i++) { - const int fd = SD_LISTEN_FDS_START + i; + const int fd = LISTEN_FDS_START + i; - if (sd_is_socket_unix(fd, SOCK_STREAM, 1, path, 0) > 0) + if (is_socket_unix(fd, SOCK_STREAM, path) > 0) return fd; } return -1; } -#else -static inline int check_systemd_activation(SPA_UNUSED const char *path) -{ - return -1; -} -#endif static int start_unix_server(struct server *server, const struct sockaddr_storage *addr) { @@ -606,10 +596,10 @@ static int start_unix_server(struct server *server, const struct sockaddr_storag spa_assert(addr_un->sun_family == AF_UNIX); - fd = check_systemd_activation(addr_un->sun_path); + fd = check_socket_activation(addr_un->sun_path); if (fd >= 0) { server->activated = true; - pw_log_info("server %p: found systemd socket activation socket for '%s'", + pw_log_info("server %p: found socket activation socket for '%s'", server, addr_un->sun_path); goto done; } diff --git a/src/modules/network-utils.h b/src/modules/network-utils.h index 16f9d9273..a89b7d3bd 100644 --- a/src/modules/network-utils.h +++ b/src/modules/network-utils.h @@ -7,6 +7,12 @@ #include #include #include +#include +#include +#include +#include + +#include #ifdef __FreeBSD__ #define ifr_ifindex ifr_index @@ -131,5 +137,70 @@ static inline bool pw_net_addr_is_any(struct sockaddr_storage *addr) return false; } +#ifndef LISTEN_FDS_START +#define LISTEN_FDS_START 3 +#endif + +/* Returns the number of file descriptors passed for socket activation. + * Returns 0 if none, -1 on error. */ +static inline int listen_fd(void) +{ + uint32_t n; + int i, flags; + + if (!spa_atou32(getenv("LISTEN_FDS"), &n, 10) || n > INT_MAX - LISTEN_FDS_START) { + errno = EINVAL; + return -1; + } + + for (i = 0; i < (int)n; i++) { + flags = fcntl(LISTEN_FDS_START + i, F_GETFD); + if (flags == -1) + return -1; + if (fcntl(LISTEN_FDS_START + i, F_SETFD, flags | FD_CLOEXEC) == -1) + return -1; + } + + unsetenv("LISTEN_FDS"); + + return (int)n; +} + +/* Check if the fd is a listening unix socket of the given type, + * optionally bound to the given path. */ +static inline int is_socket_unix(int fd, int type, const char *path) +{ + struct sockaddr_un addr; + int val; + socklen_t len = sizeof(val); + + if (getsockopt(fd, SOL_SOCKET, SO_TYPE, &val, &len) < 0) + return -errno; + if (val != type) + return 0; + + if (getsockopt(fd, SOL_SOCKET, SO_ACCEPTCONN, &val, &len) < 0) + return -errno; + if (!val) + return 0; + + if (path) { + len = sizeof(addr); + memset(&addr, 0, sizeof(addr)); + if (getsockname(fd, (struct sockaddr *)&addr, &len) < 0) + return -errno; + if (addr.sun_family != AF_UNIX) + return 0; + size_t length = strlen(path); + if (length > 0) { + if (len < offsetof(struct sockaddr_un, sun_path) + length) + return 0; + if (memcmp(addr.sun_path, path, length) != 0) + return 0; + } + } + + return 1; +} #endif /* NETWORK_UTILS_H */