Blame SOURCES/0028-Add-systemd-socket-activation.patch

46eaa5
From fa2594ee2f73901b729651f7ed31e37d141a0863 Mon Sep 17 00:00:00 2001
46eaa5
From: Jonathon Jongsma <jjongsma@redhat.com>
46eaa5
Date: Fri, 16 Jun 2017 09:46:56 -0500
46eaa5
Subject: [PATCH linux vdagent] Add systemd socket activation
46eaa5
46eaa5
If we are configured to use the systemd init script, also add support
46eaa5
for systemd socket activation. systemd will listen on the socket that is
46eaa5
used to communicate between the session agent and the system daemon.
46eaa5
When the session agent connects, the system daemon will automatically be
46eaa5
started. The socket will be enabled only if the required virtio-port
46eaa5
device exists. The socket is disabled when the device is removed.
46eaa5
46eaa5
This has a couple minor advantages to the previous approach:
46eaa5
  - For VMS that are not running a graphical desktop (and thus no
46eaa5
    session agents are running), the system vdagent daemon won't get
46eaa5
    started at all even if the spice virtio port is configured. Only the
46eaa5
    socket will be enabled. In the previous approach, the system daemon
46eaa5
    was started when the virtio device was added regardless of whether
46eaa5
    it was needed or not.
46eaa5
  - Solves issues related to switching between systemd targets. With the
46eaa5
    previous approach, when a user switches to a different target
46eaa5
    ("systemctl isolate multi-user.target"), spice-vdagentd.target was
46eaa5
    stopped by systemd (since "isolate" by definition stops all targets
46eaa5
    except the one specified). This meant that if the user subsequently
46eaa5
    switched back to graphical.target, the spice-vdagentd.target would
46eaa5
    still be disabled and vdagent functionality would not work. With
46eaa5
    this change, the socket will still be listening after switching to a
46eaa5
    different target, so as soon as a session agent tries to connect to
46eaa5
    the socket, the system daemon will get restarted.
46eaa5
46eaa5
Fixes: rhbz#1340160
46eaa5
46eaa5
Signed-off-by: Jonathon Jongsma <jjongsma@redhat.com>
46eaa5
Acked-by: Victor Toso <victortoso@redhat.com>
46eaa5
---
46eaa5
 Makefile.am                  |  8 +++----
46eaa5
 configure.ac                 |  8 +++++++
46eaa5
 data/70-spice-vdagentd.rules |  2 +-
46eaa5
 data/spice-vdagentd.service  |  8 +++----
46eaa5
 data/spice-vdagentd.socket   | 10 ++++++++
46eaa5
 data/spice-vdagentd.target   |  2 --
46eaa5
 src/udscs.c                  | 45 ++++++++++++++++++++++++------------
46eaa5
 src/udscs.h                  | 12 +++++++++-
46eaa5
 src/vdagentd.c               | 54 +++++++++++++++++++++++++++++++++++---------
46eaa5
 9 files changed, 110 insertions(+), 39 deletions(-)
46eaa5
 create mode 100644 data/spice-vdagentd.socket
46eaa5
 delete mode 100644 data/spice-vdagentd.target
46eaa5
46eaa5
diff --git a/Makefile.am b/Makefile.am
46eaa5
index 2effe4e..3ba7692 100644
46eaa5
--- a/Makefile.am
46eaa5
+++ b/Makefile.am
46eaa5
@@ -14,9 +14,9 @@ src_spice_vdagent_SOURCES = src/vdagent.c \
46eaa5
                             src/udscs.c
46eaa5
 
46eaa5
 src_spice_vdagentd_CFLAGS = $(DBUS_CFLAGS) $(LIBSYSTEMD_LOGIN_CFLAGS) \
46eaa5
-  $(PCIACCESS_CFLAGS) $(SPICE_CFLAGS) $(GLIB2_CFLAGS) $(PIE_CFLAGS)
46eaa5
+  $(PCIACCESS_CFLAGS) $(SPICE_CFLAGS) $(GLIB2_CFLAGS) $(PIE_CFLAGS) $(LIBSYSTEMD_DAEMON_CFLAGS)
46eaa5
 src_spice_vdagentd_LDADD = $(DBUS_LIBS) $(LIBSYSTEMD_LOGIN_LIBS) \
46eaa5
-  $(PCIACCESS_LIBS) $(SPICE_LIBS) $(GLIB2_LIBS) $(PIE_LDFLAGS)
46eaa5
+  $(PCIACCESS_LIBS) $(SPICE_LIBS) $(GLIB2_LIBS) $(PIE_LDFLAGS) $(LIBSYSTEMD_DAEMON_LIBS)
46eaa5
 src_spice_vdagentd_SOURCES = src/vdagentd.c \
