[CRIU] [PATCHv4 4/4] crtools: cr_service() and fixes for it

Andrew Vagin avagin at parallels.com
Fri Sep 6 00:47:07 EDT 2013


On Tue, Sep 03, 2013 at 06:44:35PM +0400, Ruslan Kuprieiev wrote:
> cr_service() is function to start daemon, that will create unix
> domain socket and will listen for requests from libcriu on it. If
> request is obtained, it will fork() child, that will recv, process
> arguments and dump program.

> diff --git a/cr-service.c b/cr-service.c
> index e921828..7ac1a04 100644
> --- a/cr-service.c
> +++ b/cr-service.c
> @@ -1,4 +1,260 @@
> +#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 "libcriu.h"
> +#include "version.h"
> +#include "protobuf/criu-dump-args.pb-c.h"
> +
> +
> +static CriuDumpArgs *args;
> +static int client_fd;
> +
> +static int criu_recv_dump_args(int socket_fd)
> +{
> +	int len;
> +	unsigned char *buf;
> +	/*
> +	 * Recv the size of args and then
> +	 * recv args.
> +	 */
> +	if (recv(socket_fd, &len, sizeof(len), 0) == -1) {
> +		pr_perror("Can't recv size of args");
> +		return -1;
> +	}
> +
> +	buf = malloc(len);

Use xmallo/xfree

> +	if (buf == NULL) {
> +		pr_perror("Can't allocate memory");
> +		return -1;
> +	}
> +
> +	errno = 0;

isn't required

> +	if (recv(socket_fd, buf, len, MSG_WAITALL) == -1) {
> +		if (errno == EAGAIN || errno == EWOULDBLOCK)
> +			pr_perror("Recv timeout");
> +		pr_perror("Can't recv args");
> +		return -1;
> +	}
> +
> +	args = criu_dump_args__unpack(NULL, len, buf);
> +	if (args == NULL) {
> +		pr_perror("Can't unpack msg");
Usually such messages is printed from criu_dump_args__unpack
> +		return -1;
> +	}
> +
> +	/*
> +	 * Recv opened dir fd, where to put images
> +	 */
> +	args->images_dir_fd = recv_fd(socket_fd);
> +	if (args->images_dir_fd == -1) {
> +		pr_perror("Can't recv file descriptor");

recv_fd prints a similar error message

> +		return -1;
> +	}
> +
> +	return 0;
> +}
> +
> +/*
> + * This function is needed in sk-unix.c,
> + * to recognize connection to service socket
> + * and dump 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 get_client_pid(void)
> +{
> +	struct ucred ids;
> +	socklen_t ids_len;
> +	ids_len = sizeof(struct ucred);
> +
> +	if (getsockopt(client_fd, SOL_SOCKET, SO_PEERCRED, &ids, &ids_len)) {
> +		pr_perror("Can't get socket options.");
> +		return -1;
> +	}
> +
> +	return ids.pid;
> +}
> +
> +static int child_work(void)
> +{
> +	int pid;
> +	int ret;
> +
> +	if (criu_recv_dump_args(client_fd) == -1) {
> +		pr_perror("Can't recv args");
> +		goto child_err;
> +	}
> +
> +	/* check version */
> +	if (args->version_major != CRIU_VERSION_MAJOR ||
> +	    args->version_minor != CRIU_VERSION_MINOR) {
> +		pr_perror("Looks like criu (version %s) is out-of-date. "
> +			  "Please, upgrade criu.", version);
> +		ret = CRIU_OLD_VERSION;
> +		goto child_out;
> +	}
> +
> +	pid = get_client_pid();
> +	if (pid == -1) {
> +		pr_perror("Can't get pid");
> +		goto child_err;
> +	}
> +
> +	/* going to dir, where to place images*/
> +	if (fchdir(args->images_dir_fd)) {
> +		pr_perror("Can't change directory.");
> +		goto child_err;
> +	}
> +
> +	/* initiate log file in imgs dir */
> +	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;
> +	}
> +
> +	/* FIXME: log level of dump.log is the same as log level,
> +	 * which was set on start of criu service. Add log level
> +	 * to args.
> +	 */
> +	if (log_init(opts.output) == -1) {
> +		pr_perror("Can't initiate log.");
> +		goto child_err;
> +	}
> +
> +	/* checking dump flags from client */
> +	if (args->flags & CRIU_DUMP_AND_CONTINUE) {
> +		opts.final_state = TASK_ALIVE;
> +		args->flags &= CRIU_DUMP_AND_CONTINUE;
> +		ret = CRIU_DUMP_SUCCESS; /* pre set */
> +	}
> +
> +	if (args->flags != 0) {
> +		/*
> +		 * FIXME add flag for bad args to libcriu.h
> +		 */
> +		pr_perror("Bad flags");
> +		goto child_err;
> +	}
> +
> +	/* dump */
> +	if (cr_dump_tasks(pid) < 0) {
> +		pr_perror("Dump failed.");
> +		goto child_err;
> +	}
> +
> +	close(client_fd);
> +	exit(0);
> +
> +child_err:
> +	ret = CRIU_DUMP_FAIL;
> +child_out:
> +	if (send(client_fd, &ret, sizeof(ret), 0) == -1)
> +		pr_perror("Can't send msg to lib");

I think we will use a protobuf message here too.

> +
> +	close(client_fd);
> +	exit(1);
> +}
> +
>  int cr_service(void)
>  {
> +	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;
> +
> +	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;
> +	}

Should this service daemonizes here? Look at page-server for example.

> +
> +	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:
> +			exit(child_work());
> +		default:
> +			close(client_fd);
> +		}
> +	}
> +
>  	return 0;
>  }
> diff --git a/include/crtools.h b/include/crtools.h
> index d867522..ce7466d 100644
> --- a/include/crtools.h
> +++ b/include/crtools.h
> @@ -121,6 +121,8 @@ int cr_check(void);
>  int cr_exec(int pid, char **opts);
>  int cr_service(void);
>  
> +int get_lib_sk_ino(void);
> +
>  #define O_DUMP	(O_RDWR | O_CREAT | O_EXCL)
>  #define O_SHOW	(O_RDONLY)
>  #define O_RSTR	(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/sk-unix.c b/sk-unix.c
> index cf024a5..365c5ac 100644
> --- a/sk-unix.c
> +++ b/sk-unix.c
> @@ -21,6 +21,7 @@
>  #include "sockets.h"
>  #include "sk-queue.h"
>  #include "mount.h"
> +#include "libcriu.h"
>  
>  #include "protobuf.h"
>  #include "protobuf/sk-unix.pb-c.h"
> @@ -138,6 +139,17 @@ static int dump_one_unix_fd(int lfd, u32 id, const struct fd_parms *p)
>  	ue.opts		= &skopts;
>  	ue.uflags	= 0;
>  
> +	/*
> +	 * Check if this is library socket,
> +	 * that 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 +728,23 @@ 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) {
> +	if (ui->ue->uflags & USK_LIB_CTRL) {
> +		int sks[2];
> +
> +		if (socketpair(PF_UNIX, ui->ue->type, 0, sks)) {
> +			pr_perror("Can't create socketpair");
> +			return -1;
> +		}
> +
> +		int msg = CRIU_RESUME;
> +		if (write(sks[1], &msg, sizeof(msg)) != sizeof(msg)) {
> +			pr_perror("Can't write msg for lib to socket");
> +			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) {
> 

> _______________________________________________
> CRIU mailing list
> CRIU at openvz.org
> https://lists.openvz.org/mailman/listinfo/criu



More information about the CRIU mailing list