[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