46eaa5
                              src/vdagentd-uinput.c \
46eaa5
                              src/vdagentd-xorg-conf.c \
46eaa5
@@ -66,7 +66,7 @@ if INIT_SCRIPT_SYSTEMD
46eaa5
 systemdunitdir = $(SYSTEMDSYSTEMUNITDIR)
46eaa5
 systemdunit_DATA = \
46eaa5
 	$(top_srcdir)/data/spice-vdagentd.service \
46eaa5
-	$(top_srcdir)/data/spice-vdagentd.target
46eaa5
+	$(top_srcdir)/data/spice-vdagentd.socket
46eaa5
 
46eaa5
 udevrulesdir = /lib/udev/rules.d
46eaa5
 udevrules_DATA = $(top_srcdir)/data/70-spice-vdagentd.rules
46eaa5
@@ -89,7 +89,7 @@ EXTRA_DIST =					\
46eaa5
 	data/spice-vdagent.desktop		\
46eaa5
 	data/spice-vdagentd			\
46eaa5
 	data/spice-vdagentd.service		\
46eaa5
-	data/spice-vdagentd.target		\
46eaa5
+	data/spice-vdagentd.socket		\
46eaa5
 	data/tmpfiles.d/spice-vdagentd.conf	\
46eaa5
 	data/xorg.conf.RHEL-5			\
46eaa5
 	$(NULL)
46eaa5
diff --git a/configure.ac b/configure.ac
46eaa5
index 9903ea9..3ce2208 100644
46eaa5
--- a/configure.ac
46eaa5
+++ b/configure.ac
46eaa5
@@ -65,6 +65,14 @@ AC_MSG_RESULT($with_init_script)
46eaa5
 if test "x$init_systemd" = "xyes"; then
46eaa5
   SYSTEMDSYSTEMUNITDIR=`${PKG_CONFIG} systemd --variable=systemdsystemunitdir`
46eaa5
   AC_SUBST(SYSTEMDSYSTEMUNITDIR)
46eaa5
+  # earlier versions of systemd require a separate libsystemd-daemon library
46eaa5
+  PKG_CHECK_MODULES([LIBSYSTEMD_DAEMON],
46eaa5
+                    [libsystemd >= 209],
46eaa5
+                    [have_libsystemd_daemon="yes"],
46eaa5
+                    [have_libsystemd_daemon="no"])
46eaa5
+  if test "$have_libsystemd_daemon" = "yes"; then
46eaa5
+      AC_DEFINE([WITH_SYSTEMD_SOCKET_ACTIVATION], [1], [If defined, vdagentd will support socket activation with systemd] )
46eaa5
+  fi
46eaa5
 fi
46eaa5
 
