[CRIU] [PATCH] systemd socket activation support

Shawn Landden shawn at churchofgit.com
Wed Dec 11 14:54:48 PST 2013


Makes the criu RPC socket socket-activated with
systemd [1], meaning that systemd will create and listen to
the UNIX socket /var/run/criu-srvice.socket
on behalf of criu until a connection comes in, when it will
then pass control of the socket, along with the first connection
over to a newly spawned criu daemon.

This is similar to inetd, but criu stays around after getting
started, listening itsself on the socket.

[1] http://0pointer.de/blog/projects/socket-activation.html

v2: stripped down sd-daemon.[ch]
    moved units to scripts/sd

v3: stripped down further by removing unneeded includes
---
 Makefile                |   3 ++
 Makefile.crtools        |   1 +
 Makefile.inc            |   1 +
 cr-service.c            |  73 ++++++++++++++------------
 scripts/sd/criu.service |   5 ++
 scripts/sd/criu.socket  |   8 +++
 sd-daemon.c             | 132 ++++++++++++++++++++++++++++++++++++++++++++++++
 sd-daemon.h             |  91 +++++++++++++++++++++++++++++++++
 8 files changed, 282 insertions(+), 32 deletions(-)
 create mode 100644 scripts/sd/criu.service
 create mode 100644 scripts/sd/criu.socket
 create mode 100644 sd-daemon.c
 create mode 100644 sd-daemon.h

diff --git a/Makefile b/Makefile
index cd312e7..c9a6042 100644
--- a/Makefile
+++ b/Makefile
@@ -232,6 +232,9 @@ install: $(PROGRAM) install-man
 	$(E) "  INSTALL " $(PROGRAM)
 	$(Q) mkdir -p $(DESTDIR)$(SBINDIR)
 	$(Q) install -m 755 $(PROGRAM) $(DESTDIR)$(SBINDIR)
+	$(Q) mkdir -p $(DESTDIR)$(SYSTEMDUNITDIR)
+	$(Q) install -m 644 scripts/sd/criu.socket $(DESTDIR)$(SYSTEMDUNITDIR)
+	$(Q) install -m 644 scripts/sd/criu.service $(DESTDIR)$(SYSTEMDUNITDIR)
 
 install-man:
 	$(Q) $(MAKE) -C Documentation install
diff --git a/Makefile.crtools b/Makefile.crtools
index 71e9d1c..18140fd 100644
--- a/Makefile.crtools
+++ b/Makefile.crtools
@@ -57,6 +57,7 @@ obj-y	+= string.o
 obj-y	+= sigframe.o
 obj-y	+= arch/$(ARCH)/vdso.o
 obj-y	+= cr-service.o
+obj-y	+= sd-daemon.o
 
 ifneq ($(MAKECMDGOALS),clean)
 incdeps := y
diff --git a/Makefile.inc b/Makefile.inc
index 23b4d9e..87a267a 100644
--- a/Makefile.inc
+++ b/Makefile.inc
@@ -14,3 +14,4 @@ export E Q
 PREFIX		:= /usr/local
 SBINDIR		:= $(PREFIX)/sbin
 MANDIR		:= $(PREFIX)/share/man
+SYSTEMDUNITDIR	:= $(PREFIX)/lib/systemd/system/
diff --git a/cr-service.c b/cr-service.c
index a82ef9e..88ae183 100644
--- a/cr-service.c
+++ b/cr-service.c
@@ -19,6 +19,7 @@
 #include "log.h"
 #include "pstree.h"
 #include "cr-service.h"
+#include "sd-daemon.h"
 
 unsigned int service_sk_ino = -1;
 
