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