[CRIU] [PATCH]v2 criu: cr_service for crtools and criu_dump_me for criu library

Ruslan Kuprieiev kupruser at gmail.com
Wed Aug 7 21:10:10 EDT 2013


Hello!

I make a cr_service() function for crtools and criu_dump_me for criu 
library. It is used to interact with programs though function criu 
library. cr_service() opens a socket and listening to connections on it. 
criu_dump_me() gets a proper argument from program and sends argument to 
socket.

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

-------------- next part --------------
commit 4429040cbe0b159b367a0d5da23090746b4ac2fd
Author: Ruslan Kuprieiev <kupruser at gmail.com>
Date:   Thu Aug 8 03:46:18 2013 +0300

    Added cr_service() to crtools, that opens socket and listens to connections.
    If it get one, it forks child, that reveives dump argument from process and dumps it.
    
    opts.addr changed to char *, so it can be used to store address of service socket.
    Command line option "--address" was changed properly.
    Added get_ps_addr() to page-xfer.c to convert char * opts.addr to struct sock_addr_in.
    
    Added criu_dump_me() to criu library to provide usage of service socket for programs.
    
    Added check for service socket to dump_one_unix_fd(), so if program socket has peer
    that is service socket, we well dump it like closed one. We do so, because we need to
    tell the program if dump was successfull or not.

diff --git a/Makefile.crtools b/Makefile.crtools
index efc5c82..7b8b2b9 100644
--- a/Makefile.crtools
+++ b/Makefile.crtools
@@ -51,6 +51,7 @@ obj-y	+= kerndat.o
 obj-y	+= stats.o
 obj-y	+= sigframe.o
 obj-y	+= arch/$(ARCH)/vdso.o
+obj-y 	+= cr-service.o
 
 ifneq ($(MAKECMDGOALS),clean)
 incdeps := y
diff --git a/cr-dump.c b/cr-dump.c
index 49677db..bc029ae 100644
--- a/cr-dump.c
+++ b/cr-dump.c
@@ -994,7 +994,7 @@ static int collect_pstree_ids(void)
 	return 0;
 }
 
-static int collect_pstree(pid_t pid)
+int collect_pstree(pid_t pid)
 {
 	int ret, attempts = 5;
 
diff --git a/cr-service.c b/cr-service.c
new file mode 100644
index 0000000..31cc3df
--- /dev/null
+++ b/cr-service.c
@@ -0,0 +1,193 @@
+#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-net.h"
+#include "log.h"
+#include "libcriu.h"
+
+static int criu_recv_dump_arg(int socket_fd, struct criu_dump_args *arg)
+{
+	int ret;
+	arg->images_dir_fd = recv_fd(socket_fd);
+	if (arg->images_dir_fd == -1) {
+		pr_perror("recv_fd() failed");
+		return -1;
+	}
+
+	ret = recv(socket_fd, &(arg->flags), sizeof(arg->flags), 0);
+	if (ret == -1) {
+		pr_perror("recv() failed");
+		return -1;
+	}
+
+	return 0;
+}
+
+/*
+ *FIXME 
+ *	Provide ability to set logfile path and loglevel through criu_dump_args.
+ */
+
+int cr_service(void)
+{
+	int server_fd;
+	int client_fd;
+
+	server_fd = socket(AF_LOCAL, SOCK_STREAM, 0);
+	if (server_fd == -1) {
+		pr_perror("Can't initialize service socket.");
+		return -1;
+	}
+
+	struct sockaddr_un server_addr;
+	struct sockaddr_un client_addr;
+
+	memset(&server_addr, 0, sizeof(server_addr));
+	memset(&client_addr, 0, sizeof(client_addr));
+	server_addr.sun_family = AF_LOCAL;
+
+	socklen_t server_addr_len;
+	socklen_t client_addr_len;
+
+	if (opts.addr == NULL) {
+		opts.addr = strdup(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;
+	}
+
+	if (chmod(server_addr.sun_path, 0666)) {
+		pr_perror("Can't change permissions of the service socket.");
+		return -1;
+	}
+
+	if (listen(server_fd, 16) == -1) {
+		pr_perror("Can't listen for socket connections.");
+		return -1;
+	}
+
+	struct criu_dump_args *criu_arg;
+	criu_arg = malloc(sizeof(*criu_arg));
+
+	pid_t child_pid;
+
+	int ret;
+
+	struct ucred ids;
+	socklen_t ids_len;
+	ids_len = sizeof(struct ucred);
+
+	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 (getsockopt(client_fd, SOL_SOCKET, SO_PEERCRED, &ids, &ids_len)) {
+				pr_perror("Can't get socket options.");
+				goto child_err;
+			}
+
+			client_addr_len = criu_recv_dump_arg(client_fd, criu_arg);
+			if (client_addr_len == -1) {
+				pr_perror("Can't get msg from socket.");
+				goto child_err;
+			}
+
+			if (fchdir(criu_arg->images_dir_fd)) {
+				pr_perror("Can't change directory.");
+				goto child_err;
+			}
+
+			opts.output = malloc(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 child_err;
+			}
+
+			log_set_loglevel(3);/*loglevel is temporary constant*/	
+			if (log_init(opts.output) == -1) {
+				pr_perror("Can't initiate log.");
+				goto child_err;
+			}
+
+			switch (criu_arg->flags) {
+			case CRIU_DUMP_AND_CONTINUE:
+				opts.final_state = TASK_ALIVE;
+			}
+
+			struct stat tmp;
+			if (fstat(client_fd, &tmp)) {
+				pr_perror("Can't get socket stat.");
+				goto child_err;
+			}
+			cr_service_sk_ino = tmp.st_ino;
+
+			if (cr_dump_tasks(ids.pid) < 0) {
+				pr_perror("Dumping failed.");
+				goto child_err;
+			}
+
+			if (opts.final_state == TASK_ALIVE) {
+				ret = CRIU_DUMP_SUCCESS;
+				send(client_fd, &ret, sizeof(ret), 0);
+			}
+ 
+			close(client_fd);
+			exit(0);
+
+child_err:		ret = CRIU_DUMP_FAIL;
+			send(client_fd, &ret, sizeof(ret), 0);
+			close(client_fd);
+			exit(-1);
+
+		default:
+			close(client_fd);
+			
+			pid_t who;
+			while ((who = waitpid(-1, &ret, WNOHANG)) != 0) {
+				if (ret) 
+					pr_perror("Child %d exited with problems.\n", who);
+			}
+		}
+	}
+
+	return 0;
+}
diff --git a/crtools.c b/crtools.c
index 51d188a..cb0ee1c 100644
--- a/crtools.c
+++ b/crtools.c
@@ -235,14 +235,11 @@ int main(int argc, char *argv[])
 			opts.use_page_server = true;
 			break;
 		case 51:
-			if (!inet_aton(optarg, &opts.ps_addr.sin_addr)) {
-				pr_perror("Bad address");
-				return -1;
-			}
+			opts.addr = strdup(optarg);
 			break;
 		case 52:
-			opts.ps_addr.sin_port = htons(atoi(optarg));
-			if (!opts.ps_addr.sin_port) {
+			opts.port = htons(atoi(optarg));
+			if (!opts.port) {
 				pr_err("Bad port\n");
 				return -1;
 			}
@@ -334,8 +331,16 @@ int main(int argc, char *argv[])
 		return cr_exec(pid, argv + optind + 1);
 	}
 
-	if (!strcmp(argv[optind], "page-server"))
+	if (!strcmp(argv[optind], "page-server")) {
+		if (!inet_aton(optarg, NULL)) {
+			pr_perror("Bad address");
+			return -1;
+		}
 		return cr_page_server(opts.restore_detach);
+	}
+
+	if (!strcmp(argv[optind], "service"))
+		return cr_service();
 
 	pr_msg("Unknown command \"%s\"\n", argv[optind]);
 usage:
@@ -347,6 +352,7 @@ usage:
 "  criu check [--ms]\n"
 "  criu exec -p PID <syscall-string>\n"
 "  criu page-server\n"
+"  criu service\n"
 "\n"
 "Commands:\n"
 "  dump           checkpoint a process/tree identified by pid\n"
@@ -356,6 +362,7 @@ usage:
 "  check          checks whether the kernel support is up-to-date\n"
 "  exec           execute a system call by other task\n"
 "  page-server    launch page server\n"
