[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