From 467b4b9bee66257da7ce51c1158f9fe2178daac1 Mon Sep 17 00:00:00 2001 From: Colin Guthrie Date: Thu, 16 Oct 2014 10:05:19 +0100 Subject: [PATCH] socket-server: Add support for systemd socket activation. 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 | 68 +++++++++++++++++++++++++++++-------------- src/pulsecore/socket-util.c | 18 ++++++++++++ 5 files changed, 116 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_@PA_MAJORMINOR@_la_CFLAGS += $(X11_CFLAGS) libpulsecommon_@PA_MAJORMINOR@_la_LDFLAGS += $(X11_LIBS) endif +if HAVE_SYSTEMD_DAEMON +libpulsecommon_@PA_MAJORMINOR@_la_CFLAGS += $(SYSTEMD_FLAGS) $(SYSTEMDDAEMON_FLAGS) +libpulsecommon_@PA_MAJORMINOR@_la_LDFLAGS += $(SYSTEMD_LIBS) $(SYSTEMDDAEMON_LIBS) +endif if HAVE_SYSTEMD_JOURNAL libpulsecommon_@PA_MAJORMINOR@_la_CFLAGS += $(SYSTEMD_FLAGS) $(SYSTEMDJOURNAL_FLAGS) libpulsecommon_@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 #endif +#ifdef HAVE_SYSTEMD_DAEMON +#include +#endif + #include #include #include @@ -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 25a2f8a..2138cbc 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 +#endif + #include #include @@ -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,63 @@ 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; @@ -421,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..ccb9442 100644 --- a/src/pulsecore/socket-util.c +++ b/src/pulsecore/socket-util.c @@ -52,6 +52,9 @@ #ifdef HAVE_NETDB_H #include #endif +#ifdef HAVE_SYSTEMD_DAEMON +#include +#endif #include #include @@ -255,6 +258,21 @@ 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.7.4