[pulseaudio-discuss] [PATCH 4/8] socket-server: Add support for systemd socket activation.

Colin Guthrie colin at mageia.org
Mon Nov 3 01:42:36 PST 2014


This adds support to module-native-protocol-unix to take over already
listening sockets passed in via socket activation (e.g. from systemd)

Most of the code is isolated to socket-server but some cleanup code also
had to be tweaked to ensure we do not overzealously close open fds.
---
 configure.ac                  | 21 +++++++++++++-
 src/Makefile.am               |  4 +++
 src/daemon/main.c             | 34 ++++++++++++++++++----
 src/pulsecore/socket-server.c | 66 ++++++++++++++++++++++++++++---------------
 src/pulsecore/socket-util.c   | 16 +++++++++++
 5 files changed, 112 insertions(+), 29 deletions(-)

diff --git a/configure.ac b/configure.ac
index 98a3ee2..25cb207 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1180,6 +1180,9 @@ ORC_CHECK([0.4.11])
 
 #### systemd support (optional) ####
 
+AC_ARG_ENABLE([systemd-daemon],
+    AS_HELP_STRING([--disable-systemd-daemon],[Disable optional systemd daemon (socket activation) support]))
+
 AC_ARG_ENABLE([systemd-login],
     AS_HELP_STRING([--disable-systemd-login],[Disable optional systemd login support]))
 