46eaa5
 AC_ARG_ENABLE([pciaccess],
46eaa5
diff --git a/data/70-spice-vdagentd.rules b/data/70-spice-vdagentd.rules
46eaa5
index a1785ba..64b4166 100644
46eaa5
--- a/data/70-spice-vdagentd.rules
46eaa5
+++ b/data/70-spice-vdagentd.rules
46eaa5
@@ -1 +1 @@
46eaa5
-ACTION=="add", SUBSYSTEM=="virtio-ports", ENV{DEVLINKS}=="/dev/virtio-ports/com.redhat.spice.0", ENV{SYSTEMD_WANTS}="spice-vdagentd.target"
46eaa5
+ACTION=="add", SUBSYSTEM=="virtio-ports", ENV{DEVLINKS}=="/dev/virtio-ports/com.redhat.spice.0", ENV{SYSTEMD_WANTS}="spice-vdagentd.socket"
46eaa5
diff --git a/data/spice-vdagentd.service b/data/spice-vdagentd.service
46eaa5
index 8c5effe..365b2c1 100644
46eaa5
--- a/data/spice-vdagentd.service
46eaa5
+++ b/data/spice-vdagentd.service
46eaa5
@@ -1,17 +1,15 @@
46eaa5
 [Unit]
46eaa5
 Description=Agent daemon for Spice guests
46eaa5
 After=dbus.target
46eaa5
-
46eaa5
-# TODO we should use:
46eaa5
-#Requires=spice-vdagentd.socket
46eaa5
+Requires=spice-vdagentd.socket
46eaa5
 
46eaa5
 [Service]
46eaa5
 Type=forking
46eaa5
 EnvironmentFile=-/etc/sysconfig/spice-vdagentd
46eaa5
-ExecStartPre=/bin/rm -f /var/run/spice-vdagentd/spice-vdagent-sock
46eaa5
 ExecStart=/usr/sbin/spice-vdagentd $SPICE_VDAGENTD_EXTRA_ARGS
46eaa5
 PIDFile=/var/run/spice-vdagentd/spice-vdagentd.pid
46eaa5
 PrivateTmp=true
46eaa5
+Restart=on-failure
46eaa5
 
46eaa5
 [Install]
46eaa5
-WantedBy=spice-vdagentd.target
46eaa5
+Also=spice-vdagentd.socket
46eaa5
diff --git a/data/spice-vdagentd.socket b/data/spice-vdagentd.socket
46eaa5
new file mode 100644
46eaa5
index 0000000..e34a188
46eaa5
--- /dev/null
46eaa5
+++ b/data/spice-vdagentd.socket
46eaa5
@@ -0,0 +1,10 @@
46eaa5
+[Unit]
46eaa5
+Description=Activation socket for spice guest agent daemon
46eaa5
+# only start the socket if the virtio port device exists
46eaa5
+Requisite=dev-virtio\x2dports-com.redhat.spice.0.device
46eaa5
+
46eaa5
+[Socket]
46eaa5
+ListenStream=/var/run/spice-vdagentd/spice-vdagent-sock
46eaa5
+
46eaa5
+[Install]
46eaa5
+WantedBy=sockets.target
46eaa5
diff --git a/data/spice-vdagentd.target b/data/spice-vdagentd.target
46eaa5
deleted file mode 100644
46eaa5
index 1f74931..0000000
46eaa5
--- a/data/spice-vdagentd.target
46eaa5
+++ /dev/null
46eaa5
@@ -1,2 +0,0 @@
46eaa5
-[Unit]
46eaa5
-Description=Agent daemon for Spice guests
46eaa5
diff --git a/src/udscs.c b/src/udscs.c
46eaa5
index 288aca2..f04a31c 100644
46eaa5
--- a/src/udscs.c
46eaa5
+++ b/src/udscs.c
46eaa5
@@ -81,52 +81,67 @@ static void udscs_do_write(struct udscs_connection **connp);
46eaa5
 static void udscs_do_read(struct udscs_connection **connp);
46eaa5
 
46eaa5
 
46eaa5
-struct udscs_server *udscs_create_server(const char *socketname,
46eaa5
+struct udscs_server *udscs_create_server_for_fd(int fd,
46eaa5
     udscs_connect_callback connect_callback,
46eaa5
     udscs_read_callback read_callback,
46eaa5
     udscs_disconnect_callback disconnect_callback,
46eaa5
     const char * const type_to_string[], int no_types, int debug)
46eaa5
 {
46eaa5
-    int c;
46eaa5
-    struct sockaddr_un address;
46eaa5
     struct udscs_server *server;
46eaa5
 
46eaa5
     server = calloc(1, sizeof(*server));
46eaa5
     if (!server)
46eaa5
         return NULL;
46eaa5
 
46eaa5
+    if (fd <= 0) {
46eaa5
+        syslog(LOG_ERR, "Invalid file descriptor: %i", fd);
46eaa5
+        return NULL;
46eaa5
+    }
46eaa5
+
46eaa5
     server->type_to_string = type_to_string;
46eaa5
     server->no_types = no_types;
46eaa5
     server->debug = debug;
46eaa5
+    server->fd = fd;
46eaa5
+    server->connect_callback = connect_callback;
46eaa5
+    server->read_callback = read_callback;
46eaa5
+    server->disconnect_callback = disconnect_callback;
46eaa5
+
46eaa5
+    return server;
46eaa5
+}
46eaa5
+
46eaa5
+struct udscs_server *udscs_create_server(const char *socketname,
46eaa5
+    udscs_connect_callback connect_callback,
46eaa5
+    udscs_read_callback read_callback,
46eaa5
+    udscs_disconnect_callback disconnect_callback,
46eaa5
+    const char * const type_to_string[], int no_types, int debug)
46eaa5
+{
46eaa5
+    int c;
46eaa5
+    int fd;
46eaa5
+    struct sockaddr_un address;
46eaa5
 
46eaa5
-    server->fd = socket(PF_UNIX, SOCK_STREAM, 0);
46eaa5
-    if (server->fd == -1) {
46eaa5
+    fd = socket(PF_UNIX, SOCK_STREAM, 0);
46eaa5
+    if (fd == -1) {
46eaa5
         syslog(LOG_ERR, "creating unix domain socket: %m");
46eaa5
-        free(server);
46eaa5
         return NULL;
46eaa5
     }
46eaa5
 
46eaa5
     address.sun_family = AF_UNIX;
46eaa5
     snprintf(address.sun_path, sizeof(address.sun_path), "%s", socketname);
46eaa5
-    c = bind(server->fd, (struct sockaddr *)&address, sizeof(address));
46eaa5
+    c = bind(fd, (struct sockaddr *)&address, sizeof(address));
46eaa5
     if (c != 0) {
46eaa5
         syslog(LOG_ERR, "bind %s: %m", socketname);
46eaa5
-        free(server);
46eaa5
         return NULL;
46eaa5
     }
46eaa5
 
46eaa5
-    c = listen(server->fd, 5);
46eaa5
+    c = listen(fd, 5);
46eaa5
     if (c != 0) {
46eaa5
         syslog(LOG_ERR, "listen: %m");
46eaa5
-        free(server);
46eaa5
         return NULL;
46eaa5
     }
46eaa5
 
46eaa5
-    server->connect_callback = connect_callback;
46eaa5
-    server->read_callback = read_callback;
46eaa5
-    server->disconnect_callback = disconnect_callback;
46eaa5
-
46eaa5
-    return server;
46eaa5
+    return udscs_create_server_for_fd(fd, connect_callback, read_callback,
46eaa5
+                                      disconnect_callback, type_to_string,
46eaa5
+                                      no_types, debug);
46eaa5
 }
46eaa5
 
46eaa5
 void udscs_destroy_server(struct udscs_server *server)
46eaa5
diff --git a/src/udscs.h b/src/udscs.h
46eaa5
index 89ab617..cb67059 100644
46eaa5
--- a/src/udscs.h
46eaa5
+++ b/src/udscs.h
46eaa5
@@ -57,6 +57,17 @@ typedef int (*udscs_for_all_clients_callback)(struct udscs_connection **connp,
46eaa5
       by explictly calling udscs_destroy_connection */
46eaa5
 typedef void (*udscs_disconnect_callback)(struct udscs_connection *conn);
46eaa5
 
46eaa5
+/* Create a server for the given file descriptor. This allows us to use
46eaa5
+ * pre-configured sockets for use with systemd socket activation, etc.
46eaa5
+ *
46eaa5
+ * See udscs_create_server() for more information
46eaa5
+ */
46eaa5
+struct udscs_server *udscs_create_server_for_fd(int fd,
46eaa5
+    udscs_connect_callback connect_callback,
46eaa5
+    udscs_read_callback read_callback,
46eaa5
+    udscs_disconnect_callback disconnect_callback,
46eaa5
+    const char * const type_to_string[], int no_types, int debug);
46eaa5
+
46eaa5
 /* Create a unix domain socket named name and start listening on it. */
46eaa5
 struct udscs_server *udscs_create_server(const char *socketname,
46eaa5
     udscs_connect_callback connect_callback,
46eaa5
@@ -98,7 +109,6 @@ void udscs_client_handle_fds(struct udscs_connection **connp, fd_set *readfds,
46eaa5
 
46eaa5
 
46eaa5
 /* Queue a message for delivery to the client connected through conn.
46eaa5
-
46eaa5
    Returns 0 on success -1 on error (only happens when malloc fails) */
46eaa5
 int udscs_write(struct udscs_connection *conn, uint32_t type, uint32_t arg1,
46eaa5
         uint32_t arg2, const uint8_t *data, uint32_t size);
46eaa5
diff --git a/src/vdagentd.c b/src/vdagentd.c
46eaa5
index 59ea8da..d5cc33b 100644
46eaa5
--- a/src/vdagentd.c
46eaa5
+++ b/src/vdagentd.c
46eaa5
@@ -36,6 +36,10 @@
46eaa5
 #include <spice/vd_agent.h>
46eaa5
 #include <glib.h>
46eaa5
 
46eaa5
+#ifdef WITH_SYSTEMD_SOCKET_ACTIVATION
46eaa5
+#include <systemd/sd-daemon.h>
46eaa5
+#endif /* WITH_SYSTEMD_SOCKET_ACTIVATION */
46eaa5
+
46eaa5
 #include "udscs.h"
46eaa5
 #include "vdagentd-proto.h"
46eaa5
 #include "vdagentd-proto-strings.h"
46eaa5
@@ -873,6 +877,7 @@ int main(int argc, char *argv[])
46eaa5
     int do_daemonize = 1;
46eaa5
     int want_session_info = 1;
46eaa5
     struct sigaction act;
46eaa5
+    gboolean own_socket = TRUE;
46eaa5
 
46eaa5
     for (;;) {
46eaa5
         if (-1 == (c = getopt(argc, argv, "-dhxXs:u:")))
46eaa5
@@ -914,20 +919,43 @@ int main(int argc, char *argv[])
46eaa5
     openlog("spice-vdagentd", do_daemonize ? 0 : LOG_PERROR, LOG_USER);
46eaa5
 
46eaa5
     /* Setup communication with vdagent process(es) */
46eaa5
-    server = udscs_create_server(VDAGENTD_SOCKET, agent_connect,
46eaa5
-                                 agent_read_complete, agent_disconnect,
46eaa5
-                                 vdagentd_messages, VDAGENTD_NO_MESSAGES,
46eaa5
-                                 debug);
46eaa5
+#ifdef WITH_SYSTEMD_SOCKET_ACTIVATION
46eaa5
+    int n_fds;
46eaa5
+    /* try to retrieve pre-configured sockets from systemd */
46eaa5
+    n_fds = sd_listen_fds(0);
46eaa5
+    if (n_fds > 1) {
46eaa5
+        syslog(LOG_CRIT, "Received too many sockets from systemd (%i)", n_fds);
46eaa5
+        return 1;
46eaa5
+    } else if (n_fds == 1) {
46eaa5
+        server = udscs_create_server_for_fd(SD_LISTEN_FDS_START, agent_connect,
46eaa5
+                                            agent_read_complete,
46eaa5
+                                            agent_disconnect,
46eaa5
+                                            vdagentd_messages,
46eaa5
+                                            VDAGENTD_NO_MESSAGES, debug);
46eaa5
+        own_socket = FALSE;
46eaa5
+    } else
46eaa5
+    /* systemd socket activation not enabled, create our own */
46eaa5
+#endif /* WITH_SYSTEMD_SOCKET_ACTIVATION */
46eaa5
+    {
46eaa5
+        server = udscs_create_server(VDAGENTD_SOCKET, agent_connect,
46eaa5
+                                     agent_read_complete, agent_disconnect,
46eaa5
+                                     vdagentd_messages, VDAGENTD_NO_MESSAGES,
46eaa5
+                                     debug);
46eaa5
+    }
46eaa5
+
46eaa5
     if (!server) {
46eaa5
         syslog(LOG_CRIT, "Fatal could not create server socket %s",
46eaa5
                VDAGENTD_SOCKET);
46eaa5
         return 1;
46eaa5
     }
46eaa5
-    if (chmod(VDAGENTD_SOCKET, 0666)) {
46eaa5
-        syslog(LOG_CRIT, "Fatal could not change permissions on %s: %m",
46eaa5
-               VDAGENTD_SOCKET);
46eaa5
-        udscs_destroy_server(server);
46eaa5
-        return 1;
46eaa5
+    /* no need to set permissions on a socket that was provided by systemd */
46eaa5
+    if (own_socket) {
46eaa5
+        if (chmod(VDAGENTD_SOCKET, 0666)) {
46eaa5
+            syslog(LOG_CRIT, "Fatal could not change permissions on %s: %m",
46eaa5
+                   VDAGENTD_SOCKET);
46eaa5
+            udscs_destroy_server(server);
46eaa5
+            return 1;
46eaa5
+        }
46eaa5
     }
46eaa5
 
46eaa5
     if (do_daemonize)
46eaa5
@@ -957,8 +985,12 @@ int main(int argc, char *argv[])
46eaa5
     vdagent_virtio_port_destroy(&virtio_port);
46eaa5
     session_info_destroy(session_info);
46eaa5
     udscs_destroy_server(server);
46eaa5
-    if (unlink(VDAGENTD_SOCKET) != 0)
46eaa5
-        syslog(LOG_ERR, "unlink %s: %s", VDAGENTD_SOCKET, strerror(errno));
46eaa5
+
46eaa5
+    /* leave the socket around if it was provided by systemd */
46eaa5
+    if (own_socket) {
46eaa5
+        if (unlink(VDAGENTD_SOCKET) != 0)
46eaa5
+            syslog(LOG_ERR, "unlink %s: %s", VDAGENTD_SOCKET, strerror(errno));
46eaa5
+    }
46eaa5
     syslog(LOG_INFO, "vdagentd quiting, returning status %d", retval);
46eaa5
 
46eaa5
     if (do_daemonize)
46eaa5
-- 
46eaa5
2.13.6
46eaa5