[CRIU] [PATCH 06/12] build: Move C and Python libraries into lib
Cyrill Gorcunov
gorcunov at openvz.org
Fri Feb 12 10:05:12 PST 2016
Both CRIU library and CRIT python data are moved into
lib/c and lib/py.
Signed-off-by: Cyrill Gorcunov <gorcunov at openvz.org>
---
Makefile | 18 +-
lib/Makefile | 48 +-
lib/c/Makefile | 13 +
lib/c/criu.c | 1239 +++++++++++++++++++++++++++++++++++++++++++++
lib/c/criu.h | 207 ++++++++
lib/c/criu.pc.in | 8 +
lib/criu.c | 1239 ---------------------------------------------
lib/criu.h | 207 --------
lib/criu.pc.in | 8 -
lib/py/.gitignore | 3 +
lib/py/Makefile | 16 +
lib/py/__init__.py | 3 +
lib/py/criu.py | 282 +++++++++++
lib/py/images/.gitignore | 4 +
lib/py/images/Makefile | 26 +
lib/py/images/__init__.py | 3 +
lib/py/images/images.py | 479 ++++++++++++++++++
lib/py/images/pb2dict.py | 276 ++++++++++
pycriu/.gitignore | 3 -
pycriu/Makefile | 16 -
pycriu/__init__.py | 3 -
pycriu/criu.py | 282 -----------
pycriu/images/.gitignore | 4 -
pycriu/images/Makefile | 26 -
pycriu/images/__init__.py | 3 -
pycriu/images/images.py | 479 ------------------
pycriu/images/pb2dict.py | 276 ----------
27 files changed, 2607 insertions(+), 2564 deletions(-)
create mode 100644 lib/c/Makefile
create mode 100644 lib/c/criu.c
create mode 100644 lib/c/criu.h
create mode 100644 lib/c/criu.pc.in
delete mode 100644 lib/criu.c
delete mode 100644 lib/criu.h
delete mode 100644 lib/criu.pc.in
create mode 100644 lib/py/.gitignore
create mode 100644 lib/py/Makefile
create mode 100644 lib/py/__init__.py
create mode 100644 lib/py/criu.py
create mode 100644 lib/py/images/.gitignore
create mode 100644 lib/py/images/Makefile
create mode 100644 lib/py/images/__init__.py
create mode 100644 lib/py/images/images.py
create mode 100644 lib/py/images/pb2dict.py
delete mode 100644 pycriu/.gitignore
delete mode 100644 pycriu/Makefile
delete mode 100644 pycriu/__init__.py
delete mode 100644 pycriu/criu.py
delete mode 100644 pycriu/images/.gitignore
delete mode 100644 pycriu/images/Makefile
delete mode 100644 pycriu/images/__init__.py
delete mode 100644 pycriu/images/images.py
delete mode 100644 pycriu/images/pb2dict.py
diff --git a/Makefile b/Makefile
index 70447b888c58..18956b455596 100644
--- a/Makefile
+++ b/Makefile
@@ -162,7 +162,7 @@ CFLAGS += $(WARNINGS) $(DEFINES)
SYSCALL-LIB := $(ARCH_DIR)/syscalls.built-in.o
ARCH-LIB := $(ARCH_DIR)/crtools.built-in.o
CRIU-SO := libcriu
-CRIU-LIB := lib/$(CRIU-SO).so
+CRIU-LIB := lib/c/$(CRIU-SO).so
CRIU-INC := lib/criu.h include/criu-plugin.h include/criu-log.h protobuf/rpc.proto
ifeq ($(piegen-y),y)
piegen := pie/piegen/piegen
@@ -225,9 +225,15 @@ built-in.o: $(VERSION_HEADER) pie
$(Q) $(MAKE) $(build-old-crtools)=. $@
lib/%:: $(VERSION_HEADER) config built-in.o
- $(Q) $(MAKE) $(build-old)=lib $@
+ $(Q) $(MAKE) -C lib $@
lib: $(VERSION_HEADER) config built-in.o
- $(Q) $(MAKE) $(build-old)=lib all
+ $(Q) $(MAKE) -C lib all
+
+$(CRIU-LIB): lib
+ @true
+crit: lib
+ @true
+
PROGRAM-BUILTINS += protobuf/built-in.o
PROGRAM-BUILTINS += built-in.o
@@ -238,9 +244,6 @@ $(PROGRAM): $(ARCH-LIB) $(PROGRAM-BUILTINS)
$(E) " LINK " $@
$(Q) $(CC) $(CFLAGS) $^ $(LIBS) $(LDFLAGS) $(GMONLDOPT) -rdynamic -o $@
-crit:
- $(Q) $(MAKE) -C pycriu all
-
zdtm: all
$(Q) $(MAKE) -C test/zdtm all
@@ -253,7 +256,7 @@ clean-built:
$(Q) $(MAKE) $(build-old)=protobuf clean
$(Q) $(MAKE) $(build-old)=pie/piegen clean
$(Q) $(MAKE) $(build-old)=pie clean
- $(Q) $(MAKE) $(build-old)=lib clean
+ $(Q) $(MAKE) -C lib clean
$(Q) $(MAKE) $(build-old-crtools)=. clean
$(Q) $(MAKE) -C Documentation clean
$(Q) $(RM) ./include/config.h
@@ -273,7 +276,6 @@ clean: clean-built
$(Q) $(RM) -r ./gcov
$(Q) $(RM) protobuf-desc-gen.h
$(Q) $(MAKE) -C test $@
- $(Q) $(MAKE) -C pycriu $@
$(Q) $(RM) ./*.pyc
$(Q) $(RM) -r build
$(Q) $(RM) -r usr
diff --git a/lib/Makefile b/lib/Makefile
index a9a661ff6afd..f07e07b9051e 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -1,14 +1,42 @@
-lib-so += $(CRIU-SO)
-obj-y += criu.o
-obj-ext-src-y += protobuf/rpc.pb-c.o
+include $(__nmk_dir)/include.mk
+include $(__nmk_dir)/macro.mk
-includes += -iquote $(obj)/../$(ARCH_DIR)/include -iquote $(obj)/../include -iquote $(obj)/.. -iquote $(obj)/../protobuf
-cflags-y += $(includes) -fPIC -Wa,--noexecstack -fno-stack-protector
-cflags-so += $(CFLAGS) -rdynamic -Wl,-soname,$(lib-so).so.$(VERSION_SO_MAJOR)
+VERSION_SO_MAJOR := 1
+VERSION_SO_MINOR := 0
+
+CRIU_SO := libcriu.so
+
+#
+# C language bindings.
+c/%:
+ $(call msg-gen, $@)
+ $(Q) $(MAKE) $(build)=c $@
+c/built-in.o:
+ $(call msg-gen, $@)
+ $(Q) $(MAKE) $(build)=c all
+
+ccflags-so += $(CFLAGS) -rdynamic -Wl,-soname,$(lib-so).so.$(VERSION_SO_MAJOR)
ldflags-so += -lprotobuf-c
+c/$(CRIU_SO): c/built-in.o
+ $(call msg-link, $@)
+ $(Q) $(CC) -shared $(ccflags-so) -o $@ $^ $(ldflags-so) $(LDFLAGS)
+lib-c: c/$(CRIU_SO)
+PHONY += lib-c
+
+#
+# Python bindings.
+lib-py:
+ $(call msg-gen, $@)
+ $(Q) $(MAKE) -C py/images all
+PHONY += lib-py
-.SECONDARY:
+clean:
+ $(call msg-clean, lib-c)
+ $(Q) $(MAKE) $(build)=c $@
+ $(Q) $(RM) c/$(CRIU_SO)
+ $(call msg-clean, lib-py)
+ $(Q) $(MAKE) -C py/images $@
-ifneq ($(MAKECMDGOALS),clean)
-incdeps := y
-endif
+all: $(PHONY)
+ @true
+PHONY += all
diff --git a/lib/c/Makefile b/lib/c/Makefile
new file mode 100644
index 000000000000..16b4ef194004
--- /dev/null
+++ b/lib/c/Makefile
@@ -0,0 +1,13 @@
+obj-y += criu.o
+obj-y += $(SRC_DIR)/images/rpc.pb-c.o
+
+ccflags-y += -iquote $(SRC_DIR)/crtools/$(ARCH_DIR)/include
+ccflags-y += -iquote $(SRC_DIR)/crtools/include -iquote $(obj)/..
+ccflags-y += -iquote $(SRC_DIR)/images
+ccflags-y += -fPIC -Wa,--noexecstack -fno-stack-protector
+
+#
+# Remove once criu moved into proper place.
+ccflags-y += -iquote $(SRC_DIR)/$(ARCH_DIR)/include
+ccflags-y += -iquote $(SRC_DIR)/include -iquote $(obj)/..
+ccflags-y += -iquote $(SRC_DIR)/images
diff --git a/lib/c/criu.c b/lib/c/criu.c
new file mode 100644
index 000000000000..52d1b61d6371
--- /dev/null
+++ b/lib/c/criu.c
@@ -0,0 +1,1239 @@
+#include "version.h"
+#include <sys/prctl.h>
+#include <sys/wait.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <limits.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <signal.h>
+
+#include "criu.h"
+#include "rpc.pb-c.h"
+#include "cr-service-const.h"
+
+#define CR_DEFAULT_SERVICE_BIN "criu"
+
+const char *criu_lib_version = CRIU_VERSION;
+
+struct criu_opts {
+ CriuOpts *rpc;
+ int (*notify)(char *action, criu_notify_arg_t na);
+ enum criu_service_comm service_comm;
+ union {
+ char *service_address;
+ int service_fd;
+ char *service_binary;
+ };
+ int swrk_pid;
+};
+
+static criu_opts *global_opts;
+static int saved_errno;
+
+void criu_local_set_service_comm(criu_opts *opts, enum criu_service_comm comm)
+{
+ opts->service_comm = comm;
+}
+
+void criu_set_service_comm(enum criu_service_comm comm)
+{
+ criu_local_set_service_comm(global_opts, comm);
+}
+
+void criu_local_set_service_address(criu_opts *opts, char *path)
+{
+ if (path)
+ opts->service_address = path;
+ else
+ opts->service_address = CR_DEFAULT_SERVICE_ADDRESS;
+}
+
+void criu_set_service_address(char *path)
+{
+ criu_local_set_service_address(global_opts, path);
+}
+
+void criu_local_set_service_fd(criu_opts *opts, int fd)
+{
+ opts->service_fd = fd;
+}
+
+void criu_set_service_fd(int fd)
+{
+ criu_local_set_service_fd(global_opts, fd);
+}
+
+void criu_local_set_service_binary(criu_opts *opts, char *path)
+{
+ if (path)
+ opts->service_binary = path;
+ else
+ opts->service_binary = CR_DEFAULT_SERVICE_BIN;
+}
+
+void criu_set_service_binary(char *path)
+{
+ criu_local_set_service_binary(global_opts, path);
+}
+
+int criu_local_init_opts(criu_opts **o)
+{
+ criu_opts *opts = NULL;
+ CriuOpts *rpc = NULL;
+
+ opts = *o;
+
+ if (opts) {
+ if (opts->rpc)
+ criu_opts__free_unpacked(opts->rpc, NULL);
+
+ free(opts);
+ opts = NULL;
+ }
+
+ rpc = malloc(sizeof(CriuOpts));
+ if (rpc == NULL) {
+ perror("Can't allocate memory for criu RPC opts");
+ return -1;
+ }
+
+ criu_opts__init(rpc);
+
+ opts = malloc(sizeof(criu_opts));
+ if (opts == NULL) {
+ perror("Can't allocate memory for criu opts");
+ criu_opts__free_unpacked(rpc, NULL);
+ return -1;
+ }
+
+ opts->rpc = rpc;
+ opts->notify = NULL;
+
+ opts->service_comm = CRIU_COMM_BIN;
+ opts->service_address = CR_DEFAULT_SERVICE_BIN;
+
+ *o = opts;
+
+ return 0;
+}
+
+int criu_init_opts(void)
+{
+ return criu_local_init_opts(&global_opts);
+}
+
+void criu_local_set_notify_cb(criu_opts *opts, int (*cb)(char *action, criu_notify_arg_t na))
+{
+ opts->notify = cb;
+ opts->rpc->has_notify_scripts = true;
+ opts->rpc->notify_scripts = true;
+}
+
+void criu_set_notify_cb(int (*cb)(char *action, criu_notify_arg_t na))
+{
+ criu_local_set_notify_cb(global_opts, cb);
+}
+
+int criu_notify_pid(criu_notify_arg_t na)
+{
+ return na->has_pid ? na->pid : 0;
+}
+
+void criu_local_set_pid(criu_opts *opts, int pid)
+{
+ opts->rpc->has_pid = true;
+ opts->rpc->pid = pid;
+}
+
+void criu_set_pid(int pid)
+{
+ criu_local_set_pid(global_opts, pid);
+}
+
+void criu_local_set_images_dir_fd(criu_opts *opts, int fd)
+{
+ opts->rpc->images_dir_fd = fd;
+}
+
+void criu_set_images_dir_fd(int fd)
+{
+ criu_local_set_images_dir_fd(global_opts, fd);
+}
+
+void criu_local_set_parent_images(criu_opts *opts, char *path)
+{
+ opts->rpc->parent_img = strdup(path);
+}
+
+void criu_set_parent_images(char *path)
+{
+ criu_local_set_parent_images(global_opts, path);
+}
+
+void criu_local_set_track_mem(criu_opts *opts, bool track_mem)
+{
+ opts->rpc->has_track_mem = true;
+ opts->rpc->track_mem = track_mem;
+}
+
+void criu_set_track_mem(bool track_mem)
+{
+ criu_local_set_track_mem(global_opts, track_mem);
+}
+
+void criu_local_set_auto_dedup(criu_opts *opts, bool auto_dedup)
+{
+ opts->rpc->has_auto_dedup = true;
+ opts->rpc->auto_dedup = auto_dedup;
+}
+
+void criu_set_auto_dedup(bool auto_dedup)
+{
+ criu_local_set_auto_dedup(global_opts, auto_dedup);
+}
+
+void criu_local_set_force_irmap(criu_opts *opts, bool force_irmap)
+{
+ opts->rpc->has_force_irmap = true;
+ opts->rpc->force_irmap = force_irmap;
+}
+
+void criu_set_force_irmap(bool force_irmap)
+{
+ criu_local_set_force_irmap(global_opts, force_irmap);
+}
+
+void criu_local_set_link_remap(criu_opts *opts, bool link_remap)
+{
+ opts->rpc->has_link_remap = true;
+ opts->rpc->link_remap = link_remap;
+}
+
+void criu_set_link_remap(bool link_remap)
+{
+ criu_local_set_link_remap(global_opts, link_remap);
+}
+
+void criu_local_set_work_dir_fd(criu_opts *opts, int fd)
+{
+ opts->rpc->has_work_dir_fd = true;
+ opts->rpc->work_dir_fd = fd;
+}
+
+void criu_set_work_dir_fd(int fd)
+{
+ criu_local_set_work_dir_fd(global_opts, fd);
+}
+
+void criu_local_set_leave_running(criu_opts *opts, bool leave_running)
+{
+ opts->rpc->has_leave_running = true;
+ opts->rpc->leave_running = leave_running;
+}
+
+void criu_set_leave_running(bool leave_running)
+{
+ criu_local_set_leave_running(global_opts, leave_running);
+}
+
+void criu_local_set_ext_unix_sk(criu_opts *opts, bool ext_unix_sk)
+{
+ opts->rpc->has_ext_unix_sk = true;
+ opts->rpc->ext_unix_sk = ext_unix_sk;
+}
+
+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;
+ opts->rpc->tcp_established = tcp_established;
+}
+
+void criu_set_tcp_established(bool tcp_established)
+{
+ criu_local_set_tcp_established(global_opts, tcp_established);
+}
+
+void criu_local_set_evasive_devices(criu_opts *opts, bool evasive_devices)
+{
+ opts->rpc->has_evasive_devices = true;
+ opts->rpc->evasive_devices = evasive_devices;
+}
+
+void criu_set_evasive_devices(bool evasive_devices)
+{
+ criu_local_set_evasive_devices(global_opts, evasive_devices);
+}
+
+void criu_local_set_shell_job(criu_opts *opts, bool shell_job)
+{
+ opts->rpc->has_shell_job = true;
+ opts->rpc->shell_job = shell_job;
+}
+
+void criu_set_shell_job(bool shell_job)
+{
+ criu_local_set_shell_job(global_opts, shell_job);
+}
+
+void criu_local_set_file_locks(criu_opts *opts, bool file_locks)
+{
+ opts->rpc->has_file_locks = true;
+ opts->rpc->file_locks = file_locks;
+}
+
+void criu_set_file_locks(bool file_locks)
+{
+ criu_local_set_file_locks(global_opts, file_locks);
+}
+
+void criu_local_set_log_level(criu_opts *opts, int log_level)
+{
+ opts->rpc->has_log_level = true;
+ opts->rpc->log_level = log_level;
+}
+
+void criu_set_log_level(int log_level)
+{
+ criu_local_set_log_level(global_opts, log_level);
+}
+
+void criu_local_set_root(criu_opts *opts, char *root)
+{
+ opts->rpc->root = strdup(root);
+}
+
+void criu_set_root(char *root)
+{
+ criu_local_set_root(global_opts, root);
+}
+
+void criu_local_set_manage_cgroups(criu_opts *opts, bool manage)
+{
+ opts->rpc->has_manage_cgroups = true;
+ opts->rpc->manage_cgroups = manage;
+}
+
+void criu_set_manage_cgroups(bool manage)
+{
+ criu_local_set_manage_cgroups(global_opts, manage);
+}
+
+void criu_local_set_manage_cgroups_mode(criu_opts *opts, enum criu_cg_mode mode)
+{
+ opts->rpc->has_manage_cgroups_mode = true;
+ opts->rpc->manage_cgroups_mode = (CriuCgMode)mode;
+}
+
+void criu_set_manage_cgroups_mode(enum criu_cg_mode mode)
+{
+ criu_local_set_manage_cgroups_mode(global_opts, mode);
+}
+
+void criu_local_set_auto_ext_mnt(criu_opts *opts, bool val)
+{
+ opts->rpc->has_auto_ext_mnt = true;
+ opts->rpc->auto_ext_mnt = val;
+}
+
+void criu_set_auto_ext_mnt(bool val)
+{
+ criu_local_set_auto_ext_mnt(global_opts, val);
+}
+
+void criu_local_set_ext_sharing(criu_opts *opts, bool val)
+{
+ opts->rpc->has_ext_sharing = true;
+ opts->rpc->ext_sharing = val;
+}
+
+void criu_set_ext_sharing(bool val)
+{
+ criu_local_set_ext_sharing(global_opts, val);
+}
+
+void criu_local_set_ext_masters(criu_opts *opts, bool val)
+{
+ opts->rpc->has_ext_masters = true;
+ opts->rpc->ext_masters = val;
+}
+
+void criu_set_ext_masters(bool val)
+{
+ criu_local_set_ext_masters(global_opts, val);
+}
+
+void criu_local_set_log_file(criu_opts *opts, char *log_file)
+{
+ opts->rpc->log_file = strdup(log_file);
+}
+
+void criu_set_log_file(char *log_file)
+{
+ criu_local_set_log_file(global_opts, log_file);
+}
+
+void criu_local_set_cpu_cap(criu_opts *opts, unsigned int cap)
+{
+ opts->rpc->has_cpu_cap = true;
+ opts->rpc->cpu_cap = cap;
+}
+
+void criu_set_cpu_cap(unsigned int cap)
+{
+ criu_local_set_cpu_cap(global_opts, cap);
+}
+
+int criu_local_set_exec_cmd(criu_opts *opts, int argc, char *argv[])
+{
+ int i;
+
+ opts->rpc->n_exec_cmd = argc;
+ opts->rpc->exec_cmd = malloc((argc) * sizeof(char *));
+
+ if (opts->rpc->exec_cmd) {
+ for (i = 0; i < argc; i++) {
+ opts->rpc->exec_cmd[i] = strdup(argv[i]);
+ if (!opts->rpc->exec_cmd[i]) {
+ while (i > 0)
+ free(opts->rpc->exec_cmd[i--]);
+ free(opts->rpc->exec_cmd);
+ opts->rpc->n_exec_cmd = 0;
+ opts->rpc->exec_cmd = NULL;
+ goto out;
+ }
+ }
+ return 0;
+ }
+
+out:
+ return -ENOMEM;
+}
+
+int criu_set_exec_cmd(int argc, char *argv[])
+{
+ return criu_local_set_exec_cmd(global_opts, argc, argv);
+}
+
+int criu_local_add_ext_mount(criu_opts *opts, char *key, char *val)
+{
+ int nr;
+ ExtMountMap **a, *m;
+
+ m = malloc(sizeof(*m));
+ if (!m)
+ goto er;
+ ext_mount_map__init(m);
+
+ m->key = strdup(key);
+ if (!m->key)
+ goto er_n;
+ m->val = strdup(val);
+ if (!m->val)
+ goto er_k;
+
+ nr = opts->rpc->n_ext_mnt + 1;
+ a = realloc(opts->rpc->ext_mnt, nr * sizeof(m));
+ if (!a)
+ goto er_v;
+
+ a[nr - 1] = m;
+ opts->rpc->ext_mnt = a;
+ opts->rpc->n_ext_mnt = nr;
+ return 0;
+
+er_v:
+ free(m->val);
+er_k:
+ free(m->key);
+er_n:
+ free(m);
+er:
+ return -ENOMEM;
+}
+
+int criu_add_ext_mount(char *key, char *val)
+{
+ return criu_local_add_ext_mount(global_opts, key, val);
+}
+
+int criu_local_add_cg_root(criu_opts *opts, char *ctrl, char *path)
+{
+ int nr;
+ CgroupRoot **a, *root;
+
+ root = malloc(sizeof(*root));
+ if (!root)
+ goto er;
+ cgroup_root__init(root);
+
+ if (ctrl) {
+ root->ctrl = strdup(ctrl);
+ if (!root->ctrl)
+ goto er_r;
+ }
+
+ root->path = strdup(path);
+ if (!root->path)
+ goto er_c;
+
+ nr = opts->rpc->n_cg_root + 1;
+ a = realloc(opts->rpc->cg_root, nr * sizeof(root));
+ if (!a)
+ goto er_p;
+
+ a[nr - 1] = root;
+ opts->rpc->cg_root = a;
+ opts->rpc->n_cg_root = nr;
+ return 0;
+
+er_p:
+ free(root->path);
+er_c:
+ if (root->ctrl)
+ free(root->ctrl);
+er_r:
+ free(root);
+er:
+ return -ENOMEM;
+}
+
+int criu_add_cg_root(char *ctrl, char *path)
+{
+ return criu_local_add_cg_root(global_opts, ctrl, path);
+}
+
+int criu_local_add_veth_pair(criu_opts *opts, char *in, char *out)
+{
+ int nr;
+ CriuVethPair **a, *p;
+
+ p = malloc(sizeof(*p));
+ if (!p)
+ goto er;
+ criu_veth_pair__init(p);
+
+ p->if_in = strdup(in);
+ if (!p->if_in)
+ goto er_p;
+ p->if_out = strdup(out);
+ if (!p->if_out)
+ goto er_i;
+
+ nr = opts->rpc->n_veths + 1;
+ a = realloc(opts->rpc->veths, nr * sizeof(p));
+ if (!a)
+ goto er_o;
+
+ a[nr - 1] = p;
+ opts->rpc->veths = a;
+ opts->rpc->n_veths = nr;
+ return 0;
+
+er_o:
+ free(p->if_out);
+er_i:
+ free(p->if_in);
+er_p:
+ free(p);
+er:
+ return -ENOMEM;
+}
+
+int criu_add_veth_pair(char *in, char *out)
+{
+ return criu_local_add_veth_pair(global_opts, in, out);
+}
+
+int criu_local_add_enable_fs(criu_opts *opts, char *fs)
+{
+ int nr;
+ char *str = NULL;
+ char **ptr = NULL;
+
+ str = strdup(fs);
+ if (!str)
+ goto err;
+
+ nr = opts->rpc->n_enable_fs + 1;
+ ptr = realloc(opts->rpc->enable_fs, nr * sizeof(*ptr));
+ if (!ptr)
+ goto err;
+
+ ptr[nr - 1] = str;
+
+ opts->rpc->n_enable_fs = nr;
+ opts->rpc->enable_fs = ptr;
+
+ return 0;
+
+err:
+ if (str)
+ free(str);
+ if (ptr)
+ free(ptr);
+
+ return -ENOMEM;
+}
+
+int criu_add_enable_fs(char *fs)
+{
+ return criu_local_add_enable_fs(global_opts, fs);
+}
+
+
+int criu_local_add_skip_mnt(criu_opts *opts, char *mnt)
+{
+ int nr;
+ char *str = NULL;
+ char **ptr = NULL;
+
+ str = strdup(mnt);
+ if (!str)
+ goto err;
+
+ nr = opts->rpc->n_skip_mnt + 1;
+ ptr = realloc(opts->rpc->skip_mnt, nr * sizeof(*ptr));
+ if (!ptr)
+ goto err;
+
+ ptr[nr - 1] = str;
+
+ opts->rpc->n_skip_mnt = nr;
+ opts->rpc->skip_mnt = ptr;
+
+ return 0;
+
+err:
+ if (str)
+ free(str);
+ if (ptr)
+ free(ptr);
+
+ return -ENOMEM;
+}
+
+int criu_local_add_irmap_path(criu_opts *opts, char *path)
+{
+ int nr;
+ char *my_path;
+ char **m;
+
+ if (!opts)
+ return -1;
+
+ my_path = strdup(path);
+ if (!my_path)
+ goto err;
+
+ nr = opts->rpc->n_irmap_scan_paths + 1;
+ m = realloc(opts->rpc->irmap_scan_paths, nr * sizeof(*m));
+ if (!m)
+ goto err;
+
+ m[nr - 1] = my_path;
+
+ opts->rpc->n_irmap_scan_paths = nr;
+ opts->rpc->irmap_scan_paths = m;
+
+ return 0;
+
+err:
+ if (my_path)
+ free(my_path);
+
+ return -ENOMEM;
+}
+
+int criu_add_skip_mnt(char *mnt)
+{
+ return criu_local_add_skip_mnt(global_opts, mnt);
+}
+
+void criu_local_set_ghost_limit(criu_opts *opts, unsigned int limit)
+{
+ opts->rpc->has_ghost_limit = true;
+ opts->rpc->ghost_limit = limit;
+}
+
+void criu_set_ghost_limit(unsigned int limit)
+{
+ criu_local_set_ghost_limit(global_opts, limit);
+}
+
+int criu_add_irmap_path(char *path)
+{
+ return criu_local_add_irmap_path(global_opts, path);
+}
+
+static CriuResp *recv_resp(int socket_fd)
+{
+ unsigned char *buf = NULL;
+ int len;
+ CriuResp *msg = 0;
+
+ len = recv(socket_fd, NULL, 0, MSG_TRUNC | MSG_PEEK);
+ if (len == -1) {
+ perror("Can't read request");
+ goto err;
+ }
+
+ buf = malloc(len);
+ if (!buf) {
+ errno = ENOMEM;
+ perror("Can't receive response");
+ goto err;
+ }
+
+ len = recv(socket_fd, buf, len, MSG_TRUNC);
+ if (len == -1) {
+ perror("Can't read request");
+ goto err;
+ }
+
+ msg = criu_resp__unpack(NULL, len, buf);
+ if (!msg) {
+ perror("Failed unpacking response");
+ goto err;
+ }
+
+ free(buf);
+ return msg;
+err:
+ free(buf);
+ saved_errno = errno;
+ return NULL;
+}
+
+static int send_req(int socket_fd, CriuReq *req)
+{
+ unsigned char *buf;
+ int len;
+
+ len = criu_req__get_packed_size(req);
+
+ buf = malloc(len);
+ if (!buf) {
+ errno = ENOMEM;
+ perror("Can't send request");
+ goto err;
+ }
+
+ if (criu_req__pack(req, buf) != len) {
+ perror("Failed packing request");
+ goto err;
+ }
+
+ if (write(socket_fd, buf, len) == -1) {
+ perror("Can't send request");
+ goto err;
+ }
+
+ free(buf);
+ return 0;
+err:
+ free(buf);
+ saved_errno = errno;
+ return -1;
+}
+
+static int send_notify_ack(int socket_fd, int ret)
+{
+ int send_ret;
+ CriuReq req = CRIU_REQ__INIT;
+
+ req.type = CRIU_REQ_TYPE__NOTIFY;
+ req.has_notify_success = true;
+ req.notify_success = (ret == 0);
+
+ send_ret = send_req(socket_fd, &req);
+
+ /*
+ * If we're failing the notification then report
+ * back the original error code (and it will be
+ * propagated back to user).
+ *
+ * If the notification was OK, then report the
+ * result of acking it.
+ */
+
+ return ret ? : send_ret;
+}
+
+static void swrk_wait(criu_opts *opts)
+{
+ if (opts->service_comm == CRIU_COMM_BIN)
+ waitpid(opts->swrk_pid, NULL, 0);
+}
+
+static int swrk_connect(criu_opts *opts, bool d)
+{
+ int sks[2], pid, ret = -1;
+
+ if (socketpair(PF_LOCAL, SOCK_SEQPACKET, 0, sks))
+ goto out;
+
+ pid = fork();
+ if (pid < 0)
+ goto err;
+
+ if (pid == 0) {
+ sigset_t mask;
+ char fds[11];
+
+ /*
+ * Unblock SIGCHLD.
+ *
+ * The caller of this function is supposed to have
+ * this signal blocked. Otherwise it risks to get
+ * into situation, when this routine is not yet
+ * returned, but the restore subtree exits and
+ * emits the SIGCHLD.
+ *
+ * In turn, unblocked SIGCHLD is required to make
+ * criu restoration process work -- it catches
+ * subtasks restore errors in this handler.
+ */
+
+ sigemptyset(&mask);
+ sigaddset(&mask, SIGCHLD);
+ sigprocmask(SIG_UNBLOCK, &mask, NULL);
+
+ close(sks[0]);
+ sprintf(fds, "%d", sks[1]);
+
+ if (d)
+ if (daemon(0, 1)) {
+ perror("Can't detach for a self-dump");
+ goto child_err;
+ }
+
+ pid = getpid();
+ if (write(sks[1], &pid, sizeof(pid)) != sizeof(pid)) {
+ perror("Can't write swrk pid");
+ goto child_err;
+ }
+
+ execlp(opts->service_binary, opts->service_binary, "swrk", fds, NULL);
+ perror("Can't exec criu swrk");
+child_err:
+ close(sks[1]);
+ exit(1);
+ }
+
+ close(sks[1]);
+
+ if (read(sks[0], &pid, sizeof(pid)) != sizeof(pid)) {
+ perror("Can't read swrk pid");
+ goto err;
+ }
+
+ opts->swrk_pid = pid;
+ ret = sks[0];
+
+out:
+ return ret;
+
+err:
+ close(sks[0]);
+ close(sks[1]);
+ goto out;
+}
+
+static int criu_connect(criu_opts *opts, bool d)
+{
+ int fd, ret;
+ struct sockaddr_un addr;
+ socklen_t addr_len;
+
+ if (opts->service_comm == CRIU_COMM_FD)
+ return opts->service_fd;
+ else if (opts->service_comm == CRIU_COMM_BIN)
+ return swrk_connect(opts, d);
+
+ fd = socket(AF_LOCAL, SOCK_SEQPACKET, 0);
+ if (fd < 0) {
+ saved_errno = errno;
+ perror("Can't create socket");
+ return -1;
+ }
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sun_family = AF_LOCAL;
+
+ strncpy(addr.sun_path, opts->service_address, sizeof(addr.sun_path));
+
+ addr_len = strlen(opts->service_address) + sizeof(addr.sun_family);
+
+ ret = connect(fd, (struct sockaddr *) &addr, addr_len);
+ if (ret < 0) {
+ saved_errno = errno;
+ perror("Can't connect to socket");
+ close(fd);
+ return -1;
+ }
+
+ return fd;
+}
+
+static int send_req_and_recv_resp_sk(int fd, criu_opts *opts, CriuReq *req, CriuResp **resp)
+{
+ int ret = 0;
+
+ if (send_req(fd, req) < 0) {
+ ret = -ECOMM;
+ goto exit;
+ }
+
+again:
+ *resp = recv_resp(fd);
+ if (!*resp) {
+ perror("Can't receive response");
+ ret = -ECOMM;
+ goto exit;
+ }
+
+ if ((*resp)->type == CRIU_REQ_TYPE__NOTIFY) {
+ if (opts->notify)
+ ret = opts->notify((*resp)->notify->script, (*resp)->notify);
+
+ ret = send_notify_ack(fd, ret);
+ if (!ret)
+ goto again;
+ else
+ goto exit;
+ }
+
+ if ((*resp)->type != req->type) {
+ if ((*resp)->type == CRIU_REQ_TYPE__EMPTY &&
+ (*resp)->success == false)
+ ret = -EINVAL;
+ else {
+ perror("Unexpected response type");
+ ret = -EBADMSG;
+ }
+ }
+
+ if ((*resp)->has_cr_errno)
+ saved_errno = (*resp)->cr_errno;
+
+exit:
+ return ret;
+}
+
+static int send_req_and_recv_resp(criu_opts *opts, CriuReq *req, CriuResp **resp)
+{
+ int fd;
+ int ret = 0;
+ bool d = false;
+
+ if (req->type == CRIU_REQ_TYPE__DUMP && req->opts->has_pid == false)
+ d = true;
+
+ fd = criu_connect(opts, d);
+ if (fd < 0) {
+ perror("Can't connect to criu");
+ ret = -ECONNREFUSED;
+ } else {
+ ret = send_req_and_recv_resp_sk(fd, opts, req, resp);
+ close(fd);
+ }
+
+ return ret;
+}
+
+int criu_local_check(criu_opts *opts)
+{
+ int ret = -1;
+ CriuReq req = CRIU_REQ__INIT;
+ CriuResp *resp = NULL;
+
+ saved_errno = 0;
+
+ req.type = CRIU_REQ_TYPE__CHECK;
+
+ ret = send_req_and_recv_resp(opts, &req, &resp);
+ if (ret)
+ goto exit;
+
+ ret = resp->success ? 0 : -EBADE;
+
+exit:
+ if (resp)
+ criu_resp__free_unpacked(resp, NULL);
+
+ swrk_wait(opts);
+
+ errno = saved_errno;
+
+ return ret;
+}
+
+int criu_check(void)
+{
+ return criu_local_check(global_opts);
+}
+
+int criu_local_dump(criu_opts *opts)
+{
+ int ret = -1;
+ CriuReq req = CRIU_REQ__INIT;
+ CriuResp *resp = NULL;
+
+ saved_errno = 0;
+
+ req.type = CRIU_REQ_TYPE__DUMP;
+ req.opts = opts->rpc;
+
+ ret = send_req_and_recv_resp(opts, &req, &resp);
+ if (ret)
+ goto exit;
+
+ if (resp->success) {
+ if (resp->dump->has_restored && resp->dump->restored)
+ ret = 1;
+ else
+ ret = 0;
+ } else
+ ret = -EBADE;
+
+exit:
+ if (resp)
+ criu_resp__free_unpacked(resp, NULL);
+
+ swrk_wait(opts);
+
+ errno = saved_errno;
+
+ return ret;
+}
+
+int criu_dump(void)
+{
+ return criu_local_dump(global_opts);
+}
+
+int criu_local_dump_iters(criu_opts *opts, int (*more)(criu_predump_info pi))
+{
+ int ret = -1, fd = -1, uret;
+ CriuReq req = CRIU_REQ__INIT;
+ CriuResp *resp = NULL;
+
+ saved_errno = 0;
+
+ req.type = CRIU_REQ_TYPE__PRE_DUMP;
+ req.opts = opts->rpc;
+
+ ret = -EINVAL;
+ /*
+ * Self-dump in iterable manner is tricky and
+ * not supported for the moment.
+ *
+ * Calls w/o iteration callback is, well, not
+ * allowed either.
+ */
+ if (!opts->rpc->has_pid || !more)
+ goto exit;
+
+ ret = -ECONNREFUSED;
+ fd = criu_connect(opts, false);
+ if (fd < 0)
+ goto exit;
+
+ while (1) {
+ ret = send_req_and_recv_resp_sk(fd, opts, &req, &resp);
+ if (ret)
+ goto exit;
+
+ if (!resp->success) {
+ ret = -EBADE;
+ goto exit;
+ }
+
+ uret = more(NULL);
+ if (uret < 0) {
+ ret = uret;
+ goto exit;
+ }
+
+ criu_resp__free_unpacked(resp, NULL);
+
+ if (uret == 0)
+ break;
+ }
+
+ req.type = CRIU_REQ_TYPE__DUMP;
+ ret = send_req_and_recv_resp_sk(fd, opts, &req, &resp);
+ if (!ret)
+ ret = (resp->success ? 0 : -EBADE);
+exit:
+ if (fd >= 0)
+ close(fd);
+ if (resp)
+ criu_resp__free_unpacked(resp, NULL);
+
+ swrk_wait(opts);
+
+ errno = saved_errno;
+
+ return ret;
+}
+
+int criu_dump_iters(int (*more)(criu_predump_info pi))
+{
+ return criu_local_dump_iters((void *)global_opts, more);
+}
+
+int criu_local_restore(criu_opts *opts)
+{
+ int ret = -1;
+ CriuReq req = CRIU_REQ__INIT;
+ CriuResp *resp = NULL;
+
+ saved_errno = 0;
+
+ req.type = CRIU_REQ_TYPE__RESTORE;
+ req.opts = opts->rpc;
+
+ ret = send_req_and_recv_resp(opts, &req, &resp);
+ if (ret)
+ goto exit;
+
+ if (resp->success)
+ ret = resp->restore->pid;
+ else
+ ret = -EBADE;
+
+exit:
+ if (resp)
+ criu_resp__free_unpacked(resp, NULL);
+
+ swrk_wait(opts);
+
+ errno = saved_errno;
+
+ return ret;
+}
+
+int criu_restore(void)
+{
+ return criu_local_restore(global_opts);
+}
+
+int criu_local_restore_child(criu_opts *opts)
+{
+ int sk, ret = -1;
+ enum criu_service_comm saved_comm;
+ char *saved_comm_data;
+ bool save_comm;
+ CriuReq req = CRIU_REQ__INIT;
+ CriuResp *resp = NULL;
+
+ /*
+ * restore_child is not possible with criu running as a system
+ * service, so we need to switch comm method to CRIU_COMM_BIN.
+ * We're doing so because of the backward compatibility, and we
+ * should probably consider requiring CRIU_COMM_BIN to be set by
+ * user at some point.
+ */
+ save_comm = (opts->service_comm != CRIU_COMM_BIN);
+ if (save_comm) {
+ /* Save comm */
+ saved_comm = opts->service_comm;
+ saved_comm_data = opts->service_address;
+
+ opts->service_comm = CRIU_COMM_BIN;
+ opts->service_binary = CR_DEFAULT_SERVICE_BIN;
+ }
+
+ sk = swrk_connect(opts, false);
+ if (save_comm) {
+ /* Restore comm */
+ opts->service_comm = saved_comm;
+ opts->service_address = saved_comm_data;
+ }
+
+ if (sk < 0)
+ return -1;
+
+ saved_errno = 0;
+
+ req.type = CRIU_REQ_TYPE__RESTORE;
+ req.opts = opts->rpc;
+
+ req.opts->has_rst_sibling = true;
+ req.opts->rst_sibling = true;
+
+ ret = send_req_and_recv_resp_sk(sk, opts, &req, &resp);
+
+ swrk_wait(opts);
+
+ if (!ret) {
+ ret = resp->success ? resp->restore->pid : -EBADE;
+ criu_resp__free_unpacked(resp, NULL);
+ }
+
+ close(sk);
+ errno = saved_errno;
+ return ret;
+}
+
+int criu_restore_child(void)
+{
+ return criu_local_restore_child(global_opts);
+}
diff --git a/lib/c/criu.h b/lib/c/criu.h
new file mode 100644
index 000000000000..0898be024f63
--- /dev/null
+++ b/lib/c/criu.h
@@ -0,0 +1,207 @@
+/*
+ * (C) Copyright 2013 Parallels, Inc. (www.parallels.com).
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the GNU Lesser General Public License
+ * (LGPL) version 2.1 which accompanies this distribution, and is available at
+ * http://www.gnu.org/licenses/lgpl-2.1.html
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, you can find it here:
+ * www.gnu.org/licenses/lgpl.html
+ */
+
+#ifndef __CRIU_LIB_H__
+#define __CRIU_LIB_H__
+
+#include <stdbool.h>
+
+#ifdef __GNUG__
+ extern "C" {
+#endif
+
+enum criu_service_comm {
+ CRIU_COMM_SK,
+ CRIU_COMM_FD,
+ CRIU_COMM_BIN
+};
+
+enum criu_cg_mode {
+ CRIU_CG_MODE_IGNORE = 0,
+ CRIU_CG_MODE_NONE = 1,
+ CRIU_CG_MODE_PROPS = 2,
+ CRIU_CG_MODE_SOFT = 3,
+ CRIU_CG_MODE_FULL = 4,
+ CRIU_CG_MODE_STRICT = 5,
+ CRIU_CG_MODE_DEFAULT = 6
+};
+
+void criu_set_service_address(char *path);
+void criu_set_service_fd(int fd);
+void criu_set_service_binary(char *path);
+
+/*
+ * You can choose if you want libcriu to connect to service socket
+ * by itself, use provided file descriptor or spawn swrk by itself
+ */
+void criu_set_service_comm(enum criu_service_comm);
+
+/*
+ * Set opts to defaults. _Must_ be called first before using any functions from
+ * the list down below. 0 on success, -1 on fail.
+ */
+int criu_init_opts(void);
+
+void criu_set_pid(int pid);
+void criu_set_images_dir_fd(int fd); /* must be set for dump/restore */
+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);
+void criu_set_file_locks(bool file_locks);
+void criu_set_track_mem(bool track_mem);
+void criu_set_auto_dedup(bool auto_dedup);
+void criu_set_force_irmap(bool force_irmap);
+void criu_set_link_remap(bool link_remap);
+void criu_set_log_level(int log_level);
+void criu_set_log_file(char *log_file);
+void criu_set_cpu_cap(unsigned int cap);
+void criu_set_root(char *root);
+void criu_set_manage_cgroups(bool manage);
+void criu_set_manage_cgroups_mode(enum criu_cg_mode mode);
+void criu_set_auto_ext_mnt(bool val);
+void criu_set_ext_sharing(bool val);
+void criu_set_ext_masters(bool val);
+int criu_set_exec_cmd(int argc, char *argv[]);
+int criu_add_ext_mount(char *key, char *val);
+int criu_add_veth_pair(char *in, char *out);
+int criu_add_cg_root(char *ctrl, char *path);
+int criu_add_enable_fs(char *fs);
+int criu_add_skip_mnt(char *mnt);
+void criu_set_ghost_limit(unsigned int limit);
+int criu_add_irmap_path(char *path);
+
+/*
+ * The criu_notify_arg_t na argument is an opaque
+ * value that callbacks (cb-s) should pass into
+ * criu_notify_xxx() calls to fetch arbitrary values
+ * from notification. If the value is not available
+ * some non-existing one is reported.
+ */
+
+typedef struct _CriuNotify *criu_notify_arg_t;
+void criu_set_notify_cb(int (*cb)(char *action, criu_notify_arg_t na));
+
+/* Get pid of root task. 0 if not available */
+int criu_notify_pid(criu_notify_arg_t na);
+
+/* Here is a table of return values and errno's of functions
+ * from the list down below.
+ *
+ * Return value errno Description
+ * ----------------------------------------------------------------------------
+ * 0 undefined Success.
+ *
+ * >0 undefined Success(criu_restore() only).
+ *
+ * -BADE rpc err (0 for now) RPC has returned fail.
+ *
+ * -ECONNREFUSED errno Unable to connect to CRIU.
+ *
+ * -ECOMM errno Unable to send/recv msg to/from CRIU.
+ *
+ * -EINVAL undefined CRIU doesn't support this type of request.
+ * You should probably update CRIU.
+ *
+ * -EBADMSG undefined Unexpected response from CRIU.
+ * You should probably update CRIU.
+ */
+int criu_check(void);
+int criu_dump(void);
+int criu_restore(void);
+int criu_restore_child(void);
+
+/*
+ * Perform dumping but with preliminary iterations. Each
+ * time an iteration ends the ->more callback is called.
+ * The callback's return value is
+ * - positive -- one more iteration starts
+ * - zero -- final dump is performed and call exits
+ * - negative -- dump is aborted, the value is returned
+ * back from criu_dump_iters
+ *
+ * The @pi argument is an opaque value that caller may
+ * use to request pre-dump statistics (not yet implemented).
+ */
+typedef void *criu_predump_info;
+int criu_dump_iters(int (*more)(criu_predump_info pi));
+
+/*
+ * Same as the list above, but lets you have your very own options
+ * structure and lets you set individual options in it.
+ */
+typedef struct criu_opts criu_opts;
+
+int criu_local_init_opts(criu_opts **opts);
+
+void criu_local_set_service_address(criu_opts *opts, char *path);
+void criu_local_set_service_fd(criu_opts *opts, int fd);
+void criu_local_set_service_comm(criu_opts *opts, enum criu_service_comm);
+
+void criu_local_set_service_fd(criu_opts *opts, int fd);
+
+void criu_local_set_pid(criu_opts *opts, int pid);
+void criu_local_set_images_dir_fd(criu_opts *opts, int fd); /* must be set for dump/restore */
+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);
+void criu_local_set_file_locks(criu_opts *opts, bool file_locks);
+void criu_local_set_track_mem(criu_opts *opts, bool track_mem);
+void criu_local_set_auto_dedup(criu_opts *opts, bool auto_dedup);
+void criu_local_set_force_irmap(criu_opts *opts, bool force_irmap);
+void criu_local_set_link_remap(criu_opts *opts, bool link_remap);
+void criu_local_set_log_level(criu_opts *opts, int log_level);
+void criu_local_set_log_file(criu_opts *opts, char *log_file);
+void criu_local_set_cpu_cap(criu_opts *opts, unsigned int cap);
+void criu_local_set_root(criu_opts *opts, char *root);
+void criu_local_set_manage_cgroups(criu_opts *opts, bool manage);
+void criu_local_set_manage_cgroups_mode(criu_opts *opts, enum criu_cg_mode mode);
+void criu_local_set_auto_ext_mnt(criu_opts *opts, bool val);
+void criu_local_set_ext_sharing(criu_opts *opts, bool val);
+void criu_local_set_ext_masters(criu_opts *opts, bool val);
+int criu_local_set_exec_cmd(criu_opts *opts, int argc, char *argv[]);
+int criu_local_add_ext_mount(criu_opts *opts, char *key, char *val);
+int criu_local_add_veth_pair(criu_opts *opts, char *in, char *out);
+int criu_local_add_cg_root(criu_opts *opts, char *ctrl, char *path);
+int criu_local_add_enable_fs(criu_opts *opts, char *fs);
+int criu_local_add_skip_mnt(criu_opts *opts, char *mnt);
+void criu_local_set_ghost_limit(criu_opts *opts, unsigned int limit);
+int criu_local_add_irmap_path(criu_opts *opts, char *path);
+
+void criu_local_set_notify_cb(criu_opts *opts, int (*cb)(char *action, criu_notify_arg_t na));
+
+int criu_local_check(criu_opts *opts);
+int criu_local_dump(criu_opts *opts);
+int criu_local_restore(criu_opts *opts);
+int criu_local_restore_child(criu_opts *opts);
+int criu_local_dump_iters(criu_opts *opts, int (*more)(criu_predump_info pi));
+
+#ifdef __GNUG__
+}
+#endif
+
+#endif /* __CRIU_LIB_H__ */
diff --git a/lib/c/criu.pc.in b/lib/c/criu.pc.in
new file mode 100644
index 000000000000..33986d10d316
--- /dev/null
+++ b/lib/c/criu.pc.in
@@ -0,0 +1,8 @@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: CRIU
+Description: RPC library for userspace checkpoint and restore
+Version: @version@
+Libs: -L${libdir} -lcriu
+Cflags: -I${includedir}
diff --git a/lib/criu.c b/lib/criu.c
deleted file mode 100644
index 52d1b61d6371..000000000000
--- a/lib/criu.c
+++ /dev/null
@@ -1,1239 +0,0 @@
-#include "version.h"
-#include <sys/prctl.h>
-#include <sys/wait.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <sys/un.h>
-#include <limits.h>
-#include <stdio.h>
-#include <unistd.h>
-#include <stdlib.h>
-#include <errno.h>
-#include <signal.h>
-
-#include "criu.h"
-#include "rpc.pb-c.h"
-#include "cr-service-const.h"
-
-#define CR_DEFAULT_SERVICE_BIN "criu"
-
-const char *criu_lib_version = CRIU_VERSION;
-
-struct criu_opts {
- CriuOpts *rpc;
- int (*notify)(char *action, criu_notify_arg_t na);
- enum criu_service_comm service_comm;
- union {
- char *service_address;
- int service_fd;
- char *service_binary;
- };
- int swrk_pid;
-};
-
-static criu_opts *global_opts;
-static int saved_errno;
-
-void criu_local_set_service_comm(criu_opts *opts, enum criu_service_comm comm)
-{
- opts->service_comm = comm;
-}
-
-void criu_set_service_comm(enum criu_service_comm comm)
-{
- criu_local_set_service_comm(global_opts, comm);
-}
-
-void criu_local_set_service_address(criu_opts *opts, char *path)
-{
- if (path)
- opts->service_address = path;
- else
- opts->service_address = CR_DEFAULT_SERVICE_ADDRESS;
-}
-
-void criu_set_service_address(char *path)
-{
- criu_local_set_service_address(global_opts, path);
-}
-
-void criu_local_set_service_fd(criu_opts *opts, int fd)
-{
- opts->service_fd = fd;
-}
-
-void criu_set_service_fd(int fd)
-{
- criu_local_set_service_fd(global_opts, fd);
-}
-
-void criu_local_set_service_binary(criu_opts *opts, char *path)
-{
- if (path)
- opts->service_binary = path;
- else
- opts->service_binary = CR_DEFAULT_SERVICE_BIN;
-}
-
-void criu_set_service_binary(char *path)
-{
- criu_local_set_service_binary(global_opts, path);
-}
-
-int criu_local_init_opts(criu_opts **o)
-{
- criu_opts *opts = NULL;
- CriuOpts *rpc = NULL;
-
- opts = *o;
-
- if (opts) {
- if (opts->rpc)
- criu_opts__free_unpacked(opts->rpc, NULL);
-
- free(opts);
- opts = NULL;
- }
-
- rpc = malloc(sizeof(CriuOpts));
- if (rpc == NULL) {
- perror("Can't allocate memory for criu RPC opts");
- return -1;
- }
-
- criu_opts__init(rpc);
-
- opts = malloc(sizeof(criu_opts));
- if (opts == NULL) {
- perror("Can't allocate memory for criu opts");
- criu_opts__free_unpacked(rpc, NULL);
- return -1;
- }
-
- opts->rpc = rpc;
- opts->notify = NULL;
-
- opts->service_comm = CRIU_COMM_BIN;
- opts->service_address = CR_DEFAULT_SERVICE_BIN;
-
- *o = opts;
-
- return 0;
-}
-
-int criu_init_opts(void)
-{
- return criu_local_init_opts(&global_opts);
-}
-
-void criu_local_set_notify_cb(criu_opts *opts, int (*cb)(char *action, criu_notify_arg_t na))
-{
- opts->notify = cb;
- opts->rpc->has_notify_scripts = true;
- opts->rpc->notify_scripts = true;
-}
-
-void criu_set_notify_cb(int (*cb)(char *action, criu_notify_arg_t na))
-{
- criu_local_set_notify_cb(global_opts, cb);
-}
-
-int criu_notify_pid(criu_notify_arg_t na)
-{
- return na->has_pid ? na->pid : 0;
-}
-
-void criu_local_set_pid(criu_opts *opts, int pid)
-{
- opts->rpc->has_pid = true;
- opts->rpc->pid = pid;
-}
-
-void criu_set_pid(int pid)
-{
- criu_local_set_pid(global_opts, pid);
-}
-
-void criu_local_set_images_dir_fd(criu_opts *opts, int fd)
-{
- opts->rpc->images_dir_fd = fd;
-}
-
-void criu_set_images_dir_fd(int fd)
-{
- criu_local_set_images_dir_fd(global_opts, fd);
-}
-
-void criu_local_set_parent_images(criu_opts *opts, char *path)
-{
- opts->rpc->parent_img = strdup(path);
-}
-
-void criu_set_parent_images(char *path)
-{
- criu_local_set_parent_images(global_opts, path);
-}
-
-void criu_local_set_track_mem(criu_opts *opts, bool track_mem)
-{
- opts->rpc->has_track_mem = true;
- opts->rpc->track_mem = track_mem;
-}
-
-void criu_set_track_mem(bool track_mem)
-{
- criu_local_set_track_mem(global_opts, track_mem);
-}
-
-void criu_local_set_auto_dedup(criu_opts *opts, bool auto_dedup)
-{
- opts->rpc->has_auto_dedup = true;
- opts->rpc->auto_dedup = auto_dedup;
-}
-
-void criu_set_auto_dedup(bool auto_dedup)
-{
- criu_local_set_auto_dedup(global_opts, auto_dedup);
-}
-
-void criu_local_set_force_irmap(criu_opts *opts, bool force_irmap)
-{
- opts->rpc->has_force_irmap = true;
- opts->rpc->force_irmap = force_irmap;
-}
-
-void criu_set_force_irmap(bool force_irmap)
-{
- criu_local_set_force_irmap(global_opts, force_irmap);
-}
-
-void criu_local_set_link_remap(criu_opts *opts, bool link_remap)
-{
- opts->rpc->has_link_remap = true;
- opts->rpc->link_remap = link_remap;
-}
-
-void criu_set_link_remap(bool link_remap)
-{
- criu_local_set_link_remap(global_opts, link_remap);
-}
-
-void criu_local_set_work_dir_fd(criu_opts *opts, int fd)
-{
- opts->rpc->has_work_dir_fd = true;
- opts->rpc->work_dir_fd = fd;
-}
-
-void criu_set_work_dir_fd(int fd)
-{
- criu_local_set_work_dir_fd(global_opts, fd);
-}
-
-void criu_local_set_leave_running(criu_opts *opts, bool leave_running)
-{
- opts->rpc->has_leave_running = true;
- opts->rpc->leave_running = leave_running;
-}
-
-void criu_set_leave_running(bool leave_running)
-{
- criu_local_set_leave_running(global_opts, leave_running);
-}
-
-void criu_local_set_ext_unix_sk(criu_opts *opts, bool ext_unix_sk)
-{
- opts->rpc->has_ext_unix_sk = true;
- opts->rpc->ext_unix_sk = ext_unix_sk;
-}
-
-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;
- opts->rpc->tcp_established = tcp_established;
-}
-
-void criu_set_tcp_established(bool tcp_established)
-{
- criu_local_set_tcp_established(global_opts, tcp_established);
-}
-
-void criu_local_set_evasive_devices(criu_opts *opts, bool evasive_devices)
-{
- opts->rpc->has_evasive_devices = true;
- opts->rpc->evasive_devices = evasive_devices;
-}
-
-void criu_set_evasive_devices(bool evasive_devices)
-{
- criu_local_set_evasive_devices(global_opts, evasive_devices);
-}
-
-void criu_local_set_shell_job(criu_opts *opts, bool shell_job)
-{
- opts->rpc->has_shell_job = true;
- opts->rpc->shell_job = shell_job;
-}
-
-void criu_set_shell_job(bool shell_job)
-{
- criu_local_set_shell_job(global_opts, shell_job);
-}
-
-void criu_local_set_file_locks(criu_opts *opts, bool file_locks)
-{
- opts->rpc->has_file_locks = true;
- opts->rpc->file_locks = file_locks;
-}
-
-void criu_set_file_locks(bool file_locks)
-{
- criu_local_set_file_locks(global_opts, file_locks);
-}
-
-void criu_local_set_log_level(criu_opts *opts, int log_level)
-{
- opts->rpc->has_log_level = true;
- opts->rpc->log_level = log_level;
-}
-
-void criu_set_log_level(int log_level)
-{
- criu_local_set_log_level(global_opts, log_level);
-}
-
-void criu_local_set_root(criu_opts *opts, char *root)
-{
- opts->rpc->root = strdup(root);
-}
-
-void criu_set_root(char *root)
-{
- criu_local_set_root(global_opts, root);
-}
-
-void criu_local_set_manage_cgroups(criu_opts *opts, bool manage)
-{
- opts->rpc->has_manage_cgroups = true;
- opts->rpc->manage_cgroups = manage;
-}
-
-void criu_set_manage_cgroups(bool manage)
-{
- criu_local_set_manage_cgroups(global_opts, manage);
-}
-
-void criu_local_set_manage_cgroups_mode(criu_opts *opts, enum criu_cg_mode mode)
-{
- opts->rpc->has_manage_cgroups_mode = true;
- opts->rpc->manage_cgroups_mode = (CriuCgMode)mode;
-}
-
-void criu_set_manage_cgroups_mode(enum criu_cg_mode mode)
-{
- criu_local_set_manage_cgroups_mode(global_opts, mode);
-}
-
-void criu_local_set_auto_ext_mnt(criu_opts *opts, bool val)
-{
- opts->rpc->has_auto_ext_mnt = true;
- opts->rpc->auto_ext_mnt = val;
-}
-
-void criu_set_auto_ext_mnt(bool val)
-{
- criu_local_set_auto_ext_mnt(global_opts, val);
-}
-
-void criu_local_set_ext_sharing(criu_opts *opts, bool val)
-{
- opts->rpc->has_ext_sharing = true;
- opts->rpc->ext_sharing = val;
-}
-
-void criu_set_ext_sharing(bool val)
-{
- criu_local_set_ext_sharing(global_opts, val);
-}
-
-void criu_local_set_ext_masters(criu_opts *opts, bool val)
-{
- opts->rpc->has_ext_masters = true;
- opts->rpc->ext_masters = val;
-}
-
-void criu_set_ext_masters(bool val)
-{
- criu_local_set_ext_masters(global_opts, val);
-}
-
-void criu_local_set_log_file(criu_opts *opts, char *log_file)
-{
- opts->rpc->log_file = strdup(log_file);
-}
-
-void criu_set_log_file(char *log_file)
-{
- criu_local_set_log_file(global_opts, log_file);
-}
-
-void criu_local_set_cpu_cap(criu_opts *opts, unsigned int cap)
-{
- opts->rpc->has_cpu_cap = true;
- opts->rpc->cpu_cap = cap;
-}
-
-void criu_set_cpu_cap(unsigned int cap)
-{
- criu_local_set_cpu_cap(global_opts, cap);
-}
-
-int criu_local_set_exec_cmd(criu_opts *opts, int argc, char *argv[])
-{
- int i;
-
- opts->rpc->n_exec_cmd = argc;
- opts->rpc->exec_cmd = malloc((argc) * sizeof(char *));
-
- if (opts->rpc->exec_cmd) {
- for (i = 0; i < argc; i++) {
- opts->rpc->exec_cmd[i] = strdup(argv[i]);
- if (!opts->rpc->exec_cmd[i]) {
- while (i > 0)
- free(opts->rpc->exec_cmd[i--]);
- free(opts->rpc->exec_cmd);
- opts->rpc->n_exec_cmd = 0;
- opts->rpc->exec_cmd = NULL;
- goto out;
- }
- }
- return 0;
- }
-
-out:
- return -ENOMEM;
-}
-
-int criu_set_exec_cmd(int argc, char *argv[])
-{
- return criu_local_set_exec_cmd(global_opts, argc, argv);
-}
-
-int criu_local_add_ext_mount(criu_opts *opts, char *key, char *val)
-{
- int nr;
- ExtMountMap **a, *m;
-
- m = malloc(sizeof(*m));
- if (!m)
- goto er;
- ext_mount_map__init(m);
-
- m->key = strdup(key);
- if (!m->key)
- goto er_n;
- m->val = strdup(val);
- if (!m->val)
- goto er_k;
-
- nr = opts->rpc->n_ext_mnt + 1;
- a = realloc(opts->rpc->ext_mnt, nr * sizeof(m));
- if (!a)
- goto er_v;
-
- a[nr - 1] = m;
- opts->rpc->ext_mnt = a;
- opts->rpc->n_ext_mnt = nr;
- return 0;
-
-er_v:
- free(m->val);
-er_k:
- free(m->key);
-er_n:
- free(m);
-er:
- return -ENOMEM;
-}
-
-int criu_add_ext_mount(char *key, char *val)
-{
- return criu_local_add_ext_mount(global_opts, key, val);
-}
-
-int criu_local_add_cg_root(criu_opts *opts, char *ctrl, char *path)
-{
- int nr;
- CgroupRoot **a, *root;
-
- root = malloc(sizeof(*root));
- if (!root)
- goto er;
- cgroup_root__init(root);
-
- if (ctrl) {
- root->ctrl = strdup(ctrl);
- if (!root->ctrl)
- goto er_r;
- }
-
- root->path = strdup(path);
- if (!root->path)
- goto er_c;
-
- nr = opts->rpc->n_cg_root + 1;
- a = realloc(opts->rpc->cg_root, nr * sizeof(root));
- if (!a)
- goto er_p;
-
- a[nr - 1] = root;
- opts->rpc->cg_root = a;
- opts->rpc->n_cg_root = nr;
- return 0;
-
-er_p:
- free(root->path);
-er_c:
- if (root->ctrl)
- free(root->ctrl);
-er_r:
- free(root);
-er:
- return -ENOMEM;
-}
-
-int criu_add_cg_root(char *ctrl, char *path)
-{
- return criu_local_add_cg_root(global_opts, ctrl, path);
-}
-
-int criu_local_add_veth_pair(criu_opts *opts, char *in, char *out)
-{
- int nr;
- CriuVethPair **a, *p;
-
- p = malloc(sizeof(*p));
- if (!p)
- goto er;
- criu_veth_pair__init(p);
-
- p->if_in = strdup(in);
- if (!p->if_in)
- goto er_p;
- p->if_out = strdup(out);
- if (!p->if_out)
- goto er_i;
-
- nr = opts->rpc->n_veths + 1;
- a = realloc(opts->rpc->veths, nr * sizeof(p));
- if (!a)
- goto er_o;
-
- a[nr - 1] = p;
- opts->rpc->veths = a;
- opts->rpc->n_veths = nr;
- return 0;
-
-er_o:
- free(p->if_out);
-er_i:
- free(p->if_in);
-er_p:
- free(p);
-er:
- return -ENOMEM;
-}
-
-int criu_add_veth_pair(char *in, char *out)
-{
- return criu_local_add_veth_pair(global_opts, in, out);
-}
-
-int criu_local_add_enable_fs(criu_opts *opts, char *fs)
-{
- int nr;
- char *str = NULL;
- char **ptr = NULL;
-
- str = strdup(fs);
- if (!str)
- goto err;
-
- nr = opts->rpc->n_enable_fs + 1;
- ptr = realloc(opts->rpc->enable_fs, nr * sizeof(*ptr));
- if (!ptr)
- goto err;
-
- ptr[nr - 1] = str;
-
- opts->rpc->n_enable_fs = nr;
- opts->rpc->enable_fs = ptr;
-
- return 0;
-
-err:
- if (str)
- free(str);
- if (ptr)
- free(ptr);
-
- return -ENOMEM;
-}
-
-int criu_add_enable_fs(char *fs)
-{
- return criu_local_add_enable_fs(global_opts, fs);
-}
-
-
-int criu_local_add_skip_mnt(criu_opts *opts, char *mnt)
-{
- int nr;
- char *str = NULL;
- char **ptr = NULL;
-
- str = strdup(mnt);
- if (!str)
- goto err;
-
- nr = opts->rpc->n_skip_mnt + 1;
- ptr = realloc(opts->rpc->skip_mnt, nr * sizeof(*ptr));
- if (!ptr)
- goto err;
-
- ptr[nr - 1] = str;
-
- opts->rpc->n_skip_mnt = nr;
- opts->rpc->skip_mnt = ptr;
-
- return 0;
-
-err:
- if (str)
- free(str);
- if (ptr)
- free(ptr);
-
- return -ENOMEM;
-}
-
-int criu_local_add_irmap_path(criu_opts *opts, char *path)
-{
- int nr;
- char *my_path;
- char **m;
-
- if (!opts)
- return -1;
-
- my_path = strdup(path);
- if (!my_path)
- goto err;
-
- nr = opts->rpc->n_irmap_scan_paths + 1;
- m = realloc(opts->rpc->irmap_scan_paths, nr * sizeof(*m));
- if (!m)
- goto err;
-
- m[nr - 1] = my_path;
-
- opts->rpc->n_irmap_scan_paths = nr;
- opts->rpc->irmap_scan_paths = m;
-
- return 0;
-
-err:
- if (my_path)
- free(my_path);
-
- return -ENOMEM;
-}
-
-int criu_add_skip_mnt(char *mnt)
-{
- return criu_local_add_skip_mnt(global_opts, mnt);
-}
-
-void criu_local_set_ghost_limit(criu_opts *opts, unsigned int limit)
-{
- opts->rpc->has_ghost_limit = true;
- opts->rpc->ghost_limit = limit;
-}
-
-void criu_set_ghost_limit(unsigned int limit)
-{
- criu_local_set_ghost_limit(global_opts, limit);
-}
-
-int criu_add_irmap_path(char *path)
-{
- return criu_local_add_irmap_path(global_opts, path);
-}
-
-static CriuResp *recv_resp(int socket_fd)
-{
- unsigned char *buf = NULL;
- int len;
- CriuResp *msg = 0;
-
- len = recv(socket_fd, NULL, 0, MSG_TRUNC | MSG_PEEK);
- if (len == -1) {
- perror("Can't read request");
- goto err;
- }
-
- buf = malloc(len);
- if (!buf) {
- errno = ENOMEM;
- perror("Can't receive response");
- goto err;
- }
-
- len = recv(socket_fd, buf, len, MSG_TRUNC);
- if (len == -1) {
- perror("Can't read request");
- goto err;
- }
-
- msg = criu_resp__unpack(NULL, len, buf);
- if (!msg) {
- perror("Failed unpacking response");
- goto err;
- }
-
- free(buf);
- return msg;
-err:
- free(buf);
- saved_errno = errno;
- return NULL;
-}
-
-static int send_req(int socket_fd, CriuReq *req)
-{
- unsigned char *buf;
- int len;
-
- len = criu_req__get_packed_size(req);
-
- buf = malloc(len);
- if (!buf) {
- errno = ENOMEM;
- perror("Can't send request");
- goto err;
- }
-
- if (criu_req__pack(req, buf) != len) {
- perror("Failed packing request");
- goto err;
- }
-
- if (write(socket_fd, buf, len) == -1) {
- perror("Can't send request");
- goto err;
- }
-
- free(buf);
- return 0;
-err:
- free(buf);
- saved_errno = errno;
- return -1;
-}
-
-static int send_notify_ack(int socket_fd, int ret)
-{
- int send_ret;
- CriuReq req = CRIU_REQ__INIT;
-
- req.type = CRIU_REQ_TYPE__NOTIFY;
- req.has_notify_success = true;
- req.notify_success = (ret == 0);
-
- send_ret = send_req(socket_fd, &req);
-
- /*
- * If we're failing the notification then report
- * back the original error code (and it will be
- * propagated back to user).
- *
- * If the notification was OK, then report the
- * result of acking it.
- */
-
- return ret ? : send_ret;
-}
-
-static void swrk_wait(criu_opts *opts)
-{
- if (opts->service_comm == CRIU_COMM_BIN)
- waitpid(opts->swrk_pid, NULL, 0);
-}
-
-static int swrk_connect(criu_opts *opts, bool d)
-{
- int sks[2], pid, ret = -1;
-
- if (socketpair(PF_LOCAL, SOCK_SEQPACKET, 0, sks))
- goto out;
-
- pid = fork();
- if (pid < 0)
- goto err;
-
- if (pid == 0) {
- sigset_t mask;
- char fds[11];
-
- /*
- * Unblock SIGCHLD.
- *
- * The caller of this function is supposed to have
- * this signal blocked. Otherwise it risks to get
- * into situation, when this routine is not yet
- * returned, but the restore subtree exits and
- * emits the SIGCHLD.
- *
- * In turn, unblocked SIGCHLD is required to make
- * criu restoration process work -- it catches
- * subtasks restore errors in this handler.
- */
-
- sigemptyset(&mask);
- sigaddset(&mask, SIGCHLD);
- sigprocmask(SIG_UNBLOCK, &mask, NULL);
-
- close(sks[0]);
- sprintf(fds, "%d", sks[1]);
-
- if (d)
- if (daemon(0, 1)) {
- perror("Can't detach for a self-dump");
- goto child_err;
- }
-
- pid = getpid();
- if (write(sks[1], &pid, sizeof(pid)) != sizeof(pid)) {
- perror("Can't write swrk pid");
- goto child_err;
- }
-
- execlp(opts->service_binary, opts->service_binary, "swrk", fds, NULL);
- perror("Can't exec criu swrk");
-child_err:
- close(sks[1]);
- exit(1);
- }
-
- close(sks[1]);
-
- if (read(sks[0], &pid, sizeof(pid)) != sizeof(pid)) {
- perror("Can't read swrk pid");
- goto err;
- }
-
- opts->swrk_pid = pid;
- ret = sks[0];
-
-out:
- return ret;
-
-err:
- close(sks[0]);
- close(sks[1]);
- goto out;
-}
-
-static int criu_connect(criu_opts *opts, bool d)
-{
- int fd, ret;
- struct sockaddr_un addr;
- socklen_t addr_len;
-
- if (opts->service_comm == CRIU_COMM_FD)
- return opts->service_fd;
- else if (opts->service_comm == CRIU_COMM_BIN)
- return swrk_connect(opts, d);
-
- fd = socket(AF_LOCAL, SOCK_SEQPACKET, 0);
- if (fd < 0) {
- saved_errno = errno;
- perror("Can't create socket");
- return -1;
- }
-
- memset(&addr, 0, sizeof(addr));
- addr.sun_family = AF_LOCAL;
-
- strncpy(addr.sun_path, opts->service_address, sizeof(addr.sun_path));
-
- addr_len = strlen(opts->service_address) + sizeof(addr.sun_family);
-
- ret = connect(fd, (struct sockaddr *) &addr, addr_len);
- if (ret < 0) {
- saved_errno = errno;
- perror("Can't connect to socket");
- close(fd);
- return -1;
- }
-
- return fd;
-}
-
-static int send_req_and_recv_resp_sk(int fd, criu_opts *opts, CriuReq *req, CriuResp **resp)
-{
- int ret = 0;
-
- if (send_req(fd, req) < 0) {
- ret = -ECOMM;
- goto exit;
- }
-
-again:
- *resp = recv_resp(fd);
- if (!*resp) {
- perror("Can't receive response");
- ret = -ECOMM;
- goto exit;
- }
-
- if ((*resp)->type == CRIU_REQ_TYPE__NOTIFY) {
- if (opts->notify)
- ret = opts->notify((*resp)->notify->script, (*resp)->notify);
-
- ret = send_notify_ack(fd, ret);
- if (!ret)
- goto again;
- else
- goto exit;
- }
-
- if ((*resp)->type != req->type) {
- if ((*resp)->type == CRIU_REQ_TYPE__EMPTY &&
- (*resp)->success == false)
- ret = -EINVAL;
- else {
- perror("Unexpected response type");
- ret = -EBADMSG;
- }
- }
-
- if ((*resp)->has_cr_errno)
- saved_errno = (*resp)->cr_errno;
-
-exit:
- return ret;
-}
-
-static int send_req_and_recv_resp(criu_opts *opts, CriuReq *req, CriuResp **resp)
-{
- int fd;
- int ret = 0;
- bool d = false;
-
- if (req->type == CRIU_REQ_TYPE__DUMP && req->opts->has_pid == false)
- d = true;
-
- fd = criu_connect(opts, d);
- if (fd < 0) {
- perror("Can't connect to criu");
- ret = -ECONNREFUSED;
- } else {
- ret = send_req_and_recv_resp_sk(fd, opts, req, resp);
- close(fd);
- }
-
- return ret;
-}
-
-int criu_local_check(criu_opts *opts)
-{
- int ret = -1;
- CriuReq req = CRIU_REQ__INIT;
- CriuResp *resp = NULL;
-
- saved_errno = 0;
-
- req.type = CRIU_REQ_TYPE__CHECK;
-
- ret = send_req_and_recv_resp(opts, &req, &resp);
- if (ret)
- goto exit;
-
- ret = resp->success ? 0 : -EBADE;
-
-exit:
- if (resp)
- criu_resp__free_unpacked(resp, NULL);
-
- swrk_wait(opts);
-
- errno = saved_errno;
-
- return ret;
-}
-
-int criu_check(void)
-{
- return criu_local_check(global_opts);
-}
-
-int criu_local_dump(criu_opts *opts)
-{
- int ret = -1;
- CriuReq req = CRIU_REQ__INIT;
- CriuResp *resp = NULL;
-
- saved_errno = 0;
-
- req.type = CRIU_REQ_TYPE__DUMP;
- req.opts = opts->rpc;
-
- ret = send_req_and_recv_resp(opts, &req, &resp);
- if (ret)
- goto exit;
-
- if (resp->success) {
- if (resp->dump->has_restored && resp->dump->restored)
- ret = 1;
- else
- ret = 0;
- } else
- ret = -EBADE;
-
-exit:
- if (resp)
- criu_resp__free_unpacked(resp, NULL);
-
- swrk_wait(opts);
-
- errno = saved_errno;
-
- return ret;
-}
-
-int criu_dump(void)
-{
- return criu_local_dump(global_opts);
-}
-
-int criu_local_dump_iters(criu_opts *opts, int (*more)(criu_predump_info pi))
-{
- int ret = -1, fd = -1, uret;
- CriuReq req = CRIU_REQ__INIT;
- CriuResp *resp = NULL;
-
- saved_errno = 0;
-
- req.type = CRIU_REQ_TYPE__PRE_DUMP;
- req.opts = opts->rpc;
-
- ret = -EINVAL;
- /*
- * Self-dump in iterable manner is tricky and
- * not supported for the moment.
- *
- * Calls w/o iteration callback is, well, not
- * allowed either.
- */
- if (!opts->rpc->has_pid || !more)
- goto exit;
-
- ret = -ECONNREFUSED;
- fd = criu_connect(opts, false);
- if (fd < 0)
- goto exit;
-
- while (1) {
- ret = send_req_and_recv_resp_sk(fd, opts, &req, &resp);
- if (ret)
- goto exit;
-
- if (!resp->success) {
- ret = -EBADE;
- goto exit;
- }
-
- uret = more(NULL);
- if (uret < 0) {
- ret = uret;
- goto exit;
- }
-
- criu_resp__free_unpacked(resp, NULL);
-
- if (uret == 0)
- break;
- }
-
- req.type = CRIU_REQ_TYPE__DUMP;
- ret = send_req_and_recv_resp_sk(fd, opts, &req, &resp);
- if (!ret)
- ret = (resp->success ? 0 : -EBADE);
-exit:
- if (fd >= 0)
- close(fd);
- if (resp)
- criu_resp__free_unpacked(resp, NULL);
-
- swrk_wait(opts);
-
- errno = saved_errno;
-
- return ret;
-}
-
-int criu_dump_iters(int (*more)(criu_predump_info pi))
-{
- return criu_local_dump_iters((void *)global_opts, more);
-}
-
-int criu_local_restore(criu_opts *opts)
-{
- int ret = -1;
- CriuReq req = CRIU_REQ__INIT;
- CriuResp *resp = NULL;
-
- saved_errno = 0;
-
- req.type = CRIU_REQ_TYPE__RESTORE;
- req.opts = opts->rpc;
-
- ret = send_req_and_recv_resp(opts, &req, &resp);
- if (ret)
- goto exit;
-
- if (resp->success)
- ret = resp->restore->pid;
- else
- ret = -EBADE;
-
-exit:
- if (resp)
- criu_resp__free_unpacked(resp, NULL);
-
- swrk_wait(opts);
-
- errno = saved_errno;
-
- return ret;
-}
-
-int criu_restore(void)
-{
- return criu_local_restore(global_opts);
-}
-
-int criu_local_restore_child(criu_opts *opts)
-{
- int sk, ret = -1;
- enum criu_service_comm saved_comm;
- char *saved_comm_data;
- bool save_comm;
- CriuReq req = CRIU_REQ__INIT;
- CriuResp *resp = NULL;
-
- /*
- * restore_child is not possible with criu running as a system
- * service, so we need to switch comm method to CRIU_COMM_BIN.
- * We're doing so because of the backward compatibility, and we
- * should probably consider requiring CRIU_COMM_BIN to be set by
- * user at some point.
- */
- save_comm = (opts->service_comm != CRIU_COMM_BIN);
- if (save_comm) {
- /* Save comm */
- saved_comm = opts->service_comm;
- saved_comm_data = opts->service_address;
-
- opts->service_comm = CRIU_COMM_BIN;
- opts->service_binary = CR_DEFAULT_SERVICE_BIN;
- }
-
- sk = swrk_connect(opts, false);
- if (save_comm) {
- /* Restore comm */
- opts->service_comm = saved_comm;
- opts->service_address = saved_comm_data;
- }
-
- if (sk < 0)
- return -1;
-
- saved_errno = 0;
-
- req.type = CRIU_REQ_TYPE__RESTORE;
- req.opts = opts->rpc;
-
- req.opts->has_rst_sibling = true;
- req.opts->rst_sibling = true;
-
- ret = send_req_and_recv_resp_sk(sk, opts, &req, &resp);
-
- swrk_wait(opts);
-
- if (!ret) {
- ret = resp->success ? resp->restore->pid : -EBADE;
- criu_resp__free_unpacked(resp, NULL);
- }
-
- close(sk);
- errno = saved_errno;
- return ret;
-}
-
-int criu_restore_child(void)
-{
- return criu_local_restore_child(global_opts);
-}
diff --git a/lib/criu.h b/lib/criu.h
deleted file mode 100644
index 0898be024f63..000000000000
--- a/lib/criu.h
+++ /dev/null
@@ -1,207 +0,0 @@
-/*
- * (C) Copyright 2013 Parallels, Inc. (www.parallels.com).
- *
- * All rights reserved. This program and the accompanying materials
- * are made available under the terms of the GNU Lesser General Public License
- * (LGPL) version 2.1 which accompanies this distribution, and is available at
- * http://www.gnu.org/licenses/lgpl-2.1.html
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, you can find it here:
- * www.gnu.org/licenses/lgpl.html
- */
-
-#ifndef __CRIU_LIB_H__
-#define __CRIU_LIB_H__
-
-#include <stdbool.h>
-
-#ifdef __GNUG__
- extern "C" {
-#endif
-
-enum criu_service_comm {
- CRIU_COMM_SK,
- CRIU_COMM_FD,
- CRIU_COMM_BIN
-};
-
-enum criu_cg_mode {
- CRIU_CG_MODE_IGNORE = 0,
- CRIU_CG_MODE_NONE = 1,
- CRIU_CG_MODE_PROPS = 2,
- CRIU_CG_MODE_SOFT = 3,
- CRIU_CG_MODE_FULL = 4,
- CRIU_CG_MODE_STRICT = 5,
- CRIU_CG_MODE_DEFAULT = 6
-};
-
-void criu_set_service_address(char *path);
-void criu_set_service_fd(int fd);
-void criu_set_service_binary(char *path);
-
-/*
- * You can choose if you want libcriu to connect to service socket
- * by itself, use provided file descriptor or spawn swrk by itself
- */
-void criu_set_service_comm(enum criu_service_comm);
-
-/*
- * Set opts to defaults. _Must_ be called first before using any functions from
- * the list down below. 0 on success, -1 on fail.
- */
-int criu_init_opts(void);
-
-void criu_set_pid(int pid);
-void criu_set_images_dir_fd(int fd); /* must be set for dump/restore */
-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);
-void criu_set_file_locks(bool file_locks);
-void criu_set_track_mem(bool track_mem);
-void criu_set_auto_dedup(bool auto_dedup);
-void criu_set_force_irmap(bool force_irmap);
-void criu_set_link_remap(bool link_remap);
-void criu_set_log_level(int log_level);
-void criu_set_log_file(char *log_file);
-void criu_set_cpu_cap(unsigned int cap);
-void criu_set_root(char *root);
-void criu_set_manage_cgroups(bool manage);
-void criu_set_manage_cgroups_mode(enum criu_cg_mode mode);
-void criu_set_auto_ext_mnt(bool val);
-void criu_set_ext_sharing(bool val);
-void criu_set_ext_masters(bool val);
-int criu_set_exec_cmd(int argc, char *argv[]);
-int criu_add_ext_mount(char *key, char *val);
-int criu_add_veth_pair(char *in, char *out);
-int criu_add_cg_root(char *ctrl, char *path);
-int criu_add_enable_fs(char *fs);
-int criu_add_skip_mnt(char *mnt);
-void criu_set_ghost_limit(unsigned int limit);
-int criu_add_irmap_path(char *path);
-
-/*
- * The criu_notify_arg_t na argument is an opaque
- * value that callbacks (cb-s) should pass into
- * criu_notify_xxx() calls to fetch arbitrary values
- * from notification. If the value is not available
- * some non-existing one is reported.
- */
-
-typedef struct _CriuNotify *criu_notify_arg_t;
-void criu_set_notify_cb(int (*cb)(char *action, criu_notify_arg_t na));
-
-/* Get pid of root task. 0 if not available */
-int criu_notify_pid(criu_notify_arg_t na);
-
-/* Here is a table of return values and errno's of functions
- * from the list down below.
- *
- * Return value errno Description
- * ----------------------------------------------------------------------------
- * 0 undefined Success.
- *
- * >0 undefined Success(criu_restore() only).
- *
- * -BADE rpc err (0 for now) RPC has returned fail.
- *
- * -ECONNREFUSED errno Unable to connect to CRIU.
- *
- * -ECOMM errno Unable to send/recv msg to/from CRIU.
- *
- * -EINVAL undefined CRIU doesn't support this type of request.
- * You should probably update CRIU.
- *
- * -EBADMSG undefined Unexpected response from CRIU.
- * You should probably update CRIU.
- */
-int criu_check(void);
-int criu_dump(void);
-int criu_restore(void);
-int criu_restore_child(void);
-
-/*
- * Perform dumping but with preliminary iterations. Each
- * time an iteration ends the ->more callback is called.
- * The callback's return value is
- * - positive -- one more iteration starts
- * - zero -- final dump is performed and call exits
- * - negative -- dump is aborted, the value is returned
- * back from criu_dump_iters
- *
- * The @pi argument is an opaque value that caller may
- * use to request pre-dump statistics (not yet implemented).
- */
-typedef void *criu_predump_info;
-int criu_dump_iters(int (*more)(criu_predump_info pi));
-
-/*
- * Same as the list above, but lets you have your very own options
- * structure and lets you set individual options in it.
- */
-typedef struct criu_opts criu_opts;
-
-int criu_local_init_opts(criu_opts **opts);
-
-void criu_local_set_service_address(criu_opts *opts, char *path);
-void criu_local_set_service_fd(criu_opts *opts, int fd);
-void criu_local_set_service_comm(criu_opts *opts, enum criu_service_comm);
-
-void criu_local_set_service_fd(criu_opts *opts, int fd);
-
-void criu_local_set_pid(criu_opts *opts, int pid);
-void criu_local_set_images_dir_fd(criu_opts *opts, int fd); /* must be set for dump/restore */
-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);
-void criu_local_set_file_locks(criu_opts *opts, bool file_locks);
-void criu_local_set_track_mem(criu_opts *opts, bool track_mem);
-void criu_local_set_auto_dedup(criu_opts *opts, bool auto_dedup);
-void criu_local_set_force_irmap(criu_opts *opts, bool force_irmap);
-void criu_local_set_link_remap(criu_opts *opts, bool link_remap);
-void criu_local_set_log_level(criu_opts *opts, int log_level);
-void criu_local_set_log_file(criu_opts *opts, char *log_file);
-void criu_local_set_cpu_cap(criu_opts *opts, unsigned int cap);
-void criu_local_set_root(criu_opts *opts, char *root);
-void criu_local_set_manage_cgroups(criu_opts *opts, bool manage);
-void criu_local_set_manage_cgroups_mode(criu_opts *opts, enum criu_cg_mode mode);
-void criu_local_set_auto_ext_mnt(criu_opts *opts, bool val);
-void criu_local_set_ext_sharing(criu_opts *opts, bool val);
-void criu_local_set_ext_masters(criu_opts *opts, bool val);
-int criu_local_set_exec_cmd(criu_opts *opts, int argc, char *argv[]);
-int criu_local_add_ext_mount(criu_opts *opts, char *key, char *val);
-int criu_local_add_veth_pair(criu_opts *opts, char *in, char *out);
-int criu_local_add_cg_root(criu_opts *opts, char *ctrl, char *path);
-int criu_local_add_enable_fs(criu_opts *opts, char *fs);
-int criu_local_add_skip_mnt(criu_opts *opts, char *mnt);
-void criu_local_set_ghost_limit(criu_opts *opts, unsigned int limit);
-int criu_local_add_irmap_path(criu_opts *opts, char *path);
-
-void criu_local_set_notify_cb(criu_opts *opts, int (*cb)(char *action, criu_notify_arg_t na));
-
-int criu_local_check(criu_opts *opts);
-int criu_local_dump(criu_opts *opts);
-int criu_local_restore(criu_opts *opts);
-int criu_local_restore_child(criu_opts *opts);
-int criu_local_dump_iters(criu_opts *opts, int (*more)(criu_predump_info pi));
-
-#ifdef __GNUG__
-}
-#endif
-
-#endif /* __CRIU_LIB_H__ */
diff --git a/lib/criu.pc.in b/lib/criu.pc.in
deleted file mode 100644
index 33986d10d316..000000000000
--- a/lib/criu.pc.in
+++ /dev/null
@@ -1,8 +0,0 @@
-libdir=@libdir@
-includedir=@includedir@
-
-Name: CRIU
-Description: RPC library for userspace checkpoint and restore
-Version: @version@
-Libs: -L${libdir} -lcriu
-Cflags: -I${includedir}
diff --git a/lib/py/.gitignore b/lib/py/.gitignore
new file mode 100644
index 000000000000..8d503da7f031
--- /dev/null
+++ b/lib/py/.gitignore
@@ -0,0 +1,3 @@
+*_pb2.py
+*.pyc
+rpc.py
diff --git a/lib/py/Makefile b/lib/py/Makefile
new file mode 100644
index 000000000000..135fe1c01600
--- /dev/null
+++ b/lib/py/Makefile
@@ -0,0 +1,16 @@
+all: images rpc.py
+
+.PHONY: all images clean
+
+images:
+ $(Q) $(MAKE) -C images all
+
+# rpc_pb2.py doesn't depend on any other file, so
+# it is safe to rename it, dropping ugly _pb2 suffix.
+rpc.py:
+ $(Q) protoc -I=$(SRC_DIR)/protobuf/ --python_out=./ $(SRC_DIR)/protobuf/$(@:.py=.proto)
+ $(Q) mv $(@:.py=_pb2.py) $@
+
+clean:
+ $(Q) $(MAKE) -C images clean
+ $(Q) $(RM) rpc.py *.pyc
diff --git a/lib/py/__init__.py b/lib/py/__init__.py
new file mode 100644
index 000000000000..7de62838cf2f
--- /dev/null
+++ b/lib/py/__init__.py
@@ -0,0 +1,3 @@
+import rpc
+import images
+from criu import *
diff --git a/lib/py/criu.py b/lib/py/criu.py
new file mode 100644
index 000000000000..84dcefed25e1
--- /dev/null
+++ b/lib/py/criu.py
@@ -0,0 +1,282 @@
+# Same as libcriu for C.
+
+import socket
+import errno
+import subprocess
+import fcntl
+import os
+import signal
+import sys
+import struct
+
+import rpc
+
+class _criu_comm:
+ """
+ Base class for communication classes.
+ """
+ COMM_SK = 0
+ COMM_FD = 1
+ COMM_BIN = 2
+ comm_type = None
+ comm = None
+ sk = None
+
+ def connect(self, daemon):
+ """
+ Connect to criu and return socket object.
+ daemon -- is for whether or not criu should daemonize if executing criu from binary(comm_bin).
+ """
+ pass
+
+ def disconnect(self):
+ """
+ Disconnect from criu.
+ """
+ pass
+
+
+class _criu_comm_sk(_criu_comm):
+ """
+ Communication class for unix socket.
+ """
+ def __init__(self, sk_path):
+ self.comm_type = self.COMM_SK
+ self.comm = sk_path
+
+ def connect(self, daemon):
+ self.sk = socket.socket(socket.AF_UNIX, socket.SOCK_SEQPACKET)
+ self.sk.connect(self.comm)
+
+ return self.sk
+
+ def disconnect(self):
+ self.sk.close()
+
+
+class _criu_comm_fd(_criu_comm):
+ """
+ Commnunication class for file descriptor.
+ """
+ def __init__(self, fd):
+ self.comm_type = self.COMM_FD
+ self.comm = fd
+
+ def connect(self, daemon):
+ self.sk = socket.fromfd(self.comm, socket.AF_UNIX, socket.SOCK_SEQPACKET)
+
+ return self.sk
+
+ def disconnect(self):
+ self.sk.close()
+
+class _criu_comm_bin(_criu_comm):
+ """
+ Communication class for binary.
+ """
+ def __init__(self, bin_path):
+ self.comm_type = self.COMM_BIN
+ self.comm = bin_path
+ self.swrk = None
+ self.daemon = None
+
+ def connect(self, daemon):
+ # Kind of the same thing we do in libcriu
+ css = socket.socketpair(socket.AF_UNIX, socket.SOCK_SEQPACKET)
+ flags = fcntl.fcntl(css[1], fcntl.F_GETFD)
+ fcntl.fcntl(css[1], fcntl.F_SETFD, flags | fcntl.FD_CLOEXEC)
+
+ self.daemon = daemon
+
+ p = os.fork()
+
+ if p == 0:
+ def exec_criu():
+ os.close(0)
+ os.close(1)
+ os.close(2)
+
+ css[0].send(struct.pack('i', os.getpid()))
+ os.execv(self.comm, [self.comm, 'swrk', "%d" % css[0].fileno()])
+ os._exit(1)
+
+ if daemon:
+ # Python has no daemon(3) alternative,
+ # so we need to mimic it ourself.
+ p = os.fork()
+
+ if p == 0:
+ os.setsid()
+
+ exec_criu()
+ else:
+ os._exit(0)
+ else:
+ exec_criu()
+
+ css[0].close()
+ self.swrk = struct.unpack('i', css[1].recv(4))[0]
+ self.sk = css[1]
+
+ return self.sk
+
+ def disconnect(self):
+ self.sk.close()
+ if not self.daemon:
+ os.waitpid(self.swrk, 0)
+
+
+class CRIUException(Exception):
+ """
+ Exception class for handling and storing criu errors.
+ """
+ typ = None
+ _str = None
+
+ def __str__(self):
+ return self._str
+
+
+class CRIUExceptionInternal(CRIUException):
+ """
+ Exception class for handling and storing internal errors.
+ """
+ def __init__(self, typ, s):
+ self.typ = typ
+ self._str = "%s failed with internal error: %s" % (rpc.criu_req_type.Name(self.typ), s)
+
+
+class CRIUExceptionExternal(CRIUException):
+ """
+ Exception class for handling and storing criu RPC errors.
+ """
+
+ def __init__(self, req_typ, resp_typ, errno):
+ self.typ = req_typ
+ self.resp_typ = resp_typ
+ self.errno = errno
+ self._str = self._gen_error_str()
+
+ def _gen_error_str(self):
+ s = "%s failed: " % (rpc.criu_req_type.Name(self.typ), )
+
+ if self.typ != self.resp_typ:
+ s += "Unxecpected response type %d: " % (self.resp_typ, )
+
+ s += "Error(%d): " % (self.errno, )
+
+ if self.errno == errno.EBADRQC:
+ s += "Bad options"
+
+ if self.typ == rpc.DUMP:
+ if self.errno == errno.ESRCH:
+ s += "No process with such pid"
+
+ if self.typ == rpc.RESTORE:
+ if self.errno == errno.EEXIST:
+ s += "Process with requested pid already exists"
+
+ s += "Unknown"
+
+ return s
+
+
+class criu:
+ """
+ Call criu through RPC.
+ """
+ opts = None #CRIU options in pb format
+
+ _comm = None #Communication method
+
+ def __init__(self):
+ self.use_binary('criu')
+ self.opts = rpc.criu_opts()
+
+ def use_sk(self, sk_name):
+ """
+ Access criu using unix socket which that belongs to criu service daemon.
+ """
+ self._comm = _criu_comm_sk(sk_name)
+
+ def use_fd(self, fd):
+ """
+ Access criu using provided fd.
+ """
+ self._comm = _criu_comm_fd(fd)
+
+ def use_binary(self, bin_name):
+ """
+ Access criu by execing it using provided path to criu binary.
+ """
+ self._comm = _criu_comm_bin(bin_name)
+
+ def _send_req_and_recv_resp(self, req):
+ """
+ As simple as send request and receive response.
+ """
+ # In case of self-dump we need to spawn criu swrk detached
+ # from our current process, as criu has a hard time separating
+ # process resources from its own if criu is located in a same
+ # process tree it is trying to dump.
+ daemon = False
+ if req.type == rpc.DUMP and not req.opts.HasField('pid'):
+ daemon = True
+
+ try:
+ s = self._comm.connect(daemon)
+
+ s.send(req.SerializeToString())
+
+ buf = s.recv(len(s.recv(1, socket.MSG_TRUNC | socket.MSG_PEEK)))
+
+ self._comm.disconnect()
+
+ resp = rpc.criu_resp()
+ resp.ParseFromString(buf)
+ except Exception as e:
+ raise CRIUExceptionInternal(req.type, str(e))
+
+ return resp
+
+ def check(self):
+ """
+ Checks whether the kernel support is up-to-date.
+ """
+ req = rpc.criu_req()
+ req.type = rpc.CHECK
+
+ resp = self._send_req_and_recv_resp(req)
+
+ if not resp.success:
+ raise CRIUExceptionExternal(req.type, resp.type, resp.cr_errno)
+
+ def dump(self):
+ """
+ Checkpoint a process/tree identified by opts.pid.
+ """
+ req = rpc.criu_req()
+ req.type = rpc.DUMP
+ req.opts.MergeFrom(self.opts)
+
+ resp = self._send_req_and_recv_resp(req)
+
+ if not resp.success:
+ raise CRIUExceptionExternal(req.type, resp.type, resp.cr_errno)
+
+ return resp.dump
+
+ def restore(self):
+ """
+ Restore a process/tree.
+ """
+ req = rpc.criu_req()
+ req.type = rpc.RESTORE
+ req.opts.MergeFrom(self.opts)
+
+ resp = self._send_req_and_recv_resp(req)
+
+ if not resp.success:
+ raise CRIUExceptionExternal(req.type, resp.type, resp.errno)
+
+ return resp.restore
diff --git a/lib/py/images/.gitignore b/lib/py/images/.gitignore
new file mode 100644
index 000000000000..234bfe9f6f33
--- /dev/null
+++ b/lib/py/images/.gitignore
@@ -0,0 +1,4 @@
+*.pyc
+*_pb2.py
+magic.py
+pb.py
diff --git a/lib/py/images/Makefile b/lib/py/images/Makefile
new file mode 100644
index 000000000000..98a450065465
--- /dev/null
+++ b/lib/py/images/Makefile
@@ -0,0 +1,26 @@
+all: pb.py protobuf magic.py
+
+.PHONY: all protobuf clean pb.py
+
+proto := $(filter-out $(SRC_DIR)/protobuf/rpc.proto, $(sort $(wildcard $(SRC_DIR)/protobuf/*.proto)))
+proto-py-modules := $(foreach m,$(proto),$(subst -,_,$(notdir $(m:.proto=_pb2))))
+
+# We don't need rpc_pb2.py here, as it is not related to the
+# images.
+# Unfortunately, we can't drop ugly _pb2 suffixes here, because
+# some _pb2 files depend on others _pb2 files.
+protobuf:
+ $(Q) protoc -I=$(SRC_DIR)/protobuf -I=/usr/include/ --python_out=./ $(proto)
+
+magic.py: $(SRC_DIR)/scripts/magic-gen.py $(SRC_DIR)/include/magic.h
+ $(E) " GEN " $@
+ $(Q) python $^ $@
+
+pb.py: protobuf
+ $(Q) echo "# Autogenerated. Do not edit!" > $@
+ $(Q) for m in $(proto-py-modules); do \
+ echo "from $$m import *" >> $@ ;\
+ done
+
+clean:
+ $(Q) $(RM) ./*_pb2.py ./*.pyc magic.py pb.py
diff --git a/lib/py/images/__init__.py b/lib/py/images/__init__.py
new file mode 100644
index 000000000000..379943b977e1
--- /dev/null
+++ b/lib/py/images/__init__.py
@@ -0,0 +1,3 @@
+from magic import *
+from images import *
+from pb import *
diff --git a/lib/py/images/images.py b/lib/py/images/images.py
new file mode 100644
index 000000000000..d4e883f11006
--- /dev/null
+++ b/lib/py/images/images.py
@@ -0,0 +1,479 @@
+#!/bin/env python
+
+# This file contains methods to deal with criu images.
+#
+# According to http://criu.org/Images, criu images can be described
+# with such IOW:
+#
+# IMAGE_FILE ::= MAGIC { ENTRY }
+# ENTRY ::= SIZE PAYLOAD [ EXTRA ]
+# PAYLOAD ::= "message encoded in ProtocolBuffer format"
+# EXTRA ::= "arbitrary blob, depends on the PAYLOAD contents"
+#
+# MAGIC ::= "32 bit integer"
+# SIZE ::= "32 bit integer, equals the PAYLOAD length"
+#
+# Images v1.1 NOTE: MAGIC now consist of 2 32 bit integers, first one is
+# MAGIC_COMMON or MAGIC_SERVICE and the second one is same as MAGIC
+# in images V1.0. We don't keep "first" magic in json images.
+#
+# In order to convert images to human-readable format, we use dict(json).
+# Using json not only allows us to easily read\write images, but also
+# to use a great variety of tools out there to manipulate them.
+# It also allows us to clearly describe criu images structure.
+#
+# Using dict(json) format, criu images can be described like:
+#
+# {
+# 'magic' : 'FOO',
+# 'entries' : [
+# entry,
+# ...
+# ]
+# }
+#
+# Entry, in its turn, could be described as:
+#
+# {
+# pb_msg,
+# 'extra' : extra_msg
+# }
+#
+import io
+import google
+import struct
+import os
+import sys
+import json
+import pb2dict
+import array
+
+import magic
+from pb import *
+
+#
+# Predefined hardcoded constants
+sizeof_u16 = 2
+sizeof_u32 = 4
+sizeof_u64 = 8
+
+# A helper for rounding
+def round_up(x,y):
+ return (((x - 1) | (y - 1)) + 1)
+
+class MagicException(Exception):
+ def __init__(self, magic):
+ self.magic = magic
+
+# Generic class to handle loading/dumping criu images entries from/to bin
+# format to/from dict(json).
+class entry_handler:
+ """
+ Generic class to handle loading/dumping criu images
+ entries from/to bin format to/from dict(json).
+ """
+ def __init__(self, payload, extra_handler=None):
+ """
+ Sets payload class and extra handler class.
+ """
+ self.payload = payload
+ self.extra_handler = extra_handler
+
+ def load(self, f, pretty = False):
+ """
+ Convert criu image entries from binary format to dict(json).
+ Takes a file-like object and returnes a list with entries in
+ dict(json) format.
+ """
+ entries = []
+
+ while True:
+ entry = {}
+
+ # Read payload
+ pb = self.payload()
+ buf = f.read(4)
+ if buf == '':
+ break
+ size, = struct.unpack('i', buf)
+ pb.ParseFromString(f.read(size))
+ entry = pb2dict.pb2dict(pb, pretty)
+
+ # Read extra
+ if self.extra_handler:
+ entry['extra'] = self.extra_handler.load(f, pb)
+
+ entries.append(entry)
+
+ return entries
+
+ def loads(self, s, pretty = False):
+ """
+ Same as load(), but takes a string as an argument.
+ """
+ f = io.BytesIO(s)
+ return self.load(f, pretty)
+
+ def dump(self, entries, f):
+ """
+ Convert criu image entries from dict(json) format to binary.
+ Takes a list of entries and a file-like object to write entries
+ in binary format to.
+ """
+ for entry in entries:
+ extra = entry.pop('extra', None)
+
+ # Write payload
+ pb = self.payload()
+ pb2dict.dict2pb(entry, pb)
+ pb_str = pb.SerializeToString()
+ size = len(pb_str)
+ f.write(struct.pack('i', size))
+ f.write(pb_str)
+
+ # Write extra
+ if self.extra_handler and extra:
+ self.extra_handler.dump(extra, f, pb)
+
+ def dumps(self, entries):
+ """
+ Same as dump(), but doesn't take file-like object and just
+ returns a string.
+ """
+ f = io.BytesIO('')
+ self.dump(entries, f)
+ return f.read()
+
+ def count(self, f):
+ """
+ Counts the number of top-level object in the image file
+ """
+ entries = 0
+
+ while True:
+ buf = f.read(4)
+ if buf == '':
+ break
+ size, = struct.unpack('i', buf)
+ f.seek(size, 1)
+ entries += 1
+
+ return entries
+
+# Special handler for pagemap.img
+class pagemap_handler:
+ """
+ Special entry handler for pagemap.img, which is unique in a way
+ that it has a header of pagemap_head type followed by entries
+ of pagemap_entry type.
+ """
+ def load(self, f, pretty = False):
+ entries = []
+
+ pb = pagemap_head()
+ while True:
+ buf = f.read(4)
+ if buf == '':
+ break
+ size, = struct.unpack('i', buf)
+ pb.ParseFromString(f.read(size))
+ entries.append(pb2dict.pb2dict(pb, pretty))
+
+ pb = pagemap_entry()
+
+ return entries
+
+ def loads(self, s, pretty = False):
+ f = io.BytesIO(s)
+ return self.load(f, pretty)
+
+ def dump(self, entries, f):
+ pb = pagemap_head()
+ for item in entries:
+ pb2dict.dict2pb(item, pb)
+ pb_str = pb.SerializeToString()
+ size = len(pb_str)
+ f.write(struct.pack('i', size))
+ f.write(pb_str)
+
+ pb = pagemap_entry()
+
+ def dumps(self, entries):
+ f = io.BytesIO('')
+ self.dump(entries, f)
+ return f.read()
+
+ def count(self, f):
+ return entry_handler(None).count(f) - 1
+
+
+# In following extra handlers we use base64 encoding
+# to store binary data. Even though, the nature
+# of base64 is that it increases the total size,
+# it doesn't really matter, because our images
+# do not store big amounts of binary data. They
+# are negligible comparing to pages size.
+class pipes_data_extra_handler:
+ def load(self, f, pload):
+ size = pload.bytes
+ data = f.read(size)
+ return data.encode('base64')
+
+ def dump(self, extra, f, pload):
+ data = extra.decode('base64')
+ f.write(data)
+
+class sk_queues_extra_handler:
+ def load(self, f, pload):
+ size = pload.length
+ data = f.read(size)
+ return data.encode('base64')
+
+ def dump(self, extra, f, pb):
+ data = extra.decode('base64')
+ f.write(data)
+
+class ghost_file_extra_handler:
+ def load(self, f, pb):
+ data = f.read()
+ return data.encode('base64')
+
+ def dump(self, extra, f, pb):
+ data = extra.decode('base64')
+ f.write(data)
+
+class tcp_stream_extra_handler:
+ def load(self, f, pb):
+ d = {}
+
+ inq = f.read(pb.inq_len)
+ outq = f.read(pb.outq_len)
+
+ d['inq'] = inq.encode('base64')
+ d['outq'] = outq.encode('base64')
+
+ return d
+
+ def dump(self, extra, f, pb):
+ inq = extra['inq'].decode('base64')
+ outq = extra['outq'].decode('base64')
+
+ f.write(inq)
+ f.write(outq)
+
+class ipc_sem_set_handler:
+ def load(self, f, pb):
+ entry = pb2dict.pb2dict(pb)
+ size = sizeof_u16 * entry['nsems']
+ rounded = round_up(size, sizeof_u64)
+ s = array.array('H')
+ if s.itemsize != sizeof_u16:
+ raise Exception("Array size mismatch")
+ s.fromstring(f.read(size))
+ f.seek(rounded - size, 1)
+ return s.tolist()
+
+ def dump(self, extra, f, pb):
+ entry = pb2dict.pb2dict(pb)
+ size = sizeof_u16 * entry['nsems']
+ rounded = round_up(size, sizeof_u64)
+ s = array.array('H')
+ if s.itemsize != sizeof_u16:
+ raise Exception("Array size mismatch")
+ s.fromlist(extra)
+ if len(s) != entry['nsems']:
+ raise Exception("Number of semaphores mismatch")
+ f.write(s.tostring())
+ f.write('\0' * (rounded - size))
+
+class ipc_msg_queue_handler:
+ def load(self, f, pb):
+ entry = pb2dict.pb2dict(pb)
+ messages = []
+ for x in range (0, entry['qnum']):
+ buf = f.read(4)
+ if buf == '':
+ break
+ size, = struct.unpack('i', buf)
+ msg = ipc_msg()
+ msg.ParseFromString(f.read(size))
+ rounded = round_up(msg.msize, sizeof_u64)
+ data = f.read(msg.msize)
+ f.seek(rounded - msg.msize, 1)
+ messages.append(pb2dict.pb2dict(msg))
+ messages.append(data.encode('base64'))
+ return messages
+
+ def dump(self, extra, f, pb):
+ entry = pb2dict.pb2dict(pb)
+ for i in range (0, len(extra), 2):
+ msg = ipc_msg()
+ pb2dict.dict2pb(extra[i], msg)
+ msg_str = msg.SerializeToString()
+ size = len(msg_str)
+ f.write(struct.pack('i', size))
+ f.write(msg_str)
+ rounded = round_up(msg.msize, sizeof_u64)
+ data = extra[i + 1].decode('base64')
+ f.write(data[:msg.msize])
+ f.write('\0' * (rounded - msg.msize))
+
+class ipc_shm_handler:
+ def load(self, f, pb):
+ entry = pb2dict.pb2dict(pb)
+ size = entry['size']
+ data = f.read(size)
+ rounded = round_up(size, sizeof_u32)
+ f.seek(rounded - size, 1)
+ return data.encode('base64')
+
+ def dump(self, extra, f, pb):
+ entry = pb2dict.pb2dict(pb)
+ size = entry['size']
+ data = extra.decode('base64')
+ rounded = round_up(size, sizeof_u32)
+ f.write(data[:size])
+ f.write('\0' * (rounded - size))
+
+handlers = {
+ 'INVENTORY' : entry_handler(inventory_entry),
+ 'CORE' : entry_handler(core_entry),
+ 'IDS' : entry_handler(task_kobj_ids_entry),
+ 'CREDS' : entry_handler(creds_entry),
+ 'UTSNS' : entry_handler(utsns_entry),
+ 'IPC_VAR' : entry_handler(ipc_var_entry),
+ 'FS' : entry_handler(fs_entry),
+ 'GHOST_FILE' : entry_handler(ghost_file_entry, ghost_file_extra_handler()),
+ 'MM' : entry_handler(mm_entry),
+ 'CGROUP' : entry_handler(cgroup_entry),
+ 'TCP_STREAM' : entry_handler(tcp_stream_entry, tcp_stream_extra_handler()),
+ 'STATS' : entry_handler(stats_entry),
+ 'PAGEMAP' : pagemap_handler(), # Special one
+ 'PSTREE' : entry_handler(pstree_entry),
+ 'REG_FILES' : entry_handler(reg_file_entry),
+ 'NS_FILES' : entry_handler(ns_file_entry),
+ 'EVENTFD_FILE' : entry_handler(eventfd_file_entry),
+ 'EVENTPOLL_FILE' : entry_handler(eventpoll_file_entry),
+ 'EVENTPOLL_TFD' : entry_handler(eventpoll_tfd_entry),
+ 'SIGNALFD' : entry_handler(signalfd_entry),
+ 'TIMERFD' : entry_handler(timerfd_entry),
+ 'INOTIFY_FILE' : entry_handler(inotify_file_entry),
+ 'INOTIFY_WD' : entry_handler(inotify_wd_entry),
+ 'FANOTIFY_FILE' : entry_handler(fanotify_file_entry),
+ 'FANOTIFY_MARK' : entry_handler(fanotify_mark_entry),
+ 'VMAS' : entry_handler(vma_entry),
+ 'PIPES' : entry_handler(pipe_entry),
+ 'FIFO' : entry_handler(fifo_entry),
+ 'SIGACT' : entry_handler(sa_entry),
+ 'NETLINK_SK' : entry_handler(netlink_sk_entry),
+ 'REMAP_FPATH' : entry_handler(remap_file_path_entry),
+ 'MNTS' : entry_handler(mnt_entry),
+ 'TTY_FILES' : entry_handler(tty_file_entry),
+ 'TTY_INFO' : entry_handler(tty_info_entry),
+ 'RLIMIT' : entry_handler(rlimit_entry),
+ 'TUNFILE' : entry_handler(tunfile_entry),
+ 'EXT_FILES' : entry_handler(ext_file_entry),
+ 'IRMAP_CACHE' : entry_handler(irmap_cache_entry),
+ 'FILE_LOCKS' : entry_handler(file_lock_entry),
+ 'FDINFO' : entry_handler(fdinfo_entry),
+ 'UNIXSK' : entry_handler(unix_sk_entry),
+ 'INETSK' : entry_handler(inet_sk_entry),
+ 'PACKETSK' : entry_handler(packet_sock_entry),
+ 'ITIMERS' : entry_handler(itimer_entry),
+ 'POSIX_TIMERS' : entry_handler(posix_timer_entry),
+ 'NETDEV' : entry_handler(net_device_entry),
+ 'PIPES_DATA' : entry_handler(pipe_data_entry, pipes_data_extra_handler()),
+ 'FIFO_DATA' : entry_handler(pipe_data_entry, pipes_data_extra_handler()),
+ 'SK_QUEUES' : entry_handler(sk_packet_entry, sk_queues_extra_handler()),
+ 'IPCNS_SHM' : entry_handler(ipc_shm_entry, ipc_shm_handler()),
+ 'IPCNS_SEM' : entry_handler(ipc_sem_entry, ipc_sem_set_handler()),
+ 'IPCNS_MSG' : entry_handler(ipc_msg_entry, ipc_msg_queue_handler()),
+ 'NETNS' : entry_handler(netns_entry),
+ 'USERNS' : entry_handler(userns_entry),
+ 'SECCOMP' : entry_handler(seccomp_entry),
+ }
+
+def __rhandler(f):
+ # Images v1.1 NOTE: First read "first" magic.
+ img_magic, = struct.unpack('i', f.read(4))
+ if img_magic in (magic.by_name['IMG_COMMON'], magic.by_name['IMG_SERVICE']):
+ img_magic, = struct.unpack('i', f.read(4))
+
+ try:
+ m = magic.by_val[img_magic]
+ except:
+ raise MagicException(img_magic)
+
+ try:
+ handler = handlers[m]
+ except:
+ raise Exception("No handler found for image with magic " + m)
+
+ return m, handler
+
+def load(f, pretty = False):
+ """
+ Convert criu image from binary format to dict(json).
+ Takes a file-like object to read criu image from.
+ Returns criu image in dict(json) format.
+ """
+ image = {}
+
+ m, handler = __rhandler(f)
+
+ image['magic'] = m
+ image['entries'] = handler.load(f, pretty)
+
+ return image
+
+def info(f):
+ res = {}
+
+ m, handler = __rhandler(f)
+
+ res['magic'] = m
+ res['count'] = handler.count(f)
+
+ return res
+
+def loads(s, pretty = False):
+ """
+ Same as load(), but takes a string.
+ """
+ f = io.BytesIO(s)
+ return load(f, pretty)
+
+def dump(img, f):
+ """
+ Convert criu image from dict(json) format to binary.
+ Takes an image in dict(json) format and file-like
+ object to write to.
+ """
+ m = img['magic']
+ magic_val = magic.by_name[img['magic']]
+
+ # Images v1.1 NOTE: use "second" magic to identify what "first"
+ # should be written.
+ if m != 'INVENTORY':
+ if m in ('STATS', 'IRMAP_CACHE'):
+ f.write(struct.pack('i', magic.by_name['IMG_SERVICE']))
+ else:
+ f.write(struct.pack('i', magic.by_name['IMG_COMMON']))
+
+ f.write(struct.pack('i', magic_val))
+
+ try:
+ handler = handlers[m]
+ except:
+ raise Exception("No handler found for image with such magic")
+
+ handler.dump(img['entries'], f)
+
+def dumps(img):
+ """
+ Same as dump(), but takes only an image and returns
+ a string.
+ """
+ f = io.BytesIO('')
+ dump(img, f)
+ return f.getvalue()
diff --git a/lib/py/images/pb2dict.py b/lib/py/images/pb2dict.py
new file mode 100644
index 000000000000..177cda3154a3
--- /dev/null
+++ b/lib/py/images/pb2dict.py
@@ -0,0 +1,276 @@
+from google.protobuf.descriptor import FieldDescriptor as FD
+import opts_pb2
+import ipaddr
+import socket
+import collections
+import os
+
+# pb2dict and dict2pb are methods to convert pb to/from dict.
+# Inspired by:
+# protobuf-to-dict - https://github.com/benhodgson/protobuf-to-dict
+# protobuf-json - https://code.google.com/p/protobuf-json/
+# protobuf source - https://code.google.com/p/protobuf/
+# Both protobuf-to-dict/json do not fit here because of several reasons,
+# here are some of them:
+# - both have a common bug in treating optional field with empty
+# repeated inside.
+# - protobuf-to-json is not avalible in pip or in any other python
+# repo, so it is hard to distribute and we can't rely on it.
+# - both do not treat enums in a way we would like to. They convert
+# protobuf enum to int, but we need a string here, because it is
+# much more informative. BTW, protobuf text_format converts pb
+# enums to string value too. (i.e. "march : x86_64" is better then
+# "march : 1").
+
+
+_basic_cast = {
+ FD.TYPE_FIXED64 : long,
+ FD.TYPE_FIXED32 : int,
+ FD.TYPE_SFIXED64 : long,
+ FD.TYPE_SFIXED32 : int,
+
+ FD.TYPE_INT64 : long,
+ FD.TYPE_UINT64 : long,
+ FD.TYPE_SINT64 : long,
+
+ FD.TYPE_INT32 : int,
+ FD.TYPE_UINT32 : int,
+ FD.TYPE_SINT32 : int,
+
+ FD.TYPE_BOOL : bool,
+
+ FD.TYPE_STRING : unicode
+}
+
+def _marked_as_hex(field):
+ return field.GetOptions().Extensions[opts_pb2.criu].hex
+
+def _marked_as_ip(field):
+ return field.GetOptions().Extensions[opts_pb2.criu].ipadd
+
+def _marked_as_flags(field):
+ return field.GetOptions().Extensions[opts_pb2.criu].flags
+
+def _marked_as_dev(field):
+ return field.GetOptions().Extensions[opts_pb2.criu].dev
+
+def _marked_as_odev(field):
+ return field.GetOptions().Extensions[opts_pb2.criu].odev
+
+mmap_prot_map = [
+ ('PROT_READ', 0x1),
+ ('PROT_WRITE', 0x2),
+ ('PROT_EXEC', 0x4),
+];
+
+mmap_flags_map = [
+ ('MAP_SHARED', 0x1),
+ ('MAP_PRIVATE', 0x2),
+ ('MAP_ANON', 0x20),
+ ('MAP_GROWSDOWN', 0x0100),
+];
+
+mmap_status_map = [
+ ('VMA_AREA_NONE', 0 << 0),
+ ('VMA_AREA_REGULAR', 1 << 0),
+ ('VMA_AREA_STACK', 1 << 1),
+ ('VMA_AREA_VSYSCALL', 1 << 2),
+ ('VMA_AREA_VDSO', 1 << 3),
+ ('VMA_AREA_HEAP', 1 << 5),
+
+ ('VMA_FILE_PRIVATE', 1 << 6),
+ ('VMA_FILE_SHARED', 1 << 7),
+ ('VMA_ANON_SHARED', 1 << 8),
+ ('VMA_ANON_PRIVATE', 1 << 9),
+
+ ('VMA_AREA_SYSVIPC', 1 << 10),
+ ('VMA_AREA_SOCKET', 1 << 11),
+ ('VMA_AREA_VVAR', 1 << 12),
+ ('VMA_AREA_AIORING', 1 << 13),
+
+ ('VMA_UNSUPP', 1 << 31),
+];
+
+rfile_flags_map = [
+ ('O_WRONLY', 01),
+ ('O_RDWR', 02),
+ ('O_APPEND', 02000),
+ ('O_DIRECT', 040000),
+ ('O_LARGEFILE', 0100000),
+];
+
+flags_maps = {
+ 'mmap.prot' : mmap_prot_map,
+ 'mmap.flags' : mmap_flags_map,
+ 'mmap.status' : mmap_status_map,
+ 'rfile.flags' : rfile_flags_map,
+}
+
+def map_flags(value, flags_map):
+ bs = map(lambda x: x[0], filter(lambda x: value & x[1], flags_map))
+ value &= ~sum(map(lambda x: x[1], flags_map))
+ if value:
+ bs.append("0x%x" % value)
+ return " | ".join(bs)
+
+def unmap_flags(value, flags_map):
+ if value == '':
+ return 0
+
+ bd = dict(flags_map)
+ return sum(map(lambda x: int(str(bd.get(x, x)), 0), map(lambda x: x.strip(), value.split('|'))))
+
+kern_minorbits = 20 # This is how kernel encodes dev_t in new format
+
+def decode_dev(field, value):
+ if _marked_as_odev(field):
+ return "%d:%d" % (os.major(value), os.minor(value))
+ else:
+ return "%d:%d" % (value >> kern_minorbits, value & ((1 << kern_minorbits) - 1))
+
+def encode_dev(field, value):
+ dev = map(lambda x: int(x), value.split(':'))
+ if _marked_as_odev(field):
+ return os.makedev(dev[0], dev[1])
+ else:
+ return dev[0] << kern_minorbits | dev[1]
+
+def is_string(value):
+ return isinstance(value, unicode) or isinstance(value, str)
+
+def _pb2dict_cast(field, value, pretty = False, is_hex = False):
+ if not is_hex:
+ is_hex = _marked_as_hex(field)
+
+ if field.type == FD.TYPE_MESSAGE:
+ return pb2dict(value, pretty, is_hex)
+ elif field.type == FD.TYPE_BYTES:
+ return value.encode('base64')
+ elif field.type == FD.TYPE_ENUM:
+ return field.enum_type.values_by_number.get(value, None).name
+ elif field.type in _basic_cast:
+ cast = _basic_cast[field.type]
+ if pretty and (cast == int or cast == long):
+ if is_hex:
+ # Fields that have (criu).hex = true option set
+ # should be stored in hex string format.
+ return "0x%x" % value
+
+ if _marked_as_dev(field):
+ return decode_dev(field, value)
+
+ flags = _marked_as_flags(field)
+ if flags:
+ try:
+ flags_map = flags_maps[flags]
+ except:
+ return "0x%x" % value # flags are better seen as hex anyway
+ else:
+ return map_flags(value, flags_map)
+
+ return cast(value)
+ else:
+ raise Exception("Field(%s) has unsupported type %d" % (field.name, field.type))
+
+def pb2dict(pb, pretty = False, is_hex = False):
+ """
+ Convert protobuf msg to dictionary.
+ Takes a protobuf message and returns a dict.
+ """
+ d = collections.OrderedDict() if pretty else {}
+ for field, value in pb.ListFields():
+ if field.label == FD.LABEL_REPEATED:
+ d_val = []
+ if pretty and _marked_as_ip(field):
+ if len(value) == 1:
+ v = socket.ntohl(value[0])
+ addr = ipaddr.IPv4Address(v)
+ else:
+ v = 0 + (socket.ntohl(value[0]) << (32 * 3)) + \
+ (socket.ntohl(value[1]) << (32 * 2)) + \
+ (socket.ntohl(value[2]) << (32 * 1)) + \
+ (socket.ntohl(value[3]))
+ addr = ipaddr.IPv6Address(v)
+
+ d_val.append(addr.compressed)
+ else:
+ for v in value:
+ d_val.append(_pb2dict_cast(field, v, pretty, is_hex))
+ else:
+ d_val = _pb2dict_cast(field, value, pretty, is_hex)
+
+ d[field.name] = d_val
+ return d
+
+def _dict2pb_cast(field, value):
+ # Not considering TYPE_MESSAGE here, as repeated
+ # and non-repeated messages need special treatment
+ # in this case, and are hadled separately.
+ if field.type == FD.TYPE_BYTES:
+ return value.decode('base64')
+ elif field.type == FD.TYPE_ENUM:
+ return field.enum_type.values_by_name.get(value, None).number
+ elif field.type in _basic_cast:
+ cast = _basic_cast[field.type]
+ if (cast == int or cast == long) and is_string(value):
+ if _marked_as_dev(field):
+ return encode_dev(field, value)
+
+ flags = _marked_as_flags(field)
+ if flags:
+ try:
+ flags_map = flags_maps[flags]
+ except:
+ pass # Try to use plain string cast
+ else:
+ return unmap_flags(value, flags_map)
+
+ # Some int or long fields might be stored as hex
+ # strings. See _pb2dict_cast.
+ return cast(value, 0)
+ else:
+ return cast(value)
+ else:
+ raise Exception("Field(%s) has unsupported type %d" % (field.name, field.type))
+
+def dict2pb(d, pb):
+ """
+ Convert dictionary to protobuf msg.
+ Takes dict and protobuf message to be merged into.
+ """
+ for field in pb.DESCRIPTOR.fields:
+ if field.name not in d:
+ continue
+ value = d[field.name]
+ if field.label == FD.LABEL_REPEATED:
+ pb_val = getattr(pb, field.name, None)
+ if is_string(value[0]) and _marked_as_ip(field):
+ val = ipaddr.IPAddress(value[0])
+ if val.version == 4:
+ pb_val.append(socket.htonl(int(val)))
+ elif val.version == 6:
+ ival = int(val)
+ pb_val.append(socket.htonl((ival >> (32 * 3)) & 0xFFFFFFFF))
+ pb_val.append(socket.htonl((ival >> (32 * 2)) & 0xFFFFFFFF))
+ pb_val.append(socket.htonl((ival >> (32 * 1)) & 0xFFFFFFFF))
+ pb_val.append(socket.htonl((ival >> (32 * 0)) & 0xFFFFFFFF))
+ else:
+ raise Exception("Unknown IP address version %d" % val.version)
+ continue
+
+ for v in value:
+ if field.type == FD.TYPE_MESSAGE:
+ dict2pb(v, pb_val.add())
+ else:
+ pb_val.append(_dict2pb_cast(field, v))
+ else:
+ if field.type == FD.TYPE_MESSAGE:
+ # SetInParent method acts just like has_* = true in C,
+ # and helps to properly treat cases when we have optional
+ # field with empty repeated inside.
+ getattr(pb, field.name).SetInParent()
+
+ dict2pb(value, getattr(pb, field.name, None))
+ else:
+ setattr(pb, field.name, _dict2pb_cast(field, value))
+ return pb
diff --git a/pycriu/.gitignore b/pycriu/.gitignore
deleted file mode 100644
index 8d503da7f031..000000000000
--- a/pycriu/.gitignore
+++ /dev/null
@@ -1,3 +0,0 @@
-*_pb2.py
-*.pyc
-rpc.py
diff --git a/pycriu/Makefile b/pycriu/Makefile
deleted file mode 100644
index 135fe1c01600..000000000000
--- a/pycriu/Makefile
+++ /dev/null
@@ -1,16 +0,0 @@
-all: images rpc.py
-
-.PHONY: all images clean
-
-images:
- $(Q) $(MAKE) -C images all
-
-# rpc_pb2.py doesn't depend on any other file, so
-# it is safe to rename it, dropping ugly _pb2 suffix.
-rpc.py:
- $(Q) protoc -I=$(SRC_DIR)/protobuf/ --python_out=./ $(SRC_DIR)/protobuf/$(@:.py=.proto)
- $(Q) mv $(@:.py=_pb2.py) $@
-
-clean:
- $(Q) $(MAKE) -C images clean
- $(Q) $(RM) rpc.py *.pyc
diff --git a/pycriu/__init__.py b/pycriu/__init__.py
deleted file mode 100644
index 7de62838cf2f..000000000000
--- a/pycriu/__init__.py
+++ /dev/null
@@ -1,3 +0,0 @@
-import rpc
-import images
-from criu import *
diff --git a/pycriu/criu.py b/pycriu/criu.py
deleted file mode 100644
index 84dcefed25e1..000000000000
--- a/pycriu/criu.py
+++ /dev/null
@@ -1,282 +0,0 @@
-# Same as libcriu for C.
-
-import socket
-import errno
-import subprocess
-import fcntl
-import os
-import signal
-import sys
-import struct
-
-import rpc
-
-class _criu_comm:
- """
- Base class for communication classes.
- """
- COMM_SK = 0
- COMM_FD = 1
- COMM_BIN = 2
- comm_type = None
- comm = None
- sk = None
-
- def connect(self, daemon):
- """
- Connect to criu and return socket object.
- daemon -- is for whether or not criu should daemonize if executing criu from binary(comm_bin).
- """
- pass
-
- def disconnect(self):
- """
- Disconnect from criu.
- """
- pass
-
-
-class _criu_comm_sk(_criu_comm):
- """
- Communication class for unix socket.
- """
- def __init__(self, sk_path):
- self.comm_type = self.COMM_SK
- self.comm = sk_path
-
- def connect(self, daemon):
- self.sk = socket.socket(socket.AF_UNIX, socket.SOCK_SEQPACKET)
- self.sk.connect(self.comm)
-
- return self.sk
-
- def disconnect(self):
- self.sk.close()
-
-
-class _criu_comm_fd(_criu_comm):
- """
- Commnunication class for file descriptor.
- """
- def __init__(self, fd):
- self.comm_type = self.COMM_FD
- self.comm = fd
-
- def connect(self, daemon):
- self.sk = socket.fromfd(self.comm, socket.AF_UNIX, socket.SOCK_SEQPACKET)
-
- return self.sk
-
- def disconnect(self):
- self.sk.close()
-
-class _criu_comm_bin(_criu_comm):
- """
- Communication class for binary.
- """
- def __init__(self, bin_path):
- self.comm_type = self.COMM_BIN
- self.comm = bin_path
- self.swrk = None
- self.daemon = None
-
- def connect(self, daemon):
- # Kind of the same thing we do in libcriu
- css = socket.socketpair(socket.AF_UNIX, socket.SOCK_SEQPACKET)
- flags = fcntl.fcntl(css[1], fcntl.F_GETFD)
- fcntl.fcntl(css[1], fcntl.F_SETFD, flags | fcntl.FD_CLOEXEC)
-
- self.daemon = daemon
-
- p = os.fork()
-
- if p == 0:
- def exec_criu():
- os.close(0)
- os.close(1)
- os.close(2)
-
- css[0].send(struct.pack('i', os.getpid()))
- os.execv(self.comm, [self.comm, 'swrk', "%d" % css[0].fileno()])
- os._exit(1)
-
- if daemon:
- # Python has no daemon(3) alternative,
- # so we need to mimic it ourself.
- p = os.fork()
-
- if p == 0:
- os.setsid()
-
- exec_criu()
- else:
- os._exit(0)
- else:
- exec_criu()
-
- css[0].close()
- self.swrk = struct.unpack('i', css[1].recv(4))[0]
- self.sk = css[1]
-
- return self.sk
-
- def disconnect(self):
- self.sk.close()
- if not self.daemon:
- os.waitpid(self.swrk, 0)
-
-
-class CRIUException(Exception):
- """
- Exception class for handling and storing criu errors.
- """
- typ = None
- _str = None
-
- def __str__(self):
- return self._str
-
-
-class CRIUExceptionInternal(CRIUException):
- """
- Exception class for handling and storing internal errors.
- """
- def __init__(self, typ, s):
- self.typ = typ
- self._str = "%s failed with internal error: %s" % (rpc.criu_req_type.Name(self.typ), s)
-
-
-class CRIUExceptionExternal(CRIUException):
- """
- Exception class for handling and storing criu RPC errors.
- """
-
- def __init__(self, req_typ, resp_typ, errno):
- self.typ = req_typ
- self.resp_typ = resp_typ
- self.errno = errno
- self._str = self._gen_error_str()
-
- def _gen_error_str(self):
- s = "%s failed: " % (rpc.criu_req_type.Name(self.typ), )
-
- if self.typ != self.resp_typ:
- s += "Unxecpected response type %d: " % (self.resp_typ, )
-
- s += "Error(%d): " % (self.errno, )
-
- if self.errno == errno.EBADRQC:
- s += "Bad options"
-
- if self.typ == rpc.DUMP:
- if self.errno == errno.ESRCH:
- s += "No process with such pid"
-
- if self.typ == rpc.RESTORE:
- if self.errno == errno.EEXIST:
- s += "Process with requested pid already exists"
-
- s += "Unknown"
-
- return s
-
-
-class criu:
- """
- Call criu through RPC.
- """
- opts = None #CRIU options in pb format
-
- _comm = None #Communication method
-
- def __init__(self):
- self.use_binary('criu')
- self.opts = rpc.criu_opts()
-
- def use_sk(self, sk_name):
- """
- Access criu using unix socket which that belongs to criu service daemon.
- """
- self._comm = _criu_comm_sk(sk_name)
-
- def use_fd(self, fd):
- """
- Access criu using provided fd.
- """
- self._comm = _criu_comm_fd(fd)
-
- def use_binary(self, bin_name):
- """
- Access criu by execing it using provided path to criu binary.
- """
- self._comm = _criu_comm_bin(bin_name)
-
- def _send_req_and_recv_resp(self, req):
- """
- As simple as send request and receive response.
- """
- # In case of self-dump we need to spawn criu swrk detached
- # from our current process, as criu has a hard time separating
- # process resources from its own if criu is located in a same
- # process tree it is trying to dump.
- daemon = False
- if req.type == rpc.DUMP and not req.opts.HasField('pid'):
- daemon = True
-
- try:
- s = self._comm.connect(daemon)
-
- s.send(req.SerializeToString())
-
- buf = s.recv(len(s.recv(1, socket.MSG_TRUNC | socket.MSG_PEEK)))
-
- self._comm.disconnect()
-
- resp = rpc.criu_resp()
- resp.ParseFromString(buf)
- except Exception as e:
- raise CRIUExceptionInternal(req.type, str(e))
-
- return resp
-
- def check(self):
- """
- Checks whether the kernel support is up-to-date.
- """
- req = rpc.criu_req()
- req.type = rpc.CHECK
-
- resp = self._send_req_and_recv_resp(req)
-
- if not resp.success:
- raise CRIUExceptionExternal(req.type, resp.type, resp.cr_errno)
-
- def dump(self):
- """
- Checkpoint a process/tree identified by opts.pid.
- """
- req = rpc.criu_req()
- req.type = rpc.DUMP
- req.opts.MergeFrom(self.opts)
-
- resp = self._send_req_and_recv_resp(req)
-
- if not resp.success:
- raise CRIUExceptionExternal(req.type, resp.type, resp.cr_errno)
-
- return resp.dump
-
- def restore(self):
- """
- Restore a process/tree.
- """
- req = rpc.criu_req()
- req.type = rpc.RESTORE
- req.opts.MergeFrom(self.opts)
-
- resp = self._send_req_and_recv_resp(req)
-
- if not resp.success:
- raise CRIUExceptionExternal(req.type, resp.type, resp.errno)
-
- return resp.restore
diff --git a/pycriu/images/.gitignore b/pycriu/images/.gitignore
deleted file mode 100644
index 234bfe9f6f33..000000000000
--- a/pycriu/images/.gitignore
+++ /dev/null
@@ -1,4 +0,0 @@
-*.pyc
-*_pb2.py
-magic.py
-pb.py
diff --git a/pycriu/images/Makefile b/pycriu/images/Makefile
deleted file mode 100644
index 98a450065465..000000000000
--- a/pycriu/images/Makefile
+++ /dev/null
@@ -1,26 +0,0 @@
-all: pb.py protobuf magic.py
-
-.PHONY: all protobuf clean pb.py
-
-proto := $(filter-out $(SRC_DIR)/protobuf/rpc.proto, $(sort $(wildcard $(SRC_DIR)/protobuf/*.proto)))
-proto-py-modules := $(foreach m,$(proto),$(subst -,_,$(notdir $(m:.proto=_pb2))))
-
-# We don't need rpc_pb2.py here, as it is not related to the
-# images.
-# Unfortunately, we can't drop ugly _pb2 suffixes here, because
-# some _pb2 files depend on others _pb2 files.
-protobuf:
- $(Q) protoc -I=$(SRC_DIR)/protobuf -I=/usr/include/ --python_out=./ $(proto)
-
-magic.py: $(SRC_DIR)/scripts/magic-gen.py $(SRC_DIR)/include/magic.h
- $(E) " GEN " $@
- $(Q) python $^ $@
-
-pb.py: protobuf
- $(Q) echo "# Autogenerated. Do not edit!" > $@
- $(Q) for m in $(proto-py-modules); do \
- echo "from $$m import *" >> $@ ;\
- done
-
-clean:
- $(Q) $(RM) ./*_pb2.py ./*.pyc magic.py pb.py
diff --git a/pycriu/images/__init__.py b/pycriu/images/__init__.py
deleted file mode 100644
index 379943b977e1..000000000000
--- a/pycriu/images/__init__.py
+++ /dev/null
@@ -1,3 +0,0 @@
-from magic import *
-from images import *
-from pb import *
diff --git a/pycriu/images/images.py b/pycriu/images/images.py
deleted file mode 100644
index d4e883f11006..000000000000
--- a/pycriu/images/images.py
+++ /dev/null
@@ -1,479 +0,0 @@
-#!/bin/env python
-
-# This file contains methods to deal with criu images.
-#
-# According to http://criu.org/Images, criu images can be described
-# with such IOW:
-#
-# IMAGE_FILE ::= MAGIC { ENTRY }
-# ENTRY ::= SIZE PAYLOAD [ EXTRA ]
-# PAYLOAD ::= "message encoded in ProtocolBuffer format"
-# EXTRA ::= "arbitrary blob, depends on the PAYLOAD contents"
-#
-# MAGIC ::= "32 bit integer"
-# SIZE ::= "32 bit integer, equals the PAYLOAD length"
-#
-# Images v1.1 NOTE: MAGIC now consist of 2 32 bit integers, first one is
-# MAGIC_COMMON or MAGIC_SERVICE and the second one is same as MAGIC
-# in images V1.0. We don't keep "first" magic in json images.
-#
-# In order to convert images to human-readable format, we use dict(json).
-# Using json not only allows us to easily read\write images, but also
-# to use a great variety of tools out there to manipulate them.
-# It also allows us to clearly describe criu images structure.
-#
-# Using dict(json) format, criu images can be described like:
-#
-# {
-# 'magic' : 'FOO',
-# 'entries' : [
-# entry,
-# ...
-# ]
-# }
-#
-# Entry, in its turn, could be described as:
-#
-# {
-# pb_msg,
-# 'extra' : extra_msg
-# }
-#
-import io
-import google
-import struct
-import os
-import sys
-import json
-import pb2dict
-import array
-
-import magic
-from pb import *
-
-#
-# Predefined hardcoded constants
-sizeof_u16 = 2
-sizeof_u32 = 4
-sizeof_u64 = 8
-
-# A helper for rounding
-def round_up(x,y):
- return (((x - 1) | (y - 1)) + 1)
-
-class MagicException(Exception):
- def __init__(self, magic):
- self.magic = magic
-
-# Generic class to handle loading/dumping criu images entries from/to bin
-# format to/from dict(json).
-class entry_handler:
- """
- Generic class to handle loading/dumping criu images
- entries from/to bin format to/from dict(json).
- """
- def __init__(self, payload, extra_handler=None):
- """
- Sets payload class and extra handler class.
- """
- self.payload = payload
- self.extra_handler = extra_handler
-
- def load(self, f, pretty = False):
- """
- Convert criu image entries from binary format to dict(json).
- Takes a file-like object and returnes a list with entries in
- dict(json) format.
- """
- entries = []
-
- while True:
- entry = {}
-
- # Read payload
- pb = self.payload()
- buf = f.read(4)
- if buf == '':
- break
- size, = struct.unpack('i', buf)
- pb.ParseFromString(f.read(size))
- entry = pb2dict.pb2dict(pb, pretty)
-
- # Read extra
- if self.extra_handler:
- entry['extra'] = self.extra_handler.load(f, pb)
-
- entries.append(entry)
-
- return entries
-
- def loads(self, s, pretty = False):
- """
- Same as load(), but takes a string as an argument.
- """
- f = io.BytesIO(s)
- return self.load(f, pretty)
-
- def dump(self, entries, f):
- """
- Convert criu image entries from dict(json) format to binary.
- Takes a list of entries and a file-like object to write entries
- in binary format to.
- """
- for entry in entries:
- extra = entry.pop('extra', None)
-
- # Write payload
- pb = self.payload()
- pb2dict.dict2pb(entry, pb)
- pb_str = pb.SerializeToString()
- size = len(pb_str)
- f.write(struct.pack('i', size))
- f.write(pb_str)
-
- # Write extra
- if self.extra_handler and extra:
- self.extra_handler.dump(extra, f, pb)
-
- def dumps(self, entries):
- """
- Same as dump(), but doesn't take file-like object and just
- returns a string.
- """
- f = io.BytesIO('')
- self.dump(entries, f)
- return f.read()
-
- def count(self, f):
- """
- Counts the number of top-level object in the image file
- """
- entries = 0
-
- while True:
- buf = f.read(4)
- if buf == '':
- break
- size, = struct.unpack('i', buf)
- f.seek(size, 1)
- entries += 1
-
- return entries
-
-# Special handler for pagemap.img
-class pagemap_handler:
- """
- Special entry handler for pagemap.img, which is unique in a way
- that it has a header of pagemap_head type followed by entries
- of pagemap_entry type.
- """
- def load(self, f, pretty = False):
- entries = []
-
- pb = pagemap_head()
- while True:
- buf = f.read(4)
- if buf == '':
- break
- size, = struct.unpack('i', buf)
- pb.ParseFromString(f.read(size))
- entries.append(pb2dict.pb2dict(pb, pretty))
-
- pb = pagemap_entry()
-
- return entries
-
- def loads(self, s, pretty = False):
- f = io.BytesIO(s)
- return self.load(f, pretty)
-
- def dump(self, entries, f):
- pb = pagemap_head()
- for item in entries:
- pb2dict.dict2pb(item, pb)
- pb_str = pb.SerializeToString()
- size = len(pb_str)
- f.write(struct.pack('i', size))
- f.write(pb_str)
-
- pb = pagemap_entry()
-
- def dumps(self, entries):
- f = io.BytesIO('')
- self.dump(entries, f)
- return f.read()
-
- def count(self, f):
- return entry_handler(None).count(f) - 1
-
-
-# In following extra handlers we use base64 encoding
-# to store binary data. Even though, the nature
-# of base64 is that it increases the total size,
-# it doesn't really matter, because our images
-# do not store big amounts of binary data. They
-# are negligible comparing to pages size.
-class pipes_data_extra_handler:
- def load(self, f, pload):
- size = pload.bytes
- data = f.read(size)
- return data.encode('base64')
-
- def dump(self, extra, f, pload):
- data = extra.decode('base64')
- f.write(data)
-
-class sk_queues_extra_handler:
- def load(self, f, pload):
- size = pload.length
- data = f.read(size)
- return data.encode('base64')
-
- def dump(self, extra, f, pb):
- data = extra.decode('base64')
- f.write(data)
-
-class ghost_file_extra_handler:
- def load(self, f, pb):
- data = f.read()
- return data.encode('base64')
-
- def dump(self, extra, f, pb):
- data = extra.decode('base64')
- f.write(data)
-
-class tcp_stream_extra_handler:
- def load(self, f, pb):
- d = {}
-
- inq = f.read(pb.inq_len)
- outq = f.read(pb.outq_len)
-
- d['inq'] = inq.encode('base64')
- d['outq'] = outq.encode('base64')
-
- return d
-
- def dump(self, extra, f, pb):
- inq = extra['inq'].decode('base64')
- outq = extra['outq'].decode('base64')
-
- f.write(inq)
- f.write(outq)
-
-class ipc_sem_set_handler:
- def load(self, f, pb):
- entry = pb2dict.pb2dict(pb)
- size = sizeof_u16 * entry['nsems']
- rounded = round_up(size, sizeof_u64)
- s = array.array('H')
- if s.itemsize != sizeof_u16:
- raise Exception("Array size mismatch")
- s.fromstring(f.read(size))
- f.seek(rounded - size, 1)
- return s.tolist()
-
- def dump(self, extra, f, pb):
- entry = pb2dict.pb2dict(pb)
- size = sizeof_u16 * entry['nsems']
- rounded = round_up(size, sizeof_u64)
- s = array.array('H')
- if s.itemsize != sizeof_u16:
- raise Exception("Array size mismatch")
- s.fromlist(extra)
- if len(s) != entry['nsems']:
- raise Exception("Number of semaphores mismatch")
- f.write(s.tostring())
- f.write('\0' * (rounded - size))
-
-class ipc_msg_queue_handler:
- def load(self, f, pb):
- entry = pb2dict.pb2dict(pb)
- messages = []
- for x in range (0, entry['qnum']):
- buf = f.read(4)
- if buf == '':
- break
- size, = struct.unpack('i', buf)
- msg = ipc_msg()
- msg.ParseFromString(f.read(size))
- rounded = round_up(msg.msize, sizeof_u64)
- data = f.read(msg.msize)
- f.seek(rounded - msg.msize, 1)
- messages.append(pb2dict.pb2dict(msg))
- messages.append(data.encode('base64'))
- return messages
-
- def dump(self, extra, f, pb):
- entry = pb2dict.pb2dict(pb)
- for i in range (0, len(extra), 2):
- msg = ipc_msg()
- pb2dict.dict2pb(extra[i], msg)
- msg_str = msg.SerializeToString()
- size = len(msg_str)
- f.write(struct.pack('i', size))
- f.write(msg_str)
- rounded = round_up(msg.msize, sizeof_u64)
- data = extra[i + 1].decode('base64')
- f.write(data[:msg.msize])
- f.write('\0' * (rounded - msg.msize))
-
-class ipc_shm_handler:
- def load(self, f, pb):
- entry = pb2dict.pb2dict(pb)
- size = entry['size']
- data = f.read(size)
- rounded = round_up(size, sizeof_u32)
- f.seek(rounded - size, 1)
- return data.encode('base64')
-
- def dump(self, extra, f, pb):
- entry = pb2dict.pb2dict(pb)
- size = entry['size']
- data = extra.decode('base64')
- rounded = round_up(size, sizeof_u32)
- f.write(data[:size])
- f.write('\0' * (rounded - size))
-
-handlers = {
- 'INVENTORY' : entry_handler(inventory_entry),
- 'CORE' : entry_handler(core_entry),
- 'IDS' : entry_handler(task_kobj_ids_entry),
- 'CREDS' : entry_handler(creds_entry),
- 'UTSNS' : entry_handler(utsns_entry),
- 'IPC_VAR' : entry_handler(ipc_var_entry),
- 'FS' : entry_handler(fs_entry),
- 'GHOST_FILE' : entry_handler(ghost_file_entry, ghost_file_extra_handler()),
- 'MM' : entry_handler(mm_entry),
- 'CGROUP' : entry_handler(cgroup_entry),
- 'TCP_STREAM' : entry_handler(tcp_stream_entry, tcp_stream_extra_handler()),
- 'STATS' : entry_handler(stats_entry),
- 'PAGEMAP' : pagemap_handler(), # Special one
- 'PSTREE' : entry_handler(pstree_entry),
- 'REG_FILES' : entry_handler(reg_file_entry),
- 'NS_FILES' : entry_handler(ns_file_entry),
- 'EVENTFD_FILE' : entry_handler(eventfd_file_entry),
- 'EVENTPOLL_FILE' : entry_handler(eventpoll_file_entry),
- 'EVENTPOLL_TFD' : entry_handler(eventpoll_tfd_entry),
- 'SIGNALFD' : entry_handler(signalfd_entry),
- 'TIMERFD' : entry_handler(timerfd_entry),
- 'INOTIFY_FILE' : entry_handler(inotify_file_entry),
- 'INOTIFY_WD' : entry_handler(inotify_wd_entry),
- 'FANOTIFY_FILE' : entry_handler(fanotify_file_entry),
- 'FANOTIFY_MARK' : entry_handler(fanotify_mark_entry),
- 'VMAS' : entry_handler(vma_entry),
- 'PIPES' : entry_handler(pipe_entry),
- 'FIFO' : entry_handler(fifo_entry),
- 'SIGACT' : entry_handler(sa_entry),
- 'NETLINK_SK' : entry_handler(netlink_sk_entry),
- 'REMAP_FPATH' : entry_handler(remap_file_path_entry),
- 'MNTS' : entry_handler(mnt_entry),
- 'TTY_FILES' : entry_handler(tty_file_entry),
- 'TTY_INFO' : entry_handler(tty_info_entry),
- 'RLIMIT' : entry_handler(rlimit_entry),
- 'TUNFILE' : entry_handler(tunfile_entry),
- 'EXT_FILES' : entry_handler(ext_file_entry),
- 'IRMAP_CACHE' : entry_handler(irmap_cache_entry),
- 'FILE_LOCKS' : entry_handler(file_lock_entry),
- 'FDINFO' : entry_handler(fdinfo_entry),
- 'UNIXSK' : entry_handler(unix_sk_entry),
- 'INETSK' : entry_handler(inet_sk_entry),
- 'PACKETSK' : entry_handler(packet_sock_entry),
- 'ITIMERS' : entry_handler(itimer_entry),
- 'POSIX_TIMERS' : entry_handler(posix_timer_entry),
- 'NETDEV' : entry_handler(net_device_entry),
- 'PIPES_DATA' : entry_handler(pipe_data_entry, pipes_data_extra_handler()),
- 'FIFO_DATA' : entry_handler(pipe_data_entry, pipes_data_extra_handler()),
- 'SK_QUEUES' : entry_handler(sk_packet_entry, sk_queues_extra_handler()),
- 'IPCNS_SHM' : entry_handler(ipc_shm_entry, ipc_shm_handler()),
- 'IPCNS_SEM' : entry_handler(ipc_sem_entry, ipc_sem_set_handler()),
- 'IPCNS_MSG' : entry_handler(ipc_msg_entry, ipc_msg_queue_handler()),
- 'NETNS' : entry_handler(netns_entry),
- 'USERNS' : entry_handler(userns_entry),
- 'SECCOMP' : entry_handler(seccomp_entry),
- }
-
-def __rhandler(f):
- # Images v1.1 NOTE: First read "first" magic.
- img_magic, = struct.unpack('i', f.read(4))
- if img_magic in (magic.by_name['IMG_COMMON'], magic.by_name['IMG_SERVICE']):
- img_magic, = struct.unpack('i', f.read(4))
-
- try:
- m = magic.by_val[img_magic]
- except:
- raise MagicException(img_magic)
-
- try:
- handler = handlers[m]
- except:
- raise Exception("No handler found for image with magic " + m)
-
- return m, handler
-
-def load(f, pretty = False):
- """
- Convert criu image from binary format to dict(json).
- Takes a file-like object to read criu image from.
- Returns criu image in dict(json) format.
- """
- image = {}
-
- m, handler = __rhandler(f)
-
- image['magic'] = m
- image['entries'] = handler.load(f, pretty)
-
- return image
-
-def info(f):
- res = {}
-
- m, handler = __rhandler(f)
-
- res['magic'] = m
- res['count'] = handler.count(f)
-
- return res
-
-def loads(s, pretty = False):
- """
- Same as load(), but takes a string.
- """
- f = io.BytesIO(s)
- return load(f, pretty)
-
-def dump(img, f):
- """
- Convert criu image from dict(json) format to binary.
- Takes an image in dict(json) format and file-like
- object to write to.
- """
- m = img['magic']
- magic_val = magic.by_name[img['magic']]
-
- # Images v1.1 NOTE: use "second" magic to identify what "first"
- # should be written.
- if m != 'INVENTORY':
- if m in ('STATS', 'IRMAP_CACHE'):
- f.write(struct.pack('i', magic.by_name['IMG_SERVICE']))
- else:
- f.write(struct.pack('i', magic.by_name['IMG_COMMON']))
-
- f.write(struct.pack('i', magic_val))
-
- try:
- handler = handlers[m]
- except:
- raise Exception("No handler found for image with such magic")
-
- handler.dump(img['entries'], f)
-
-def dumps(img):
- """
- Same as dump(), but takes only an image and returns
- a string.
- """
- f = io.BytesIO('')
- dump(img, f)
- return f.getvalue()
diff --git a/pycriu/images/pb2dict.py b/pycriu/images/pb2dict.py
deleted file mode 100644
index 177cda3154a3..000000000000
--- a/pycriu/images/pb2dict.py
+++ /dev/null
@@ -1,276 +0,0 @@
-from google.protobuf.descriptor import FieldDescriptor as FD
-import opts_pb2
-import ipaddr
-import socket
-import collections
-import os
-
-# pb2dict and dict2pb are methods to convert pb to/from dict.
-# Inspired by:
-# protobuf-to-dict - https://github.com/benhodgson/protobuf-to-dict
-# protobuf-json - https://code.google.com/p/protobuf-json/
-# protobuf source - https://code.google.com/p/protobuf/
-# Both protobuf-to-dict/json do not fit here because of several reasons,
-# here are some of them:
-# - both have a common bug in treating optional field with empty
-# repeated inside.
-# - protobuf-to-json is not avalible in pip or in any other python
-# repo, so it is hard to distribute and we can't rely on it.
-# - both do not treat enums in a way we would like to. They convert
-# protobuf enum to int, but we need a string here, because it is
-# much more informative. BTW, protobuf text_format converts pb
-# enums to string value too. (i.e. "march : x86_64" is better then
-# "march : 1").
-
-
-_basic_cast = {
- FD.TYPE_FIXED64 : long,
- FD.TYPE_FIXED32 : int,
- FD.TYPE_SFIXED64 : long,
- FD.TYPE_SFIXED32 : int,
-
- FD.TYPE_INT64 : long,
- FD.TYPE_UINT64 : long,
- FD.TYPE_SINT64 : long,
-
- FD.TYPE_INT32 : int,
- FD.TYPE_UINT32 : int,
- FD.TYPE_SINT32 : int,
-
- FD.TYPE_BOOL : bool,
-
- FD.TYPE_STRING : unicode
-}
-
-def _marked_as_hex(field):
- return field.GetOptions().Extensions[opts_pb2.criu].hex
-
-def _marked_as_ip(field):
- return field.GetOptions().Extensions[opts_pb2.criu].ipadd
-
-def _marked_as_flags(field):
- return field.GetOptions().Extensions[opts_pb2.criu].flags
-
-def _marked_as_dev(field):
- return field.GetOptions().Extensions[opts_pb2.criu].dev
-
-def _marked_as_odev(field):
- return field.GetOptions().Extensions[opts_pb2.criu].odev
-
-mmap_prot_map = [
- ('PROT_READ', 0x1),
- ('PROT_WRITE', 0x2),
- ('PROT_EXEC', 0x4),
-];
-
-mmap_flags_map = [
- ('MAP_SHARED', 0x1),
- ('MAP_PRIVATE', 0x2),
- ('MAP_ANON', 0x20),
- ('MAP_GROWSDOWN', 0x0100),
-];
-
-mmap_status_map = [
- ('VMA_AREA_NONE', 0 << 0),
- ('VMA_AREA_REGULAR', 1 << 0),
- ('VMA_AREA_STACK', 1 << 1),
- ('VMA_AREA_VSYSCALL', 1 << 2),
- ('VMA_AREA_VDSO', 1 << 3),
- ('VMA_AREA_HEAP', 1 << 5),
-
- ('VMA_FILE_PRIVATE', 1 << 6),
- ('VMA_FILE_SHARED', 1 << 7),
- ('VMA_ANON_SHARED', 1 << 8),
- ('VMA_ANON_PRIVATE', 1 << 9),
-
- ('VMA_AREA_SYSVIPC', 1 << 10),
- ('VMA_AREA_SOCKET', 1 << 11),
- ('VMA_AREA_VVAR', 1 << 12),
- ('VMA_AREA_AIORING', 1 << 13),
-
- ('VMA_UNSUPP', 1 << 31),
-];
-
-rfile_flags_map = [
- ('O_WRONLY', 01),
- ('O_RDWR', 02),
- ('O_APPEND', 02000),
- ('O_DIRECT', 040000),
- ('O_LARGEFILE', 0100000),
-];
-
-flags_maps = {
- 'mmap.prot' : mmap_prot_map,
- 'mmap.flags' : mmap_flags_map,
- 'mmap.status' : mmap_status_map,
- 'rfile.flags' : rfile_flags_map,
-}
-
-def map_flags(value, flags_map):
- bs = map(lambda x: x[0], filter(lambda x: value & x[1], flags_map))
- value &= ~sum(map(lambda x: x[1], flags_map))
- if value:
- bs.append("0x%x" % value)
- return " | ".join(bs)
-
-def unmap_flags(value, flags_map):
- if value == '':
- return 0
-
- bd = dict(flags_map)
- return sum(map(lambda x: int(str(bd.get(x, x)), 0), map(lambda x: x.strip(), value.split('|'))))
-
-kern_minorbits = 20 # This is how kernel encodes dev_t in new format
-
-def decode_dev(field, value):
- if _marked_as_odev(field):
- return "%d:%d" % (os.major(value), os.minor(value))
- else:
- return "%d:%d" % (value >> kern_minorbits, value & ((1 << kern_minorbits) - 1))
-
-def encode_dev(field, value):
- dev = map(lambda x: int(x), value.split(':'))
- if _marked_as_odev(field):
- return os.makedev(dev[0], dev[1])
- else:
- return dev[0] << kern_minorbits | dev[1]
-
-def is_string(value):
- return isinstance(value, unicode) or isinstance(value, str)
-
-def _pb2dict_cast(field, value, pretty = False, is_hex = False):
- if not is_hex:
- is_hex = _marked_as_hex(field)
-
- if field.type == FD.TYPE_MESSAGE:
- return pb2dict(value, pretty, is_hex)
- elif field.type == FD.TYPE_BYTES:
- return value.encode('base64')
- elif field.type == FD.TYPE_ENUM:
- return field.enum_type.values_by_number.get(value, None).name
- elif field.type in _basic_cast:
- cast = _basic_cast[field.type]
- if pretty and (cast == int or cast == long):
- if is_hex:
- # Fields that have (criu).hex = true option set
- # should be stored in hex string format.
- return "0x%x" % value
-
- if _marked_as_dev(field):
- return decode_dev(field, value)
-
- flags = _marked_as_flags(field)
- if flags:
- try:
- flags_map = flags_maps[flags]
- except:
- return "0x%x" % value # flags are better seen as hex anyway
- else:
- return map_flags(value, flags_map)
-
- return cast(value)
- else:
- raise Exception("Field(%s) has unsupported type %d" % (field.name, field.type))
-
-def pb2dict(pb, pretty = False, is_hex = False):
- """
- Convert protobuf msg to dictionary.
- Takes a protobuf message and returns a dict.
- """
- d = collections.OrderedDict() if pretty else {}
- for field, value in pb.ListFields():
- if field.label == FD.LABEL_REPEATED:
- d_val = []
- if pretty and _marked_as_ip(field):
- if len(value) == 1:
- v = socket.ntohl(value[0])
- addr = ipaddr.IPv4Address(v)
- else:
- v = 0 + (socket.ntohl(value[0]) << (32 * 3)) + \
- (socket.ntohl(value[1]) << (32 * 2)) + \
- (socket.ntohl(value[2]) << (32 * 1)) + \
- (socket.ntohl(value[3]))
- addr = ipaddr.IPv6Address(v)
-
- d_val.append(addr.compressed)
- else:
- for v in value:
- d_val.append(_pb2dict_cast(field, v, pretty, is_hex))
- else:
- d_val = _pb2dict_cast(field, value, pretty, is_hex)
-
- d[field.name] = d_val
- return d
-
-def _dict2pb_cast(field, value):
- # Not considering TYPE_MESSAGE here, as repeated
- # and non-repeated messages need special treatment
- # in this case, and are hadled separately.
- if field.type == FD.TYPE_BYTES:
- return value.decode('base64')
- elif field.type == FD.TYPE_ENUM:
- return field.enum_type.values_by_name.get(value, None).number
- elif field.type in _basic_cast:
- cast = _basic_cast[field.type]
- if (cast == int or cast == long) and is_string(value):
- if _marked_as_dev(field):
- return encode_dev(field, value)
-
- flags = _marked_as_flags(field)
- if flags:
- try:
- flags_map = flags_maps[flags]
- except:
- pass # Try to use plain string cast
- else:
- return unmap_flags(value, flags_map)
-
- # Some int or long fields might be stored as hex
- # strings. See _pb2dict_cast.
- return cast(value, 0)
- else:
- return cast(value)
- else:
- raise Exception("Field(%s) has unsupported type %d" % (field.name, field.type))
-
-def dict2pb(d, pb):
- """
- Convert dictionary to protobuf msg.
- Takes dict and protobuf message to be merged into.
- """
- for field in pb.DESCRIPTOR.fields:
- if field.name not in d:
- continue
- value = d[field.name]
- if field.label == FD.LABEL_REPEATED:
- pb_val = getattr(pb, field.name, None)
- if is_string(value[0]) and _marked_as_ip(field):
- val = ipaddr.IPAddress(value[0])
- if val.version == 4:
- pb_val.append(socket.htonl(int(val)))
- elif val.version == 6:
- ival = int(val)
- pb_val.append(socket.htonl((ival >> (32 * 3)) & 0xFFFFFFFF))
- pb_val.append(socket.htonl((ival >> (32 * 2)) & 0xFFFFFFFF))
- pb_val.append(socket.htonl((ival >> (32 * 1)) & 0xFFFFFFFF))
- pb_val.append(socket.htonl((ival >> (32 * 0)) & 0xFFFFFFFF))
- else:
- raise Exception("Unknown IP address version %d" % val.version)
- continue
-
- for v in value:
- if field.type == FD.TYPE_MESSAGE:
- dict2pb(v, pb_val.add())
- else:
- pb_val.append(_dict2pb_cast(field, v))
- else:
- if field.type == FD.TYPE_MESSAGE:
- # SetInParent method acts just like has_* = true in C,
- # and helps to properly treat cases when we have optional
- # field with empty repeated inside.
- getattr(pb, field.name).SetInParent()
-
- dict2pb(value, getattr(pb, field.name, None))
- else:
- setattr(pb, field.name, _dict2pb_cast(field, value))
- return pb
--
2.5.0
More information about the CRIU
mailing list