diff --git a/cr-service.c b/cr-service.c index 9c4098904..105787ec2 100644 --- a/cr-service.c +++ b/cr-service.c @@ -30,6 +30,7 @@ #include "cgroup.h" #include "action-scripts.h" #include "security.h" +#include "sockets.h" #include "setproctitle.h" @@ -285,8 +286,13 @@ static int setup_opts_from_req(int sk, CriuOpts *req) req->pid = ids.pid; } - if (req->has_ext_unix_sk) + if (req->has_ext_unix_sk) { opts.ext_unix_sk = req->ext_unix_sk; + for (i = 0; i < req->n_unix_sk_ino; i++) { + if (unix_sk_id_add(req->unix_sk_ino[i]->inode) < 0) + goto err; + } + } if (req->root) opts.root = req->root; diff --git a/crtools.c b/crtools.c index b085d33d1..6af60804b 100644 --- a/crtools.c +++ b/crtools.c @@ -50,6 +50,7 @@ void init_opts(void) /* Default options */ opts.final_state = TASK_DEAD; + INIT_LIST_HEAD(&opts.ext_unixsk_ids); INIT_LIST_HEAD(&opts.veth_pairs); INIT_LIST_HEAD(&opts.scripts); INIT_LIST_HEAD(&opts.ext_mounts); @@ -184,7 +185,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::xVr:jlW:L:M:"; + static const char short_opts[] = "dSsRf:F:t:p:hcD:o:n:v::x::Vr:jlW:L:M:"; static struct option long_opts[] = { { "tree", required_argument, 0, 't' }, { "pid", required_argument, 0, 'p' }, @@ -201,7 +202,7 @@ int main(int argc, char *argv[], char *envp[]) { "log-file", required_argument, 0, 'o' }, { "namespaces", required_argument, 0, 'n' }, { "root", required_argument, 0, 'r' }, - { USK_EXT_PARAM, no_argument, 0, 'x' }, + { USK_EXT_PARAM, optional_argument, 0, 'x' }, { "help", no_argument, 0, 'h' }, { SK_EST_PARAM, no_argument, 0, 1042 }, { "close", required_argument, 0, 1043 }, @@ -278,6 +279,8 @@ int main(int argc, char *argv[], char *envp[]) opts.final_state = TASK_ALIVE; break; case 'x': + if (optarg && unix_sk_ids_parse(optarg) < 0) + return 1; opts.ext_unix_sk = true; break; case 'p': @@ -675,7 +678,7 @@ usage: " restore making it the parent of the restored process\n" "\n" "* Special resources support:\n" -" -x|--" USK_EXT_PARAM " allow external unix connections\n" +" -x|--" USK_EXT_PARAM "inode,.." " allow external unix connections (optionally can be assign socket's inode that allows one-sided dump)\n" " --" SK_EST_PARAM " checkpoint/restore established TCP connections\n" " -r|--root PATH change the root filesystem (when run in mount namespace)\n" " --evasive-devices use any path to a device file if the original one\n" diff --git a/files.c b/files.c index 071fa69c4..c82920dd0 100644 --- a/files.c +++ b/files.c @@ -1424,7 +1424,7 @@ bool inherited_fd(struct file_desc *d, int *fd_p) if (*fd_p < 0) pr_perror("Inherit fd DUP failed"); else - pr_info("File %s will be restored from fd %d duped " + pr_info("File %s will be restored from fd %d dumped " "from inherit fd %d\n", id_str, *fd_p, i_fd); return true; } diff --git a/include/cr_options.h b/include/cr_options.h index bf57ef947..19c2f7702 100644 --- a/include/cr_options.h +++ b/include/cr_options.h @@ -45,6 +45,7 @@ struct cr_options { }; bool restore_sibling; bool ext_unix_sk; + struct list_head ext_unixsk_ids; bool shell_job; bool handle_file_locks; bool tcp_established_ok; diff --git a/include/sockets.h b/include/sockets.h index a3010e1d0..39c22fd66 100644 --- a/include/sockets.h +++ b/include/sockets.h @@ -60,6 +60,9 @@ extern int inet_collect_one(struct nlmsghdr *h, int family, int type); extern int unix_receive_one(struct nlmsghdr *h, void *); extern int netlink_receive_one(struct nlmsghdr *hdr, void *arg); +extern int unix_sk_id_add(ino_t ino); +extern int unix_sk_ids_parse(char *optarg); + extern int do_dump_opt(int sk, int level, int name, void *val, int len); #define dump_opt(s, l, n, f) do_dump_opt(s, l, n, f, sizeof(*f)) extern int do_restore_opt(int sk, int level, int name, void *val, int len); diff --git a/lib/criu.c b/lib/criu.c index be848bba1..95bc2f674 100644 --- a/lib/criu.c +++ b/lib/criu.c @@ -225,6 +225,53 @@ void criu_set_ext_unix_sk(bool ext_unix_sk) criu_local_set_ext_unix_sk(global_opts, ext_unix_sk); } +int criu_local_add_unix_sk(criu_opts *opts, unsigned int inode) +{ + int nr; + UnixSk **a, *u; + + /*if caller forgot enable ext_unix_sk option we do it*/ + if (!opts->rpc->has_ext_unix_sk) { + criu_local_set_ext_unix_sk(opts, true); + } + + /*if user disabled ext_unix_sk and try to add unixsk inode after that*/ + if (opts->rpc->has_ext_unix_sk && !opts->rpc->ext_unix_sk) { + if (opts->rpc->n_unix_sk_ino > 0) { + free(opts->rpc->unix_sk_ino); + opts->rpc->n_unix_sk_ino = 0; + } + return -1; + } + + u = malloc(sizeof(*u)); + if (!u) + goto er; + unix_sk__init(u); + + u->inode = inode; + + nr = opts->rpc->n_unix_sk_ino + 1; + a = realloc(opts->rpc->unix_sk_ino, nr * sizeof(u)); + if (!a) + goto er_u; + + a[nr - 1] = u; + opts->rpc->unix_sk_ino = a; + opts->rpc->n_unix_sk_ino = nr; + return 0; + +er_u: + free(u); +er: + return -ENOMEM; +} + +int criu_add_unix_sk(unsigned int inode) +{ + return criu_local_add_unix_sk(global_opts, inode); +} + void criu_local_set_tcp_established(criu_opts *opts, bool tcp_established) { opts->rpc->has_tcp_established = true; diff --git a/lib/criu.h b/lib/criu.h index 30d0b4af7..85f7788f0 100644 --- a/lib/criu.h +++ b/lib/criu.h @@ -51,6 +51,7 @@ void criu_set_parent_images(char *path); void criu_set_work_dir_fd(int fd); void criu_set_leave_running(bool leave_running); void criu_set_ext_unix_sk(bool ext_unix_sk); +int criu_add_unix_sk(unsigned int inode); void criu_set_tcp_established(bool tcp_established); void criu_set_evasive_devices(bool evasive_devices); void criu_set_shell_job(bool shell_job); @@ -74,8 +75,6 @@ int criu_add_cg_root(char *ctrl, char *path); int criu_add_enable_fs(char *fs); int criu_add_skip_mnt(char *mnt); - - /* * The criu_notify_arg_t na argument is an opaque * value that callbacks (cb-s) should pass into @@ -160,6 +159,7 @@ void criu_local_set_parent_images(criu_opts *opts, char *path); void criu_local_set_work_dir_fd(criu_opts *opts, int fd); void criu_local_set_leave_running(criu_opts *opts, bool leave_running); void criu_local_set_ext_unix_sk(criu_opts *opts, bool ext_unix_sk); +int criu_local_add_unix_sk(criu_opts *opts, unsigned int inode); void criu_local_set_tcp_established(criu_opts *opts, bool tcp_established); void criu_local_set_evasive_devices(criu_opts *opts, bool evasive_devices); void criu_local_set_shell_job(criu_opts *opts, bool shell_job); diff --git a/protobuf/rpc.proto b/protobuf/rpc.proto index c1969c672..2f4d0c072 100644 --- a/protobuf/rpc.proto +++ b/protobuf/rpc.proto @@ -25,6 +25,10 @@ message cgroup_root { required string path = 2; }; +message unix_sk { + required uint32 inode = 1; +}; + message criu_opts { required int32 images_dir_fd = 1; optional int32 pid = 2; /* if not set on dump, will dump requesting process */ @@ -68,6 +72,8 @@ message criu_opts { repeated string skip_mnt = 31; repeated string enable_fs = 32; + + repeated unix_sk unix_sk_ino = 33; } message criu_dump_resp { diff --git a/sk-unix.c b/sk-unix.c index b789e1439..52c257e39 100644 --- a/sk-unix.c +++ b/sk-unix.c @@ -75,6 +75,11 @@ struct unix_sk_listen_icon { struct unix_sk_listen_icon *next; }; +struct unix_sk_exception { + struct list_head unix_sk_list; + ino_t unix_sk_ino; +}; + #define SK_HASH_SIZE 32 static struct unix_sk_listen_icon *unix_listen_icons[SK_HASH_SIZE]; @@ -139,6 +144,23 @@ static int can_dump_unix_sk(const struct unix_sk_desc *sk) return 1; } +static bool unix_sk_exception_lookup_id(ino_t ino) +{ + bool ret = false; + struct unix_sk_exception *sk; + + list_for_each_entry(sk, &opts.ext_unixsk_ids, unix_sk_list) { + if (sk->unix_sk_ino == ino) { + pr_debug("Found ino %u in exception unix sk list\n", (unsigned int)ino); + ret = true; + break; + } + } + + return ret; +} + + static int write_unix_entry(struct unix_sk_desc *sk) { int ret; @@ -663,16 +685,21 @@ static int dump_external_sockets(struct unix_sk_desc *peer) return -1; } - if (peer->type != SOCK_DGRAM) { - show_one_unix("Ext stream not supported", peer); - pr_err("Can't dump half of stream unix connection.\n"); - return -1; - } + if (unix_sk_exception_lookup_id(sk->sd.ino)) { + pr_debug("found exception for unix name-less external socket.\n"); + } else { + if (peer->type != SOCK_DGRAM) { + show_one_unix("Ext stream not supported", peer); + pr_err("Can't dump half of stream unix connection.\n"); + return -1; + } - if (!peer->name) { - show_one_unix("Ext dgram w/o name", peer); - pr_err("Can't dump name-less external socket.\n"); - return -1; + if (!peer->name) { + show_one_unix("Ext dgram w/o name", peer); + pr_err("Can't dump name-less external socket.\n"); + pr_err("%d\n", sk->fd); + return -1; + } } } else if (ret < 0) return -1; @@ -1257,3 +1284,47 @@ int resolve_unix_peers(void) return 0; } +int unix_sk_id_add(ino_t ino) +{ + struct unix_sk_exception *unix_sk; + + /* TODO: validate inode here? */ + + unix_sk = xmalloc(sizeof *unix_sk); + if (unix_sk == NULL) + return -1; + unix_sk->unix_sk_ino = ino; + list_add_tail(&unix_sk->unix_sk_list, &opts.ext_unixsk_ids); + + return 0; +} + +int unix_sk_ids_parse(char *optarg) +{ + /* + * parsing option of the following form: --ext-unix-sk=,... or short form -x,... + */ + + char *iter = optarg; + + while (*iter != '\0') { + if (*iter == ',') + iter++; + else { + ino_t ino = (ino_t)strtoul(iter, &iter, 10); + + if (0 == ino) { + pr_err("Can't parse unix socket inode from optarg: %s\n", optarg); + return -1; + } + if (unix_sk_id_add(ino) < 0) { + pr_err("Can't add unix socket inode in list: %s\n", optarg); + return -1; + } + } + } + + return 0; +} +