[CRIU] [PATCHv5 3/3] crtools: cr_service() meat and a few fixes to properly dump cr_service socket

Ruslan Kuprieiev kupruser at gmail.com
Mon Sep 9 08:14:04 EDT 2013


Signed-off-by: Ruslan Kuprieiev kupruser at gmail.com

-------------- next part --------------
diff --git a/cr-service.c b/cr-service.c
index e921828..d0b3c0e 100644
--- a/cr-service.c
+++ b/cr-service.c
@@ -1,4 +1,232 @@
-int cr_service(void)
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <sys/wait.h>
+#include <sys/stat.h>
+
+#include "crtools.h"
+#include "util-pie.h"
+#include "log.h"
+#include "protobuf.h"
+#include "protobuf/criu-dump-req.pb-c.h"
+#include "protobuf/criu-dump-resp.pb-c.h"
+
+static int client_fd;
+
+/*
+ * This function is needed in sk-unix.c,
+ * to recognize connection to service socket
+ * and treat it properly.
+ */
+int get_lib_sk_ino(void)
 {
+	struct stat st;
+
+	if (fstat(client_fd, &st)) {
+		pr_perror("Can't get library socket stat");
+		return -1;
+	}
+
+	return st.st_ino;
+}
+
+static int treat_req(CriuDumpReq *req, CriuDumpResp *resp)
+{
+	struct ucred ids;
+	struct stat st;
+	socklen_t ids_len = sizeof(struct ucred);
+
+	if (getsockopt(client_fd, SOL_SOCKET, SO_PEERCRED, &ids, &ids_len)) {
+		pr_perror("Can't get socket options.");
+		goto err;
+	}
+
+	if (req->pid == -1) {
+		req->pid = ids.pid;
+	} else if (req->pid != ids.pid) {
+		/*
+		 * FIXME Add some flag to resp for this case.
+		 */
+		if (ids.uid != 0) {
+			pr_perror("Client has no permissions to dump task %d",
+								     req->pid);
+			goto err;
+		}
+	}
+
+	/* going to dir, where to place images*/
+	if (req->images_dir == NULL) {
+		pr_perror("No images dir");
+		goto err;
+	}
+
+	if (stat(req->images_dir, &st) == -1) {
+		pr_perror("Can't stat images direcrtory");
+		goto err;
+	}
+
+	if (ids.uid == st.st_uid || ids.gid == st.st_gid || ids.uid == 0) {
+		if (chdir(req->images_dir)) {
+			pr_perror("Can't change dir");
+			goto err;
+		}
+	} else {
+		pr_perror("Client has no permissions to use images directory");
+		goto err;
+	}
+
+	/* initiate log file in imgs dir */
+	opts.output = xmalloc(sizeof(char)*PATH_MAX);
+	strcpy(opts.output, getcwd(NULL, PATH_MAX));
+	strcat(opts.output, "/dump.log");
+
+	if (open_image_dir() < 0) {
+		pr_perror("Can't open current directory.");
+		goto err;
+	}
+
+	/* setting loglevel */
+	log_set_loglevel(req->log_level);
+	if (log_init(opts.output) == -1) {
+		pr_perror("Can't initiate log.");
+		goto err;
+	}
+
+	/* checking dump flags from client */
+	if (req->leave_running)
+		opts.final_state = TASK_ALIVE;
+
+	opts.ext_unix_sk	= req->ext_unix_sk;
+	opts.tcp_established_ok	= req->tcp_established;
+	opts.evasive_devices	= req->evasive_devices;
+	opts.shell_job		= req->shell_job;
+	opts.handle_file_locks	= req->file_locks;
+
+	return 0;
+
+err:	resp->bad_args	= true;
+	return -1;
+}
+
+int cr_service(bool daemon_mode)
+{
+	int server_fd;
+	int child_pid;
+	struct sockaddr_un server_addr;
+	struct sockaddr_un client_addr;
+	socklen_t server_addr_len;
+	socklen_t client_addr_len;
+	CriuDumpReq *req;
+	CriuDumpResp resp = CRIU_DUMP_RESP__INIT;
+
+	server_fd = socket(AF_LOCAL, SOCK_STREAM, 0);
+	if (server_fd == -1) {
+		pr_perror("Can't initialize service socket.");
+		return -1;
+	}
+
+	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;
+
+	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);
+
+	unlink(server_addr.sun_path);
+
+	if (bind(server_fd, (struct sockaddr *) &server_addr,
+					server_addr_len) == -1) {
+		pr_perror("Can't bind.");
+		return -1;
+	}
+
+	/* 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.");
+		return -1;
+	}
+
+	if (daemon_mode) {
+		if (daemon(0, 0) == -1) {
+			pr_perror("Can't run service server in the background");
+			return -errno;
+		}
+	}
+
+	if (listen(server_fd, 16) == -1) {
+		pr_perror("Can't listen for socket connections.");
+		return -1;
+	}
+
+	/* FIXME Do not ignore children's return values */
+	signal(SIGCHLD, SIG_IGN);
+
+	while (1) {
+		pr_info("Waiting for connection...\n");
+
+		client_fd = accept(server_fd, &client_addr, &client_addr_len);
+		if (client_fd == -1) {
+			pr_perror("Can't accept connection.");
+			continue;
+		}
+
+		pr_info("Connected.\n");
+
+		switch (child_pid = fork()) {
+		case -1:
+			pr_perror("Can't fork a child.");
+			continue;
+		case 0:
+
+			if (pb_read_one_eof(client_fd,
+						&req, PB_CRIU_DUMP_REQ) == -1) {
+				pr_perror("Can't recv request");
+				resp.bad_args = true;
+				goto exit;
+			}
+
+			if (treat_req(req, &resp) == -1) {
+				pr_perror("Arguments treating fail");
+				goto exit;
+			}
+
+			if (cr_dump_tasks(req->pid) == -1) {
+				pr_perror("Dump fail");
+				goto exit;
+			}
+
+			resp.success = true;
+exit:			if (pb_write_one(client_fd,
+					 &resp, PB_CRIU_DUMP_RESP) == -1) {
+				pr_perror("Can't send response");
+				resp.success = false;
+			}
+
+			close(client_fd);
+
+			if (resp.success)
+				exit(0);
+			else
+				exit(1);
+		default:
+			close(client_fd);
+		}
+	}
+
 	return 0;
 }
