[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