@@ -332,53 +333,61 @@ static int restore_sigchld_handler()
 
 int cr_service(bool daemon_mode)
 {
-	int server_fd = -1;
+	int server_fd = -1, n;
 	int child_pid;
 
-	struct sockaddr_un server_addr;
 	struct sockaddr_un client_addr;
-
-	socklen_t server_addr_len;
 	socklen_t client_addr_len;
 
-	server_fd = socket(AF_LOCAL, SOCK_SEQPACKET, 0);
-	if (server_fd == -1) {
-		pr_perror("Can't initialize service socket.");
+	n = sd_listen_fds(0);
+	if (n > 1) {
+		pr_perror("Too many file descriptors (%d) recieved.", n);
 		goto err;
-	}
+	} else if (n == 1)
+		server_fd = SD_LISTEN_FDS_START + 0;
+	else {
+		struct sockaddr_un server_addr;
+		socklen_t server_addr_len;
+
+		server_fd = socket(AF_LOCAL, SOCK_SEQPACKET, 0);
+		if (server_fd == -1) {
+			pr_perror("Can't initialize service socket.");
+			goto err;
+		}
 
-	memset(&server_addr, 0, sizeof(server_addr));
-	memset(&client_addr, 0, sizeof(client_addr));
-	server_addr.sun_family = AF_LOCAL;
+		memset(&server_addr, 0, sizeof(server_addr));
+		memset(&client_addr, 0, sizeof(client_addr));
+		server_addr.sun_family = AF_LOCAL;
 
-	if (opts.addr == NULL)
-		opts.addr = CR_DEFAULT_SERVICE_ADDRESS;
+		if (opts.addr == NULL)
+			opts.addr = CR_DEFAULT_SERVICE_ADDRESS;
 
-	strcpy(server_addr.sun_path, opts.addr);
+		strcpy(server_addr.sun_path, opts.addr);
 
-	server_addr_len = strlen(server_addr.sun_path)
-			+ sizeof(server_addr.sun_family);
-	client_addr_len = sizeof(client_addr);
+		server_addr_len = strlen(server_addr.sun_path)
+				+ sizeof(server_addr.sun_family);
+		client_addr_len = sizeof(client_addr);
 
-	unlink(server_addr.sun_path);
+		unlink(server_addr.sun_path);
 
-	if (bind(server_fd, (struct sockaddr *) &server_addr,
-					server_addr_len) == -1) {
-		pr_perror("Can't bind.");
-		goto err;
-	}
+		if (bind(server_fd, (struct sockaddr *) &server_addr,
+						server_addr_len) == -1) {
+			pr_perror("Can't bind.");
+			goto err;
+		}
 
-	pr_info("The service socket is bound to %s\n", server_addr.sun_path);
+		pr_info("The service socket is bound to %s\n", server_addr.sun_path);
 
-	/* change service socket permissions, so anyone can connect to it */
-	if (chmod(server_addr.sun_path, 0666)) {
-		pr_perror("Can't change permissions of the service socket.");
-		goto err;
-	}
+		/* change service socket permissions, so anyone can connect to it */
+		if (chmod(server_addr.sun_path, 0666)) {
+			pr_perror("Can't change permissions of the service socket.");
+			goto err;
+		}
 
-	if (listen(server_fd, 16) == -1) {
-		pr_perror("Can't listen for socket connections.");
-		goto err;
+		if (listen(server_fd, 16) == -1) {
+			pr_perror("Can't listen for socket connections.");
+			goto err;
+		}
 	}
 
 	if (daemon_mode) {
diff --git a/scripts/sd/criu.service b/scripts/sd/criu.service
new file mode 100644
index 0000000..803ce46
--- /dev/null
+++ b/scripts/sd/criu.service
@@ -0,0 +1,5 @@
+[Unit]
+Description=Checkpoint Restore in Userspace daemon
+
+[Service]
+ExecStart=/usr/sbin/criu service
diff --git a/scripts/sd/criu.socket b/scripts/sd/criu.socket
new file mode 100644
index 0000000..7ccf7b4
--- /dev/null
+++ b/scripts/sd/criu.socket
@@ -0,0 +1,8 @@
+[Unit]
+Description=Checkpoint Restore in Userspace socket
+
+[Socket]
+ListenSequentialPacket=/var/run/criu-service.socket
+
+[Install]
+WantedBy=sockets.target
diff --git a/sd-daemon.c b/sd-daemon.c
new file mode 100644
index 0000000..653fbf5
--- /dev/null
+++ b/sd-daemon.c
@@ -0,0 +1,132 @@
+/* stripped down version */
+/***
+  Copyright 2010 Lennart Poettering
+
+  Permission is hereby granted, free of charge, to any person
+  obtaining a copy of this software and associated documentation files
+  (the "Software"), to deal in the Software without restriction,
+  including without limitation the rights to use, copy, modify, merge,
+  publish, distribute, sublicense, and/or sell copies of the Software,
+  and to permit persons to whom the Software is furnished to do so,
+  subject to the following conditions:
+
+  The above copyright notice and this permission notice shall be
+  included in all copies or substantial portions of the Software.
+
+  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+  BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+  ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+  CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+  SOFTWARE.
+***/
+
+#ifndef _GNU_SOURCE
+#  define _GNU_SOURCE
+#endif
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#include "sd-daemon.h"
+
+#if (__GNUC__ >= 4)
+#  ifdef SD_EXPORT_SYMBOLS
+/* Export symbols */
+#    define _sd_export_ __attribute__ ((visibility("default")))
+#  else
+/* Don't export the symbols */
+#    define _sd_export_ __attribute__ ((visibility("hidden")))
+#  endif
+#else
+#  define _sd_export_
+#endif
+
+_sd_export_ int sd_listen_fds(int unset_environment) {
+
+#if defined(DISABLE_SYSTEMD) || !defined(__linux__)
+        return 0;
+#else
+        int r, fd;
+        const char *e;
+        char *p = NULL;
+        unsigned long l;
+
+        e = getenv("LISTEN_PID");
+        if (!e) {
+                r = 0;
+                goto finish;
+        }
+
+        errno = 0;
+        l = strtoul(e, &p, 10);
+
+        if (errno > 0) {
+                r = -errno;
+                goto finish;
+        }
+
+        if (!p || p == e || *p || l <= 0) {
+                r = -EINVAL;
+                goto finish;
+        }
+
+        /* Is this for us? */
+        if (getpid() != (pid_t) l) {
+                r = 0;
+                goto finish;
+        }
+
+        e = getenv("LISTEN_FDS");
+        if (!e) {
+                r = 0;
+                goto finish;
+        }
+
+        errno = 0;
+        l = strtoul(e, &p, 10);
+
+        if (errno > 0) {
+                r = -errno;
+                goto finish;
+        }
+
+        if (!p || p == e || *p) {
+                r = -EINVAL;
+                goto finish;
+        }
+
+        for (fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + (int) l; fd ++) {
+                int flags;
+
+                flags = fcntl(fd, F_GETFD);
+                if (flags < 0) {
+                        r = -errno;
+                        goto finish;
+                }
+
+                if (flags & FD_CLOEXEC)
+                        continue;
+
+                if (fcntl(fd, F_SETFD, flags | FD_CLOEXEC) < 0) {
+                        r = -errno;
+                        goto finish;
+                }
+        }
+
+        r = (int) l;
+
+finish:
+        if (unset_environment) {
+                unsetenv("LISTEN_PID");
+                unsetenv("LISTEN_FDS");
+        }
+
+        return r;
+#endif
+}
+
diff --git a/sd-daemon.h b/sd-daemon.h
new file mode 100644
index 0000000..f6f2959
--- /dev/null
+++ b/sd-daemon.h
@@ -0,0 +1,91 @@
+/* stripped down version */
+
+#ifndef foosddaemonhfoo
+#define foosddaemonhfoo
+
+/***
+  Copyright 2010 Lennart Poettering
+
+  Permission is hereby granted, free of charge, to any person
+  obtaining a copy of this software and associated documentation files
+  (the "Software"), to deal in the Software without restriction,
+  including without limitation the rights to use, copy, modify, merge,
+  publish, distribute, sublicense, and/or sell copies of the Software,
+  and to permit persons to whom the Software is furnished to do so,
+  subject to the following conditions:
+
+  The above copyright notice and this permission notice shall be
+  included in all copies or substantial portions of the Software.
+
+  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+  BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+  ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+  CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+  SOFTWARE.
+***/
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+  Reference implementation of a few systemd related interfaces for
+  writing daemons. These interfaces are trivial to implement. To
+  simplify porting we provide this reference implementation.
+  Applications are welcome to reimplement the algorithms described
+  here if they do not want to include these two source files.
+
+  The following functionality is provided:
+
+  - Support for logging with log levels on stderr
+  - File descriptor passing for socket-based activation
+  - Daemon startup and status notification
+  - Detection of systemd boots
+
+  You may compile this with -DDISABLE_SYSTEMD to disable systemd
+  support. This makes all those calls NOPs that are directly related to
+  systemd (i.e. only sd_is_xxx() will stay useful).
+
+  Since this is drop-in code we don't want any of our symbols to be
+  exported in any case. Hence we declare hidden visibility for all of
+  them.
+
+  You may find an up-to-date version of these source files online:
+
+  http://cgit.freedesktop.org/systemd/systemd/plain/src/systemd/sd-daemon.h
+  http://cgit.freedesktop.org/systemd/systemd/plain/src/libsystemd-daemon/sd-daemon.c
+
+  This should compile on non-Linux systems, too, but with the
+  exception of the sd_is_xxx() calls all functions will become NOPs.
+
+  See sd-daemon(3) for more information.
+*/
+
+/* The first passed file descriptor is fd 3 */
+#define SD_LISTEN_FDS_START 3
+
+/*
+  Returns how many file descriptors have been passed, or a negative
+  errno code on failure. Optionally, removes the $LISTEN_FDS and
+  $LISTEN_PID file descriptors from the environment (recommended, but
+  problematic in threaded environments). If r is the return value of
+  this function you'll find the file descriptors passed as fds
+  SD_LISTEN_FDS_START to SD_LISTEN_FDS_START+r-1. Returns a negative
+  errno style error code on failure. This function call ensures that
+  the FD_CLOEXEC flag is set for the passed file descriptors, to make
+  sure they are not passed on to child processes. If FD_CLOEXEC shall
+  not be set, the caller needs to unset it after this call for all file
+  descriptors that are used.
+
+  See sd_listen_fds(3) for more information.
+*/
+int sd_listen_fds(int unset_environment);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
-- 
1.8.5.1



More information about the CRIU mailing list