[Spice-devel] [PATCH linux vdagent] Add systemd socket activation
Fabiano FidĂȘncio
fabiano at fidencio.org
Wed Jul 19 22:48:31 UTC 2017
On Thu, Jul 13, 2017 at 10:35 PM, Jonathon Jongsma <jjongsma at redhat.com> wrote:
> If we are configured to use the systemd init script, also add support
> for systemd socket activation. systemd will listen on the socket that is
> used to communicate between the session agent and the system daemon.
> When the session agent connects, the system daemon will automatically be
> started. The socket will be enabled only if the required virtio-port
> device exists. The socket is disabled when the device is removed.
>
> This has a couple minor advantages to the previous approach:
> - For VMS that are not running a graphical desktop (and thus no
> session agents are running), the system vdagent daemon won't get
> started at all even if the spice virtio port is configured. Only the
> socket will be enabled. In the previous approach, the system daemon
> was started when the virtio device was added regardless of whether
> it was needed or not.
> - Solves issues related to switching between systemd targets. With the
> previous approach, when a user switches to a different target
> ("systemctl isolate multi-user.target"), spice-vdagentd.target was
> stopped by systemd (since "isolate" by definition stops all targets
> except the one specified). This meant that if the user subsequently
> switched back to graphical.target, the spice-vdagentd.target would
> still be disabled and vdagent functionality would not work. With
> this change, the socket will still be listening after switching to a
> different target, so as soon as a session agent tries to connect to
> the socket, the system daemon will get restarted.
>
> Fixes: rhbz#1340160
>
> Signed-off-by: Jonathon Jongsma <jjongsma at redhat.com>
> ---
> NOTES:
> - this patch piggybacks onto the --with-init-script configure option to
> determine whether to use systemd socket activation. I'm not sure whether
> that's the correct choice or not, but it seems to work ok. If the init
> script is specified as "systemd" or "systemd+redhat" and we have a new
> enough libsystemd, we attempt to use socket activation.
> - Even if the agent is configured with systemd socket activation, it should
> still fall back to opening its own socket if it fails to get a socket from
> systemd (for example, if it is running under a different init system).
> - In theory, we could use socket activation with systemd < 209, but then we'd
> have to link against the separate libsystemd-daemon library. Since this is a
> new feature, I decided to only support more recent versions of systemd. But
> support for older versions could be added pretty easily.
>
>
> Makefile.am | 5 +++-
> configure.ac | 8 +++++++
> data/70-spice-vdagentd.rules | 2 +-
> data/spice-vdagentd.service | 8 +------
> data/spice-vdagentd.socket | 10 ++++++++
> data/spice-vdagentd.target | 2 --
> src/udscs.c | 45 ++++++++++++++++++++++++------------
> src/udscs.h | 11 +++++++++
> src/vdagentd/vdagentd.c | 55 +++++++++++++++++++++++++++++++++++---------
> 9 files changed, 109 insertions(+), 37 deletions(-)
> create mode 100644 data/spice-vdagentd.socket
> delete mode 100644 data/spice-vdagentd.target
>
> diff --git a/Makefile.am b/Makefile.am
> index 7755f09..f70d514 100644
> --- a/Makefile.am
> +++ b/Makefile.am
> @@ -42,6 +42,7 @@ src_spice_vdagent_SOURCES = \
>
> src_spice_vdagentd_CFLAGS = \
> $(DBUS_CFLAGS) \
> + $(LIBSYSTEMD_DAEMON_CFLAGS) \
> $(LIBSYSTEMD_LOGIN_CFLAGS) \
> $(PCIACCESS_CFLAGS) \
> $(SPICE_CFLAGS) \
> @@ -52,6 +53,7 @@ src_spice_vdagentd_CFLAGS = \
>
> src_spice_vdagentd_LDADD = \
> $(DBUS_LIBS) \
> + $(LIBSYSTEMD_DAEMON_LIBS) \
> $(LIBSYSTEMD_LOGIN_LIBS) \
> $(PCIACCESS_LIBS) \
> $(SPICE_LIBS) \
> @@ -102,7 +104,7 @@ if INIT_SCRIPT_SYSTEMD
> systemdunitdir = $(SYSTEMDSYSTEMUNITDIR)
> systemdunit_DATA = \
> $(top_srcdir)/data/spice-vdagentd.service \
> - $(top_srcdir)/data/spice-vdagentd.target
> + $(top_srcdir)/data/spice-vdagentd.socket
>
> udevrulesdir = /lib/udev/rules.d
> udevrules_DATA = $(top_srcdir)/data/70-spice-vdagentd.rules
> @@ -124,6 +126,7 @@ EXTRA_DIST = \
> data/spice-vdagent.desktop \
> data/spice-vdagentd \
> data/spice-vdagentd.service \
> + data/spice-vdagentd.socket \
> data/spice-vdagentd.target \
> data/tmpfiles.d/spice-vdagentd.conf \
> data/xorg.conf.RHEL-5 \
> diff --git a/configure.ac b/configure.ac
> index f207240..fbc20a9 100644
> --- a/configure.ac
> +++ b/configure.ac
> @@ -65,6 +65,14 @@ AC_MSG_RESULT($with_init_script)
> if test "x$init_systemd" = "xyes"; then
> SYSTEMDSYSTEMUNITDIR=`${PKG_CONFIG} systemd --variable=systemdsystemunitdir`
> AC_SUBST(SYSTEMDSYSTEMUNITDIR)
> + # earlier versions of systemd require a separate libsystemd-daemon library
> + PKG_CHECK_MODULES([LIBSYSTEMD_DAEMON],
> + [libsystemd >= 209],
> + [have_libsystemd_daemon="yes"],
> + [have_libsystemd_daemon="no"])
> + if test "$have_libsystemd_daemon" = "yes"; then
> + AC_DEFINE([WITH_SYSTEMD_SOCKET_ACTIVATION], [1], [If defined, vdagentd will support socket activation with systemd] )
> + fi
> fi
>
> AC_ARG_ENABLE([pciaccess],
> diff --git a/data/70-spice-vdagentd.rules b/data/70-spice-vdagentd.rules
> index a1785ba..64b4166 100644
> --- a/data/70-spice-vdagentd.rules
> +++ b/data/70-spice-vdagentd.rules
> @@ -1 +1 @@
> -ACTION=="add", SUBSYSTEM=="virtio-ports", ENV{DEVLINKS}=="/dev/virtio-ports/com.redhat.spice.0", ENV{SYSTEMD_WANTS}="spice-vdagentd.target"
> +ACTION=="add", SUBSYSTEM=="virtio-ports", ENV{DEVLINKS}=="/dev/virtio-ports/com.redhat.spice.0", ENV{SYSTEMD_WANTS}="spice-vdagentd.socket"
> diff --git a/data/spice-vdagentd.service b/data/spice-vdagentd.service
> index 8c5effe..c6a890a 100644
> --- a/data/spice-vdagentd.service
> +++ b/data/spice-vdagentd.service
> @@ -1,17 +1,11 @@
> [Unit]
> Description=Agent daemon for Spice guests
> After=dbus.target
> -
> -# TODO we should use:
> -#Requires=spice-vdagentd.socket
> +Requires=spice-vdagentd.socket
Is this really necessary? IIRC it's not when .service and the .socket
have the very same name.
>
> [Service]
> Type=forking
> EnvironmentFile=-/etc/sysconfig/spice-vdagentd
> -ExecStartPre=/bin/rm -f /var/run/spice-vdagentd/spice-vdagent-sock
> ExecStart=/usr/sbin/spice-vdagentd $SPICE_VDAGENTD_EXTRA_ARGS
> PIDFile=/var/run/spice-vdagentd/spice-vdagentd.pid
> PrivateTmp=true
Don't we want to have the service restared on failure as well?
Restart=on-failure
> -
> -[Install]
> -WantedBy=spice-vdagentd.target
It's a quite common practice to add "Also=<unit's name>.socket in the
Install part, to ensure proper installation/uninstallation when using
systemctl enable/disable.
> diff --git a/data/spice-vdagentd.socket b/data/spice-vdagentd.socket
> new file mode 100644
> index 0000000..e34a188
> --- /dev/null
> +++ b/data/spice-vdagentd.socket
> @@ -0,0 +1,10 @@
> +[Unit]
> +Description=Activation socket for spice guest agent daemon
> +# only start the socket if the virtio port device exists
> +Requisite=dev-virtio\x2dports-com.redhat.spice.0.device
> +
> +[Socket]
> +ListenStream=/var/run/spice-vdagentd/spice-vdagent-sock
> +
> +[Install]
> +WantedBy=sockets.target
> diff --git a/data/spice-vdagentd.target b/data/spice-vdagentd.target
> deleted file mode 100644
> index 1f74931..0000000
> --- a/data/spice-vdagentd.target
> +++ /dev/null
> @@ -1,2 +0,0 @@
> -[Unit]
> -Description=Agent daemon for Spice guests
> diff --git a/src/udscs.c b/src/udscs.c
> index fdd75a4..8b16f89 100644
> --- a/src/udscs.c
> +++ b/src/udscs.c
> @@ -369,16 +369,19 @@ struct udscs_server {
> udscs_disconnect_callback disconnect_callback;
> };
>
> -struct udscs_server *udscs_create_server(const char *socketname,
> +struct udscs_server *udscs_create_server_for_fd(int fd,
> udscs_connect_callback connect_callback,
> udscs_read_callback read_callback,
> udscs_disconnect_callback disconnect_callback,
> const char * const type_to_string[], int no_types, int debug)
> {
> - int c;
> - struct sockaddr_un address;
> struct udscs_server *server;
>
> + if (fd <= 0) {
> + syslog(LOG_ERR, "Invalid file descriptor: %i", fd);
> + return NULL;
> + }
> +
> server = calloc(1, sizeof(*server));
> if (!server)
> return NULL;
> @@ -386,35 +389,47 @@ struct udscs_server *udscs_create_server(const char *socketname,
> server->type_to_string = type_to_string;
> server->no_types = no_types;
> server->debug = debug;
> + server->fd = fd;
> + server->connect_callback = connect_callback;
> + server->read_callback = read_callback;
> + server->disconnect_callback = disconnect_callback;
> +
> + return server;
> +}
> +
> +struct udscs_server *udscs_create_server(const char *socketname,
> + udscs_connect_callback connect_callback,
> + udscs_read_callback read_callback,
> + udscs_disconnect_callback disconnect_callback,
> + const char * const type_to_string[], int no_types, int debug)
> +{
> + int c;
> + int fd;
> + struct sockaddr_un address;
>
> - server->fd = socket(PF_UNIX, SOCK_STREAM, 0);
> - if (server->fd == -1) {
> + fd = socket(PF_UNIX, SOCK_STREAM, 0);
> + if (fd == -1) {
> syslog(LOG_ERR, "creating unix domain socket: %m");
> - free(server);
> return NULL;
> }
>
> address.sun_family = AF_UNIX;
> snprintf(address.sun_path, sizeof(address.sun_path), "%s", socketname);
> - c = bind(server->fd, (struct sockaddr *)&address, sizeof(address));
> + c = bind(fd, (struct sockaddr *)&address, sizeof(address));
> if (c != 0) {
> syslog(LOG_ERR, "bind %s: %m", socketname);
> - free(server);
> return NULL;
> }
>
> - c = listen(server->fd, 5);
> + c = listen(fd, 5);
> if (c != 0) {
> syslog(LOG_ERR, "listen: %m");
> - free(server);
> return NULL;
> }
>
> - server->connect_callback = connect_callback;
> - server->read_callback = read_callback;
> - server->disconnect_callback = disconnect_callback;
> -
> - return server;
> + return udscs_create_server_for_fd(fd, connect_callback, read_callback,
> + disconnect_callback, type_to_string,
> + no_types, debug);
> }
>
> void udscs_destroy_server(struct udscs_server *server)
> diff --git a/src/udscs.h b/src/udscs.h
> index 7b6bff0..30a96db 100644
> --- a/src/udscs.h
> +++ b/src/udscs.h
> @@ -120,6 +120,17 @@ struct udscs_server;
> */
> typedef void (*udscs_connect_callback)(struct udscs_connection *conn);
>
> +/* Create a server for the given file descriptor. This allows us to use
> + * pre-configured sockets for use with systemd socket activation, etc.
> + *
> + * See udscs_create_server() for more information
> + */
> +struct udscs_server *udscs_create_server_for_fd(int fd,
> + udscs_connect_callback connect_callback,
> + udscs_read_callback read_callback,
> + udscs_disconnect_callback disconnect_callback,
> + const char * const type_to_string[], int no_types, int debug);
> +
> /* Create the unix domain socket specified by socketname and
> * start listening on it.
> * Only sockets bound to a pathname are supported.
> diff --git a/src/vdagentd/vdagentd.c b/src/vdagentd/vdagentd.c
> index 1f10f1c..eb6834a 100644
> --- a/src/vdagentd/vdagentd.c
> +++ b/src/vdagentd/vdagentd.c
> @@ -36,6 +36,10 @@
> #include <spice/vd_agent.h>
> #include <glib.h>
>
> +#ifdef WITH_SYSTEMD_SOCKET_ACTIVATION
> +#include <systemd/sd-daemon.h>
> +#endif /* WITH_SYSTEMD_SOCKET_ACTIVATION */
> +
> #include "udscs.h"
> #include "vdagentd-proto.h"
> #include "vdagentd-proto-strings.h"
> @@ -1083,6 +1087,7 @@ int main(int argc, char *argv[])
> int do_daemonize = 1;
> int want_session_info = 1;
> struct sigaction act;
> + gboolean own_socket = TRUE;
>
> for (;;) {
> if (-1 == (c = getopt(argc, argv, "-dhxXfos:u:S:")))
> @@ -1133,10 +1138,30 @@ int main(int argc, char *argv[])
> openlog("spice-vdagentd", do_daemonize ? 0 : LOG_PERROR, LOG_USER);
>
> /* Setup communication with vdagent process(es) */
> - server = udscs_create_server(vdagentd_socket, agent_connect,
> - agent_read_complete, agent_disconnect,
> - vdagentd_messages, VDAGENTD_NO_MESSAGES,
> - debug);
> +#ifdef WITH_SYSTEMD_SOCKET_ACTIVATION
> + int n_fds;
> + /* try to retrieve pre-configured sockets from systemd */
> + n_fds = sd_listen_fds(0);
> + if (n_fds > 1) {
> + syslog(LOG_CRIT, "Recieved too many sockets from systemd (%i)", n_fds);
> + return 1;
> + } else if (n_fds == 1) {
> + server = udscs_create_server_for_fd(SD_LISTEN_FDS_START, agent_connect,
> + agent_read_complete,
> + agent_disconnect,
> + vdagentd_messages,
> + VDAGENTD_NO_MESSAGES, debug);
> + own_socket = FALSE;
> + } else
> + /* systemd socket activation not enabled, create our own */
> +#endif /* WITH_SYSTEMD_SOCKET_ACTIVATION */
> + {
> + server = udscs_create_server(vdagentd_socket, agent_connect,
> + agent_read_complete, agent_disconnect,
> + vdagentd_messages, VDAGENTD_NO_MESSAGES,
> + debug);
> + }
> +
> if (!server) {
> if (errno == EADDRINUSE) {
> syslog(LOG_CRIT, "Fatal the server socket %s exists already. Delete it?",
> @@ -1147,11 +1172,15 @@ int main(int argc, char *argv[])
> }
> return 1;
> }
> - if (chmod(vdagentd_socket, 0666)) {
> - syslog(LOG_CRIT, "Fatal could not change permissions on %s: %m",
> - vdagentd_socket);
> - udscs_destroy_server(server);
> - return 1;
> +
> + /* no need to set permissions on a socket that was provided by systemd */
> + if (own_socket) {
> + if (chmod(vdagentd_socket, 0666)) {
> + syslog(LOG_CRIT, "Fatal could not change permissions on %s: %m",
> + vdagentd_socket);
> + udscs_destroy_server(server);
> + return 1;
> + }
> }
>
> if (do_daemonize)
> @@ -1181,8 +1210,12 @@ int main(int argc, char *argv[])
> vdagent_virtio_port_destroy(&virtio_port);
> session_info_destroy(session_info);
> udscs_destroy_server(server);
> - if (unlink(vdagentd_socket) != 0)
> - syslog(LOG_ERR, "unlink %s: %s", vdagentd_socket, strerror(errno));
> +
> + /* leave the socket around if it was provided by systemd */
> + if (own_socket) {
> + if (unlink(vdagentd_socket) != 0)
> + syslog(LOG_ERR, "unlink %s: %s", vdagentd_socket, strerror(errno));
> + }
> syslog(LOG_INFO, "vdagentd quitting, returning status %d", retval);
>
> if (do_daemonize)
> --
> 2.9.4
>
> _______________________________________________
> Spice-devel mailing list
> Spice-devel at lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/spice-devel
--
Fabiano FidĂȘncio
More information about the Spice-devel
mailing list