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

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