diff --git a/crtools.c b/crtools.c
index 6642dde..5421b0a 100644
--- a/crtools.c
+++ b/crtools.c
@@ -335,7 +335,7 @@ int main(int argc, char *argv[])
 		return cr_page_server(opts.restore_detach);
 
 	if (!strcmp(argv[optind], "service"))
-		return cr_service();
+		return cr_service(opts.restore_detach);
 
 	pr_msg("Unknown command \"%s\"\n", argv[optind]);
 usage:
@@ -403,14 +403,11 @@ usage:
 "  --prev-images-dir DIR path to images from previous dump (relative to -D)\n"
 "  --page-server         send pages to page server (see options below as well)\n"
 "\n"
-"Page server options\n"
-"  --address ADDR        address of page server\n"
+"Page/Service server options\n"
+"  --address ADDR        address of server\n"
 "  --port PORT           port of page server\n"
 "  -d|--daemon           run in the background after creating socket\n"
 "\n"
-"Service server options\n"
-"  --address ADDR        address, where to put service socket\n"
-"\n"
 "Show options:\n"
 "  -f|--file FILE        show contents of a checkpoint file\n"
 "  -D|--images-dir DIR   directory where to get images from\n"
diff --git a/include/crtools.h b/include/crtools.h
index d867522..742db88 100644
--- a/include/crtools.h
+++ b/include/crtools.h
@@ -119,7 +119,10 @@ int cr_show(int pid);
 int convert_to_elf(char *elf_path, int fd_core);
 int cr_check(void);
 int cr_exec(int pid, char **opts);
-int cr_service(void);
+int cr_service(bool daemon_mode);
+
+#define CR_DEFAULT_SERVICE_ADDRESS "/tmp/criu_service.socket"
+int get_lib_sk_ino(void);
 
 #define O_DUMP	(O_RDWR | O_CREAT | O_EXCL)
 #define O_SHOW	(O_RDONLY)
diff --git a/include/image.h b/include/image.h
index 5cd7741..b201226 100644
--- a/include/image.h
+++ b/include/image.h
@@ -18,6 +18,7 @@
 #define REMAP_GHOST	(1 << 31)
 
 #define USK_EXTERN	(1 << 0)
+#define USK_LIB_CTRL	(1 << 1)
 
 #define VMA_AREA_NONE		(0 <<  0)
 #define VMA_AREA_REGULAR	(1 <<  0)	/* Dumpable area */
