[CRIU] [PATCH 2/3] criu: Introduce cr-super client helpers

Cyrill Gorcunov gorcunov at openvz.org
Thu Sep 24 09:37:14 PDT 2015


The helpers allows to work with CAP_SYS_ADMIN
guarded aspects of CRIU (such as parsing
/proc/$pid/map_files entries) in transparent
way.

Signed-off-by: Cyrill Gorcunov <gorcunov at openvz.org>
---
 Makefile                  |   2 +-
 Makefile.crtools          |   1 +
 cr-super-client.c         | 345 ++++++++++++++++++++++++++++++++++++++++++++++
 include/cr-super-client.h |  15 ++
 4 files changed, 362 insertions(+), 1 deletion(-)
 create mode 100644 cr-super-client.c
 create mode 100644 include/cr-super-client.h

diff --git a/Makefile b/Makefile
index 056603cd6c53..08f871c67e1a 100644
--- a/Makefile
+++ b/Makefile
@@ -128,7 +128,7 @@ ifneq ($(PIEGEN),no)
 endif
 endif
 
-cflags-y		+= -iquote include -iquote pie -iquote .
+cflags-y		+= -iquote include -iquote pie -iquote . -iquote super
 cflags-y		+= -iquote $(ARCH_DIR) -iquote $(ARCH_DIR)/include
 cflags-y		+= -fno-strict-aliasing
 export cflags-y
diff --git a/Makefile.crtools b/Makefile.crtools
index 80f704fb4be1..3fec4926eaf3 100644
--- a/Makefile.crtools
+++ b/Makefile.crtools
@@ -74,6 +74,7 @@ obj-y	+= plugin.o
 obj-y	+= cr-errno.o
 obj-y	+= pie/pie-relocs.o
 obj-y	+= seize.o
+obj-y	+= cr-super-client.o
 
 ifneq ($(MAKECMDGOALS),clean)
 incdeps := y