@@ -1189,16 +1192,30 @@ AC_ARG_ENABLE([systemd-journal],
 # Newer systemd's combine their subcomponent libraries into one
 # If it exists, we should use it for the further checks
 
-AS_IF([test "x$enable_systemd_login" != "xno" || test "x$enable_systemd_journal" != "xno"],
+AS_IF([test "x$enable_systemd_daemon" != "xno" || test "x$enable_systemd_login" != "xno" || test "x$enable_systemd_journal" != "xno"],
     [PKG_CHECK_MODULES(SYSTEMD, [ libsystemd ], HAVE_SYSTEMD=1, HAVE_SYSTEMD=0)],
     HAVE_SYSTEMD=0)
 
 AS_IF([test "x$HAVE_SYSTEMD" = "x1"],
     [
+        HAVE_SYSTEMD_DAEMON=1
         HAVE_SYSTEMD_LOGIN=1
         HAVE_SYSTEMD_JOURNAL=1
     ])
 
+#### systemd daemon support (optional) ####
+
+AS_IF([test "x$enable_systemd_daemon" != "xno"],
+    [AS_IF([test "x$HAVE_SYSTEMD_DAEMON" != "x1"], [PKG_CHECK_MODULES(SYSTEMDDAEMON, [ libsystemd-daemon ], HAVE_SYSTEMD_DAEMON=1, HAVE_SYSTEMD_DAEMON=0)])],
+    HAVE_SYSTEMD_DAEMON=0)
+
+AS_IF([test "x$enable_systemd_daemon" = "xyes" && test "x$HAVE_SYSTEMD_DAEMON" = "x0"],
+    [AC_MSG_ERROR([*** Needed systemd daemon support not found])])
+
+AC_SUBST(HAVE_SYSTEMD_DAEMON)
+AM_CONDITIONAL([HAVE_SYSTEMD_DAEMON], [test "x$HAVE_SYSTEMD_DAEMON" = x1])
+AS_IF([test "x$HAVE_SYSTEMD_DAEMON" = "x1"], AC_DEFINE([HAVE_SYSTEMD_DAEMON], 1, [Have SYSTEMDDAEMON?]))
+
 #### systemd login support (optional) ####
 
 AS_IF([test "x$enable_systemd_login" != "xno"],
@@ -1473,6 +1490,7 @@ AS_IF([test "x$HAVE_LIRC" = "x1"], ENABLE_LIRC=yes, ENABLE_LIRC=no)
 AS_IF([test "x$HAVE_XEN" = "x1"], ENABLE_XEN=yes, ENABLE_XEN=no)
 AS_IF([test "x$HAVE_DBUS" = "x1"], ENABLE_DBUS=yes, ENABLE_DBUS=no)
 AS_IF([test "x$HAVE_UDEV" = "x1"], ENABLE_UDEV=yes, ENABLE_UDEV=no)
+AS_IF([test "x$HAVE_SYSTEMD_DAEMON" = "x1"], ENABLE_SYSTEMD_DAEMON=yes, ENABLE_SYSTEMD_DAEMON=no)
 AS_IF([test "x$HAVE_SYSTEMD_LOGIN" = "x1"], ENABLE_SYSTEMD_LOGIN=yes, ENABLE_SYSTEMD_LOGIN=no)
 AS_IF([test "x$HAVE_SYSTEMD_JOURNAL" = "x1"], ENABLE_SYSTEMD_JOURNAL=yes, ENABLE_SYSTEMD_JOURNAL=no)
 AS_IF([test "x$HAVE_BLUEZ_4" = "x1"], ENABLE_BLUEZ_4=yes, ENABLE_BLUEZ_4=no)
@@ -1534,6 +1552,7 @@ echo "
         headset backend:           ${BLUETOOTH_HEADSET_BACKEND}
     Enable udev:                   ${ENABLE_UDEV}
       Enable HAL->udev compat:     ${ENABLE_HAL_COMPAT}
+    Enable systemd daemon:         ${ENABLE_SYSTEMD_DAEMON}
     Enable systemd login:          ${ENABLE_SYSTEMD_LOGIN}
     Enable systemd journal:        ${ENABLE_SYSTEMD_JOURNAL}
     Enable TCP Wrappers:           ${ENABLE_TCPWRAP}
diff --git a/src/Makefile.am b/src/Makefile.am
index 71e2034..c037db7 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -706,6 +706,10 @@ libpulsecommon_ at PA_MAJORMINOR@_la_CFLAGS += $(X11_CFLAGS)
 libpulsecommon_ at PA_MAJORMINOR@_la_LDFLAGS += $(X11_LIBS)
 endif
 
+if HAVE_SYSTEMD_DAEMON
+libpulsecommon_ at PA_MAJORMINOR@_la_CFLAGS += $(SYSTEMD_FLAGS) $(SYSTEMDDAEMON_FLAGS)
+libpulsecommon_ at PA_MAJORMINOR@_la_LDFLAGS += $(SYSTEMD_LIBS) $(SYSTEMDDAEMON_LIBS)
+endif
 if HAVE_SYSTEMD_JOURNAL
 libpulsecommon_ at PA_MAJORMINOR@_la_CFLAGS += $(SYSTEMD_FLAGS) $(SYSTEMDJOURNAL_FLAGS)
 libpulsecommon_ at PA_MAJORMINOR@_la_LDFLAGS += $(SYSTEMD_LIBS) $(SYSTEMDJOURNAL_LIBS)
diff --git a/src/daemon/main.c b/src/daemon/main.c
index 372f4e1..0ffc7bd 100644
--- a/src/daemon/main.c
+++ b/src/daemon/main.c
@@ -58,6 +58,10 @@
 #include <dbus/dbus.h>
 #endif
 
+#ifdef HAVE_SYSTEMD_DAEMON
+#include <systemd/sd-daemon.h>
+#endif
+
 #include <pulse/client-conf.h>
 #include <pulse/mainloop.h>
 #include <pulse/mainloop-signal.h>
@@ -374,7 +378,7 @@ int main(int argc, char *argv[]) {
     int r = 0, retval = 1, d = 0;
     bool valid_pid_file = false;
     bool ltdl_init = false;
-    int passed_fd = -1;
+    int n_fds = 0, *passed_fds = NULL;
     const char *e;
 #ifdef HAVE_FORK
     int daemon_pipe[2] = { -1, -1 };
@@ -431,11 +435,28 @@ int main(int argc, char *argv[]) {
     }
 #endif
 
-    if ((e = getenv("PULSE_PASSED_FD"))) {
-        passed_fd = atoi(e);
+#ifdef HAVE_SYSTEMD_DAEMON
+    n_fds = sd_listen_fds(0);
+    if (n_fds > 0) {
+        int i = n_fds;
 
-        if (passed_fd <= 2)
-            passed_fd = -1;
+        passed_fds = pa_xnew(int, n_fds+2);
+        passed_fds[n_fds] = passed_fds[n_fds+1] = -1;
+        while (i--)
+            passed_fds[i] = SD_LISTEN_FDS_START + i;
+    }
+#endif
+
+    if (!passed_fds) {
+        n_fds = 0;
+        passed_fds = pa_xnew(int, 2);
+        passed_fds[0] = passed_fds[1] = -1;
+    }
+
+    if ((e = getenv("PULSE_PASSED_FD"))) {
+        int passed_fd = atoi(e);
+        if (passed_fd > 2)
+            passed_fds[n_fds] = passed_fd;
     }
 
     /* We might be autospawned, in which case have no idea in which
@@ -444,7 +465,8 @@ int main(int argc, char *argv[]) {
 
     pa_reset_personality();
     pa_drop_root();
-    pa_close_all(passed_fd, -1);
+    pa_close_allv(passed_fds);
+    pa_xfree(passed_fds);
     pa_reset_sigs(-1);
     pa_unblock_sigs(-1);
     pa_reset_priority();
diff --git a/src/pulsecore/socket-server.c b/src/pulsecore/socket-server.c
index ef77563..2202f61 100644
--- a/src/pulsecore/socket-server.c
+++ b/src/pulsecore/socket-server.c
@@ -56,6 +56,10 @@ int deny_severity = LOG_WARNING;
 
 #endif /* HAVE_LIBWRAP */
 
+#ifdef HAVE_SYSTEMD_DAEMON
+#include <systemd/sd-daemon.h>
+#endif
+
 #include <pulse/xmalloc.h>
 #include <pulse/util.h>
 
@@ -74,6 +78,7 @@ struct pa_socket_server {
     PA_REFCNT_DECLARE;
     int fd;
     char *filename;
+    bool activated;
     char *tcpwrap_service;
 
     pa_socket_server_on_connection_cb_t on_connection;
@@ -174,43 +179,61 @@ pa_socket_server* pa_socket_server_ref(pa_socket_server *s) {
 
 pa_socket_server* pa_socket_server_new_unix(pa_mainloop_api *m, const char *filename) {
     int fd = -1;
+    bool activated = false;
     struct sockaddr_un sa;
     pa_socket_server *s;
 
     pa_assert(m);
     pa_assert(filename);
 
-    if ((fd = pa_socket_cloexec(PF_UNIX, SOCK_STREAM, 0)) < 0) {
-        pa_log("socket(): %s", pa_cstrerror(errno));
-        goto fail;
+#ifdef HAVE_SYSTEMD_DAEMON
+    int n = sd_listen_fds(0);
+    if (n > 0) {
+        for (int i = 0; i < n; ++i) {
+            if (sd_is_socket_unix(SD_LISTEN_FDS_START + i, SOCK_STREAM, 1, filename, 0) > 0) {
+                fd = SD_LISTEN_FDS_START + i;
+                activated = true;
+                pa_log_info("Found socket activation socket for '%s' \\o/", filename);
+                break;
+            }
+        }
     }
+#endif
 
-    memset(&sa, 0, sizeof(sa));
-    sa.sun_family = AF_UNIX;
-    pa_strlcpy(sa.sun_path, filename, sizeof(sa.sun_path));
+    if (fd < 0) {
+        if ((fd = pa_socket_cloexec(PF_UNIX, SOCK_STREAM, 0)) < 0) {
+            pa_log("socket(PF_UNIX): %s", pa_cstrerror(errno));
+            goto fail;
+        }
 
-    pa_make_socket_low_delay(fd);
+        memset(&sa, 0, sizeof(sa));
+        sa.sun_family = AF_UNIX;
+        pa_strlcpy(sa.sun_path, filename, sizeof(sa.sun_path));
 
-    if (bind(fd, (struct sockaddr*) &sa, (socklen_t) SUN_LEN(&sa)) < 0) {
-        pa_log("bind(): %s", pa_cstrerror(errno));
-        goto fail;
-    }
+        pa_make_socket_low_delay(fd);
 
-    /* Allow access from all clients. Sockets like this one should
-     * always be put inside a directory with proper access rights,
-     * because not all OS check the access rights on the socket
-     * inodes. */
-    chmod(filename, 0777);
+        if (bind(fd, (struct sockaddr*) &sa, (socklen_t) SUN_LEN(&sa)) < 0) {
+            pa_log("bind(): %s", pa_cstrerror(errno));
+            goto fail;
+        }
 
-    if (listen(fd, 5) < 0) {
-        pa_log("listen(): %s", pa_cstrerror(errno));
-        goto fail;
+        /* Allow access from all clients. Sockets like this one should
+        * always be put inside a directory with proper access rights,
+        * because not all OS check the access rights on the socket
+        * inodes. */
+        chmod(filename, 0777);
+
+        if (listen(fd, 5) < 0) {
+            pa_log("listen(): %s", pa_cstrerror(errno));
+            goto fail;
+        }
     }
 
     pa_assert_se(s = pa_socket_server_new(m, fd));
 
     s->filename = pa_xstrdup(filename);
     s->type = SOCKET_SERVER_UNIX;
+    s->activated = activated;
 
     return s;
 
@@ -423,10 +446,9 @@ pa_socket_server* pa_socket_server_new_ipv6_string(pa_mainloop_api *m, const cha
 static void socket_server_free(pa_socket_server*s) {
     pa_assert(s);
 
-    if (s->filename) {
+    if (!s->activated && s->filename)
         unlink(s->filename);
-        pa_xfree(s->filename);
-    }
+    pa_xfree(s->filename);
 
     pa_close(s->fd);
 
diff --git a/src/pulsecore/socket-util.c b/src/pulsecore/socket-util.c
index e1c0b55..0ce0f69 100644
--- a/src/pulsecore/socket-util.c
+++ b/src/pulsecore/socket-util.c
@@ -52,6 +52,9 @@
 #ifdef HAVE_NETDB_H
 #include <netdb.h>
 #endif
+#ifdef HAVE_SYSTEMD_DAEMON
+#include <systemd/sd-daemon.h>
+#endif
 
 #include <pulsecore/core-error.h>
 #include <pulsecore/core-util.h>
@@ -255,6 +258,19 @@ int pa_unix_socket_remove_stale(const char *fn) {
 
     pa_assert(fn);
 
+#ifdef HAVE_SYSTEMD_DAEMON
+    int n = sd_listen_fds(0);
+    if (n > 0) {
+        for (int i = 0; i < n; ++i) {
+            if (sd_is_socket_unix(SD_LISTEN_FDS_START + i, SOCK_STREAM, 1, fn, 0) > 0) {
+                /* This is a socket activated socket, therefore do not consider
+                 * it stale. */
+                return 0;
+            }
+        }
+    }
+#endif
+
     if ((r = pa_unix_socket_is_stale(fn)) < 0)
         return errno != ENOENT ? -1 : 0;
 
-- 
2.1.3



More information about the pulseaudio-discuss mailing list