diff --git a/include/protobuf-desc.h b/include/protobuf-desc.h
index fd9ee43..2d6dd8d 100644
--- a/include/protobuf-desc.h
+++ b/include/protobuf-desc.h
@@ -59,6 +59,8 @@ enum {
 	PB_SK_QUEUES,
 	PB_IPCNS_MSG,
 	PB_IPCNS_MSG_ENT,
+	PB_CRIU_DUMP_REQ,
+	PB_CRIU_DUMP_RESP,
 
 	PB_MAX,
 };
diff --git a/protobuf-desc.c b/protobuf-desc.c
index 1d0f7cd..b506ad2 100644
--- a/protobuf-desc.c
+++ b/protobuf-desc.c
@@ -56,6 +56,8 @@
 #include "protobuf/sk-netlink.pb-c.h"
 #include "protobuf/vma.pb-c.h"
 #include "protobuf/tun.pb-c.h"
+#include "protobuf/criu-dump-req.pb-c.h"
+#include "protobuf/criu-dump-resp.pb-c.h"
 
 struct cr_pb_message_desc cr_pb_descs[PB_MAX];
 
@@ -92,6 +94,8 @@ void cr_pb_init(void)
 	CR_PB_DESC(REMAP_FPATH,		RemapFilePath,	remap_file_path);
 	CR_PB_DESC(NETDEV,		NetDevice,	net_device);
 	CR_PB_MDESC_INIT(cr_pb_descs[PB_PAGEMAP_HEAD],	PagemapHead,	pagemap_head);
+	CR_PB_MDESC_INIT(cr_pb_descs[PB_CRIU_DUMP_REQ], CriuDumpReq,	criu_dump_req);
+	CR_PB_MDESC_INIT(cr_pb_descs[PB_CRIU_DUMP_RESP], CriuDumpResp,	criu_dump_resp);
 
 #include "protobuf-desc-gen.h"
 }
diff --git a/sk-unix.c b/sk-unix.c
index cf024a5..8caf263 100644
--- a/sk-unix.c
+++ b/sk-unix.c
@@ -24,6 +24,7 @@
 
 #include "protobuf.h"
 #include "protobuf/sk-unix.pb-c.h"
+#include "protobuf/criu-dump-resp.pb-c.h"
 
 struct unix_sk_desc {
 	struct socket_desc	sd;
@@ -138,6 +139,16 @@ static int dump_one_unix_fd(int lfd, u32 id, const struct fd_parms *p)
 	ue.opts		= &skopts;
 	ue.uflags	= 0;
 
+	/*
+	 * Check if this socket is connected to criu service.
+	 * Dump it like closed one and mark it for restore.
+	 */
+	if (ue.peer == get_lib_sk_ino()) {
+		ue.state = TCP_CLOSE;
+		ue.peer = 0;
+		ue.uflags |= USK_LIB_CTRL;
+	}
+
 	if (sk->namelen && *sk->name) {
 		ue.file_perms = &perms;
 
@@ -716,7 +727,30 @@ static int open_unixsk_standalone(struct unix_sk_info *ui)
 	pr_info("Opening standalone socket (id %#x ino %#x peer %#x)\n",
 			ui->ue->id, ui->ue->ino, ui->ue->peer);
 
-	if ((ui->ue->state == TCP_ESTABLISHED) && !ui->ue->peer) {
+	/* 
+	 * Check if socket was connected to service socket.
+	 * If so, put response, that dumping and restoring
+	 * was successful.
+	 */
+	if (ui->ue->uflags & USK_LIB_CTRL) {
+		int sks[2];
+		CriuDumpResp resp = CRIU_DUMP_RESP__INIT;
+
+		resp.success = true;
+
+		if (socketpair(PF_UNIX, ui->ue->type, 0, sks)) {
+			pr_perror("Can't create socketpair");
+			return -1;
+		}
+
+		if (pb_write_one(sks[1], &resp, PB_CRIU_DUMP_RESP) == -1) {
+			pr_perror("Can't send response");
+			return -1;
+		}
+
+		close(sks[1]);
+		sk = sks[0];
+	} else if ((ui->ue->state == TCP_ESTABLISHED) && !ui->ue->peer) {
 		int ret, sks[2];
 
 		if (ui->ue->type != SOCK_STREAM) {




More information about the CRIU mailing list