diff --git a/cr-super-client.c b/cr-super-client.c
new file mode 100644
index 000000000000..6aeedb2b49c2
--- /dev/null
+++ b/cr-super-client.c
@@ -0,0 +1,345 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdbool.h>
+#include <signal.h>
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+
+#include "util-pie.h"
+#include "xmalloc.h"
+#include "util.h"
+#include "vma.h"
+#include "log.h"
+
+#include "cr-super-client.h"
+
+#undef LOG_PREFIX
+#define LOG_PREFIX "sup: "
+
+#define CR_SUPER_EXEC_NAME "cr-super"
+
+static uid_t cr_uid;
+static gid_t cr_gid;
+
+static pid_t cr_super_pid;
+static int cr_super_sk[2] = { -1, -1 };
+
+static DIR *map_files_dir;
+static pid_t cached_pid;
+
+static bool local_mode_only = true;
+
+#include "super/nocc-shared.c"
+
+static int __cr_super_open_vma_file(bool local, pid_t pid, struct vma_area *vma)
+{
+	int vm_file_fd = -EIO;
+
+	if (local) {
+		if (cached_pid != pid) {
+			cached_pid = 0;
+
+			if (map_files_dir)
+				closedir(map_files_dir);
+			map_files_dir = opendir_proc(pid, "map_files");
+			if (!map_files_dir)
+				return -1;
+		}
+
+		vm_file_fd = nocc_cr_super_open_mfd_file(dirfd(map_files_dir),
+							 vma->e->start,
+							 vma->e->end, O_PATH);
+	} else {
+		size_t len = sizeof(super_req_mfd_drain_t) + sizeof(super_req_vma_t);
+		super_req_mfd_drain_t *req = alloca(len);
+		super_req_hdr_t hdr;
+		ssize_t ret;
+
+		req->hdr.req_type	= SUPER_REQ_MFD_DRAIN;
+		req->pid		= pid;
+		req->nr_vmas		= 1;
+		req->vma[0].start	= vma->e->start;
+		req->vma[0].end		= vma->e->end;
+
+		ret = send(cr_super_sk[0], req, len, 0);
+		if (ret != len) {
+			pr_err("Can't send the request got %u but expected %u\n",
+			       (unsigned int)ret, (unsigned int)len);
+			goto out;
+		}
+
+		ret = recv(cr_super_sk[0], &hdr, sizeof(hdr), MSG_TRUNC);
+		if (ret != sizeof(hdr)) {
+			pr_err("Can't get response for fd request (got %u but %u expected)\n",
+			       (int)ret, (int)sizeof(hdr));
+			goto out;
+		}
+
+		if (hdr.rsp_type == SUPER_RSP_ERR) {
+			vm_file_fd = hdr.rsp_err;
+
+			pr_debug("Receive error %d for /proc/%d/map-files/%lx-%lx\n",
+				 vm_file_fd, pid,
+				 (unsigned long)vma->e->start,
+				 (unsigned long)vma->e->end);
+			goto out;
+		}
+
+		ret = recv_fds(cr_super_sk[0], &vm_file_fd, 1, NULL);
+		if (ret) {
+			pr_err("Can't receive file descriptor\n");
+			vm_file_fd = -EIO;
+		}
+
+		pr_debug("Receive fd %d for /proc/%d/map-files/%lx-%lx\n",
+			 vm_file_fd, pid,
+			 (unsigned long)vma->e->start,
+			 (unsigned long)vma->e->end);
+	}
+out:
+	return vm_file_fd;
+}
+
+int cr_super_open_vma_file(pid_t pid, struct vma_area *vma)
+{
+	int fd;
+
+	fd = __cr_super_open_vma_file(true, pid, vma);
+	if (fd >= 0 || local_mode_only)
+		return fd;
+	return __cr_super_open_vma_file(false, pid, vma);
+}
+
+static int __cr_super_fetch_vma_file_info(bool local, pid_t pid,
+					  struct vma_area *vma,
+					  super_req_mfd_info_entry_t *info)
+{
+	int ret = -EIO;
+
+	if (local) {
+		if (cached_pid != pid) {
+			cached_pid = 0;
+
+			if (map_files_dir)
+				closedir(map_files_dir);
+			map_files_dir = opendir_proc(pid, "map_files");
+			if (!map_files_dir)
+				return -1;
+		}
+
+		ret = nocc_cr_super_fetch_mfd_info(dirfd(map_files_dir),
+						   (unsigned long)vma->e->start,
+						   (unsigned long)vma->e->end,
+						   info);
+	} else {
+		size_t req_len = sizeof(super_req_mfd_info_t) + sizeof(super_req_vma_t);
+		size_t rsp_len = sizeof(super_req_mfd_info_t) + sizeof(super_req_mfd_info_entry_t);
+
+		super_req_mfd_info_t *req = alloca(max(req_len, rsp_len));
+		super_req_mfd_info_t *rsp = req;
+		ssize_t len;
+
+		req->hdr.req_type	= SUPER_REQ_STAT;
+		req->pid		= pid;
+		req->nr_vmas		= 1;
+		req->u.vma[0].start	= vma->e->start;
+		req->u.vma[0].end	= vma->e->end;
+
+		len = send(cr_super_sk[0], req, req_len, 0);
+		if (len != req_len) {
+			pr_err("Can't send the request got %li but expected %li\n",
+			       (long)req_len, (long)len);
+			goto out;
+		}
+
+		len = recv(cr_super_sk[0], rsp, rsp_len, MSG_TRUNC);
+		if (len != rsp_len) {
+			if (rsp->hdr.rsp_type == SUPER_RSP_ERR)  {
+				pr_debug("Receive error %d for /proc/%d/map-files/%lx-%lx\n",
+					 rsp->hdr.rsp_err, pid,
+					 (unsigned long)vma->e->start,
+					 (unsigned long)vma->e->end);
+				ret = rsp->hdr.rsp_err;
+			} else
+				pr_err("Can't get response for request (len %u but %u expected)\n",
+				       (int)len, (int)rsp_len);
+			goto out;
+		}
+
+		pr_debug("Receive info for /proc/%d/map-files/%lx-%lx\n",
+			 pid, (unsigned long)vma->e->start,
+			 (unsigned long)vma->e->end);
+
+		*info = rsp->u.info[0];
+		ret = 0;
+	}
+
+out:
+	return ret;
+}
+
+int cr_super_fetch_vma_file_info(pid_t pid, struct vma_area *vma,
+				 super_req_mfd_info_entry_t *info)
+{
+	int ret;
+
+	ret = __cr_super_fetch_vma_file_info(true, pid, vma, info);
+	if (ret >= 0 || local_mode_only)
+		return ret;
+	return __cr_super_fetch_vma_file_info(false, pid, vma, info);
+}
+
+void cr_super_fini(void)
+{
+	if (!local_mode_only) {
+		sigset_t blockmask, oldmask;
+		int status, ret;
+
+		pr_debug("Finalizing super service\n");
+
+		sigemptyset(&blockmask);
+		sigaddset(&blockmask, SIGCHLD);
+		if (sigprocmask(SIG_BLOCK, &blockmask, &oldmask) == -1) {
+			pr_perror("Can not set mask of blocked signals");
+			return;
+		}
+
+		if (cr_super_pid) {
+			super_req_hdr_t req_hdr = {
+				.req_type = SUPER_REQ_EXIT,
+			};
+
+			send(cr_super_sk[0], &req_hdr, sizeof(req_hdr), 0);
+
+			close(cr_super_sk[0]), cr_super_sk[0] = -1;
+			close(cr_super_sk[1]), cr_super_sk[1] = -1;
+
+			while (1) {
+				ret = waitpid(cr_super_pid, &status, 0);
+				if (ret == -1) {
+					pr_perror("waitpid() failed");
+					goto out;
+				}
+
+				if (WIFEXITED(status)) {
+					if (WEXITSTATUS(status))
+						pr_err("exited, status=%d\n", WEXITSTATUS(status));
+					break;
+				} else if (WIFSIGNALED(status)) {
+					pr_err("killed by signal %d\n", WTERMSIG(status));
+					break;
+				} else if (WIFSTOPPED(status)) {
+					pr_err("stopped by signal %d\n", WSTOPSIG(status));
+				} else if (WIFCONTINUED(status)) {
+					pr_err("continued\n");
+				}
+			}
+
+out:
+			if (sigprocmask(SIG_SETMASK, &oldmask, NULL) == -1) {
+				pr_perror("Can not unset mask of blocked signals");
+				BUG();
+			}
+
+			cr_super_pid = 0;
+		}
+	}
+
+	if (map_files_dir)
+		closedir(map_files_dir);
+	cached_pid = 0;
+}
+
+int cr_super_init(char *cr_super_exec_path, int logfd, int loglvl)
+{
+	super_req_hdr_t req_hdr = {
+		.req_type = SUPER_REQ_PING,
+	};
+	ssize_t len;
+
+	cr_uid = getuid();
+	cr_gid = getgid();
+
+	pr_debug("Initializing super service (path %s logfd %d loglvl %d)\n",
+		 cr_super_exec_path, logfd, loglvl);
+
+	/*
+	 * If I'm a root, then fine, don't do anything.
+	 * This user is really brave one if running us
+	 * this way.
+	 *
+	 * CRIU says: "Dude, seriously?! As root?! Sigh..."
+	 */
+	if ((cr_uid == 0 && cr_gid == 0) || !cr_super_exec_path) {
+		pr_debug("\tSwitch to local mode\n");
+		local_mode_only = true;
+		return 0;
+	}
+
+	if (!cr_super_exec_path) {
+		cr_super_exec_path = getenv("CRIU_CR_SUPER");
+		if (!cr_super_exec_path) {
+			pr_debug("\nNo path for super tool provided\n");
+			pr_debug("\tSwitch to local mode\n");
+			local_mode_only = true;
+			return 0;
+		}
+	}
+
+	pr_debug("\tUse %s as a super tool\n", cr_super_exec_path);
+
+	if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, cr_super_sk)) {
+		pr_perror("Can't create socket pair\n");
+		return -1;
+	}
+
+	cr_super_pid = fork();
+	if (cr_super_pid == 0) {
+		char socket_fd[10];
+		char log_fd[10];
+		char log_level[10];
+		char *args[] = {
+			cr_super_exec_path,
+			"--socket", socket_fd,
+			"--log-file", log_fd,
+			"-v", log_level,
+			NULL,
+		};
+
+		snprintf(socket_fd, sizeof(socket_fd), "%d", cr_super_sk[1]);
+		snprintf(log_fd, sizeof(log_fd), "%d", logfd);
+		snprintf(log_level, sizeof(log_level), "%d", loglvl);
+
+		if (execvp(cr_super_exec_path, args)) {
+			pr_perror("Can't run %s", cr_super_exec_path);
+			_exit(1);
+		}
+	} else if (cr_super_pid < 0) {
+		pr_perror("Can't fork");
+		goto out;
+	}
+
+	pr_debug("Run %s as %d\n", cr_super_exec_path, cr_super_pid);
+	len = send(cr_super_sk[0], &req_hdr, sizeof(req_hdr), 0);
+	if (len != sizeof(req_hdr)) {
+		pr_perror("Can't hello the service");
+		goto out;
+	}
+
+	len = recv(cr_super_sk[0], &req_hdr, sizeof(req_hdr), MSG_TRUNC);
+	if (len != sizeof(req_hdr) || req_hdr.rsp_type != SUPER_RSP_OK) {
+		pr_err("Can't get hello response\n");
+		goto out;
+	}
+
+	pr_debug("\tGot OK from %s\n", cr_super_exec_path);
+	local_mode_only = false;
+	return 0;
+out:
+	cr_super_fini();
+	return -1;
+}
diff --git a/include/cr-super-client.h b/include/cr-super-client.h
new file mode 100644
index 000000000000..9b30d6fa2eab
--- /dev/null
+++ b/include/cr-super-client.h
@@ -0,0 +1,15 @@
+#ifndef __CR_SUPER_CLIENT_H__
+#define __CR_SUPER_CLIENT_H__
+
+#include "cr-super.h"
+
+struct vma_area;
+
+extern int cr_super_init(char *cr_super_exec_path, int logfd, int loglvl);
+extern void cr_super_fini(void);
+
+extern int cr_super_open_vma_file(pid_t pid, struct vma_area *vma);
+extern int cr_super_fetch_vma_file_info(pid_t pid, struct vma_area *vma,
+					super_req_mfd_info_entry_t *info);
+
+#endif /* __CR_SUPER_CLIENT_H__ */
-- 
2.4.3



More information about the CRIU mailing list