[CRIU] [PATCH] add join-ns opt to criu restore

Andrew Vagin avagin at virtuozzo.com
Tue Mar 15 14:29:06 PDT 2016


On Tue, Mar 15, 2016 at 02:08:51PM +0800, Dengguangxing wrote:
> 
> join-ns will restore process with specified existing namespace.
> This opt can be used in this fomat:
> 	--join-ns NS:PID, for example net:12345.

A namespace may be mounted into a file:
[root at fc22-vm ~]# ip netns add test
[root at fc22-vm ~]# cat /proc/self/mountinfo | grep test
82 80 0:3 net:[4026532214] /run/netns/test rw shared:32 - nsfs nsfs rw

> 
> pid namespaces is not supported yet. As fork() is needed to make
> new pid-namespace take effect. That makes it hard for criu to track the
> child-process through pid because another child process has been
> created after fork().
> 
> Signed-off-by: Deng Guangxing <dengguangxing at huawei.com>
> ---
>  criu/cr-restore.c         |  5 +++
>  criu/crtools.c            | 44 ++++++++++++++++++++++++-
>  criu/include/cr_options.h |  1 +
>  criu/include/namespaces.h | 10 ++++++
>  criu/namespaces.c         | 82 ++++++++++++++++++++++++++++++++++++++++-------
>  5 files changed, 129 insertions(+), 13 deletions(-)
> 
> diff --git a/criu/cr-restore.c b/criu/cr-restore.c
> index 30ddff9..e3f0add 100644
> --- a/criu/cr-restore.c
> +++ b/criu/cr-restore.c
> @@ -1566,6 +1566,11 @@ static int restore_task_with_children(void *_arg)
> 
>  	/* Restore root task */
>  	if (current->parent == NULL) {
> +		if (join_namespaces()) {

If a root task is restore in a new userns, you will not ablt to join
external namespaces here. setns() will return EPERM.

> +			pr_perror("Join namespaces failed");
> +			goto err;
> +		}
> +
>  		if (restore_finish_stage(CR_STATE_RESTORE_NS) < 0)
>  			goto err;
> 
> diff --git a/criu/crtools.c b/criu/crtools.c
> index d6e8672..87d9ec4 100644
> --- a/criu/crtools.c
> +++ b/criu/crtools.c
> @@ -36,6 +36,7 @@
>  #include "cr-service.h"
>  #include "plugin.h"
>  #include "mount.h"
> +#include "namespaces.h"
>  #include "cgroup.h"
>  #include "cpu.h"
>  #include "action-scripts.h"
> @@ -58,6 +59,7 @@ void init_opts(void)
>  	INIT_LIST_HEAD(&opts.veth_pairs);
>  	INIT_LIST_HEAD(&opts.scripts);
>  	INIT_LIST_HEAD(&opts.ext_mounts);
> +	INIT_LIST_HEAD(&opts.join_ns);
>  	INIT_LIST_HEAD(&opts.inherit_fds);
>  	INIT_LIST_HEAD(&opts.external);
>  	INIT_LIST_HEAD(&opts.new_cgroup_roots);
> @@ -99,6 +101,39 @@ bad_ns:
>  	return -1;
>  }
> 
> +static int parse_join_ns(const char *ptr)
> +{
> +	char *aux;
> +	pid_t pid;
> +
> +	aux = strchr(ptr, ':');
> +	if (aux == NULL)
> +		return -1;
> +	*aux = '\0';
> +
> +	if (!strcmp(ptr, "pid")) {
> +		pr_perror("pid namespace not supported in join-ns\n");
> +		return -1;
> +	} else if (strcmp(ptr, "net") &&
> +		strcmp(ptr, "uts") &&
> +		strcmp(ptr, "ipc") &&
> +		strcmp(ptr, "user") &&

You need to set proper uid and gid to joing an user namespace

> +		strcmp(ptr, "mnt")) {
> +		pr_perror("Illegal namespace %s in join-ns\n", ptr);
> +		return -1;
> +	}
> +
> +	pid = atoi(aux+1);
> +	if (pid <= 0) {
> +		pr_perror("Invalid pid %s in join-ns\n", aux+1);
> +		return -1;
> +	}
> +	if (join_ns_add(ptr, pid))
> +		return -1;
> +
> +	return 0;
> +}
> +
>  static int parse_cpu_cap(struct cr_options *opts, const char *optarg)
>  {
>  	bool inverse = false;
> @@ -218,7 +253,7 @@ int main(int argc, char *argv[], char *envp[])
>  	int log_level = LOG_UNSET;
>  	char *imgs_dir = ".";
>  	char *work_dir = NULL;
> -	static const char short_opts[] = "dSsRf:F:t:p:hcD:o:n:v::x::Vr:jlW:L:M:";
> +	static const char short_opts[] = "dSsRf:F:t:p:hcD:o:n:v::x::Vr:jJ:lW:L:M:";
>  	static struct option long_opts[] = {
>  		{ "tree",			required_argument,	0, 't'	},
>  		{ "pid",			required_argument,	0, 'p'	},
> @@ -234,6 +269,7 @@ int main(int argc, char *argv[], char *envp[])
>  		{ "work-dir",			required_argument,	0, 'W'	},
>  		{ "log-file",			required_argument,	0, 'o'	},
>  		{ "namespaces",			required_argument,	0, 'n'	},
> +		{ "join-ns",			required_argument,	0, 'J'	},
>  		{ "root",			required_argument,	0, 'r'	},
>  		{ USK_EXT_PARAM,		optional_argument,	0, 'x'	},
>  		{ "help",			no_argument,		0, 'h'	},
> @@ -366,6 +402,10 @@ int main(int argc, char *argv[], char *envp[])
>  			if (parse_ns_string(optarg))
>  				goto bad_arg;
>  			break;
> +		case 'J':
> +			if (parse_join_ns(optarg))
> +				goto bad_arg;
> +			break;
>  		case 'v':
>  			if (log_level == LOG_UNSET)
>  				log_level = 0;
> @@ -810,6 +850,8 @@ usage:
>  "  --empty-ns {net}\n"
>  "			Create a namespace, but don't restore its properies.\n"
>  "			An user will retore them from action scripts.\n"
> +"  -J|--join-ns NS:PID\n"
> +"			Join exist namespace and restore process in it.\n"
>  "\n"
>  "* Logging:\n"
>  "  -o|--log-file FILE    log file name\n"
> diff --git a/criu/include/cr_options.h b/criu/include/cr_options.h
> index a6f0b3e..cf3cb0e 100644
> --- a/criu/include/cr_options.h
> +++ b/criu/include/cr_options.h
> @@ -82,6 +82,7 @@ struct cr_options {
>  	struct list_head	ext_mounts;
>  	struct list_head	inherit_fds;
>  	struct list_head	external;
> +	struct list_head	join_ns;
>  	char			*libdir;
>  	bool			use_page_server;
>  	unsigned short		port;
> diff --git a/criu/include/namespaces.h b/criu/include/namespaces.h
> index 303c9e6..a7a8863 100644
> --- a/criu/include/namespaces.h
> +++ b/criu/include/namespaces.h
> @@ -3,6 +3,7 @@
> 
>  #include "compiler.h"
>  #include "files.h"
> +#include "list.h"
> 
>  /* including syscall-types.h gives another weird error; do we really need to
>   * define this twice? */
> @@ -19,6 +20,13 @@ struct ns_desc {
>  	size_t		len;
>  };
> 
> +struct join_ns {
> +	struct list_head 	list;
> +	pid_t 			pid;
> +	struct ns_desc		nd;
> +
> +};
> +
>  enum ns_type {
>  	NS_UNKNOWN = 0,
>  	NS_CRIU,
> @@ -100,6 +108,8 @@ extern gid_t userns_gid(gid_t gid);
> 
>  extern int dump_user_ns(pid_t pid, int ns_id);
>  extern void free_userns_maps(void);
> +extern int join_ns_add(const char *type, pid_t pid);
> +extern int join_namespaces();
> 
>  typedef int (*uns_call_t)(void *arg, int fd, pid_t pid);
>  /*
> diff --git a/criu/namespaces.c b/criu/namespaces.c
> index d7f8a9f..b3b829e 100644
> --- a/criu/namespaces.c
> +++ b/criu/namespaces.c
> @@ -10,7 +10,9 @@
>  #include <signal.h>
>  #include <sched.h>
>  #include <sys/capability.h>
> +#include <sys/stat.h>
> 
> +#include "cr_options.h"
>  #include "util.h"
>  #include "imgset.h"
>  #include "uts_ns.h"
> @@ -35,6 +37,37 @@ static struct ns_desc *ns_desc_array[] = {
>  	&cgroup_ns_desc,
>  };
> 
> +int join_ns_add(const char *type, pid_t pid) {
> +	struct join_ns *jn;
> +
> +	jn = xmalloc(sizeof(*jn));
> +	if (!jn) {
> +		return -1;
> +	}
> +
> +	jn->pid = pid;
> +	if (!strcmp(type, "net")) {
> +			jn->nd = net_ns_desc;
> +	} else if (!strcmp(type, "uts")) {
> +			jn->nd = uts_ns_desc;
> +	} else if (!strcmp(type, "ipc")) {
> +			jn->nd = ipc_ns_desc;
> +	} else if (!strcmp(type, "pid")) {
> +			jn->nd = pid_ns_desc;
> +	} else if (!strcmp(type, "user")) {
> +			jn->nd = user_ns_desc;
> +	} else if (!strcmp(type, "mnt")) {
> +			jn->nd = mnt_ns_desc;
> +	} else {
> +			pr_perror("invalid namespace type %s\n", type);
> +			return -1;
> +	}
> +
> +	list_add_tail(&jn->list, &opts.join_ns);
> +	pr_info("Added %s:%d join namespace\n", type, pid);
> +	return 0;
> +}
> +
>  static unsigned int parse_ns_link(char *link, size_t len, struct ns_desc *d)
>  {
>  	unsigned long kid = 0;
> @@ -73,28 +106,41 @@ bool check_ns_proc(struct fd_link *link)
>  int switch_ns(int pid, struct ns_desc *nd, int *rst)
>  {
>  	char buf[32];
> -	int nsfd;
> +	int nsfd, self_fd;
>  	int ret = -1;
> +	struct stat st, self_st;
> 
>  	nsfd = open_proc(pid, "ns/%s", nd->str);
>  	if (nsfd < 0) {
> -		pr_perror("Can't open ipcns file");
> +		pr_perror("Can't open ns file");
> +		goto err_ns;
> +	}
> +	if (fstat(nsfd, &st) == -1) {
> +		pr_perror("Can't get ns file stat");
>  		goto err_ns;
>  	}
> 
> +	snprintf(buf, sizeof(buf), "/proc/self/ns/%s", nd->str);
> +	self_fd = open(buf, O_RDONLY);
> +	if (self_fd < 0) {
> +		pr_perror("Can't open ns file");
> +		goto err_rst;
> +	}
> +	if (fstat(self_fd, &self_st) == -1) {
> +		pr_perror("Can't get ns file stat");
> +		goto err_ns;
> +	}
>  	if (rst) {
> -		snprintf(buf, sizeof(buf), "/proc/self/ns/%s", nd->str);
> -		*rst = open(buf, O_RDONLY);
> -		if (*rst < 0) {
> -			pr_perror("Can't open ns file");
> -			goto err_rst;
> -		}
> +		*rst = self_fd;
>  	}
> 
> -	ret = setns(nsfd, nd->cflag);
> -	if (ret < 0) {
> -		pr_perror("Can't setns %d/%s", pid, nd->str);
> -		goto err_set;
> +	//re-set the same user-ns would fail, check it here

We don't use C99-style "// ..." comments.
> +	if (st.st_ino != self_st.st_ino) {
> +		ret = setns(nsfd, nd->cflag);
> +		if (ret < 0) {
> +			pr_perror("Can't setns %d/%s", pid, nd->str);
> +			goto err_set;
> +		}
>  	}
> 
>  	close(nsfd);
> @@ -1371,6 +1417,18 @@ static int prepare_userns_creds()
>  	return 0;
>  }
> 
> +int join_namespaces()
> +{
> +	struct join_ns *jn;
> +
> +	list_for_each_entry(jn, &opts.join_ns, list)
> +		if (switch_ns(jn->pid, &jn->nd, NULL)) {

You need to open namespace files before calling setns for them,
because namespace files can become unaccessiable after switching into
another mount namespace.

> +			return -1;
> +		}
> +
> +	return 0;
> +}
> +
>  int prepare_namespace(struct pstree_item *item, unsigned long clone_flags)
>  {
>  	pid_t pid = item->pid.virt;
> -- 
> 2.5.0
> 
> _______________________________________________
> CRIU mailing list
> CRIU at openvz.org
> https://lists.openvz.org/mailman/listinfo/criu


More information about the CRIU mailing list