+"  service        launch service server\n"
 	);
 
 	if (argc < 2) {
@@ -402,7 +409,7 @@ usage:
 "  --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"
+"  --address ADDR        address of page server or address of service server\n"
 "  --port PORT           port of page server\n"
 "  -d|--daemon           run in the background after creating socket\n"
 "\n"
diff --git a/include/crtools.h b/include/crtools.h
index 81e89e1..8087516 100644
--- a/include/crtools.h
+++ b/include/crtools.h
@@ -40,7 +40,8 @@ struct cr_options {
 	struct list_head	veth_pairs;
 	struct list_head	scripts;
 	bool			use_page_server;
-	struct sockaddr_in	ps_addr;
+	char			*addr;
+	in_port_t		port;
 	bool			track_mem;
 	char			*img_parent;
 };
@@ -119,6 +120,9 @@ 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_sk_ino;
 
 #define O_DUMP	(O_RDWR | O_CREAT | O_EXCL)
 #define O_SHOW	(O_RDONLY)
@@ -142,6 +146,7 @@ struct vm_area_list {
 int collect_mappings(pid_t pid, struct vm_area_list *vma_area_list);
 void free_mappings(struct vm_area_list *vma_area_list);
 bool privately_dump_vma(struct vma_area *vma);
+int collect_pstree(pid_t pid);
 
 struct vma_area {
 	struct list_head	list;
diff --git a/include/libcriu.h b/include/libcriu.h
new file mode 100644
index 0000000..afed0d5
--- /dev/null
+++ b/include/libcriu.h
@@ -0,0 +1,19 @@
+#ifndef __CRIU_LIB__
+#define __CRIU_LIB__
+
+#define CR_DEFAULT_SERVICE_ADDRESS "/tmp/criu_service.socket"
+
+#define CRIU_DUMP_AND_CONTINUE 1
+
+#define CRIU_DUMP_FAIL -1
+#define CRIU_RESUME 1
+#define CRIU_DUMP_SUCCESS 0
+
+struct criu_dump_args {
+	int 			images_dir_fd;
+	unsigned long 		flags;
+};
+
+int criu_dump_me(char sk_address [], struct criu_dump_args * arg);
+
+#endif
diff --git a/lib/Makefile b/lib/Makefile
index 368883e..c36dcd4 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -3,7 +3,8 @@
 all: libcriu.so
 
 libcriu.so: criu.o
-	$(Q) $(CC) $(CFLAGS) -shared -o $@ criu.o
+	$(Q) $(CC) $(CFLAGS) -c -fPIC -D __CRIU_LIB_LINKER__ ../pie/util-net.c -I ../include/ -I ../ -I ../arch/$(ARCH)/include
+	$(Q) $(CC) $(CFLAGS) -shared -o $@ criu.o util-net.o
 
 criu.o:
 	$(Q) $(CC) $(CFLAGS) -fPIC -c criu.c -I ../include/
diff --git a/lib/criu.c b/lib/criu.c
index be629d9..3ee5c7b 100644
--- a/lib/criu.c
+++ b/lib/criu.c
@@ -1,3 +1,76 @@
+#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 <stdint.h>
+#include <stdbool.h>
+
+#include "libcriu.h"
 #include "version.h"
+#include "util-net.h"
 
 const char *criu_lib_version = version;
+
+static int criu_send_dump_arg(int socket_fd, struct criu_dump_args *arg)
+{
+	if (send_fd(socket_fd, NULL, 0, arg->images_dir_fd) == -1) {
+		perror("send_fd() failed");
+		return -1;
+	}
+
+	if (send(socket_fd, &(arg->flags), sizeof(unsigned long), 0) == -1) {
+		perror("send() failed");
+		return -1;
+	}
+
+	return 0;
+}
+
+int criu_dump_me(char sk_address[], struct criu_dump_args *arg)
+{
+	int socket_fd;
+	socket_fd = socket(AF_LOCAL, SOCK_STREAM, 0);
+	if (socket_fd == -1) {
+		perror("socket() failed");
+		return -1;
+	}
+
+	struct sockaddr_un server_addr;
+
+	memset(&server_addr, 0, sizeof(server_addr));
+	server_addr.sun_family = AF_LOCAL;
+	strcpy(server_addr.sun_path, sk_address);
+
+	socklen_t server_addr_len;
+
+	server_addr_len = strlen(server_addr.sun_path)
+			+ sizeof(server_addr.sun_family);
+
+	if (connect(socket_fd, (struct sockaddr *) &server_addr,
+						server_addr_len) != 0) {
+		perror("connect() fail");
+		return -1;
+	}
+
+	if (criu_send_dump_arg(socket_fd, arg) == -1) {
+		perror("criu_send_dump_arg() failed");
+		close(socket_fd);
+		return -1;
+	}
+
+	int ret;
+	int c;
+
+	c = read(socket_fd, &ret, sizeof(ret));
+	if (c == -1)
+		ret = CRIU_RESUME;
+
+	close(socket_fd);
+
+	return ret;
+}
+
diff --git a/page-xfer.c b/page-xfer.c
index 4efa20c..89111aa 100644
--- a/page-xfer.c
+++ b/page-xfer.c
@@ -195,16 +195,35 @@ static int page_server_serve(int sk)
 	return ret;
 }
 
+static int get_ps_addr(struct sockaddr_in *addr)
+{
+	addr->sin_family = AF_INET;
+	addr->sin_port = opts.port;
+
+	if (!inet_aton(opts.addr, &addr->sin_addr)) {
+		pr_perror("Bad address");
+		return -1;
+	}
+
+	return 0;
+}
+
 int cr_page_server(bool daemon_mode)
 {
 	int sk, ask = -1;
 	struct sockaddr_in caddr;
 	socklen_t clen = sizeof(caddr);
 
+	struct sockaddr_in ps_addr;
+	if (get_ps_addr(&ps_addr)) {
+		pr_perror("Can't get page server address");
+		return -1;
+	}
+
 	up_page_ids_base();
 
 	pr_info("Starting page server on port %u\n",
-			(int)ntohs(opts.ps_addr.sin_port));
+			(int)ntohs(ps_addr.sin_port));
 
 	sk = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
 	if (sk < 0) {
@@ -212,8 +231,7 @@ int cr_page_server(bool daemon_mode)
 		return -1;
 	}
 
-	opts.ps_addr.sin_family = AF_INET;
-	if (bind(sk, (struct sockaddr *)&opts.ps_addr, sizeof(opts.ps_addr))) {
+	if (bind(sk, (struct sockaddr *)&ps_addr, sizeof(ps_addr))) {
 		pr_perror("Can't bind page server");
 		goto out;
 	}
@@ -253,9 +271,15 @@ int connect_to_page_server(void)
 	if (!opts.use_page_server)
 		return 0;
 
+	struct sockaddr_in ps_addr;
+	if (get_ps_addr(&ps_addr)) {
+		pr_perror("Can't get page server address");
+		return -1;
+	}
+
 	pr_info("Connecting to server %s:%u\n",
-			inet_ntoa(opts.ps_addr.sin_addr),
-			(int)ntohs(opts.ps_addr.sin_port));
+			inet_ntoa(ps_addr.sin_addr),
+			(int)ntohs(ps_addr.sin_port));
 
 	page_server_sk = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
 	if (page_server_sk < 0) {
@@ -263,9 +287,8 @@ int connect_to_page_server(void)
 		return -1;
 	}
 
-	opts.ps_addr.sin_family = AF_INET;
-	if (connect(page_server_sk, (struct sockaddr *)&opts.ps_addr,
-				sizeof(opts.ps_addr)) < 0) {
+	if (connect(page_server_sk, (struct sockaddr *)&ps_addr,
+				sizeof(ps_addr)) < 0) {
 		pr_perror("Can't connect to server");
 		return -1;
 	}
diff --git a/pie/util-net.c b/pie/util-net.c
index 016600b..248c294 100644
--- a/pie/util-net.c
+++ b/pie/util-net.c
@@ -6,7 +6,16 @@
 #include "compiler.h"
 #include "asm/string.h"
 #include "asm/types.h"
+
+#ifdef __CRIU_LIB_LINKER__
+#define sys_sendmsg sendmsg
+#define sys_fcntl fcntl
+#define sys_recvmsg recvmsg
+#endif
+
+#ifndef __CRIU_LIB_LINKER__
 #include "syscall.h"
+#endif
 
 #include "util-net.h"
 
diff --git a/sk-unix.c b/sk-unix.c
index c881f9b..96a2d94 100644
--- a/sk-unix.c
+++ b/sk-unix.c
@@ -138,6 +138,15 @@ static int dump_one_unix_fd(int lfd, u32 id, const struct fd_parms *p)
 	ue.opts		= &skopts;
 	ue.uflags	= 0;
 
+	/*
+	 *Checking if this socket is ours (cr_service())
+	 *and dumping it like closed.
+	 */
+	if (ue.peer == cr_service_sk_ino) {
+		ue.state = TCP_CLOSE;
+		ue.peer = 0;
+	}
+
 	if (sk->namelen && *sk->name) {
 		ue.file_perms = &perms;
 
@@ -169,7 +178,6 @@ static int dump_one_unix_fd(int lfd, u32 id, const struct fd_parms *p)
 				goto err;
 			}
 		}
-
 		/*
 		 * It can be external socket, so we defer dumping
 		 * until all sockets the program owns are processed.


More information about the CRIU mailing list