From a1f2c461ef9bb7f4623d6a3fadf7feabbebf7dcf Mon Sep 17 00:00:00 2001 From: Pavel Emelyanov Date: Wed, 3 Aug 2016 18:14:00 +0300 Subject: [PATCH 001/249] crns.py: New attempt to have --unshare option So, here's the enhanced version of the first try. Changes are: 1. The wrapper name is criu-ns instead of crns.py 2. The CLI is absolutely the same as for criu, since the script re-execl-s criu binary. E.g. scripts/criu-ns dump -t 1234 ... just works 3. Caller doesn't need to care about substituting CLI options, instead, the scripts analyzes the command line and a) replaces -t|--tree argument with virtual pid __if__ the target task lives in another pidns b) keeps the current cwd (and root) __if__ switches to another mntns. A limitation applies here -- cwd path should be the same in target ns, no "smart path mapping" is performed. So this script is for now only useful for mntns clones (which is our main goal at the moment). Signed-off-by: Pavel Emelyanov Looks-good-to: Andrey Vagin --- scripts/criu-ns | 240 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 240 insertions(+) create mode 100755 scripts/criu-ns diff --git a/scripts/criu-ns b/scripts/criu-ns new file mode 100755 index 000000000..e7ebbf0ca --- /dev/null +++ b/scripts/criu-ns @@ -0,0 +1,240 @@ +#!/usr/bin/env python +import ctypes +import ctypes.util +import errno +import sys +import os + +# constants for unshare +CLONE_NEWNS = 0x00020000 +CLONE_NEWPID = 0x20000000 + +# - constants for mount +MS_REC = 16384 +MS_PRIVATE = 1 << 18 +MS_SLAVE = 1 << 19 + +# Load libc bindings +_libc = ctypes.CDLL(ctypes.util.find_library("c"), use_errno=True) + +try: + _unshare = _libc.unshare +except AttributeError: + raise OSError(errno.EINVAL, "unshare is not supported on this platform") +else: + _unshare.argtypes = [ ctypes.c_int ] + _unshare.restype = ctypes.c_int + +try: + _setns = _libc.setns +except AttributeError: + raise OSError(errno.EINVAL, "setns is not supported on this platform") +else: + _setns.argtypes = [ ctypes.c_int, ctypes.c_int ] + _setns.restype = ctypes.c_int + +try: + _mount = _libc.mount +except AttributeError: + raise OSError(errno.EINVAL, "mount is not supported on this platform") +else: + _mount.argtypes = [ + ctypes.c_char_p, + ctypes.c_char_p, + ctypes.c_char_p, + ctypes.c_ulong, + ctypes.c_void_p + ] + _mount.restype = ctypes.c_int + +try: + _umount = _libc.umount +except AttributeError: + raise OSError(errno.EINVAL, "umount is not supported on this platform") +else: + _umount.argtypes = [ctypes.c_char] + _umount.restype = ctypes.c_int + + +def run_criu(): + print sys.argv + os.execlp('criu', *['criu'] + sys.argv[1:]) + + +def wrap_restore(): + # Unshare pid and mount namespaces + if _unshare(CLONE_NEWNS | CLONE_NEWPID) != 0: + _errno = ctypes.get_errno() + raise OSError(_errno, errno.errorcode[_errno]) + + (r_pipe, w_pipe) = os.pipe() + + # Spawn the init + if os.fork() == 0: + os.close(r_pipe) + + # Mount new /proc + if _mount(None, "/", None, MS_SLAVE|MS_REC, None) != 0: + _errno = ctypes.get_errno() + raise OSError(_errno, errno.errorcode[_errno]) + + if _mount('proc', '/proc', 'proc', 0, None) != 0: + _errno = ctypes.get_errno() + raise OSError(_errno, errno.errorcode[_errno]) + + # Spawn CRIU binary + criu_pid = os.fork() + if criu_pid == 0: + run_criu() + raise OSError(errno.ENOENT, "No such command") + + while True: + try: + (pid, status) = os.wait() + if pid == criu_pid: + status = os.WEXITSTATUS(status) + break + except OSError: + status = -251 + break + + os.write(w_pipe, "%d" % status) + os.close(w_pipe) + + if status != 0: + sys.exit(status) + + while True: + try: + os.wait() + except OSError: + break + + sys.exit(0) + + # Wait for CRIU to exit and report the status back + os.close(w_pipe) + status = os.read(r_pipe, 1024) + if not status.isdigit(): + status_i = -252 + else: + status_i = int(status) + + return status_i + + +def get_varg(args): + for i in xrange(1, len(sys.argv)): + if not sys.argv[i] in args: + continue + + if i + 1 >= len(sys.argv): + break + + return (sys.argv[i + 1], i + 1) + + return (None, None) + + + +def set_pidns(tpid, pid_idx): + # Joind pid namespace. Note, that the given pid should + # be changed in -t option, as task lives in different + # pid namespace. + + myns = os.stat('/proc/self/ns/pid').st_ino + + ns_fd = os.open('/proc/%s/ns/pid' % tpid, os.O_RDONLY) + if myns != os.fstat(ns_fd).st_ino: + + for l in open('/proc/%s/status' % tpid): + if not l.startswith('NSpid:'): + continue + + ls = l.split() + if ls[1] != tpid: + raise OSError(errno.ESRCH, 'No such pid') + + print 'Replace pid %s with %s' % (tpid, ls[2]) + sys.argv[pid_idx] = ls[2] + break + else: + raise OSError(errno.ENOENT, 'Cannot find NSpid field in proc') + + if _setns(ns_fd, 0) != 0: + _errno = ctypes.get_errno() + raise OSError(_errno, errno.errorcode[_errno]) + + os.close(ns_fd) + + +def set_mntns(tpid): + # Join mount namespace. Trick here too -- check / and . + # will be the same in target mntns. + + myns = os.stat('/proc/self/ns/mnt').st_ino + ns_fd = os.open('/proc/%s/ns/mnt' % tpid, os.O_RDONLY) + if myns != os.fstat(ns_fd).st_ino: + root_st = os.stat('/') + cwd_st = os.stat('.') + cwd_path = os.path.realpath('.') + + if _setns(ns_fd, 0) != 0: + _errno = ctypes.get_errno() + raise OSError(_errno, errno.errorcode[_errno]) + + os.chdir(cwd_path) + root_nst = os.stat('/') + cwd_nst = os.stat('.') + + def steq(st, nst): + return (st.st_dev, st.st_ino) == (nst.st_dev, nst.st_ino) + + if not steq(root_st, root_nst): + raise OSError(errno.EXDEV, 'Target ns / is not as current') + if not steq(cwd_st, cwd_nst): + raise OSError(errno.EXDEV, 'Target ns . is not as current') + + + os.close(ns_fd) + + +def wrap_dump(): + (pid, pid_idx) = get_varg(('-t', '--tree')) + if pid is None: + raise OSError(errno.EINVAL, 'No --tree option given') + + set_pidns(pid, pid_idx) + set_mntns(pid) + + # Spawn CRIU binary + criu_pid = os.fork() + if criu_pid == 0: + run_criu() + raise OSError(errno.ENOENT, "No such command") + + # Wait for CRIU to exit and report the status back + while True: + try: + (pid, status) = os.wait() + if pid == criu_pid: + status = os.WEXITSTATUS(status) + break + except OSError: + status = -251 + break + + return status + + +action = sys.argv[1] + +if action == 'restore': + res = wrap_restore() +elif action == 'dump' or action == 'pre-dump': + res = wrap_dump() +else: + print 'Unsupported action %s for nswrap' % action + res = -1 + +sys.exit(res) From 3315681b4d9fb494ed35d5c78056e6199aef3ad1 Mon Sep 17 00:00:00 2001 From: "rbruno@gsd.inesc-id.pt" Date: Sat, 11 Feb 2017 04:34:43 +0100 Subject: [PATCH 002/249] util: Copy file w/o sendfile This is the case when the in/out files are image cache/proxy sockets. Signed-off-by: Rodrigo Bruno Signed-off-by: Katerina Koukiou Signed-off-by: Pavel Emelyanov --- criu/util.c | 29 +++++++++++++++++++++++------ 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/criu/util.c b/criu/util.c index 9dd5010a1..34b8d7911 100644 --- a/criu/util.c +++ b/criu/util.c @@ -421,29 +421,46 @@ int copy_file(int fd_in, int fd_out, size_t bytes) { ssize_t written = 0; size_t chunk = bytes ? bytes : 4096; + char *buffer = (char*) malloc(chunk); + ssize_t ret; while (1) { - ssize_t ret; + if (false) { + ret = read(fd_in, buffer, chunk); + if (ret < 0) { + pr_perror("Can't read from fd_in\n"); + ret = -1; + goto err; + } + if (write(fd_out, buffer, ret) != ret) { + pr_perror("Couldn't write all read bytes\n"); + ret = -1; + goto err; + } + } else + ret = sendfile(fd_out, fd_in, NULL, chunk); - ret = sendfile(fd_out, fd_in, NULL, chunk); if (ret < 0) { pr_perror("Can't send data to ghost file"); - return -1; + ret = -1; + goto err; } if (ret == 0) { if (bytes && (written != bytes)) { pr_err("Ghost file size mismatch %zu/%zu\n", written, bytes); - return -1; + ret = -1; + goto err; } break; } written += ret; } - - return 0; +err: + free(buffer); + return ret; } int read_fd_link(int lfd, char *buf, size_t size) From 0c01d20ee951bad2b0ae7ecd3c6a22f29138d021 Mon Sep 17 00:00:00 2001 From: "rbruno@gsd.inesc-id.pt" Date: Sat, 11 Feb 2017 04:34:43 +0100 Subject: [PATCH 003/249] Process Migration using Sockets (p1) This patch introduces the --remote option and the necessary code changes to support it. This leaves user the option to decide if the checkpoint data is to be stored on disk or sent through the network (through the image-proxy). The latter forwards the data to the destination node where image-cache receives it. The overall communication is performed as follows: src_node CRIU dump -> (sends images through UNIX sockets) -> image-proxy | V dst_node: CRIU restore <- (receives images through UNIX sockets)<- image-cache Communication between image-proxy and image-cache is done through a single TCP connection. Running criu with --remote option is like this: dst_node# criu image-cache -d --port -o /tmp/image-cache.log dst_node# criu restore --remote -o /tmp/image-cache.log src_node# criu image-proxy -d --port --address -o /tmp/image-proxy.log src_node# criu dump -t --remote -o /tmp/dump.log [ xemul: here's the list of what should be done with the cache/proxy in order to have them merged into master. 0. Document the whole thing :) Please, add articles for newly introduced actions and options to https://criu.org/CLI page. Also, it would be good to have an article describing the protocols involved. 1. Make the unix sockets reside in work-dir. The good thing is that we've get rid of the socket name option :) But looking at do_open_remote_image() I see that it fchdir-s to image dir before connecting to proxy/cache. Better solution is to put the socket into workdir. 1a. After this the option -D|--images-dir should become optional. Provided the --remote is given CRIU should work purely on the work-dir and not generate anything in the images-dir. 2. Tune up the image_cache and image_proxy commands to accept the --status-fd and --pidfile options. Presumably the very cr_daemon() call should be equipped with everything that should be done for daemonizing and proxy/cache tasks should just call it :) 3. Fix local connections not to generate per-image threads. There can be many images and it's not nice to stress the system with such amount of threads. Please, look at how criu/uffd.c manages multiple descriptors with page-faults using the epoll stuff. 3a. The accept_remote_image_connections() seem not to work well with opts.ps_socket scenario as the former just calls accept() on whatever socket is passed there, while the opts.ps_socket is already an established socket for data transfer. 4. No strings in protocol. Now the hard-coded "RESTORE_FINISH" string (and DUMP_FINISHED one) is used to terminate the communication. Need to tune up the protobuf objects to send boolean (or integer) EOF sign rather that the string. 5. Check how proxy/cache works with incremental dumps. Looking at the skip_remote_bytes() I think that image-cache and -proxy still do not work well with stacked pages images. Probably for those we'll need the page-server or lazy-pages -like protocol that would request the needed regions and receive it back rather than read bytes from sockets simply to skip those. 6. Add support for cache/proxy into go-phaul code. I haven't yet finished with the prototype, but plan to do it soon, so once the above steps are done we'll be able to proceed with this one. ] Signed-off-by: Rodrigo Bruno Signed-off-by: Katerina Koukiou Signed-off-by: Pavel Emelyanov --- criu/Makefile.crtools | 4 + criu/config.c | 1 + criu/cr-dump.c | 16 ++ criu/cr-restore.c | 6 + criu/crtools.c | 13 ++ criu/image.c | 82 ++++++++--- criu/img-remote.c | 275 +++++++++++++++++++++++++++++++++++ criu/include/cr_options.h | 1 + criu/include/img-remote.h | 83 +++++++++++ criu/include/protobuf-desc.h | 4 + criu/page-xfer.c | 46 ++++-- criu/pagemap.c | 51 +++++-- criu/protobuf-desc.c | 1 + criu/util.c | 2 +- images/Makefile | 1 + images/remote-image.proto | 20 +++ 16 files changed, 568 insertions(+), 38 deletions(-) create mode 100644 criu/img-remote.c create mode 100644 criu/include/img-remote.h create mode 100644 images/remote-image.proto diff --git a/criu/Makefile.crtools b/criu/Makefile.crtools index 3717467c2..7832ccd54 100644 --- a/criu/Makefile.crtools +++ b/criu/Makefile.crtools @@ -31,6 +31,10 @@ obj-y += files-reg.o obj-y += fsnotify.o obj-y += image-desc.o obj-y += image.o +obj-y += img-remote.o +obj-y += img-proxy.o +obj-y += img-cache.o +obj-y += img-remote-proto.o obj-y += ipc_ns.o obj-y += irmap.o obj-y += kcmp-ids.o diff --git a/criu/config.c b/criu/config.c index d5354ae9c..f587b6ba2 100644 --- a/criu/config.c +++ b/criu/config.c @@ -506,6 +506,7 @@ int parse_options(int argc, char **argv, bool *usage_error, BOOL_OPT(SK_CLOSE_PARAM, &opts.tcp_close), { "verbosity", optional_argument, 0, 'v' }, { "ps-socket", required_argument, 0, 1091}, + BOOL_OPT("remote", &opts.remote), { "config", required_argument, 0, 1089}, { "no-default-config", no_argument, 0, 1090}, { }, diff --git a/criu/cr-dump.c b/criu/cr-dump.c index 91b8b383a..7f2e5edfc 100644 --- a/criu/cr-dump.c +++ b/criu/cr-dump.c @@ -80,6 +80,7 @@ #include "fault-injection.h" #include "dump.h" #include "eventpoll.h" +#include "img-remote.h" /* * Architectures can overwrite this function to restore register sets that @@ -1562,6 +1563,11 @@ int cr_pre_dump_tasks(pid_t pid) */ rlimit_unlimit_nofile(); + if (opts.remote && push_snapshot_id() < 0) { + pr_err("Failed to push image namespace.\n"); + goto err; + } + root_item = alloc_pstree_item(); if (!root_item) goto err; @@ -1738,6 +1744,11 @@ static int cr_dump_finish(int ret) close_service_fd(CR_PROC_FD_OFF); + if (opts.remote && (finish_remote_dump() < 0)) { + pr_err("Finish remote dump failed.\n"); + return post_dump_ret ? : 1; + } + if (ret) { pr_err("Dumping FAILED.\n"); } else { @@ -1766,6 +1777,11 @@ int cr_dump_tasks(pid_t pid) */ rlimit_unlimit_nofile(); + if (opts.remote && push_snapshot_id() < 0) { + pr_err("Failed to push image namespace.\n"); + goto err; + } + root_item = alloc_pstree_item(); if (!root_item) goto err; diff --git a/criu/cr-restore.c b/criu/cr-restore.c index a7b121b8c..170beb344 100644 --- a/criu/cr-restore.c +++ b/criu/cr-restore.c @@ -27,6 +27,7 @@ #include "cr_options.h" #include "servicefd.h" #include "image.h" +#include "img-remote.h" #include "util.h" #include "util-pie.h" #include "criu-log.h" @@ -2380,6 +2381,11 @@ int cr_restore_tasks(void) goto err; ret = restore_root_task(root_item); + + if (opts.remote && (finish_remote_restore() < 0)) { + pr_err("Finish remote restore failed.\n"); + goto err; + } err: cr_plugin_fini(CR_PLUGIN_STAGE__RESTORE, ret); return ret; diff --git a/criu/crtools.c b/criu/crtools.c index 9c8064462..3a5ed2197 100644 --- a/criu/crtools.c +++ b/criu/crtools.c @@ -46,6 +46,7 @@ #include "setproctitle.h" #include "sysctl.h" +#include "img-remote.h" int main(int argc, char *argv[], char *envp[]) { @@ -226,6 +227,12 @@ int main(int argc, char *argv[], char *envp[]) if (!strcmp(argv[optind], "page-server")) return cr_page_server(opts.daemon_mode, false, -1) != 0; + if (!strcmp(argv[optind], "image-cache")) + return image_cache(opts.daemon_mode, DEFAULT_CACHE_SOCKET, opts.port); + + if (!strcmp(argv[optind], "image-proxy")) + return image_proxy(opts.daemon_mode, DEFAULT_PROXY_SOCKET, opts.addr, opts.port); + if (!strcmp(argv[optind], "service")) return cr_service(opts.daemon_mode); @@ -265,6 +272,8 @@ int main(int argc, char *argv[], char *envp[]) " criu service []\n" " criu dedup\n" " criu lazy-pages -D DIR []\n" +" criu image-cache []\n" +" criu image-proxy []\n" "\n" "Commands:\n" " dump checkpoint a process/tree identified by pid\n" @@ -276,6 +285,8 @@ int main(int argc, char *argv[], char *envp[]) " dedup remove duplicates in memory dump\n" " cpuinfo dump writes cpu information into image file\n" " cpuinfo check validates cpu information read from image file\n" +" image-proxy launch dump-side proxy to sent images\n" +" image-cache launch restore-side cache to reveive images\n" ); if (usage_error) { @@ -328,6 +339,8 @@ int main(int argc, char *argv[], char *envp[]) " macvlan[IFNAME]:OUTNAME\n" " mnt[COOKIE]:ROOT\n" "\n" +" --remote dump/restore images directly to/from remote node using\n" +" image-proxy/image-cache\n" "* Special resources support:\n" " --" SK_EST_PARAM " checkpoint/restore established TCP connections\n" " --" SK_INFLIGHT_PARAM " skip (ignore) in-flight TCP connections\n" diff --git a/criu/image.c b/criu/image.c index e1740ce94..f6cb7e321 100644 --- a/criu/image.c +++ b/criu/image.c @@ -17,6 +17,7 @@ #include "images/inventory.pb-c.h" #include "images/pagemap.pb-c.h" #include "proc_parse.h" +#include "img-remote.h" #include "namespaces.h" bool ns_per_id = false; @@ -368,6 +369,43 @@ static int img_write_magic(struct cr_img *img, int oflags, int type) return write_img(img, &imgset_template[type].magic); } +int do_open_remote_image(int dfd, char *path, int flags) +{ + char *snapshot_id = NULL; + int ret; + + /* When using namespaces, the current dir is changed so we need to + * change to previous working dir and back to correctly open the image + * proxy and cache sockets. */ + int save = dirfd(opendir(".")); + if (fchdir(get_service_fd(IMG_FD_OFF)) < 0) { + pr_debug("fchdir to dfd failed!\n"); + return -1; + } + + snapshot_id = get_snapshot_id_from_idx(dfd); + + if (snapshot_id == NULL) + ret = -1; + else if (flags == O_RDONLY) { + pr_debug("do_open_remote_image RDONLY path=%s snapshot_id=%s\n", + path, snapshot_id); + ret = read_remote_image_connection(snapshot_id, path); + } else { + pr_debug("do_open_remote_image WDONLY path=%s snapshot_id=%s\n", + path, snapshot_id); + ret = write_remote_image_connection(snapshot_id, path, O_WRONLY); + } + + if (fchdir(save) < 0) { + pr_debug("fchdir to save failed!\n"); + return -1; + } + close(save); + + return ret; +} + struct openat_args { char path[PATH_MAX]; int flags; @@ -393,24 +431,28 @@ static int do_open_image(struct cr_img *img, int dfd, int type, unsigned long of flags = oflags & ~(O_NOBUF | O_SERVICE | O_FORCE_LOCAL); - /* - * For pages images dedup we need to open images read-write on - * restore, that may require proper capabilities, so we ask - * usernsd to do it for us - */ - if (root_ns_mask & CLONE_NEWUSER && - type == CR_FD_PAGES && oflags & O_RDWR) { - struct openat_args pa = { - .flags = flags, - .err = 0, - .mode = CR_FD_PERM, - }; - snprintf(pa.path, PATH_MAX, "%s", path); - ret = userns_call(userns_openat, UNS_FDOUT, &pa, sizeof(struct openat_args), dfd); - if (ret < 0) - errno = pa.err; - } else - ret = openat(dfd, path, flags, CR_FD_PERM); + if (opts.remote && !(oflags & O_FORCE_LOCAL)) + ret = do_open_remote_image(dfd, path, flags); + else { + /* + * For pages images dedup we need to open images read-write on + * restore, that may require proper capabilities, so we ask + * usernsd to do it for us + */ + if (root_ns_mask & CLONE_NEWUSER && + type == CR_FD_PAGES && oflags & O_RDWR) { + struct openat_args pa = { + .flags = flags, + .err = 0, + .mode = CR_FD_PERM, + }; + snprintf(pa.path, PATH_MAX, "%s", path); + ret = userns_call(userns_openat, UNS_FDOUT, &pa, sizeof(struct openat_args), dfd); + if (ret < 0) + errno = pa.err; + } else + ret = openat(dfd, path, flags, CR_FD_PERM); + } if (ret < 0) { if (!(flags & O_CREAT) && (errno == ENOENT || ret == -ENOENT)) { pr_info("No %s image\n", path); @@ -513,7 +555,9 @@ int open_image_dir(char *dir) return -1; fd = ret; - if (opts.img_parent) { + if (opts.remote) { + init_snapshot_id(dir); + } else if (opts.img_parent) { ret = symlinkat(opts.img_parent, fd, CR_PARENT_LINK); if (ret < 0 && errno != EEXIST) { pr_perror("Can't link parent snapshot"); diff --git a/criu/img-remote.c b/criu/img-remote.c new file mode 100644 index 000000000..337cb4a7f --- /dev/null +++ b/criu/img-remote.c @@ -0,0 +1,275 @@ +#include +#include +#include +#include +#include +#include +#include +#include "xmalloc.h" +#include "criu-log.h" +#include "img-remote.h" +#include "img-remote-proto.h" +#include "images/remote-image.pb-c.h" +#include "protobuf-desc.h" +#include +#include "servicefd.h" +#include "common/compiler.h" +#include "cr_options.h" + +#define PB_LOCAL_IMAGE_SIZE PATHLEN + +static char *snapshot_id; +bool restoring = true; + +LIST_HEAD(snapshot_head); + +/* A snapshot is a dump or pre-dump operation. Each snapshot is identified by an + * ID which corresponds to the working directory specefied by the user. + */ +struct snapshot { + char snapshot_id[PATHLEN]; + struct list_head l; +}; + +struct snapshot *new_snapshot(char *snapshot_id) +{ + struct snapshot *s = malloc(sizeof(struct snapshot)); + + if (!s) { + pr_perror("Failed to allocate snapshot structure"); + return NULL; + } + strncpy(s->snapshot_id, snapshot_id, PATHLEN); + return s; +} + +void add_snapshot(struct snapshot *snapshot) +{ + list_add_tail(&(snapshot->l), &snapshot_head); +} + +int read_remote_image_connection(char *snapshot_id, char *path) +{ + int error; + int sockfd = setup_UNIX_client_socket(restoring ? DEFAULT_CACHE_SOCKET: DEFAULT_PROXY_SOCKET); + + if (sockfd < 0) { + pr_perror("Error opening local connection for %s:%s", path, snapshot_id); + return -1; + } + + if (write_header(sockfd, snapshot_id, path, O_RDONLY) < 0) { + pr_perror("Error writing header for %s:%s", path, snapshot_id); + return -1; + } + + if (read_reply_header(sockfd, &error) < 0) { + pr_perror("Error reading reply header for %s:%s", path, snapshot_id); + return -1; + } + if (!error || !strncmp(path, RESTORE_FINISH, sizeof(RESTORE_FINISH))) + return sockfd; + else if (error == ENOENT) { + pr_info("Image does not exist (%s:%s)\n", path, snapshot_id); + close(sockfd); + return -ENOENT; + } + pr_perror("Unexpected error returned: %d (%s:%s)\n", error, path, snapshot_id); + close(sockfd); + return -1; +} + +int write_remote_image_connection(char *snapshot_id, char *path, int flags) +{ + int sockfd = setup_UNIX_client_socket(DEFAULT_PROXY_SOCKET); + + if (sockfd < 0) + return -1; + + if (write_header(sockfd, snapshot_id, path, flags) < 0) { + pr_perror("Error writing header for %s:%s", path, snapshot_id); + return -1; + } + return sockfd; +} + +int finish_remote_dump(void) +{ + pr_info("Dump side is calling finish\n"); + int fd = write_remote_image_connection(NULL_SNAPSHOT_ID, DUMP_FINISH, O_WRONLY); + + if (fd == -1) { + pr_perror("Unable to open finish dump connection"); + return -1; + } + + close(fd); + return 0; +} + +int finish_remote_restore(void) +{ + pr_info("Restore side is calling finish\n"); + int fd = read_remote_image_connection(NULL_SNAPSHOT_ID, RESTORE_FINISH); + + if (fd == -1) { + pr_perror("Unable to open finish restore connection"); + return -1; + } + + close(fd); + return 0; +} + +int skip_remote_bytes(int fd, unsigned long len) +{ + static char buf[4096]; + int n = 0; + unsigned long curr = 0; + + for (; curr < len; ) { + n = read(fd, buf, min(len - curr, (unsigned long)4096)); + if (n == 0) { + pr_perror("Unexpected end of stream (skipping %lx/%lx bytes)", + curr, len); + return -1; + } else if (n > 0) { + curr += n; + } else { + pr_perror("Error while skipping bytes from stream (%lx/%lx)", + curr, len); + return -1; + } + } + + if (curr != len) { + pr_perror("Unable to skip the current number of bytes: %lx instead of %lx", + curr, len); + return -1; + } + return 0; +} + +static int pull_snapshot_ids(void) +{ + int n, sockfd; + SnapshotIdEntry *ls; + struct snapshot *s = NULL; + + sockfd = read_remote_image_connection(NULL_SNAPSHOT_ID, PARENT_IMG); + + /* The connection was successful but there is not file. */ + if (sockfd < 0 && errno == ENOENT) + return 0; + else if (sockfd < 0) { + pr_perror("Unable to open snapshot id read connection"); + return -1; + } + + while (1) { + n = pb_read_obj(sockfd, (void **)&ls, PB_SNAPSHOT_ID); + if (!n) { + close(sockfd); + return n; + } else if (n < 0) { + pr_perror("Unable to read remote snapshot ids"); + close(sockfd); + return n; + } + + s = new_snapshot(ls->snapshot_id); + if (!s) { + pr_perror("Unable create new snapshot structure"); + close(sockfd); + return -1; + } + add_snapshot(s); + pr_info("[read_snapshot ids] parent = %s\n", ls->snapshot_id); + } + free(ls); + close(sockfd); + return n; +} + +int push_snapshot_id(void) +{ + int n; + restoring = false; + SnapshotIdEntry rn = SNAPSHOT_ID_ENTRY__INIT; + int sockfd = write_remote_image_connection(NULL_SNAPSHOT_ID, PARENT_IMG, O_APPEND); + + if (sockfd < 0) { + pr_perror("Unable to open snapshot id push connection"); + return -1; + } + + rn.snapshot_id = xmalloc(sizeof(char) * PATHLEN); + if (!rn.snapshot_id) { + pr_perror("Unable to allocate snapshot id buffer"); + close(sockfd); + return -1; + } + strncpy(rn.snapshot_id, snapshot_id, PATHLEN); + + n = pb_write_obj(sockfd, &rn, PB_SNAPSHOT_ID); + + xfree(rn.snapshot_id); + close(sockfd); + return n; +} + +void init_snapshot_id(char *si) +{ + snapshot_id = si; +} + +char *get_curr_snapshot_id(void) +{ + return snapshot_id; +} + +int get_curr_snapshot_id_idx(void) +{ + struct snapshot *si; + int idx = 0; + + if (list_empty(&snapshot_head)) + pull_snapshot_ids(); + + list_for_each_entry(si, &snapshot_head, l) { + if (!strncmp(si->snapshot_id, snapshot_id, PATHLEN)) + return idx; + idx++; + } + + pr_perror("Error, could not find current snapshot id (%s) fd", snapshot_id); + return -1; +} + +char *get_snapshot_id_from_idx(int idx) +{ + struct snapshot *si; + + if (list_empty(&snapshot_head)) + pull_snapshot_ids(); + + /* Note: if idx is the service fd then we need the current + * snapshot_id idx. Else we need a parent snapshot_id idx. + */ + if (idx == get_service_fd(IMG_FD_OFF)) + idx = get_curr_snapshot_id_idx(); + + list_for_each_entry(si, &snapshot_head, l) { + if (!idx) + return si->snapshot_id; + idx--; + } + + pr_perror("Error, could not find snapshot id for idx %d", idx); + return NULL; +} + +int get_curr_parent_snapshot_id_idx(void) +{ + return get_curr_snapshot_id_idx() - 1; +} diff --git a/criu/include/cr_options.h b/criu/include/cr_options.h index 9f152fac0..8ddbf2341 100644 --- a/criu/include/cr_options.h +++ b/criu/include/cr_options.h @@ -135,6 +135,7 @@ struct cr_options { int weak_sysctls; int status_fd; bool orphan_pts_master; + int remote; pid_t tree_id; int log_level; char *imgs_dir; diff --git a/criu/include/img-remote.h b/criu/include/img-remote.h new file mode 100644 index 000000000..38acbd26d --- /dev/null +++ b/criu/include/img-remote.h @@ -0,0 +1,83 @@ +#include +#include + +#ifndef IMAGE_REMOTE_H +#define IMAGE_REMOTE_H + +#define PATHLEN PATH_MAX +#define DUMP_FINISH "DUMP_FINISH" +#define RESTORE_FINISH "RESTORE_FINISH" +#define PARENT_IMG "parent" +#define NULL_SNAPSHOT_ID "null" +#define DEFAULT_CACHE_SOCKET "img-cache.sock" +#define DEFAULT_PROXY_SOCKET "img-proxy.sock" +#define DEFAULT_CACHE_PORT 9996 +#define DEFAULT_CACHE_HOST "localhost" + +/* Called by restore to get the fd correspondent to a particular path. This call + * will block until the connection is received. + */ +int read_remote_image_connection(char *snapshot_id, char *path); + +/* Called by dump to create a socket connection to the restore side. The socket + * fd is returned for further writing operations. + */ +int write_remote_image_connection(char *snapshot_id, char *path, int flags); + +/* Called by dump/restore when everything is dumped/restored. This function + * creates a new connection with a special control name. The receiver side uses + * it to ack that no more files are coming. + */ +int finish_remote_dump(); +int finish_remote_restore(); + +/* Starts an image proxy daemon (dump side). It receives image files through + * socket connections and forwards them to the image cache (restore side). + */ +int image_proxy(bool background, char *local_proxy_path, char *cache_host, unsigned short cache_port); + +/* Starts an image cache daemon (restore side). It receives image files through + * socket connections and caches them until they are requested by the restore + * process. + */ +int image_cache(bool background, char *local_cache_path, unsigned short cache_port); + +/* Reads (discards) 'len' bytes from fd. This is used to emulate the function + * lseek, which is used to advance the file needle. + */ +int skip_remote_bytes(int fd, unsigned long len); + +/* To support iterative migration, the concept of snapshot_id is introduced + * (only when remote migration is enabled). Each image is tagged with one + * snapshot_id. The snapshot_id is the image directory used for the operation + * that creates the image (either predump or dump). Images stored in memory + * (both in Image Proxy and Image Cache) are identified by their name and + * snapshot_id. Snapshot_ids are ordered so that we can find parent pagemaps + * (that will be used when restoring the process). + */ + +/* Sets the current snapshot_id */ +void init_snapshot_id(char *ns); + +/* Returns the current snapshot_id. */ +char *get_curr_snapshot_id(); + +/* Returns the snapshot_id index representing the current snapshot_id. This + * index represents the hierarchy position. For example: images tagged with + * the snapshot_id with index 1 are more recent than the images tagged with + * the snapshot_id with index 0. + */ +int get_curr_snapshot_id_idx(); + +/* Returns the snapshot_id associated with the snapshot_id index. */ +char *get_snapshot_id_from_idx(int idx); + +/* Pushes the current snapshot_id into the snapshot_id hierarchy (into the Image + * Proxy and Image Cache). + */ +int push_snapshot_id(); + +/* Returns the snapshot id index that preceeds the current snapshot_id. */ +int get_curr_parent_snapshot_id_idx(); + +#endif diff --git a/criu/include/protobuf-desc.h b/criu/include/protobuf-desc.h index 31f5b9a79..696a5800b 100644 --- a/criu/include/protobuf-desc.h +++ b/criu/include/protobuf-desc.h @@ -61,6 +61,10 @@ enum { PB_AUTOFS, PB_GHOST_CHUNK, PB_FILE, + PB_REMOTE_IMAGE, /* Header for images sent from proxy to cache.*/ + PB_LOCAL_IMAGE, /* Header for reading/writing images from/to proxy or cache. */ + PB_LOCAL_IMAGE_REPLY, /* Header for reading/writing images reply. */ + PB_SNAPSHOT_ID, /* Contains a single id. Used for reading/writing ids from proxy or cache. */ /* PB_AUTOGEN_STOP */ diff --git a/criu/page-xfer.c b/criu/page-xfer.c index e3c9c7b25..df1572403 100644 --- a/criu/page-xfer.c +++ b/criu/page-xfer.c @@ -21,6 +21,7 @@ #include "parasite-syscall.h" #include "rst_info.h" #include "stats.h" +#include "img-remote.h" static int page_server_sk = -1; @@ -357,13 +358,29 @@ static int open_page_local_xfer(struct page_xfer *xfer, int fd_type, unsigned lo int pfd; int pr_flags = (fd_type == CR_FD_PAGEMAP) ? PR_TASK : PR_SHMEM; - pfd = openat(get_service_fd(IMG_FD_OFF), CR_PARENT_LINK, O_RDONLY); - if (pfd < 0 && errno == ENOENT) - goto out; + + if (opts.remote) { + /* Note: we are replacing a real directory FD for a snapshot_id + * index. Since we need the parent of the current snapshot_id, + * we want the current snapshot_id index minus one. It is + * possible that dfd is already a snapshot_id index. We test it + * by comparing it to the service FD. When opening an image (see + * do_open_image) we convert the snapshot_id index into a real + * snapshot_id. + */ + pfd = get_curr_snapshot_id_idx() - 1; + if (pfd < 0) + goto out; + } else { + pfd = openat(get_service_fd(IMG_FD_OFF), CR_PARENT_LINK, O_RDONLY); + if (pfd < 0 && errno == ENOENT) + goto out; + } xfer->parent = xmalloc(sizeof(*xfer->parent)); if (!xfer->parent) { - close(pfd); + if (!opts.remote) + close(pfd); return -1; } @@ -372,10 +389,12 @@ static int open_page_local_xfer(struct page_xfer *xfer, int fd_type, unsigned lo pr_perror("No parent image found, though parent directory is set"); xfree(xfer->parent); xfer->parent = NULL; - close(pfd); + if (!opts.remote) + close(pfd); goto out; } - close(pfd); + if (!opts.remote) + close(pfd); } out: @@ -507,9 +526,16 @@ int check_parent_local_xfer(int fd_type, unsigned long img_id) struct stat st; int ret, pfd; - pfd = openat(get_service_fd(IMG_FD_OFF), CR_PARENT_LINK, O_RDONLY); - if (pfd < 0 && errno == ENOENT) - return 0; + if (opts.remote) { + pfd = get_curr_parent_snapshot_id_idx(); + pr_err("Unable to get parent snapshot id\n"); + if (pfd == -1) + return -1; + } else { + pfd = openat(get_service_fd(IMG_FD_OFF), CR_PARENT_LINK, O_RDONLY); + if (pfd < 0 && errno == ENOENT) + return 0; + } snprintf(path, sizeof(path), imgset_template[fd_type].fmt, img_id); ret = fstatat(pfd, path, &st, 0); @@ -572,6 +598,8 @@ int check_parent_page_xfer(int fd_type, unsigned long img_id) { if (opts.use_page_server) return check_parent_server_xfer(fd_type, img_id); + else if (opts.remote) + return get_curr_parent_snapshot_id_idx() == -1 ? 0 : 1; else return check_parent_local_xfer(fd_type, img_id); } diff --git a/criu/pagemap.c b/criu/pagemap.c index 4c4e88685..47b6cb91c 100644 --- a/criu/pagemap.c +++ b/criu/pagemap.c @@ -18,6 +18,7 @@ #include "xmalloc.h" #include "protobuf.h" #include "images/pagemap.pb-c.h" +#include "img-remote.h" #ifndef SEEK_DATA #define SEEK_DATA 3 @@ -138,8 +139,12 @@ static void skip_pagemap_pages(struct page_read *pr, unsigned long len) if (!len) return; - if (pagemap_present(pr->pe)) + if (pagemap_present(pr->pe)) { + if (opts.remote) + if (skip_remote_bytes(img_raw_fd(pr->pi), len)) + pr_perror("Error skipping remote bytes"); pr->pi_off += len; + } pr->cvaddr += len; } @@ -156,7 +161,7 @@ static int seek_pagemap(struct page_read *pr, unsigned long vaddr) break; if (vaddr >= start && vaddr < end) { - skip_pagemap_pages(pr, vaddr - pr->cvaddr); + skip_pagemap_pages(pr, vaddr > pr->cvaddr ? vaddr - pr->cvaddr : 0); return 1; } @@ -388,7 +393,7 @@ static int maybe_read_page_local(struct page_read *pr, unsigned long vaddr, * for us for urgent async read, just do the regular * cached read. */ - if ((flags & (PR_ASYNC|PR_ASAP)) == PR_ASYNC) + if ((flags & (PR_ASYNC|PR_ASAP)) == PR_ASYNC && !opts.remote) ret = pagemap_enqueue_iovec(pr, buf, len, &pr->async); else { ret = read_local_page(pr, vaddr, len, buf); @@ -595,9 +600,24 @@ static int try_open_parent(int dfd, unsigned long id, struct page_read *pr, int int pfd, ret; struct page_read *parent = NULL; - pfd = openat(dfd, CR_PARENT_LINK, O_RDONLY); - if (pfd < 0 && errno == ENOENT) - goto out; + if (opts.remote) { + /* Note: we are replacing a real directory FD for a snapshot_id + * index. Since we need the parent of the current snapshot_id, + * we want the current snapshot_id index minus one. It is + * possible that dfd is already a snapshot_id index. We test it + * by comparing it to the service FD. When opening an image (see + * do_open_image) we convert the snapshot_id index into a real + * snapshot_id. + */ + pfd = dfd == get_service_fd(IMG_FD_OFF) ? + get_curr_snapshot_id_idx() - 1 : dfd - 1; + if (pfd < 0) + goto out; + } else { + pfd = openat(dfd, CR_PARENT_LINK, O_RDONLY); + if (pfd < 0 && errno == ENOENT) + goto out; + } parent = xmalloc(sizeof(*parent)); if (!parent) @@ -612,7 +632,8 @@ static int try_open_parent(int dfd, unsigned long id, struct page_read *pr, int parent = NULL; } - close(pfd); + if (!opts.remote) + close(pfd); out: pr->parent = parent; return 0; @@ -620,7 +641,8 @@ static int try_open_parent(int dfd, unsigned long id, struct page_read *pr, int err_free: xfree(parent); err_cl: - close(pfd); + if (!opts.remote) + close(pfd); return -1; } @@ -651,7 +673,18 @@ static int init_pagemaps(struct page_read *pr) off_t fsize; int nr_pmes, nr_realloc; - fsize = img_raw_size(pr->pmi); + if (!opts.remote) + fsize = img_raw_size(pr->pmi); + else + /* + * FIXME - There is no easy way to estimate the size of the + * pagemap that is still to be read from the socket. Possible + * solution is to ask Image Proxy or Image Cache about the size + * of the image. 1024 is a wild guess (more space is allocated + * if needed). + */ + fsize = 1024; + if (fsize < 0) return -1; diff --git a/criu/protobuf-desc.c b/criu/protobuf-desc.c index 41c208037..bfe00c561 100644 --- a/criu/protobuf-desc.c +++ b/criu/protobuf-desc.c @@ -62,6 +62,7 @@ #include "images/seccomp.pb-c.h" #include "images/binfmt-misc.pb-c.h" #include "images/autofs.pb-c.h" +#include "images/remote-image.pb-c.h" struct cr_pb_message_desc cr_pb_descs[PB_MAX]; diff --git a/criu/util.c b/criu/util.c index 34b8d7911..daf051058 100644 --- a/criu/util.c +++ b/criu/util.c @@ -425,7 +425,7 @@ int copy_file(int fd_in, int fd_out, size_t bytes) ssize_t ret; while (1) { - if (false) { + if (opts.remote) { ret = read(fd_in, buffer, chunk); if (ret < 0) { pr_perror("Can't read from fd_in\n"); diff --git a/images/Makefile b/images/Makefile index edaab0633..4de6990b4 100644 --- a/images/Makefile +++ b/images/Makefile @@ -63,6 +63,7 @@ proto-obj-y += sysctl.o proto-obj-y += autofs.o proto-obj-y += macvlan.o proto-obj-y += sit.o +proto-obj-y += remote-image.o CFLAGS += -iquote $(obj)/ diff --git a/images/remote-image.proto b/images/remote-image.proto new file mode 100644 index 000000000..1212627e3 --- /dev/null +++ b/images/remote-image.proto @@ -0,0 +1,20 @@ +message local_image_entry { + required string name = 1; + required string snapshot_id = 2; + required uint32 open_mode = 3; +} + +message remote_image_entry { + required string name = 1; + required string snapshot_id = 2; + required uint32 open_mode = 3; + required uint64 size = 4; +} + +message local_image_reply_entry { + required uint32 error = 1; +} + +message snapshot_id_entry { + required string snapshot_id = 1; +} From bc22231bd992ab7a4b7e6c9dc693b6172e2916cd Mon Sep 17 00:00:00 2001 From: "rbruno@gsd.inesc-id.pt" Date: Sat, 11 Feb 2017 04:34:44 +0100 Subject: [PATCH 004/249] Process Migration using Sockets (p2) The current patch brings the implementation of the image proxy and image cache. These components are necessary to perform in-memory live migration of processes using CRIU. The image proxy receives images from CRIU Dump/Pre-Dump (through UNIX sockets) and forwards them to the image cache (through a TCP socket). The image cache caches image in memory and sends them to CRIU Restore (through UNIX sockets) when requested. Signed-off-by: Rodrigo Bruno Signed-off-by: Pavel Emelyanov --- criu/img-cache.c | 154 +++++++ criu/img-proxy.c | 90 ++++ criu/img-remote-proto.c | 742 ++++++++++++++++++++++++++++++++ criu/include/criu-log.h | 2 + criu/include/img-remote-proto.h | 88 ++++ criu/shmem.c | 2 +- 6 files changed, 1077 insertions(+), 1 deletion(-) create mode 100644 criu/img-cache.c create mode 100644 criu/img-proxy.c create mode 100644 criu/img-remote-proto.c create mode 100644 criu/include/img-remote-proto.h diff --git a/criu/img-cache.c b/criu/img-cache.c new file mode 100644 index 000000000..293597088 --- /dev/null +++ b/criu/img-cache.c @@ -0,0 +1,154 @@ +#include + +#include "img-remote-proto.h" +#include "criu-log.h" +#include +#include +#include +#include +#include "cr_options.h" + +static struct rimage *wait_for_image(struct wthread *wt) +{ + struct rimage *result; + + if (!strncmp(wt->path, RESTORE_FINISH, sizeof(RESTORE_FINISH))) { + finished = true; + shutdown(local_req_fd, SHUT_RD); + return NULL; + } + + result = get_rimg_by_name(wt->snapshot_id, wt->path); + if (result != NULL && result->size > 0) + return result; + + /* The file does not exist and we do not expect new files */ + if (finished && !is_receiving()) + return NULL; + + /* NOTE: at this point, when the thread wakes up, either the image is + * already in memory or it will never come (the dump is finished). + */ + sem_wait(&(wt->wakeup_sem)); + result = get_rimg_by_name(wt->snapshot_id, wt->path); + if (result != NULL && result->size > 0) + return result; + else + return NULL; +} + +/* The image cache creates a thread that calls this function. It waits for remote + * images from the image-cache. + */ +void *accept_remote_image_connections(void *port) +{ + int fd = *((int *) port); + struct sockaddr_in cli_addr; + socklen_t clilen = sizeof(cli_addr); + char snapshot_id_buf[PATHLEN], path_buf[PATHLEN]; + uint64_t size; + int64_t ret; + int flags, proxy_fd; + struct rimage *rimg; + + proxy_fd = accept(fd, (struct sockaddr *) &cli_addr, &clilen); + if (proxy_fd < 0) { + pr_perror("Unable to accept remote image connection from image proxy"); + return NULL; + } + while (1) { + ret = read_remote_header(proxy_fd, snapshot_id_buf, path_buf, &flags, &size); + if (ret < 0) { + pr_perror("Unable to receive remote header from image proxy"); + return NULL; + } + /* This means that the no more images are coming. */ + else if (!ret) { + pr_info("Image Proxy connection closed.\n"); + finished = true; + unlock_workers(); + return NULL; + } + + pr_info("Received %s request for %s:%s\n", + flags == O_RDONLY ? "read" : + flags == O_APPEND ? "append" : "write", + path_buf, snapshot_id_buf); + + rimg = prepare_remote_image(path_buf, snapshot_id_buf, flags); + + prepare_recv_rimg(); + if (!size) + ret = 0; + else + ret = recv_image(proxy_fd, rimg, size, flags, false); + if (ret < 0) { + pr_perror("Unable to receive %s:%s from image proxy", + rimg->path, rimg->snapshot_id); + finalize_recv_rimg(NULL); + return NULL; + } else if (ret != size) { + pr_perror("Unable to receive %s:%s from image proxy (received %ld bytes, expected %lu bytes)", + rimg->path, rimg->snapshot_id, (long)ret, (unsigned long)size); + finalize_recv_rimg(NULL); + return NULL; + } + finalize_recv_rimg(rimg); + + pr_info("Finished receiving %s:%s (received %ld bytes)\n", + rimg->path, rimg->snapshot_id, (long)ret); + } +} + +int image_cache(bool background, char *local_cache_path, unsigned short cache_write_port) +{ + pthread_t local_req_thr, remote_req_thr; + + pr_info("Proxy to Cache Port %d, CRIU to Cache Path %s\n", + cache_write_port, local_cache_path); + + + if (opts.ps_socket != -1) { + proxy_to_cache_fd = opts.ps_socket; + pr_info("Re-using ps socket %d\n", proxy_to_cache_fd); + } else { + proxy_to_cache_fd = setup_TCP_server_socket(cache_write_port); + if (proxy_to_cache_fd < 0) { + pr_perror("Unable to open proxy to cache TCP socket"); + return -1; + } + } + + local_req_fd = setup_UNIX_server_socket(local_cache_path); + if (local_req_fd < 0) { + pr_perror("Unable to open cache to proxy UNIX socket"); + return -1; + } + + if (init_daemon(background, wait_for_image)) { + pr_perror("Unable to initialize daemon"); + return -1; + } + + if (pthread_create( + &remote_req_thr, + NULL, accept_remote_image_connections, + (void *) &proxy_to_cache_fd)) { + pr_perror("Unable to create remote requests thread"); + return -1; + } + if (pthread_create( + &local_req_thr, + NULL, + accept_local_image_connections, + (void *) &local_req_fd)) { + pr_perror("Unable to create local requests thread"); + return -1; + } + + pthread_join(remote_req_thr, NULL); + pthread_join(local_req_thr, NULL); + join_workers(); + pr_info("Finished image cache."); + return 0; +} diff --git a/criu/img-proxy.c b/criu/img-proxy.c new file mode 100644 index 000000000..58123dccb --- /dev/null +++ b/criu/img-proxy.c @@ -0,0 +1,90 @@ +#include + +#include "img-remote.h" +#include "img-remote-proto.h" +#include "criu-log.h" +#include +#include +#include +#include "cr_options.h" + +static struct rimage *wait_for_image(struct wthread *wt) +{ + return get_rimg_by_name(wt->snapshot_id, wt->path); +} + +int64_t forward_image(struct rimage *rimg) +{ + int64_t ret; + int fd = proxy_to_cache_fd; + + pthread_mutex_lock(&(rimg->in_use)); + pr_info("Forwarding %s:%s (%lu bytes)\n", + rimg->path, rimg->snapshot_id, (unsigned long)rimg->size); + if (write_remote_header( + fd, rimg->snapshot_id, rimg->path, O_APPEND, rimg->size) < 0) { + pr_perror("Error writing header for %s:%s", + rimg->path, rimg->snapshot_id); + pthread_mutex_unlock(&(rimg->in_use)); + return -1; + } + + ret = send_image(fd, rimg, O_APPEND, false); + if (ret < 0) { + pr_perror("Unable to send %s:%s to image cache", + rimg->path, rimg->snapshot_id); + pthread_mutex_unlock(&(rimg->in_use)); + return -1; + } else if (ret != rimg->size) { + pr_perror("Unable to send %s:%s to image proxy (sent %ld bytes, expected %lu bytes", + rimg->path, rimg->snapshot_id, (long)ret, (unsigned long)rimg->size); + pthread_mutex_unlock(&(rimg->in_use)); + return -1; + } + pr_info("Finished forwarding %s:%s (sent %lu bytes)\n", + rimg->path, rimg->snapshot_id, (unsigned long)rimg->size); + pthread_mutex_unlock(&(rimg->in_use)); + return ret; +} + +int image_proxy(bool background, char *local_proxy_path, char *fwd_host, unsigned short fwd_port) +{ + pthread_t local_req_thr; + + pr_info("CRIU to Proxy Path: %s, Cache Address %s:%hu\n", + local_proxy_path, fwd_host, fwd_port); + + local_req_fd = setup_UNIX_server_socket(local_proxy_path); + if (local_req_fd < 0) { + pr_perror("Unable to open CRIU to proxy UNIX socket"); + return -1; + } + + if (opts.ps_socket != -1) { + proxy_to_cache_fd = opts.ps_socket; + pr_info("Re-using ps socket %d\n", proxy_to_cache_fd); + } else { + proxy_to_cache_fd = setup_TCP_client_socket(fwd_host, fwd_port); + if (proxy_to_cache_fd < 0) { + pr_perror("Unable to open proxy to cache TCP socket"); + return -1; + } + } + + if (init_daemon(background, wait_for_image)) + return -1; + + if (pthread_create( + &local_req_thr, + NULL, + accept_local_image_connections, + (void *) &local_req_fd)) { + pr_perror("Unable to create local requests thread"); + return -1; + } + + pthread_join(local_req_thr, NULL); + join_workers(); + pr_info("Finished image proxy."); + return 0; +} diff --git a/criu/img-remote-proto.c b/criu/img-remote-proto.c new file mode 100644 index 000000000..ad39c42e7 --- /dev/null +++ b/criu/img-remote-proto.c @@ -0,0 +1,742 @@ +#include +#include + +#include +#include +#include +#include +#include "sys/un.h" +#include +#include +#include + +#include "img-remote-proto.h" +#include "criu-log.h" +#include "common/compiler.h" + +#include "protobuf.h" +#include "images/remote-image.pb-c.h" +#include "image.h" + +LIST_HEAD(rimg_head); +pthread_mutex_t rimg_lock; + +pthread_mutex_t proxy_to_cache_lock; + +LIST_HEAD(workers_head); +pthread_mutex_t workers_lock; +sem_t workers_semph; + +struct rimage * (*wait_for_image) (struct wthread *wt); + +bool finished = false; +int writing = 0; +int forwarding = 0; +int proxy_to_cache_fd; +int local_req_fd; + +struct rimage *get_rimg_by_name(const char *snapshot_id, const char *path) +{ + struct rimage *rimg = NULL; + + pthread_mutex_lock(&rimg_lock); + list_for_each_entry(rimg, &rimg_head, l) { + if (!strncmp(rimg->path, path, PATHLEN) && + !strncmp(rimg->snapshot_id, snapshot_id, PATHLEN)) { + pthread_mutex_unlock(&rimg_lock); + return rimg; + } + } + pthread_mutex_unlock(&rimg_lock); + return NULL; +} + +static struct wthread *get_wt_by_name(const char *snapshot_id, const char *path) +{ + struct wthread *wt = NULL; + + pthread_mutex_lock(&workers_lock); + list_for_each_entry(wt, &workers_head, l) { + if (!strncmp(wt->path, path, PATHLEN) && + !strncmp(wt->snapshot_id, snapshot_id, PATHLEN)) { + pthread_mutex_unlock(&workers_lock); + return wt; + } + } + pthread_mutex_unlock(&workers_lock); + return NULL; +} + +static int init_sync_structures(void) +{ + if (pthread_mutex_init(&rimg_lock, NULL) != 0) { + pr_perror("Remote image list mutex init failed"); + return -1; + } + + if (pthread_mutex_init(&proxy_to_cache_lock, NULL) != 0) { + pr_perror("Remote image connection mutex init failed"); + return -1; + } + + if (pthread_mutex_init(&workers_lock, NULL) != 0) { + pr_perror("Workers mutex init failed"); + return -1; + } + + if (sem_init(&workers_semph, 0, 0) != 0) { + pr_perror("Workers semaphore init failed"); + return -1; + } + + return 0; +} + +void prepare_recv_rimg(void) +{ + pthread_mutex_lock(&rimg_lock); + writing++; + pthread_mutex_unlock(&rimg_lock); +} + +void finalize_recv_rimg(struct rimage *rimg) +{ + + pthread_mutex_lock(&rimg_lock); + + if (rimg) + list_add_tail(&(rimg->l), &rimg_head); + writing--; + pthread_mutex_unlock(&rimg_lock); + /* Wake thread waiting for this image. */ + if (rimg) { + struct wthread *wt = get_wt_by_name(rimg->snapshot_id, rimg->path); + if (wt) + sem_post(&(wt->wakeup_sem)); + } +} + +bool is_receiving(void) +{ + int ret; + + pthread_mutex_lock(&rimg_lock); + ret = writing; + pthread_mutex_unlock(&rimg_lock); + return ret > 0; +} + +static void prepare_fwd_rimg(void) +{ + pthread_mutex_lock(&rimg_lock); + forwarding++; + pthread_mutex_unlock(&rimg_lock); +} + +static void finalize_fwd_rimg(void) +{ + pthread_mutex_lock(&rimg_lock); + forwarding--; + pthread_mutex_unlock(&rimg_lock); +} + +static bool is_forwarding(void) +{ + int ret; + + pthread_mutex_lock(&rimg_lock); + ret = forwarding; + pthread_mutex_unlock(&rimg_lock); + return ret > 0; +} + +/* This function is called when no more images are coming. Threads still waiting + * for images will be awaken to send a ENOENT (no such file) to the requester. + */ +void unlock_workers(void) +{ + struct wthread *wt = NULL; + + pthread_mutex_lock(&workers_lock); + list_for_each_entry(wt, &workers_head, l) + sem_post(&(wt->wakeup_sem)); + pthread_mutex_unlock(&workers_lock); +} + +int init_daemon(bool background, struct rimage *(*wfi)(struct wthread*)) +{ + if (background) { + if (daemon(1, 0) == -1) { + pr_perror("Can't run service server in the background"); + return -1; + } + } + wait_for_image = wfi; + return init_sync_structures(); +} + +int setup_TCP_server_socket(int port) +{ + struct sockaddr_in serv_addr; + int sockopt = 1; + int sockfd = socket(AF_INET, SOCK_STREAM, 0); + + if (sockfd < 0) { + pr_perror("Unable to open image socket"); + return -1; + } + + bzero((char *) &serv_addr, sizeof(serv_addr)); + serv_addr.sin_family = AF_INET; + serv_addr.sin_addr.s_addr = INADDR_ANY; + serv_addr.sin_port = htons(port); + + if (setsockopt( + sockfd, SOL_SOCKET, SO_REUSEADDR, &sockopt, sizeof(sockopt)) == -1) { + pr_perror("Unable to set SO_REUSEADDR"); + return -1; + } + + if (bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) { + pr_perror("Unable to bind image socket"); + return -1; + } + + if (listen(sockfd, DEFAULT_LISTEN)) { + pr_perror("Unable to listen image socket"); + return -1; + } + + return sockfd; +} + +int setup_TCP_client_socket(char *hostname, int port) +{ + int sockfd; + struct sockaddr_in serv_addr; + struct hostent *server; + + sockfd = socket(AF_INET, SOCK_STREAM, 0); + if (sockfd < 0) { + pr_perror("Unable to open remote image socket"); + return -1; + } + + server = gethostbyname(hostname); + if (server == NULL) { + pr_perror("Unable to get host by name (%s)", hostname); + return -1; + } + + bzero((char *) &serv_addr, sizeof(serv_addr)); + serv_addr.sin_family = AF_INET; + bcopy((char *) server->h_addr, + (char *) &serv_addr.sin_addr.s_addr, + server->h_length); + serv_addr.sin_port = htons(port); + + if (connect(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) { + pr_perror("Unable to connect to remote %s", hostname); + return -1; + } + + return sockfd; +} + +int setup_UNIX_server_socket(char *path) +{ + struct sockaddr_un addr; + int sockfd = socket(AF_UNIX, SOCK_STREAM, 0); + + if (sockfd < 0) { + pr_perror("Unable to open image socket"); + return -1; + } + + memset(&addr, 0, sizeof(addr)); + addr.sun_family = AF_UNIX; + strncpy(addr.sun_path, path, sizeof(addr.sun_path)-1); + + unlink(path); + + if (bind(sockfd, (struct sockaddr *)&addr, sizeof(addr)) == -1) { + pr_perror("Unable to bind image socket"); + return -1; + } + + if (listen(sockfd, 50) == -1) { + pr_perror("Unable to listen image socket"); + return -1; + } + + return sockfd; +} + +int setup_UNIX_client_socket(char *path) +{ + struct sockaddr_un addr; + int sockfd = socket(AF_UNIX, SOCK_STREAM, 0); + + if (sockfd < 0) { + pr_perror("Unable to open local image socket"); + return -1; + } + + memset(&addr, 0, sizeof(addr)); + addr.sun_family = AF_UNIX; + strncpy(addr.sun_path, path, sizeof(addr.sun_path)-1); + + if (connect(sockfd, (struct sockaddr *)&addr, sizeof(addr)) < 0) { + pr_perror("Unable to connect to local socket: %s", path); + close(sockfd); + return -1; + } + + return sockfd; +} + +int64_t pb_write_obj(int fd, void *obj, int type) +{ + struct cr_img img; + + img._x.fd = fd; + bfd_setraw(&img._x); + return pb_write_one(&img, obj, type); +} + +int64_t pb_read_obj(int fd, void **pobj, int type) +{ + struct cr_img img; + + img._x.fd = fd; + bfd_setraw(&img._x); + return do_pb_read_one(&img, pobj, type, true); +} + +int64_t write_header(int fd, char *snapshot_id, char *path, int flags) +{ + LocalImageEntry li = LOCAL_IMAGE_ENTRY__INIT; + + li.name = path; + li.snapshot_id = snapshot_id; + li.open_mode = flags; + return pb_write_obj(fd, &li, PB_LOCAL_IMAGE); +} + +int64_t write_reply_header(int fd, int error) +{ + LocalImageReplyEntry lir = LOCAL_IMAGE_REPLY_ENTRY__INIT; + + lir.error = error; + return pb_write_obj(fd, &lir, PB_LOCAL_IMAGE_REPLY); +} + +int64_t write_remote_header(int fd, char *snapshot_id, char *path, int flags, uint64_t size) +{ + RemoteImageEntry ri = REMOTE_IMAGE_ENTRY__INIT; + + ri.name = path; + ri.snapshot_id = snapshot_id; + ri.open_mode = flags; + ri.size = size; + return pb_write_obj(fd, &ri, PB_REMOTE_IMAGE); +} + +int64_t read_header(int fd, char *snapshot_id, char *path, int *flags) +{ + LocalImageEntry *li; + int ret = pb_read_obj(fd, (void **)&li, PB_LOCAL_IMAGE); + + if (ret > 0) { + strncpy(snapshot_id, li->snapshot_id, PATHLEN); + strncpy(path, li->name, PATHLEN); + *flags = li->open_mode; + } + free(li); + return ret; +} + +int64_t read_reply_header(int fd, int *error) +{ + LocalImageReplyEntry *lir; + int ret = pb_read_obj(fd, (void **)&lir, PB_LOCAL_IMAGE_REPLY); + + if (ret > 0) + *error = lir->error; + free(lir); + return ret; +} + +int64_t read_remote_header(int fd, char *snapshot_id, char *path, int *flags, uint64_t *size) +{ + RemoteImageEntry *ri; + int ret = pb_read_obj(fd, (void **)&ri, PB_REMOTE_IMAGE); + + if (ret > 0) { + strncpy(snapshot_id, ri->snapshot_id, PATHLEN); + strncpy(path, ri->name, PATHLEN); + *flags = ri->open_mode; + *size = ri->size; + } + free(ri); + return ret; +} + +static struct wthread *new_worker(void) +{ + struct wthread *wt = malloc(sizeof(struct wthread)); + + if (!wt) { + pr_perror("Unable to allocate worker thread structure"); + return NULL; + } + if (sem_init(&(wt->wakeup_sem), 0, 0) != 0) { + pr_perror("Workers semaphore init failed"); + return NULL; + } + return wt; +} + +static void add_worker(struct wthread *wt) +{ + pthread_mutex_lock(&workers_lock); + list_add_tail(&(wt->l), &workers_head); + pthread_mutex_unlock(&workers_lock); + sem_post(&workers_semph); +} + +void join_workers(void) +{ + struct wthread *wthread = NULL; + + while (! list_empty(&workers_head)) { + wthread = list_entry(workers_head.next, struct wthread, l); + pthread_join(wthread->tid, NULL); + list_del(&(wthread->l)); + free(wthread); + } +} + +static struct rimage *new_remote_image(char *path, char *snapshot_id) +{ + struct rimage *rimg = malloc(sizeof(struct rimage)); + struct rbuf *buf = malloc(sizeof(struct rbuf)); + + if (rimg == NULL) { + pr_perror("Unable to allocate remote_image structures"); + return NULL; + } + + if (buf == NULL) { + pr_perror("Unable to allocate remote_buffer structures"); + return NULL; + } + + strncpy(rimg->path, path, PATHLEN); + strncpy(rimg->snapshot_id, snapshot_id, PATHLEN); + rimg->size = 0; + buf->nbytes = 0; + INIT_LIST_HEAD(&(rimg->buf_head)); + list_add_tail(&(buf->l), &(rimg->buf_head)); + rimg->curr_sent_buf = list_entry(rimg->buf_head.next, struct rbuf, l); + rimg->curr_sent_bytes = 0; + + if (pthread_mutex_init(&(rimg->in_use), NULL) != 0) { + pr_perror("Remote image in_use mutex init failed"); + return NULL; + } + return rimg; +} + +/* Clears a remote image struct for reusing it. */ +static struct rimage *clear_remote_image(struct rimage *rimg) +{ + pthread_mutex_lock(&(rimg->in_use)); + + while (!list_is_singular(&(rimg->buf_head))) { + struct rbuf *buf = list_entry(rimg->buf_head.prev, struct rbuf, l); + + list_del(rimg->buf_head.prev); + free(buf); + } + + list_entry(rimg->buf_head.next, struct rbuf, l)->nbytes = 0; + rimg->size = 0; + rimg->curr_sent_buf = list_entry(rimg->buf_head.next, struct rbuf, l); + rimg->curr_sent_bytes = 0; + + pthread_mutex_unlock(&(rimg->in_use)); + + return rimg; +} + +struct rimage *prepare_remote_image(char *path, char *snapshot_id, int open_mode) +{ + struct rimage *rimg = get_rimg_by_name(snapshot_id, path); + /* There is no record of such image, create a new one. */ + + if (rimg == NULL) + return new_remote_image(path, snapshot_id); + + pthread_mutex_lock(&rimg_lock); + list_del(&(rimg->l)); + pthread_mutex_unlock(&rimg_lock); + + /* There is already an image record. Simply return it for appending. */ + if (open_mode == O_APPEND) + return rimg; + /* There is already an image record. Clear it for writing. */ + else + return clear_remote_image(rimg); +} + +void *process_local_read(struct wthread *wt) +{ + struct rimage *rimg = NULL; + int64_t ret; + /* TODO - split wait_for_image + * in cache - improve the parent stuf + * in proxy - do not wait for anything, return no file + */ + rimg = wait_for_image(wt); + if (!rimg) { + pr_info("No image %s:%s.\n", wt->path, wt->snapshot_id); + if (write_reply_header(wt->fd, ENOENT) < 0) + pr_perror("Error writing reply header for unexisting image"); + close(wt->fd); + return NULL; + } else { + if (write_reply_header(wt->fd, 0) < 0) { + pr_perror("Error writing reply header for %s:%s", + wt->path, wt->snapshot_id); + close(wt->fd); + return NULL; + } + } + + pthread_mutex_lock(&(rimg->in_use)); + ret = send_image(wt->fd, rimg, wt->flags, true); + if (ret < 0) + pr_perror("Unable to send %s:%s to CRIU (sent %ld bytes)", + rimg->path, rimg->snapshot_id, (long)ret); + else + pr_info("Finished sending %s:%s to CRIU (sent %ld bytes)\n", + rimg->path, rimg->snapshot_id, (long)ret); + pthread_mutex_unlock(&(rimg->in_use)); + return NULL; +} + +static void *process_local_image_connection(void *ptr) +{ + struct wthread *wt = (struct wthread *) ptr; + struct rimage *rimg = NULL; + int64_t ret; + + /* NOTE: the code inside this if is shared for both cache and proxy. */ + if (wt->flags == O_RDONLY) + return process_local_read(wt); + + /* NOTE: IMAGE PROXY ONLY. The image cache receives write connections + * through TCP (see accept_remote_image_connections). + */ + rimg = prepare_remote_image(wt->path, wt->snapshot_id, wt->flags); + ret = recv_image(wt->fd, rimg, 0, wt->flags, true); + if (ret < 0) { + pr_perror("Unable to receive %s:%s to CRIU (received %ld bytes)", + rimg->path, rimg->snapshot_id, (long)ret); + finalize_recv_rimg(NULL); + return NULL; + } + finalize_recv_rimg(rimg); + pr_info("Finished receiving %s:%s (received %ld bytes)\n", + rimg->path, rimg->snapshot_id, (long)ret); + + + if (!strncmp(rimg->path, DUMP_FINISH, sizeof(DUMP_FINISH))) { + finished = true; + shutdown(local_req_fd, SHUT_RD); + } else { + pthread_mutex_lock(&proxy_to_cache_lock); + ret = forward_image(rimg); + pthread_mutex_unlock(&proxy_to_cache_lock); + } + + finalize_fwd_rimg(); + if (ret < 0) { + pr_perror("Unable to forward %s:%s to Image Cache", + rimg->path, rimg->snapshot_id); + + return NULL; + } + + if (finished && !is_forwarding() && !is_receiving()) { + pr_info("Closing connection to Image Cache.\n"); + close(proxy_to_cache_fd); + unlock_workers(); + } + return NULL; +} + + +void *accept_local_image_connections(void *port) +{ + int fd = *((int *) port); + int cli_fd; + struct sockaddr_in cli_addr; + + socklen_t clilen = sizeof(cli_addr); + pthread_t tid; + struct wthread *wt; + + while (1) { + cli_fd = accept(fd, (struct sockaddr *) &cli_addr, &clilen); + if (cli_fd < 0) { + if (!finished) + pr_err("Unable to accept local image connection"); + close(cli_fd); + return NULL; + } + + wt = new_worker(); + wt->fd = cli_fd; + + if (read_header(wt->fd, wt->snapshot_id, wt->path, &(wt->flags)) < 0) { + pr_perror("Error reading local image header"); + return NULL; + } + + pr_info("Received %s request for %s:%s\n", + wt->flags == O_RDONLY ? "read" : + wt->flags == O_APPEND ? "append" : "write", + wt->path, wt->snapshot_id); + + /* These function calls are used to avoid other threads from + * thinking that there are no more images are coming. + */ + if (wt->flags != O_RDONLY) { + prepare_recv_rimg(); + prepare_fwd_rimg(); + } + + /* We need to flock the last pid file to avoid stealing pids + * from restore. + */ + int fd = open_proc_rw(PROC_GEN, LAST_PID_PATH); + if (fd < 0) { + pr_perror("Can't open %s", LAST_PID_PATH); + } + + if (flock(fd, LOCK_EX)) { + close(fd); + pr_perror("Can't lock %s", LAST_PID_PATH); + return NULL; + } + + if (pthread_create( + &tid, NULL, process_local_image_connection, (void *) wt)) { + pr_perror("Unable to create worker thread"); + return NULL; + } + + if (flock(fd, LOCK_UN)) + pr_perror("Can't unlock %s", LAST_PID_PATH); + close(fd); + + wt->tid = tid; + add_worker(wt); + } +} + +/* Note: size is a limit on how much we want to read from the socket. Zero means + * read until the socket is closed. + */ +int64_t recv_image(int fd, struct rimage *rimg, uint64_t size, int flags, bool close_fd) +{ + struct rbuf *curr_buf = NULL; + int n; + + if (flags == O_APPEND) + curr_buf = list_entry(rimg->buf_head.prev, struct rbuf, l); + else + curr_buf = list_entry(rimg->buf_head.next, struct rbuf, l); + + while (1) { + n = read(fd, + curr_buf->buffer + curr_buf->nbytes, + size ? + min((int) (size - rimg->size), BUF_SIZE - curr_buf->nbytes) : + BUF_SIZE - curr_buf->nbytes); + if (n == 0) { + if (close_fd) + close(fd); + return rimg->size; + } else if (n > 0) { + curr_buf->nbytes += n; + rimg->size += n; + if (curr_buf->nbytes == BUF_SIZE) { + struct rbuf *buf = malloc(sizeof(struct rbuf)); + if (buf == NULL) { + pr_perror("Unable to allocate remote_buffer structures"); + if (close_fd) + close(fd); + return -1; + } + buf->nbytes = 0; + list_add_tail(&(buf->l), &(rimg->buf_head)); + curr_buf = buf; + } + if (size && rimg->size == size) { + if (close_fd) + close(fd); + return rimg->size; + } + } else { + pr_perror("Read on %s:%s socket failed", + rimg->path, rimg->snapshot_id); + if (close_fd) + close(fd); + return -1; + } + } +} + +int64_t send_image(int fd, struct rimage *rimg, int flags, bool close_fd) +{ + + int n, nblocks = 0; + + if (flags != O_APPEND) { + rimg->curr_sent_buf = list_entry(rimg->buf_head.next, struct rbuf, l); + rimg->curr_sent_bytes = 0; + } + + while (1) { + n = send( + fd, + rimg->curr_sent_buf->buffer + rimg->curr_sent_bytes, + min(BUF_SIZE, rimg->curr_sent_buf->nbytes) - rimg->curr_sent_bytes, + MSG_NOSIGNAL); + if (n > -1) { + rimg->curr_sent_bytes += n; + if (rimg->curr_sent_bytes == BUF_SIZE) { + rimg->curr_sent_buf = + list_entry(rimg->curr_sent_buf->l.next, struct rbuf, l); + nblocks++; + rimg->curr_sent_bytes = 0; + } else if (rimg->curr_sent_bytes == rimg->curr_sent_buf->nbytes) { + if (close_fd) + close(fd); + return nblocks*BUF_SIZE + rimg->curr_sent_buf->nbytes; + } + } else if (errno == EPIPE || errno == ECONNRESET) { + pr_warn("Connection for %s:%s was closed early than expected\n", + rimg->path, rimg->snapshot_id); + return 0; + } else { + pr_perror("Write on %s:%s socket failed", + rimg->path, rimg->snapshot_id); + return -1; + } + } + +} diff --git a/criu/include/criu-log.h b/criu/include/criu-log.h index c2a635ba7..21ef54307 100644 --- a/criu/include/criu-log.h +++ b/criu/include/criu-log.h @@ -22,6 +22,8 @@ #include "log.h" +struct timeval; + extern int log_init(const char *output); extern void log_fini(void); extern int log_init_by_pid(pid_t pid); diff --git a/criu/include/img-remote-proto.h b/criu/include/img-remote-proto.h new file mode 100644 index 000000000..13cf6c6d2 --- /dev/null +++ b/criu/include/img-remote-proto.h @@ -0,0 +1,88 @@ +#ifndef IMAGE_REMOTE_PVT_H +#define IMAGE_REMOTE_PVT_H + +#include +#include +#include "common/list.h" +#include "img-remote.h" +#include +#include + +#define DEFAULT_LISTEN 50 +#ifndef PAGESIZE +#define PAGESIZE 4096 +#endif +#define BUF_SIZE PAGESIZE + +struct rbuf { + char buffer[BUF_SIZE]; + int nbytes; /* How many bytes are in the buffer. */ + struct list_head l; +}; + +struct rimage { + char path[PATHLEN]; + char snapshot_id[PATHLEN]; + struct list_head l; + struct list_head buf_head; + /* Used to track already sent buffers when the image is appended. */ + struct rbuf *curr_sent_buf; + /* Similar to the previous field. Number of bytes sent in 'curr_sent_buf'. */ + int curr_sent_bytes; + uint64_t size; /* number of bytes */ + pthread_mutex_t in_use; /* Only one operation at a time, per image. */ +}; + +struct wthread { + pthread_t tid; + struct list_head l; + /* Client fd. */ + int fd; + /* The path and snapshot_id identify the request handled by this thread. */ + char path[PATHLEN]; + char snapshot_id[PATHLEN]; + int flags; + /* This semph is used to wake this thread if the image is in memory.*/ + sem_t wakeup_sem; +}; + +/* This variable is used to indicate when the dump is finished. */ +extern bool finished; +/* This is the proxy to cache TCP socket FD. */ +extern int proxy_to_cache_fd; +/* This the unix socket used to fulfill local requests. */ +extern int local_req_fd; + +int init_daemon(bool background, struct rimage *(*wfi)(struct wthread*)); + +void join_workers(void); +void unlock_workers(void); + +void prepare_recv_rimg(void); +void finalize_recv_rimg(struct rimage *rimg); +struct rimage *prepare_remote_image(char *path, char *namesapce, int flags); +struct rimage *get_rimg_by_name(const char *snapshot_id, const char *path); +bool is_receiving(void); + +void *accept_local_image_connections(void *ptr); +void *accept_remote_image_connections(void *ptr); + +int64_t forward_image(struct rimage *rimg); +int64_t send_image(int fd, struct rimage *rimg, int flags, bool image_check); +int64_t recv_image(int fd, struct rimage *rimg, uint64_t size, int flags, bool image_check); + +int64_t pb_write_obj(int fd, void *obj, int type); +int64_t pb_read_obj(int fd, void **obj, int type); + +int64_t write_header(int fd, char *snapshot_id, char *path, int open_mode); +int64_t read_header(int fd, char *snapshot_id, char *path, int *open_mode); +int64_t write_reply_header(int fd, int error); +int64_t read_reply_header(int fd, int *error); +int64_t read_remote_header(int fd, char *snapshot_id, char *path, int *open_mode, uint64_t *size); +int64_t write_remote_header(int fd, char *snapshot_id, char *path, int open_mode, uint64_t size); + +int setup_TCP_server_socket(int port); +int setup_TCP_client_socket(char *hostname, int port); +int setup_UNIX_client_socket(char *path); +int setup_UNIX_server_socket(char *path); +#endif diff --git a/criu/shmem.c b/criu/shmem.c index 358d848db..a3c7c5caf 100644 --- a/criu/shmem.c +++ b/criu/shmem.c @@ -287,7 +287,7 @@ static int open_shmem_sysv(int pid, struct vma_area *vma) VmaEntry *vme = vma->e; struct shmem_info *si; struct shmem_sysv_att *att; - uint64_t ret_fd; + int64_t ret_fd; si = shmem_find(vme->shmid); if (!si) { From c62b42240ab3270b04b7795f241a49df504b1620 Mon Sep 17 00:00:00 2001 From: "rbruno@gsd.inesc-id.pt" Date: Sat, 11 Feb 2017 04:34:45 +0100 Subject: [PATCH 005/249] zdtm: Add support for image-proxy/image-cache Signed-off-by: Rodrigo Bruno Signed-off-by: Katerina Koukiou Signed-off-by: Pavel Emelyanov --- test/zdtm.py | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/test/zdtm.py b/test/zdtm.py index 3344c0022..5f81e938b 100755 --- a/test/zdtm.py +++ b/test/zdtm.py @@ -955,6 +955,7 @@ def __init__(self, opts): self.__mdedup = (opts['noauto_dedup'] and True or False) self.__user = (opts['user'] and True or False) self.__leave_stopped = (opts['stop'] and True or False) + self.__remote = (opts['remote'] and True or False) self.__criu = (opts['rpc'] and criu_rpc or criu_cli) self.__show_stats = (opts['show_stats'] and True or False) self.__lazy_pages_p = None @@ -1128,6 +1129,27 @@ def dump(self, action, opts = []): a_opts += self.__test.getdopts() + if self.__remote: + logdir = os.getcwd() + "/" + self.__dump_path + "/" + str(self.__iter) + print "Adding image cache" + + cache_opts = [self.__criu_bin, "image-cache", "--port", "12345", "-v4", "-o", + logdir + "/image-cache.log", "-D", logdir] + + subprocess.Popen(cache_opts).pid + time.sleep(1) + + print "Adding image proxy" + + proxy_opts = [self.__criu_bin, "image-proxy", "--port", "12345", "--address", + "localhost", "-v4", "-o", logdir + "/image-proxy.log", + "-D", logdir] + + subprocess.Popen(proxy_opts).pid + time.sleep(1) + + a_opts += ["--remote"] + if self.__dedup: a_opts += ["--auto-dedup"] @@ -1177,6 +1199,9 @@ def restore(self): r_opts += ['--empty-ns', 'net'] r_opts += ['--action-script', os.getcwd() + '/empty-netns-prep.sh'] + if self.__remote: + r_opts += ["--remote"] + if self.__dedup: r_opts += ["--auto-dedup"] @@ -1696,7 +1721,7 @@ def run_test(self, name, desc, flavor): nd = ('nocr', 'norst', 'pre', 'iters', 'page_server', 'sibling', 'stop', 'empty_ns', 'fault', 'keep_img', 'report', 'snaps', 'sat', 'script', 'rpc', 'lazy_pages', 'join_ns', 'dedup', 'sbs', 'freezecg', 'user', 'dry_run', 'noauto_dedup', - 'remote_lazy_pages', 'show_stats', 'lazy_migrate', + 'remote_lazy_pages', 'show_stats', 'lazy_migrate', 'remote', 'criu_bin', 'crit_bin') arg = repr((name, desc, flavor, {d: self.__opts[d] for d in nd})) @@ -2249,6 +2274,7 @@ def clean_stuff(opts): rp.add_argument("--rpc", help = "Run CRIU via RPC rather than CLI", action = 'store_true') rp.add_argument("--page-server", help = "Use page server dump", action = 'store_true') +rp.add_argument("--remote", help = "Use remote option for diskless C/R", action = 'store_true') rp.add_argument("-p", "--parallel", help = "Run test in parallel") rp.add_argument("--dry-run", help="Don't run tests, just pretend to", action='store_true') rp.add_argument("--script", help="Add script to get notified by criu") From cf9572f7e37424c2023b611b53c014fe4edec707 Mon Sep 17 00:00:00 2001 From: Dmitry Safonov Date: Tue, 7 Mar 2017 15:55:10 +0300 Subject: [PATCH 006/249] images: add proto2 syntax specification to remote-image.proto To suppress protobuf's warning: > [libprotobuf WARNING google/protobuf/compiler/parser.cc:546] > No syntax specified for the proto file: remote-image.proto. > Please use 'syntax = "proto2";' or 'syntax = "proto3";' > to specify a syntax version. (Defaulted to proto2 syntax.) Signed-off-by: Dmitry Safonov Signed-off-by: Andrei Vagin --- images/remote-image.proto | 2 ++ 1 file changed, 2 insertions(+) diff --git a/images/remote-image.proto b/images/remote-image.proto index 1212627e3..f6b81503a 100644 --- a/images/remote-image.proto +++ b/images/remote-image.proto @@ -1,3 +1,5 @@ +syntax = "proto2"; + message local_image_entry { required string name = 1; required string snapshot_id = 2; From 520afd7e6b6ffeb4faa1b374ce17b7793c6d8547 Mon Sep 17 00:00:00 2001 From: Rodrigo Bruno Date: Fri, 10 Mar 2017 09:55:53 +0000 Subject: [PATCH 007/249] Fixed BUFFER_SIZE_WARNING issues introduced by remote images code. Signed-off-by: Rodrigo Bruno Signed-off-by: Andrei Vagin --- criu/img-remote-proto.c | 6 ++++-- criu/img-remote.c | 3 ++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/criu/img-remote-proto.c b/criu/img-remote-proto.c index ad39c42e7..33a07631f 100644 --- a/criu/img-remote-proto.c +++ b/criu/img-remote-proto.c @@ -432,8 +432,10 @@ static struct rimage *new_remote_image(char *path, char *snapshot_id) return NULL; } - strncpy(rimg->path, path, PATHLEN); - strncpy(rimg->snapshot_id, snapshot_id, PATHLEN); + strncpy(rimg->path, path, PATHLEN -1 ); + rimg->path[PATHLEN - 1] = '\0'; + strncpy(rimg->snapshot_id, snapshot_id, PATHLEN - 1); + rimg->snapshot_id[PATHLEN - 1] = '\0'; rimg->size = 0; buf->nbytes = 0; INIT_LIST_HEAD(&(rimg->buf_head)); diff --git a/criu/img-remote.c b/criu/img-remote.c index 337cb4a7f..c53217f0f 100644 --- a/criu/img-remote.c +++ b/criu/img-remote.c @@ -39,7 +39,8 @@ struct snapshot *new_snapshot(char *snapshot_id) pr_perror("Failed to allocate snapshot structure"); return NULL; } - strncpy(s->snapshot_id, snapshot_id, PATHLEN); + strncpy(s->snapshot_id, snapshot_id, PATHLEN - 1); + s->snapshot_id[PATHLEN - 1]= '\0'; return s; } From 6a647c3e46f9a2bb7b8554b355924d46f9c7bf4d Mon Sep 17 00:00:00 2001 From: Rodrigo Bruno Date: Sun, 19 Mar 2017 20:44:15 +0000 Subject: [PATCH 008/249] Fixed RESOURCE_LEAK issues introduced by remote images code. Signed-off-by: rodrigo-bruno Signed-off-by: Andrei Vagin --- criu/img-remote-proto.c | 59 ++++++++++++++++++++++++++--------------- 1 file changed, 37 insertions(+), 22 deletions(-) diff --git a/criu/img-remote-proto.c b/criu/img-remote-proto.c index 33a07631f..b3762331a 100644 --- a/criu/img-remote-proto.c +++ b/criu/img-remote-proto.c @@ -194,20 +194,23 @@ int setup_TCP_server_socket(int port) if (setsockopt( sockfd, SOL_SOCKET, SO_REUSEADDR, &sockopt, sizeof(sockopt)) == -1) { pr_perror("Unable to set SO_REUSEADDR"); - return -1; + goto err; } if (bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) { pr_perror("Unable to bind image socket"); - return -1; + goto err; } if (listen(sockfd, DEFAULT_LISTEN)) { pr_perror("Unable to listen image socket"); - return -1; + goto err; } return sockfd; +err: + close(sockfd); + return -1; } int setup_TCP_client_socket(char *hostname, int port) @@ -225,7 +228,7 @@ int setup_TCP_client_socket(char *hostname, int port) server = gethostbyname(hostname); if (server == NULL) { pr_perror("Unable to get host by name (%s)", hostname); - return -1; + goto err; } bzero((char *) &serv_addr, sizeof(serv_addr)); @@ -237,10 +240,13 @@ int setup_TCP_client_socket(char *hostname, int port) if (connect(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) { pr_perror("Unable to connect to remote %s", hostname); - return -1; + goto err; } return sockfd; +err: + close(sockfd); + return -1; } int setup_UNIX_server_socket(char *path) @@ -261,15 +267,18 @@ int setup_UNIX_server_socket(char *path) if (bind(sockfd, (struct sockaddr *)&addr, sizeof(addr)) == -1) { pr_perror("Unable to bind image socket"); - return -1; + goto err; } if (listen(sockfd, 50) == -1) { pr_perror("Unable to listen image socket"); - return -1; + goto err; } return sockfd; +err: + close(sockfd); + return -1; } int setup_UNIX_client_socket(char *path) @@ -388,13 +397,16 @@ static struct wthread *new_worker(void) if (!wt) { pr_perror("Unable to allocate worker thread structure"); - return NULL; + goto err; } if (sem_init(&(wt->wakeup_sem), 0, 0) != 0) { pr_perror("Workers semaphore init failed"); - return NULL; + goto err; } return wt; +err: + free(wt); + return NULL; } static void add_worker(struct wthread *wt) @@ -422,14 +434,9 @@ static struct rimage *new_remote_image(char *path, char *snapshot_id) struct rimage *rimg = malloc(sizeof(struct rimage)); struct rbuf *buf = malloc(sizeof(struct rbuf)); - if (rimg == NULL) { - pr_perror("Unable to allocate remote_image structures"); - return NULL; - } - - if (buf == NULL) { - pr_perror("Unable to allocate remote_buffer structures"); - return NULL; + if (rimg == NULL || buf == NULL) { + pr_perror("Unable to allocate remote image structures"); + goto err; } strncpy(rimg->path, path, PATHLEN -1 ); @@ -445,9 +452,13 @@ static struct rimage *new_remote_image(char *path, char *snapshot_id) if (pthread_mutex_init(&(rimg->in_use), NULL) != 0) { pr_perror("Remote image in_use mutex init failed"); - return NULL; + goto err; } return rimg; +err: + free(rimg); + free(buf); + return NULL; } /* Clears a remote image struct for reusing it. */ @@ -604,7 +615,7 @@ void *accept_local_image_connections(void *port) if (read_header(wt->fd, wt->snapshot_id, wt->path, &(wt->flags)) < 0) { pr_perror("Error reading local image header"); - return NULL; + goto err; } pr_info("Received %s request for %s:%s\n", @@ -626,18 +637,18 @@ void *accept_local_image_connections(void *port) int fd = open_proc_rw(PROC_GEN, LAST_PID_PATH); if (fd < 0) { pr_perror("Can't open %s", LAST_PID_PATH); + goto err; } if (flock(fd, LOCK_EX)) { - close(fd); pr_perror("Can't lock %s", LAST_PID_PATH); - return NULL; + goto err; } if (pthread_create( &tid, NULL, process_local_image_connection, (void *) wt)) { pr_perror("Unable to create worker thread"); - return NULL; + goto err; } if (flock(fd, LOCK_UN)) @@ -647,6 +658,10 @@ void *accept_local_image_connections(void *port) wt->tid = tid; add_worker(wt); } +err: + close(cli_fd); + free(wt); + return NULL; } /* Note: size is a limit on how much we want to read from the socket. Zero means From a07bce5dafdcdecb33cfdcd94ec1c10cb69fa43d Mon Sep 17 00:00:00 2001 From: Rodrigo Bruno Date: Sat, 18 Mar 2017 17:51:37 +0000 Subject: [PATCH 009/249] Fixed NULL_RETURNS issues introduced by remote images code. Signed-off-by: rodrigo-bruno Signed-off-by: Andrei Vagin --- criu/image.c | 15 +++++++++++---- criu/util.c | 10 ++++++++-- 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/criu/image.c b/criu/image.c index f6cb7e321..0c9f7666c 100644 --- a/criu/image.c +++ b/criu/image.c @@ -372,14 +372,20 @@ static int img_write_magic(struct cr_img *img, int oflags, int type) int do_open_remote_image(int dfd, char *path, int flags) { char *snapshot_id = NULL; - int ret; + int ret, save; /* When using namespaces, the current dir is changed so we need to * change to previous working dir and back to correctly open the image * proxy and cache sockets. */ - int save = dirfd(opendir(".")); + save = open(".", O_RDONLY); + if (save < 0) { + pr_perror("unable to open current working directory"); + return -1; + } + if (fchdir(get_service_fd(IMG_FD_OFF)) < 0) { - pr_debug("fchdir to dfd failed!\n"); + pr_perror("fchdir to dfd failed!\n"); + close(save); return -1; } @@ -398,7 +404,8 @@ int do_open_remote_image(int dfd, char *path, int flags) } if (fchdir(save) < 0) { - pr_debug("fchdir to save failed!\n"); + pr_perror("fchdir to save failed"); + close(save); return -1; } close(save); diff --git a/criu/util.c b/criu/util.c index daf051058..c4f9dcdf8 100644 --- a/criu/util.c +++ b/criu/util.c @@ -421,9 +421,15 @@ int copy_file(int fd_in, int fd_out, size_t bytes) { ssize_t written = 0; size_t chunk = bytes ? bytes : 4096; - char *buffer = (char*) malloc(chunk); + char *buffer; ssize_t ret; + buffer = xmalloc(chunk); + if (buffer == NULL) { + pr_perror("failed to allocate buffer to copy file"); + return -1; + } + while (1) { if (opts.remote) { ret = read(fd_in, buffer, chunk); @@ -459,7 +465,7 @@ int copy_file(int fd_in, int fd_out, size_t bytes) written += ret; } err: - free(buffer); + xfree(buffer); return ret; } From 0d5711cae16d393528588299f29bd802e89bbb18 Mon Sep 17 00:00:00 2001 From: Kir Kolyshkin Date: Thu, 23 Mar 2017 15:01:58 -0700 Subject: [PATCH 010/249] criu/img-remote-proto.c: use static mutex init I see no need to do dynamic init here. Cc: Rodrigo Bruno Signed-off-by: Kir Kolyshkin Signed-off-by: Andrei Vagin --- criu/img-remote-proto.c | 21 +++------------------ 1 file changed, 3 insertions(+), 18 deletions(-) diff --git a/criu/img-remote-proto.c b/criu/img-remote-proto.c index b3762331a..3199b7eb5 100644 --- a/criu/img-remote-proto.c +++ b/criu/img-remote-proto.c @@ -19,12 +19,12 @@ #include "image.h" LIST_HEAD(rimg_head); -pthread_mutex_t rimg_lock; +pthread_mutex_t rimg_lock = PTHREAD_MUTEX_INITIALIZER; -pthread_mutex_t proxy_to_cache_lock; +pthread_mutex_t proxy_to_cache_lock = PTHREAD_MUTEX_INITIALIZER; LIST_HEAD(workers_head); -pthread_mutex_t workers_lock; +pthread_mutex_t workers_lock = PTHREAD_MUTEX_INITIALIZER; sem_t workers_semph; struct rimage * (*wait_for_image) (struct wthread *wt); @@ -69,21 +69,6 @@ static struct wthread *get_wt_by_name(const char *snapshot_id, const char *path) static int init_sync_structures(void) { - if (pthread_mutex_init(&rimg_lock, NULL) != 0) { - pr_perror("Remote image list mutex init failed"); - return -1; - } - - if (pthread_mutex_init(&proxy_to_cache_lock, NULL) != 0) { - pr_perror("Remote image connection mutex init failed"); - return -1; - } - - if (pthread_mutex_init(&workers_lock, NULL) != 0) { - pr_perror("Workers mutex init failed"); - return -1; - } - if (sem_init(&workers_semph, 0, 0) != 0) { pr_perror("Workers semaphore init failed"); return -1; From 28ae24dd96a34719c199bcdda92c0615f37cc6b6 Mon Sep 17 00:00:00 2001 From: Kir Kolyshkin Date: Thu, 23 Mar 2017 15:01:59 -0700 Subject: [PATCH 011/249] criu/img-remote-proto.c: error printing fixes OK, so we have pr_perror() for cases where errno is set (and it makes sense to show it), and pr_err() for other errors. A correct function is to be used, depending on the context. 1. pthread_mutex_*() functions don't set errno, therefore pr_perror() should not be used. 2. accept() sets errno => makes sense to use pr_perror(). 3. read_header() arguably sets errno => use pr_err(). 4. open_proc_rw() already prints an error message, there is no need for yet another one. Cc: Rodrigo Bruno Signed-off-by: Kir Kolyshkin Signed-off-by: Andrei Vagin --- criu/img-remote-proto.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/criu/img-remote-proto.c b/criu/img-remote-proto.c index 3199b7eb5..4f440976a 100644 --- a/criu/img-remote-proto.c +++ b/criu/img-remote-proto.c @@ -436,7 +436,7 @@ static struct rimage *new_remote_image(char *path, char *snapshot_id) rimg->curr_sent_bytes = 0; if (pthread_mutex_init(&(rimg->in_use), NULL) != 0) { - pr_perror("Remote image in_use mutex init failed"); + pr_err("Remote image in_use mutex init failed\n"); goto err; } return rimg; @@ -590,7 +590,7 @@ void *accept_local_image_connections(void *port) cli_fd = accept(fd, (struct sockaddr *) &cli_addr, &clilen); if (cli_fd < 0) { if (!finished) - pr_err("Unable to accept local image connection"); + pr_perror("Unable to accept local image connection"); close(cli_fd); return NULL; } @@ -599,7 +599,7 @@ void *accept_local_image_connections(void *port) wt->fd = cli_fd; if (read_header(wt->fd, wt->snapshot_id, wt->path, &(wt->flags)) < 0) { - pr_perror("Error reading local image header"); + pr_err("Error reading local image header\n"); goto err; } @@ -620,10 +620,8 @@ void *accept_local_image_connections(void *port) * from restore. */ int fd = open_proc_rw(PROC_GEN, LAST_PID_PATH); - if (fd < 0) { - pr_perror("Can't open %s", LAST_PID_PATH); + if (fd < 0) goto err; - } if (flock(fd, LOCK_EX)) { pr_perror("Can't lock %s", LAST_PID_PATH); From 1d52044c1c794faf0f62f92e0dea220fcdc728f6 Mon Sep 17 00:00:00 2001 From: Kir Kolyshkin Date: Thu, 23 Mar 2017 15:02:07 -0700 Subject: [PATCH 012/249] criu/img-remote.c: use xmalloc 1. Use xmalloc() where possible. 2. There is no need to print an error message, as xmalloc() has already printed it for you. Cc: Rodrigo Bruno Signed-off-by: Kir Kolyshkin Signed-off-by: Andrei Vagin --- criu/img-remote.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/criu/img-remote.c b/criu/img-remote.c index c53217f0f..1e37bf33d 100644 --- a/criu/img-remote.c +++ b/criu/img-remote.c @@ -33,12 +33,11 @@ struct snapshot { struct snapshot *new_snapshot(char *snapshot_id) { - struct snapshot *s = malloc(sizeof(struct snapshot)); + struct snapshot *s = xmalloc(sizeof(struct snapshot)); - if (!s) { - pr_perror("Failed to allocate snapshot structure"); + if (!s) return NULL; - } + strncpy(s->snapshot_id, snapshot_id, PATHLEN - 1); s->snapshot_id[PATHLEN - 1]= '\0'; return s; @@ -180,7 +179,6 @@ static int pull_snapshot_ids(void) s = new_snapshot(ls->snapshot_id); if (!s) { - pr_perror("Unable create new snapshot structure"); close(sockfd); return -1; } @@ -206,7 +204,6 @@ int push_snapshot_id(void) rn.snapshot_id = xmalloc(sizeof(char) * PATHLEN); if (!rn.snapshot_id) { - pr_perror("Unable to allocate snapshot id buffer"); close(sockfd); return -1; } From 1c68f0a20f6db84dc55989ae74b5cf5b99db80f7 Mon Sep 17 00:00:00 2001 From: Kir Kolyshkin Date: Thu, 23 Mar 2017 15:02:08 -0700 Subject: [PATCH 013/249] criu/img-remote.c: use pr_err not pr_perror In those error paths where we don't have errno set, don't use pr_perror(), use pr_err() instead. Cc: Rodrigo Bruno Signed-off-by: Kir Kolyshkin Signed-off-by: Andrei Vagin --- criu/img-remote.c | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/criu/img-remote.c b/criu/img-remote.c index 1e37bf33d..2bf62d2a9 100644 --- a/criu/img-remote.c +++ b/criu/img-remote.c @@ -54,17 +54,19 @@ int read_remote_image_connection(char *snapshot_id, char *path) int sockfd = setup_UNIX_client_socket(restoring ? DEFAULT_CACHE_SOCKET: DEFAULT_PROXY_SOCKET); if (sockfd < 0) { - pr_perror("Error opening local connection for %s:%s", path, snapshot_id); + pr_err("Error opening local connection for %s:%s\n", + path, snapshot_id); return -1; } if (write_header(sockfd, snapshot_id, path, O_RDONLY) < 0) { - pr_perror("Error writing header for %s:%s", path, snapshot_id); + pr_err("Error writing header for %s:%s\n", path, snapshot_id); return -1; } if (read_reply_header(sockfd, &error) < 0) { - pr_perror("Error reading reply header for %s:%s", path, snapshot_id); + pr_err("Error reading reply header for %s:%s\n", + path, snapshot_id); return -1; } if (!error || !strncmp(path, RESTORE_FINISH, sizeof(RESTORE_FINISH))) @@ -74,7 +76,8 @@ int read_remote_image_connection(char *snapshot_id, char *path) close(sockfd); return -ENOENT; } - pr_perror("Unexpected error returned: %d (%s:%s)\n", error, path, snapshot_id); + pr_err("Unexpected error returned: %d (%s:%s)\n", + error, path, snapshot_id); close(sockfd); return -1; } @@ -87,7 +90,7 @@ int write_remote_image_connection(char *snapshot_id, char *path, int flags) return -1; if (write_header(sockfd, snapshot_id, path, flags) < 0) { - pr_perror("Error writing header for %s:%s", path, snapshot_id); + pr_err("Error writing header for %s:%s\n", path, snapshot_id); return -1; } return sockfd; @@ -99,7 +102,7 @@ int finish_remote_dump(void) int fd = write_remote_image_connection(NULL_SNAPSHOT_ID, DUMP_FINISH, O_WRONLY); if (fd == -1) { - pr_perror("Unable to open finish dump connection"); + pr_err("Unable to open finish dump connection"); return -1; } @@ -113,7 +116,7 @@ int finish_remote_restore(void) int fd = read_remote_image_connection(NULL_SNAPSHOT_ID, RESTORE_FINISH); if (fd == -1) { - pr_perror("Unable to open finish restore connection"); + pr_err("Unable to open finish restore connection\n"); return -1; } @@ -143,7 +146,7 @@ int skip_remote_bytes(int fd, unsigned long len) } if (curr != len) { - pr_perror("Unable to skip the current number of bytes: %lx instead of %lx", + pr_err("Unable to skip the current number of bytes: %lx instead of %lx\n", curr, len); return -1; } @@ -162,7 +165,7 @@ static int pull_snapshot_ids(void) if (sockfd < 0 && errno == ENOENT) return 0; else if (sockfd < 0) { - pr_perror("Unable to open snapshot id read connection"); + pr_err("Unable to open snapshot id read connection\n"); return -1; } @@ -172,7 +175,7 @@ static int pull_snapshot_ids(void) close(sockfd); return n; } else if (n < 0) { - pr_perror("Unable to read remote snapshot ids"); + pr_err("Unable to read remote snapshot ids\n"); close(sockfd); return n; } @@ -198,7 +201,7 @@ int push_snapshot_id(void) int sockfd = write_remote_image_connection(NULL_SNAPSHOT_ID, PARENT_IMG, O_APPEND); if (sockfd < 0) { - pr_perror("Unable to open snapshot id push connection"); + pr_err("Unable to open snapshot id push connection\n"); return -1; } @@ -240,7 +243,8 @@ int get_curr_snapshot_id_idx(void) idx++; } - pr_perror("Error, could not find current snapshot id (%s) fd", snapshot_id); + pr_err("Error, could not find current snapshot id (%s) fd\n", + snapshot_id); return -1; } @@ -263,7 +267,7 @@ char *get_snapshot_id_from_idx(int idx) idx--; } - pr_perror("Error, could not find snapshot id for idx %d", idx); + pr_err("Error, could not find snapshot id for idx %d\n", idx); return NULL; } From dc236fd0637a6efde08363665183bb5ada35a3e2 Mon Sep 17 00:00:00 2001 From: Mike Rapoport Date: Tue, 25 Apr 2017 13:38:01 +0300 Subject: [PATCH 014/249] pagemap: fix reading pages from socket for --remote case When --remote option is specified, read_local_page tries to pread from a socket, and fails with "Illegal seek" error. Restore single pread call for regular image files case and introduce maybe_read_page_img_cache version of maybe_read_page method. Generally-approved-by: Rodrigo Bruno Acked-by: Pavel Emelyanov Signed-off-by: Mike Rapoport Signed-off-by: Andrei Vagin --- criu/pagemap.c | 52 +++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 41 insertions(+), 11 deletions(-) diff --git a/criu/pagemap.c b/criu/pagemap.c index 47b6cb91c..d2e6775c2 100644 --- a/criu/pagemap.c +++ b/criu/pagemap.c @@ -244,7 +244,6 @@ static int read_local_page(struct page_read *pr, unsigned long vaddr, { int fd = img_raw_fd(pr->pi); ssize_t ret; - size_t curr = 0; /* * Flush any pending async requests if any not to break the @@ -254,15 +253,10 @@ static int read_local_page(struct page_read *pr, unsigned long vaddr, return -1; pr_debug("\tpr%lu-%u Read page from self %lx/%"PRIx64"\n", pr->img_id, pr->id, pr->cvaddr, pr->pi_off); - while (1) { - ret = pread(fd, buf + curr, len - curr, pr->pi_off + curr); - if (ret < 1) { - pr_perror("Can't read mapping page %zd", ret); - return -1; - } - curr += ret; - if (curr == len) - break; + ret = pread(fd, buf, len, pr->pi_off); + if (ret != len) { + pr_perror("Can't read mapping page %zd", ret); + return -1; } if (opts.auto_dedup) { @@ -406,6 +400,40 @@ static int maybe_read_page_local(struct page_read *pr, unsigned long vaddr, return ret; } +static int maybe_read_page_img_cache(struct page_read *pr, unsigned long vaddr, + int nr, void *buf, unsigned flags) +{ + unsigned long len = nr * PAGE_SIZE; + int fd = img_raw_fd(pr->pi); + int ret; + size_t curr = 0; + + pr_debug("\tpr%lu-%u Read page from self %lx/%"PRIx64"\n", pr->img_id, pr->id, pr->cvaddr, pr->pi_off); + while (1) { + ret = read(fd, buf + curr, len - curr); + if (ret < 0) { + pr_perror("Can't read mapping page %d", ret); + return -1; + } + curr += ret; + if (curr == len) + break; + } + + if (opts.auto_dedup) { + ret = punch_hole(pr, pr->pi_off, len, false); + if (ret == -1) + return -1; + } + + if (ret == 0 && pr->io_complete) + ret = pr->io_complete(pr, vaddr, nr); + + pr->pi_off += len; + + return ret; +} + static int read_page_complete(unsigned long img_id, unsigned long vaddr, int nr_pages, void *priv) { int ret = 0; @@ -804,7 +832,9 @@ int open_page_read_at(int dfd, unsigned long img_id, struct page_read *pr, int p pr->id = ids++; pr->img_id = img_id; - if (remote) + if (opts.remote) + pr->maybe_read_page = maybe_read_page_img_cache; + else if (remote) pr->maybe_read_page = maybe_read_page_remote; else { pr->maybe_read_page = maybe_read_page_local; From dae52f3568e1a62fc9b24badf5ccbba1bebc3da3 Mon Sep 17 00:00:00 2001 From: Omri Kramer Date: Sat, 1 Jul 2017 18:42:21 +0300 Subject: [PATCH 015/249] Merge img-remote and img-remote-proto There is no real need to have both. Signed-off-by: Omri Kramer Singed-off-by: Lior Fisch Reviewed-by: Mike Rapoport Signed-off-by: Andrei Vagin --- criu/Makefile.crtools | 1 - criu/img-cache.c | 2 +- criu/img-proxy.c | 2 +- criu/img-remote-proto.c | 742 -------------------------------- criu/img-remote.c | 719 ++++++++++++++++++++++++++++++- criu/include/img-remote-proto.h | 88 ---- criu/include/img-remote.h | 75 ++++ 7 files changed, 794 insertions(+), 835 deletions(-) delete mode 100644 criu/img-remote-proto.c delete mode 100644 criu/include/img-remote-proto.h diff --git a/criu/Makefile.crtools b/criu/Makefile.crtools index 7832ccd54..b598f0ef5 100644 --- a/criu/Makefile.crtools +++ b/criu/Makefile.crtools @@ -34,7 +34,6 @@ obj-y += image.o obj-y += img-remote.o obj-y += img-proxy.o obj-y += img-cache.o -obj-y += img-remote-proto.o obj-y += ipc_ns.o obj-y += irmap.o obj-y += kcmp-ids.o diff --git a/criu/img-cache.c b/criu/img-cache.c index 293597088..7020a30f0 100644 --- a/criu/img-cache.c +++ b/criu/img-cache.c @@ -1,6 +1,6 @@ #include -#include "img-remote-proto.h" +#include "img-remote.h" #include "criu-log.h" #include #include diff --git a/criu/img-proxy.c b/criu/img-proxy.c index 58123dccb..f56073b4e 100644 --- a/criu/img-proxy.c +++ b/criu/img-proxy.c @@ -1,7 +1,7 @@ #include #include "img-remote.h" -#include "img-remote-proto.h" +#include "img-remote.h" #include "criu-log.h" #include #include diff --git a/criu/img-remote-proto.c b/criu/img-remote-proto.c deleted file mode 100644 index 4f440976a..000000000 --- a/criu/img-remote-proto.c +++ /dev/null @@ -1,742 +0,0 @@ -#include -#include - -#include -#include -#include -#include -#include "sys/un.h" -#include -#include -#include - -#include "img-remote-proto.h" -#include "criu-log.h" -#include "common/compiler.h" - -#include "protobuf.h" -#include "images/remote-image.pb-c.h" -#include "image.h" - -LIST_HEAD(rimg_head); -pthread_mutex_t rimg_lock = PTHREAD_MUTEX_INITIALIZER; - -pthread_mutex_t proxy_to_cache_lock = PTHREAD_MUTEX_INITIALIZER; - -LIST_HEAD(workers_head); -pthread_mutex_t workers_lock = PTHREAD_MUTEX_INITIALIZER; -sem_t workers_semph; - -struct rimage * (*wait_for_image) (struct wthread *wt); - -bool finished = false; -int writing = 0; -int forwarding = 0; -int proxy_to_cache_fd; -int local_req_fd; - -struct rimage *get_rimg_by_name(const char *snapshot_id, const char *path) -{ - struct rimage *rimg = NULL; - - pthread_mutex_lock(&rimg_lock); - list_for_each_entry(rimg, &rimg_head, l) { - if (!strncmp(rimg->path, path, PATHLEN) && - !strncmp(rimg->snapshot_id, snapshot_id, PATHLEN)) { - pthread_mutex_unlock(&rimg_lock); - return rimg; - } - } - pthread_mutex_unlock(&rimg_lock); - return NULL; -} - -static struct wthread *get_wt_by_name(const char *snapshot_id, const char *path) -{ - struct wthread *wt = NULL; - - pthread_mutex_lock(&workers_lock); - list_for_each_entry(wt, &workers_head, l) { - if (!strncmp(wt->path, path, PATHLEN) && - !strncmp(wt->snapshot_id, snapshot_id, PATHLEN)) { - pthread_mutex_unlock(&workers_lock); - return wt; - } - } - pthread_mutex_unlock(&workers_lock); - return NULL; -} - -static int init_sync_structures(void) -{ - if (sem_init(&workers_semph, 0, 0) != 0) { - pr_perror("Workers semaphore init failed"); - return -1; - } - - return 0; -} - -void prepare_recv_rimg(void) -{ - pthread_mutex_lock(&rimg_lock); - writing++; - pthread_mutex_unlock(&rimg_lock); -} - -void finalize_recv_rimg(struct rimage *rimg) -{ - - pthread_mutex_lock(&rimg_lock); - - if (rimg) - list_add_tail(&(rimg->l), &rimg_head); - writing--; - pthread_mutex_unlock(&rimg_lock); - /* Wake thread waiting for this image. */ - if (rimg) { - struct wthread *wt = get_wt_by_name(rimg->snapshot_id, rimg->path); - if (wt) - sem_post(&(wt->wakeup_sem)); - } -} - -bool is_receiving(void) -{ - int ret; - - pthread_mutex_lock(&rimg_lock); - ret = writing; - pthread_mutex_unlock(&rimg_lock); - return ret > 0; -} - -static void prepare_fwd_rimg(void) -{ - pthread_mutex_lock(&rimg_lock); - forwarding++; - pthread_mutex_unlock(&rimg_lock); -} - -static void finalize_fwd_rimg(void) -{ - pthread_mutex_lock(&rimg_lock); - forwarding--; - pthread_mutex_unlock(&rimg_lock); -} - -static bool is_forwarding(void) -{ - int ret; - - pthread_mutex_lock(&rimg_lock); - ret = forwarding; - pthread_mutex_unlock(&rimg_lock); - return ret > 0; -} - -/* This function is called when no more images are coming. Threads still waiting - * for images will be awaken to send a ENOENT (no such file) to the requester. - */ -void unlock_workers(void) -{ - struct wthread *wt = NULL; - - pthread_mutex_lock(&workers_lock); - list_for_each_entry(wt, &workers_head, l) - sem_post(&(wt->wakeup_sem)); - pthread_mutex_unlock(&workers_lock); -} - -int init_daemon(bool background, struct rimage *(*wfi)(struct wthread*)) -{ - if (background) { - if (daemon(1, 0) == -1) { - pr_perror("Can't run service server in the background"); - return -1; - } - } - wait_for_image = wfi; - return init_sync_structures(); -} - -int setup_TCP_server_socket(int port) -{ - struct sockaddr_in serv_addr; - int sockopt = 1; - int sockfd = socket(AF_INET, SOCK_STREAM, 0); - - if (sockfd < 0) { - pr_perror("Unable to open image socket"); - return -1; - } - - bzero((char *) &serv_addr, sizeof(serv_addr)); - serv_addr.sin_family = AF_INET; - serv_addr.sin_addr.s_addr = INADDR_ANY; - serv_addr.sin_port = htons(port); - - if (setsockopt( - sockfd, SOL_SOCKET, SO_REUSEADDR, &sockopt, sizeof(sockopt)) == -1) { - pr_perror("Unable to set SO_REUSEADDR"); - goto err; - } - - if (bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) { - pr_perror("Unable to bind image socket"); - goto err; - } - - if (listen(sockfd, DEFAULT_LISTEN)) { - pr_perror("Unable to listen image socket"); - goto err; - } - - return sockfd; -err: - close(sockfd); - return -1; -} - -int setup_TCP_client_socket(char *hostname, int port) -{ - int sockfd; - struct sockaddr_in serv_addr; - struct hostent *server; - - sockfd = socket(AF_INET, SOCK_STREAM, 0); - if (sockfd < 0) { - pr_perror("Unable to open remote image socket"); - return -1; - } - - server = gethostbyname(hostname); - if (server == NULL) { - pr_perror("Unable to get host by name (%s)", hostname); - goto err; - } - - bzero((char *) &serv_addr, sizeof(serv_addr)); - serv_addr.sin_family = AF_INET; - bcopy((char *) server->h_addr, - (char *) &serv_addr.sin_addr.s_addr, - server->h_length); - serv_addr.sin_port = htons(port); - - if (connect(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) { - pr_perror("Unable to connect to remote %s", hostname); - goto err; - } - - return sockfd; -err: - close(sockfd); - return -1; -} - -int setup_UNIX_server_socket(char *path) -{ - struct sockaddr_un addr; - int sockfd = socket(AF_UNIX, SOCK_STREAM, 0); - - if (sockfd < 0) { - pr_perror("Unable to open image socket"); - return -1; - } - - memset(&addr, 0, sizeof(addr)); - addr.sun_family = AF_UNIX; - strncpy(addr.sun_path, path, sizeof(addr.sun_path)-1); - - unlink(path); - - if (bind(sockfd, (struct sockaddr *)&addr, sizeof(addr)) == -1) { - pr_perror("Unable to bind image socket"); - goto err; - } - - if (listen(sockfd, 50) == -1) { - pr_perror("Unable to listen image socket"); - goto err; - } - - return sockfd; -err: - close(sockfd); - return -1; -} - -int setup_UNIX_client_socket(char *path) -{ - struct sockaddr_un addr; - int sockfd = socket(AF_UNIX, SOCK_STREAM, 0); - - if (sockfd < 0) { - pr_perror("Unable to open local image socket"); - return -1; - } - - memset(&addr, 0, sizeof(addr)); - addr.sun_family = AF_UNIX; - strncpy(addr.sun_path, path, sizeof(addr.sun_path)-1); - - if (connect(sockfd, (struct sockaddr *)&addr, sizeof(addr)) < 0) { - pr_perror("Unable to connect to local socket: %s", path); - close(sockfd); - return -1; - } - - return sockfd; -} - -int64_t pb_write_obj(int fd, void *obj, int type) -{ - struct cr_img img; - - img._x.fd = fd; - bfd_setraw(&img._x); - return pb_write_one(&img, obj, type); -} - -int64_t pb_read_obj(int fd, void **pobj, int type) -{ - struct cr_img img; - - img._x.fd = fd; - bfd_setraw(&img._x); - return do_pb_read_one(&img, pobj, type, true); -} - -int64_t write_header(int fd, char *snapshot_id, char *path, int flags) -{ - LocalImageEntry li = LOCAL_IMAGE_ENTRY__INIT; - - li.name = path; - li.snapshot_id = snapshot_id; - li.open_mode = flags; - return pb_write_obj(fd, &li, PB_LOCAL_IMAGE); -} - -int64_t write_reply_header(int fd, int error) -{ - LocalImageReplyEntry lir = LOCAL_IMAGE_REPLY_ENTRY__INIT; - - lir.error = error; - return pb_write_obj(fd, &lir, PB_LOCAL_IMAGE_REPLY); -} - -int64_t write_remote_header(int fd, char *snapshot_id, char *path, int flags, uint64_t size) -{ - RemoteImageEntry ri = REMOTE_IMAGE_ENTRY__INIT; - - ri.name = path; - ri.snapshot_id = snapshot_id; - ri.open_mode = flags; - ri.size = size; - return pb_write_obj(fd, &ri, PB_REMOTE_IMAGE); -} - -int64_t read_header(int fd, char *snapshot_id, char *path, int *flags) -{ - LocalImageEntry *li; - int ret = pb_read_obj(fd, (void **)&li, PB_LOCAL_IMAGE); - - if (ret > 0) { - strncpy(snapshot_id, li->snapshot_id, PATHLEN); - strncpy(path, li->name, PATHLEN); - *flags = li->open_mode; - } - free(li); - return ret; -} - -int64_t read_reply_header(int fd, int *error) -{ - LocalImageReplyEntry *lir; - int ret = pb_read_obj(fd, (void **)&lir, PB_LOCAL_IMAGE_REPLY); - - if (ret > 0) - *error = lir->error; - free(lir); - return ret; -} - -int64_t read_remote_header(int fd, char *snapshot_id, char *path, int *flags, uint64_t *size) -{ - RemoteImageEntry *ri; - int ret = pb_read_obj(fd, (void **)&ri, PB_REMOTE_IMAGE); - - if (ret > 0) { - strncpy(snapshot_id, ri->snapshot_id, PATHLEN); - strncpy(path, ri->name, PATHLEN); - *flags = ri->open_mode; - *size = ri->size; - } - free(ri); - return ret; -} - -static struct wthread *new_worker(void) -{ - struct wthread *wt = malloc(sizeof(struct wthread)); - - if (!wt) { - pr_perror("Unable to allocate worker thread structure"); - goto err; - } - if (sem_init(&(wt->wakeup_sem), 0, 0) != 0) { - pr_perror("Workers semaphore init failed"); - goto err; - } - return wt; -err: - free(wt); - return NULL; -} - -static void add_worker(struct wthread *wt) -{ - pthread_mutex_lock(&workers_lock); - list_add_tail(&(wt->l), &workers_head); - pthread_mutex_unlock(&workers_lock); - sem_post(&workers_semph); -} - -void join_workers(void) -{ - struct wthread *wthread = NULL; - - while (! list_empty(&workers_head)) { - wthread = list_entry(workers_head.next, struct wthread, l); - pthread_join(wthread->tid, NULL); - list_del(&(wthread->l)); - free(wthread); - } -} - -static struct rimage *new_remote_image(char *path, char *snapshot_id) -{ - struct rimage *rimg = malloc(sizeof(struct rimage)); - struct rbuf *buf = malloc(sizeof(struct rbuf)); - - if (rimg == NULL || buf == NULL) { - pr_perror("Unable to allocate remote image structures"); - goto err; - } - - strncpy(rimg->path, path, PATHLEN -1 ); - rimg->path[PATHLEN - 1] = '\0'; - strncpy(rimg->snapshot_id, snapshot_id, PATHLEN - 1); - rimg->snapshot_id[PATHLEN - 1] = '\0'; - rimg->size = 0; - buf->nbytes = 0; - INIT_LIST_HEAD(&(rimg->buf_head)); - list_add_tail(&(buf->l), &(rimg->buf_head)); - rimg->curr_sent_buf = list_entry(rimg->buf_head.next, struct rbuf, l); - rimg->curr_sent_bytes = 0; - - if (pthread_mutex_init(&(rimg->in_use), NULL) != 0) { - pr_err("Remote image in_use mutex init failed\n"); - goto err; - } - return rimg; -err: - free(rimg); - free(buf); - return NULL; -} - -/* Clears a remote image struct for reusing it. */ -static struct rimage *clear_remote_image(struct rimage *rimg) -{ - pthread_mutex_lock(&(rimg->in_use)); - - while (!list_is_singular(&(rimg->buf_head))) { - struct rbuf *buf = list_entry(rimg->buf_head.prev, struct rbuf, l); - - list_del(rimg->buf_head.prev); - free(buf); - } - - list_entry(rimg->buf_head.next, struct rbuf, l)->nbytes = 0; - rimg->size = 0; - rimg->curr_sent_buf = list_entry(rimg->buf_head.next, struct rbuf, l); - rimg->curr_sent_bytes = 0; - - pthread_mutex_unlock(&(rimg->in_use)); - - return rimg; -} - -struct rimage *prepare_remote_image(char *path, char *snapshot_id, int open_mode) -{ - struct rimage *rimg = get_rimg_by_name(snapshot_id, path); - /* There is no record of such image, create a new one. */ - - if (rimg == NULL) - return new_remote_image(path, snapshot_id); - - pthread_mutex_lock(&rimg_lock); - list_del(&(rimg->l)); - pthread_mutex_unlock(&rimg_lock); - - /* There is already an image record. Simply return it for appending. */ - if (open_mode == O_APPEND) - return rimg; - /* There is already an image record. Clear it for writing. */ - else - return clear_remote_image(rimg); -} - -void *process_local_read(struct wthread *wt) -{ - struct rimage *rimg = NULL; - int64_t ret; - /* TODO - split wait_for_image - * in cache - improve the parent stuf - * in proxy - do not wait for anything, return no file - */ - rimg = wait_for_image(wt); - if (!rimg) { - pr_info("No image %s:%s.\n", wt->path, wt->snapshot_id); - if (write_reply_header(wt->fd, ENOENT) < 0) - pr_perror("Error writing reply header for unexisting image"); - close(wt->fd); - return NULL; - } else { - if (write_reply_header(wt->fd, 0) < 0) { - pr_perror("Error writing reply header for %s:%s", - wt->path, wt->snapshot_id); - close(wt->fd); - return NULL; - } - } - - pthread_mutex_lock(&(rimg->in_use)); - ret = send_image(wt->fd, rimg, wt->flags, true); - if (ret < 0) - pr_perror("Unable to send %s:%s to CRIU (sent %ld bytes)", - rimg->path, rimg->snapshot_id, (long)ret); - else - pr_info("Finished sending %s:%s to CRIU (sent %ld bytes)\n", - rimg->path, rimg->snapshot_id, (long)ret); - pthread_mutex_unlock(&(rimg->in_use)); - return NULL; -} - -static void *process_local_image_connection(void *ptr) -{ - struct wthread *wt = (struct wthread *) ptr; - struct rimage *rimg = NULL; - int64_t ret; - - /* NOTE: the code inside this if is shared for both cache and proxy. */ - if (wt->flags == O_RDONLY) - return process_local_read(wt); - - /* NOTE: IMAGE PROXY ONLY. The image cache receives write connections - * through TCP (see accept_remote_image_connections). - */ - rimg = prepare_remote_image(wt->path, wt->snapshot_id, wt->flags); - ret = recv_image(wt->fd, rimg, 0, wt->flags, true); - if (ret < 0) { - pr_perror("Unable to receive %s:%s to CRIU (received %ld bytes)", - rimg->path, rimg->snapshot_id, (long)ret); - finalize_recv_rimg(NULL); - return NULL; - } - finalize_recv_rimg(rimg); - pr_info("Finished receiving %s:%s (received %ld bytes)\n", - rimg->path, rimg->snapshot_id, (long)ret); - - - if (!strncmp(rimg->path, DUMP_FINISH, sizeof(DUMP_FINISH))) { - finished = true; - shutdown(local_req_fd, SHUT_RD); - } else { - pthread_mutex_lock(&proxy_to_cache_lock); - ret = forward_image(rimg); - pthread_mutex_unlock(&proxy_to_cache_lock); - } - - finalize_fwd_rimg(); - if (ret < 0) { - pr_perror("Unable to forward %s:%s to Image Cache", - rimg->path, rimg->snapshot_id); - - return NULL; - } - - if (finished && !is_forwarding() && !is_receiving()) { - pr_info("Closing connection to Image Cache.\n"); - close(proxy_to_cache_fd); - unlock_workers(); - } - return NULL; -} - - -void *accept_local_image_connections(void *port) -{ - int fd = *((int *) port); - int cli_fd; - struct sockaddr_in cli_addr; - - socklen_t clilen = sizeof(cli_addr); - pthread_t tid; - struct wthread *wt; - - while (1) { - cli_fd = accept(fd, (struct sockaddr *) &cli_addr, &clilen); - if (cli_fd < 0) { - if (!finished) - pr_perror("Unable to accept local image connection"); - close(cli_fd); - return NULL; - } - - wt = new_worker(); - wt->fd = cli_fd; - - if (read_header(wt->fd, wt->snapshot_id, wt->path, &(wt->flags)) < 0) { - pr_err("Error reading local image header\n"); - goto err; - } - - pr_info("Received %s request for %s:%s\n", - wt->flags == O_RDONLY ? "read" : - wt->flags == O_APPEND ? "append" : "write", - wt->path, wt->snapshot_id); - - /* These function calls are used to avoid other threads from - * thinking that there are no more images are coming. - */ - if (wt->flags != O_RDONLY) { - prepare_recv_rimg(); - prepare_fwd_rimg(); - } - - /* We need to flock the last pid file to avoid stealing pids - * from restore. - */ - int fd = open_proc_rw(PROC_GEN, LAST_PID_PATH); - if (fd < 0) - goto err; - - if (flock(fd, LOCK_EX)) { - pr_perror("Can't lock %s", LAST_PID_PATH); - goto err; - } - - if (pthread_create( - &tid, NULL, process_local_image_connection, (void *) wt)) { - pr_perror("Unable to create worker thread"); - goto err; - } - - if (flock(fd, LOCK_UN)) - pr_perror("Can't unlock %s", LAST_PID_PATH); - close(fd); - - wt->tid = tid; - add_worker(wt); - } -err: - close(cli_fd); - free(wt); - return NULL; -} - -/* Note: size is a limit on how much we want to read from the socket. Zero means - * read until the socket is closed. - */ -int64_t recv_image(int fd, struct rimage *rimg, uint64_t size, int flags, bool close_fd) -{ - struct rbuf *curr_buf = NULL; - int n; - - if (flags == O_APPEND) - curr_buf = list_entry(rimg->buf_head.prev, struct rbuf, l); - else - curr_buf = list_entry(rimg->buf_head.next, struct rbuf, l); - - while (1) { - n = read(fd, - curr_buf->buffer + curr_buf->nbytes, - size ? - min((int) (size - rimg->size), BUF_SIZE - curr_buf->nbytes) : - BUF_SIZE - curr_buf->nbytes); - if (n == 0) { - if (close_fd) - close(fd); - return rimg->size; - } else if (n > 0) { - curr_buf->nbytes += n; - rimg->size += n; - if (curr_buf->nbytes == BUF_SIZE) { - struct rbuf *buf = malloc(sizeof(struct rbuf)); - if (buf == NULL) { - pr_perror("Unable to allocate remote_buffer structures"); - if (close_fd) - close(fd); - return -1; - } - buf->nbytes = 0; - list_add_tail(&(buf->l), &(rimg->buf_head)); - curr_buf = buf; - } - if (size && rimg->size == size) { - if (close_fd) - close(fd); - return rimg->size; - } - } else { - pr_perror("Read on %s:%s socket failed", - rimg->path, rimg->snapshot_id); - if (close_fd) - close(fd); - return -1; - } - } -} - -int64_t send_image(int fd, struct rimage *rimg, int flags, bool close_fd) -{ - - int n, nblocks = 0; - - if (flags != O_APPEND) { - rimg->curr_sent_buf = list_entry(rimg->buf_head.next, struct rbuf, l); - rimg->curr_sent_bytes = 0; - } - - while (1) { - n = send( - fd, - rimg->curr_sent_buf->buffer + rimg->curr_sent_bytes, - min(BUF_SIZE, rimg->curr_sent_buf->nbytes) - rimg->curr_sent_bytes, - MSG_NOSIGNAL); - if (n > -1) { - rimg->curr_sent_bytes += n; - if (rimg->curr_sent_bytes == BUF_SIZE) { - rimg->curr_sent_buf = - list_entry(rimg->curr_sent_buf->l.next, struct rbuf, l); - nblocks++; - rimg->curr_sent_bytes = 0; - } else if (rimg->curr_sent_bytes == rimg->curr_sent_buf->nbytes) { - if (close_fd) - close(fd); - return nblocks*BUF_SIZE + rimg->curr_sent_buf->nbytes; - } - } else if (errno == EPIPE || errno == ECONNRESET) { - pr_warn("Connection for %s:%s was closed early than expected\n", - rimg->path, rimg->snapshot_id); - return 0; - } else { - pr_perror("Write on %s:%s socket failed", - rimg->path, rimg->snapshot_id); - return -1; - } - } - -} diff --git a/criu/img-remote.c b/criu/img-remote.c index 2bf62d2a9..f812c52d5 100644 --- a/criu/img-remote.c +++ b/criu/img-remote.c @@ -8,7 +8,6 @@ #include "xmalloc.h" #include "criu-log.h" #include "img-remote.h" -#include "img-remote-proto.h" #include "images/remote-image.pb-c.h" #include "protobuf-desc.h" #include @@ -16,11 +15,37 @@ #include "common/compiler.h" #include "cr_options.h" +#include +#include "sys/un.h" +#include +#include + +#include "protobuf.h" +#include "image.h" + #define PB_LOCAL_IMAGE_SIZE PATHLEN static char *snapshot_id; bool restoring = true; +LIST_HEAD(rimg_head); +pthread_mutex_t rimg_lock = PTHREAD_MUTEX_INITIALIZER; + +pthread_mutex_t proxy_to_cache_lock = PTHREAD_MUTEX_INITIALIZER; + +LIST_HEAD(workers_head); +pthread_mutex_t workers_lock = PTHREAD_MUTEX_INITIALIZER; +sem_t workers_semph; + +struct rimage * (*wait_for_image) (struct wthread *wt); + +bool finished = false; +int writing = 0; +int forwarding = 0; +int proxy_to_cache_fd; +int local_req_fd; + + LIST_HEAD(snapshot_head); /* A snapshot is a dump or pre-dump operation. Each snapshot is identified by an @@ -48,9 +73,699 @@ void add_snapshot(struct snapshot *snapshot) list_add_tail(&(snapshot->l), &snapshot_head); } +struct rimage *get_rimg_by_name(const char *snapshot_id, const char *path) +{ + struct rimage *rimg = NULL; + + pthread_mutex_lock(&rimg_lock); + list_for_each_entry(rimg, &rimg_head, l) { + if (!strncmp(rimg->path, path, PATHLEN) && + !strncmp(rimg->snapshot_id, snapshot_id, PATHLEN)) { + pthread_mutex_unlock(&rimg_lock); + return rimg; + } + } + pthread_mutex_unlock(&rimg_lock); + return NULL; +} + +static struct wthread *get_wt_by_name(const char *snapshot_id, const char *path) +{ + struct wthread *wt = NULL; + + pthread_mutex_lock(&workers_lock); + list_for_each_entry(wt, &workers_head, l) { + if (!strncmp(wt->path, path, PATHLEN) && + !strncmp(wt->snapshot_id, snapshot_id, PATHLEN)) { + pthread_mutex_unlock(&workers_lock); + return wt; + } + } + pthread_mutex_unlock(&workers_lock); + return NULL; +} + +static int init_sync_structures(void) +{ + if (sem_init(&workers_semph, 0, 0) != 0) { + pr_perror("Workers semaphore init failed"); + return -1; + } + + return 0; +} + +void prepare_recv_rimg(void) +{ + pthread_mutex_lock(&rimg_lock); + writing++; + pthread_mutex_unlock(&rimg_lock); +} + +void finalize_recv_rimg(struct rimage *rimg) +{ + + pthread_mutex_lock(&rimg_lock); + + if (rimg) + list_add_tail(&(rimg->l), &rimg_head); + writing--; + pthread_mutex_unlock(&rimg_lock); + /* Wake thread waiting for this image. */ + if (rimg) { + struct wthread *wt = get_wt_by_name(rimg->snapshot_id, rimg->path); + if (wt) + sem_post(&(wt->wakeup_sem)); + } +} + +bool is_receiving(void) +{ + int ret; + + pthread_mutex_lock(&rimg_lock); + ret = writing; + pthread_mutex_unlock(&rimg_lock); + return ret > 0; +} + +static void prepare_fwd_rimg(void) +{ + pthread_mutex_lock(&rimg_lock); + forwarding++; + pthread_mutex_unlock(&rimg_lock); +} + +static void finalize_fwd_rimg(void) +{ + pthread_mutex_lock(&rimg_lock); + forwarding--; + pthread_mutex_unlock(&rimg_lock); +} + +static bool is_forwarding(void) +{ + int ret; + + pthread_mutex_lock(&rimg_lock); + ret = forwarding; + pthread_mutex_unlock(&rimg_lock); + return ret > 0; +} + +/* This function is called when no more images are coming. Threads still waiting + * for images will be awaken to send a ENOENT (no such file) to the requester. + */ +void unlock_workers(void) +{ + struct wthread *wt = NULL; + + pthread_mutex_lock(&workers_lock); + list_for_each_entry(wt, &workers_head, l) + sem_post(&(wt->wakeup_sem)); + pthread_mutex_unlock(&workers_lock); +} + +int init_daemon(bool background, struct rimage *(*wfi)(struct wthread*)) +{ + if (background) { + if (daemon(1, 0) == -1) { + pr_perror("Can't run service server in the background"); + return -1; + } + } + wait_for_image = wfi; + return init_sync_structures(); +} + +int setup_TCP_server_socket(int port) +{ + struct sockaddr_in serv_addr; + int sockopt = 1; + int sockfd = socket(AF_INET, SOCK_STREAM, 0); + + if (sockfd < 0) { + pr_perror("Unable to open image socket"); + return -1; + } + + bzero((char *) &serv_addr, sizeof(serv_addr)); + serv_addr.sin_family = AF_INET; + serv_addr.sin_addr.s_addr = INADDR_ANY; + serv_addr.sin_port = htons(port); + + if (setsockopt( + sockfd, SOL_SOCKET, SO_REUSEADDR, &sockopt, sizeof(sockopt)) == -1) { + pr_perror("Unable to set SO_REUSEADDR"); + goto err; + } + + if (bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) { + pr_perror("Unable to bind image socket"); + goto err; + } + + if (listen(sockfd, DEFAULT_LISTEN)) { + pr_perror("Unable to listen image socket"); + goto err; + } + + return sockfd; +err: + close(sockfd); + return -1; +} + +int setup_TCP_client_socket(char *hostname, int port) +{ + int sockfd; + struct sockaddr_in serv_addr; + struct hostent *server; + + sockfd = socket(AF_INET, SOCK_STREAM, 0); + if (sockfd < 0) { + pr_perror("Unable to open remote image socket"); + return -1; + } + + server = gethostbyname(hostname); + if (server == NULL) { + pr_perror("Unable to get host by name (%s)", hostname); + goto err; + } + + bzero((char *) &serv_addr, sizeof(serv_addr)); + serv_addr.sin_family = AF_INET; + bcopy((char *) server->h_addr, + (char *) &serv_addr.sin_addr.s_addr, + server->h_length); + serv_addr.sin_port = htons(port); + + if (connect(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) { + pr_perror("Unable to connect to remote %s", hostname); + goto err; + } + + return sockfd; +err: + close(sockfd); + return -1; +} + +int setup_UNIX_server_socket(char *path) +{ + struct sockaddr_un addr; + int sockfd = socket(AF_UNIX, SOCK_STREAM, 0); + + if (sockfd < 0) { + pr_perror("Unable to open image socket"); + return -1; + } + + memset(&addr, 0, sizeof(addr)); + addr.sun_family = AF_UNIX; + strncpy(addr.sun_path, path, sizeof(addr.sun_path)-1); + + unlink(path); + + if (bind(sockfd, (struct sockaddr *)&addr, sizeof(addr)) == -1) { + pr_perror("Unable to bind image socket"); + goto err; + } + + if (listen(sockfd, 50) == -1) { + pr_perror("Unable to listen image socket"); + goto err; + } + + return sockfd; +err: + close(sockfd); + return -1; +} + +static int setup_UNIX_client_socket(char *path) +{ + struct sockaddr_un addr; + int sockfd = socket(AF_UNIX, SOCK_STREAM, 0); + + if (sockfd < 0) { + pr_perror("Unable to open local image socket"); + return -1; + } + + memset(&addr, 0, sizeof(addr)); + addr.sun_family = AF_UNIX; + strncpy(addr.sun_path, path, sizeof(addr.sun_path)-1); + + if (connect(sockfd, (struct sockaddr *)&addr, sizeof(addr)) < 0) { + pr_perror("Unable to connect to local socket: %s", path); + close(sockfd); + return -1; + } + + return sockfd; +} + +static int64_t pb_write_obj(int fd, void *obj, int type) +{ + struct cr_img img; + + img._x.fd = fd; + bfd_setraw(&img._x); + return pb_write_one(&img, obj, type); +} + +static int64_t pb_read_obj(int fd, void **pobj, int type) +{ + struct cr_img img; + + img._x.fd = fd; + bfd_setraw(&img._x); + return do_pb_read_one(&img, pobj, type, true); +} + +static int64_t write_header(int fd, char *snapshot_id, char *path, int flags) +{ + LocalImageEntry li = LOCAL_IMAGE_ENTRY__INIT; + + li.name = path; + li.snapshot_id = snapshot_id; + li.open_mode = flags; + return pb_write_obj(fd, &li, PB_LOCAL_IMAGE); +} + +static int64_t write_reply_header(int fd, int error) +{ + LocalImageReplyEntry lir = LOCAL_IMAGE_REPLY_ENTRY__INIT; + + lir.error = error; + return pb_write_obj(fd, &lir, PB_LOCAL_IMAGE_REPLY); +} + +int64_t write_remote_header(int fd, char *snapshot_id, char *path, int flags, uint64_t size) +{ + RemoteImageEntry ri = REMOTE_IMAGE_ENTRY__INIT; + + ri.name = path; + ri.snapshot_id = snapshot_id; + ri.open_mode = flags; + ri.size = size; + return pb_write_obj(fd, &ri, PB_REMOTE_IMAGE); +} + +static int64_t read_header(int fd, char *snapshot_id, char *path, int *flags) +{ + LocalImageEntry *li; + int ret = pb_read_obj(fd, (void **)&li, PB_LOCAL_IMAGE); + + if (ret > 0) { + strncpy(snapshot_id, li->snapshot_id, PATHLEN); + strncpy(path, li->name, PATHLEN); + *flags = li->open_mode; + } + free(li); + return ret; +} + +static int64_t read_reply_header(int fd, int *error) +{ + LocalImageReplyEntry *lir; + int ret = pb_read_obj(fd, (void **)&lir, PB_LOCAL_IMAGE_REPLY); + + if (ret > 0) + *error = lir->error; + free(lir); + return ret; +} + +int64_t read_remote_header(int fd, char *snapshot_id, char *path, int *flags, uint64_t *size) +{ + RemoteImageEntry *ri; + int ret = pb_read_obj(fd, (void **)&ri, PB_REMOTE_IMAGE); + + if (ret > 0) { + strncpy(snapshot_id, ri->snapshot_id, PATHLEN); + strncpy(path, ri->name, PATHLEN); + *flags = ri->open_mode; + *size = ri->size; + } + free(ri); + return ret; +} + +static struct wthread *new_worker(void) +{ + struct wthread *wt = malloc(sizeof(struct wthread)); + + if (!wt) { + pr_perror("Unable to allocate worker thread structure"); + goto err; + } + if (sem_init(&(wt->wakeup_sem), 0, 0) != 0) { + pr_perror("Workers semaphore init failed"); + goto err; + } + return wt; +err: + free(wt); + return NULL; +} + +static void add_worker(struct wthread *wt) +{ + pthread_mutex_lock(&workers_lock); + list_add_tail(&(wt->l), &workers_head); + pthread_mutex_unlock(&workers_lock); + sem_post(&workers_semph); +} + +void join_workers(void) +{ + struct wthread *wthread = NULL; + + while (! list_empty(&workers_head)) { + wthread = list_entry(workers_head.next, struct wthread, l); + pthread_join(wthread->tid, NULL); + list_del(&(wthread->l)); + free(wthread); + } +} + +static struct rimage *new_remote_image(char *path, char *snapshot_id) +{ + struct rimage *rimg = malloc(sizeof(struct rimage)); + struct rbuf *buf = malloc(sizeof(struct rbuf)); + + if (rimg == NULL || buf == NULL) { + pr_perror("Unable to allocate remote image structures"); + goto err; + } + + strncpy(rimg->path, path, PATHLEN -1 ); + rimg->path[PATHLEN - 1] = '\0'; + strncpy(rimg->snapshot_id, snapshot_id, PATHLEN - 1); + rimg->snapshot_id[PATHLEN - 1] = '\0'; + rimg->size = 0; + buf->nbytes = 0; + INIT_LIST_HEAD(&(rimg->buf_head)); + list_add_tail(&(buf->l), &(rimg->buf_head)); + rimg->curr_sent_buf = list_entry(rimg->buf_head.next, struct rbuf, l); + rimg->curr_sent_bytes = 0; + + if (pthread_mutex_init(&(rimg->in_use), NULL) != 0) { + pr_err("Remote image in_use mutex init failed\n"); + goto err; + } + return rimg; +err: + free(rimg); + free(buf); + return NULL; +} + +/* Clears a remote image struct for reusing it. */ +static struct rimage *clear_remote_image(struct rimage *rimg) +{ + pthread_mutex_lock(&(rimg->in_use)); + + while (!list_is_singular(&(rimg->buf_head))) { + struct rbuf *buf = list_entry(rimg->buf_head.prev, struct rbuf, l); + + list_del(rimg->buf_head.prev); + free(buf); + } + + list_entry(rimg->buf_head.next, struct rbuf, l)->nbytes = 0; + rimg->size = 0; + rimg->curr_sent_buf = list_entry(rimg->buf_head.next, struct rbuf, l); + rimg->curr_sent_bytes = 0; + + pthread_mutex_unlock(&(rimg->in_use)); + + return rimg; +} + +struct rimage *prepare_remote_image(char *path, char *snapshot_id, int open_mode) +{ + struct rimage *rimg = get_rimg_by_name(snapshot_id, path); + /* There is no record of such image, create a new one. */ + + if (rimg == NULL) + return new_remote_image(path, snapshot_id); + + pthread_mutex_lock(&rimg_lock); + list_del(&(rimg->l)); + pthread_mutex_unlock(&rimg_lock); + + /* There is already an image record. Simply return it for appending. */ + if (open_mode == O_APPEND) + return rimg; + /* There is already an image record. Clear it for writing. */ + else + return clear_remote_image(rimg); +} + +static void *process_local_read(struct wthread *wt) +{ + struct rimage *rimg = NULL; + int64_t ret; + /* TODO - split wait_for_image + * in cache - improve the parent stuf + * in proxy - do not wait for anything, return no file + */ + rimg = wait_for_image(wt); + if (!rimg) { + pr_info("No image %s:%s.\n", wt->path, wt->snapshot_id); + if (write_reply_header(wt->fd, ENOENT) < 0) + pr_perror("Error writing reply header for unexisting image"); + close(wt->fd); + return NULL; + } else { + if (write_reply_header(wt->fd, 0) < 0) { + pr_perror("Error writing reply header for %s:%s", + wt->path, wt->snapshot_id); + close(wt->fd); + return NULL; + } + } + + pthread_mutex_lock(&(rimg->in_use)); + ret = send_image(wt->fd, rimg, wt->flags, true); + if (ret < 0) + pr_perror("Unable to send %s:%s to CRIU (sent %ld bytes)", + rimg->path, rimg->snapshot_id, (long)ret); + else + pr_info("Finished sending %s:%s to CRIU (sent %ld bytes)\n", + rimg->path, rimg->snapshot_id, (long)ret); + pthread_mutex_unlock(&(rimg->in_use)); + return NULL; +} + +static void *process_local_image_connection(void *ptr) +{ + struct wthread *wt = (struct wthread *) ptr; + struct rimage *rimg = NULL; + int64_t ret; + + /* NOTE: the code inside this if is shared for both cache and proxy. */ + if (wt->flags == O_RDONLY) + return process_local_read(wt); + + /* NOTE: IMAGE PROXY ONLY. The image cache receives write connections + * through TCP (see accept_remote_image_connections). + */ + rimg = prepare_remote_image(wt->path, wt->snapshot_id, wt->flags); + ret = recv_image(wt->fd, rimg, 0, wt->flags, true); + if (ret < 0) { + pr_perror("Unable to receive %s:%s to CRIU (received %ld bytes)", + rimg->path, rimg->snapshot_id, (long)ret); + finalize_recv_rimg(NULL); + return NULL; + } + finalize_recv_rimg(rimg); + pr_info("Finished receiving %s:%s (received %ld bytes)\n", + rimg->path, rimg->snapshot_id, (long)ret); + + + if (!strncmp(rimg->path, DUMP_FINISH, sizeof(DUMP_FINISH))) { + finished = true; + shutdown(local_req_fd, SHUT_RD); + } else { + pthread_mutex_lock(&proxy_to_cache_lock); + ret = forward_image(rimg); + pthread_mutex_unlock(&proxy_to_cache_lock); + } + + finalize_fwd_rimg(); + if (ret < 0) { + pr_perror("Unable to forward %s:%s to Image Cache", + rimg->path, rimg->snapshot_id); + + return NULL; + } + + if (finished && !is_forwarding() && !is_receiving()) { + pr_info("Closing connection to Image Cache.\n"); + close(proxy_to_cache_fd); + unlock_workers(); + } + return NULL; +} + + +void *accept_local_image_connections(void *port) +{ + int fd = *((int *) port); + int cli_fd; + struct sockaddr_in cli_addr; + + socklen_t clilen = sizeof(cli_addr); + pthread_t tid; + struct wthread *wt; + + while (1) { + cli_fd = accept(fd, (struct sockaddr *) &cli_addr, &clilen); + if (cli_fd < 0) { + if (!finished) + pr_perror("Unable to accept local image connection"); + close(cli_fd); + return NULL; + } + + wt = new_worker(); + wt->fd = cli_fd; + + if (read_header(wt->fd, wt->snapshot_id, wt->path, &(wt->flags)) < 0) { + pr_err("Error reading local image header\n"); + goto err; + } + + pr_info("Received %s request for %s:%s\n", + wt->flags == O_RDONLY ? "read" : + wt->flags == O_APPEND ? "append" : "write", + wt->path, wt->snapshot_id); + + /* These function calls are used to avoid other threads from + * thinking that there are no more images are coming. + */ + if (wt->flags != O_RDONLY) { + prepare_recv_rimg(); + prepare_fwd_rimg(); + } + + if (pthread_create( + &tid, NULL, process_local_image_connection, (void *) wt)) { + pr_perror("Unable to create worker thread"); + goto err; + } + + wt->tid = tid; + add_worker(wt); + } +err: + close(cli_fd); + free(wt); + return NULL; +} + +/* Note: size is a limit on how much we want to read from the socket. Zero means + * read until the socket is closed. + */ +int64_t recv_image(int fd, struct rimage *rimg, uint64_t size, int flags, bool close_fd) +{ + struct rbuf *curr_buf = NULL; + int n; + + if (flags == O_APPEND) + curr_buf = list_entry(rimg->buf_head.prev, struct rbuf, l); + else + curr_buf = list_entry(rimg->buf_head.next, struct rbuf, l); + + while (1) { + n = read(fd, + curr_buf->buffer + curr_buf->nbytes, + size ? + min((int) (size - rimg->size), BUF_SIZE - curr_buf->nbytes) : + BUF_SIZE - curr_buf->nbytes); + if (n == 0) { + if (close_fd) + close(fd); + return rimg->size; + } else if (n > 0) { + curr_buf->nbytes += n; + rimg->size += n; + if (curr_buf->nbytes == BUF_SIZE) { + struct rbuf *buf = malloc(sizeof(struct rbuf)); + if (buf == NULL) { + pr_perror("Unable to allocate remote_buffer structures"); + if (close_fd) + close(fd); + return -1; + } + buf->nbytes = 0; + list_add_tail(&(buf->l), &(rimg->buf_head)); + curr_buf = buf; + } + if (size && rimg->size == size) { + if (close_fd) + close(fd); + return rimg->size; + } + } else { + pr_perror("Read on %s:%s socket failed", + rimg->path, rimg->snapshot_id); + if (close_fd) + close(fd); + return -1; + } + } +} + +int64_t send_image(int fd, struct rimage *rimg, int flags, bool close_fd) +{ + + int n, nblocks = 0; + + if (flags != O_APPEND) { + rimg->curr_sent_buf = list_entry(rimg->buf_head.next, struct rbuf, l); + rimg->curr_sent_bytes = 0; + } + + while (1) { + n = send( + fd, + rimg->curr_sent_buf->buffer + rimg->curr_sent_bytes, + min(BUF_SIZE, rimg->curr_sent_buf->nbytes) - rimg->curr_sent_bytes, + MSG_NOSIGNAL); + if (n > -1) { + rimg->curr_sent_bytes += n; + if (rimg->curr_sent_bytes == BUF_SIZE) { + rimg->curr_sent_buf = + list_entry(rimg->curr_sent_buf->l.next, struct rbuf, l); + nblocks++; + rimg->curr_sent_bytes = 0; + } else if (rimg->curr_sent_bytes == rimg->curr_sent_buf->nbytes) { + if (close_fd) + close(fd); + return nblocks*BUF_SIZE + rimg->curr_sent_buf->nbytes; + } + } else if (errno == EPIPE || errno == ECONNRESET) { + pr_warn("Connection for %s:%s was closed early than expected\n", + rimg->path, rimg->snapshot_id); + return 0; + } else { + pr_perror("Write on %s:%s socket failed", + rimg->path, rimg->snapshot_id); + return -1; + } + } + +} + int read_remote_image_connection(char *snapshot_id, char *path) { - int error; + int error = 0; int sockfd = setup_UNIX_client_socket(restoring ? DEFAULT_CACHE_SOCKET: DEFAULT_PROXY_SOCKET); if (sockfd < 0) { diff --git a/criu/include/img-remote-proto.h b/criu/include/img-remote-proto.h deleted file mode 100644 index 13cf6c6d2..000000000 --- a/criu/include/img-remote-proto.h +++ /dev/null @@ -1,88 +0,0 @@ -#ifndef IMAGE_REMOTE_PVT_H -#define IMAGE_REMOTE_PVT_H - -#include -#include -#include "common/list.h" -#include "img-remote.h" -#include -#include - -#define DEFAULT_LISTEN 50 -#ifndef PAGESIZE -#define PAGESIZE 4096 -#endif -#define BUF_SIZE PAGESIZE - -struct rbuf { - char buffer[BUF_SIZE]; - int nbytes; /* How many bytes are in the buffer. */ - struct list_head l; -}; - -struct rimage { - char path[PATHLEN]; - char snapshot_id[PATHLEN]; - struct list_head l; - struct list_head buf_head; - /* Used to track already sent buffers when the image is appended. */ - struct rbuf *curr_sent_buf; - /* Similar to the previous field. Number of bytes sent in 'curr_sent_buf'. */ - int curr_sent_bytes; - uint64_t size; /* number of bytes */ - pthread_mutex_t in_use; /* Only one operation at a time, per image. */ -}; - -struct wthread { - pthread_t tid; - struct list_head l; - /* Client fd. */ - int fd; - /* The path and snapshot_id identify the request handled by this thread. */ - char path[PATHLEN]; - char snapshot_id[PATHLEN]; - int flags; - /* This semph is used to wake this thread if the image is in memory.*/ - sem_t wakeup_sem; -}; - -/* This variable is used to indicate when the dump is finished. */ -extern bool finished; -/* This is the proxy to cache TCP socket FD. */ -extern int proxy_to_cache_fd; -/* This the unix socket used to fulfill local requests. */ -extern int local_req_fd; - -int init_daemon(bool background, struct rimage *(*wfi)(struct wthread*)); - -void join_workers(void); -void unlock_workers(void); - -void prepare_recv_rimg(void); -void finalize_recv_rimg(struct rimage *rimg); -struct rimage *prepare_remote_image(char *path, char *namesapce, int flags); -struct rimage *get_rimg_by_name(const char *snapshot_id, const char *path); -bool is_receiving(void); - -void *accept_local_image_connections(void *ptr); -void *accept_remote_image_connections(void *ptr); - -int64_t forward_image(struct rimage *rimg); -int64_t send_image(int fd, struct rimage *rimg, int flags, bool image_check); -int64_t recv_image(int fd, struct rimage *rimg, uint64_t size, int flags, bool image_check); - -int64_t pb_write_obj(int fd, void *obj, int type); -int64_t pb_read_obj(int fd, void **obj, int type); - -int64_t write_header(int fd, char *snapshot_id, char *path, int open_mode); -int64_t read_header(int fd, char *snapshot_id, char *path, int *open_mode); -int64_t write_reply_header(int fd, int error); -int64_t read_reply_header(int fd, int *error); -int64_t read_remote_header(int fd, char *snapshot_id, char *path, int *open_mode, uint64_t *size); -int64_t write_remote_header(int fd, char *snapshot_id, char *path, int open_mode, uint64_t size); - -int setup_TCP_server_socket(int port); -int setup_TCP_client_socket(char *hostname, int port); -int setup_UNIX_client_socket(char *path); -int setup_UNIX_server_socket(char *path); -#endif diff --git a/criu/include/img-remote.h b/criu/include/img-remote.h index 38acbd26d..1771d310b 100644 --- a/criu/include/img-remote.h +++ b/criu/include/img-remote.h @@ -1,6 +1,11 @@ #include #include +#include +#include "common/list.h" +#include +#include + #ifndef IMAGE_REMOTE_H #define IMAGE_REMOTE_H @@ -14,6 +19,76 @@ #define DEFAULT_CACHE_PORT 9996 #define DEFAULT_CACHE_HOST "localhost" +#define DEFAULT_LISTEN 50 +#ifndef PAGESIZE +#define PAGESIZE 4096 +#endif +#define BUF_SIZE PAGESIZE + +struct rbuf { + char buffer[BUF_SIZE]; + int nbytes; /* How many bytes are in the buffer. */ + struct list_head l; +}; + +struct rimage { + char path[PATHLEN]; + char snapshot_id[PATHLEN]; + struct list_head l; + struct list_head buf_head; + /* Used to track already sent buffers when the image is appended. */ + struct rbuf *curr_sent_buf; + /* Similar to the previous field. Number of bytes sent in 'curr_sent_buf'. */ + int curr_sent_bytes; + uint64_t size; /* number of bytes */ + pthread_mutex_t in_use; /* Only one operation at a time, per image. */ +}; + +struct wthread { + pthread_t tid; + struct list_head l; + /* Client fd. */ + int fd; + /* The path and snapshot_id identify the request handled by this thread. */ + char path[PATHLEN]; + char snapshot_id[PATHLEN]; + int flags; + /* This semph is used to wake this thread if the image is in memory.*/ + sem_t wakeup_sem; +}; + +/* This variable is used to indicate when the dump is finished. */ +extern bool finished; +/* This is the proxy to cache TCP socket FD. */ +extern int proxy_to_cache_fd; +/* This the unix socket used to fulfill local requests. */ +extern int local_req_fd; + +int init_daemon(bool background, struct rimage *(*wfi)(struct wthread*)); + +void join_workers(void); +void unlock_workers(void); + +void prepare_recv_rimg(void); +void finalize_recv_rimg(struct rimage *rimg); +struct rimage *prepare_remote_image(char *path, char *namesapce, int flags); +struct rimage *get_rimg_by_name(const char *snapshot_id, const char *path); +bool is_receiving(void); + +void *accept_local_image_connections(void *ptr); +void *accept_remote_image_connections(void *ptr); + +int64_t forward_image(struct rimage *rimg); +int64_t send_image(int fd, struct rimage *rimg, int flags, bool image_check); +int64_t recv_image(int fd, struct rimage *rimg, uint64_t size, int flags, bool image_check); + +int64_t read_remote_header(int fd, char *snapshot_id, char *path, int *open_mode, uint64_t *size); +int64_t write_remote_header(int fd, char *snapshot_id, char *path, int open_mode, uint64_t size); + +int setup_TCP_server_socket(int port); +int setup_TCP_client_socket(char *hostname, int port); +int setup_UNIX_server_socket(char *path); + /* Called by restore to get the fd correspondent to a particular path. This call * will block until the connection is received. */ From c4e16724e1af8d94675def5874e648a754e6f0df Mon Sep 17 00:00:00 2001 From: Pavel Emelyanov Date: Thu, 6 Jul 2017 12:38:13 +0300 Subject: [PATCH 016/249] page-read: Don't try to dedup from img cache/proxy It's simply impossible (yet), so emit a warning. Acked-by: Mike Rapoport Signed-off-by: Pavel Emelyanov Signed-off-by: Andrei Vagin --- criu/pagemap.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/criu/pagemap.c b/criu/pagemap.c index d2e6775c2..44cf0ce55 100644 --- a/criu/pagemap.c +++ b/criu/pagemap.c @@ -420,11 +420,8 @@ static int maybe_read_page_img_cache(struct page_read *pr, unsigned long vaddr, break; } - if (opts.auto_dedup) { - ret = punch_hole(pr, pr->pi_off, len, false); - if (ret == -1) - return -1; - } + if (opts.auto_dedup) + pr_warn_once("Can't dedup from image cache\n"); if (ret == 0 && pr->io_complete) ret = pr->io_complete(pr, vaddr, nr); From 0a1d8f0ee3da759634d4bb213f04f171776f83b3 Mon Sep 17 00:00:00 2001 From: Pavel Emelyanov Date: Thu, 6 Jul 2017 12:38:30 +0300 Subject: [PATCH 017/249] page-read: Don't check for cache/proxy in local case The opts.remote is always false in this code. Acked-by: Mike Rapoport Signed-off-by: Pavel Emelyanov Signed-off-by: Andrei Vagin --- criu/pagemap.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/criu/pagemap.c b/criu/pagemap.c index 44cf0ce55..35bfc4cd7 100644 --- a/criu/pagemap.c +++ b/criu/pagemap.c @@ -387,7 +387,7 @@ static int maybe_read_page_local(struct page_read *pr, unsigned long vaddr, * for us for urgent async read, just do the regular * cached read. */ - if ((flags & (PR_ASYNC|PR_ASAP)) == PR_ASYNC && !opts.remote) + if ((flags & (PR_ASYNC|PR_ASAP)) == PR_ASYNC) ret = pagemap_enqueue_iovec(pr, buf, len, &pr->async); else { ret = read_local_page(pr, vaddr, len, buf); From 6d30ccdb6a25eee163c9719ba923df3aca800154 Mon Sep 17 00:00:00 2001 From: Pavel Emelyanov Date: Thu, 6 Jul 2017 12:38:48 +0300 Subject: [PATCH 018/249] page-read: Warn about async read w/o completion cb Acked-by: Mike Rapoport Signed-off-by: Pavel Emelyanov Signed-off-by: Andrei Vagin --- criu/pagemap.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/criu/pagemap.c b/criu/pagemap.c index 35bfc4cd7..8d2e16320 100644 --- a/criu/pagemap.c +++ b/criu/pagemap.c @@ -444,6 +444,8 @@ static int read_page_complete(unsigned long img_id, unsigned long vaddr, int nr_ if (pr->io_complete) ret = pr->io_complete(pr, vaddr, nr_pages); + else + pr_warn_once("Remote page read w/o io_complete!\n"); return ret; } From 164ad4af3cc95cc09d13ee58562aac77125b6f13 Mon Sep 17 00:00:00 2001 From: Pavel Emelyanov Date: Thu, 6 Jul 2017 12:40:39 +0300 Subject: [PATCH 019/249] page-xfer: Normalize remote/local parent xfer checks We have two places to check for parent via page server -- as a part of _OPEN req and explicit req. Make the latter code be in-sync with the opening one. Acked-by: Mike Rapoport Signed-off-by: Pavel Emelyanov Signed-off-by: Andrei Vagin --- criu/page-xfer.c | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/criu/page-xfer.c b/criu/page-xfer.c index df1572403..d32de369c 100644 --- a/criu/page-xfer.c +++ b/criu/page-xfer.c @@ -526,16 +526,12 @@ int check_parent_local_xfer(int fd_type, unsigned long img_id) struct stat st; int ret, pfd; - if (opts.remote) { - pfd = get_curr_parent_snapshot_id_idx(); - pr_err("Unable to get parent snapshot id\n"); - if (pfd == -1) - return -1; - } else { - pfd = openat(get_service_fd(IMG_FD_OFF), CR_PARENT_LINK, O_RDONLY); - if (pfd < 0 && errno == ENOENT) - return 0; - } + if (opts.remote) + return get_curr_parent_snapshot_id_idx() == -1 ? 0 : 1; + + pfd = openat(get_service_fd(IMG_FD_OFF), CR_PARENT_LINK, O_RDONLY); + if (pfd < 0 && errno == ENOENT) + return 0; snprintf(path, sizeof(path), imgset_template[fd_type].fmt, img_id); ret = fstatat(pfd, path, &st, 0); @@ -598,8 +594,6 @@ int check_parent_page_xfer(int fd_type, unsigned long img_id) { if (opts.use_page_server) return check_parent_server_xfer(fd_type, img_id); - else if (opts.remote) - return get_curr_parent_snapshot_id_idx() == -1 ? 0 : 1; else return check_parent_local_xfer(fd_type, img_id); } From e57e2952d4fe7fce4d270330f9c2fadf53196748 Mon Sep 17 00:00:00 2001 From: Pavel Emelyanov Date: Tue, 15 Aug 2017 18:28:13 +0300 Subject: [PATCH 020/249] files,remote: Support chunked ghost files Those may not support sendfiles, so use read/write-s instead Signed-off-by: Pavel Emelyanov --- criu/files-reg.c | 61 ++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 49 insertions(+), 12 deletions(-) diff --git a/criu/files-reg.c b/criu/files-reg.c index 1a35166a5..d25ad8e4e 100644 --- a/criu/files-reg.c +++ b/criu/files-reg.c @@ -155,11 +155,30 @@ static int copy_chunk_from_file(int fd, int img, off_t off, size_t len) char *buf = NULL; int ret; - while (len > 0) { - ret = sendfile(img, fd, &off, len); - if (ret <= 0) { - pr_perror("Can't send ghost to image"); + if (opts.remote) { + buf = xmalloc(BUFSIZE); + if (!buf) return -1; + } + + while (len > 0) { + if (opts.remote) { + ret = pread(fd, buf, min_t(size_t, BUFSIZE, len), off); + if (ret <= 0) { + pr_perror("Can't read from ghost file"); + return -1; + } + if (write(img, buf, ret) != ret) { + pr_perror("Can't write to image"); + return -1; + } + off += ret; + } else { + ret = sendfile(img, fd, &off, len); + if (ret <= 0) { + pr_perror("Can't send ghost to image"); + return -1; + } } len -= ret; @@ -214,15 +233,33 @@ static int copy_chunk_to_file(int img, int fd, off_t off, size_t len) char *buf = NULL; int ret; - while (len > 0) { - if (lseek(fd, off, SEEK_SET) < 0) { - pr_perror("Can't seek file"); - return -1; - } - ret = sendfile(fd, img, NULL, len); - if (ret < 0) { - pr_perror("Can't send data"); + if (opts.remote) { + buf = xmalloc(BUFSIZE); + if (!buf) return -1; + } + + while (len > 0) { + if (opts.remote) { + ret = read(img, buf, min_t(size_t, BUFSIZE, len)); + if (ret <= 0) { + pr_perror("Can't read from image"); + return -1; + } + if (pwrite(fd, buf, ret, off) != ret) { + pr_perror("Can't write to file"); + return -1; + } + } else { + if (lseek(fd, off, SEEK_SET) < 0) { + pr_perror("Can't seek file"); + return -1; + } + ret = sendfile(fd, img, NULL, len); + if (ret < 0) { + pr_perror("Can't send data"); + return -1; + } } off += ret; From 7b3c8870488fa8028cb08408606640c77fea5b55 Mon Sep 17 00:00:00 2001 From: Radostin Stoyanov Date: Sat, 20 Jan 2018 19:07:26 +0000 Subject: [PATCH 021/249] img-remote: Fix typo in comment Signed-off-by: Radostin Stoyanov --- criu/img-remote.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/criu/img-remote.c b/criu/img-remote.c index f812c52d5..91e18a1a2 100644 --- a/criu/img-remote.c +++ b/criu/img-remote.c @@ -49,7 +49,7 @@ int local_req_fd; LIST_HEAD(snapshot_head); /* A snapshot is a dump or pre-dump operation. Each snapshot is identified by an - * ID which corresponds to the working directory specefied by the user. + * ID which corresponds to the working directory specified by the user. */ struct snapshot { char snapshot_id[PATHLEN]; @@ -531,7 +531,7 @@ static void *process_local_read(struct wthread *wt) struct rimage *rimg = NULL; int64_t ret; /* TODO - split wait_for_image - * in cache - improve the parent stuf + * in cache - improve the parent stuff * in proxy - do not wait for anything, return no file */ rimg = wait_for_image(wt); From 52ac13ea9995e3035319c1629f702f50d5fca6a4 Mon Sep 17 00:00:00 2001 From: Rodrigo Bruno Date: Mon, 14 May 2018 01:29:47 +0100 Subject: [PATCH 022/249] remote: Preparing image receive and send for asynchronous sockets. --- criu/img-remote.c | 93 +++++++++++++++++++++++++++++---------- criu/include/img-remote.h | 28 ++++++++++-- 2 files changed, 93 insertions(+), 28 deletions(-) diff --git a/criu/img-remote.c b/criu/img-remote.c index 91e18a1a2..7242b0c8d 100644 --- a/criu/img-remote.c +++ b/criu/img-remote.c @@ -470,8 +470,6 @@ static struct rimage *new_remote_image(char *path, char *snapshot_id) buf->nbytes = 0; INIT_LIST_HEAD(&(rimg->buf_head)); list_add_tail(&(buf->l), &(rimg->buf_head)); - rimg->curr_sent_buf = list_entry(rimg->buf_head.next, struct rbuf, l); - rimg->curr_sent_bytes = 0; if (pthread_mutex_init(&(rimg->in_use), NULL) != 0) { pr_err("Remote image in_use mutex init failed\n"); @@ -498,8 +496,6 @@ static struct rimage *clear_remote_image(struct rimage *rimg) list_entry(rimg->buf_head.next, struct rbuf, l)->nbytes = 0; rimg->size = 0; - rimg->curr_sent_buf = list_entry(rimg->buf_head.next, struct rbuf, l); - rimg->curr_sent_bytes = 0; pthread_mutex_unlock(&(rimg->in_use)); @@ -669,18 +665,43 @@ void *accept_local_image_connections(void *port) return NULL; } + +int64_t recv_image(int fd, struct rimage *rimg, uint64_t size, int flags, bool close_fd) +{ + int ret; + struct roperation *op = malloc(sizeof(struct roperation)); + bzero(op, sizeof(struct roperation)); + op->fd = fd; + op->rimg = rimg; + op->size = size; + op->flags = flags; + op->close_fd = close_fd; + op->curr_recv_buf = list_entry(rimg->buf_head.next, struct rbuf, l); + while ((ret = recv_image_async(op)) < 0) + if (ret != EAGAIN && ret != EWOULDBLOCK) + return -1; + return ret; +} + /* Note: size is a limit on how much we want to read from the socket. Zero means * read until the socket is closed. */ -int64_t recv_image(int fd, struct rimage *rimg, uint64_t size, int flags, bool close_fd) +int64_t recv_image_async(struct roperation *op) { - struct rbuf *curr_buf = NULL; + int fd = op->fd; + struct rimage *rimg = op->rimg; + uint64_t size = op->size; + int flags = op->flags; + bool close_fd = op->close_fd; + struct rbuf *curr_buf = op->curr_recv_buf; int n; - if (flags == O_APPEND) - curr_buf = list_entry(rimg->buf_head.prev, struct rbuf, l); - else - curr_buf = list_entry(rimg->buf_head.next, struct rbuf, l); + if (curr_buf == NULL) { + if (flags == O_APPEND) + curr_buf = list_entry(rimg->buf_head.prev, struct rbuf, l); + else + curr_buf = list_entry(rimg->buf_head.next, struct rbuf, l); + } while (1) { n = read(fd, @@ -712,6 +733,8 @@ int64_t recv_image(int fd, struct rimage *rimg, uint64_t size, int flags, bool c close(fd); return rimg->size; } + } else if (errno == EAGAIN || errno == EWOULDBLOCK) { + return errno; } else { pr_perror("Read on %s:%s socket failed", rimg->path, rimg->snapshot_id); @@ -724,37 +747,59 @@ int64_t recv_image(int fd, struct rimage *rimg, uint64_t size, int flags, bool c int64_t send_image(int fd, struct rimage *rimg, int flags, bool close_fd) { + int ret; + struct roperation *op = malloc(sizeof(struct roperation)); + bzero(op, sizeof(struct roperation)); + op->fd = fd; + op->rimg = rimg; + op->flags = flags; + op->close_fd = close_fd; + op->curr_sent_buf = list_entry(rimg->buf_head.next, struct rbuf, l); + while ((ret = send_image_async(op)) < 0) + if (ret != EAGAIN && ret != EWOULDBLOCK) + return -1; + return ret; +} - int n, nblocks = 0; +int64_t send_image_async(struct roperation *op) +{ + int fd = op->fd; + struct rimage *rimg = op->rimg; + int flags = op->flags; + bool close_fd = op->close_fd; + int n; if (flags != O_APPEND) { - rimg->curr_sent_buf = list_entry(rimg->buf_head.next, struct rbuf, l); - rimg->curr_sent_bytes = 0; + op->curr_sent_buf = list_entry(rimg->buf_head.next, struct rbuf, l); + op->curr_sent_bytes = 0; } while (1) { n = send( fd, - rimg->curr_sent_buf->buffer + rimg->curr_sent_bytes, - min(BUF_SIZE, rimg->curr_sent_buf->nbytes) - rimg->curr_sent_bytes, + op->curr_sent_buf->buffer + op->curr_sent_bytes, + min(BUF_SIZE, op->curr_sent_buf->nbytes) - op->curr_sent_bytes, MSG_NOSIGNAL); if (n > -1) { - rimg->curr_sent_bytes += n; - if (rimg->curr_sent_bytes == BUF_SIZE) { - rimg->curr_sent_buf = - list_entry(rimg->curr_sent_buf->l.next, struct rbuf, l); - nblocks++; - rimg->curr_sent_bytes = 0; - } else if (rimg->curr_sent_bytes == rimg->curr_sent_buf->nbytes) { + op->curr_sent_bytes += n; + if (op->curr_sent_bytes == BUF_SIZE) { + op->curr_sent_buf = + list_entry(op->curr_sent_buf->l.next, struct rbuf, l); + op->nblocks++; + op->curr_sent_bytes = 0; + } else if (op->curr_sent_bytes == op->curr_sent_buf->nbytes) { if (close_fd) close(fd); - return nblocks*BUF_SIZE + rimg->curr_sent_buf->nbytes; + return op->nblocks*BUF_SIZE + op->curr_sent_buf->nbytes; } } else if (errno == EPIPE || errno == ECONNRESET) { pr_warn("Connection for %s:%s was closed early than expected\n", rimg->path, rimg->snapshot_id); return 0; - } else { + } else if (errno == EAGAIN || errno == EWOULDBLOCK) { + return errno; + } + else { pr_perror("Write on %s:%s socket failed", rimg->path, rimg->snapshot_id); return -1; diff --git a/criu/include/img-remote.h b/criu/include/img-remote.h index 1771d310b..0947e7f0c 100644 --- a/criu/include/img-remote.h +++ b/criu/include/img-remote.h @@ -36,10 +36,6 @@ struct rimage { char snapshot_id[PATHLEN]; struct list_head l; struct list_head buf_head; - /* Used to track already sent buffers when the image is appended. */ - struct rbuf *curr_sent_buf; - /* Similar to the previous field. Number of bytes sent in 'curr_sent_buf'. */ - int curr_sent_bytes; uint64_t size; /* number of bytes */ pthread_mutex_t in_use; /* Only one operation at a time, per image. */ }; @@ -57,6 +53,28 @@ struct wthread { sem_t wakeup_sem; }; +/* Structure that describes the state of a remote operation on remote images. */ +struct roperation { + /* File descriptor being used. */ + int fd; + /* Remote image being used. */ + struct rimage *rimg; + /* Flags for the operation. */ + int flags; + /* If fd should be closed when the operation is done. */ + bool close_fd; + /* Note: recv operation only. How much bytes should be received. */ + uint64_t size; + /* Note: recv operation only. Buffer being writen. */ + struct rbuf *curr_recv_buf; + /* Note: send operation only. Number of blocks already sent. */ + int nblocks; + /* Note: send operation only. Pointer to buffer being sent. */ + struct rbuf *curr_sent_buf; + /* Note: send operation only. Number of bytes sent in 'curr_send_buf. */ + uint64_t curr_sent_bytes; +}; + /* This variable is used to indicate when the dump is finished. */ extern bool finished; /* This is the proxy to cache TCP socket FD. */ @@ -80,7 +98,9 @@ void *accept_remote_image_connections(void *ptr); int64_t forward_image(struct rimage *rimg); int64_t send_image(int fd, struct rimage *rimg, int flags, bool image_check); +int64_t send_image_async(struct roperation *op); int64_t recv_image(int fd, struct rimage *rimg, uint64_t size, int flags, bool image_check); +int64_t recv_image_async(struct roperation *op); int64_t read_remote_header(int fd, char *snapshot_id, char *path, int *open_mode, uint64_t *size); int64_t write_remote_header(int fd, char *snapshot_id, char *path, int open_mode, uint64_t size); From f3731533c0921f9b340f6aff06c1fc849a09de5e Mon Sep 17 00:00:00 2001 From: Rodrigo Bruno Date: Mon, 14 May 2018 01:29:48 +0100 Subject: [PATCH 023/249] remote: Unix socket for local connections is async. --- criu/img-cache.c | 2 + criu/img-proxy.c | 2 + criu/img-remote.c | 158 ++++++++++++++++++++++++++++---------- criu/include/img-remote.h | 1 + 4 files changed, 124 insertions(+), 39 deletions(-) diff --git a/criu/img-cache.c b/criu/img-cache.c index 7020a30f0..7b828b9b6 100644 --- a/criu/img-cache.c +++ b/criu/img-cache.c @@ -125,6 +125,8 @@ int image_cache(bool background, char *local_cache_path, unsigned short cache_wr return -1; } + socket_set_non_blocking(local_req_fd); + if (init_daemon(background, wait_for_image)) { pr_perror("Unable to initialize daemon"); return -1; diff --git a/criu/img-proxy.c b/criu/img-proxy.c index f56073b4e..b63d69a03 100644 --- a/criu/img-proxy.c +++ b/criu/img-proxy.c @@ -60,6 +60,8 @@ int image_proxy(bool background, char *local_proxy_path, char *fwd_host, unsigne return -1; } + socket_set_non_blocking(local_req_fd); + if (opts.ps_socket != -1) { proxy_to_cache_fd = opts.ps_socket; pr_info("Re-using ps socket %d\n", proxy_to_cache_fd); diff --git a/criu/img-remote.c b/criu/img-remote.c index 7242b0c8d..ed8587996 100644 --- a/criu/img-remote.c +++ b/criu/img-remote.c @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include "xmalloc.h" @@ -24,6 +25,7 @@ #include "image.h" #define PB_LOCAL_IMAGE_SIZE PATHLEN +#define EPOLL_MAX_EVENTS 50 static char *snapshot_id; bool restoring = true; @@ -236,6 +238,8 @@ int setup_TCP_server_socket(int port) return -1; } + + int setup_TCP_client_socket(char *hostname, int port) { int sockfd; @@ -272,10 +276,33 @@ int setup_TCP_client_socket(char *hostname, int port) return -1; } +int event_set(int epoll_fd, int op, int fd, uint32_t events, void *data) +{ + struct epoll_event event; + event.events = events; + event.data.ptr = data; + // TODO - check if this is okay to send a stack allocated object! + return epoll_ctl(epoll_fd, op, fd, &event); +} + +void socket_set_non_blocking(int fd) +{ + int flags = fcntl(fd, F_GETFL, NULL); + + if (flags < 0) { + pr_perror("Failed to obtain flags from fd %d", fd); + return; + } + flags |= O_NONBLOCK; + + if (fcntl(fd, F_SETFL, flags) < 0) + pr_perror("Failed to set flags for fd %d", fd); +} + int setup_UNIX_server_socket(char *path) { struct sockaddr_un addr; - int sockfd = socket(AF_UNIX, SOCK_STREAM, 0); + int sockfd = socket(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK, 0); if (sockfd < 0) { pr_perror("Unable to open image socket"); @@ -609,59 +636,111 @@ static void *process_local_image_connection(void *ptr) return NULL; } - -void *accept_local_image_connections(void *port) +void handle_local_accept(int fd) { - int fd = *((int *) port); + struct wthread *wt = NULL; int cli_fd; + pthread_t tid; struct sockaddr_in cli_addr; - socklen_t clilen = sizeof(cli_addr); - pthread_t tid; - struct wthread *wt; - while (1) { - cli_fd = accept(fd, (struct sockaddr *) &cli_addr, &clilen); - if (cli_fd < 0) { - if (!finished) - pr_perror("Unable to accept local image connection"); - close(cli_fd); - return NULL; - } + cli_fd = accept(fd, (struct sockaddr *) &cli_addr, &clilen); + if (cli_fd < 0) { + pr_perror("Unable to accept local image connection"); + goto err; + } - wt = new_worker(); - wt->fd = cli_fd; + wt = new_worker(); + wt->fd = cli_fd; - if (read_header(wt->fd, wt->snapshot_id, wt->path, &(wt->flags)) < 0) { - pr_err("Error reading local image header\n"); - goto err; - } + if (read_header(wt->fd, wt->snapshot_id, wt->path, &(wt->flags)) < 0) { + pr_err("Error reading local image header\n"); + goto err; + } + + /* These function calls are used to avoid other threads from + * thinking that there are no more images are coming. + */ + if (wt->flags != O_RDONLY) { + prepare_recv_rimg(); + prepare_fwd_rimg(); + } - pr_info("Received %s request for %s:%s\n", - wt->flags == O_RDONLY ? "read" : + pr_info("Received %s request for %s:%s\n", + wt->flags == O_RDONLY ? "read" : wt->flags == O_APPEND ? "append" : "write", - wt->path, wt->snapshot_id); - - /* These function calls are used to avoid other threads from - * thinking that there are no more images are coming. - */ - if (wt->flags != O_RDONLY) { - prepare_recv_rimg(); - prepare_fwd_rimg(); - } + wt->path, wt->snapshot_id); - if (pthread_create( - &tid, NULL, process_local_image_connection, (void *) wt)) { - pr_perror("Unable to create worker thread"); - goto err; - } - wt->tid = tid; - add_worker(wt); + if (pthread_create( + &tid, NULL, process_local_image_connection, (void *) wt)) { + pr_perror("Unable to create worker thread"); + goto err; } + wt->tid = tid; + add_worker(wt); + return; err: close(cli_fd); free(wt); +} + + +void *accept_local_image_connections(void *port) +{ + int fd = *((int *) port); + int epoll_fd; + struct epoll_event *events; + int ret; + + epoll_fd = epoll_create(EPOLL_MAX_EVENTS); + if (epoll_fd < 0) { + pr_perror("Unable to open epoll"); + return NULL; + } + + events = calloc(EPOLL_MAX_EVENTS, sizeof(struct epoll_event)); + if (events == NULL) { + pr_perror("Failed to allocated epoll events"); + goto end; + } + + ret = event_set(epoll_fd, EPOLL_CTL_ADD, fd, EPOLLIN, &fd); + if (ret) { + pr_perror("Failed to set event for epoll"); + goto end; + } + + while (1) { + int n_events = epoll_wait(epoll_fd, events, EPOLL_MAX_EVENTS, -1); + if (n_events < 0) { + pr_perror("Failed to epoll wait"); + goto end; + } + + for (int i = 0; i < n_events; i++) { + if (events[i].data.ptr == &fd) { + if ( events[i].events & EPOLLHUP || + events[i].events & EPOLLERR) { + if (!finished) + pr_perror("Unable to accept more local image connections"); + goto end; + } + // accept + pr_perror("Calling accept %d", i); + handle_local_accept(fd); + } + else { + // TODO - handle write/read + pr_perror("Event on unexpected file descripor"); + goto end; + } + } + } +end: + close(epoll_fd); + close(fd); + free(events); return NULL; } @@ -680,6 +759,7 @@ int64_t recv_image(int fd, struct rimage *rimg, uint64_t size, int flags, bool c while ((ret = recv_image_async(op)) < 0) if (ret != EAGAIN && ret != EWOULDBLOCK) return -1; + free(op); return ret; } diff --git a/criu/include/img-remote.h b/criu/include/img-remote.h index 0947e7f0c..779a137fc 100644 --- a/criu/include/img-remote.h +++ b/criu/include/img-remote.h @@ -108,6 +108,7 @@ int64_t write_remote_header(int fd, char *snapshot_id, char *path, int open_mode int setup_TCP_server_socket(int port); int setup_TCP_client_socket(char *hostname, int port); int setup_UNIX_server_socket(char *path); +void socket_set_non_blocking(int fd); /* Called by restore to get the fd correspondent to a particular path. This call * will block until the connection is received. From df0b0fb26caebbca115783544ad1e55492a8598c Mon Sep 17 00:00:00 2001 From: Rodrigo Bruno Date: Mon, 14 May 2018 01:29:49 +0100 Subject: [PATCH 024/249] remote: Unblocking implementation of img cache and proxy seems to be working. --- criu/img-cache.c | 137 +----- criu/img-proxy.c | 65 +-- criu/img-remote.c | 961 +++++++++++++++++++++++--------------- criu/include/img-remote.h | 53 +-- 4 files changed, 629 insertions(+), 587 deletions(-) diff --git a/criu/img-cache.c b/criu/img-cache.c index 7b828b9b6..c941f14e2 100644 --- a/criu/img-cache.c +++ b/criu/img-cache.c @@ -8,105 +8,25 @@ #include #include "cr_options.h" -static struct rimage *wait_for_image(struct wthread *wt) +int accept_proxy_to_cache(int sockfd) { - struct rimage *result; + struct sockaddr_in cli_addr; + socklen_t clilen = sizeof(cli_addr); + int proxy_fd = accept(sockfd, (struct sockaddr *) &cli_addr, &clilen); - if (!strncmp(wt->path, RESTORE_FINISH, sizeof(RESTORE_FINISH))) { - finished = true; - shutdown(local_req_fd, SHUT_RD); - return NULL; - } - - result = get_rimg_by_name(wt->snapshot_id, wt->path); - if (result != NULL && result->size > 0) - return result; - - /* The file does not exist and we do not expect new files */ - if (finished && !is_receiving()) - return NULL; - - /* NOTE: at this point, when the thread wakes up, either the image is - * already in memory or it will never come (the dump is finished). - */ - sem_wait(&(wt->wakeup_sem)); - result = get_rimg_by_name(wt->snapshot_id, wt->path); - if (result != NULL && result->size > 0) - return result; - else - return NULL; -} - -/* The image cache creates a thread that calls this function. It waits for remote - * images from the image-cache. - */ -void *accept_remote_image_connections(void *port) -{ - int fd = *((int *) port); - struct sockaddr_in cli_addr; - socklen_t clilen = sizeof(cli_addr); - char snapshot_id_buf[PATHLEN], path_buf[PATHLEN]; - uint64_t size; - int64_t ret; - int flags, proxy_fd; - struct rimage *rimg; - - proxy_fd = accept(fd, (struct sockaddr *) &cli_addr, &clilen); - if (proxy_fd < 0) { - pr_perror("Unable to accept remote image connection from image proxy"); - return NULL; - } - while (1) { - ret = read_remote_header(proxy_fd, snapshot_id_buf, path_buf, &flags, &size); - if (ret < 0) { - pr_perror("Unable to receive remote header from image proxy"); - return NULL; - } - /* This means that the no more images are coming. */ - else if (!ret) { - pr_info("Image Proxy connection closed.\n"); - finished = true; - unlock_workers(); - return NULL; - } - - pr_info("Received %s request for %s:%s\n", - flags == O_RDONLY ? "read" : - flags == O_APPEND ? "append" : "write", - path_buf, snapshot_id_buf); + if (proxy_fd < 0) { + pr_perror("Unable to accept remote image connection from image proxy"); + return -1; + } - rimg = prepare_remote_image(path_buf, snapshot_id_buf, flags); - - prepare_recv_rimg(); - if (!size) - ret = 0; - else - ret = recv_image(proxy_fd, rimg, size, flags, false); - if (ret < 0) { - pr_perror("Unable to receive %s:%s from image proxy", - rimg->path, rimg->snapshot_id); - finalize_recv_rimg(NULL); - return NULL; - } else if (ret != size) { - pr_perror("Unable to receive %s:%s from image proxy (received %ld bytes, expected %lu bytes)", - rimg->path, rimg->snapshot_id, (long)ret, (unsigned long)size); - finalize_recv_rimg(NULL); - return NULL; - } - finalize_recv_rimg(rimg); - - pr_info("Finished receiving %s:%s (received %ld bytes)\n", - rimg->path, rimg->snapshot_id, (long)ret); - } + return proxy_fd; } int image_cache(bool background, char *local_cache_path, unsigned short cache_write_port) { - pthread_t local_req_thr, remote_req_thr; - pr_info("Proxy to Cache Port %d, CRIU to Cache Path %s\n", cache_write_port, local_cache_path); - + restoring = true; if (opts.ps_socket != -1) { proxy_to_cache_fd = opts.ps_socket; @@ -117,40 +37,29 @@ int image_cache(bool background, char *local_cache_path, unsigned short cache_wr pr_perror("Unable to open proxy to cache TCP socket"); return -1; } + // Wait to accept connection from proxy. + proxy_to_cache_fd = accept_proxy_to_cache(proxy_to_cache_fd); + if (proxy_to_cache_fd < 0) + return -1; // TODO - should close other sockets. } + pr_info("Cache is connected to Proxy through fd %d\n", proxy_to_cache_fd); + local_req_fd = setup_UNIX_server_socket(local_cache_path); if (local_req_fd < 0) { pr_perror("Unable to open cache to proxy UNIX socket"); - return -1; - } - - socket_set_non_blocking(local_req_fd); + return -1; // TODO - should close other sockets. - if (init_daemon(background, wait_for_image)) { - pr_perror("Unable to initialize daemon"); - return -1; } - if (pthread_create( - &remote_req_thr, - NULL, accept_remote_image_connections, - (void *) &proxy_to_cache_fd)) { - pr_perror("Unable to create remote requests thread"); - return -1; - } - if (pthread_create( - &local_req_thr, - NULL, - accept_local_image_connections, - (void *) &local_req_fd)) { - pr_perror("Unable to create local requests thread"); - return -1; + if (background) { + if (daemon(1, 0) == -1) { + pr_perror("Can't run service server in the background"); + return -1; + } } - pthread_join(remote_req_thr, NULL); - pthread_join(local_req_thr, NULL); - join_workers(); + accept_image_connections(); pr_info("Finished image cache."); return 0; } diff --git a/criu/img-proxy.c b/criu/img-proxy.c index b63d69a03..9551a7dcb 100644 --- a/criu/img-proxy.c +++ b/criu/img-proxy.c @@ -8,51 +8,11 @@ #include #include "cr_options.h" -static struct rimage *wait_for_image(struct wthread *wt) -{ - return get_rimg_by_name(wt->snapshot_id, wt->path); -} - -int64_t forward_image(struct rimage *rimg) -{ - int64_t ret; - int fd = proxy_to_cache_fd; - - pthread_mutex_lock(&(rimg->in_use)); - pr_info("Forwarding %s:%s (%lu bytes)\n", - rimg->path, rimg->snapshot_id, (unsigned long)rimg->size); - if (write_remote_header( - fd, rimg->snapshot_id, rimg->path, O_APPEND, rimg->size) < 0) { - pr_perror("Error writing header for %s:%s", - rimg->path, rimg->snapshot_id); - pthread_mutex_unlock(&(rimg->in_use)); - return -1; - } - - ret = send_image(fd, rimg, O_APPEND, false); - if (ret < 0) { - pr_perror("Unable to send %s:%s to image cache", - rimg->path, rimg->snapshot_id); - pthread_mutex_unlock(&(rimg->in_use)); - return -1; - } else if (ret != rimg->size) { - pr_perror("Unable to send %s:%s to image proxy (sent %ld bytes, expected %lu bytes", - rimg->path, rimg->snapshot_id, (long)ret, (unsigned long)rimg->size); - pthread_mutex_unlock(&(rimg->in_use)); - return -1; - } - pr_info("Finished forwarding %s:%s (sent %lu bytes)\n", - rimg->path, rimg->snapshot_id, (unsigned long)rimg->size); - pthread_mutex_unlock(&(rimg->in_use)); - return ret; -} - int image_proxy(bool background, char *local_proxy_path, char *fwd_host, unsigned short fwd_port) { - pthread_t local_req_thr; - pr_info("CRIU to Proxy Path: %s, Cache Address %s:%hu\n", local_proxy_path, fwd_host, fwd_port); + restoring = false; local_req_fd = setup_UNIX_server_socket(local_proxy_path); if (local_req_fd < 0) { @@ -60,8 +20,6 @@ int image_proxy(bool background, char *local_proxy_path, char *fwd_host, unsigne return -1; } - socket_set_non_blocking(local_req_fd); - if (opts.ps_socket != -1) { proxy_to_cache_fd = opts.ps_socket; pr_info("Re-using ps socket %d\n", proxy_to_cache_fd); @@ -69,24 +27,21 @@ int image_proxy(bool background, char *local_proxy_path, char *fwd_host, unsigne proxy_to_cache_fd = setup_TCP_client_socket(fwd_host, fwd_port); if (proxy_to_cache_fd < 0) { pr_perror("Unable to open proxy to cache TCP socket"); - return -1; + return -1; // TODO - should close other sockets. } } - if (init_daemon(background, wait_for_image)) - return -1; + pr_info("Proxy is connected to Cache through fd %d\n", proxy_to_cache_fd); - if (pthread_create( - &local_req_thr, - NULL, - accept_local_image_connections, - (void *) &local_req_fd)) { - pr_perror("Unable to create local requests thread"); - return -1; + if (background) { + if (daemon(1, 0) == -1) { + pr_perror("Can't run service server in the background"); + return -1; + } } - pthread_join(local_req_thr, NULL); - join_workers(); + // TODO - local_req_fd and proxy_to_cache_fd send as args. + accept_image_connections(); pr_info("Finished image proxy."); return 0; } diff --git a/criu/img-remote.c b/criu/img-remote.c index ed8587996..218c29684 100644 --- a/criu/img-remote.c +++ b/criu/img-remote.c @@ -27,29 +27,35 @@ #define PB_LOCAL_IMAGE_SIZE PATHLEN #define EPOLL_MAX_EVENTS 50 -static char *snapshot_id; -bool restoring = true; - +// List of images already in memory. LIST_HEAD(rimg_head); -pthread_mutex_t rimg_lock = PTHREAD_MUTEX_INITIALIZER; -pthread_mutex_t proxy_to_cache_lock = PTHREAD_MUTEX_INITIALIZER; +// List of local operations currently in-progess. +LIST_HEAD(rop_inprogress); -LIST_HEAD(workers_head); -pthread_mutex_t workers_lock = PTHREAD_MUTEX_INITIALIZER; -sem_t workers_semph; +// List of local operations pending (reads on the restore side for images that +// still haven't arrived). -struct rimage * (*wait_for_image) (struct wthread *wt); +LIST_HEAD(rop_pending); +// List of images waiting to be forwarded. The head of the list is currently +// being forwarded. +LIST_HEAD(rop_forwarding); + +// List of snapshots (useful when doing incremental restores/dumps +LIST_HEAD(snapshot_head); -bool finished = false; -int writing = 0; -int forwarding = 0; +static char *snapshot_id; +bool restoring = true; // TODO - check where this is used! +// TODO - split this into two vars, recv_from_proxy, send_to_cache +bool forwarding = false; // TODO - true if proxy_to_cache_fd is being used. +bool finished_local = false; +bool finished_remote = false; int proxy_to_cache_fd; int local_req_fd; +int epoll_fd; +struct epoll_event *events; -LIST_HEAD(snapshot_head); - /* A snapshot is a dump or pre-dump operation. Each snapshot is identified by an * ID which corresponds to the working directory specified by the user. */ @@ -79,125 +85,27 @@ struct rimage *get_rimg_by_name(const char *snapshot_id, const char *path) { struct rimage *rimg = NULL; - pthread_mutex_lock(&rimg_lock); list_for_each_entry(rimg, &rimg_head, l) { if (!strncmp(rimg->path, path, PATHLEN) && !strncmp(rimg->snapshot_id, snapshot_id, PATHLEN)) { - pthread_mutex_unlock(&rimg_lock); return rimg; } } - pthread_mutex_unlock(&rimg_lock); - return NULL; -} - -static struct wthread *get_wt_by_name(const char *snapshot_id, const char *path) -{ - struct wthread *wt = NULL; - - pthread_mutex_lock(&workers_lock); - list_for_each_entry(wt, &workers_head, l) { - if (!strncmp(wt->path, path, PATHLEN) && - !strncmp(wt->snapshot_id, snapshot_id, PATHLEN)) { - pthread_mutex_unlock(&workers_lock); - return wt; - } - } - pthread_mutex_unlock(&workers_lock); return NULL; } -static int init_sync_structures(void) -{ - if (sem_init(&workers_semph, 0, 0) != 0) { - pr_perror("Workers semaphore init failed"); - return -1; - } - - return 0; -} - -void prepare_recv_rimg(void) -{ - pthread_mutex_lock(&rimg_lock); - writing++; - pthread_mutex_unlock(&rimg_lock); -} - -void finalize_recv_rimg(struct rimage *rimg) -{ - - pthread_mutex_lock(&rimg_lock); - - if (rimg) - list_add_tail(&(rimg->l), &rimg_head); - writing--; - pthread_mutex_unlock(&rimg_lock); - /* Wake thread waiting for this image. */ - if (rimg) { - struct wthread *wt = get_wt_by_name(rimg->snapshot_id, rimg->path); - if (wt) - sem_post(&(wt->wakeup_sem)); - } -} - -bool is_receiving(void) -{ - int ret; - - pthread_mutex_lock(&rimg_lock); - ret = writing; - pthread_mutex_unlock(&rimg_lock); - return ret > 0; -} - -static void prepare_fwd_rimg(void) -{ - pthread_mutex_lock(&rimg_lock); - forwarding++; - pthread_mutex_unlock(&rimg_lock); -} - -static void finalize_fwd_rimg(void) -{ - pthread_mutex_lock(&rimg_lock); - forwarding--; - pthread_mutex_unlock(&rimg_lock); -} - -static bool is_forwarding(void) -{ - int ret; - - pthread_mutex_lock(&rimg_lock); - ret = forwarding; - pthread_mutex_unlock(&rimg_lock); - return ret > 0; -} - -/* This function is called when no more images are coming. Threads still waiting - * for images will be awaken to send a ENOENT (no such file) to the requester. - */ -void unlock_workers(void) +struct roperation *get_rop_by_name( + struct list_head *head, const char *snapshot_id, const char *path) { - struct wthread *wt = NULL; - - pthread_mutex_lock(&workers_lock); - list_for_each_entry(wt, &workers_head, l) - sem_post(&(wt->wakeup_sem)); - pthread_mutex_unlock(&workers_lock); -} + struct roperation *rop = NULL; -int init_daemon(bool background, struct rimage *(*wfi)(struct wthread*)) -{ - if (background) { - if (daemon(1, 0) == -1) { - pr_perror("Can't run service server in the background"); - return -1; + list_for_each_entry(rop, head, l) { + if (!strncmp(rop->path, path, PATHLEN) && + !strncmp(rop->snapshot_id, snapshot_id, PATHLEN)) { + return rop; } } - wait_for_image = wfi; - return init_sync_structures(); + return NULL; } int setup_TCP_server_socket(int port) @@ -278,11 +186,15 @@ int setup_TCP_client_socket(char *hostname, int port) int event_set(int epoll_fd, int op, int fd, uint32_t events, void *data) { + int ret; struct epoll_event event; event.events = events; event.data.ptr = data; - // TODO - check if this is okay to send a stack allocated object! - return epoll_ctl(epoll_fd, op, fd, &event); + + ret = epoll_ctl(epoll_fd, op, fd, &event); + if (ret) + pr_perror("[fd=%d] Unable to set event", fd); + return ret; } void socket_set_non_blocking(int fd) @@ -299,6 +211,20 @@ void socket_set_non_blocking(int fd) pr_perror("Failed to set flags for fd %d", fd); } +void socket_set_blocking(int fd) +{ + int flags = fcntl(fd, F_GETFL, NULL); + + if (flags < 0) { + pr_perror("Failed to obtain flags from fd %d", fd); + return; + } + flags &= (~O_NONBLOCK); + + if (fcntl(fd, F_SETFL, flags) < 0) + pr_perror("Failed to set flags for fd %d", fd); +} + int setup_UNIX_server_socket(char *path) { struct sockaddr_un addr; @@ -441,48 +367,10 @@ int64_t read_remote_header(int fd, char *snapshot_id, char *path, int *flags, ui return ret; } -static struct wthread *new_worker(void) -{ - struct wthread *wt = malloc(sizeof(struct wthread)); - - if (!wt) { - pr_perror("Unable to allocate worker thread structure"); - goto err; - } - if (sem_init(&(wt->wakeup_sem), 0, 0) != 0) { - pr_perror("Workers semaphore init failed"); - goto err; - } - return wt; -err: - free(wt); - return NULL; -} - -static void add_worker(struct wthread *wt) -{ - pthread_mutex_lock(&workers_lock); - list_add_tail(&(wt->l), &workers_head); - pthread_mutex_unlock(&workers_lock); - sem_post(&workers_semph); -} - -void join_workers(void) -{ - struct wthread *wthread = NULL; - - while (! list_empty(&workers_head)) { - wthread = list_entry(workers_head.next, struct wthread, l); - pthread_join(wthread->tid, NULL); - list_del(&(wthread->l)); - free(wthread); - } -} - static struct rimage *new_remote_image(char *path, char *snapshot_id) { - struct rimage *rimg = malloc(sizeof(struct rimage)); - struct rbuf *buf = malloc(sizeof(struct rbuf)); + struct rimage *rimg = calloc(1, sizeof(struct rimage)); + struct rbuf *buf = calloc(1, sizeof(struct rbuf)); if (rimg == NULL || buf == NULL) { pr_perror("Unable to allocate remote image structures"); @@ -490,18 +378,13 @@ static struct rimage *new_remote_image(char *path, char *snapshot_id) } strncpy(rimg->path, path, PATHLEN -1 ); - rimg->path[PATHLEN - 1] = '\0'; strncpy(rimg->snapshot_id, snapshot_id, PATHLEN - 1); + rimg->path[PATHLEN - 1] = '\0'; rimg->snapshot_id[PATHLEN - 1] = '\0'; - rimg->size = 0; - buf->nbytes = 0; INIT_LIST_HEAD(&(rimg->buf_head)); list_add_tail(&(buf->l), &(rimg->buf_head)); + rimg->curr_fwd_buf = buf; - if (pthread_mutex_init(&(rimg->in_use), NULL) != 0) { - pr_err("Remote image in_use mutex init failed\n"); - goto err; - } return rimg; err: free(rimg); @@ -509,11 +392,56 @@ static struct rimage *new_remote_image(char *path, char *snapshot_id) return NULL; } +static struct roperation *new_remote_operation( + char *path, char *snapshot_id, int cli_fd, int flags, bool close_fd) +{ + struct roperation *rop = calloc(1, sizeof(struct roperation)); + + if (rop == NULL) { + pr_perror("Unable to allocate remote operation structures"); + return NULL; + } + strncpy(rop->path, path, PATHLEN -1 ); + strncpy(rop->snapshot_id, snapshot_id, PATHLEN - 1); + rop->path[PATHLEN - 1] = '\0'; + rop->snapshot_id[PATHLEN - 1] = '\0'; + rop->fd = cli_fd; + rop->flags = flags; + rop->close_fd = close_fd; + + return rop; +} + +static void rop_set_rimg(struct roperation* rop, struct rimage* rimg) +{ + rop->rimg = rimg; + rop->size = rimg->size; + if (rop->flags == O_APPEND) { + // Image forward on append must start where the last fwd finished. + if (rop->fd == proxy_to_cache_fd) { + rop->curr_sent_buf = rimg->curr_fwd_buf; + rop->curr_sent_bytes = rimg->curr_fwd_bytes; + } + // For local appends, just write at the end. + else { + rop->curr_sent_buf = list_entry(rimg->buf_head.prev, struct rbuf, l); + rop->curr_sent_bytes = rop->curr_sent_buf->nbytes; + } + // On the receiver size, we just append + rop->curr_recv_buf = list_entry(rimg->buf_head.prev, struct rbuf, l); + } + else { + // Writes or reads are simple. Just do it from the beginnig. + rop->curr_recv_buf = list_entry(rimg->buf_head.next, struct rbuf, l); + rop->curr_sent_buf = list_entry(rimg->buf_head.next, struct rbuf, l); + rop->curr_sent_bytes = 0; + + } +} + /* Clears a remote image struct for reusing it. */ static struct rimage *clear_remote_image(struct rimage *rimg) { - pthread_mutex_lock(&(rimg->in_use)); - while (!list_is_singular(&(rimg->buf_head))) { struct rbuf *buf = list_entry(rimg->buf_head.prev, struct rbuf, l); @@ -524,179 +452,428 @@ static struct rimage *clear_remote_image(struct rimage *rimg) list_entry(rimg->buf_head.next, struct rbuf, l)->nbytes = 0; rimg->size = 0; - pthread_mutex_unlock(&(rimg->in_use)); - return rimg; } -struct rimage *prepare_remote_image(char *path, char *snapshot_id, int open_mode) +void handle_accept_write( + int cli_fd, char* snapshot_id, char* path, int flags, bool close_fd, uint64_t size) { + struct roperation *rop = NULL; struct rimage *rimg = get_rimg_by_name(snapshot_id, path); - /* There is no record of such image, create a new one. */ - if (rimg == NULL) - return new_remote_image(path, snapshot_id); - - pthread_mutex_lock(&rimg_lock); - list_del(&(rimg->l)); - pthread_mutex_unlock(&rimg_lock); + if (rimg == NULL) { + rimg = new_remote_image(path, snapshot_id); + if (rimg == NULL) { + pr_perror("Error preparing remote image"); + goto err; + } + } + else { + list_del(&(rimg->l)); + if (flags == O_APPEND) + clear_remote_image(rimg); + } + + rop = new_remote_operation(path, snapshot_id, cli_fd, flags, close_fd); + if (rop == NULL) { + pr_perror("Error preparing remote operation"); + goto err; + } + + rop_set_rimg(rop, rimg); + rop->size = size; + list_add_tail(&(rop->l), &rop_inprogress); + event_set(epoll_fd, EPOLL_CTL_ADD, rop->fd, EPOLLIN, rop); + return; +err: + free(rimg); + free(rop); +} - /* There is already an image record. Simply return it for appending. */ - if (open_mode == O_APPEND) - return rimg; - /* There is already an image record. Clear it for writing. */ - else - return clear_remote_image(rimg); +void handle_accept_proxy_write( + int cli_fd, char* snapshot_id, char* path, int flags) +{ + handle_accept_write(cli_fd, snapshot_id, path, flags, true, 0); } -static void *process_local_read(struct wthread *wt) +void handle_accept_proxy_read( + int cli_fd, char* snapshot_id, char* path, int flags) { + struct roperation *rop = NULL; struct rimage *rimg = NULL; - int64_t ret; - /* TODO - split wait_for_image - * in cache - improve the parent stuff - * in proxy - do not wait for anything, return no file - */ - rimg = wait_for_image(wt); - if (!rimg) { - pr_info("No image %s:%s.\n", wt->path, wt->snapshot_id); - if (write_reply_header(wt->fd, ENOENT) < 0) + + rimg = get_rimg_by_name(snapshot_id, path); + + // Check if we already have the image. + if (rimg == NULL) { + pr_info("No image %s:%s.\n", path, snapshot_id); + if (write_reply_header(cli_fd, ENOENT) < 0) { pr_perror("Error writing reply header for unexisting image"); - close(wt->fd); - return NULL; - } else { - if (write_reply_header(wt->fd, 0) < 0) { + goto err; + } + } + else { + if (write_reply_header(cli_fd, 0) < 0) { pr_perror("Error writing reply header for %s:%s", - wt->path, wt->snapshot_id); - close(wt->fd); - return NULL; + path, snapshot_id); + goto err; } - } - pthread_mutex_lock(&(rimg->in_use)); - ret = send_image(wt->fd, rimg, wt->flags, true); - if (ret < 0) - pr_perror("Unable to send %s:%s to CRIU (sent %ld bytes)", - rimg->path, rimg->snapshot_id, (long)ret); - else - pr_info("Finished sending %s:%s to CRIU (sent %ld bytes)\n", - rimg->path, rimg->snapshot_id, (long)ret); - pthread_mutex_unlock(&(rimg->in_use)); - return NULL; + rop = new_remote_operation(path, snapshot_id, cli_fd, flags, true); + if (rop == NULL) { + pr_perror("Error preparing remote operation"); + goto err; + } + rop_set_rimg(rop, rimg); + list_add_tail(&(rop->l), &rop_inprogress); + event_set(epoll_fd, EPOLL_CTL_ADD, rop->fd, EPOLLOUT, rop); + } + return; +err: + close(cli_fd); } -static void *process_local_image_connection(void *ptr) +void finish_local() { - struct wthread *wt = (struct wthread *) ptr; - struct rimage *rimg = NULL; - int64_t ret; + int ret; + finished_local = true; + //shutdown(local_req_fd, SHUT_RD); //TODO - should this be removed? + ret = event_set(epoll_fd, EPOLL_CTL_DEL, local_req_fd, 0, 0); + if (ret) { + pr_perror("Failed to del local fd from epoll"); + } +} - /* NOTE: the code inside this if is shared for both cache and proxy. */ - if (wt->flags == O_RDONLY) - return process_local_read(wt); +void handle_accept_cache_read( + int cli_fd, char* snapshot_id, char* path, int flags) +{ + struct rimage *rimg = NULL; + struct roperation *rop = NULL; - /* NOTE: IMAGE PROXY ONLY. The image cache receives write connections - * through TCP (see accept_remote_image_connections). - */ - rimg = prepare_remote_image(wt->path, wt->snapshot_id, wt->flags); - ret = recv_image(wt->fd, rimg, 0, wt->flags, true); - if (ret < 0) { - pr_perror("Unable to receive %s:%s to CRIU (received %ld bytes)", - rimg->path, rimg->snapshot_id, (long)ret); - finalize_recv_rimg(NULL); - return NULL; + // Check if this is the restore finish message. + if (!strncmp(path, RESTORE_FINISH, sizeof(RESTORE_FINISH))) { + close(cli_fd); + finish_local(); + return; } - finalize_recv_rimg(rimg); - pr_info("Finished receiving %s:%s (received %ld bytes)\n", - rimg->path, rimg->snapshot_id, (long)ret); + rop = new_remote_operation(path, snapshot_id, cli_fd, flags, true); + if (rop == NULL) { + pr_perror("Error preparing remote operation"); + close(cli_fd); + return; + } - if (!strncmp(rimg->path, DUMP_FINISH, sizeof(DUMP_FINISH))) { - finished = true; - shutdown(local_req_fd, SHUT_RD); - } else { - pthread_mutex_lock(&proxy_to_cache_lock); - ret = forward_image(rimg); - pthread_mutex_unlock(&proxy_to_cache_lock); + // Check if we already have the image. + rimg = get_rimg_by_name(snapshot_id, path); + if (rimg != NULL && rimg->size > 0) { + if (write_reply_header(cli_fd, 0) < 0) { + pr_perror("Error writing reply header for %s:%s", + path, snapshot_id); + free(rop); + close(rop->fd); + } + rop_set_rimg(rop, rimg); + list_add_tail(&(rop->l), &rop_inprogress); + event_set(epoll_fd, EPOLL_CTL_ADD, rop->fd, EPOLLOUT, rop); } + // The file may exist in future. + else if (!finished_remote){ + list_add_tail(&(rop->l), &rop_pending); + } + // The file does not exist. + else { + pr_info("No image %s:%s.\n", path, snapshot_id); + if (write_reply_header(cli_fd, ENOENT) < 0) + pr_perror("Error writing reply header for unexisting image"); + free(rop); + close(cli_fd); + } +} - finalize_fwd_rimg(); - if (ret < 0) { - pr_perror("Unable to forward %s:%s to Image Cache", - rimg->path, rimg->snapshot_id); +void forward_remote_image(struct roperation* rop) +{ + uint64_t ret = 0; + // Set blocking during the setup. +// socket_set_blocking(rop->fd); // TODO - test - return NULL; + ret = write_remote_header( + rop->fd, rop->snapshot_id, rop->path, rop->flags, rop->size); + + if (ret < 0) { + pr_perror("Error writing header for %s:%s", + rop->path, rop->snapshot_id); + return; } - if (finished && !is_forwarding() && !is_receiving()) { - pr_info("Closing connection to Image Cache.\n"); - close(proxy_to_cache_fd); - unlock_workers(); + pr_info("[fd=%d] Fowarding %s request for %s:%s (%lu bytes\n", + rop->fd, + rop->flags == O_RDONLY ? "read" : + rop->flags == O_APPEND ? "append" : "write", + rop->path, rop->snapshot_id, rop->size); + + + // Go back to non-blocking +// socket_set_non_blocking(rop->fd); // TODO - test + + forwarding = true; + event_set(epoll_fd, EPOLL_CTL_ADD, rop->fd, EPOLLOUT, rop); +} + +void handle_remote_accept(int fd) +{ + char path[PATHLEN]; + char snapshot_id[PATHLEN]; + int flags; + uint64_t size = 0; + uint64_t ret; + + // Set blocking during the setup. +// socket_set_blocking(fd); // TODO - test! + + ret = read_remote_header(fd, snapshot_id, path, &flags, &size); + if (ret < 0) { + pr_perror("Unable to receive remote header from image proxy"); + goto err; } - return NULL; + /* This means that the no more images are coming. */ + else if (!ret) { + finished_remote = true; + pr_info("Image Proxy connection closed.\n"); + return; + } + + // Go back to non-blocking +// socket_set_non_blocking(fd); // TODO - test! + + pr_info("[fd=%d] Received %s request for %s:%s with %lu bytes\n", + fd, + flags == O_RDONLY ? "read" : + flags == O_APPEND ? "append" : "write", + path, snapshot_id, size); + + + forwarding = true; + handle_accept_write(fd, snapshot_id, path, flags, false, size); + return; +err: + close(fd); } void handle_local_accept(int fd) { - struct wthread *wt = NULL; int cli_fd; - pthread_t tid; + char path[PATHLEN]; + char snapshot_id[PATHLEN]; + int flags = 0; struct sockaddr_in cli_addr; socklen_t clilen = sizeof(cli_addr); cli_fd = accept(fd, (struct sockaddr *) &cli_addr, &clilen); if (cli_fd < 0) { pr_perror("Unable to accept local image connection"); - goto err; + return; } - wt = new_worker(); - wt->fd = cli_fd; - - if (read_header(wt->fd, wt->snapshot_id, wt->path, &(wt->flags)) < 0) { + if (read_header(cli_fd, snapshot_id, path, &flags) < 0) { pr_err("Error reading local image header\n"); goto err; } - /* These function calls are used to avoid other threads from - * thinking that there are no more images are coming. - */ - if (wt->flags != O_RDONLY) { - prepare_recv_rimg(); - prepare_fwd_rimg(); + pr_info("[fd=%d] Received %s request for %s:%s\n", + cli_fd, + flags == O_RDONLY ? "read" : + flags == O_APPEND ? "append" : "write", + path, snapshot_id); + + // Write/Append case (only possible in img-proxy). + if (flags != O_RDONLY) { + handle_accept_proxy_write(cli_fd, snapshot_id, path, flags); + } + // Read case while restoring (img-cache). + else if (restoring) { + handle_accept_cache_read(cli_fd, snapshot_id, path, flags); + } + // Read case while dumping (img-proxy). + else { + handle_accept_proxy_read(cli_fd, snapshot_id, path, flags); } - pr_info("Received %s request for %s:%s\n", - wt->flags == O_RDONLY ? "read" : - wt->flags == O_APPEND ? "append" : "write", - wt->path, wt->snapshot_id); + // Set socket non-blocking. + socket_set_non_blocking(cli_fd); + return; +err: + close(cli_fd); +} - if (pthread_create( - &tid, NULL, process_local_image_connection, (void *) wt)) { - pr_perror("Unable to create worker thread"); - goto err; +void finish_proxy_read(struct roperation* rop) +{ + // If finished forwarding image + if (rop->fd == proxy_to_cache_fd) { + // Update fwd buffer and byte count on rimg. + rop->rimg->curr_fwd_buf = rop->curr_sent_buf; + rop->rimg->curr_fwd_bytes = rop->curr_sent_bytes; + + forwarding = false; + + // If there are images waiting to be forwarded, forward the next. + if (!list_empty(&rop_forwarding)) { + forward_remote_image(list_entry(rop_forwarding.next, struct roperation, l)); + } + } +} + +void finish_proxy_write(struct roperation* rop) +{ + // No more local images are comming. Close local socket. + if (!strncmp(rop->path, DUMP_FINISH, sizeof(DUMP_FINISH))) { + // TODO - couldn't we handle the DUMP_FINISH in inside handle_accept_proxy_write? + finish_local(); + } + // Normal image received, forward it. + else { + struct roperation *rop_to_forward = new_remote_operation( + rop->path, rop->snapshot_id, proxy_to_cache_fd, rop->flags, false); + + // Add image to list of images. + list_add_tail(&(rop->rimg->l), &rimg_head); + + rop_set_rimg(rop_to_forward, rop->rimg); + if (list_empty(&rop_forwarding)) { + forward_remote_image(rop_to_forward); + } + list_add_tail(&(rop_to_forward->l), &rop_forwarding); + } +} + +void finish_cache_write(struct roperation* rop) +{ + struct roperation *prop = get_rop_by_name( + &rop_pending, rop->snapshot_id, rop->path); + + forwarding = false; + event_set(epoll_fd, EPOLL_CTL_ADD, proxy_to_cache_fd, EPOLLIN, &proxy_to_cache_fd); + + // Add image to list of images. + list_add_tail(&(rop->rimg->l), &rimg_head); + + // TODO - what if we have multiple requests for the same name? + if (prop != NULL) { + pr_info("\t[fd=%d] Resuming pending %s for %s:%s\n", + prop->fd, + prop->flags == O_APPEND ? + "append" : prop->flags == O_RDONLY ? + "read" : "write", + prop->snapshot_id, prop->path); + + // Write header for pending image. + if (write_reply_header(prop->fd, 0) < 0) { + pr_perror("Error writing reply header for %s:%s", + prop->path, prop->snapshot_id); + close(prop->fd); + free(prop); + return; + } + + rop_set_rimg(prop, rop->rimg); + list_del(&(prop->l)); + list_add_tail(&(prop->l), &rop_inprogress); + event_set(epoll_fd, EPOLL_CTL_ADD, prop->fd, EPOLLOUT, prop); + } +} + +void handle_roperation(struct epoll_event *event, struct roperation *rop) +{ + int64_t ret = (EPOLLOUT & event->events) ? + send_image_async(rop) : + recv_image_async(rop); + + if (ret > 0 || ret == EAGAIN || ret == EWOULDBLOCK) { + event_set( + epoll_fd, + EPOLL_CTL_ADD, + rop->fd, + event->events, + rop); + return; } - wt->tid = tid; - add_worker(wt); - return; + + // Remove rop from list (either in progress or forwarding). + list_del(&(rop->l)); + + // Operation is finished. + if (ret < 0) { + pr_perror("Unable to %s %s:%s (returned %ld)", + event->events & EPOLLOUT ? "send" : "receive", + rop->rimg->path, rop->rimg->snapshot_id, ret); + goto err; + } else { + pr_info("[fd=%d] Finished %s %s:%s to CRIU (size %ld)\n", + rop->fd, + event->events & EPOLLOUT ? "sending" : "receiving", + rop->rimg->path, rop->rimg->snapshot_id, rop->rimg->size); + } + + // If receive operation is finished + if (event->events & EPOLLIN) { + + // Cached side (finished receiving forwarded image) + if (restoring) { + finish_cache_write(rop); + } + // Proxy side (finished receiving local image) + else { + finish_proxy_write(rop); + } + } + // If send operation if finished + else { + // Proxy side (Finished forwarding image or reading it locally). + if (!restoring) + finish_proxy_read(rop); + // Nothing to be done when a read is finished on the cache side. + } err: - close(cli_fd); - free(wt); + free(rop); } +void check_pending_forwards() +{ + struct roperation *rop = NULL; + struct rimage *rimg = NULL; + + list_for_each_entry(rop, &rop_forwarding, l) { + rimg = get_rimg_by_name(rop->snapshot_id, rop->path); + if (rimg != NULL) { + rop_set_rimg(rop, rimg); + forward_remote_image(rop); + return; + } + } +} -void *accept_local_image_connections(void *port) +void check_pending_reads() { - int fd = *((int *) port); - int epoll_fd; - struct epoll_event *events; + struct roperation *rop = NULL; + struct rimage *rimg = NULL; + + list_for_each_entry(rop, &rop_pending, l) { + rimg = get_rimg_by_name(rop->snapshot_id, rop->path); + if (rimg != NULL) { + rop_set_rimg(rop, rimg); + event_set(epoll_fd, EPOLL_CTL_ADD, rop->fd, EPOLLOUT, rop); + } + } +} + +void accept_image_connections() { int ret; epoll_fd = epoll_create(EPOLL_MAX_EVENTS); if (epoll_fd < 0) { pr_perror("Unable to open epoll"); - return NULL; + return; } events = calloc(EPOLL_MAX_EVENTS, sizeof(struct epoll_event)); @@ -705,57 +882,89 @@ void *accept_local_image_connections(void *port) goto end; } - ret = event_set(epoll_fd, EPOLL_CTL_ADD, fd, EPOLLIN, &fd); + ret = event_set(epoll_fd, EPOLL_CTL_ADD, local_req_fd, EPOLLIN, &local_req_fd); if (ret) { - pr_perror("Failed to set event for epoll"); + pr_perror("Failed to add local fd to epoll"); goto end; } + // Only if we are restoring (cache-side) we need to add the remote sock to + // the epoll. + if (restoring) { + ret = event_set(epoll_fd, EPOLL_CTL_ADD, proxy_to_cache_fd, + EPOLLIN, &proxy_to_cache_fd); + if (ret) { + pr_perror("Failed to add proxy to cache fd to epoll"); + goto end; + } + } + while (1) { - int n_events = epoll_wait(epoll_fd, events, EPOLL_MAX_EVENTS, -1); + int n_events = epoll_wait(epoll_fd, events, EPOLL_MAX_EVENTS, 250); if (n_events < 0) { pr_perror("Failed to epoll wait"); goto end; } for (int i = 0; i < n_events; i++) { - if (events[i].data.ptr == &fd) { + // Accept from local dump/restore? + if (events[i].data.ptr == &local_req_fd) { if ( events[i].events & EPOLLHUP || events[i].events & EPOLLERR) { - if (!finished) + if (!finished_local) pr_perror("Unable to accept more local image connections"); goto end; } - // accept - pr_perror("Calling accept %d", i); - handle_local_accept(fd); + handle_local_accept(local_req_fd); } + else if (restoring && !forwarding && events[i].data.ptr == &proxy_to_cache_fd) { + event_set(epoll_fd, EPOLL_CTL_DEL, proxy_to_cache_fd, 0, 0); + handle_remote_accept(proxy_to_cache_fd); + } else { - // TODO - handle write/read - pr_perror("Event on unexpected file descripor"); - goto end; + struct roperation *rop = + (struct roperation*)events[i].data.ptr; + event_set(epoll_fd, EPOLL_CTL_DEL, rop->fd, 0, 0); + handle_roperation(&events[i], rop); } } - } + + // Check if there are any pending operations + if (restoring) + check_pending_reads(); + else if (!forwarding) + check_pending_forwards(); + + // Check if we can close the tcp socket (this will unblock the cache + // to answer "no image" to restore). + if (!restoring && + finished_local && + !finished_remote && + list_empty(&rop_forwarding)) { + close(proxy_to_cache_fd); + finished_remote = true; + } + + // If both local and remote sockets are closed, leave. + if (finished_local && finished_remote) { + pr_info("\tFinished both local and remote, exiting\n"); + goto end; + } + } end: + // TODO - release pending when no receiving and finished. close(epoll_fd); - close(fd); + close(local_req_fd); free(events); - return NULL; } int64_t recv_image(int fd, struct rimage *rimg, uint64_t size, int flags, bool close_fd) { int ret; - struct roperation *op = malloc(sizeof(struct roperation)); - bzero(op, sizeof(struct roperation)); - op->fd = fd; - op->rimg = rimg; - op->size = size; - op->flags = flags; - op->close_fd = close_fd; - op->curr_recv_buf = list_entry(rimg->buf_head.next, struct rbuf, l); + struct roperation *op = new_remote_operation( + rimg->path, rimg->snapshot_id, fd, flags, close_fd); + rop_set_rimg(op, rimg); while ((ret = recv_image_async(op)) < 0) if (ret != EAGAIN && ret != EWOULDBLOCK) return -1; @@ -771,70 +980,58 @@ int64_t recv_image_async(struct roperation *op) int fd = op->fd; struct rimage *rimg = op->rimg; uint64_t size = op->size; - int flags = op->flags; bool close_fd = op->close_fd; struct rbuf *curr_buf = op->curr_recv_buf; int n; - if (curr_buf == NULL) { - if (flags == O_APPEND) - curr_buf = list_entry(rimg->buf_head.prev, struct rbuf, l); - else - curr_buf = list_entry(rimg->buf_head.next, struct rbuf, l); - } - - while (1) { - n = read(fd, + n = read(fd, curr_buf->buffer + curr_buf->nbytes, size ? min((int) (size - rimg->size), BUF_SIZE - curr_buf->nbytes) : BUF_SIZE - curr_buf->nbytes); - if (n == 0) { - if (close_fd) - close(fd); - return rimg->size; - } else if (n > 0) { - curr_buf->nbytes += n; - rimg->size += n; - if (curr_buf->nbytes == BUF_SIZE) { - struct rbuf *buf = malloc(sizeof(struct rbuf)); - if (buf == NULL) { - pr_perror("Unable to allocate remote_buffer structures"); - if (close_fd) - close(fd); - return -1; - } - buf->nbytes = 0; - list_add_tail(&(buf->l), &(rimg->buf_head)); - curr_buf = buf; - } - if (size && rimg->size == size) { + if (n == 0) { + if (close_fd) + close(fd); + return n; + } else if (n > 0) { + curr_buf->nbytes += n; + rimg->size += n; + if (curr_buf->nbytes == BUF_SIZE) { + struct rbuf *buf = malloc(sizeof(struct rbuf)); + if (buf == NULL) { + pr_perror("Unable to allocate remote_buffer structures"); if (close_fd) close(fd); - return rimg->size; + return -1; } - } else if (errno == EAGAIN || errno == EWOULDBLOCK) { - return errno; - } else { - pr_perror("Read on %s:%s socket failed", - rimg->path, rimg->snapshot_id); + buf->nbytes = 0; + list_add_tail(&(buf->l), &(rimg->buf_head)); + op->curr_recv_buf = buf; + return n; + } + if (size && rimg->size == size) { if (close_fd) close(fd); - return -1; + return 0; } + } else if (errno == EAGAIN || errno == EWOULDBLOCK) { + return errno; + } else { + pr_perror("Read for %s:%s socket on fd=%d failed", + rimg->path, rimg->snapshot_id, fd); + if (close_fd) + close(fd); + return -1; } + return n; } int64_t send_image(int fd, struct rimage *rimg, int flags, bool close_fd) { int ret; - struct roperation *op = malloc(sizeof(struct roperation)); - bzero(op, sizeof(struct roperation)); - op->fd = fd; - op->rimg = rimg; - op->flags = flags; - op->close_fd = close_fd; - op->curr_sent_buf = list_entry(rimg->buf_head.next, struct rbuf, l); + struct roperation *op = new_remote_operation( + rimg->path, rimg->snapshot_id, fd, flags, close_fd); + rop_set_rimg(op, rimg); while ((ret = send_image_async(op)) < 0) if (ret != EAGAIN && ret != EWOULDBLOCK) return -1; @@ -845,47 +1042,43 @@ int64_t send_image_async(struct roperation *op) { int fd = op->fd; struct rimage *rimg = op->rimg; - int flags = op->flags; bool close_fd = op->close_fd; int n; - if (flags != O_APPEND) { - op->curr_sent_buf = list_entry(rimg->buf_head.next, struct rbuf, l); - op->curr_sent_bytes = 0; - } - - while (1) { - n = send( - fd, - op->curr_sent_buf->buffer + op->curr_sent_bytes, - min(BUF_SIZE, op->curr_sent_buf->nbytes) - op->curr_sent_bytes, - MSG_NOSIGNAL); - if (n > -1) { - op->curr_sent_bytes += n; - if (op->curr_sent_bytes == BUF_SIZE) { - op->curr_sent_buf = - list_entry(op->curr_sent_buf->l.next, struct rbuf, l); - op->nblocks++; - op->curr_sent_bytes = 0; - } else if (op->curr_sent_bytes == op->curr_sent_buf->nbytes) { - if (close_fd) - close(fd); - return op->nblocks*BUF_SIZE + op->curr_sent_buf->nbytes; - } - } else if (errno == EPIPE || errno == ECONNRESET) { - pr_warn("Connection for %s:%s was closed early than expected\n", - rimg->path, rimg->snapshot_id); - return 0; - } else if (errno == EAGAIN || errno == EWOULDBLOCK) { - return errno; + n = write( + fd, + op->curr_sent_buf->buffer + op->curr_sent_bytes, + min(BUF_SIZE, op->curr_sent_buf->nbytes) - op->curr_sent_bytes); + + if (n > -1) { + op->curr_sent_bytes += n; + if (op->curr_sent_bytes == BUF_SIZE) { + op->curr_sent_buf = + list_entry(op->curr_sent_buf->l.next, struct rbuf, l); + op->curr_sent_bytes = 0; + return n; } - else { - pr_perror("Write on %s:%s socket failed", - rimg->path, rimg->snapshot_id); - return -1; + // TODO - cloudn't we just compare to the img size? + else if (op->curr_sent_bytes == op->curr_sent_buf->nbytes) { + if (close_fd) + close(fd); + return 0; } + return n; + } + // TODO - clouldn't these checks be made upstream? + else if (errno == EPIPE || errno == ECONNRESET) { + pr_warn("Connection for %s:%s was closed early than expected\n", + rimg->path, rimg->snapshot_id); + return 0; + } else if (errno == EAGAIN || errno == EWOULDBLOCK) { + return errno; + } + else { + pr_perror("Write on %s:%s socket failed", + rimg->path, rimg->snapshot_id); + return -1; } - } int read_remote_image_connection(char *snapshot_id, char *path) diff --git a/criu/include/img-remote.h b/criu/include/img-remote.h index 779a137fc..029857f70 100644 --- a/criu/include/img-remote.h +++ b/criu/include/img-remote.h @@ -32,32 +32,31 @@ struct rbuf { }; struct rimage { + /* Path and snapshot id identify the image. */ char path[PATHLEN]; char snapshot_id[PATHLEN]; + /* List anchor. */ struct list_head l; + /* List of buffers that compose the image. */ struct list_head buf_head; - uint64_t size; /* number of bytes */ - pthread_mutex_t in_use; /* Only one operation at a time, per image. */ -}; - -struct wthread { - pthread_t tid; - struct list_head l; - /* Client fd. */ - int fd; - /* The path and snapshot_id identify the request handled by this thread. */ - char path[PATHLEN]; - char snapshot_id[PATHLEN]; - int flags; - /* This semph is used to wake this thread if the image is in memory.*/ - sem_t wakeup_sem; + /* Number of bytes. */ + uint64_t size; + /* Note: forward (send) operation only. Buffer to start forwarding. */ + struct rbuf *curr_fwd_buf; + /* Note: forward (send) operation only. Number of fwd bytes in 'curr_fw_buf'. */ + uint64_t curr_fwd_bytes; }; /* Structure that describes the state of a remote operation on remote images. */ struct roperation { + /* List anchor. */ + struct list_head l; /* File descriptor being used. */ int fd; - /* Remote image being used. */ + /* Path and snapshot id identify the required image. */ + char path[PATHLEN]; + char snapshot_id[PATHLEN]; + /* Remote image being used (may be null if the operation is pending). */ struct rimage *rimg; /* Flags for the operation. */ int flags; @@ -66,37 +65,23 @@ struct roperation { /* Note: recv operation only. How much bytes should be received. */ uint64_t size; /* Note: recv operation only. Buffer being writen. */ - struct rbuf *curr_recv_buf; - /* Note: send operation only. Number of blocks already sent. */ - int nblocks; + struct rbuf *curr_recv_buf; // TODO - needed? Could be replaced by list.last! /* Note: send operation only. Pointer to buffer being sent. */ struct rbuf *curr_sent_buf; /* Note: send operation only. Number of bytes sent in 'curr_send_buf. */ uint64_t curr_sent_bytes; }; -/* This variable is used to indicate when the dump is finished. */ -extern bool finished; /* This is the proxy to cache TCP socket FD. */ extern int proxy_to_cache_fd; /* This the unix socket used to fulfill local requests. */ extern int local_req_fd; +/* True if we are running the cache/restore, false if proxy/dump. */ +extern bool restoring; -int init_daemon(bool background, struct rimage *(*wfi)(struct wthread*)); - -void join_workers(void); -void unlock_workers(void); - -void prepare_recv_rimg(void); -void finalize_recv_rimg(struct rimage *rimg); -struct rimage *prepare_remote_image(char *path, char *namesapce, int flags); +void accept_image_connections(); struct rimage *get_rimg_by_name(const char *snapshot_id, const char *path); -bool is_receiving(void); - -void *accept_local_image_connections(void *ptr); -void *accept_remote_image_connections(void *ptr); -int64_t forward_image(struct rimage *rimg); int64_t send_image(int fd, struct rimage *rimg, int flags, bool image_check); int64_t send_image_async(struct roperation *op); int64_t recv_image(int fd, struct rimage *rimg, uint64_t size, int flags, bool image_check); From fa3113c81541692294ddf91a8e1c8ad29f109a23 Mon Sep 17 00:00:00 2001 From: Rodrigo Bruno Date: Mon, 14 May 2018 01:29:50 +0100 Subject: [PATCH 025/249] remote: Minor improvements on img-remote.c --- criu/img-remote.c | 155 +++++++++++++++++++++++----------------------- 1 file changed, 77 insertions(+), 78 deletions(-) diff --git a/criu/img-remote.c b/criu/img-remote.c index 218c29684..13a56fc2c 100644 --- a/criu/img-remote.c +++ b/criu/img-remote.c @@ -44,14 +44,26 @@ LIST_HEAD(rop_forwarding); // List of snapshots (useful when doing incremental restores/dumps LIST_HEAD(snapshot_head); +// Snapshot id (setup at launch time by dump or restore). static char *snapshot_id; -bool restoring = true; // TODO - check where this is used! -// TODO - split this into two vars, recv_from_proxy, send_to_cache -bool forwarding = false; // TODO - true if proxy_to_cache_fd is being used. + +// True if restoring (cache := true; proxy := false). +bool restoring = true; + +// True if the proxy to cache socket is being used (receiving or sending). +bool forwarding = false; + +// True if the local dump or restore is finished. bool finished_local = false; + +// True if the communication between the proxy and cache can be closed. bool finished_remote = false; + +// Proxy to cache socket fd; Local dump or restore servicing fd. int proxy_to_cache_fd; int local_req_fd; + +// Epoll fd and event array. int epoll_fd; struct epoll_event *events; @@ -455,7 +467,7 @@ static struct rimage *clear_remote_image(struct rimage *rimg) return rimg; } -void handle_accept_write( +struct roperation* handle_accept_write( int cli_fd, char* snapshot_id, char* path, int flags, bool close_fd, uint64_t size) { struct roperation *rop = NULL; @@ -482,25 +494,24 @@ void handle_accept_write( rop_set_rimg(rop, rimg); rop->size = size; - list_add_tail(&(rop->l), &rop_inprogress); - event_set(epoll_fd, EPOLL_CTL_ADD, rop->fd, EPOLLIN, rop); - return; + return rop; err: free(rimg); free(rop); + return NULL; } -void handle_accept_proxy_write( +struct roperation* handle_accept_proxy_write( int cli_fd, char* snapshot_id, char* path, int flags) { - handle_accept_write(cli_fd, snapshot_id, path, flags, true, 0); + return handle_accept_write(cli_fd, snapshot_id, path, flags, true, 0); } -void handle_accept_proxy_read( +struct roperation* handle_accept_proxy_read( int cli_fd, char* snapshot_id, char* path, int flags) { struct roperation *rop = NULL; - struct rimage *rimg = NULL; + struct rimage *rimg = NULL; rimg = get_rimg_by_name(snapshot_id, path); @@ -511,40 +522,40 @@ void handle_accept_proxy_read( pr_perror("Error writing reply header for unexisting image"); goto err; } + close(cli_fd); + return NULL; } - else { - if (write_reply_header(cli_fd, 0) < 0) { - pr_perror("Error writing reply header for %s:%s", - path, snapshot_id); - goto err; - } - rop = new_remote_operation(path, snapshot_id, cli_fd, flags, true); - if (rop == NULL) { - pr_perror("Error preparing remote operation"); - goto err; - } - rop_set_rimg(rop, rimg); - list_add_tail(&(rop->l), &rop_inprogress); - event_set(epoll_fd, EPOLL_CTL_ADD, rop->fd, EPOLLOUT, rop); + if (write_reply_header(cli_fd, 0) < 0) { + pr_perror("Error writing reply header for %s:%s", + path, snapshot_id); + goto err; } - return; + + rop = new_remote_operation(path, snapshot_id, cli_fd, flags, true); + if (rop == NULL) { + pr_perror("Error preparing remote operation"); + goto err; + } + + rop_set_rimg(rop, rimg); + return rop; err: close(cli_fd); + return NULL; } void finish_local() { int ret; finished_local = true; - //shutdown(local_req_fd, SHUT_RD); //TODO - should this be removed? ret = event_set(epoll_fd, EPOLL_CTL_DEL, local_req_fd, 0, 0); if (ret) { pr_perror("Failed to del local fd from epoll"); } } -void handle_accept_cache_read( +struct roperation* handle_accept_cache_read( int cli_fd, char* snapshot_id, char* path, int flags) { struct rimage *rimg = NULL; @@ -554,14 +565,14 @@ void handle_accept_cache_read( if (!strncmp(path, RESTORE_FINISH, sizeof(RESTORE_FINISH))) { close(cli_fd); finish_local(); - return; + return NULL; } rop = new_remote_operation(path, snapshot_id, cli_fd, flags, true); if (rop == NULL) { pr_perror("Error preparing remote operation"); close(cli_fd); - return; + return NULL; } // Check if we already have the image. @@ -574,28 +585,25 @@ void handle_accept_cache_read( close(rop->fd); } rop_set_rimg(rop, rimg); - list_add_tail(&(rop->l), &rop_inprogress); - event_set(epoll_fd, EPOLL_CTL_ADD, rop->fd, EPOLLOUT, rop); - } - // The file may exist in future. - else if (!finished_remote){ - list_add_tail(&(rop->l), &rop_pending); + return rop; } // The file does not exist. - else { + else if (finished_remote) { pr_info("No image %s:%s.\n", path, snapshot_id); if (write_reply_header(cli_fd, ENOENT) < 0) pr_perror("Error writing reply header for unexisting image"); free(rop); close(cli_fd); } + return NULL; } void forward_remote_image(struct roperation* rop) { uint64_t ret = 0; + // Set blocking during the setup. -// socket_set_blocking(rop->fd); // TODO - test + socket_set_blocking(rop->fd); ret = write_remote_header( rop->fd, rop->snapshot_id, rop->path, rop->flags, rop->size); @@ -614,7 +622,7 @@ void forward_remote_image(struct roperation* rop) // Go back to non-blocking -// socket_set_non_blocking(rop->fd); // TODO - test + socket_set_non_blocking(rop->fd); forwarding = true; event_set(epoll_fd, EPOLL_CTL_ADD, rop->fd, EPOLLOUT, rop); @@ -627,9 +635,10 @@ void handle_remote_accept(int fd) int flags; uint64_t size = 0; uint64_t ret; + struct roperation* rop = NULL; // Set blocking during the setup. -// socket_set_blocking(fd); // TODO - test! + socket_set_blocking(fd); ret = read_remote_header(fd, snapshot_id, path, &flags, &size); if (ret < 0) { @@ -644,7 +653,7 @@ void handle_remote_accept(int fd) } // Go back to non-blocking -// socket_set_non_blocking(fd); // TODO - test! + socket_set_non_blocking(fd); pr_info("[fd=%d] Received %s request for %s:%s with %lu bytes\n", fd, @@ -654,8 +663,13 @@ void handle_remote_accept(int fd) forwarding = true; - handle_accept_write(fd, snapshot_id, path, flags, false, size); - return; + rop = handle_accept_write(fd, snapshot_id, path, flags, false, size); + + if (rop != NULL) { + list_add_tail(&(rop->l), &rop_inprogress); + event_set(epoll_fd, EPOLL_CTL_ADD, rop->fd, EPOLLIN, rop); + } + return; err: close(fd); } @@ -668,6 +682,7 @@ void handle_local_accept(int fd) int flags = 0; struct sockaddr_in cli_addr; socklen_t clilen = sizeof(cli_addr); + struct roperation *rop = NULL; cli_fd = accept(fd, (struct sockaddr *) &cli_addr, &clilen); if (cli_fd < 0) { @@ -688,19 +703,32 @@ void handle_local_accept(int fd) // Write/Append case (only possible in img-proxy). if (flags != O_RDONLY) { - handle_accept_proxy_write(cli_fd, snapshot_id, path, flags); + rop = handle_accept_proxy_write(cli_fd, snapshot_id, path, flags); } // Read case while restoring (img-cache). else if (restoring) { - handle_accept_cache_read(cli_fd, snapshot_id, path, flags); + rop = handle_accept_cache_read(cli_fd, snapshot_id, path, flags); } // Read case while dumping (img-proxy). else { - handle_accept_proxy_read(cli_fd, snapshot_id, path, flags); + rop = handle_accept_proxy_read(cli_fd, snapshot_id, path, flags); } - // Set socket non-blocking. - socket_set_non_blocking(cli_fd); + // If we have an operation. Check if we are ready to start or not. + if (rop != NULL) { + if (rop->rimg != NULL) { + list_add_tail(&(rop->l), &rop_inprogress); + event_set( + epoll_fd, + EPOLL_CTL_ADD, + rop->fd, + rop->flags == O_RDONLY ? EPOLLOUT : EPOLLIN, + rop); + } else { + list_add_tail(&(rop->l), &rop_pending); + } + socket_set_non_blocking(rop->fd); + } return; err: @@ -758,7 +786,6 @@ void finish_cache_write(struct roperation* rop) // Add image to list of images. list_add_tail(&(rop->rimg->l), &rimg_head); - // TODO - what if we have multiple requests for the same name? if (prop != NULL) { pr_info("\t[fd=%d] Resuming pending %s for %s:%s\n", prop->fd, @@ -947,31 +974,17 @@ void accept_image_connections() { // If both local and remote sockets are closed, leave. if (finished_local && finished_remote) { - pr_info("\tFinished both local and remote, exiting\n"); + pr_info("Finished both local and remote, exiting\n"); goto end; } } end: - // TODO - release pending when no receiving and finished. close(epoll_fd); close(local_req_fd); free(events); } -int64_t recv_image(int fd, struct rimage *rimg, uint64_t size, int flags, bool close_fd) -{ - int ret; - struct roperation *op = new_remote_operation( - rimg->path, rimg->snapshot_id, fd, flags, close_fd); - rop_set_rimg(op, rimg); - while ((ret = recv_image_async(op)) < 0) - if (ret != EAGAIN && ret != EWOULDBLOCK) - return -1; - free(op); - return ret; -} - /* Note: size is a limit on how much we want to read from the socket. Zero means * read until the socket is closed. */ @@ -1026,18 +1039,6 @@ int64_t recv_image_async(struct roperation *op) return n; } -int64_t send_image(int fd, struct rimage *rimg, int flags, bool close_fd) -{ - int ret; - struct roperation *op = new_remote_operation( - rimg->path, rimg->snapshot_id, fd, flags, close_fd); - rop_set_rimg(op, rimg); - while ((ret = send_image_async(op)) < 0) - if (ret != EAGAIN && ret != EWOULDBLOCK) - return -1; - return ret; -} - int64_t send_image_async(struct roperation *op) { int fd = op->fd; @@ -1058,7 +1059,6 @@ int64_t send_image_async(struct roperation *op) op->curr_sent_bytes = 0; return n; } - // TODO - cloudn't we just compare to the img size? else if (op->curr_sent_bytes == op->curr_sent_buf->nbytes) { if (close_fd) close(fd); @@ -1066,7 +1066,6 @@ int64_t send_image_async(struct roperation *op) } return n; } - // TODO - clouldn't these checks be made upstream? else if (errno == EPIPE || errno == ECONNRESET) { pr_warn("Connection for %s:%s was closed early than expected\n", rimg->path, rimg->snapshot_id); From 74d6fc084dd0b9d901fc5a4b5bfb60deef0ab89a Mon Sep 17 00:00:00 2001 From: Rodrigo Bruno Date: Mon, 14 May 2018 01:29:51 +0100 Subject: [PATCH 026/249] remote: Fixing identation. --- criu/img-cache.c | 32 +-- criu/img-proxy.c | 10 +- criu/img-remote.c | 395 +++++++++++++++++++------------------- criu/include/img-remote.h | 6 +- 4 files changed, 221 insertions(+), 222 deletions(-) diff --git a/criu/img-cache.c b/criu/img-cache.c index c941f14e2..98fdf80e6 100644 --- a/criu/img-cache.c +++ b/criu/img-cache.c @@ -10,23 +10,23 @@ int accept_proxy_to_cache(int sockfd) { - struct sockaddr_in cli_addr; - socklen_t clilen = sizeof(cli_addr); - int proxy_fd = accept(sockfd, (struct sockaddr *) &cli_addr, &clilen); + struct sockaddr_in cli_addr; + socklen_t clilen = sizeof(cli_addr); + int proxy_fd = accept(sockfd, (struct sockaddr *) &cli_addr, &clilen); - if (proxy_fd < 0) { - pr_perror("Unable to accept remote image connection from image proxy"); - return -1; - } + if (proxy_fd < 0) { + pr_perror("Unable to accept remote image connection from image proxy"); + return -1; + } - return proxy_fd; + return proxy_fd; } int image_cache(bool background, char *local_cache_path, unsigned short cache_write_port) { pr_info("Proxy to Cache Port %d, CRIU to Cache Path %s\n", cache_write_port, local_cache_path); - restoring = true; + restoring = true; if (opts.ps_socket != -1) { proxy_to_cache_fd = opts.ps_socket; @@ -37,13 +37,13 @@ int image_cache(bool background, char *local_cache_path, unsigned short cache_wr pr_perror("Unable to open proxy to cache TCP socket"); return -1; } - // Wait to accept connection from proxy. - proxy_to_cache_fd = accept_proxy_to_cache(proxy_to_cache_fd); - if (proxy_to_cache_fd < 0) - return -1; // TODO - should close other sockets. + // Wait to accept connection from proxy. + proxy_to_cache_fd = accept_proxy_to_cache(proxy_to_cache_fd); + if (proxy_to_cache_fd < 0) + return -1; // TODO - should close other sockets. } - pr_info("Cache is connected to Proxy through fd %d\n", proxy_to_cache_fd); + pr_info("Cache is connected to Proxy through fd %d\n", proxy_to_cache_fd); local_req_fd = setup_UNIX_server_socket(local_cache_path); if (local_req_fd < 0) { @@ -52,14 +52,14 @@ int image_cache(bool background, char *local_cache_path, unsigned short cache_wr } - if (background) { + if (background) { if (daemon(1, 0) == -1) { pr_perror("Can't run service server in the background"); return -1; } } - accept_image_connections(); + accept_image_connections(); pr_info("Finished image cache."); return 0; } diff --git a/criu/img-proxy.c b/criu/img-proxy.c index 9551a7dcb..c4d442e67 100644 --- a/criu/img-proxy.c +++ b/criu/img-proxy.c @@ -12,7 +12,7 @@ int image_proxy(bool background, char *local_proxy_path, char *fwd_host, unsigne { pr_info("CRIU to Proxy Path: %s, Cache Address %s:%hu\n", local_proxy_path, fwd_host, fwd_port); - restoring = false; + restoring = false; local_req_fd = setup_UNIX_server_socket(local_proxy_path); if (local_req_fd < 0) { @@ -31,17 +31,17 @@ int image_proxy(bool background, char *local_proxy_path, char *fwd_host, unsigne } } - pr_info("Proxy is connected to Cache through fd %d\n", proxy_to_cache_fd); + pr_info("Proxy is connected to Cache through fd %d\n", proxy_to_cache_fd); - if (background) { + if (background) { if (daemon(1, 0) == -1) { pr_perror("Can't run service server in the background"); return -1; } } - // TODO - local_req_fd and proxy_to_cache_fd send as args. - accept_image_connections(); + // TODO - local_req_fd and proxy_to_cache_fd send as args. + accept_image_connections(); pr_info("Finished image proxy."); return 0; } diff --git a/criu/img-remote.c b/criu/img-remote.c index 13a56fc2c..af4d88dcc 100644 --- a/criu/img-remote.c +++ b/criu/img-remote.c @@ -99,7 +99,7 @@ struct rimage *get_rimg_by_name(const char *snapshot_id, const char *path) list_for_each_entry(rimg, &rimg_head, l) { if (!strncmp(rimg->path, path, PATHLEN) && - !strncmp(rimg->snapshot_id, snapshot_id, PATHLEN)) { + !strncmp(rimg->snapshot_id, snapshot_id, PATHLEN)) { return rimg; } } @@ -107,13 +107,13 @@ struct rimage *get_rimg_by_name(const char *snapshot_id, const char *path) } struct roperation *get_rop_by_name( - struct list_head *head, const char *snapshot_id, const char *path) + struct list_head *head, const char *snapshot_id, const char *path) { struct roperation *rop = NULL; list_for_each_entry(rop, head, l) { if (!strncmp(rop->path, path, PATHLEN) && - !strncmp(rop->snapshot_id, snapshot_id, PATHLEN)) { + !strncmp(rop->snapshot_id, snapshot_id, PATHLEN)) { return rop; } } @@ -137,7 +137,7 @@ int setup_TCP_server_socket(int port) serv_addr.sin_port = htons(port); if (setsockopt( - sockfd, SOL_SOCKET, SO_REUSEADDR, &sockopt, sizeof(sockopt)) == -1) { + sockfd, SOL_SOCKET, SO_REUSEADDR, &sockopt, sizeof(sockopt)) == -1) { pr_perror("Unable to set SO_REUSEADDR"); goto err; } @@ -181,8 +181,8 @@ int setup_TCP_client_socket(char *hostname, int port) bzero((char *) &serv_addr, sizeof(serv_addr)); serv_addr.sin_family = AF_INET; bcopy((char *) server->h_addr, - (char *) &serv_addr.sin_addr.s_addr, - server->h_length); + (char *) &serv_addr.sin_addr.s_addr, + server->h_length); serv_addr.sin_port = htons(port); if (connect(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) { @@ -216,7 +216,7 @@ void socket_set_non_blocking(int fd) if (flags < 0) { pr_perror("Failed to obtain flags from fd %d", fd); return; - } + } flags |= O_NONBLOCK; if (fcntl(fd, F_SETFL, flags) < 0) @@ -230,7 +230,7 @@ void socket_set_blocking(int fd) if (flags < 0) { pr_perror("Failed to obtain flags from fd %d", fd); return; - } + } flags &= (~O_NONBLOCK); if (fcntl(fd, F_SETFL, flags) < 0) @@ -405,29 +405,29 @@ static struct rimage *new_remote_image(char *path, char *snapshot_id) } static struct roperation *new_remote_operation( - char *path, char *snapshot_id, int cli_fd, int flags, bool close_fd) + char *path, char *snapshot_id, int cli_fd, int flags, bool close_fd) { - struct roperation *rop = calloc(1, sizeof(struct roperation)); + struct roperation *rop = calloc(1, sizeof(struct roperation)); - if (rop == NULL) { - pr_perror("Unable to allocate remote operation structures"); + if (rop == NULL) { + pr_perror("Unable to allocate remote operation structures"); return NULL; - } - strncpy(rop->path, path, PATHLEN -1 ); + } + strncpy(rop->path, path, PATHLEN -1 ); strncpy(rop->snapshot_id, snapshot_id, PATHLEN - 1); rop->path[PATHLEN - 1] = '\0'; rop->snapshot_id[PATHLEN - 1] = '\0'; - rop->fd = cli_fd; - rop->flags = flags; - rop->close_fd = close_fd; + rop->fd = cli_fd; + rop->flags = flags; + rop->close_fd = close_fd; - return rop; + return rop; } static void rop_set_rimg(struct roperation* rop, struct rimage* rimg) { - rop->rimg = rimg; - rop->size = rimg->size; + rop->rimg = rimg; + rop->size = rimg->size; if (rop->flags == O_APPEND) { // Image forward on append must start where the last fwd finished. if (rop->fd == proxy_to_cache_fd) { @@ -468,36 +468,36 @@ static struct rimage *clear_remote_image(struct rimage *rimg) } struct roperation* handle_accept_write( - int cli_fd, char* snapshot_id, char* path, int flags, bool close_fd, uint64_t size) + int cli_fd, char* snapshot_id, char* path, int flags, bool close_fd, uint64_t size) { - struct roperation *rop = NULL; + struct roperation *rop = NULL; struct rimage *rimg = get_rimg_by_name(snapshot_id, path); if (rimg == NULL) { rimg = new_remote_image(path, snapshot_id); - if (rimg == NULL) { - pr_perror("Error preparing remote image"); - goto err; - } - } - else { - list_del(&(rimg->l)); - if (flags == O_APPEND) - clear_remote_image(rimg); - } - - rop = new_remote_operation(path, snapshot_id, cli_fd, flags, close_fd); - if (rop == NULL) { - pr_perror("Error preparing remote operation"); - goto err; - } - - rop_set_rimg(rop, rimg); + if (rimg == NULL) { + pr_perror("Error preparing remote image"); + goto err; + } + } + else { + list_del(&(rimg->l)); + if (flags == O_APPEND) + clear_remote_image(rimg); + } + + rop = new_remote_operation(path, snapshot_id, cli_fd, flags, close_fd); + if (rop == NULL) { + pr_perror("Error preparing remote operation"); + goto err; + } + + rop_set_rimg(rop, rimg); rop->size = size; return rop; err: - free(rimg); - free(rop); + free(rimg); + free(rop); return NULL; } @@ -508,21 +508,21 @@ struct roperation* handle_accept_proxy_write( } struct roperation* handle_accept_proxy_read( - int cli_fd, char* snapshot_id, char* path, int flags) + int cli_fd, char* snapshot_id, char* path, int flags) { - struct roperation *rop = NULL; + struct roperation *rop = NULL; struct rimage *rimg = NULL; - rimg = get_rimg_by_name(snapshot_id, path); + rimg = get_rimg_by_name(snapshot_id, path); - // Check if we already have the image. + // Check if we already have the image. if (rimg == NULL) { pr_info("No image %s:%s.\n", path, snapshot_id); if (write_reply_header(cli_fd, ENOENT) < 0) { pr_perror("Error writing reply header for unexisting image"); - goto err; - } - close(cli_fd); + goto err; + } + close(cli_fd); return NULL; } @@ -532,16 +532,16 @@ struct roperation* handle_accept_proxy_read( goto err; } - rop = new_remote_operation(path, snapshot_id, cli_fd, flags, true); - if (rop == NULL) { + rop = new_remote_operation(path, snapshot_id, cli_fd, flags, true); + if (rop == NULL) { pr_perror("Error preparing remote operation"); - goto err; - } + goto err; + } rop_set_rimg(rop, rimg); return rop; err: - close(cli_fd); + close(cli_fd); return NULL; } @@ -556,10 +556,10 @@ void finish_local() } struct roperation* handle_accept_cache_read( - int cli_fd, char* snapshot_id, char* path, int flags) + int cli_fd, char* snapshot_id, char* path, int flags) { - struct rimage *rimg = NULL; - struct roperation *rop = NULL; + struct rimage *rimg = NULL; + struct roperation *rop = NULL; // Check if this is the restore finish message. if (!strncmp(path, RESTORE_FINISH, sizeof(RESTORE_FINISH))) { @@ -568,12 +568,12 @@ struct roperation* handle_accept_cache_read( return NULL; } - rop = new_remote_operation(path, snapshot_id, cli_fd, flags, true); - if (rop == NULL) { - pr_perror("Error preparing remote operation"); - close(cli_fd); + rop = new_remote_operation(path, snapshot_id, cli_fd, flags, true); + if (rop == NULL) { + pr_perror("Error preparing remote operation"); + close(cli_fd); return NULL; - } + } // Check if we already have the image. rimg = get_rimg_by_name(snapshot_id, path); @@ -581,10 +581,10 @@ struct roperation* handle_accept_cache_read( if (write_reply_header(cli_fd, 0) < 0) { pr_perror("Error writing reply header for %s:%s", path, snapshot_id); - free(rop); + free(rop); close(rop->fd); } - rop_set_rimg(rop, rimg); + rop_set_rimg(rop, rimg); return rop; } // The file does not exist. @@ -592,7 +592,7 @@ struct roperation* handle_accept_cache_read( pr_info("No image %s:%s.\n", path, snapshot_id); if (write_reply_header(cli_fd, ENOENT) < 0) pr_perror("Error writing reply header for unexisting image"); - free(rop); + free(rop); close(cli_fd); } return NULL; @@ -602,8 +602,8 @@ void forward_remote_image(struct roperation* rop) { uint64_t ret = 0; - // Set blocking during the setup. - socket_set_blocking(rop->fd); + // Set blocking during the setup. + socket_set_blocking(rop->fd); ret = write_remote_header( rop->fd, rop->snapshot_id, rop->path, rop->flags, rop->size); @@ -617,30 +617,30 @@ void forward_remote_image(struct roperation* rop) pr_info("[fd=%d] Fowarding %s request for %s:%s (%lu bytes\n", rop->fd, rop->flags == O_RDONLY ? "read" : - rop->flags == O_APPEND ? "append" : "write", + rop->flags == O_APPEND ? "append" : "write", rop->path, rop->snapshot_id, rop->size); - // Go back to non-blocking - socket_set_non_blocking(rop->fd); + // Go back to non-blocking + socket_set_non_blocking(rop->fd); forwarding = true; - event_set(epoll_fd, EPOLL_CTL_ADD, rop->fd, EPOLLOUT, rop); + event_set(epoll_fd, EPOLL_CTL_ADD, rop->fd, EPOLLOUT, rop); } void handle_remote_accept(int fd) { - char path[PATHLEN]; + char path[PATHLEN]; char snapshot_id[PATHLEN]; int flags; - uint64_t size = 0; - uint64_t ret; + uint64_t size = 0; + uint64_t ret; struct roperation* rop = NULL; - // Set blocking during the setup. - socket_set_blocking(fd); + // Set blocking during the setup. + socket_set_blocking(fd); - ret = read_remote_header(fd, snapshot_id, path, &flags, &size); + ret = read_remote_header(fd, snapshot_id, path, &flags, &size); if (ret < 0) { pr_perror("Unable to receive remote header from image proxy"); goto err; @@ -652,18 +652,18 @@ void handle_remote_accept(int fd) return; } - // Go back to non-blocking - socket_set_non_blocking(fd); + // Go back to non-blocking + socket_set_non_blocking(fd); pr_info("[fd=%d] Received %s request for %s:%s with %lu bytes\n", fd, flags == O_RDONLY ? "read" : - flags == O_APPEND ? "append" : "write", + flags == O_APPEND ? "append" : "write", path, snapshot_id, size); forwarding = true; - rop = handle_accept_write(fd, snapshot_id, path, flags, false, size); + rop = handle_accept_write(fd, snapshot_id, path, flags, false, size); if (rop != NULL) { list_add_tail(&(rop->l), &rop_inprogress); @@ -671,15 +671,15 @@ void handle_remote_accept(int fd) } return; err: - close(fd); + close(fd); } void handle_local_accept(int fd) { int cli_fd; - char path[PATHLEN]; + char path[PATHLEN]; char snapshot_id[PATHLEN]; - int flags = 0; + int flags = 0; struct sockaddr_in cli_addr; socklen_t clilen = sizeof(cli_addr); struct roperation *rop = NULL; @@ -703,90 +703,90 @@ void handle_local_accept(int fd) // Write/Append case (only possible in img-proxy). if (flags != O_RDONLY) { - rop = handle_accept_proxy_write(cli_fd, snapshot_id, path, flags); + rop = handle_accept_proxy_write(cli_fd, snapshot_id, path, flags); } // Read case while restoring (img-cache). else if (restoring) { - rop = handle_accept_cache_read(cli_fd, snapshot_id, path, flags); + rop = handle_accept_cache_read(cli_fd, snapshot_id, path, flags); } // Read case while dumping (img-proxy). else { - rop = handle_accept_proxy_read(cli_fd, snapshot_id, path, flags); + rop = handle_accept_proxy_read(cli_fd, snapshot_id, path, flags); } - // If we have an operation. Check if we are ready to start or not. + // If we have an operation. Check if we are ready to start or not. if (rop != NULL) { if (rop->rimg != NULL) { - list_add_tail(&(rop->l), &rop_inprogress); - event_set( + list_add_tail(&(rop->l), &rop_inprogress); + event_set( epoll_fd, EPOLL_CTL_ADD, rop->fd, rop->flags == O_RDONLY ? EPOLLOUT : EPOLLIN, rop); } else { - list_add_tail(&(rop->l), &rop_pending); + list_add_tail(&(rop->l), &rop_pending); } socket_set_non_blocking(rop->fd); } - return; + return; err: - close(cli_fd); + close(cli_fd); } void finish_proxy_read(struct roperation* rop) { - // If finished forwarding image - if (rop->fd == proxy_to_cache_fd) { + // If finished forwarding image + if (rop->fd == proxy_to_cache_fd) { // Update fwd buffer and byte count on rimg. rop->rimg->curr_fwd_buf = rop->curr_sent_buf; rop->rimg->curr_fwd_bytes = rop->curr_sent_bytes; forwarding = false; - // If there are images waiting to be forwarded, forward the next. - if (!list_empty(&rop_forwarding)) { - forward_remote_image(list_entry(rop_forwarding.next, struct roperation, l)); - } - } + // If there are images waiting to be forwarded, forward the next. + if (!list_empty(&rop_forwarding)) { + forward_remote_image(list_entry(rop_forwarding.next, struct roperation, l)); + } + } } void finish_proxy_write(struct roperation* rop) { - // No more local images are comming. Close local socket. - if (!strncmp(rop->path, DUMP_FINISH, sizeof(DUMP_FINISH))) { - // TODO - couldn't we handle the DUMP_FINISH in inside handle_accept_proxy_write? + // No more local images are comming. Close local socket. + if (!strncmp(rop->path, DUMP_FINISH, sizeof(DUMP_FINISH))) { + // TODO - couldn't we handle the DUMP_FINISH in inside handle_accept_proxy_write? finish_local(); - } - // Normal image received, forward it. - else { - struct roperation *rop_to_forward = new_remote_operation( - rop->path, rop->snapshot_id, proxy_to_cache_fd, rop->flags, false); + } + // Normal image received, forward it. + else { + struct roperation *rop_to_forward = new_remote_operation( + rop->path, rop->snapshot_id, proxy_to_cache_fd, rop->flags, false); - // Add image to list of images. + // Add image to list of images. list_add_tail(&(rop->rimg->l), &rimg_head); - rop_set_rimg(rop_to_forward, rop->rimg); - if (list_empty(&rop_forwarding)) { - forward_remote_image(rop_to_forward); - } - list_add_tail(&(rop_to_forward->l), &rop_forwarding); - } + rop_set_rimg(rop_to_forward, rop->rimg); + if (list_empty(&rop_forwarding)) { + forward_remote_image(rop_to_forward); + } + list_add_tail(&(rop_to_forward->l), &rop_forwarding); + } } void finish_cache_write(struct roperation* rop) { - struct roperation *prop = get_rop_by_name( - &rop_pending, rop->snapshot_id, rop->path); + struct roperation *prop = get_rop_by_name( + &rop_pending, rop->snapshot_id, rop->path); forwarding = false; - event_set(epoll_fd, EPOLL_CTL_ADD, proxy_to_cache_fd, EPOLLIN, &proxy_to_cache_fd); + event_set(epoll_fd, EPOLL_CTL_ADD, proxy_to_cache_fd, EPOLLIN, &proxy_to_cache_fd); - // Add image to list of images. + // Add image to list of images. list_add_tail(&(rop->rimg->l), &rimg_head); - if (prop != NULL) { + if (prop != NULL) { pr_info("\t[fd=%d] Resuming pending %s for %s:%s\n", prop->fd, prop->flags == O_APPEND ? @@ -799,15 +799,15 @@ void finish_cache_write(struct roperation* rop) pr_perror("Error writing reply header for %s:%s", prop->path, prop->snapshot_id); close(prop->fd); - free(prop); + free(prop); return; } - rop_set_rimg(prop, rop->rimg); - list_del(&(prop->l)); + rop_set_rimg(prop, rop->rimg); + list_del(&(prop->l)); list_add_tail(&(prop->l), &rop_inprogress); - event_set(epoll_fd, EPOLL_CTL_ADD, prop->fd, EPOLLOUT, prop); - } + event_set(epoll_fd, EPOLL_CTL_ADD, prop->fd, EPOLLOUT, prop); + } } void handle_roperation(struct epoll_event *event, struct roperation *rop) @@ -823,74 +823,73 @@ void handle_roperation(struct epoll_event *event, struct roperation *rop) rop->fd, event->events, rop); - return; + return; } - // Remove rop from list (either in progress or forwarding). - list_del(&(rop->l)); + // Remove rop from list (either in progress or forwarding). + list_del(&(rop->l)); - // Operation is finished. - if (ret < 0) { - pr_perror("Unable to %s %s:%s (returned %ld)", + // Operation is finished. + if (ret < 0) { + pr_perror("Unable to %s %s:%s (returned %ld)", event->events & EPOLLOUT ? "send" : "receive", - rop->rimg->path, rop->rimg->snapshot_id, ret); - goto err; - } else { - pr_info("[fd=%d] Finished %s %s:%s to CRIU (size %ld)\n", + rop->rimg->path, rop->rimg->snapshot_id, ret); + goto err; + } else { + pr_info("[fd=%d] Finished %s %s:%s to CRIU (size %ld)\n", rop->fd, event->events & EPOLLOUT ? "sending" : "receiving", rop->rimg->path, rop->rimg->snapshot_id, rop->rimg->size); - } - - // If receive operation is finished - if (event->events & EPOLLIN) { - - // Cached side (finished receiving forwarded image) - if (restoring) { - finish_cache_write(rop); - } - // Proxy side (finished receiving local image) - else { - finish_proxy_write(rop); - } - } - // If send operation if finished - else { - // Proxy side (Finished forwarding image or reading it locally). - if (!restoring) - finish_proxy_read(rop); - // Nothing to be done when a read is finished on the cache side. - } + } + + // If receive operation is finished + if (event->events & EPOLLIN) { + // Cached side (finished receiving forwarded image) + if (restoring) { + finish_cache_write(rop); + } + // Proxy side (finished receiving local image) + else { + finish_proxy_write(rop); + } + } + // If send operation if finished + else { + // Proxy side (Finished forwarding image or reading it locally). + if (!restoring) + finish_proxy_read(rop); + // Nothing to be done when a read is finished on the cache side. + } err: - free(rop); + free(rop); } void check_pending_forwards() { - struct roperation *rop = NULL; - struct rimage *rimg = NULL; + struct roperation *rop = NULL; + struct rimage *rimg = NULL; list_for_each_entry(rop, &rop_forwarding, l) { - rimg = get_rimg_by_name(rop->snapshot_id, rop->path); - if (rimg != NULL) { - rop_set_rimg(rop, rimg); + rimg = get_rimg_by_name(rop->snapshot_id, rop->path); + if (rimg != NULL) { + rop_set_rimg(rop, rimg); forward_remote_image(rop); return; - } + } } } void check_pending_reads() { - struct roperation *rop = NULL; - struct rimage *rimg = NULL; + struct roperation *rop = NULL; + struct rimage *rimg = NULL; list_for_each_entry(rop, &rop_pending, l) { - rimg = get_rimg_by_name(rop->snapshot_id, rop->path); - if (rimg != NULL) { - rop_set_rimg(rop, rimg); - event_set(epoll_fd, EPOLL_CTL_ADD, rop->fd, EPOLLOUT, rop); - } + rimg = get_rimg_by_name(rop->snapshot_id, rop->path); + if (rimg != NULL) { + rop_set_rimg(rop, rimg); + event_set(epoll_fd, EPOLL_CTL_ADD, rop->fd, EPOLLOUT, rop); + } } } @@ -915,16 +914,16 @@ void accept_image_connections() { goto end; } - // Only if we are restoring (cache-side) we need to add the remote sock to - // the epoll. - if (restoring) { - ret = event_set(epoll_fd, EPOLL_CTL_ADD, proxy_to_cache_fd, - EPOLLIN, &proxy_to_cache_fd); - if (ret) { - pr_perror("Failed to add proxy to cache fd to epoll"); - goto end; - } - } + // Only if we are restoring (cache-side) we need to add the remote sock to + // the epoll. + if (restoring) { + ret = event_set(epoll_fd, EPOLL_CTL_ADD, proxy_to_cache_fd, + EPOLLIN, &proxy_to_cache_fd); + if (ret) { + pr_perror("Failed to add proxy to cache fd to epoll"); + goto end; + } + } while (1) { int n_events = epoll_wait(epoll_fd, events, EPOLL_MAX_EVENTS, 250); @@ -934,33 +933,33 @@ void accept_image_connections() { } for (int i = 0; i < n_events; i++) { - // Accept from local dump/restore? + // Accept from local dump/restore? if (events[i].data.ptr == &local_req_fd) { if ( events[i].events & EPOLLHUP || - events[i].events & EPOLLERR) { + events[i].events & EPOLLERR) { if (!finished_local) pr_perror("Unable to accept more local image connections"); goto end; } handle_local_accept(local_req_fd); } - else if (restoring && !forwarding && events[i].data.ptr == &proxy_to_cache_fd) { - event_set(epoll_fd, EPOLL_CTL_DEL, proxy_to_cache_fd, 0, 0); - handle_remote_accept(proxy_to_cache_fd); - } + else if (restoring && !forwarding && events[i].data.ptr == &proxy_to_cache_fd) { + event_set(epoll_fd, EPOLL_CTL_DEL, proxy_to_cache_fd, 0, 0); + handle_remote_accept(proxy_to_cache_fd); + } else { struct roperation *rop = (struct roperation*)events[i].data.ptr; - event_set(epoll_fd, EPOLL_CTL_DEL, rop->fd, 0, 0); + event_set(epoll_fd, EPOLL_CTL_DEL, rop->fd, 0, 0); handle_roperation(&events[i], rop); } } - // Check if there are any pending operations - if (restoring) - check_pending_reads(); + // Check if there are any pending operations + if (restoring) + check_pending_reads(); else if (!forwarding) - check_pending_forwards(); + check_pending_forwards(); // Check if we can close the tcp socket (this will unblock the cache // to answer "no image" to restore). @@ -972,12 +971,12 @@ void accept_image_connections() { finished_remote = true; } - // If both local and remote sockets are closed, leave. - if (finished_local && finished_remote) { + // If both local and remote sockets are closed, leave. + if (finished_local && finished_remote) { pr_info("Finished both local and remote, exiting\n"); - goto end; + goto end; } - } + } end: close(epoll_fd); close(local_req_fd); @@ -1000,8 +999,8 @@ int64_t recv_image_async(struct roperation *op) n = read(fd, curr_buf->buffer + curr_buf->nbytes, size ? - min((int) (size - rimg->size), BUF_SIZE - curr_buf->nbytes) : - BUF_SIZE - curr_buf->nbytes); + min((int) (size - rimg->size), BUF_SIZE - curr_buf->nbytes) : + BUF_SIZE - curr_buf->nbytes); if (n == 0) { if (close_fd) close(fd); @@ -1055,7 +1054,7 @@ int64_t send_image_async(struct roperation *op) op->curr_sent_bytes += n; if (op->curr_sent_bytes == BUF_SIZE) { op->curr_sent_buf = - list_entry(op->curr_sent_buf->l.next, struct rbuf, l); + list_entry(op->curr_sent_buf->l.next, struct rbuf, l); op->curr_sent_bytes = 0; return n; } diff --git a/criu/include/img-remote.h b/criu/include/img-remote.h index 029857f70..c32e6ea58 100644 --- a/criu/include/img-remote.h +++ b/criu/include/img-remote.h @@ -32,7 +32,7 @@ struct rbuf { }; struct rimage { - /* Path and snapshot id identify the image. */ + /* Path and snapshot id identify the image. */ char path[PATHLEN]; char snapshot_id[PATHLEN]; /* List anchor. */ @@ -53,8 +53,8 @@ struct roperation { struct list_head l; /* File descriptor being used. */ int fd; - /* Path and snapshot id identify the required image. */ - char path[PATHLEN]; + /* Path and snapshot id identify the required image. */ + char path[PATHLEN]; char snapshot_id[PATHLEN]; /* Remote image being used (may be null if the operation is pending). */ struct rimage *rimg; From ec2123df5eadc1cf45380c15e260ca6e155c21e9 Mon Sep 17 00:00:00 2001 From: Andrei Vagin Date: Tue, 15 May 2018 04:27:53 +0300 Subject: [PATCH 027/249] remote: a few minor fixes to make travis happy --- criu/img-remote.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/criu/img-remote.c b/criu/img-remote.c index af4d88dcc..9a7eec6a9 100644 --- a/criu/img-remote.c +++ b/criu/img-remote.c @@ -600,7 +600,7 @@ struct roperation* handle_accept_cache_read( void forward_remote_image(struct roperation* rop) { - uint64_t ret = 0; + int64_t ret = 0; // Set blocking during the setup. socket_set_blocking(rop->fd); @@ -614,7 +614,7 @@ void forward_remote_image(struct roperation* rop) return; } - pr_info("[fd=%d] Fowarding %s request for %s:%s (%lu bytes\n", + pr_info("[fd=%d] Fowarding %s request for %s:%s (%" PRIu64 " bytes\n", rop->fd, rop->flags == O_RDONLY ? "read" : rop->flags == O_APPEND ? "append" : "write", @@ -634,7 +634,7 @@ void handle_remote_accept(int fd) char snapshot_id[PATHLEN]; int flags; uint64_t size = 0; - uint64_t ret; + int64_t ret; struct roperation* rop = NULL; // Set blocking during the setup. @@ -655,7 +655,7 @@ void handle_remote_accept(int fd) // Go back to non-blocking socket_set_non_blocking(fd); - pr_info("[fd=%d] Received %s request for %s:%s with %lu bytes\n", + pr_info("[fd=%d] Received %s request for %s:%s with %" PRIu64 " bytes\n", fd, flags == O_RDONLY ? "read" : flags == O_APPEND ? "append" : "write", @@ -831,12 +831,12 @@ void handle_roperation(struct epoll_event *event, struct roperation *rop) // Operation is finished. if (ret < 0) { - pr_perror("Unable to %s %s:%s (returned %ld)", + pr_perror("Unable to %s %s:%s (returned %" PRId64 ")", event->events & EPOLLOUT ? "send" : "receive", rop->rimg->path, rop->rimg->snapshot_id, ret); goto err; } else { - pr_info("[fd=%d] Finished %s %s:%s to CRIU (size %ld)\n", + pr_info("[fd=%d] Finished %s %s:%s to CRIU (size %" PRIu64 ")\n", rop->fd, event->events & EPOLLOUT ? "sending" : "receiving", rop->rimg->path, rop->rimg->snapshot_id, rop->rimg->size); @@ -926,13 +926,15 @@ void accept_image_connections() { } while (1) { - int n_events = epoll_wait(epoll_fd, events, EPOLL_MAX_EVENTS, 250); + int n_events, i; + + n_events = epoll_wait(epoll_fd, events, EPOLL_MAX_EVENTS, 250); if (n_events < 0) { pr_perror("Failed to epoll wait"); goto end; } - for (int i = 0; i < n_events; i++) { + for (i = 0; i < n_events; i++) { // Accept from local dump/restore? if (events[i].data.ptr == &local_req_fd) { if ( events[i].events & EPOLLHUP || From 751aa22a2b123c8679c50ef685f22187432189b5 Mon Sep 17 00:00:00 2001 From: Radostin Stoyanov Date: Thu, 14 Jun 2018 08:57:38 +0100 Subject: [PATCH 028/249] remote: Remove unused constants Drop the constants for default cache host/port and page size because they are not used anywhere. Signed-off-by: Radostin Stoyanov Reviewed-by: Dmitry Safonov <0x7f454c46@gmail.com> Signed-off-by: Andrei Vagin --- criu/include/img-remote.h | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/criu/include/img-remote.h b/criu/include/img-remote.h index c32e6ea58..15e1bc5d5 100644 --- a/criu/include/img-remote.h +++ b/criu/include/img-remote.h @@ -16,14 +16,9 @@ #define NULL_SNAPSHOT_ID "null" #define DEFAULT_CACHE_SOCKET "img-cache.sock" #define DEFAULT_PROXY_SOCKET "img-proxy.sock" -#define DEFAULT_CACHE_PORT 9996 -#define DEFAULT_CACHE_HOST "localhost" #define DEFAULT_LISTEN 50 -#ifndef PAGESIZE -#define PAGESIZE 4096 -#endif -#define BUF_SIZE PAGESIZE +#define BUF_SIZE 4096 struct rbuf { char buffer[BUF_SIZE]; From 8864b63961d05e35d13902aabfa5f5476089f5c5 Mon Sep 17 00:00:00 2001 From: Radostin Stoyanov Date: Sat, 20 Jan 2018 18:11:34 +0000 Subject: [PATCH 029/249] crtools: Fix typo Signed-off-by: Radostin Stoyanov --- criu/crtools.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/criu/crtools.c b/criu/crtools.c index 3a5ed2197..e57af0cac 100644 --- a/criu/crtools.c +++ b/criu/crtools.c @@ -286,7 +286,7 @@ int main(int argc, char *argv[], char *envp[]) " cpuinfo dump writes cpu information into image file\n" " cpuinfo check validates cpu information read from image file\n" " image-proxy launch dump-side proxy to sent images\n" -" image-cache launch restore-side cache to reveive images\n" +" image-cache launch restore-side cache to receive images\n" ); if (usage_error) { From 4d1dd2ee608c430eb74e20d59ec7d713f5c845d8 Mon Sep 17 00:00:00 2001 From: Pavel Tikhomirov Date: Thu, 1 Feb 2018 19:04:09 +0300 Subject: [PATCH 030/249] criu: fix leaks detected by coverity scan 1) fix sfle memory leak on get_fle_for_scm error 2) fix gfd open descriptor leak on get_fle_for_scm error 3-6) fix buf memory leak on read and pwrite errors Signed-off-by: Pavel Tikhomirov --- criu/files-reg.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/criu/files-reg.c b/criu/files-reg.c index d25ad8e4e..b30da8c99 100644 --- a/criu/files-reg.c +++ b/criu/files-reg.c @@ -166,10 +166,12 @@ static int copy_chunk_from_file(int fd, int img, off_t off, size_t len) ret = pread(fd, buf, min_t(size_t, BUFSIZE, len), off); if (ret <= 0) { pr_perror("Can't read from ghost file"); + xfree(buf); return -1; } if (write(img, buf, ret) != ret) { pr_perror("Can't write to image"); + xfree(buf); return -1; } off += ret; @@ -244,10 +246,12 @@ static int copy_chunk_to_file(int img, int fd, off_t off, size_t len) ret = read(img, buf, min_t(size_t, BUFSIZE, len)); if (ret <= 0) { pr_perror("Can't read from image"); + xfree(buf); return -1; } if (pwrite(fd, buf, ret, off) != ret) { pr_perror("Can't write to file"); + xfree(buf); return -1; } } else { From c24e8bb30112ab98f4b84d5f48132f7f69417291 Mon Sep 17 00:00:00 2001 From: Andrei Vagin Date: Sat, 2 Jun 2018 00:02:51 +0300 Subject: [PATCH 031/249] test: make zdtm.py python2/python3 compatible Cc: Adrian Reber Signed-off-by: Andrei Vagin --- test/zdtm.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/zdtm.py b/test/zdtm.py index 5f81e938b..18501a19d 100755 --- a/test/zdtm.py +++ b/test/zdtm.py @@ -1131,7 +1131,7 @@ def dump(self, action, opts = []): if self.__remote: logdir = os.getcwd() + "/" + self.__dump_path + "/" + str(self.__iter) - print "Adding image cache" + print("Adding image cache") cache_opts = [self.__criu_bin, "image-cache", "--port", "12345", "-v4", "-o", logdir + "/image-cache.log", "-D", logdir] @@ -1139,7 +1139,7 @@ def dump(self, action, opts = []): subprocess.Popen(cache_opts).pid time.sleep(1) - print "Adding image proxy" + print("Adding image proxy") proxy_opts = [self.__criu_bin, "image-proxy", "--port", "12345", "--address", "localhost", "-v4", "-o", logdir + "/image-proxy.log", From 08d476e7ef55f3aefd9ad0a0732e887a0a2e959a Mon Sep 17 00:00:00 2001 From: Radostin Stoyanov Date: Fri, 16 Feb 2018 18:11:32 +0000 Subject: [PATCH 032/249] crtools: image-{cache, proxy} requires address/port Show error message when image-{cache,proxy} is called without --port and image-proxy without --address argument. Signed-off-by: Radostin Stoyanov --- criu/crtools.c | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/criu/crtools.c b/criu/crtools.c index e57af0cac..b0cba4708 100644 --- a/criu/crtools.c +++ b/criu/crtools.c @@ -227,11 +227,21 @@ int main(int argc, char *argv[], char *envp[]) if (!strcmp(argv[optind], "page-server")) return cr_page_server(opts.daemon_mode, false, -1) != 0; - if (!strcmp(argv[optind], "image-cache")) + if (!strcmp(argv[optind], "image-cache")) { + if (!opts.port) + goto opt_port_missing; return image_cache(opts.daemon_mode, DEFAULT_CACHE_SOCKET, opts.port); + } - if (!strcmp(argv[optind], "image-proxy")) + if (!strcmp(argv[optind], "image-proxy")) { + if (!opts.addr) { + pr_msg("Error: address not specified\n"); + return 1; + } + if (!opts.port) + goto opt_port_missing; return image_proxy(opts.daemon_mode, DEFAULT_PROXY_SOCKET, opts.addr, opts.port); + } if (!strcmp(argv[optind], "service")) return cr_service(opts.daemon_mode); @@ -449,6 +459,10 @@ int main(int argc, char *argv[], char *envp[]) return 0; +opt_port_missing: + pr_msg("Error: port not specified\n"); + return 1; + opt_pid_missing: pr_msg("Error: pid not specified\n"); return 1; From 8ec03732e1b179ddf47e5f490ce9faca9fd91492 Mon Sep 17 00:00:00 2001 From: Andrei Vagin Date: Sun, 4 Feb 2018 08:22:59 +0300 Subject: [PATCH 033/249] criu: fix gcc-8 warnings criu/sk-packet.c:443:3: error: 'strncpy' output may be truncated copying 14 bytes from a string of length 15 strncpy(addr_spkt.sa_data, req.ifr_name, sa_data_size); ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ criu/img-remote.c:383:3: error: 'strncpy' specified bound 4096 equals destination size strncpy(snapshot_id, li->snapshot_id, PATHLEN); ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ criu/img-remote.c:384:3: error: 'strncpy' specified bound 4096 equals destination size strncpy(path, li->name, PATHLEN); ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ criu/files.c:288:3: error: 'strncpy' output may be truncated copying 4095 bytes from a string of length 4096 strncpy(buf, link->name, PATH_MAX - 1); ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ criu/sk-unix.c:239:36: error: '/' directive output may be truncated writing 1 byte into a region of size between 0 and 4095 snprintf(path, sizeof(path), ".%s/%s", dir, sk->name); ^ criu/sk-unix.c:239:3: note: 'snprintf' output 3 or more bytes (assuming 4098) into a destination of size 4096 snprintf(path, sizeof(path), ".%s/%s", dir, sk->name); ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ criu/mount.c:2563:3: error: 'strncpy' specified bound 4096 equals destination size strncpy(path, m->mountpoint, PATH_MAX); ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ criu/cr-restore.c:3647:2: error: 'strncpy' specified bound 16 equals destination size strncpy(task_args->comm, core->tc->comm, sizeof(task_args->comm)); ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Signed-off-by: Andrei Vagin --- criu/img-remote.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/criu/img-remote.c b/criu/img-remote.c index 9a7eec6a9..f148e23f3 100644 --- a/criu/img-remote.c +++ b/criu/img-remote.c @@ -345,8 +345,10 @@ static int64_t read_header(int fd, char *snapshot_id, char *path, int *flags) int ret = pb_read_obj(fd, (void **)&li, PB_LOCAL_IMAGE); if (ret > 0) { - strncpy(snapshot_id, li->snapshot_id, PATHLEN); - strncpy(path, li->name, PATHLEN); + strncpy(snapshot_id, li->snapshot_id, PATHLEN - 1); + snapshot_id[PATHLEN - 1] = 0; + strncpy(path, li->name, PATHLEN - 1); + path[PATHLEN - 1] = 0; *flags = li->open_mode; } free(li); From b605eb07d11356527cb77d3505363969157321dc Mon Sep 17 00:00:00 2001 From: Andrei Vagin Date: Thu, 12 Jul 2018 23:41:42 +0300 Subject: [PATCH 034/249] remote: don't read from pointer after free CID 190778 (#1 of 1): Read from pointer after free (USE_AFTER_FREE) 7. deref_after_free: Dereferencing freed pointer rop. Signed-off-by: Andrei Vagin --- criu/img-remote.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/criu/img-remote.c b/criu/img-remote.c index f148e23f3..a9140423b 100644 --- a/criu/img-remote.c +++ b/criu/img-remote.c @@ -583,8 +583,8 @@ struct roperation* handle_accept_cache_read( if (write_reply_header(cli_fd, 0) < 0) { pr_perror("Error writing reply header for %s:%s", path, snapshot_id); - free(rop); close(rop->fd); + free(rop); } rop_set_rimg(rop, rimg); return rop; @@ -594,8 +594,8 @@ struct roperation* handle_accept_cache_read( pr_info("No image %s:%s.\n", path, snapshot_id); if (write_reply_header(cli_fd, ENOENT) < 0) pr_perror("Error writing reply header for unexisting image"); - free(rop); close(cli_fd); + free(rop); } return NULL; } From 9a699e37d51823ef6ea14b58451f6702955e559f Mon Sep 17 00:00:00 2001 From: Andrei Vagin Date: Fri, 27 Jul 2018 22:22:15 +0300 Subject: [PATCH 035/249] Revert "test: check criu restore with --auto-dedup" This reverts commit 8f1ba5892dd2b137a24e144febed2464f245ed1a. This test doesn't work right now: =[log]=> dump/zdtm/transition/maps007/174/3/restore.log ------------------------ grep Error ------------------------ (00.237564) 1: `- FD 2 pid 6 (00.237566) 1: `- type 1 ID 0xa (00.237567) 1: `- FD 3 pid 5 (00.237568) 1: `- FD 3 pid 6 (00.240025) 1: Error (criu/image.c:432): Unable to open pages-3.img: Permission denied (00.270600) uns: calling exit_usernsd (-1, 1) (00.270640) uns: daemon calls 0x469f80 (199, -1, 1) (00.270648) uns: `- daemon exits w/ 0 (00.271193) uns: daemon stopped (00.271199) Error (criu/cr-restore.c:2308): Restoring FAILED. ------------------------ ERROR OVER ------------------------ Signed-off-by: Andrei Vagin --- test/zdtm.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/test/zdtm.py b/test/zdtm.py index 18501a19d..c77c8559d 100755 --- a/test/zdtm.py +++ b/test/zdtm.py @@ -1202,12 +1202,6 @@ def restore(self): if self.__remote: r_opts += ["--remote"] - if self.__dedup: - r_opts += ["--auto-dedup"] - - if self.__dedup: - r_opts += ["--auto-dedup"] - self.__prev_dump_iter = None criu_dir = os.path.dirname(os.getcwd()) if os.getenv("GCOV"): From 154eb1b4410d4a6830499608cd97585f75075319 Mon Sep 17 00:00:00 2001 From: Radostin Stoyanov Date: Wed, 22 Aug 2018 20:54:31 +0100 Subject: [PATCH 036/249] img-proxy: Remove duplicated include Signed-off-by: Radostin Stoyanov Signed-off-by: Andrei Vagin --- criu/img-proxy.c | 1 - 1 file changed, 1 deletion(-) diff --git a/criu/img-proxy.c b/criu/img-proxy.c index c4d442e67..b48228481 100644 --- a/criu/img-proxy.c +++ b/criu/img-proxy.c @@ -1,6 +1,5 @@ #include -#include "img-remote.h" #include "img-remote.h" #include "criu-log.h" #include From 342188bff20dcef777063062f4d5df4b40b7006c Mon Sep 17 00:00:00 2001 From: Radostin Stoyanov Date: Wed, 22 Aug 2018 20:54:33 +0100 Subject: [PATCH 037/249] remote: Merge check_pending_{reads,forwards} Both functions check_pending_forwards() and check_pending_reads() have very similar functionality. This patch aims to reduce the duplication of code by merging both functions into check_pending() Signed-off-by: Radostin Stoyanov Signed-off-by: Andrei Vagin --- criu/img-remote.c | 30 +++++++++--------------------- 1 file changed, 9 insertions(+), 21 deletions(-) diff --git a/criu/img-remote.c b/criu/img-remote.c index a9140423b..972b18dff 100644 --- a/criu/img-remote.c +++ b/criu/img-remote.c @@ -866,22 +866,7 @@ void handle_roperation(struct epoll_event *event, struct roperation *rop) free(rop); } -void check_pending_forwards() -{ - struct roperation *rop = NULL; - struct rimage *rimg = NULL; - - list_for_each_entry(rop, &rop_forwarding, l) { - rimg = get_rimg_by_name(rop->snapshot_id, rop->path); - if (rimg != NULL) { - rop_set_rimg(rop, rimg); - forward_remote_image(rop); - return; - } - } -} - -void check_pending_reads() +void check_pending() { struct roperation *rop = NULL; struct rimage *rimg = NULL; @@ -890,7 +875,12 @@ void check_pending_reads() rimg = get_rimg_by_name(rop->snapshot_id, rop->path); if (rimg != NULL) { rop_set_rimg(rop, rimg); - event_set(epoll_fd, EPOLL_CTL_ADD, rop->fd, EPOLLOUT, rop); + if (restoring) { + event_set(epoll_fd, EPOLL_CTL_ADD, rop->fd, EPOLLOUT, rop); + } else { + forward_remote_image(rop); + return; + } } } } @@ -960,10 +950,8 @@ void accept_image_connections() { } // Check if there are any pending operations - if (restoring) - check_pending_reads(); - else if (!forwarding) - check_pending_forwards(); + if (restoring || !forwarding) + check_pending(); // Check if we can close the tcp socket (this will unblock the cache // to answer "no image" to restore). From d09ace52d57a75420ce68f92065dc53399a626c4 Mon Sep 17 00:00:00 2001 From: Radostin Stoyanov Date: Thu, 30 Aug 2018 13:10:31 +0100 Subject: [PATCH 038/249] remote: Replace PATHLEN with PATH_MAX The macro PATHLEN is redundant. It is defined such that its replacement token sequence is the token PATH_MAX. Signed-off-by: Radostin Stoyanov Acked-by: Adrian Reber Signed-off-by: Andrei Vagin --- criu/img-remote.c | 58 +++++++++++++++++++-------------------- criu/include/img-remote.h | 9 +++--- 2 files changed, 33 insertions(+), 34 deletions(-) diff --git a/criu/img-remote.c b/criu/img-remote.c index 972b18dff..579f5ed1d 100644 --- a/criu/img-remote.c +++ b/criu/img-remote.c @@ -24,7 +24,7 @@ #include "protobuf.h" #include "image.h" -#define PB_LOCAL_IMAGE_SIZE PATHLEN +#define PB_LOCAL_IMAGE_SIZE PATH_MAX #define EPOLL_MAX_EVENTS 50 // List of images already in memory. @@ -72,7 +72,7 @@ struct epoll_event *events; * ID which corresponds to the working directory specified by the user. */ struct snapshot { - char snapshot_id[PATHLEN]; + char snapshot_id[PATH_MAX]; struct list_head l; }; @@ -83,8 +83,8 @@ struct snapshot *new_snapshot(char *snapshot_id) if (!s) return NULL; - strncpy(s->snapshot_id, snapshot_id, PATHLEN - 1); - s->snapshot_id[PATHLEN - 1]= '\0'; + strncpy(s->snapshot_id, snapshot_id, PATH_MAX - 1); + s->snapshot_id[PATH_MAX - 1]= '\0'; return s; } @@ -98,8 +98,8 @@ struct rimage *get_rimg_by_name(const char *snapshot_id, const char *path) struct rimage *rimg = NULL; list_for_each_entry(rimg, &rimg_head, l) { - if (!strncmp(rimg->path, path, PATHLEN) && - !strncmp(rimg->snapshot_id, snapshot_id, PATHLEN)) { + if (!strncmp(rimg->path, path, PATH_MAX) && + !strncmp(rimg->snapshot_id, snapshot_id, PATH_MAX)) { return rimg; } } @@ -112,8 +112,8 @@ struct roperation *get_rop_by_name( struct roperation *rop = NULL; list_for_each_entry(rop, head, l) { - if (!strncmp(rop->path, path, PATHLEN) && - !strncmp(rop->snapshot_id, snapshot_id, PATHLEN)) { + if (!strncmp(rop->path, path, PATH_MAX) && + !strncmp(rop->snapshot_id, snapshot_id, PATH_MAX)) { return rop; } } @@ -345,10 +345,10 @@ static int64_t read_header(int fd, char *snapshot_id, char *path, int *flags) int ret = pb_read_obj(fd, (void **)&li, PB_LOCAL_IMAGE); if (ret > 0) { - strncpy(snapshot_id, li->snapshot_id, PATHLEN - 1); - snapshot_id[PATHLEN - 1] = 0; - strncpy(path, li->name, PATHLEN - 1); - path[PATHLEN - 1] = 0; + strncpy(snapshot_id, li->snapshot_id, PATH_MAX - 1); + snapshot_id[PATH_MAX - 1] = 0; + strncpy(path, li->name, PATH_MAX - 1); + path[PATH_MAX - 1] = 0; *flags = li->open_mode; } free(li); @@ -372,8 +372,8 @@ int64_t read_remote_header(int fd, char *snapshot_id, char *path, int *flags, ui int ret = pb_read_obj(fd, (void **)&ri, PB_REMOTE_IMAGE); if (ret > 0) { - strncpy(snapshot_id, ri->snapshot_id, PATHLEN); - strncpy(path, ri->name, PATHLEN); + strncpy(snapshot_id, ri->snapshot_id, PATH_MAX); + strncpy(path, ri->name, PATH_MAX); *flags = ri->open_mode; *size = ri->size; } @@ -391,10 +391,10 @@ static struct rimage *new_remote_image(char *path, char *snapshot_id) goto err; } - strncpy(rimg->path, path, PATHLEN -1 ); - strncpy(rimg->snapshot_id, snapshot_id, PATHLEN - 1); - rimg->path[PATHLEN - 1] = '\0'; - rimg->snapshot_id[PATHLEN - 1] = '\0'; + strncpy(rimg->path, path, PATH_MAX -1 ); + strncpy(rimg->snapshot_id, snapshot_id, PATH_MAX - 1); + rimg->path[PATH_MAX - 1] = '\0'; + rimg->snapshot_id[PATH_MAX - 1] = '\0'; INIT_LIST_HEAD(&(rimg->buf_head)); list_add_tail(&(buf->l), &(rimg->buf_head)); rimg->curr_fwd_buf = buf; @@ -415,10 +415,10 @@ static struct roperation *new_remote_operation( pr_perror("Unable to allocate remote operation structures"); return NULL; } - strncpy(rop->path, path, PATHLEN -1 ); - strncpy(rop->snapshot_id, snapshot_id, PATHLEN - 1); - rop->path[PATHLEN - 1] = '\0'; - rop->snapshot_id[PATHLEN - 1] = '\0'; + strncpy(rop->path, path, PATH_MAX -1 ); + strncpy(rop->snapshot_id, snapshot_id, PATH_MAX - 1); + rop->path[PATH_MAX - 1] = '\0'; + rop->snapshot_id[PATH_MAX - 1] = '\0'; rop->fd = cli_fd; rop->flags = flags; rop->close_fd = close_fd; @@ -632,8 +632,8 @@ void forward_remote_image(struct roperation* rop) void handle_remote_accept(int fd) { - char path[PATHLEN]; - char snapshot_id[PATHLEN]; + char path[PATH_MAX]; + char snapshot_id[PATH_MAX]; int flags; uint64_t size = 0; int64_t ret; @@ -679,8 +679,8 @@ void handle_remote_accept(int fd) void handle_local_accept(int fd) { int cli_fd; - char path[PATHLEN]; - char snapshot_id[PATHLEN]; + char path[PATH_MAX]; + char snapshot_id[PATH_MAX]; int flags = 0; struct sockaddr_in cli_addr; socklen_t clilen = sizeof(cli_addr); @@ -1228,12 +1228,12 @@ int push_snapshot_id(void) return -1; } - rn.snapshot_id = xmalloc(sizeof(char) * PATHLEN); + rn.snapshot_id = xmalloc(sizeof(char) * PATH_MAX); if (!rn.snapshot_id) { close(sockfd); return -1; } - strncpy(rn.snapshot_id, snapshot_id, PATHLEN); + strncpy(rn.snapshot_id, snapshot_id, PATH_MAX); n = pb_write_obj(sockfd, &rn, PB_SNAPSHOT_ID); @@ -1261,7 +1261,7 @@ int get_curr_snapshot_id_idx(void) pull_snapshot_ids(); list_for_each_entry(si, &snapshot_head, l) { - if (!strncmp(si->snapshot_id, snapshot_id, PATHLEN)) + if (!strncmp(si->snapshot_id, snapshot_id, PATH_MAX)) return idx; idx++; } diff --git a/criu/include/img-remote.h b/criu/include/img-remote.h index 15e1bc5d5..e19d06736 100644 --- a/criu/include/img-remote.h +++ b/criu/include/img-remote.h @@ -9,7 +9,6 @@ #ifndef IMAGE_REMOTE_H #define IMAGE_REMOTE_H -#define PATHLEN PATH_MAX #define DUMP_FINISH "DUMP_FINISH" #define RESTORE_FINISH "RESTORE_FINISH" #define PARENT_IMG "parent" @@ -28,8 +27,8 @@ struct rbuf { struct rimage { /* Path and snapshot id identify the image. */ - char path[PATHLEN]; - char snapshot_id[PATHLEN]; + char path[PATH_MAX]; + char snapshot_id[PATH_MAX]; /* List anchor. */ struct list_head l; /* List of buffers that compose the image. */ @@ -49,8 +48,8 @@ struct roperation { /* File descriptor being used. */ int fd; /* Path and snapshot id identify the required image. */ - char path[PATHLEN]; - char snapshot_id[PATHLEN]; + char path[PATH_MAX]; + char snapshot_id[PATH_MAX]; /* Remote image being used (may be null if the operation is pending). */ struct rimage *rimg; /* Flags for the operation. */ From 24314fded9f22888d3f344e7107d7e7a43156448 Mon Sep 17 00:00:00 2001 From: Radostin Stoyanov Date: Thu, 13 Sep 2018 18:28:05 +0100 Subject: [PATCH 039/249] remote: Refactor TCP server setup The function `setup_TCP_server_socket` (defined in img-remote.c) and `setup_tcp_server` (defined in util.c) have very similar functionality. Replace setup_TCP_server_socket() with setup_tcp_server() to reduce code duplication and to enable IPv6 support for the image-cache action of CRIU. We set SO_REUSEADDR flag to allow reuse of local addresses. Signed-off-by: Radostin Stoyanov Signed-off-by: Andrei Vagin --- criu/img-cache.c | 3 ++- criu/img-remote.c | 40 --------------------------------------- criu/include/img-remote.h | 1 - 3 files changed, 2 insertions(+), 42 deletions(-) diff --git a/criu/img-cache.c b/criu/img-cache.c index 98fdf80e6..b93874c9f 100644 --- a/criu/img-cache.c +++ b/criu/img-cache.c @@ -7,6 +7,7 @@ #include #include #include "cr_options.h" +#include "util.h" int accept_proxy_to_cache(int sockfd) { @@ -32,7 +33,7 @@ int image_cache(bool background, char *local_cache_path, unsigned short cache_wr proxy_to_cache_fd = opts.ps_socket; pr_info("Re-using ps socket %d\n", proxy_to_cache_fd); } else { - proxy_to_cache_fd = setup_TCP_server_socket(cache_write_port); + proxy_to_cache_fd = setup_tcp_server("image cache", NULL, &cache_write_port); if (proxy_to_cache_fd < 0) { pr_perror("Unable to open proxy to cache TCP socket"); return -1; diff --git a/criu/img-remote.c b/criu/img-remote.c index 579f5ed1d..ce7f1a670 100644 --- a/criu/img-remote.c +++ b/criu/img-remote.c @@ -120,46 +120,6 @@ struct roperation *get_rop_by_name( return NULL; } -int setup_TCP_server_socket(int port) -{ - struct sockaddr_in serv_addr; - int sockopt = 1; - int sockfd = socket(AF_INET, SOCK_STREAM, 0); - - if (sockfd < 0) { - pr_perror("Unable to open image socket"); - return -1; - } - - bzero((char *) &serv_addr, sizeof(serv_addr)); - serv_addr.sin_family = AF_INET; - serv_addr.sin_addr.s_addr = INADDR_ANY; - serv_addr.sin_port = htons(port); - - if (setsockopt( - sockfd, SOL_SOCKET, SO_REUSEADDR, &sockopt, sizeof(sockopt)) == -1) { - pr_perror("Unable to set SO_REUSEADDR"); - goto err; - } - - if (bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) { - pr_perror("Unable to bind image socket"); - goto err; - } - - if (listen(sockfd, DEFAULT_LISTEN)) { - pr_perror("Unable to listen image socket"); - goto err; - } - - return sockfd; -err: - close(sockfd); - return -1; -} - - - int setup_TCP_client_socket(char *hostname, int port) { int sockfd; diff --git a/criu/include/img-remote.h b/criu/include/img-remote.h index e19d06736..d6297024a 100644 --- a/criu/include/img-remote.h +++ b/criu/include/img-remote.h @@ -84,7 +84,6 @@ int64_t recv_image_async(struct roperation *op); int64_t read_remote_header(int fd, char *snapshot_id, char *path, int *open_mode, uint64_t *size); int64_t write_remote_header(int fd, char *snapshot_id, char *path, int open_mode, uint64_t size); -int setup_TCP_server_socket(int port); int setup_TCP_client_socket(char *hostname, int port); int setup_UNIX_server_socket(char *path); void socket_set_non_blocking(int fd); From 35b2a3d54523ca16e95be77dc4bb05a1491b74f5 Mon Sep 17 00:00:00 2001 From: Radostin Stoyanov Date: Thu, 13 Sep 2018 18:28:08 +0100 Subject: [PATCH 040/249] remote: Refactor TCP client setup Remove setup_TCP_client_socket() and use setup_tcp_server() instead as both functions have very similar functionality. Signed-off-by: Radostin Stoyanov Signed-off-by: Andrei Vagin --- criu/img-proxy.c | 3 ++- criu/img-remote.c | 36 ------------------------------------ criu/include/img-remote.h | 1 - criu/include/util.h | 2 +- criu/page-xfer.c | 2 +- criu/util.c | 8 ++++---- 6 files changed, 8 insertions(+), 44 deletions(-) diff --git a/criu/img-proxy.c b/criu/img-proxy.c index b48228481..65490d29f 100644 --- a/criu/img-proxy.c +++ b/criu/img-proxy.c @@ -6,6 +6,7 @@ #include #include #include "cr_options.h" +#include "util.h" int image_proxy(bool background, char *local_proxy_path, char *fwd_host, unsigned short fwd_port) { @@ -23,7 +24,7 @@ int image_proxy(bool background, char *local_proxy_path, char *fwd_host, unsigne proxy_to_cache_fd = opts.ps_socket; pr_info("Re-using ps socket %d\n", proxy_to_cache_fd); } else { - proxy_to_cache_fd = setup_TCP_client_socket(fwd_host, fwd_port); + proxy_to_cache_fd = setup_tcp_client(fwd_host, fwd_port); if (proxy_to_cache_fd < 0) { pr_perror("Unable to open proxy to cache TCP socket"); return -1; // TODO - should close other sockets. diff --git a/criu/img-remote.c b/criu/img-remote.c index ce7f1a670..a83ce38f1 100644 --- a/criu/img-remote.c +++ b/criu/img-remote.c @@ -120,42 +120,6 @@ struct roperation *get_rop_by_name( return NULL; } -int setup_TCP_client_socket(char *hostname, int port) -{ - int sockfd; - struct sockaddr_in serv_addr; - struct hostent *server; - - sockfd = socket(AF_INET, SOCK_STREAM, 0); - if (sockfd < 0) { - pr_perror("Unable to open remote image socket"); - return -1; - } - - server = gethostbyname(hostname); - if (server == NULL) { - pr_perror("Unable to get host by name (%s)", hostname); - goto err; - } - - bzero((char *) &serv_addr, sizeof(serv_addr)); - serv_addr.sin_family = AF_INET; - bcopy((char *) server->h_addr, - (char *) &serv_addr.sin_addr.s_addr, - server->h_length); - serv_addr.sin_port = htons(port); - - if (connect(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) { - pr_perror("Unable to connect to remote %s", hostname); - goto err; - } - - return sockfd; -err: - close(sockfd); - return -1; -} - int event_set(int epoll_fd, int op, int fd, uint32_t events, void *data) { int ret; diff --git a/criu/include/img-remote.h b/criu/include/img-remote.h index d6297024a..ec4b15e7c 100644 --- a/criu/include/img-remote.h +++ b/criu/include/img-remote.h @@ -84,7 +84,6 @@ int64_t recv_image_async(struct roperation *op); int64_t read_remote_header(int fd, char *snapshot_id, char *path, int *open_mode, uint64_t *size); int64_t write_remote_header(int fd, char *snapshot_id, char *path, int open_mode, uint64_t size); -int setup_TCP_client_socket(char *hostname, int port); int setup_UNIX_server_socket(char *path); void socket_set_non_blocking(int fd); diff --git a/criu/include/util.h b/criu/include/util.h index 621abb4a0..644b348c9 100644 --- a/criu/include/util.h +++ b/criu/include/util.h @@ -294,7 +294,7 @@ void print_data(unsigned long addr, unsigned char *data, size_t size); int setup_tcp_server(char *type, char *addr, unsigned short *port); int run_tcp_server(bool daemon_mode, int *ask, int cfd, int sk); -int setup_tcp_client(char *hostname); +int setup_tcp_client(char *hostname, unsigned short port); #define LAST_PID_PATH "sys/kernel/ns_last_pid" #define PID_MAX_PATH "sys/kernel/pid_max" diff --git a/criu/page-xfer.c b/criu/page-xfer.c index d32de369c..a7b5c70f1 100644 --- a/criu/page-xfer.c +++ b/criu/page-xfer.c @@ -1054,7 +1054,7 @@ static int connect_to_page_server(void) goto out; } - page_server_sk = setup_tcp_client(opts.addr); + page_server_sk = setup_tcp_client(opts.addr, opts.port); if (page_server_sk == -1) return -1; out: diff --git a/criu/util.c b/criu/util.c index c4f9dcdf8..9523fc115 100644 --- a/criu/util.c +++ b/criu/util.c @@ -1207,7 +1207,7 @@ int run_tcp_server(bool daemon_mode, int *ask, int cfd, int sk) return -1; } -int setup_tcp_client(char *hostname) +int setup_tcp_client(char *hostname, unsigned short port) { struct sockaddr_storage saddr; struct addrinfo addr_criteria, *addr_list, *p; @@ -1244,9 +1244,9 @@ int setup_tcp_client(char *hostname) } inet_ntop(p->ai_family, ip, ipstr, sizeof(ipstr)); - pr_info("Connecting to server %s:%u\n", ipstr, opts.port); + pr_info("Connecting to server %s:%u\n", ipstr, port); - if (get_sockaddr_in(&saddr, ipstr, opts.port)) + if (get_sockaddr_in(&saddr, ipstr, port)) goto out; sk = socket(saddr.ss_family, SOCK_STREAM, IPPROTO_TCP); @@ -1256,7 +1256,7 @@ int setup_tcp_client(char *hostname) } if (connect(sk, (struct sockaddr *)&saddr, sizeof(saddr)) < 0) { - pr_info("Can't connect to server %s:%u\n", ipstr, opts.port); + pr_info("Can't connect to server %s:%u\n", ipstr, port); close(sk); sk = -1; } else { From cffe6e373e52a9108f74b7335c1f1b6944a5f512 Mon Sep 17 00:00:00 2001 From: Radostin Stoyanov Date: Thu, 13 Sep 2018 18:28:09 +0100 Subject: [PATCH 041/249] remote: pull_snapshot_ids: Simplify if condition Check only once if (sockfd < 0) Signed-off-by: Radostin Stoyanov Signed-off-by: Andrei Vagin --- criu/img-remote.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/criu/img-remote.c b/criu/img-remote.c index a83ce38f1..9805c7417 100644 --- a/criu/img-remote.c +++ b/criu/img-remote.c @@ -1109,11 +1109,12 @@ static int pull_snapshot_ids(void) sockfd = read_remote_image_connection(NULL_SNAPSHOT_ID, PARENT_IMG); /* The connection was successful but there is not file. */ - if (sockfd < 0 && errno == ENOENT) + if (sockfd < 0) { + if (errno != ENOENT) { + pr_err("Unable to open snapshot id read connection\n"); + return -1; + } return 0; - else if (sockfd < 0) { - pr_err("Unable to open snapshot id read connection\n"); - return -1; } while (1) { From 967a6ab99e934941ba23183333a3c643257b950a Mon Sep 17 00:00:00 2001 From: Radostin Stoyanov Date: Thu, 13 Sep 2018 18:28:10 +0100 Subject: [PATCH 042/249] remote: Use tmp file buffer when restore ip dump When CRIU calls the ip tool on restore, it passes the fd of remote socket by replacing the STDIN before execvp. The stdin is used by the ip tool to receive input. However, the ip tool calls ftell(stdin) which fails with "Illegal seek" since UNIX sockets do not support file positioning operations. To resolve this issue, read the received content from the UNIX socket and store it into temporary file, then replace STDIN with the fd of this tmp file. # python test/zdtm.py run -t zdtm/static/env00 --remote -f ns === Run 1/1 ================ zdtm/static/env00 ========================= Run zdtm/static/env00 in ns ========================== Start test ./env00 --pidfile=env00.pid --outfile=env00.out --envname=ENV_00_TEST Adding image cache Adding image proxy Run criu dump Run criu restore =[log]=> dump/zdtm/static/env00/31/1/restore.log ------------------------ grep Error ------------------------ RTNETLINK answers: File exists (00.229895) 1: do_open_remote_image RDONLY path=route-9.img snapshot_id=dump/zdtm/static/env00/31/1 (00.230316) 1: Running ip route restore Failed to restore: ftell: Illegal seek (00.232757) 1: Error (criu/util.c:712): exited, status=255 (00.232777) 1: Error (criu/net.c:1479): IP tool failed on route restore (00.232803) 1: Error (criu/net.c:2153): Can't create net_ns (00.255091) Error (criu/cr-restore.c:1177): 105 killed by signal 9: Killed (00.255307) Error (criu/mount.c:2960): mnt: Can't remove the directory /tmp/.criu.mntns.dTd7ak: No such file or directory (00.255339) Error (criu/cr-restore.c:2119): Restoring FAILED. ------------------------ ERROR OVER ------------------------ ################# Test zdtm/static/env00 FAIL at CRIU restore ################## ##################################### FAIL ##################################### Fixes #311 Signed-off-by: Radostin Stoyanov Signed-off-by: Andrei Vagin --- criu/net.c | 31 +++++++++++++++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/criu/net.c b/criu/net.c index a5f632dd6..43ff8e894 100644 --- a/criu/net.c +++ b/criu/net.c @@ -1919,19 +1919,46 @@ static int dump_netns_conf(struct ns_id *ns, struct cr_imgset *fds) static int restore_ip_dump(int type, int pid, char *cmd) { - int ret = -1; + int ret = -1, sockfd, n, written; + FILE *tmp_file; struct cr_img *img; + char buf[1024]; img = open_image(type, O_RSTR, pid); if (empty_image(img)) { close_image(img); return 0; } + sockfd = img_raw_fd(img); + tmp_file = tmpfile(); + if (!tmp_file) { + pr_perror("Failed to open tmpfile"); + return -1; + } + + while ((n = read(sockfd, buf, 1024)) > 0) { + written = fwrite(buf, sizeof(char), n, tmp_file); + if (written < n) { + pr_perror("Failed to write to tmpfile " + "[written: %d; total: %d]", written, n); + return -1; + } + } + + if (fseek(tmp_file, 0, SEEK_SET)) { + pr_perror("Failed to set file position to beginning of tmpfile"); + return -1; + } + if (img) { - ret = run_ip_tool(cmd, "restore", NULL, NULL, img_raw_fd(img), -1, 0); + ret = run_ip_tool(cmd, "restore", NULL, NULL, fileno(tmp_file), -1, 0); close_image(img); } + if(fclose(tmp_file)) { + pr_perror("Failed to close tmpfile"); + } + return ret; } From 291bb9739ec9cbad20f2ea8d18ee42dd6010fc4a Mon Sep 17 00:00:00 2001 From: Radostin Stoyanov Date: Sun, 23 Sep 2018 15:31:54 +0100 Subject: [PATCH 043/249] python: Replace xrange with range In Py2 `range` returns a list and `xrange` creates a sequence object that evaluates lazily. In Py3 `range` is equivalent to `xrange` in Py2. Signed-off-by: Radostin Stoyanov Signed-off-by: Andrei Vagin --- scripts/criu-ns | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/criu-ns b/scripts/criu-ns index e7ebbf0ca..0910f2a33 100755 --- a/scripts/criu-ns +++ b/scripts/criu-ns @@ -124,7 +124,7 @@ def wrap_restore(): def get_varg(args): - for i in xrange(1, len(sys.argv)): + for i in range(1, len(sys.argv)): if not sys.argv[i] in args: continue From f8945baf1cc7a60917c0e82825635ae3bcb916a4 Mon Sep 17 00:00:00 2001 From: Pavel Tikhomirov Date: Fri, 28 Sep 2018 16:11:33 +0300 Subject: [PATCH 044/249] image: fix typo in debug message Signed-off-by: Pavel Tikhomirov Signed-off-by: Andrei Vagin --- criu/image.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/criu/image.c b/criu/image.c index 0c9f7666c..173ca21ad 100644 --- a/criu/image.c +++ b/criu/image.c @@ -398,7 +398,7 @@ int do_open_remote_image(int dfd, char *path, int flags) path, snapshot_id); ret = read_remote_image_connection(snapshot_id, path); } else { - pr_debug("do_open_remote_image WDONLY path=%s snapshot_id=%s\n", + pr_debug("do_open_remote_image WRONLY path=%s snapshot_id=%s\n", path, snapshot_id); ret = write_remote_image_connection(snapshot_id, path, O_WRONLY); } From 12b2e1be97b290f8b58cccbf74478ec61fd2cd2f Mon Sep 17 00:00:00 2001 From: Pavel Tikhomirov Date: Fri, 28 Sep 2018 16:11:35 +0300 Subject: [PATCH 045/249] zdtm: check criu restore with --auto-dedup Signed-off-by: Pavel Tikhomirov Signed-off-by: Andrei Vagin --- test/zdtm.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test/zdtm.py b/test/zdtm.py index c77c8559d..e6325d5f5 100755 --- a/test/zdtm.py +++ b/test/zdtm.py @@ -1202,6 +1202,9 @@ def restore(self): if self.__remote: r_opts += ["--remote"] + if self.__dedup: + r_opts += ["--auto-dedup"] + self.__prev_dump_iter = None criu_dir = os.path.dirname(os.getcwd()) if os.getenv("GCOV"): From a2fcf4c9aae6fc5c0bccaa84ebec3d5b5b9caf43 Mon Sep 17 00:00:00 2001 From: Radostin Stoyanov Date: Wed, 22 Aug 2018 20:54:30 +0100 Subject: [PATCH 046/249] Fix typos Most of the typos were found by codespell. Signed-off-by: Radostin Stoyanov Signed-off-by: Andrei Vagin --- criu/img-remote.c | 10 +++++----- criu/include/img-remote.h | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/criu/img-remote.c b/criu/img-remote.c index 9805c7417..96528dfee 100644 --- a/criu/img-remote.c +++ b/criu/img-remote.c @@ -30,18 +30,18 @@ // List of images already in memory. LIST_HEAD(rimg_head); -// List of local operations currently in-progess. +// List of local operations currently in-progress. LIST_HEAD(rop_inprogress); // List of local operations pending (reads on the restore side for images that // still haven't arrived). - LIST_HEAD(rop_pending); + // List of images waiting to be forwarded. The head of the list is currently // being forwarded. LIST_HEAD(rop_forwarding); -// List of snapshots (useful when doing incremental restores/dumps +// List of snapshots (useful when doing incremental restores/dumps) LIST_HEAD(snapshot_head); // Snapshot id (setup at launch time by dump or restore). @@ -369,7 +369,7 @@ static void rop_set_rimg(struct roperation* rop, struct rimage* rimg) rop->curr_recv_buf = list_entry(rimg->buf_head.prev, struct rbuf, l); } else { - // Writes or reads are simple. Just do it from the beginnig. + // Writes or reads are simple. Just do it from the beginning. rop->curr_recv_buf = list_entry(rimg->buf_head.next, struct rbuf, l); rop->curr_sent_buf = list_entry(rimg->buf_head.next, struct rbuf, l); rop->curr_sent_bytes = 0; @@ -540,7 +540,7 @@ void forward_remote_image(struct roperation* rop) return; } - pr_info("[fd=%d] Fowarding %s request for %s:%s (%" PRIu64 " bytes\n", + pr_info("[fd=%d] Forwarding %s request for %s:%s (%" PRIu64 " bytes\n", rop->fd, rop->flags == O_RDONLY ? "read" : rop->flags == O_APPEND ? "append" : "write", diff --git a/criu/include/img-remote.h b/criu/include/img-remote.h index ec4b15e7c..bb70e81e5 100644 --- a/criu/include/img-remote.h +++ b/criu/include/img-remote.h @@ -58,7 +58,7 @@ struct roperation { bool close_fd; /* Note: recv operation only. How much bytes should be received. */ uint64_t size; - /* Note: recv operation only. Buffer being writen. */ + /* Note: recv operation only. Buffer being written. */ struct rbuf *curr_recv_buf; // TODO - needed? Could be replaced by list.last! /* Note: send operation only. Pointer to buffer being sent. */ struct rbuf *curr_sent_buf; @@ -150,7 +150,7 @@ char *get_snapshot_id_from_idx(int idx); */ int push_snapshot_id(); -/* Returns the snapshot id index that preceeds the current snapshot_id. */ +/* Returns the snapshot id index that precedes the current snapshot_id. */ int get_curr_parent_snapshot_id_idx(); #endif From 98b44fb700f8eb944b042317f8a755f6c9c1bba8 Mon Sep 17 00:00:00 2001 From: Radostin Stoyanov Date: Tue, 29 Jan 2019 17:37:45 +0000 Subject: [PATCH 047/249] remote: Fix code indentation This patch does not introduce any functional changes. Signed-off-by: Radostin Stoyanov Signed-off-by: Andrei Vagin --- criu/img-remote.c | 71 +++++++++++++++++++---------------------------- 1 file changed, 28 insertions(+), 43 deletions(-) diff --git a/criu/img-remote.c b/criu/img-remote.c index 96528dfee..5731fc1a8 100644 --- a/criu/img-remote.c +++ b/criu/img-remote.c @@ -359,16 +359,14 @@ static void rop_set_rimg(struct roperation* rop, struct rimage* rimg) if (rop->fd == proxy_to_cache_fd) { rop->curr_sent_buf = rimg->curr_fwd_buf; rop->curr_sent_bytes = rimg->curr_fwd_bytes; - } - // For local appends, just write at the end. - else { + } else { + // For local appends, just write at the end. rop->curr_sent_buf = list_entry(rimg->buf_head.prev, struct rbuf, l); rop->curr_sent_bytes = rop->curr_sent_buf->nbytes; } // On the receiver size, we just append rop->curr_recv_buf = list_entry(rimg->buf_head.prev, struct rbuf, l); - } - else { + } else { // Writes or reads are simple. Just do it from the beginning. rop->curr_recv_buf = list_entry(rimg->buf_head.next, struct rbuf, l); rop->curr_sent_buf = list_entry(rimg->buf_head.next, struct rbuf, l); @@ -405,8 +403,7 @@ struct roperation* handle_accept_write( pr_perror("Error preparing remote image"); goto err; } - } - else { + } else { list_del(&(rimg->l)); if (flags == O_APPEND) clear_remote_image(rimg); @@ -512,9 +509,8 @@ struct roperation* handle_accept_cache_read( } rop_set_rimg(rop, rimg); return rop; - } - // The file does not exist. - else if (finished_remote) { + } else if (finished_remote) { + // The file does not exist. pr_info("No image %s:%s.\n", path, snapshot_id); if (write_reply_header(cli_fd, ENOENT) < 0) pr_perror("Error writing reply header for unexisting image"); @@ -630,13 +626,11 @@ void handle_local_accept(int fd) // Write/Append case (only possible in img-proxy). if (flags != O_RDONLY) { rop = handle_accept_proxy_write(cli_fd, snapshot_id, path, flags); - } - // Read case while restoring (img-cache). - else if (restoring) { + } else if (restoring) { + // Read case while restoring (img-cache). rop = handle_accept_cache_read(cli_fd, snapshot_id, path, flags); - } - // Read case while dumping (img-proxy). - else { + } else { + // Read case while dumping (img-proxy). rop = handle_accept_proxy_read(cli_fd, snapshot_id, path, flags); } @@ -671,8 +665,8 @@ void finish_proxy_read(struct roperation* rop) forwarding = false; - // If there are images waiting to be forwarded, forward the next. - if (!list_empty(&rop_forwarding)) { + // If there are images waiting to be forwarded, forward the next. + if (!list_empty(&rop_forwarding)) { forward_remote_image(list_entry(rop_forwarding.next, struct roperation, l)); } } @@ -684,9 +678,8 @@ void finish_proxy_write(struct roperation* rop) if (!strncmp(rop->path, DUMP_FINISH, sizeof(DUMP_FINISH))) { // TODO - couldn't we handle the DUMP_FINISH in inside handle_accept_proxy_write? finish_local(); - } - // Normal image received, forward it. - else { + } else { + // Normal image received, forward it. struct roperation *rop_to_forward = new_remote_operation( rop->path, rop->snapshot_id, proxy_to_cache_fd, rop->flags, false); @@ -696,7 +689,7 @@ void finish_proxy_write(struct roperation* rop) rop_set_rimg(rop_to_forward, rop->rimg); if (list_empty(&rop_forwarding)) { forward_remote_image(rop_to_forward); - } + } list_add_tail(&(rop_to_forward->l), &rop_forwarding); } } @@ -773,14 +766,11 @@ void handle_roperation(struct epoll_event *event, struct roperation *rop) // Cached side (finished receiving forwarded image) if (restoring) { finish_cache_write(rop); - } - // Proxy side (finished receiving local image) - else { + } else { + // Proxy side (finished receiving local image) finish_proxy_write(rop); } - } - // If send operation if finished - else { + } else { // Proxy side (Finished forwarding image or reading it locally). if (!restoring) finish_proxy_read(rop); @@ -853,19 +843,17 @@ void accept_image_connections() { for (i = 0; i < n_events; i++) { // Accept from local dump/restore? if (events[i].data.ptr == &local_req_fd) { - if ( events[i].events & EPOLLHUP || + if (events[i].events & EPOLLHUP || events[i].events & EPOLLERR) { if (!finished_local) pr_perror("Unable to accept more local image connections"); goto end; } handle_local_accept(local_req_fd); - } - else if (restoring && !forwarding && events[i].data.ptr == &proxy_to_cache_fd) { + } else if (restoring && !forwarding && events[i].data.ptr == &proxy_to_cache_fd) { event_set(epoll_fd, EPOLL_CTL_DEL, proxy_to_cache_fd, 0, 0); handle_remote_accept(proxy_to_cache_fd); - } - else { + } else { struct roperation *rop = (struct roperation*)events[i].data.ptr; event_set(epoll_fd, EPOLL_CTL_DEL, rop->fd, 0, 0); @@ -973,22 +961,19 @@ int64_t send_image_async(struct roperation *op) list_entry(op->curr_sent_buf->l.next, struct rbuf, l); op->curr_sent_bytes = 0; return n; - } - else if (op->curr_sent_bytes == op->curr_sent_buf->nbytes) { + } else if (op->curr_sent_bytes == op->curr_sent_buf->nbytes) { if (close_fd) close(fd); return 0; } return n; - } - else if (errno == EPIPE || errno == ECONNRESET) { + } else if (errno == EPIPE || errno == ECONNRESET) { pr_warn("Connection for %s:%s was closed early than expected\n", rimg->path, rimg->snapshot_id); return 0; } else if (errno == EAGAIN || errno == EWOULDBLOCK) { return errno; - } - else { + } else { pr_perror("Write on %s:%s socket failed", rimg->path, rimg->snapshot_id); return -1; @@ -1016,9 +1001,9 @@ int read_remote_image_connection(char *snapshot_id, char *path) path, snapshot_id); return -1; } - if (!error || !strncmp(path, RESTORE_FINISH, sizeof(RESTORE_FINISH))) + if (!error || !strncmp(path, RESTORE_FINISH, sizeof(RESTORE_FINISH))) { return sockfd; - else if (error == ENOENT) { + } else if (error == ENOENT) { pr_info("Image does not exist (%s:%s)\n", path, snapshot_id); close(sockfd); return -ENOENT; @@ -1186,13 +1171,13 @@ int get_curr_snapshot_id_idx(void) pull_snapshot_ids(); list_for_each_entry(si, &snapshot_head, l) { - if (!strncmp(si->snapshot_id, snapshot_id, PATH_MAX)) + if (!strncmp(si->snapshot_id, snapshot_id, PATH_MAX)) return idx; idx++; } pr_err("Error, could not find current snapshot id (%s) fd\n", - snapshot_id); + snapshot_id); return -1; } From b1522cd12852421dda0b4d013613997918a2f945 Mon Sep 17 00:00:00 2001 From: Radostin Stoyanov Date: Tue, 29 Jan 2019 17:37:46 +0000 Subject: [PATCH 048/249] img-cache: Drop unnecessary accept_proxy_to_cache The function accept_proxy_to_cache() is a wrapper around accept(). Use accept() directly instead. Signed-off-by: Radostin Stoyanov Signed-off-by: Andrei Vagin --- criu/img-cache.c | 21 +++++---------------- 1 file changed, 5 insertions(+), 16 deletions(-) diff --git a/criu/img-cache.c b/criu/img-cache.c index b93874c9f..70c3a1663 100644 --- a/criu/img-cache.c +++ b/criu/img-cache.c @@ -9,20 +9,6 @@ #include "cr_options.h" #include "util.h" -int accept_proxy_to_cache(int sockfd) -{ - struct sockaddr_in cli_addr; - socklen_t clilen = sizeof(cli_addr); - int proxy_fd = accept(sockfd, (struct sockaddr *) &cli_addr, &clilen); - - if (proxy_fd < 0) { - pr_perror("Unable to accept remote image connection from image proxy"); - return -1; - } - - return proxy_fd; -} - int image_cache(bool background, char *local_cache_path, unsigned short cache_write_port) { pr_info("Proxy to Cache Port %d, CRIU to Cache Path %s\n", @@ -39,9 +25,12 @@ int image_cache(bool background, char *local_cache_path, unsigned short cache_wr return -1; } // Wait to accept connection from proxy. - proxy_to_cache_fd = accept_proxy_to_cache(proxy_to_cache_fd); - if (proxy_to_cache_fd < 0) + proxy_to_cache_fd = accept(proxy_to_cache_fd, NULL, 0); + if (proxy_to_cache_fd < 0) { + pr_perror("Unable to accept remote image connection" + " from image proxy"); return -1; // TODO - should close other sockets. + } } pr_info("Cache is connected to Proxy through fd %d\n", proxy_to_cache_fd); From 181687a07146b34959b72437502bb5f37e998c75 Mon Sep 17 00:00:00 2001 From: Radostin Stoyanov Date: Tue, 29 Jan 2019 17:37:47 +0000 Subject: [PATCH 049/249] img-cache/proxy: Close sockets on failure Signed-off-by: Radostin Stoyanov Signed-off-by: Andrei Vagin --- criu/img-cache.c | 13 +++++++++---- criu/img-proxy.c | 3 ++- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/criu/img-cache.c b/criu/img-cache.c index 70c3a1663..53da022ac 100644 --- a/criu/img-cache.c +++ b/criu/img-cache.c @@ -11,6 +11,8 @@ int image_cache(bool background, char *local_cache_path, unsigned short cache_write_port) { + int tmp; + pr_info("Proxy to Cache Port %d, CRIU to Cache Path %s\n", cache_write_port, local_cache_path); restoring = true; @@ -25,12 +27,14 @@ int image_cache(bool background, char *local_cache_path, unsigned short cache_wr return -1; } // Wait to accept connection from proxy. - proxy_to_cache_fd = accept(proxy_to_cache_fd, NULL, 0); - if (proxy_to_cache_fd < 0) { + tmp = accept(proxy_to_cache_fd, NULL, 0); + if (tmp < 0) { pr_perror("Unable to accept remote image connection" " from image proxy"); - return -1; // TODO - should close other sockets. + close(proxy_to_cache_fd); + return -1; } + proxy_to_cache_fd = tmp; } pr_info("Cache is connected to Proxy through fd %d\n", proxy_to_cache_fd); @@ -38,7 +42,8 @@ int image_cache(bool background, char *local_cache_path, unsigned short cache_wr local_req_fd = setup_UNIX_server_socket(local_cache_path); if (local_req_fd < 0) { pr_perror("Unable to open cache to proxy UNIX socket"); - return -1; // TODO - should close other sockets. + close(proxy_to_cache_fd); + return -1; } diff --git a/criu/img-proxy.c b/criu/img-proxy.c index 65490d29f..73a726305 100644 --- a/criu/img-proxy.c +++ b/criu/img-proxy.c @@ -27,7 +27,8 @@ int image_proxy(bool background, char *local_proxy_path, char *fwd_host, unsigne proxy_to_cache_fd = setup_tcp_client(fwd_host, fwd_port); if (proxy_to_cache_fd < 0) { pr_perror("Unable to open proxy to cache TCP socket"); - return -1; // TODO - should close other sockets. + close(local_req_fd); + return -1; } } From 0bcca9a326c914782cfc1aac8776471a2f8422ee Mon Sep 17 00:00:00 2001 From: Radostin Stoyanov Date: Wed, 30 Jan 2019 00:07:59 +0000 Subject: [PATCH 050/249] remote: Drop unused PB_LOCAL_IMAGE_SIZE Signed-off-by: Radostin Stoyanov --- criu/img-remote.c | 1 - 1 file changed, 1 deletion(-) diff --git a/criu/img-remote.c b/criu/img-remote.c index 5731fc1a8..12e0c7f98 100644 --- a/criu/img-remote.c +++ b/criu/img-remote.c @@ -24,7 +24,6 @@ #include "protobuf.h" #include "image.h" -#define PB_LOCAL_IMAGE_SIZE PATH_MAX #define EPOLL_MAX_EVENTS 50 // List of images already in memory. From 69b5abd54d4833114d4e9ea17127c26760963e33 Mon Sep 17 00:00:00 2001 From: Radostin Stoyanov Date: Fri, 8 Feb 2019 22:01:33 +0000 Subject: [PATCH 051/249] remote: Introduce strflags() macro Reduce code duplication by introducing a strflags() macro which maps a flag to corresponding string. Signed-off-by: Radostin Stoyanov --- criu/img-remote.c | 25 ++++++++----------------- 1 file changed, 8 insertions(+), 17 deletions(-) diff --git a/criu/img-remote.c b/criu/img-remote.c index 12e0c7f98..045847a27 100644 --- a/criu/img-remote.c +++ b/criu/img-remote.c @@ -26,6 +26,9 @@ #define EPOLL_MAX_EVENTS 50 +#define strflags(f) ((f) == O_RDONLY ? "read" : \ + (f) == O_APPEND ? "append" : "write") + // List of images already in memory. LIST_HEAD(rimg_head); @@ -536,11 +539,8 @@ void forward_remote_image(struct roperation* rop) } pr_info("[fd=%d] Forwarding %s request for %s:%s (%" PRIu64 " bytes\n", - rop->fd, - rop->flags == O_RDONLY ? "read" : - rop->flags == O_APPEND ? "append" : "write", - rop->path, rop->snapshot_id, rop->size); - + rop->fd, strflags(rop->flags), rop->path, rop->snapshot_id, + rop->size); // Go back to non-blocking socket_set_non_blocking(rop->fd); @@ -577,10 +577,7 @@ void handle_remote_accept(int fd) socket_set_non_blocking(fd); pr_info("[fd=%d] Received %s request for %s:%s with %" PRIu64 " bytes\n", - fd, - flags == O_RDONLY ? "read" : - flags == O_APPEND ? "append" : "write", - path, snapshot_id, size); + fd, strflags(flags), path, snapshot_id, size); forwarding = true; @@ -617,10 +614,7 @@ void handle_local_accept(int fd) } pr_info("[fd=%d] Received %s request for %s:%s\n", - cli_fd, - flags == O_RDONLY ? "read" : - flags == O_APPEND ? "append" : "write", - path, snapshot_id); + cli_fd, strflags(flags), path, snapshot_id); // Write/Append case (only possible in img-proxy). if (flags != O_RDONLY) { @@ -706,10 +700,7 @@ void finish_cache_write(struct roperation* rop) if (prop != NULL) { pr_info("\t[fd=%d] Resuming pending %s for %s:%s\n", - prop->fd, - prop->flags == O_APPEND ? - "append" : prop->flags == O_RDONLY ? - "read" : "write", + prop->fd, strflags(prop->flags), prop->snapshot_id, prop->path); // Write header for pending image. From 197f5bc0be6e844cfd30415780d89617bd9fae0b Mon Sep 17 00:00:00 2001 From: Radostin Stoyanov Date: Fri, 8 Feb 2019 22:51:48 +0000 Subject: [PATCH 052/249] remote: Fix stringop-truncation warning Signed-off-by: Radostin Stoyanov --- criu/img-remote.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/criu/img-remote.c b/criu/img-remote.c index 045847a27..e6924f15f 100644 --- a/criu/img-remote.c +++ b/criu/img-remote.c @@ -298,8 +298,8 @@ int64_t read_remote_header(int fd, char *snapshot_id, char *path, int *flags, ui int ret = pb_read_obj(fd, (void **)&ri, PB_REMOTE_IMAGE); if (ret > 0) { - strncpy(snapshot_id, ri->snapshot_id, PATH_MAX); - strncpy(path, ri->name, PATH_MAX); + strncpy(snapshot_id, ri->snapshot_id, PATH_MAX - 1); + strncpy(path, ri->name, PATH_MAX - 1); *flags = ri->open_mode; *size = ri->size; } From dfbfa4fa92c3f3ee51e36582ac0207435b055925 Mon Sep 17 00:00:00 2001 From: Radostin Stoyanov Date: Fri, 8 Feb 2019 22:57:08 +0000 Subject: [PATCH 053/249] remote: Fix 'flags' maybe uninitialized warning Signed-off-by: Radostin Stoyanov --- criu/img-remote.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/criu/img-remote.c b/criu/img-remote.c index e6924f15f..f198ffaef 100644 --- a/criu/img-remote.c +++ b/criu/img-remote.c @@ -553,7 +553,7 @@ void handle_remote_accept(int fd) { char path[PATH_MAX]; char snapshot_id[PATH_MAX]; - int flags; + int flags = 0; uint64_t size = 0; int64_t ret; struct roperation* rop = NULL; From fb1c845624f1bef92015332e6ac88ccae59129c1 Mon Sep 17 00:00:00 2001 From: Radostin Stoyanov Date: Fri, 8 Feb 2019 23:43:05 +0000 Subject: [PATCH 054/249] remote: Make private functions static This patch makes various private variables and procedures static. These changes conform to the following code style conventions: When declaring pointer data or a function that returns a pointer type, the preferred use of ``*`` is adjacent to the data name or function name and not adjacent to the type name. Statements longer than 80 columns will be broken into sensible chunks, unless exceeding 80 columns significantly increases readability and does not hide information. Descendants are always substantially shorter than the parent and are placed substantially to the right. The same applies to function headers with a long argument list. from https://www.kernel.org/doc/Documentation/process/coding-style.rst The function declarations {send,recv}_image() from img-remote.h are removed because they do not have a corresponding implementation. The following functions are made static because they are not used outside img-remote.c: * {send,recv}_image_async() * {read,write}_remote_header() * socket_set_non_blocking() Signed-off-by: Radostin Stoyanov --- criu/img-remote.c | 104 ++++++++++++++++++++------------------ criu/include/img-remote.h | 9 ---- 2 files changed, 55 insertions(+), 58 deletions(-) diff --git a/criu/img-remote.c b/criu/img-remote.c index f198ffaef..090e1e775 100644 --- a/criu/img-remote.c +++ b/criu/img-remote.c @@ -30,21 +30,21 @@ (f) == O_APPEND ? "append" : "write") // List of images already in memory. -LIST_HEAD(rimg_head); +static LIST_HEAD(rimg_head); // List of local operations currently in-progress. -LIST_HEAD(rop_inprogress); +static LIST_HEAD(rop_inprogress); // List of local operations pending (reads on the restore side for images that // still haven't arrived). -LIST_HEAD(rop_pending); +static LIST_HEAD(rop_pending); // List of images waiting to be forwarded. The head of the list is currently // being forwarded. -LIST_HEAD(rop_forwarding); +static LIST_HEAD(rop_forwarding); // List of snapshots (useful when doing incremental restores/dumps) -LIST_HEAD(snapshot_head); +static LIST_HEAD(snapshot_head); // Snapshot id (setup at launch time by dump or restore). static char *snapshot_id; @@ -53,22 +53,24 @@ static char *snapshot_id; bool restoring = true; // True if the proxy to cache socket is being used (receiving or sending). -bool forwarding = false; +static bool forwarding = false; // True if the local dump or restore is finished. -bool finished_local = false; +static bool finished_local = false; // True if the communication between the proxy and cache can be closed. -bool finished_remote = false; +static bool finished_remote = false; // Proxy to cache socket fd; Local dump or restore servicing fd. int proxy_to_cache_fd; int local_req_fd; // Epoll fd and event array. -int epoll_fd; -struct epoll_event *events; +static int epoll_fd; +static struct epoll_event *events; +static int64_t recv_image_async(struct roperation *op); +static int64_t send_image_async(struct roperation *op); /* A snapshot is a dump or pre-dump operation. Each snapshot is identified by an * ID which corresponds to the working directory specified by the user. @@ -78,7 +80,7 @@ struct snapshot { struct list_head l; }; -struct snapshot *new_snapshot(char *snapshot_id) +static struct snapshot *new_snapshot(char *snapshot_id) { struct snapshot *s = xmalloc(sizeof(struct snapshot)); @@ -90,7 +92,7 @@ struct snapshot *new_snapshot(char *snapshot_id) return s; } -void add_snapshot(struct snapshot *snapshot) +static inline void add_snapshot(struct snapshot *snapshot) { list_add_tail(&(snapshot->l), &snapshot_head); } @@ -108,8 +110,8 @@ struct rimage *get_rimg_by_name(const char *snapshot_id, const char *path) return NULL; } -struct roperation *get_rop_by_name( - struct list_head *head, const char *snapshot_id, const char *path) +static inline struct roperation *get_rop_by_name(struct list_head *head, + const char *snapshot_id, const char *path) { struct roperation *rop = NULL; @@ -122,7 +124,7 @@ struct roperation *get_rop_by_name( return NULL; } -int event_set(int epoll_fd, int op, int fd, uint32_t events, void *data) +static int event_set(int epoll_fd, int op, int fd, uint32_t events, void *data) { int ret; struct epoll_event event; @@ -135,7 +137,7 @@ int event_set(int epoll_fd, int op, int fd, uint32_t events, void *data) return ret; } -void socket_set_non_blocking(int fd) +static inline void socket_set_non_blocking(int fd) { int flags = fcntl(fd, F_GETFL, NULL); @@ -149,7 +151,7 @@ void socket_set_non_blocking(int fd) pr_perror("Failed to set flags for fd %d", fd); } -void socket_set_blocking(int fd) +static inline void socket_set_blocking(int fd) { int flags = fcntl(fd, F_GETFL, NULL); @@ -218,7 +220,7 @@ static int setup_UNIX_client_socket(char *path) return sockfd; } -static int64_t pb_write_obj(int fd, void *obj, int type) +static inline int64_t pb_write_obj(int fd, void *obj, int type) { struct cr_img img; @@ -227,7 +229,7 @@ static int64_t pb_write_obj(int fd, void *obj, int type) return pb_write_one(&img, obj, type); } -static int64_t pb_read_obj(int fd, void **pobj, int type) +static inline int64_t pb_read_obj(int fd, void **pobj, int type) { struct cr_img img; @@ -236,7 +238,8 @@ static int64_t pb_read_obj(int fd, void **pobj, int type) return do_pb_read_one(&img, pobj, type, true); } -static int64_t write_header(int fd, char *snapshot_id, char *path, int flags) +static inline int64_t write_header(int fd, char *snapshot_id, char *path, + int flags) { LocalImageEntry li = LOCAL_IMAGE_ENTRY__INIT; @@ -246,7 +249,7 @@ static int64_t write_header(int fd, char *snapshot_id, char *path, int flags) return pb_write_obj(fd, &li, PB_LOCAL_IMAGE); } -static int64_t write_reply_header(int fd, int error) +static inline int64_t write_reply_header(int fd, int error) { LocalImageReplyEntry lir = LOCAL_IMAGE_REPLY_ENTRY__INIT; @@ -254,7 +257,8 @@ static int64_t write_reply_header(int fd, int error) return pb_write_obj(fd, &lir, PB_LOCAL_IMAGE_REPLY); } -int64_t write_remote_header(int fd, char *snapshot_id, char *path, int flags, uint64_t size) +static inline int64_t write_remote_header(int fd, char *snapshot_id, + char *path, int flags, uint64_t size) { RemoteImageEntry ri = REMOTE_IMAGE_ENTRY__INIT; @@ -265,7 +269,8 @@ int64_t write_remote_header(int fd, char *snapshot_id, char *path, int flags, ui return pb_write_obj(fd, &ri, PB_REMOTE_IMAGE); } -static int64_t read_header(int fd, char *snapshot_id, char *path, int *flags) +static inline int64_t read_header(int fd, char *snapshot_id, char *path, + int *flags) { LocalImageEntry *li; int ret = pb_read_obj(fd, (void **)&li, PB_LOCAL_IMAGE); @@ -281,7 +286,7 @@ static int64_t read_header(int fd, char *snapshot_id, char *path, int *flags) return ret; } -static int64_t read_reply_header(int fd, int *error) +static inline int64_t read_reply_header(int fd, int *error) { LocalImageReplyEntry *lir; int ret = pb_read_obj(fd, (void **)&lir, PB_LOCAL_IMAGE_REPLY); @@ -292,7 +297,8 @@ static int64_t read_reply_header(int fd, int *error) return ret; } -int64_t read_remote_header(int fd, char *snapshot_id, char *path, int *flags, uint64_t *size) +static inline int64_t read_remote_header(int fd, char *snapshot_id, char *path, + int *flags, uint64_t *size) { RemoteImageEntry *ri; int ret = pb_read_obj(fd, (void **)&ri, PB_REMOTE_IMAGE); @@ -332,8 +338,8 @@ static struct rimage *new_remote_image(char *path, char *snapshot_id) return NULL; } -static struct roperation *new_remote_operation( - char *path, char *snapshot_id, int cli_fd, int flags, bool close_fd) +static struct roperation *new_remote_operation(char *path, + char *snapshot_id, int cli_fd, int flags, bool close_fd) { struct roperation *rop = calloc(1, sizeof(struct roperation)); @@ -352,7 +358,7 @@ static struct roperation *new_remote_operation( return rop; } -static void rop_set_rimg(struct roperation* rop, struct rimage* rimg) +static inline void rop_set_rimg(struct roperation *rop, struct rimage *rimg) { rop->rimg = rimg; rop->size = rimg->size; @@ -373,12 +379,11 @@ static void rop_set_rimg(struct roperation* rop, struct rimage* rimg) rop->curr_recv_buf = list_entry(rimg->buf_head.next, struct rbuf, l); rop->curr_sent_buf = list_entry(rimg->buf_head.next, struct rbuf, l); rop->curr_sent_bytes = 0; - } } /* Clears a remote image struct for reusing it. */ -static struct rimage *clear_remote_image(struct rimage *rimg) +static inline struct rimage *clear_remote_image(struct rimage *rimg) { while (!list_is_singular(&(rimg->buf_head))) { struct rbuf *buf = list_entry(rimg->buf_head.prev, struct rbuf, l); @@ -393,8 +398,8 @@ static struct rimage *clear_remote_image(struct rimage *rimg) return rimg; } -struct roperation* handle_accept_write( - int cli_fd, char* snapshot_id, char* path, int flags, bool close_fd, uint64_t size) +static struct roperation *handle_accept_write(int cli_fd, char *snapshot_id, + char *path, int flags, bool close_fd, uint64_t size) { struct roperation *rop = NULL; struct rimage *rimg = get_rimg_by_name(snapshot_id, path); @@ -426,14 +431,14 @@ struct roperation* handle_accept_write( return NULL; } -struct roperation* handle_accept_proxy_write( - int cli_fd, char* snapshot_id, char* path, int flags) +static inline struct roperation *handle_accept_proxy_write(int cli_fd, + char *snapshot_id, char *path, int flags) { return handle_accept_write(cli_fd, snapshot_id, path, flags, true, 0); } -struct roperation* handle_accept_proxy_read( - int cli_fd, char* snapshot_id, char* path, int flags) +static struct roperation *handle_accept_proxy_read(int cli_fd, + char *snapshot_id, char *path, int flags) { struct roperation *rop = NULL; struct rimage *rimg = NULL; @@ -470,7 +475,7 @@ struct roperation* handle_accept_proxy_read( return NULL; } -void finish_local() +static inline void finish_local() { int ret; finished_local = true; @@ -480,8 +485,8 @@ void finish_local() } } -struct roperation* handle_accept_cache_read( - int cli_fd, char* snapshot_id, char* path, int flags) +static struct roperation *handle_accept_cache_read(int cli_fd, + char *snapshot_id, char *path, int flags) { struct rimage *rimg = NULL; struct roperation *rop = NULL; @@ -522,7 +527,7 @@ struct roperation* handle_accept_cache_read( return NULL; } -void forward_remote_image(struct roperation* rop) +static void forward_remote_image(struct roperation *rop) { int64_t ret = 0; @@ -549,7 +554,7 @@ void forward_remote_image(struct roperation* rop) event_set(epoll_fd, EPOLL_CTL_ADD, rop->fd, EPOLLOUT, rop); } -void handle_remote_accept(int fd) +static void handle_remote_accept(int fd) { char path[PATH_MAX]; char snapshot_id[PATH_MAX]; @@ -592,7 +597,7 @@ void handle_remote_accept(int fd) close(fd); } -void handle_local_accept(int fd) +static void handle_local_accept(int fd) { int cli_fd; char path[PATH_MAX]; @@ -648,7 +653,7 @@ void handle_local_accept(int fd) close(cli_fd); } -void finish_proxy_read(struct roperation* rop) +static inline void finish_proxy_read(struct roperation *rop) { // If finished forwarding image if (rop->fd == proxy_to_cache_fd) { @@ -665,7 +670,7 @@ void finish_proxy_read(struct roperation* rop) } } -void finish_proxy_write(struct roperation* rop) +static inline void finish_proxy_write(struct roperation *rop) { // No more local images are comming. Close local socket. if (!strncmp(rop->path, DUMP_FINISH, sizeof(DUMP_FINISH))) { @@ -687,7 +692,7 @@ void finish_proxy_write(struct roperation* rop) } } -void finish_cache_write(struct roperation* rop) +static void finish_cache_write(struct roperation *rop) { struct roperation *prop = get_rop_by_name( &rop_pending, rop->snapshot_id, rop->path); @@ -719,7 +724,8 @@ void finish_cache_write(struct roperation* rop) } } -void handle_roperation(struct epoll_event *event, struct roperation *rop) +static void handle_roperation(struct epoll_event *event, + struct roperation *rop) { int64_t ret = (EPOLLOUT & event->events) ? send_image_async(rop) : @@ -770,7 +776,7 @@ void handle_roperation(struct epoll_event *event, struct roperation *rop) free(rop); } -void check_pending() +static void check_pending() { struct roperation *rop = NULL; struct rimage *rimg = NULL; @@ -881,7 +887,7 @@ void accept_image_connections() { /* Note: size is a limit on how much we want to read from the socket. Zero means * read until the socket is closed. */ -int64_t recv_image_async(struct roperation *op) +static int64_t recv_image_async(struct roperation *op) { int fd = op->fd; struct rimage *rimg = op->rimg; @@ -932,7 +938,7 @@ int64_t recv_image_async(struct roperation *op) return n; } -int64_t send_image_async(struct roperation *op) +static int64_t send_image_async(struct roperation *op) { int fd = op->fd; struct rimage *rimg = op->rimg; diff --git a/criu/include/img-remote.h b/criu/include/img-remote.h index bb70e81e5..38a03deab 100644 --- a/criu/include/img-remote.h +++ b/criu/include/img-remote.h @@ -76,16 +76,7 @@ extern bool restoring; void accept_image_connections(); struct rimage *get_rimg_by_name(const char *snapshot_id, const char *path); -int64_t send_image(int fd, struct rimage *rimg, int flags, bool image_check); -int64_t send_image_async(struct roperation *op); -int64_t recv_image(int fd, struct rimage *rimg, uint64_t size, int flags, bool image_check); -int64_t recv_image_async(struct roperation *op); - -int64_t read_remote_header(int fd, char *snapshot_id, char *path, int *open_mode, uint64_t *size); -int64_t write_remote_header(int fd, char *snapshot_id, char *path, int open_mode, uint64_t size); - int setup_UNIX_server_socket(char *path); -void socket_set_non_blocking(int fd); /* Called by restore to get the fd correspondent to a particular path. This call * will block until the connection is received. From a899158f1fec30aca290df93e743db2d47935c9b Mon Sep 17 00:00:00 2001 From: Radostin Stoyanov Date: Sat, 9 Feb 2019 00:35:39 +0000 Subject: [PATCH 055/249] remote: Use xmalloc/xzalloc/xfree There is no need to print an error message, __xalloc() does that. Signed-off-by: Radostin Stoyanov --- criu/img-remote.c | 36 ++++++++++++++++-------------------- 1 file changed, 16 insertions(+), 20 deletions(-) diff --git a/criu/img-remote.c b/criu/img-remote.c index 090e1e775..74c5261bf 100644 --- a/criu/img-remote.c +++ b/criu/img-remote.c @@ -315,13 +315,11 @@ static inline int64_t read_remote_header(int fd, char *snapshot_id, char *path, static struct rimage *new_remote_image(char *path, char *snapshot_id) { - struct rimage *rimg = calloc(1, sizeof(struct rimage)); - struct rbuf *buf = calloc(1, sizeof(struct rbuf)); + struct rimage *rimg = xzalloc(sizeof(struct rimage)); + struct rbuf *buf = xzalloc(sizeof(struct rbuf)); - if (rimg == NULL || buf == NULL) { - pr_perror("Unable to allocate remote image structures"); + if (rimg == NULL || buf == NULL) goto err; - } strncpy(rimg->path, path, PATH_MAX -1 ); strncpy(rimg->snapshot_id, snapshot_id, PATH_MAX - 1); @@ -333,20 +331,19 @@ static struct rimage *new_remote_image(char *path, char *snapshot_id) return rimg; err: - free(rimg); - free(buf); + xfree(rimg); + xfree(buf); return NULL; } static struct roperation *new_remote_operation(char *path, char *snapshot_id, int cli_fd, int flags, bool close_fd) { - struct roperation *rop = calloc(1, sizeof(struct roperation)); + struct roperation *rop = xzalloc(sizeof(struct roperation)); - if (rop == NULL) { - pr_perror("Unable to allocate remote operation structures"); + if (rop == NULL) return NULL; - } + strncpy(rop->path, path, PATH_MAX -1 ); strncpy(rop->snapshot_id, snapshot_id, PATH_MAX - 1); rop->path[PATH_MAX - 1] = '\0'; @@ -389,7 +386,7 @@ static inline struct rimage *clear_remote_image(struct rimage *rimg) struct rbuf *buf = list_entry(rimg->buf_head.prev, struct rbuf, l); list_del(rimg->buf_head.prev); - free(buf); + xfree(buf); } list_entry(rimg->buf_head.next, struct rbuf, l)->nbytes = 0; @@ -426,8 +423,8 @@ static struct roperation *handle_accept_write(int cli_fd, char *snapshot_id, rop->size = size; return rop; err: - free(rimg); - free(rop); + xfree(rimg); + xfree(rop); return NULL; } @@ -512,7 +509,7 @@ static struct roperation *handle_accept_cache_read(int cli_fd, pr_perror("Error writing reply header for %s:%s", path, snapshot_id); close(rop->fd); - free(rop); + xfree(rop); } rop_set_rimg(rop, rimg); return rop; @@ -522,7 +519,7 @@ static struct roperation *handle_accept_cache_read(int cli_fd, if (write_reply_header(cli_fd, ENOENT) < 0) pr_perror("Error writing reply header for unexisting image"); close(cli_fd); - free(rop); + xfree(rop); } return NULL; } @@ -713,7 +710,7 @@ static void finish_cache_write(struct roperation *rop) pr_perror("Error writing reply header for %s:%s", prop->path, prop->snapshot_id); close(prop->fd); - free(prop); + xfree(prop); return; } @@ -773,7 +770,7 @@ static void handle_roperation(struct epoll_event *event, // Nothing to be done when a read is finished on the cache side. } err: - free(rop); + xfree(rop); } static void check_pending() @@ -909,9 +906,8 @@ static int64_t recv_image_async(struct roperation *op) curr_buf->nbytes += n; rimg->size += n; if (curr_buf->nbytes == BUF_SIZE) { - struct rbuf *buf = malloc(sizeof(struct rbuf)); + struct rbuf *buf = xmalloc(sizeof(struct rbuf)); if (buf == NULL) { - pr_perror("Unable to allocate remote_buffer structures"); if (close_fd) close(fd); return -1; From aa1627102ec3a365b4e021da67d67dd5595c68f7 Mon Sep 17 00:00:00 2001 From: Radostin Stoyanov Date: Mon, 11 Feb 2019 08:39:05 +0000 Subject: [PATCH 056/249] remote: Ignore interupt signals When an interrupt signal (even SIGWINCH when strace is used) is received while epoll_wait() sleeps, it will return a value of -1 and set errno to EINTR, which is not an error and should be ignored. Signed-off-by: Radostin Stoyanov --- criu/img-remote.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/criu/img-remote.c b/criu/img-remote.c index 74c5261bf..13445b983 100644 --- a/criu/img-remote.c +++ b/criu/img-remote.c @@ -828,7 +828,9 @@ void accept_image_connections() { int n_events, i; n_events = epoll_wait(epoll_fd, events, EPOLL_MAX_EVENTS, 250); - if (n_events < 0) { + + /* epoll_wait isn't restarted after interrupted by a signal */ + if (n_events < 0 && errno != EINTR) { pr_perror("Failed to epoll wait"); goto end; } From 7187c12414d6a09682c9226482a53a412fde6e2c Mon Sep 17 00:00:00 2001 From: Radostin Stoyanov Date: Wed, 13 Feb 2019 13:03:58 +0000 Subject: [PATCH 057/249] util: Don't pass address/port as arguments There is no need to pass the values for address and port as arguments when creating a TCP server. The external `opts` object, which provides opts.addr and opts.port, is accessible in all components that require these values. With this change, a value specified with the `--address` option will used by image-cache in the same way as with page-server. Example: criu image-cache --address 127.0.0.1 --port 1234 criu page-server --address 127.0.0.1 --port 1234 Signed-off-by: Radostin Stoyanov --- criu/crtools.c | 4 ++-- criu/img-cache.c | 8 ++++---- criu/img-proxy.c | 8 ++++---- criu/include/img-remote.h | 4 ++-- criu/include/util.h | 4 ++-- criu/page-xfer.c | 4 ++-- criu/util.c | 35 +++++++++++++++++------------------ 7 files changed, 33 insertions(+), 34 deletions(-) diff --git a/criu/crtools.c b/criu/crtools.c index b0cba4708..fbc707b04 100644 --- a/criu/crtools.c +++ b/criu/crtools.c @@ -230,7 +230,7 @@ int main(int argc, char *argv[], char *envp[]) if (!strcmp(argv[optind], "image-cache")) { if (!opts.port) goto opt_port_missing; - return image_cache(opts.daemon_mode, DEFAULT_CACHE_SOCKET, opts.port); + return image_cache(opts.daemon_mode, DEFAULT_CACHE_SOCKET); } if (!strcmp(argv[optind], "image-proxy")) { @@ -240,7 +240,7 @@ int main(int argc, char *argv[], char *envp[]) } if (!opts.port) goto opt_port_missing; - return image_proxy(opts.daemon_mode, DEFAULT_PROXY_SOCKET, opts.addr, opts.port); + return image_proxy(opts.daemon_mode, DEFAULT_PROXY_SOCKET); } if (!strcmp(argv[optind], "service")) diff --git a/criu/img-cache.c b/criu/img-cache.c index 53da022ac..4c1e6ebe7 100644 --- a/criu/img-cache.c +++ b/criu/img-cache.c @@ -9,19 +9,19 @@ #include "cr_options.h" #include "util.h" -int image_cache(bool background, char *local_cache_path, unsigned short cache_write_port) +int image_cache(bool background, char *local_cache_path) { int tmp; - pr_info("Proxy to Cache Port %d, CRIU to Cache Path %s\n", - cache_write_port, local_cache_path); + pr_info("Proxy to Cache Port %u, CRIU to Cache Path %s\n", + opts.port, local_cache_path); restoring = true; if (opts.ps_socket != -1) { proxy_to_cache_fd = opts.ps_socket; pr_info("Re-using ps socket %d\n", proxy_to_cache_fd); } else { - proxy_to_cache_fd = setup_tcp_server("image cache", NULL, &cache_write_port); + proxy_to_cache_fd = setup_tcp_server("image cache"); if (proxy_to_cache_fd < 0) { pr_perror("Unable to open proxy to cache TCP socket"); return -1; diff --git a/criu/img-proxy.c b/criu/img-proxy.c index 73a726305..b11ab83e0 100644 --- a/criu/img-proxy.c +++ b/criu/img-proxy.c @@ -8,10 +8,10 @@ #include "cr_options.h" #include "util.h" -int image_proxy(bool background, char *local_proxy_path, char *fwd_host, unsigned short fwd_port) +int image_proxy(bool background, char *local_proxy_path) { - pr_info("CRIU to Proxy Path: %s, Cache Address %s:%hu\n", - local_proxy_path, fwd_host, fwd_port); + pr_info("CRIU to Proxy Path: %s, Cache Address %s:%u\n", + local_proxy_path, opts.addr, opts.port); restoring = false; local_req_fd = setup_UNIX_server_socket(local_proxy_path); @@ -24,7 +24,7 @@ int image_proxy(bool background, char *local_proxy_path, char *fwd_host, unsigne proxy_to_cache_fd = opts.ps_socket; pr_info("Re-using ps socket %d\n", proxy_to_cache_fd); } else { - proxy_to_cache_fd = setup_tcp_client(fwd_host, fwd_port); + proxy_to_cache_fd = setup_tcp_client(); if (proxy_to_cache_fd < 0) { pr_perror("Unable to open proxy to cache TCP socket"); close(local_req_fd); diff --git a/criu/include/img-remote.h b/criu/include/img-remote.h index 38a03deab..e853d3552 100644 --- a/criu/include/img-remote.h +++ b/criu/include/img-remote.h @@ -98,13 +98,13 @@ int finish_remote_restore(); /* Starts an image proxy daemon (dump side). It receives image files through * socket connections and forwards them to the image cache (restore side). */ -int image_proxy(bool background, char *local_proxy_path, char *cache_host, unsigned short cache_port); +int image_proxy(bool background, char *local_proxy_path); /* Starts an image cache daemon (restore side). It receives image files through * socket connections and caches them until they are requested by the restore * process. */ -int image_cache(bool background, char *local_cache_path, unsigned short cache_port); +int image_cache(bool background, char *local_cache_path); /* Reads (discards) 'len' bytes from fd. This is used to emulate the function * lseek, which is used to advance the file needle. diff --git a/criu/include/util.h b/criu/include/util.h index 644b348c9..1f77629bb 100644 --- a/criu/include/util.h +++ b/criu/include/util.h @@ -292,9 +292,9 @@ char *xsprintf(const char *fmt, ...) void print_data(unsigned long addr, unsigned char *data, size_t size); -int setup_tcp_server(char *type, char *addr, unsigned short *port); +int setup_tcp_server(char *type); int run_tcp_server(bool daemon_mode, int *ask, int cfd, int sk); -int setup_tcp_client(char *hostname, unsigned short port); +int setup_tcp_client(void); #define LAST_PID_PATH "sys/kernel/ns_last_pid" #define PID_MAX_PATH "sys/kernel/pid_max" diff --git a/criu/page-xfer.c b/criu/page-xfer.c index a7b5c70f1..e0ee58c0f 100644 --- a/criu/page-xfer.c +++ b/criu/page-xfer.c @@ -1013,7 +1013,7 @@ int cr_page_server(bool daemon_mode, bool lazy_dump, int cfd) goto no_server; } - sk = setup_tcp_server("page", opts.addr, &opts.port); + sk = setup_tcp_server("page"); if (sk == -1) return -1; no_server: @@ -1054,7 +1054,7 @@ static int connect_to_page_server(void) goto out; } - page_server_sk = setup_tcp_client(opts.addr, opts.port); + page_server_sk = setup_tcp_client(); if (page_server_sk == -1) return -1; out: diff --git a/criu/util.c b/criu/util.c index 9523fc115..5cc57667b 100644 --- a/criu/util.c +++ b/criu/util.c @@ -1075,8 +1075,7 @@ void print_data(unsigned long addr, unsigned char *data, size_t size) } } -static int get_sockaddr_in(struct sockaddr_storage *addr, char *host, - unsigned short port) +static int get_sockaddr_in(struct sockaddr_storage *addr, char *host) { memset(addr, 0, sizeof(*addr)); @@ -1094,26 +1093,26 @@ static int get_sockaddr_in(struct sockaddr_storage *addr, char *host, } if (addr->ss_family == AF_INET6) { - ((struct sockaddr_in6 *)addr)->sin6_port = htons(port); + ((struct sockaddr_in6 *)addr)->sin6_port = htons(opts.port); } else if (addr->ss_family == AF_INET) { - ((struct sockaddr_in *)addr)->sin_port = htons(port); + ((struct sockaddr_in *)addr)->sin_port = htons(opts.port); } return 0; } -int setup_tcp_server(char *type, char *addr, unsigned short *port) +int setup_tcp_server(char *type) { int sk = -1; int sockopt = 1; struct sockaddr_storage saddr; socklen_t slen = sizeof(saddr); - if (get_sockaddr_in(&saddr, addr, (*port))) { + if (get_sockaddr_in(&saddr, opts.addr)) { return -1; } - pr_info("Starting %s server on port %u\n", type, *port); + pr_info("Starting %s server on port %u\n", type, opts.port); sk = socket(saddr.ss_family, SOCK_STREAM, IPPROTO_TCP); @@ -1139,19 +1138,19 @@ int setup_tcp_server(char *type, char *addr, unsigned short *port) } /* Get socket port in case of autobind */ - if ((*port) == 0) { + if (opts.port == 0) { if (getsockname(sk, (struct sockaddr *)&saddr, &slen)) { pr_perror("Can't get %s server name", type); goto out; } if (saddr.ss_family == AF_INET6) { - (*port) = ntohs(((struct sockaddr_in *)&saddr)->sin_port); + opts.port = ntohs(((struct sockaddr_in *)&saddr)->sin_port); } else if (saddr.ss_family == AF_INET) { - (*port) = ntohs(((struct sockaddr_in6 *)&saddr)->sin6_port); + opts.port = ntohs(((struct sockaddr_in6 *)&saddr)->sin6_port); } - pr_info("Using %u port\n", (*port)); + pr_info("Using %u port\n", opts.port); } return sk; @@ -1207,7 +1206,7 @@ int run_tcp_server(bool daemon_mode, int *ask, int cfd, int sk) return -1; } -int setup_tcp_client(char *hostname, unsigned short port) +int setup_tcp_client(void) { struct sockaddr_storage saddr; struct addrinfo addr_criteria, *addr_list, *p; @@ -1222,10 +1221,10 @@ int setup_tcp_client(char *hostname, unsigned short port) /* * addr_list contains a list of addrinfo structures that corresponding - * to the criteria specified in hostname and addr_criteria. + * to the criteria specified in opts.addr and addr_criteria. */ - if (getaddrinfo(hostname, NULL, &addr_criteria, &addr_list)) { - pr_perror("Failed to resolve hostname: %s", hostname); + if (getaddrinfo(opts.addr, NULL, &addr_criteria, &addr_list)) { + pr_perror("Failed to resolve hostname: %s", opts.addr); goto out; } @@ -1244,9 +1243,9 @@ int setup_tcp_client(char *hostname, unsigned short port) } inet_ntop(p->ai_family, ip, ipstr, sizeof(ipstr)); - pr_info("Connecting to server %s:%u\n", ipstr, port); + pr_info("Connecting to server %s:%u\n", ipstr, opts.port); - if (get_sockaddr_in(&saddr, ipstr, port)) + if (get_sockaddr_in(&saddr, ipstr)) goto out; sk = socket(saddr.ss_family, SOCK_STREAM, IPPROTO_TCP); @@ -1256,7 +1255,7 @@ int setup_tcp_client(char *hostname, unsigned short port) } if (connect(sk, (struct sockaddr *)&saddr, sizeof(saddr)) < 0) { - pr_info("Can't connect to server %s:%u\n", ipstr, port); + pr_info("Can't connect to server %s:%u\n", ipstr, opts.port); close(sk); sk = -1; } else { From 909b924e7f0cf97e099031efe1a2060af0d0b9d4 Mon Sep 17 00:00:00 2001 From: Radostin Stoyanov Date: Sun, 24 Feb 2019 22:49:13 +0000 Subject: [PATCH 058/249] remote: Rename 'proxy_to_cache_fd' to 'remote_sk' The variable name 'remote_sk' is shorter than 'proxy_to_cache_fd' and it is more similar to 'page_server_sk' (used in criu/page-xfer.c). Signed-off-by: Radostin Stoyanov --- criu/img-cache.c | 18 +++++++++--------- criu/img-proxy.c | 12 ++++++------ criu/img-remote.c | 22 +++++++++++----------- criu/include/img-remote.h | 2 +- 4 files changed, 27 insertions(+), 27 deletions(-) diff --git a/criu/img-cache.c b/criu/img-cache.c index 4c1e6ebe7..5603309b1 100644 --- a/criu/img-cache.c +++ b/criu/img-cache.c @@ -18,31 +18,31 @@ int image_cache(bool background, char *local_cache_path) restoring = true; if (opts.ps_socket != -1) { - proxy_to_cache_fd = opts.ps_socket; - pr_info("Re-using ps socket %d\n", proxy_to_cache_fd); + remote_sk = opts.ps_socket; + pr_info("Re-using ps socket %d\n", remote_sk); } else { - proxy_to_cache_fd = setup_tcp_server("image cache"); - if (proxy_to_cache_fd < 0) { + remote_sk = setup_tcp_server("image cache"); + if (remote_sk < 0) { pr_perror("Unable to open proxy to cache TCP socket"); return -1; } // Wait to accept connection from proxy. - tmp = accept(proxy_to_cache_fd, NULL, 0); + tmp = accept(remote_sk, NULL, 0); if (tmp < 0) { pr_perror("Unable to accept remote image connection" " from image proxy"); - close(proxy_to_cache_fd); + close(remote_sk); return -1; } - proxy_to_cache_fd = tmp; + remote_sk = tmp; } - pr_info("Cache is connected to Proxy through fd %d\n", proxy_to_cache_fd); + pr_info("Cache is connected to Proxy through fd %d\n", remote_sk); local_req_fd = setup_UNIX_server_socket(local_cache_path); if (local_req_fd < 0) { pr_perror("Unable to open cache to proxy UNIX socket"); - close(proxy_to_cache_fd); + close(remote_sk); return -1; } diff --git a/criu/img-proxy.c b/criu/img-proxy.c index b11ab83e0..d6b92148e 100644 --- a/criu/img-proxy.c +++ b/criu/img-proxy.c @@ -21,18 +21,18 @@ int image_proxy(bool background, char *local_proxy_path) } if (opts.ps_socket != -1) { - proxy_to_cache_fd = opts.ps_socket; - pr_info("Re-using ps socket %d\n", proxy_to_cache_fd); + remote_sk = opts.ps_socket; + pr_info("Re-using ps socket %d\n", remote_sk); } else { - proxy_to_cache_fd = setup_tcp_client(); - if (proxy_to_cache_fd < 0) { + remote_sk = setup_tcp_client(); + if (remote_sk < 0) { pr_perror("Unable to open proxy to cache TCP socket"); close(local_req_fd); return -1; } } - pr_info("Proxy is connected to Cache through fd %d\n", proxy_to_cache_fd); + pr_info("Proxy is connected to Cache through fd %d\n", remote_sk); if (background) { if (daemon(1, 0) == -1) { @@ -41,7 +41,7 @@ int image_proxy(bool background, char *local_proxy_path) } } - // TODO - local_req_fd and proxy_to_cache_fd send as args. + // TODO - local_req_fd and remote_sk send as args. accept_image_connections(); pr_info("Finished image proxy."); return 0; diff --git a/criu/img-remote.c b/criu/img-remote.c index 13445b983..a3813f4a8 100644 --- a/criu/img-remote.c +++ b/criu/img-remote.c @@ -62,7 +62,7 @@ static bool finished_local = false; static bool finished_remote = false; // Proxy to cache socket fd; Local dump or restore servicing fd. -int proxy_to_cache_fd; +int remote_sk; int local_req_fd; // Epoll fd and event array. @@ -361,7 +361,7 @@ static inline void rop_set_rimg(struct roperation *rop, struct rimage *rimg) rop->size = rimg->size; if (rop->flags == O_APPEND) { // Image forward on append must start where the last fwd finished. - if (rop->fd == proxy_to_cache_fd) { + if (rop->fd == remote_sk) { rop->curr_sent_buf = rimg->curr_fwd_buf; rop->curr_sent_bytes = rimg->curr_fwd_bytes; } else { @@ -653,7 +653,7 @@ static void handle_local_accept(int fd) static inline void finish_proxy_read(struct roperation *rop) { // If finished forwarding image - if (rop->fd == proxy_to_cache_fd) { + if (rop->fd == remote_sk) { // Update fwd buffer and byte count on rimg. rop->rimg->curr_fwd_buf = rop->curr_sent_buf; rop->rimg->curr_fwd_bytes = rop->curr_sent_bytes; @@ -676,7 +676,7 @@ static inline void finish_proxy_write(struct roperation *rop) } else { // Normal image received, forward it. struct roperation *rop_to_forward = new_remote_operation( - rop->path, rop->snapshot_id, proxy_to_cache_fd, rop->flags, false); + rop->path, rop->snapshot_id, remote_sk, rop->flags, false); // Add image to list of images. list_add_tail(&(rop->rimg->l), &rimg_head); @@ -695,7 +695,7 @@ static void finish_cache_write(struct roperation *rop) &rop_pending, rop->snapshot_id, rop->path); forwarding = false; - event_set(epoll_fd, EPOLL_CTL_ADD, proxy_to_cache_fd, EPOLLIN, &proxy_to_cache_fd); + event_set(epoll_fd, EPOLL_CTL_ADD, remote_sk, EPOLLIN, &remote_sk); // Add image to list of images. list_add_tail(&(rop->rimg->l), &rimg_head); @@ -816,8 +816,8 @@ void accept_image_connections() { // Only if we are restoring (cache-side) we need to add the remote sock to // the epoll. if (restoring) { - ret = event_set(epoll_fd, EPOLL_CTL_ADD, proxy_to_cache_fd, - EPOLLIN, &proxy_to_cache_fd); + ret = event_set(epoll_fd, EPOLL_CTL_ADD, remote_sk, + EPOLLIN, &remote_sk); if (ret) { pr_perror("Failed to add proxy to cache fd to epoll"); goto end; @@ -845,9 +845,9 @@ void accept_image_connections() { goto end; } handle_local_accept(local_req_fd); - } else if (restoring && !forwarding && events[i].data.ptr == &proxy_to_cache_fd) { - event_set(epoll_fd, EPOLL_CTL_DEL, proxy_to_cache_fd, 0, 0); - handle_remote_accept(proxy_to_cache_fd); + } else if (restoring && !forwarding && events[i].data.ptr == &remote_sk) { + event_set(epoll_fd, EPOLL_CTL_DEL, remote_sk, 0, 0); + handle_remote_accept(remote_sk); } else { struct roperation *rop = (struct roperation*)events[i].data.ptr; @@ -866,7 +866,7 @@ void accept_image_connections() { finished_local && !finished_remote && list_empty(&rop_forwarding)) { - close(proxy_to_cache_fd); + close(remote_sk); finished_remote = true; } diff --git a/criu/include/img-remote.h b/criu/include/img-remote.h index e853d3552..a0cb22791 100644 --- a/criu/include/img-remote.h +++ b/criu/include/img-remote.h @@ -67,7 +67,7 @@ struct roperation { }; /* This is the proxy to cache TCP socket FD. */ -extern int proxy_to_cache_fd; +extern int remote_sk; /* This the unix socket used to fulfill local requests. */ extern int local_req_fd; /* True if we are running the cache/restore, false if proxy/dump. */ From 07388000b9af9605a531cfc4e16dba64227d2ec3 Mon Sep 17 00:00:00 2001 From: Radostin Stoyanov Date: Sun, 24 Feb 2019 22:51:15 +0000 Subject: [PATCH 059/249] remote: Rename 'local_req_fd' to 'local_sk' The name 'local_sk' is shorter than 'local_req_fd', and it is more similar to the name 'page_server_sk' used in criu/page-xfer.c Signed-off-by: Radostin Stoyanov --- criu/img-cache.c | 4 ++-- criu/img-proxy.c | 8 ++++---- criu/img-remote.c | 12 ++++++------ criu/include/img-remote.h | 2 +- 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/criu/img-cache.c b/criu/img-cache.c index 5603309b1..f28ba1c3c 100644 --- a/criu/img-cache.c +++ b/criu/img-cache.c @@ -39,8 +39,8 @@ int image_cache(bool background, char *local_cache_path) pr_info("Cache is connected to Proxy through fd %d\n", remote_sk); - local_req_fd = setup_UNIX_server_socket(local_cache_path); - if (local_req_fd < 0) { + local_sk = setup_UNIX_server_socket(local_cache_path); + if (local_sk < 0) { pr_perror("Unable to open cache to proxy UNIX socket"); close(remote_sk); return -1; diff --git a/criu/img-proxy.c b/criu/img-proxy.c index d6b92148e..09fa6dc04 100644 --- a/criu/img-proxy.c +++ b/criu/img-proxy.c @@ -14,8 +14,8 @@ int image_proxy(bool background, char *local_proxy_path) local_proxy_path, opts.addr, opts.port); restoring = false; - local_req_fd = setup_UNIX_server_socket(local_proxy_path); - if (local_req_fd < 0) { + local_sk = setup_UNIX_server_socket(local_proxy_path); + if (local_sk < 0) { pr_perror("Unable to open CRIU to proxy UNIX socket"); return -1; } @@ -27,7 +27,7 @@ int image_proxy(bool background, char *local_proxy_path) remote_sk = setup_tcp_client(); if (remote_sk < 0) { pr_perror("Unable to open proxy to cache TCP socket"); - close(local_req_fd); + close(local_sk); return -1; } } @@ -41,7 +41,7 @@ int image_proxy(bool background, char *local_proxy_path) } } - // TODO - local_req_fd and remote_sk send as args. + // TODO - local_sk and remote_sk send as args. accept_image_connections(); pr_info("Finished image proxy."); return 0; diff --git a/criu/img-remote.c b/criu/img-remote.c index a3813f4a8..38028ca55 100644 --- a/criu/img-remote.c +++ b/criu/img-remote.c @@ -63,7 +63,7 @@ static bool finished_remote = false; // Proxy to cache socket fd; Local dump or restore servicing fd. int remote_sk; -int local_req_fd; +int local_sk; // Epoll fd and event array. static int epoll_fd; @@ -476,7 +476,7 @@ static inline void finish_local() { int ret; finished_local = true; - ret = event_set(epoll_fd, EPOLL_CTL_DEL, local_req_fd, 0, 0); + ret = event_set(epoll_fd, EPOLL_CTL_DEL, local_sk, 0, 0); if (ret) { pr_perror("Failed to del local fd from epoll"); } @@ -807,7 +807,7 @@ void accept_image_connections() { goto end; } - ret = event_set(epoll_fd, EPOLL_CTL_ADD, local_req_fd, EPOLLIN, &local_req_fd); + ret = event_set(epoll_fd, EPOLL_CTL_ADD, local_sk, EPOLLIN, &local_sk); if (ret) { pr_perror("Failed to add local fd to epoll"); goto end; @@ -837,14 +837,14 @@ void accept_image_connections() { for (i = 0; i < n_events; i++) { // Accept from local dump/restore? - if (events[i].data.ptr == &local_req_fd) { + if (events[i].data.ptr == &local_sk) { if (events[i].events & EPOLLHUP || events[i].events & EPOLLERR) { if (!finished_local) pr_perror("Unable to accept more local image connections"); goto end; } - handle_local_accept(local_req_fd); + handle_local_accept(local_sk); } else if (restoring && !forwarding && events[i].data.ptr == &remote_sk) { event_set(epoll_fd, EPOLL_CTL_DEL, remote_sk, 0, 0); handle_remote_accept(remote_sk); @@ -878,7 +878,7 @@ void accept_image_connections() { } end: close(epoll_fd); - close(local_req_fd); + close(local_sk); free(events); } diff --git a/criu/include/img-remote.h b/criu/include/img-remote.h index a0cb22791..087c395bf 100644 --- a/criu/include/img-remote.h +++ b/criu/include/img-remote.h @@ -69,7 +69,7 @@ struct roperation { /* This is the proxy to cache TCP socket FD. */ extern int remote_sk; /* This the unix socket used to fulfill local requests. */ -extern int local_req_fd; +extern int local_sk; /* True if we are running the cache/restore, false if proxy/dump. */ extern bool restoring; From 8f2752f82509c1d0b516e07ee73b6de555134475 Mon Sep 17 00:00:00 2001 From: Radostin Stoyanov Date: Fri, 22 Feb 2019 18:04:32 +0000 Subject: [PATCH 060/249] util: Fix addr casting for IPv4/IPv6 in autobind When saddr.ss_family is AF_INET6 we should cast &saddr to (struct sockaddr_in6 *). Signed-off-by: Radostin Stoyanov --- criu/util.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/criu/util.c b/criu/util.c index 5cc57667b..95aecee91 100644 --- a/criu/util.c +++ b/criu/util.c @@ -1145,9 +1145,9 @@ int setup_tcp_server(char *type) } if (saddr.ss_family == AF_INET6) { - opts.port = ntohs(((struct sockaddr_in *)&saddr)->sin_port); - } else if (saddr.ss_family == AF_INET) { opts.port = ntohs(((struct sockaddr_in6 *)&saddr)->sin6_port); + } else if (saddr.ss_family == AF_INET) { + opts.port = ntohs(((struct sockaddr_in *)&saddr)->sin_port); } pr_info("Using %u port\n", opts.port); From 79a4c67d98167a866b87d8ba62236d38530c0e1d Mon Sep 17 00:00:00 2001 From: Radostin Stoyanov Date: Sun, 24 Feb 2019 23:00:26 +0000 Subject: [PATCH 061/249] util: Introduce fd_set_nonblocking() Combine the functionality of socket_set_non_blocking() and socket_set_blocking() into a new function, and move it in criu/util.c to enable reusability throughout the code base. Signed-off-by: Radostin Stoyanov --- criu/img-remote.c | 38 +++++--------------------------------- 1 file changed, 5 insertions(+), 33 deletions(-) diff --git a/criu/img-remote.c b/criu/img-remote.c index 38028ca55..d82863600 100644 --- a/criu/img-remote.c +++ b/criu/img-remote.c @@ -137,34 +137,6 @@ static int event_set(int epoll_fd, int op, int fd, uint32_t events, void *data) return ret; } -static inline void socket_set_non_blocking(int fd) -{ - int flags = fcntl(fd, F_GETFL, NULL); - - if (flags < 0) { - pr_perror("Failed to obtain flags from fd %d", fd); - return; - } - flags |= O_NONBLOCK; - - if (fcntl(fd, F_SETFL, flags) < 0) - pr_perror("Failed to set flags for fd %d", fd); -} - -static inline void socket_set_blocking(int fd) -{ - int flags = fcntl(fd, F_GETFL, NULL); - - if (flags < 0) { - pr_perror("Failed to obtain flags from fd %d", fd); - return; - } - flags &= (~O_NONBLOCK); - - if (fcntl(fd, F_SETFL, flags) < 0) - pr_perror("Failed to set flags for fd %d", fd); -} - int setup_UNIX_server_socket(char *path) { struct sockaddr_un addr; @@ -529,7 +501,7 @@ static void forward_remote_image(struct roperation *rop) int64_t ret = 0; // Set blocking during the setup. - socket_set_blocking(rop->fd); + fd_set_nonblocking(rop->fd, false); ret = write_remote_header( rop->fd, rop->snapshot_id, rop->path, rop->flags, rop->size); @@ -545,7 +517,7 @@ static void forward_remote_image(struct roperation *rop) rop->size); // Go back to non-blocking - socket_set_non_blocking(rop->fd); + fd_set_nonblocking(rop->fd, true); forwarding = true; event_set(epoll_fd, EPOLL_CTL_ADD, rop->fd, EPOLLOUT, rop); @@ -561,7 +533,7 @@ static void handle_remote_accept(int fd) struct roperation* rop = NULL; // Set blocking during the setup. - socket_set_blocking(fd); + fd_set_nonblocking(fd, false); ret = read_remote_header(fd, snapshot_id, path, &flags, &size); if (ret < 0) { @@ -576,7 +548,7 @@ static void handle_remote_accept(int fd) } // Go back to non-blocking - socket_set_non_blocking(fd); + fd_set_nonblocking(fd, true); pr_info("[fd=%d] Received %s request for %s:%s with %" PRIu64 " bytes\n", fd, strflags(flags), path, snapshot_id, size); @@ -642,7 +614,7 @@ static void handle_local_accept(int fd) } else { list_add_tail(&(rop->l), &rop_pending); } - socket_set_non_blocking(rop->fd); + fd_set_nonblocking(rop->fd, false); } return; From 142c0432ae9db5e2fb67420dff170c1230c08fbf Mon Sep 17 00:00:00 2001 From: Radostin Stoyanov Date: Wed, 27 Feb 2019 21:32:50 +0000 Subject: [PATCH 062/249] remote: Use \0 as indicator for a "finish" msg Combine the macro constants DUMP_FINISH and RESTORE_FINISH, into a single one, called FINISH. In addition, replace the key-word strings used by the above-mentioned constants, and NULL_SNAPSHOT_ID, with a \0 character that will be used to indicate a "finish" message. Signed-off-by: Radostin Stoyanov --- criu/img-remote.c | 47 +++++++++++++++++---------------------- criu/include/img-remote.h | 5 ++--- 2 files changed, 23 insertions(+), 29 deletions(-) diff --git a/criu/img-remote.c b/criu/img-remote.c index d82863600..e2e3ed019 100644 --- a/criu/img-remote.c +++ b/criu/img-remote.c @@ -460,13 +460,6 @@ static struct roperation *handle_accept_cache_read(int cli_fd, struct rimage *rimg = NULL; struct roperation *rop = NULL; - // Check if this is the restore finish message. - if (!strncmp(path, RESTORE_FINISH, sizeof(RESTORE_FINISH))) { - close(cli_fd); - finish_local(); - return NULL; - } - rop = new_remote_operation(path, snapshot_id, cli_fd, flags, true); if (rop == NULL) { pr_perror("Error preparing remote operation"); @@ -587,6 +580,12 @@ static void handle_local_accept(int fd) goto err; } + if (snapshot_id[0] == NULL_SNAPSHOT_ID && path[0] == FINISH) { + close(cli_fd); + finish_local(); + return; + } + pr_info("[fd=%d] Received %s request for %s:%s\n", cli_fd, strflags(flags), path, snapshot_id); @@ -641,24 +640,18 @@ static inline void finish_proxy_read(struct roperation *rop) static inline void finish_proxy_write(struct roperation *rop) { - // No more local images are comming. Close local socket. - if (!strncmp(rop->path, DUMP_FINISH, sizeof(DUMP_FINISH))) { - // TODO - couldn't we handle the DUMP_FINISH in inside handle_accept_proxy_write? - finish_local(); - } else { - // Normal image received, forward it. - struct roperation *rop_to_forward = new_remote_operation( - rop->path, rop->snapshot_id, remote_sk, rop->flags, false); + // Normal image received, forward it. + struct roperation *rop_to_forward = new_remote_operation( + rop->path, rop->snapshot_id, remote_sk, rop->flags, false); - // Add image to list of images. - list_add_tail(&(rop->rimg->l), &rimg_head); + // Add image to list of images. + list_add_tail(&(rop->rimg->l), &rimg_head); - rop_set_rimg(rop_to_forward, rop->rimg); - if (list_empty(&rop_forwarding)) { - forward_remote_image(rop_to_forward); - } - list_add_tail(&(rop_to_forward->l), &rop_forwarding); + rop_set_rimg(rop_to_forward, rop->rimg); + if (list_empty(&rop_forwarding)) { + forward_remote_image(rop_to_forward); } + list_add_tail(&(rop_to_forward->l), &rop_forwarding); } static void finish_cache_write(struct roperation *rop) @@ -967,9 +960,11 @@ int read_remote_image_connection(char *snapshot_id, char *path) path, snapshot_id); return -1; } - if (!error || !strncmp(path, RESTORE_FINISH, sizeof(RESTORE_FINISH))) { + + if (!error || (snapshot_id[0] == NULL_SNAPSHOT_ID && path[0] != FINISH)) return sockfd; - } else if (error == ENOENT) { + + if (error == ENOENT) { pr_info("Image does not exist (%s:%s)\n", path, snapshot_id); close(sockfd); return -ENOENT; @@ -997,7 +992,7 @@ int write_remote_image_connection(char *snapshot_id, char *path, int flags) int finish_remote_dump(void) { pr_info("Dump side is calling finish\n"); - int fd = write_remote_image_connection(NULL_SNAPSHOT_ID, DUMP_FINISH, O_WRONLY); + int fd = write_remote_image_connection(NULL_SNAPSHOT_ID, FINISH, O_WRONLY); if (fd == -1) { pr_err("Unable to open finish dump connection"); @@ -1011,7 +1006,7 @@ int finish_remote_dump(void) int finish_remote_restore(void) { pr_info("Restore side is calling finish\n"); - int fd = read_remote_image_connection(NULL_SNAPSHOT_ID, RESTORE_FINISH); + int fd = read_remote_image_connection(NULL_SNAPSHOT_ID, FINISH); if (fd == -1) { pr_err("Unable to open finish restore connection\n"); diff --git a/criu/include/img-remote.h b/criu/include/img-remote.h index 087c395bf..66d75b90f 100644 --- a/criu/include/img-remote.h +++ b/criu/include/img-remote.h @@ -9,10 +9,9 @@ #ifndef IMAGE_REMOTE_H #define IMAGE_REMOTE_H -#define DUMP_FINISH "DUMP_FINISH" -#define RESTORE_FINISH "RESTORE_FINISH" +#define FINISH 0 #define PARENT_IMG "parent" -#define NULL_SNAPSHOT_ID "null" +#define NULL_SNAPSHOT_ID 0 #define DEFAULT_CACHE_SOCKET "img-cache.sock" #define DEFAULT_PROXY_SOCKET "img-proxy.sock" From b9103c835216360ac1ecbda71392e23f1c6ba8f4 Mon Sep 17 00:00:00 2001 From: Radostin Stoyanov Date: Wed, 13 Feb 2019 21:11:43 +0000 Subject: [PATCH 063/249] Sort includes in criu/img-*.c Sort and remove unused/unnecessary include statements in criu/img-*.c Signed-off-by: Radostin Stoyanov --- criu/img-cache.c | 8 ++------ criu/img-proxy.c | 7 ++----- criu/img-remote.c | 28 ++++++++-------------------- 3 files changed, 12 insertions(+), 31 deletions(-) diff --git a/criu/img-cache.c b/criu/img-cache.c index f28ba1c3c..3887b500d 100644 --- a/criu/img-cache.c +++ b/criu/img-cache.c @@ -1,12 +1,8 @@ +#include #include -#include "img-remote.h" -#include "criu-log.h" -#include -#include -#include -#include #include "cr_options.h" +#include "img-remote.h" #include "util.h" int image_cache(bool background, char *local_cache_path) diff --git a/criu/img-proxy.c b/criu/img-proxy.c index 09fa6dc04..f15bd7c9a 100644 --- a/criu/img-proxy.c +++ b/criu/img-proxy.c @@ -1,11 +1,8 @@ #include -#include "img-remote.h" -#include "criu-log.h" -#include -#include -#include #include "cr_options.h" +#include "criu-log.h" +#include "img-remote.h" #include "util.h" int image_proxy(bool background, char *local_proxy_path) diff --git a/criu/img-remote.c b/criu/img-remote.c index e2e3ed019..433c012ab 100644 --- a/criu/img-remote.c +++ b/criu/img-remote.c @@ -1,28 +1,16 @@ -#include -#include -#include -#include +#include #include #include -#include -#include -#include "xmalloc.h" -#include "criu-log.h" +#include +#include + +#include "cr_options.h" #include "img-remote.h" +#include "image.h" #include "images/remote-image.pb-c.h" -#include "protobuf-desc.h" -#include -#include "servicefd.h" -#include "common/compiler.h" -#include "cr_options.h" - -#include -#include "sys/un.h" -#include -#include - #include "protobuf.h" -#include "image.h" +#include "servicefd.h" +#include "xmalloc.h" #define EPOLL_MAX_EVENTS 50 From b605afd1f1c69eec96cad6ce4b54f96a2c17bb5c Mon Sep 17 00:00:00 2001 From: Radostin Stoyanov Date: Sun, 24 Mar 2019 14:26:10 +0000 Subject: [PATCH 064/249] util: Remove deprecated print_data() routine The print_data() function was part of the deprecated (and removed) 'show' action, and it was moved in util.c with the following commit: a501b4804b3c95e1d83d64dd10ed95c37f0378bb The 'show' action has been deprecated since 1.6, let's finally drop it. The print_data() routine is kept for yet another (to be deprecated too) feature called 'criu exec'. The criu exec feature was removed with: 909590a3558560655c1ce5b72215efbb325999ca Remove criu exec code It's now obsoleted by compel library. Maybe-TODO: Add compel tool exec action? Therefore, now we can drop print_data() as well. Signed-off-by: Radostin Stoyanov --- criu/include/util.h | 2 -- criu/util.c | 73 --------------------------------------------- 2 files changed, 75 deletions(-) diff --git a/criu/include/util.h b/criu/include/util.h index 1f77629bb..136ca14ca 100644 --- a/criu/include/util.h +++ b/criu/include/util.h @@ -290,8 +290,6 @@ char *xstrcat(char *str, const char *fmt, ...) char *xsprintf(const char *fmt, ...) __attribute__ ((__format__ (__printf__, 1, 2))); -void print_data(unsigned long addr, unsigned char *data, size_t size); - int setup_tcp_server(char *type); int run_tcp_server(bool daemon_mode, int *ask, int cfd, int sk); int setup_tcp_client(void); diff --git a/criu/util.c b/criu/util.c index 95aecee91..65ca3ac41 100644 --- a/criu/util.c +++ b/criu/util.c @@ -26,7 +26,6 @@ #include #include #include -#include #include "kerndat.h" #include "page.h" @@ -1003,78 +1002,6 @@ void tcp_nodelay(int sk, bool on) pr_perror("Unable to restore TCP_NODELAY (%d)", val); } -static inline void pr_xsym(unsigned char *data, size_t len, int pos) -{ - char sym; - - if (pos < len) - sym = data[pos]; - else - sym = ' '; - - pr_msg("%c", isprint(sym) ? sym : '.'); -} - -static inline void pr_xdigi(unsigned char *data, size_t len, int pos) -{ - if (pos < len) - pr_msg("%02x ", data[pos]); - else - pr_msg(" "); -} - -static int nice_width_for(unsigned long addr) -{ - int ret = 3; - - while (addr) { - addr >>= 4; - ret++; - } - - return ret; -} - -void print_data(unsigned long addr, unsigned char *data, size_t size) -{ - int i, j, addr_len; - unsigned zero_line = 0; - - addr_len = nice_width_for(addr + size); - - for (i = 0; i < size; i += 16) { - if (*(u64 *)(data + i) == 0 && *(u64 *)(data + i + 8) == 0) { - if (zero_line == 0) - zero_line = 1; - else { - if (zero_line == 1) { - pr_msg("*\n"); - zero_line = 2; - } - - continue; - } - } else - zero_line = 0; - - pr_msg("%#0*lx: ", addr_len, addr + i); - for (j = 0; j < 8; j++) - pr_xdigi(data, size, i + j); - pr_msg(" "); - for (j = 8; j < 16; j++) - pr_xdigi(data, size, i + j); - - pr_msg(" |"); - for (j = 0; j < 8; j++) - pr_xsym(data, size, i + j); - pr_msg(" "); - for (j = 8; j < 16; j++) - pr_xsym(data, size, i + j); - - pr_msg("|\n"); - } -} - static int get_sockaddr_in(struct sockaddr_storage *addr, char *host) { memset(addr, 0, sizeof(*addr)); From b7e589ea453778bff020e1e9e307b1571b929ed0 Mon Sep 17 00:00:00 2001 From: Radostin Stoyanov Date: Sun, 7 Apr 2019 20:41:37 +0100 Subject: [PATCH 065/249] criu-ns: Convert to python3 style print() syntax Signed-off-by: Radostin Stoyanov --- scripts/criu-ns | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/scripts/criu-ns b/scripts/criu-ns index 0910f2a33..b6b6a1111 100755 --- a/scripts/criu-ns +++ b/scripts/criu-ns @@ -57,7 +57,7 @@ else: def run_criu(): - print sys.argv + print(sys.argv) os.execlp('criu', *['criu'] + sys.argv[1:]) @@ -155,7 +155,7 @@ def set_pidns(tpid, pid_idx): if ls[1] != tpid: raise OSError(errno.ESRCH, 'No such pid') - print 'Replace pid %s with %s' % (tpid, ls[2]) + print('Replace pid {} with {}'.format(tpid, ls[2])) sys.argv[pid_idx] = ls[2] break else: @@ -234,7 +234,7 @@ if action == 'restore': elif action == 'dump' or action == 'pre-dump': res = wrap_dump() else: - print 'Unsupported action %s for nswrap' % action + print('Unsupported action {} for nswrap'.format(action)) res = -1 sys.exit(res) From fea04827653c96052256db5a9c863da0a3b13fc5 Mon Sep 17 00:00:00 2001 From: Radostin Stoyanov Date: Sun, 7 Apr 2019 20:43:12 +0100 Subject: [PATCH 066/249] criu-ns: Print usage info when no args provided Signed-off-by: Radostin Stoyanov --- scripts/criu-ns | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/scripts/criu-ns b/scripts/criu-ns index b6b6a1111..b582f7580 100755 --- a/scripts/criu-ns +++ b/scripts/criu-ns @@ -227,6 +227,18 @@ def wrap_dump(): return status +if len(sys.argv) == 1: + print(""" +Usage: + {0} dump|pre-dump -t PID [] + {0} restore [] +\nCommands: + dump checkpoint a process/tree identified by pid + pre-dump pre-dump task(s) minimizing their frozen time + restore restore a process/tree +""".format(sys.argv[0])) + exit(1) + action = sys.argv[1] if action == 'restore': From 351fbe1b3b4b8c3e5e87a731d2826199f0df78c5 Mon Sep 17 00:00:00 2001 From: Radostin Stoyanov Date: Sun, 7 Apr 2019 20:55:32 +0100 Subject: [PATCH 067/249] criu-ns: Convert c_char_p strings to bytes object class ctypes.c_char_p Represents the C char * datatype when it points to a zero- terminated string. For a general character pointer that may also point to binary data, POINTER(c_char) must be used. The constructor accepts an integer address, or a bytes object. https://docs.python.org/3/library/ctypes.html#ctypes.c_char_p Signed-off-by: Radostin Stoyanov --- scripts/criu-ns | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/scripts/criu-ns b/scripts/criu-ns index b582f7580..e065c5971 100755 --- a/scripts/criu-ns +++ b/scripts/criu-ns @@ -74,11 +74,11 @@ def wrap_restore(): os.close(r_pipe) # Mount new /proc - if _mount(None, "/", None, MS_SLAVE|MS_REC, None) != 0: + if _mount(None, b"/", None, MS_SLAVE|MS_REC, None) != 0: _errno = ctypes.get_errno() raise OSError(_errno, errno.errorcode[_errno]) - if _mount('proc', '/proc', 'proc', 0, None) != 0: + if _mount(b'proc', b'/proc', b'proc', 0, None) != 0: _errno = ctypes.get_errno() raise OSError(_errno, errno.errorcode[_errno]) @@ -98,7 +98,7 @@ def wrap_restore(): status = -251 break - os.write(w_pipe, "%d" % status) + os.write(w_pipe, b"%d" % status) os.close(w_pipe) if status != 0: From 6d18c867154ea6b3e34d94bb6c4394e22b851de5 Mon Sep 17 00:00:00 2001 From: Radostin Stoyanov Date: Fri, 12 Apr 2019 21:01:36 +0100 Subject: [PATCH 068/249] Convert spaces to tabs There are a few places where spaces have been used instead of tabs for indentation. This patch converts the spaces to tabs for consistency with the rest of the code base. Signed-off-by: Radostin Stoyanov --- .../arm/plugins/std/syscalls/syscall-aux.h | 12 ++-- .../lib/include/uapi/asm/processor-flags.h | 56 ++++++++-------- .../src/lib/include/uapi/asm/breakpoints.h | 4 +- .../arch/x86/src/lib/include/uapi/asm/fpu.h | 2 +- compel/arch/x86/src/lib/infect.c | 18 +++--- compel/include/uapi/sigframe-common.h | 4 +- criu/arch/x86/include/asm/parasite.h | 4 +- criu/arch/x86/include/asm/restorer.h | 12 ++-- criu/cr-check.c | 2 +- criu/cr-restore.c | 8 +-- criu/filesystems.c | 2 +- criu/include/aio.h | 18 +++--- criu/include/asm-generic/vdso.h | 4 +- criu/include/autofs.h | 62 +++++++++--------- criu/include/packet_diag.h | 60 ++++++++--------- criu/include/page-pipe.h | 10 +-- criu/include/protobuf-desc.h | 6 +- criu/include/ptrace-compat.h | 6 +- criu/include/sockets.h | 2 +- criu/include/util-pie.h | 2 +- criu/libnetlink.c | 2 +- criu/mount.c | 4 +- criu/net.c | 24 +++---- criu/pagemap-cache.c | 2 +- criu/sk-queue.c | 2 +- include/common/arch/arm/asm/atomic.h | 18 +++--- include/common/arch/ppc64/asm/atomic.h | 2 +- include/common/arch/ppc64/asm/linkage.h | 64 +++++++++---------- include/common/arch/x86/asm/cmpxchg.h | 2 +- lib/c/criu.c | 4 +- soccr/soccr.c | 14 ++-- 31 files changed, 216 insertions(+), 216 deletions(-) diff --git a/compel/arch/arm/plugins/std/syscalls/syscall-aux.h b/compel/arch/arm/plugins/std/syscalls/syscall-aux.h index 0b029301f..3d2056b5a 100644 --- a/compel/arch/arm/plugins/std/syscalls/syscall-aux.h +++ b/compel/arch/arm/plugins/std/syscalls/syscall-aux.h @@ -3,25 +3,25 @@ #endif #ifndef __ARM_NR_BASE -# define __ARM_NR_BASE 0x0f0000 +# define __ARM_NR_BASE 0x0f0000 #endif #ifndef __ARM_NR_breakpoint -# define __ARM_NR_breakpoint (__ARM_NR_BASE+1) +# define __ARM_NR_breakpoint (__ARM_NR_BASE+1) #endif #ifndef __ARM_NR_cacheflush -# define __ARM_NR_cacheflush (__ARM_NR_BASE+2) +# define __ARM_NR_cacheflush (__ARM_NR_BASE+2) #endif #ifndef __ARM_NR_usr26 -# define __ARM_NR_usr26 (__ARM_NR_BASE+3) +# define __ARM_NR_usr26 (__ARM_NR_BASE+3) #endif #ifndef __ARM_NR_usr32 -# define __ARM_NR_usr32 (__ARM_NR_BASE+4) +# define __ARM_NR_usr32 (__ARM_NR_BASE+4) #endif #ifndef __ARM_NR_set_tls -# define __ARM_NR_set_tls (__ARM_NR_BASE+5) +# define __ARM_NR_set_tls (__ARM_NR_BASE+5) #endif diff --git a/compel/arch/arm/src/lib/include/uapi/asm/processor-flags.h b/compel/arch/arm/src/lib/include/uapi/asm/processor-flags.h index fc00a9e64..8745f4459 100644 --- a/compel/arch/arm/src/lib/include/uapi/asm/processor-flags.h +++ b/compel/arch/arm/src/lib/include/uapi/asm/processor-flags.h @@ -6,37 +6,37 @@ /* * PSR bits */ -#define USR26_MODE 0x00000000 -#define FIQ26_MODE 0x00000001 -#define IRQ26_MODE 0x00000002 -#define SVC26_MODE 0x00000003 -#define USR_MODE 0x00000010 -#define FIQ_MODE 0x00000011 -#define IRQ_MODE 0x00000012 -#define SVC_MODE 0x00000013 -#define ABT_MODE 0x00000017 -#define UND_MODE 0x0000001b -#define SYSTEM_MODE 0x0000001f -#define MODE32_BIT 0x00000010 -#define MODE_MASK 0x0000001f -#define PSR_T_BIT 0x00000020 -#define PSR_F_BIT 0x00000040 -#define PSR_I_BIT 0x00000080 -#define PSR_A_BIT 0x00000100 -#define PSR_E_BIT 0x00000200 -#define PSR_J_BIT 0x01000000 -#define PSR_Q_BIT 0x08000000 -#define PSR_V_BIT 0x10000000 -#define PSR_C_BIT 0x20000000 -#define PSR_Z_BIT 0x40000000 -#define PSR_N_BIT 0x80000000 +#define USR26_MODE 0x00000000 +#define FIQ26_MODE 0x00000001 +#define IRQ26_MODE 0x00000002 +#define SVC26_MODE 0x00000003 +#define USR_MODE 0x00000010 +#define FIQ_MODE 0x00000011 +#define IRQ_MODE 0x00000012 +#define SVC_MODE 0x00000013 +#define ABT_MODE 0x00000017 +#define UND_MODE 0x0000001b +#define SYSTEM_MODE 0x0000001f +#define MODE32_BIT 0x00000010 +#define MODE_MASK 0x0000001f +#define PSR_T_BIT 0x00000020 +#define PSR_F_BIT 0x00000040 +#define PSR_I_BIT 0x00000080 +#define PSR_A_BIT 0x00000100 +#define PSR_E_BIT 0x00000200 +#define PSR_J_BIT 0x01000000 +#define PSR_Q_BIT 0x08000000 +#define PSR_V_BIT 0x10000000 +#define PSR_C_BIT 0x20000000 +#define PSR_Z_BIT 0x40000000 +#define PSR_N_BIT 0x80000000 /* * Groups of PSR bits */ -#define PSR_f 0xff000000 /* Flags */ -#define PSR_s 0x00ff0000 /* Status */ -#define PSR_x 0x0000ff00 /* Extension */ -#define PSR_c 0x000000ff /* Control */ +#define PSR_f 0xff000000 /* Flags */ +#define PSR_s 0x00ff0000 /* Status */ +#define PSR_x 0x0000ff00 /* Extension */ +#define PSR_c 0x000000ff /* Control */ #endif diff --git a/compel/arch/ppc64/src/lib/include/uapi/asm/breakpoints.h b/compel/arch/ppc64/src/lib/include/uapi/asm/breakpoints.h index 1ab89af76..5f090490d 100644 --- a/compel/arch/ppc64/src/lib/include/uapi/asm/breakpoints.h +++ b/compel/arch/ppc64/src/lib/include/uapi/asm/breakpoints.h @@ -4,12 +4,12 @@ static inline int ptrace_set_breakpoint(pid_t pid, void *addr) { - return 0; + return 0; } static inline int ptrace_flush_breakpoints(pid_t pid) { - return 0; + return 0; } #endif diff --git a/compel/arch/x86/src/lib/include/uapi/asm/fpu.h b/compel/arch/x86/src/lib/include/uapi/asm/fpu.h index 896c3f253..509f4488b 100644 --- a/compel/arch/x86/src/lib/include/uapi/asm/fpu.h +++ b/compel/arch/x86/src/lib/include/uapi/asm/fpu.h @@ -149,7 +149,7 @@ struct xsave_hdr_struct { * The high 128 bits are stored here. */ struct ymmh_struct { - uint32_t ymmh_space[64]; + uint32_t ymmh_space[64]; } __packed; /* Intel MPX support: */ diff --git a/compel/arch/x86/src/lib/infect.c b/compel/arch/x86/src/lib/infect.c index e76f7787d..0737e07a3 100644 --- a/compel/arch/x86/src/lib/infect.c +++ b/compel/arch/x86/src/lib/infect.c @@ -481,15 +481,15 @@ int arch_fetch_sas(struct parasite_ctl *ctl, struct rt_sigframe *s) /* Copied from the gdb header gdb/nat/x86-dregs.h */ /* Debug registers' indices. */ -#define DR_FIRSTADDR 0 -#define DR_LASTADDR 3 -#define DR_NADDR 4 /* The number of debug address registers. */ -#define DR_STATUS 6 /* Index of debug status register (DR6). */ -#define DR_CONTROL 7 /* Index of debug control register (DR7). */ - -#define DR_LOCAL_ENABLE_SHIFT 0 /* Extra shift to the local enable bit. */ -#define DR_GLOBAL_ENABLE_SHIFT 1 /* Extra shift to the global enable bit. */ -#define DR_ENABLE_SIZE 2 /* Two enable bits per debug register. */ +#define DR_FIRSTADDR 0 +#define DR_LASTADDR 3 +#define DR_NADDR 4 /* The number of debug address registers. */ +#define DR_STATUS 6 /* Index of debug status register (DR6). */ +#define DR_CONTROL 7 /* Index of debug control register (DR7). */ + +#define DR_LOCAL_ENABLE_SHIFT 0 /* Extra shift to the local enable bit. */ +#define DR_GLOBAL_ENABLE_SHIFT 1 /* Extra shift to the global enable bit. */ +#define DR_ENABLE_SIZE 2 /* Two enable bits per debug register. */ /* Locally enable the break/watchpoint in the I'th debug register. */ #define X86_DR_LOCAL_ENABLE(i) (1 << (DR_LOCAL_ENABLE_SHIFT + DR_ENABLE_SIZE * (i))) diff --git a/compel/include/uapi/sigframe-common.h b/compel/include/uapi/sigframe-common.h index e35c8655e..fc93c5480 100644 --- a/compel/include/uapi/sigframe-common.h +++ b/compel/include/uapi/sigframe-common.h @@ -52,8 +52,8 @@ struct rt_ucontext { rt_stack_t uc_stack; struct rt_sigcontext uc_mcontext; k_rtsigset_t uc_sigmask; /* mask last for extensibility */ - int _unused[32 - (sizeof (k_rtsigset_t) / sizeof (int))]; - unsigned long uc_regspace[128] __attribute__((aligned(8))); + int _unused[32 - (sizeof (k_rtsigset_t) / sizeof (int))]; + unsigned long uc_regspace[128] __attribute__((aligned(8))); }; extern int sigreturn_prep_fpu_frame(struct rt_sigframe *frame, diff --git a/criu/arch/x86/include/asm/parasite.h b/criu/arch/x86/include/asm/parasite.h index 0ef1d9a86..6b4d4ac59 100644 --- a/criu/arch/x86/include/asm/parasite.h +++ b/criu/arch/x86/include/asm/parasite.h @@ -28,8 +28,8 @@ static int arch_get_user_desc(user_desc_t *desc) * }; */ asm volatile ( - " mov %0,%%eax \n" - " mov %1,%%rbx \n" + " mov %0,%%eax \n" + " mov %1,%%rbx \n" " int $0x80 \n" " mov %%eax,%0 \n" : "+m"(ret) diff --git a/criu/arch/x86/include/asm/restorer.h b/criu/arch/x86/include/asm/restorer.h index 15867aa12..3c43ce688 100644 --- a/criu/arch/x86/include/asm/restorer.h +++ b/criu/arch/x86/include/asm/restorer.h @@ -25,12 +25,12 @@ static inline int set_compat_robust_list(uint32_t head_ptr, uint32_t len) } #endif /* !CONFIG_COMPAT */ -#define RUN_CLONE_RESTORE_FN(ret, clone_flags, new_sp, parent_tid, \ - thread_args, clone_restore_fn) \ +#define RUN_CLONE_RESTORE_FN(ret, clone_flags, new_sp, parent_tid, \ + thread_args, clone_restore_fn) \ asm volatile( \ "clone_emul: \n" \ "movq %2, %%rsi \n" \ - "subq $16, %%rsi \n" \ + "subq $16, %%rsi \n" \ "movq %6, %%rdi \n" \ "movq %%rdi, 8(%%rsi) \n" \ "movq %5, %%rdi \n" \ @@ -39,16 +39,16 @@ static inline int set_compat_robust_list(uint32_t head_ptr, uint32_t len) "movq %3, %%rdx \n" \ "movq %4, %%r10 \n" \ "movl $"__stringify(__NR_clone)", %%eax \n" \ - "syscall \n" \ + "syscall \n" \ \ - "testq %%rax,%%rax \n" \ + "testq %%rax,%%rax \n" \ "jz thread_run \n" \ \ "movq %%rax, %0 \n" \ "jmp clone_end \n" \ \ "thread_run: \n" \ - "xorq %%rbp, %%rbp \n" \ + "xorq %%rbp, %%rbp \n" \ "popq %%rax \n" \ "popq %%rdi \n" \ "callq *%%rax \n" \ diff --git a/criu/cr-check.c b/criu/cr-check.c index 7addb9fb0..e24668305 100644 --- a/criu/cr-check.c +++ b/criu/cr-check.c @@ -468,7 +468,7 @@ static int check_unaligned_vmsplice(void) } #ifndef SO_GET_FILTER -#define SO_GET_FILTER SO_ATTACH_FILTER +#define SO_GET_FILTER SO_ATTACH_FILTER #endif static int check_so_gets(void) diff --git a/criu/cr-restore.c b/criu/cr-restore.c index 170beb344..1073a4069 100644 --- a/criu/cr-restore.c +++ b/criu/cr-restore.c @@ -391,10 +391,10 @@ static int populate_root_fd_off(void) struct ns_id *mntns = NULL; int ret; - if (root_ns_mask & CLONE_NEWNS) { - mntns = lookup_ns_by_id(root_item->ids->mnt_ns_id, &mnt_ns_desc); - BUG_ON(!mntns); - } + if (root_ns_mask & CLONE_NEWNS) { + mntns = lookup_ns_by_id(root_item->ids->mnt_ns_id, &mnt_ns_desc); + BUG_ON(!mntns); + } ret = mntns_get_root_fd(mntns); if (ret < 0) diff --git a/criu/filesystems.c b/criu/filesystems.c index 8a2c41853..1e4550b37 100644 --- a/criu/filesystems.c +++ b/criu/filesystems.c @@ -58,7 +58,7 @@ static int parse_binfmt_misc_entry(struct bfd *f, BinfmtMiscEntry *bme) char *str; str = breadline(f); - if (IS_ERR(str)) + if (IS_ERR(str)) return -1; if (!str) break; diff --git a/criu/include/aio.h b/criu/include/aio.h index 9a58089b6..858ccd3cf 100644 --- a/criu/include/aio.h +++ b/criu/include/aio.h @@ -13,18 +13,18 @@ struct task_restore_args; int prepare_aios(struct pstree_item *t, struct task_restore_args *ta); struct aio_ring { - unsigned id; /* kernel internal index number */ - unsigned nr; /* number of io_events */ - unsigned head; /* Written to by userland or under ring_lock + unsigned id; /* kernel internal index number */ + unsigned nr; /* number of io_events */ + unsigned head; /* Written to by userland or under ring_lock * mutex by aio_read_events_ring(). */ - unsigned tail; + unsigned tail; - unsigned magic; - unsigned compat_features; - unsigned incompat_features; - unsigned header_length; /* size of aio_ring */ + unsigned magic; + unsigned compat_features; + unsigned incompat_features; + unsigned header_length; /* size of aio_ring */ - struct io_event io_events[0]; + struct io_event io_events[0]; }; struct rst_aio_ring { diff --git a/criu/include/asm-generic/vdso.h b/criu/include/asm-generic/vdso.h index 81e54d264..6c3e3d137 100644 --- a/criu/include/asm-generic/vdso.h +++ b/criu/include/asm-generic/vdso.h @@ -1,8 +1,8 @@ #ifndef __CR_ASM_GENERIC_VDSO_H__ #define __CR_ASM_GENERIC_VDSO_H__ -#define VDSO_PROT (PROT_READ | PROT_EXEC) -#define VVAR_PROT (PROT_READ) +#define VDSO_PROT (PROT_READ | PROT_EXEC) +#define VVAR_PROT (PROT_READ) /* Just in case of LPAE system PFN is u64. */ #define VDSO_BAD_PFN (-1ull) diff --git a/criu/include/autofs.h b/criu/include/autofs.h index d294277f6..c4618859b 100644 --- a/criu/include/autofs.h +++ b/criu/include/autofs.h @@ -20,70 +20,70 @@ int autofs_mount(struct mount_info *mi, const char *source, const #include -#define AUTOFS_DEVICE_NAME "autofs" +#define AUTOFS_DEVICE_NAME "autofs" #define AUTOFS_DEV_IOCTL_VERSION_MAJOR 1 #define AUTOFS_DEV_IOCTL_VERSION_MINOR 0 -#define AUTOFS_DEVID_LEN 16 +#define AUTOFS_DEVID_LEN 16 -#define AUTOFS_DEV_IOCTL_SIZE sizeof(struct autofs_dev_ioctl) +#define AUTOFS_DEV_IOCTL_SIZE sizeof(struct autofs_dev_ioctl) /* * An ioctl interface for autofs mount point control. */ struct args_protover { - __u32 version; + __u32 version; }; struct args_protosubver { - __u32 sub_version; + __u32 sub_version; }; struct args_openmount { - __u32 devid; + __u32 devid; }; struct args_ready { - __u32 token; + __u32 token; }; struct args_fail { - __u32 token; - __s32 status; + __u32 token; + __s32 status; }; struct args_setpipefd { - __s32 pipefd; + __s32 pipefd; }; struct args_timeout { - __u64 timeout; + __u64 timeout; }; struct args_requester { - __u32 uid; - __u32 gid; + __u32 uid; + __u32 gid; }; struct args_expire { - __u32 how; + __u32 how; }; struct args_askumount { - __u32 may_umount; + __u32 may_umount; }; struct args_ismountpoint { union { struct args_in { - __u32 type; + __u32 type; } in; struct args_out { - __u32 devid; - __u32 magic; + __u32 devid; + __u32 magic; } out; }; }; @@ -98,24 +98,24 @@ struct args_ismountpoint { struct autofs_dev_ioctl { __u32 ver_major; __u32 ver_minor; - __u32 size; /* total size of data passed in + __u32 size; /* total size of data passed in * including this struct */ - __s32 ioctlfd; /* automount command fd */ + __s32 ioctlfd; /* automount command fd */ /* Command parameters */ union { - struct args_protover protover; - struct args_protosubver protosubver; - struct args_openmount openmount; - struct args_ready ready; - struct args_fail fail; - struct args_setpipefd setpipefd; - struct args_timeout timeout; - struct args_requester requester; - struct args_expire expire; - struct args_askumount askumount; - struct args_ismountpoint ismountpoint; + struct args_protover protover; + struct args_protosubver protosubver; + struct args_openmount openmount; + struct args_ready ready; + struct args_fail fail; + struct args_setpipefd setpipefd; + struct args_timeout timeout; + struct args_requester requester; + struct args_expire expire; + struct args_askumount askumount; + struct args_ismountpoint ismountpoint; }; char path[0]; diff --git a/criu/include/packet_diag.h b/criu/include/packet_diag.h index e5d9193a8..287de84ec 100644 --- a/criu/include/packet_diag.h +++ b/criu/include/packet_diag.h @@ -12,18 +12,18 @@ struct packet_diag_req { __u32 pdiag_cookie[2]; }; -#define PACKET_SHOW_INFO 0x00000001 /* Basic packet_sk information */ -#define PACKET_SHOW_MCLIST 0x00000002 /* A set of packet_diag_mclist-s */ +#define PACKET_SHOW_INFO 0x00000001 /* Basic packet_sk information */ +#define PACKET_SHOW_MCLIST 0x00000002 /* A set of packet_diag_mclist-s */ #define PACKET_SHOW_RING_CFG 0x00000004 /* Rings configuration parameters */ #define PACKET_SHOW_FANOUT 0x00000008 struct packet_diag_msg { - __u8 pdiag_family; - __u8 pdiag_type; - __u16 pdiag_num; + __u8 pdiag_family; + __u8 pdiag_type; + __u16 pdiag_num; - __u32 pdiag_ino; - __u32 pdiag_cookie[2]; + __u32 pdiag_ino; + __u32 pdiag_cookie[2]; }; enum { @@ -37,18 +37,18 @@ enum { }; struct packet_diag_info { - __u32 pdi_index; - __u32 pdi_version; - __u32 pdi_reserve; - __u32 pdi_copy_thresh; - __u32 pdi_tstamp; - __u32 pdi_flags; + __u32 pdi_index; + __u32 pdi_version; + __u32 pdi_reserve; + __u32 pdi_copy_thresh; + __u32 pdi_tstamp; + __u32 pdi_flags; -#define PDI_RUNNING 0x1 -#define PDI_AUXDATA 0x2 -#define PDI_ORIGDEV 0x4 -#define PDI_VNETHDR 0x8 -#define PDI_LOSS 0x10 +#define PDI_RUNNING 0x1 +#define PDI_AUXDATA 0x2 +#define PDI_ORIGDEV 0x4 +#define PDI_VNETHDR 0x8 +#define PDI_LOSS 0x10 }; #ifndef MAX_ADDR_LEN @@ -56,21 +56,21 @@ struct packet_diag_info { #endif struct packet_diag_mclist { - __u32 pdmc_index; - __u32 pdmc_count; - __u16 pdmc_type; - __u16 pdmc_alen; - __u8 pdmc_addr[MAX_ADDR_LEN]; + __u32 pdmc_index; + __u32 pdmc_count; + __u16 pdmc_type; + __u16 pdmc_alen; + __u8 pdmc_addr[MAX_ADDR_LEN]; }; struct packet_diag_ring { - __u32 pdr_block_size; - __u32 pdr_block_nr; - __u32 pdr_frame_size; - __u32 pdr_frame_nr; - __u32 pdr_retire_tmo; - __u32 pdr_sizeof_priv; - __u32 pdr_features; + __u32 pdr_block_size; + __u32 pdr_block_nr; + __u32 pdr_frame_size; + __u32 pdr_frame_nr; + __u32 pdr_retire_tmo; + __u32 pdr_sizeof_priv; + __u32 pdr_features; }; #endif /* __CR_PACKET_DIAG_H__ */ diff --git a/criu/include/page-pipe.h b/criu/include/page-pipe.h index 8fa1bfa5e..80e595871 100644 --- a/criu/include/page-pipe.h +++ b/criu/include/page-pipe.h @@ -6,11 +6,11 @@ #define PAGE_ALLOC_COSTLY_ORDER 3 /* from the kernel source code */ struct kernel_pipe_buffer { - struct page *page; - unsigned int offset, len; - const struct pipe_buf_operations *ops; - unsigned int flags; - unsigned long private; + struct page *page; + unsigned int offset, len; + const struct pipe_buf_operations *ops; + unsigned int flags; + unsigned long private; }; /* diff --git a/criu/include/protobuf-desc.h b/criu/include/protobuf-desc.h index 696a5800b..21ba27193 100644 --- a/criu/include/protobuf-desc.h +++ b/criu/include/protobuf-desc.h @@ -61,10 +61,10 @@ enum { PB_AUTOFS, PB_GHOST_CHUNK, PB_FILE, - PB_REMOTE_IMAGE, /* Header for images sent from proxy to cache.*/ - PB_LOCAL_IMAGE, /* Header for reading/writing images from/to proxy or cache. */ + PB_REMOTE_IMAGE, /* Header for images sent from proxy to cache.*/ + PB_LOCAL_IMAGE, /* Header for reading/writing images from/to proxy or cache. */ PB_LOCAL_IMAGE_REPLY, /* Header for reading/writing images reply. */ - PB_SNAPSHOT_ID, /* Contains a single id. Used for reading/writing ids from proxy or cache. */ + PB_SNAPSHOT_ID, /* Contains a single id. Used for reading/writing ids from proxy or cache. */ /* PB_AUTOGEN_STOP */ diff --git a/criu/include/ptrace-compat.h b/criu/include/ptrace-compat.h index 476da3536..e16fef036 100644 --- a/criu/include/ptrace-compat.h +++ b/criu/include/ptrace-compat.h @@ -7,9 +7,9 @@ #ifndef CONFIG_HAS_PTRACE_PEEKSIGINFO struct ptrace_peeksiginfo_args { - __u64 off; /* from which siginfo to start */ - __u32 flags; - __u32 nr; /* how may siginfos to take */ + __u64 off; /* from which siginfo to start */ + __u32 flags; + __u32 nr; /* how may siginfos to take */ }; #endif diff --git a/criu/include/sockets.h b/criu/include/sockets.h index 65b230131..cd98d18e0 100644 --- a/criu/include/sockets.h +++ b/criu/include/sockets.h @@ -92,7 +92,7 @@ static inline int sk_decode_shutdown(int val) extern int set_netns(uint32_t ns_id); #ifndef SIOCGSKNS -#define SIOCGSKNS 0x894C /* get socket network namespace */ +#define SIOCGSKNS 0x894C /* get socket network namespace */ #endif extern int kerndat_socket_netns(void); diff --git a/criu/include/util-pie.h b/criu/include/util-pie.h index ce78b0d19..a8137f441 100644 --- a/criu/include/util-pie.h +++ b/criu/include/util-pie.h @@ -10,7 +10,7 @@ #endif #ifndef SO_PEEK_OFF -#define SO_PEEK_OFF 42 +#define SO_PEEK_OFF 42 #endif #include "common/scm.h" diff --git a/criu/libnetlink.c b/criu/libnetlink.c index ca9968309..18a323b8d 100644 --- a/criu/libnetlink.c +++ b/criu/libnetlink.c @@ -222,5 +222,5 @@ int __wrap_nlmsg_parse(struct nlmsghdr *nlh, int hdrlen, struct nlattr *tb[], int32_t nla_get_s32(const struct nlattr *nla) { - return *(const int32_t *) nla_data(nla); + return *(const int32_t *) nla_data(nla); } diff --git a/criu/mount.c b/criu/mount.c index 118ba623e..c03a435c5 100644 --- a/criu/mount.c +++ b/criu/mount.c @@ -2325,8 +2325,8 @@ static int do_bind_mount(struct mount_info *mi) * mi->shared_id && !shared - create a new shared group */ if (restore_shared_options(mi, private, - mi->shared_id && !shared, - mi->master_id && !master)) + mi->shared_id && !shared, + mi->master_id && !master)) return -1; mi->mounted = true; diff --git a/criu/net.c b/criu/net.c index 43ff8e894..b9f6669c3 100644 --- a/criu/net.c +++ b/criu/net.c @@ -344,7 +344,7 @@ static int ipv6_conf_op(char *tgt, SysctlEntry **conf, int n, int op, SysctlEntr * the kernel, simply write DEVCONFS_UNUSED * into the image so we would skip it. */ -#define DEVCONFS_UNUSED (-1u) +#define DEVCONFS_UNUSED (-1u) static int ipv4_conf_op_old(char *tgt, int *conf, int n, int op, int *def_conf) { @@ -2792,7 +2792,7 @@ static int prep_ns_sockets(struct ns_id *ns, bool for_dump) freecon(ctx); if (ret < 0) { pr_perror("Setting SELinux socket context for PID %d failed", - root_item->pid->real); + root_item->pid->real); goto err_sq; } } @@ -3046,22 +3046,22 @@ int move_veth_to_bridge(void) #ifndef NETNSA_MAX /* Attributes of RTM_NEWNSID/RTM_GETNSID messages */ enum { - NETNSA_NONE, + NETNSA_NONE, #define NETNSA_NSID_NOT_ASSIGNED -1 - NETNSA_NSID, - NETNSA_PID, - NETNSA_FD, - __NETNSA_MAX, + NETNSA_NSID, + NETNSA_PID, + NETNSA_FD, + __NETNSA_MAX, }; -#define NETNSA_MAX (__NETNSA_MAX - 1) +#define NETNSA_MAX (__NETNSA_MAX - 1) #endif static struct nla_policy rtnl_net_policy[NETNSA_MAX + 1] = { - [NETNSA_NONE] = { .type = NLA_UNSPEC }, - [NETNSA_NSID] = { .type = NLA_S32 }, - [NETNSA_PID] = { .type = NLA_U32 }, - [NETNSA_FD] = { .type = NLA_U32 }, + [NETNSA_NONE] = { .type = NLA_UNSPEC }, + [NETNSA_NSID] = { .type = NLA_S32 }, + [NETNSA_PID] = { .type = NLA_U32 }, + [NETNSA_FD] = { .type = NLA_U32 }, }; static int nsid_cb(struct nlmsghdr *msg, struct ns_id *ns, void *arg) diff --git a/criu/pagemap-cache.c b/criu/pagemap-cache.c index aa39dacaa..a1c2d42f4 100644 --- a/criu/pagemap-cache.c +++ b/criu/pagemap-cache.c @@ -119,7 +119,7 @@ static int pmc_fill_cache(pmc_t *pmc, const struct vma_area *vma) * is to walk page tables less. */ if (!pagemap_cache_disabled && - len < PMC_SIZE && (vma->e->start - low) < PMC_SIZE_GAP) { + len < PMC_SIZE && (vma->e->start - low) < PMC_SIZE_GAP) { size_t size_cov = len; size_t nr_vmas = 1; diff --git a/criu/sk-queue.c b/criu/sk-queue.c index 613e38461..fdf610170 100644 --- a/criu/sk-queue.c +++ b/criu/sk-queue.c @@ -29,7 +29,7 @@ struct sk_packet { struct list_head list; SkPacketEntry *entry; - char *data; + char *data; unsigned scm_len; int *scm; }; diff --git a/include/common/arch/arm/asm/atomic.h b/include/common/arch/arm/asm/atomic.h index 94e65cf3e..7998a20f2 100644 --- a/include/common/arch/arm/asm/atomic.h +++ b/include/common/arch/arm/asm/atomic.h @@ -29,9 +29,9 @@ static inline int atomic_cmpxchg(atomic_t *ptr, int old, int new) "teq %1, %4\n" "it eq\n" "strexeq %0, %5, [%3]\n" - : "=&r" (res), "=&r" (oldval), "+Qo" (ptr->counter) - : "r" (&ptr->counter), "Ir" (old), "r" (new) - : "cc"); + : "=&r" (res), "=&r" (oldval), "+Qo" (ptr->counter) + : "r" (&ptr->counter), "Ir" (old), "r" (new) + : "cc"); } while (res); smp_mb(); @@ -47,13 +47,13 @@ static inline int atomic_cmpxchg(atomic_t *ptr, int old, int new) static inline int atomic_cmpxchg(atomic_t *v, int old, int new) { - int ret; + int ret; - ret = v->counter; - if (ret == old) - v->counter = new; + ret = v->counter; + if (ret == old) + v->counter = new; - return ret; + return ret; } #else @@ -88,7 +88,7 @@ static inline int atomic_add_return(int i, atomic_t *v) " teq %1, #0\n" " bne 1b\n" : "=&r" (result), "=&r" (tmp), "+Qo" (v->counter) - : "r" (&v->counter), "Ir" (i) + : "r" (&v->counter), "Ir" (i) : "cc"); smp_mb(); diff --git a/include/common/arch/ppc64/asm/atomic.h b/include/common/arch/ppc64/asm/atomic.h index 461875c6e..4c6477412 100644 --- a/include/common/arch/ppc64/asm/atomic.h +++ b/include/common/arch/ppc64/asm/atomic.h @@ -8,7 +8,7 @@ */ typedef struct { - int counter; + int counter; } atomic_t; #include "common/arch/ppc64/asm/cmpxchg.h" diff --git a/include/common/arch/ppc64/asm/linkage.h b/include/common/arch/ppc64/asm/linkage.h index 506edc711..01a47ab1a 100644 --- a/include/common/arch/ppc64/asm/linkage.h +++ b/include/common/arch/ppc64/asm/linkage.h @@ -261,38 +261,38 @@ #define N_SLINE 68 #define N_SO 100 -#define __REG_R0 0 -#define __REG_R1 1 -#define __REG_R2 2 -#define __REG_R3 3 -#define __REG_R4 4 -#define __REG_R5 5 -#define __REG_R6 6 -#define __REG_R7 7 -#define __REG_R8 8 -#define __REG_R9 9 -#define __REG_R10 10 -#define __REG_R11 11 -#define __REG_R12 12 -#define __REG_R13 13 -#define __REG_R14 14 -#define __REG_R15 15 -#define __REG_R16 16 -#define __REG_R17 17 -#define __REG_R18 18 -#define __REG_R19 19 -#define __REG_R20 20 -#define __REG_R21 21 -#define __REG_R22 22 -#define __REG_R23 23 -#define __REG_R24 24 -#define __REG_R25 25 -#define __REG_R26 26 -#define __REG_R27 27 -#define __REG_R28 28 -#define __REG_R29 29 -#define __REG_R30 30 -#define __REG_R31 31 +#define __REG_R0 0 +#define __REG_R1 1 +#define __REG_R2 2 +#define __REG_R3 3 +#define __REG_R4 4 +#define __REG_R5 5 +#define __REG_R6 6 +#define __REG_R7 7 +#define __REG_R8 8 +#define __REG_R9 9 +#define __REG_R10 10 +#define __REG_R11 11 +#define __REG_R12 12 +#define __REG_R13 13 +#define __REG_R14 14 +#define __REG_R15 15 +#define __REG_R16 16 +#define __REG_R17 17 +#define __REG_R18 18 +#define __REG_R19 19 +#define __REG_R20 20 +#define __REG_R21 21 +#define __REG_R22 22 +#define __REG_R23 23 +#define __REG_R24 24 +#define __REG_R25 25 +#define __REG_R26 26 +#define __REG_R27 27 +#define __REG_R28 28 +#define __REG_R29 29 +#define __REG_R30 30 +#define __REG_R31 31 diff --git a/include/common/arch/x86/asm/cmpxchg.h b/include/common/arch/x86/asm/cmpxchg.h index 4b6951933..fa5eccf09 100644 --- a/include/common/arch/x86/asm/cmpxchg.h +++ b/include/common/arch/x86/asm/cmpxchg.h @@ -17,7 +17,7 @@ */ #define __xchg_op(ptr, arg, op, lock) \ ({ \ - __typeof__ (*(ptr)) __ret = (arg); \ + __typeof__ (*(ptr)) __ret = (arg); \ switch (sizeof(*(ptr))) { \ case __X86_CASE_B: \ asm volatile (lock #op "b %b0, %1\n" \ diff --git a/lib/c/criu.c b/lib/c/criu.c index 6ac510a87..9e36a9795 100644 --- a/lib/c/criu.c +++ b/lib/c/criu.c @@ -41,10 +41,10 @@ void criu_free_service(criu_opts *opts) case CRIU_COMM_SK: free((void*)(opts->service_address)); break; - case CRIU_COMM_BIN: + case CRIU_COMM_BIN: free((void*)(opts->service_binary)); break; - default: + default: break; } } diff --git a/soccr/soccr.c b/soccr/soccr.c index 1e4827e48..20eabfbd4 100644 --- a/soccr/soccr.c +++ b/soccr/soccr.c @@ -158,13 +158,13 @@ void libsoccr_release(struct libsoccr_sk *sk) } struct soccr_tcp_info { - __u8 tcpi_state; - __u8 tcpi_ca_state; - __u8 tcpi_retransmits; - __u8 tcpi_probes; - __u8 tcpi_backoff; - __u8 tcpi_options; - __u8 tcpi_snd_wscale : 4, tcpi_rcv_wscale : 4; + __u8 tcpi_state; + __u8 tcpi_ca_state; + __u8 tcpi_retransmits; + __u8 tcpi_probes; + __u8 tcpi_backoff; + __u8 tcpi_options; + __u8 tcpi_snd_wscale : 4, tcpi_rcv_wscale : 4; }; static int refresh_sk(struct libsoccr_sk *sk, From d50048a426ebde96a3bef22d5a856a62a3ec0cb9 Mon Sep 17 00:00:00 2001 From: hygonsoc Date: Mon, 15 Apr 2019 01:35:04 +0800 Subject: [PATCH 069/249] add Hygon CPU Vendor ID("HygonGenuine") checking in compel_cpuid() to enable Hygon Dhyana, which can reuse most AMD CPU support codes. Signed-off-by: hygonsoc --- compel/arch/x86/src/lib/cpu.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/compel/arch/x86/src/lib/cpu.c b/compel/arch/x86/src/lib/cpu.c index 9152765bf..617512167 100644 --- a/compel/arch/x86/src/lib/cpu.c +++ b/compel/arch/x86/src/lib/cpu.c @@ -269,7 +269,8 @@ int compel_cpuid(compel_cpuinfo_t *c) if (!strcmp(c->x86_vendor_id, "GenuineIntel")) { c->x86_vendor = X86_VENDOR_INTEL; - } else if (!strcmp(c->x86_vendor_id, "AuthenticAMD")) { + } else if (!strcmp(c->x86_vendor_id, "AuthenticAMD") || + !strcmp(c->x86_vendor_id, "HygonGenuine")) { c->x86_vendor = X86_VENDOR_AMD; } else { pr_err("Unsupported CPU vendor %s\n", From 692b854085040e606ed247fd6800ea76cd5c3865 Mon Sep 17 00:00:00 2001 From: Cyrill Gorcunov Date: Thu, 18 Apr 2019 12:57:15 +0300 Subject: [PATCH 070/249] sk-inet: udp -- Don't fail on calling shutdown on disconnected socket If socket has been connected and shutted down, it may get disconnected then leaving shutdown mode set inside (which we pull into image). On restore we should not fail when calling shutdown over -- the kernel has a hack to inform listeners even on closed sockets. From userspace perspective to reuse such socket one have to connect it back, so should be safe. Signed-off-by: Cyrill Gorcunov Signed-off-by: Andrei Vagin --- criu/sk-inet.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/criu/sk-inet.c b/criu/sk-inet.c index cc7e1cc28..60ee4c315 100644 --- a/criu/sk-inet.c +++ b/criu/sk-inet.c @@ -883,9 +883,14 @@ static int open_inet_sk(struct file_desc *d, int *new_fd) (ie->proto == IPPROTO_UDP || ie->proto == IPPROTO_UDPLITE)) { if (shutdown(sk, sk_decode_shutdown(ie->shutdown))) { - pr_perror("Can't shutdown socket into %d", - sk_decode_shutdown(ie->shutdown)); - goto err; + if (ie->state != TCP_CLOSE && errno != ENOTCONN) { + pr_perror("Can't shutdown socket into %d", + sk_decode_shutdown(ie->shutdown)); + goto err; + } else { + pr_debug("Called shutdown on closed socket, " + "proto %d ino %x", ie->proto, ie->ino); + } } } From 131c557cc8acbd0e020628e23b70dd7f00df1721 Mon Sep 17 00:00:00 2001 From: Cyrill Gorcunov Date: Thu, 18 Apr 2019 12:57:16 +0300 Subject: [PATCH 071/249] test: socket_udplite -- Test shudowned sockets Signed-off-by: Cyrill Gorcunov Signed-off-by: Andrei Vagin --- test/zdtm/static/socket_udplite.c | 60 +++++++++++++++++++++++++++++-- 1 file changed, 58 insertions(+), 2 deletions(-) diff --git a/test/zdtm/static/socket_udplite.c b/test/zdtm/static/socket_udplite.c index d2510ef2f..229005a10 100644 --- a/test/zdtm/static/socket_udplite.c +++ b/test/zdtm/static/socket_udplite.c @@ -28,9 +28,9 @@ static char buf[8]; int main(int argc, char **argv) { - int ret, sk1, sk2; + int ret, sk1, sk2, sk3, sk4; socklen_t len = sizeof(struct sockaddr_in); - struct sockaddr_in addr1, addr2, addr; + struct sockaddr_in addr1, addr2, addr3, addr4, addr; test_init(argc, argv); @@ -74,6 +74,62 @@ int main(int argc, char **argv) return 1; } + sk3 = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDPLITE); + if (sk3 < 0) { + pr_perror("Can't create socket"); + return 1; + } + + memset(&addr3, 0, sizeof(addr3)); + addr3.sin_family = AF_INET; + addr3.sin_addr.s_addr = inet_addr("127.0.0.1"); + addr3.sin_port = htons(port + 2); + + ret = bind(sk3, (struct sockaddr *)&addr3, len); + if (ret < 0) { + pr_perror("Can't bind socket"); + return 1; + } + + sk4 = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDPLITE); + if (sk4 < 0) { + pr_perror("Can't create socket"); + return 1; + } + + memset(&addr4, 0, sizeof(addr4)); + addr4.sin_family = AF_INET; + addr4.sin_addr.s_addr = inet_addr("0.0.0.0"); + addr4.sin_port = htons(0); + + ret = bind(sk4, (struct sockaddr *)&addr4, len); + if (ret < 0) { + pr_perror("Can't bind socket"); + return 1; + } + + ret = connect(sk4, (struct sockaddr *)&addr3, len); + if (ret < 0) { + pr_perror("Can't connect"); + return 1; + } + + ret = connect(sk3, (struct sockaddr *)&addr4, len); + if (ret < 0) { + pr_perror("Can't connect"); + return 1; + } + + if (shutdown(sk4, SHUT_RDWR)) { + pr_perror("Can't shutdown socket"); + return 1; + } + + if (shutdown(sk3, SHUT_RDWR)) { + pr_perror("Can't shutdown socket"); + return 1; + } + test_daemon(); test_waitsig(); From bcdd276003064c28620cbf221c6cb21e16d2fc86 Mon Sep 17 00:00:00 2001 From: Zhang Ning Date: Thu, 18 Apr 2019 10:13:58 +0800 Subject: [PATCH 072/249] criu/clone: stack size is too small for Android stack for clone is too small, child process will get wild pointer, and segfault. Error (criu/cr-restore.c:1418): 6082 killed by signal 11: Segmentation fault Error (criu/cr-restore.c:2303): Restoring FAILED. enlarge stack size to 1024, then no segfault. Cc: Chen Hu Signed-off-by: Zhang Ning Signed-off-by: Andrei Vagin --- criu/clone-noasan.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/criu/clone-noasan.c b/criu/clone-noasan.c index c44e71969..5ca280eb8 100644 --- a/criu/clone-noasan.c +++ b/criu/clone-noasan.c @@ -21,7 +21,7 @@ */ int clone_noasan(int (*fn)(void *), int flags, void *arg) { - void *stack_ptr = (void *)round_down((unsigned long)&stack_ptr - 256, 16); + void *stack_ptr = (void *)round_down((unsigned long)&stack_ptr - 1024, 16); BUG_ON((flags & CLONE_VM) && !(flags & CLONE_VFORK)); /* * Reserve some bytes for clone() internal needs From c8a597b68d3da87803bf1979ffa6c7901ef2822b Mon Sep 17 00:00:00 2001 From: Rikard Falkeborn Date: Sun, 28 Apr 2019 20:22:49 +0200 Subject: [PATCH 073/249] compel/s390: Fix memset sizeof sizeof(sizeof(x)) is the size of size_t. Instead use the size of the array to ensure the entire array is zeroed. Signed-off-by: Rikard Falkeborn Reviewed-by: Dmitry Safonov <0x7f454c46@gmail.com> Signed-off-by: Andrei Vagin --- compel/arch/s390/src/lib/infect.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/compel/arch/s390/src/lib/infect.c b/compel/arch/s390/src/lib/infect.c index b690b8122..fcb463fa8 100644 --- a/compel/arch/s390/src/lib/infect.c +++ b/compel/arch/s390/src/lib/infect.c @@ -148,10 +148,8 @@ int sigreturn_prep_regs_plain(struct rt_sigframe *sigframe, memcpy(&dst_ext->vxrs_high, &fpregs->vxrs_high, sizeof(fpregs->vxrs_high)); } else { - memset(&dst_ext->vxrs_low, 0, - sizeof(sizeof(fpregs->vxrs_low))); - memset(&dst_ext->vxrs_high, 0, - sizeof(sizeof(fpregs->vxrs_high))); + memset(&dst_ext->vxrs_low, 0, sizeof(dst_ext->vxrs_low)); + memset(&dst_ext->vxrs_high, 0, sizeof(dst_ext->vxrs_high)); } return 0; } From 725bd70ec1a283090b3417988afe3cdbf117502c Mon Sep 17 00:00:00 2001 From: Rikard Falkeborn Date: Sun, 28 Apr 2019 20:22:50 +0200 Subject: [PATCH 074/249] compel/s390: Fix return value in error path In a function with return type bool, returning a non-zero value is interpreted as returning true. In the error paths we want to return false to indicate failure. Change -1 to false to fix this. Signed-off-by: Rikard Falkeborn Reviewed-by: Dmitry Safonov <0x7f454c46@gmail.com> Signed-off-by: Andrei Vagin --- compel/arch/s390/src/lib/infect.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compel/arch/s390/src/lib/infect.c b/compel/arch/s390/src/lib/infect.c index fcb463fa8..00e9c36d2 100644 --- a/compel/arch/s390/src/lib/infect.c +++ b/compel/arch/s390/src/lib/infect.c @@ -493,7 +493,7 @@ bool arch_can_dump_task(struct parasite_ctl *ctl) if (psw->mask & PSW_MASK_RI) { if (get_ri_cb(pid, &fpregs) < 0) { pr_perror("Can't dump process with RI bit active"); - return -1; + return false; } } /* We don't support 24 and 31 bit mode - only 64 bit */ From 07e7c53723188dcb77bf1f6a3e10ca5a28990d98 Mon Sep 17 00:00:00 2001 From: Rikard Falkeborn Date: Sun, 28 Apr 2019 20:22:51 +0200 Subject: [PATCH 075/249] test/zdtm: Move assignment after return value check If read() fails we can not use the return value as index. Move the use of it to after the error check to avoid this. Signed-off-by: Rikard Falkeborn Reviewed-by: Dmitry Safonov <0x7f454c46@gmail.com> Signed-off-by: Andrei Vagin --- test/zdtm/lib/ns.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/zdtm/lib/ns.c b/test/zdtm/lib/ns.c index 6b4a75399..3099f7495 100644 --- a/test/zdtm/lib/ns.c +++ b/test/zdtm/lib/ns.c @@ -325,11 +325,11 @@ int ns_init(int argc, char **argv) exit(1); } ret = read(fd, buf, sizeof(buf) - 1); - buf[ret] = '\0'; if (ret == -1) { fprintf(stderr, "read() failed: %m\n"); exit(1); } + buf[ret] = '\0'; pid = atoi(buf); fprintf(stderr, "kill(%d, SIGTERM)\n", pid); From 59e1f1636ff7c86791305e01e05631d0e465d0a4 Mon Sep 17 00:00:00 2001 From: Rikard Falkeborn Date: Sun, 28 Apr 2019 20:22:52 +0200 Subject: [PATCH 076/249] test: remove unused variables Signed-off-by: Rikard Falkeborn Reviewed-by: Dmitry Safonov <0x7f454c46@gmail.com> Signed-off-by: Andrei Vagin --- test/others/bers/bers.c | 4 +--- test/others/libcriu/test_errno.c | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/test/others/bers/bers.c b/test/others/bers/bers.c index 208325c67..0954868ff 100644 --- a/test/others/bers/bers.c +++ b/test/others/bers/bers.c @@ -93,7 +93,6 @@ static int sys_gettid(void) static void dirtify_memory(unsigned long *chunks, size_t nr_chunks, size_t chunk_size, int mode, const size_t nr_pages) { - void *page; size_t i; pr_trace("filling memory\n"); @@ -115,7 +114,7 @@ static void dirtify_memory(unsigned long *chunks, size_t nr_chunks, static void dirtify_files(int *fd, size_t nr_files, size_t size) { size_t buf[8192]; - size_t i, j, c; + size_t i; /* * Note we don't write any _sane_ data here, the only @@ -265,7 +264,6 @@ int main(int argc, char *argv[]) char workdir[PATH_MAX]; int opt, idx, pidfd; char pidbuf[32]; - int status; pid_t pid; size_t i; diff --git a/test/others/libcriu/test_errno.c b/test/others/libcriu/test_errno.c index e09144304..8bd19fe2f 100644 --- a/test/others/libcriu/test_errno.c +++ b/test/others/libcriu/test_errno.c @@ -56,7 +56,7 @@ static int no_process(void) size_t len; ssize_t count; char *buf = NULL; - int pid, fd, ret; + int pid, ret; printf("--- Try to dump unexisting process\n"); From 4a4d3f72b2e7cfd6b43388506d627392afa6d245 Mon Sep 17 00:00:00 2001 From: Rikard Falkeborn Date: Sun, 28 Apr 2019 20:22:53 +0200 Subject: [PATCH 077/249] files-reg: Remove redundant inner if Remove a redundant if-statement, since the same condition is already checked in the outer if-statement. Signed-off-by: Rikard Falkeborn Reviewed-by: Dmitry Safonov <0x7f454c46@gmail.com> Signed-off-by: Andrei Vagin --- criu/files-reg.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/criu/files-reg.c b/criu/files-reg.c index b30da8c99..b7e043841 100644 --- a/criu/files-reg.c +++ b/criu/files-reg.c @@ -1745,12 +1745,10 @@ int open_path(struct file_desc *d, } if (rfi->rfe->has_mode && (st.st_mode != rfi->rfe->mode)) { - if (st.st_mode != rfi->rfe->mode) { - pr_err("File %s has bad mode 0%o (expect 0%o)\n", - rfi->path, (int)st.st_mode, - rfi->rfe->mode); - return -1; - } + pr_err("File %s has bad mode 0%o (expect 0%o)\n", + rfi->path, (int)st.st_mode, + rfi->rfe->mode); + return -1; } /* From b90c46f32103acb8c02cefbfd86d7caa49bec95c Mon Sep 17 00:00:00 2001 From: Rikard Falkeborn Date: Sun, 28 Apr 2019 20:22:54 +0200 Subject: [PATCH 078/249] test: add missing va_end Signed-off-by: Rikard Falkeborn Reviewed-by: Dmitry Safonov <0x7f454c46@gmail.com> Signed-off-by: Andrei Vagin --- test/zdtm/static/autofs.c | 1 + 1 file changed, 1 insertion(+) diff --git a/test/zdtm/static/autofs.c b/test/zdtm/static/autofs.c index c2e047714..f74bc35ac 100644 --- a/test/zdtm/static/autofs.c +++ b/test/zdtm/static/autofs.c @@ -49,6 +49,7 @@ static char *xvstrcat(char *str, const char *fmt, va_list args) if (new) { va_copy(tmp, args); ret = vsnprintf(new + offset, delta, fmt, tmp); + va_end(tmp); if (ret >= delta) { /* NOTE: vsnprintf returns the amount of bytes * * to allocate. */ From ec8e824ad5179bd71a981295cd57bd74cdff0fd2 Mon Sep 17 00:00:00 2001 From: Rikard Falkeborn Date: Sun, 28 Apr 2019 20:22:55 +0200 Subject: [PATCH 079/249] test/bers: Fix sizeof to memset sizeof(fd) is the size of the pointer. Make sure the entire array is set by using the number of elements times the size of the elements. Signed-off-by: Rikard Falkeborn Reviewed-by: Dmitry Safonov <0x7f454c46@gmail.com> Signed-off-by: Andrei Vagin --- test/others/bers/bers.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/others/bers/bers.c b/test/others/bers/bers.c index 0954868ff..90b70c349 100644 --- a/test/others/bers/bers.c +++ b/test/others/bers/bers.c @@ -138,7 +138,7 @@ static int create_files(shared_data_t *shared, int *fd, size_t nr_files) char path[PATH_MAX]; size_t i; - memset(fd, 0xff, sizeof(fd)); + memset(fd, 0xff, sizeof(*fd) * MAX_CHUNK); pr_info("\tCreating %lu files\n", shared->opt_files); From 10e87265121865ba233904d12ce7803d03d95848 Mon Sep 17 00:00:00 2001 From: Adrian Reber Date: Thu, 2 May 2019 13:41:46 +0000 Subject: [PATCH 080/249] lsm: also dump and restore sockcreate The file /proc/PID/attr/sockcreate is used by SELinux to label newly created sockets with the label available at sockcreate. If it is NULL, the default label of the process will be used. This reads out that file during checkpoint and restores the value during restore. This value is irrelevant for existing sockets as they might have been created with another context. This is only to make sure that newly created sockets have the correct context. Signed-off-by: Adrian Reber --- criu/cr-restore.c | 36 ++++++++++++++++++++++++++++++++++++ criu/include/restorer.h | 2 ++ criu/lsm.c | 32 ++++++++++++++++++++++++++++++++ criu/pie/restorer.c | 15 ++++++++++----- images/creds.proto | 1 + 5 files changed, 81 insertions(+), 5 deletions(-) diff --git a/criu/cr-restore.c b/criu/cr-restore.c index 1073a4069..b6274affb 100644 --- a/criu/cr-restore.c +++ b/criu/cr-restore.c @@ -3000,6 +3000,8 @@ static void rst_reloc_creds(struct thread_restore_args *thread_args, if (args->lsm_profile) args->lsm_profile = rst_mem_remap_ptr(args->mem_lsm_profile_pos, RM_PRIVATE); + if (args->lsm_sockcreate) + args->lsm_sockcreate = rst_mem_remap_ptr(args->mem_lsm_sockcreate_pos, RM_PRIVATE); if (args->groups) args->groups = rst_mem_remap_ptr(args->mem_groups_pos, RM_PRIVATE); @@ -3065,6 +3067,40 @@ rst_prep_creds_args(CredsEntry *ce, unsigned long *prev_pos) args->mem_lsm_profile_pos = 0; } + if (ce->lsm_sockcreate) { + char *rendered = NULL; + char *profile; + + profile = ce->lsm_sockcreate; + + if (validate_lsm(profile) < 0) + return ERR_PTR(-EINVAL); + + if (profile && render_lsm_profile(profile, &rendered)) { + return ERR_PTR(-EINVAL); + } + if (rendered) { + size_t lsm_sockcreate_len; + char *lsm_sockcreate; + + args->mem_lsm_sockcreate_pos = rst_mem_align_cpos(RM_PRIVATE); + lsm_sockcreate_len = strlen(rendered); + lsm_sockcreate = rst_mem_alloc(lsm_sockcreate_len + 1, RM_PRIVATE); + if (!lsm_sockcreate) { + xfree(rendered); + return ERR_PTR(-ENOMEM); + } + + args = rst_mem_remap_ptr(this_pos, RM_PRIVATE); + args->lsm_sockcreate = lsm_sockcreate; + strncpy(args->lsm_sockcreate, rendered, lsm_sockcreate_len); + xfree(rendered); + } + } else { + args->lsm_sockcreate = NULL; + args->mem_lsm_sockcreate_pos = 0; + } + /* * Zap fields which we can't use. */ diff --git a/criu/include/restorer.h b/criu/include/restorer.h index 2884ce9e6..b83e9130c 100644 --- a/criu/include/restorer.h +++ b/criu/include/restorer.h @@ -69,8 +69,10 @@ struct thread_creds_args { unsigned int secbits; char *lsm_profile; unsigned int *groups; + char *lsm_sockcreate; unsigned long mem_lsm_profile_pos; + unsigned long mem_lsm_sockcreate_pos; unsigned long mem_groups_pos; unsigned long mem_pos_next; diff --git a/criu/lsm.c b/criu/lsm.c index 849ec37cd..b0ef0c396 100644 --- a/criu/lsm.c +++ b/criu/lsm.c @@ -98,6 +98,32 @@ static int selinux_get_label(pid_t pid, char **output) freecon(ctx); return ret; } + +/* + * selinux_get_sockcreate_label reads /proc/PID/attr/sockcreate + * to see if the PID has a special label specified for sockets. + * Most of the time this will be empty and the process will use + * the process context also for sockets. + */ +static int selinux_get_sockcreate_label(pid_t pid, char **output) +{ + FILE *f; + + f = fopen_proc(pid, "attr/sockcreate"); + if (!f) + return -1; + + fscanf(f, "%ms", output); + /* + * No need to check the result of fscanf(). If there is something + * in /proc/PID/attr/sockcreate it will be copied to *output. If + * there is nothing it will stay NULL. So whatever fscanf() does + * it should be correct. + */ + + fclose(f); + return 0; +} #endif void kerndat_lsm(void) @@ -132,6 +158,7 @@ int collect_lsm_profile(pid_t pid, CredsEntry *ce) int ret; ce->lsm_profile = NULL; + ce->lsm_sockcreate = NULL; switch (kdat.lsm) { case LSMTYPE__NO_LSM: @@ -143,6 +170,9 @@ int collect_lsm_profile(pid_t pid, CredsEntry *ce) #ifdef CONFIG_HAS_SELINUX case LSMTYPE__SELINUX: ret = selinux_get_label(pid, &ce->lsm_profile); + if (ret) + break; + ret = selinux_get_sockcreate_label(pid, &ce->lsm_sockcreate); break; #endif default: @@ -153,6 +183,8 @@ int collect_lsm_profile(pid_t pid, CredsEntry *ce) if (ce->lsm_profile) pr_info("%d has lsm profile %s\n", pid, ce->lsm_profile); + if (ce->lsm_sockcreate) + pr_info("%d has lsm sockcreate label %s\n", pid, ce->lsm_sockcreate); return ret; } diff --git a/criu/pie/restorer.c b/criu/pie/restorer.c index 6e18cc260..4f42605a0 100644 --- a/criu/pie/restorer.c +++ b/criu/pie/restorer.c @@ -149,7 +149,7 @@ static void sigchld_handler(int signal, siginfo_t *siginfo, void *data) sys_exit_group(1); } -static int lsm_set_label(char *label, int procfd) +static int lsm_set_label(char *label, char *type, int procfd) { int ret = -1, len, lsmfd; char path[STD_LOG_SIMPLE_CHUNK]; @@ -157,9 +157,9 @@ static int lsm_set_label(char *label, int procfd) if (!label) return 0; - pr_info("restoring lsm profile %s\n", label); + pr_info("restoring lsm profile (%s) %s\n", type, label); - std_sprintf(path, "self/task/%ld/attr/current", sys_gettid()); + std_sprintf(path, "self/task/%ld/attr/%s", sys_gettid(), type); lsmfd = sys_openat(procfd, path, O_WRONLY, 0); if (lsmfd < 0) { @@ -305,9 +305,14 @@ static int restore_creds(struct thread_creds_args *args, int procfd, * SELinux and instead the process context is set before the * threads are created. */ - if (lsm_set_label(args->lsm_profile, procfd) < 0) + if (lsm_set_label(args->lsm_profile, "current", procfd) < 0) return -1; } + + /* Also set the sockcreate label for all threads */ + if (lsm_set_label(args->lsm_sockcreate, "sockcreate", procfd) < 0) + return -1; + return 0; } @@ -1571,7 +1576,7 @@ long __export_restore_task(struct task_restore_args *args) if (args->lsm_type == LSMTYPE__SELINUX) { /* Only for SELinux */ if (lsm_set_label(args->t->creds_args->lsm_profile, - args->proc_fd) < 0) + "current", args->proc_fd) < 0) goto core_restore_end; } diff --git a/images/creds.proto b/images/creds.proto index 29fb8652e..23b84c7e5 100644 --- a/images/creds.proto +++ b/images/creds.proto @@ -20,4 +20,5 @@ message creds_entry { repeated uint32 groups = 14; optional string lsm_profile = 15; + optional string lsm_sockcreate = 16; } From c421f747c3355860f0687f74043fdac9655c4838 Mon Sep 17 00:00:00 2001 From: Adrian Reber Date: Thu, 2 May 2019 13:47:29 +0000 Subject: [PATCH 081/249] test: Verify that sockcreate does not change during restore This makes sure that sockcreate stays empty for selinux00 before and after checkpoint/restore. Signed-off-by: Adrian Reber --- test/zdtm/static/selinux00.c | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/test/zdtm/static/selinux00.c b/test/zdtm/static/selinux00.c index dd9096a6f..db8420eac 100644 --- a/test/zdtm/static/selinux00.c +++ b/test/zdtm/static/selinux00.c @@ -83,6 +83,31 @@ int checkprofile() return 0; } +int check_sockcreate() +{ + char *output = NULL; + FILE *f = fopen("/proc/self/attr/sockcreate", "r"); + int ret = fscanf(f, "%ms", &output); + fclose(f); + + if (ret >= 1) { + free(output); + /* sockcreate should be empty, if fscanf found something + * it is wrong.*/ + fail("sockcreate should be empty\n"); + return -1; + } + + if (output) { + free(output); + /* Same here, output should still be NULL. */ + fail("sockcreate should be empty\n"); + return -1; + } + + return 0; +} + int main(int argc, char **argv) { test_init(argc, argv); @@ -95,12 +120,21 @@ int main(int argc, char **argv) return 0; } + if (check_sockcreate()) + return -1; + if (setprofile()) return -1; + if (check_sockcreate()) + return -1; + test_daemon(); test_waitsig(); + if (check_sockcreate()) + return -1; + if (checkprofile() == 0) pass(); From b58160e3f2dbdf593ed3911fe3acf782578e8a30 Mon Sep 17 00:00:00 2001 From: Adrian Reber Date: Mon, 29 Apr 2019 15:21:59 +0200 Subject: [PATCH 082/249] sockets: dump and restore xattr security labels Restoring a SELinux process also requires to correctly label sockets. During checkpointing fgetxattr() is used to retrieve the "security.selinux" xattr and during restore setsockcreatecon() is used before a socket is created. Previous commits are already restoring the sockcreate SELinux setting if set by the process. Signed-off-by: Adrian Reber --- criu/include/lsm.h | 18 +++++++++++++++ criu/lsm.c | 56 +++++++++++++++++++++++++++++++++++++++++++++ criu/sk-inet.c | 12 ++++++++++ criu/sockets.c | 4 ++++ images/fdinfo.proto | 1 + 5 files changed, 91 insertions(+) diff --git a/criu/include/lsm.h b/criu/include/lsm.h index b4fce1303..3b8271282 100644 --- a/criu/include/lsm.h +++ b/criu/include/lsm.h @@ -3,6 +3,7 @@ #include "images/inventory.pb-c.h" #include "images/creds.pb-c.h" +#include "images/fdinfo.pb-c.h" #define AA_SECURITYFS_PATH "/sys/kernel/security/apparmor" @@ -34,4 +35,21 @@ int validate_lsm(char *profile); int render_lsm_profile(char *profile, char **val); extern int lsm_check_opts(void); + +#ifdef CONFIG_HAS_SELINUX +int dump_xattr_security_selinux(int fd, FdinfoEntry *e); +int run_setsockcreatecon(FdinfoEntry *e); +int reset_setsockcreatecon(); +#else +static inline int dump_xattr_security_selinux(int fd, FdinfoEntry *e) { + return 0; +} +static inline int run_setsockcreatecon(FdinfoEntry *e) { + return 0; +} +static inline int reset_setsockcreatecon() { + return 0; +} +#endif + #endif /* __CR_LSM_H__ */ diff --git a/criu/lsm.c b/criu/lsm.c index b0ef0c396..ef6ba112b 100644 --- a/criu/lsm.c +++ b/criu/lsm.c @@ -3,6 +3,7 @@ #include #include #include +#include #include #include "common/config.h" @@ -11,10 +12,12 @@ #include "util.h" #include "cr_options.h" #include "lsm.h" +#include "fdstore.h" #include "protobuf.h" #include "images/inventory.pb-c.h" #include "images/creds.pb-c.h" +#include "images/fdinfo.pb-c.h" #ifdef CONFIG_HAS_SELINUX #include @@ -124,6 +127,59 @@ static int selinux_get_sockcreate_label(pid_t pid, char **output) fclose(f); return 0; } + +int reset_setsockcreatecon() +{ + return setsockcreatecon_raw(NULL); +} + +int run_setsockcreatecon(FdinfoEntry *e) +{ + char *ctx = NULL; + + /* Currently this only works for SELinux. */ + if (kdat.lsm != LSMTYPE__SELINUX) + return 0; + + ctx = e->xattr_security_selinux; + /* Writing to the FD using fsetxattr() did not work for some reason. */ + return setsockcreatecon_raw(ctx); +} + +int dump_xattr_security_selinux(int fd, FdinfoEntry *e) +{ + char *ctx = NULL; + int len; + int ret; + + /* Currently this only works for SELinux. */ + if (kdat.lsm != LSMTYPE__SELINUX) + return 0; + + /* Get the size of the xattr. */ + len = fgetxattr(fd, "security.selinux", ctx, 0); + if (len == -1) { + pr_err("Reading xattr %s to FD %d failed\n", ctx, fd); + return -1; + } + + ctx = xmalloc(len); + if (!ctx) { + pr_err("xmalloc to read xattr for FD %d failed\n", fd); + return -1; + } + + ret = fgetxattr(fd, "security.selinux", ctx, len); + if (len != ret) { + pr_err("Reading xattr %s to FD %d failed\n", ctx, fd); + return -1; + } + + e->xattr_security_selinux = ctx; + + return 0; +} + #endif void kerndat_lsm(void) diff --git a/criu/sk-inet.c b/criu/sk-inet.c index 60ee4c315..ca5c9bf2c 100644 --- a/criu/sk-inet.c +++ b/criu/sk-inet.c @@ -23,6 +23,9 @@ #include "files.h" #include "image.h" #include "log.h" +#include "lsm.h" +#include "kerndat.h" +#include "pstree.h" #include "rst-malloc.h" #include "sockets.h" #include "sk-inet.h" @@ -30,6 +33,8 @@ #include "util.h" #include "namespaces.h" +#include "images/inventory.pb-c.h" + #undef LOG_PREFIX #define LOG_PREFIX "inet: " @@ -804,12 +809,18 @@ static int open_inet_sk(struct file_desc *d, int *new_fd) if (set_netns(ie->ns_id)) return -1; + if (run_setsockcreatecon(fle->fe)) + return -1; + sk = socket(ie->family, ie->type, ie->proto); if (sk < 0) { pr_perror("Can't create inet socket"); return -1; } + if (reset_setsockcreatecon()) + return -1; + if (ie->v6only) { if (restore_opt(sk, SOL_IPV6, IPV6_V6ONLY, &yes) == -1) goto err; @@ -895,6 +906,7 @@ static int open_inet_sk(struct file_desc *d, int *new_fd) } *new_fd = sk; + return 1; err: close(sk); diff --git a/criu/sockets.c b/criu/sockets.c index 30072ac73..7f7453ca1 100644 --- a/criu/sockets.c +++ b/criu/sockets.c @@ -22,6 +22,7 @@ #include "util-pie.h" #include "sk-packet.h" #include "namespaces.h" +#include "lsm.h" #include "net.h" #include "xmalloc.h" #include "fs-magic.h" @@ -663,6 +664,9 @@ int dump_socket(struct fd_parms *p, int lfd, FdinfoEntry *e) int family; const struct fdtype_ops *ops; + if (dump_xattr_security_selinux(lfd, e)) + return -1; + if (dump_opt(lfd, SOL_SOCKET, SO_DOMAIN, &family)) return -1; diff --git a/images/fdinfo.proto b/images/fdinfo.proto index ed82ceffe..77e375aa9 100644 --- a/images/fdinfo.proto +++ b/images/fdinfo.proto @@ -47,6 +47,7 @@ message fdinfo_entry { required uint32 flags = 2; required fd_types type = 3; required uint32 fd = 4; + optional string xattr_security_selinux = 5; } message file_entry { From 28723e416810778bdd14830f5362228f3637af23 Mon Sep 17 00:00:00 2001 From: Adrian Reber Date: Tue, 30 Apr 2019 09:47:32 +0000 Subject: [PATCH 083/249] selinux: add socket label test This adds two more SELinux test to verfy that checkpointing and restoring SELinux socket labels works correctly, if the process uses setsockcreatecon() or if the process leaves the default context for newly created sockets. Signed-off-by: Adrian Reber --- test/zdtm/static/Makefile | 3 + test/zdtm/static/selinux01.c | 200 +++++++++++++++++++++++++++ test/zdtm/static/selinux01.checkskip | 1 + test/zdtm/static/selinux01.desc | 1 + test/zdtm/static/selinux01.hook | 1 + test/zdtm/static/selinux02.c | 1 + test/zdtm/static/selinux02.checkskip | 1 + test/zdtm/static/selinux02.desc | 1 + test/zdtm/static/selinux02.hook | 1 + 9 files changed, 210 insertions(+) create mode 100644 test/zdtm/static/selinux01.c create mode 120000 test/zdtm/static/selinux01.checkskip create mode 120000 test/zdtm/static/selinux01.desc create mode 120000 test/zdtm/static/selinux01.hook create mode 120000 test/zdtm/static/selinux02.c create mode 120000 test/zdtm/static/selinux02.checkskip create mode 120000 test/zdtm/static/selinux02.desc create mode 120000 test/zdtm/static/selinux02.hook diff --git a/test/zdtm/static/Makefile b/test/zdtm/static/Makefile index 8e3f39276..1ffaa9039 100644 --- a/test/zdtm/static/Makefile +++ b/test/zdtm/static/Makefile @@ -211,6 +211,8 @@ TST_NOFILE := \ thp_disable \ pid_file \ selinux00 \ + selinux01 \ + selinux02 \ # jobctl00 \ ifneq ($(SRCARCH),arm) @@ -513,6 +515,7 @@ unlink_fstat041: CFLAGS += -DUNLINK_FSTAT041 -DUNLINK_FSTAT04 ghost_holes01: CFLAGS += -DTAIL_HOLE ghost_holes02: CFLAGS += -DHEAD_HOLE sk-freebind-false: CFLAGS += -DZDTM_FREEBIND_FALSE +selinux02: CFLAGS += -DUSING_SOCKCREATE stopped01: CFLAGS += -DZDTM_STOPPED_KILL stopped02: CFLAGS += -DZDTM_STOPPED_TKILL stopped12: CFLAGS += -DZDTM_STOPPED_KILL -DZDTM_STOPPED_TKILL diff --git a/test/zdtm/static/selinux01.c b/test/zdtm/static/selinux01.c new file mode 100644 index 000000000..9966455c4 --- /dev/null +++ b/test/zdtm/static/selinux01.c @@ -0,0 +1,200 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "zdtmtst.h" + +/* Enabling the right policy happens in selinux00.hook and selinx00.checkskip */ + +const char *test_doc = "Check that a SELinux socket context is restored"; +const char *test_author = "Adrian Reber "; + +/* This is all based on Tycho's apparmor code */ + +#define CONTEXT "unconfined_u:unconfined_r:unconfined_dbusd_t:s0" + +/* + * This is used to store the state of SELinux. For this test + * SELinux is switched to permissive mode and later the previous + * SELinux state is restored. + */ +char state; + +int check_for_selinux() +{ + if (access("/sys/fs/selinux", F_OK) == 0) + return 0; + return 1; +} + +int setprofile() +{ + int fd, len; + + fd = open("/proc/self/attr/current", O_WRONLY); + if (fd < 0) { + fail("Could not open /proc/self/attr/current\n"); + return -1; + } + + len = write(fd, CONTEXT, strlen(CONTEXT)); + close(fd); + + if (len < 0) { + fail("Could not write context\n"); + return -1; + } + + return 0; +} + +int set_sockcreate() +{ + int fd, len; + + fd = open("/proc/self/attr/sockcreate", O_WRONLY); + if (fd < 0) { + fail("Could not open /proc/self/attr/sockcreate\n"); + return -1; + } + + len = write(fd, CONTEXT, strlen(CONTEXT)); + close(fd); + + if (len < 0) { + fail("Could not write context\n"); + return -1; + } + + return 0; +} + +int check_sockcreate() +{ + int fd; + char context[1024]; + int len; + + + fd = open("/proc/self/attr/sockcreate", O_RDONLY); + if (fd < 0) { + fail("Could not open /proc/self/attr/sockcreate\n"); + return -1; + } + + len = read(fd, context, strlen(CONTEXT)); + close(fd); + if (len != strlen(CONTEXT)) { + fail("SELinux context has unexpected length %d, expected %zd\n", + len, strlen(CONTEXT)); + return -1; + } + + if (strncmp(context, CONTEXT, strlen(CONTEXT)) != 0) { + fail("Wrong SELinux context %s expected %s\n", context, CONTEXT); + return -1; + } + + return 0; +} + +int check_sockcreate_empty() +{ + char *output = NULL; + FILE *f = fopen("/proc/self/attr/sockcreate", "r"); + int ret = fscanf(f, "%ms", &output); + fclose(f); + + if (ret >= 1) { + free(output); + /* sockcreate should be empty, if fscanf found something + * it is wrong.*/ + fail("sockcreate should be empty\n"); + return -1; + } + + if (output) { + free(output); + /* Same here, output should still be NULL. */ + fail("sockcreate should be empty\n"); + return -1; + } + + return 0; +} + +int main(int argc, char **argv) +{ + char ctx[1024]; + test_init(argc, argv); + + if (check_for_selinux()) { + skip("SELinux not found on this system."); + test_daemon(); + test_waitsig(); + pass(); + return 0; + } + +#ifdef USING_SOCKCREATE + if (set_sockcreate()) + return -1; +#else + if (check_sockcreate_empty()) + return -1; + + if (setprofile()) + return -1; + + if (check_sockcreate_empty()) + return -1; +#endif + + /* Open our test socket */ + int sk = socket(AF_INET, SOCK_STREAM, 0); + memset(ctx, 0, 1024); + /* Read out the socket label */ + if (fgetxattr(sk, "security.selinux", ctx, 1024) == -1) { + fail("Reading xattr 'security.selinux' failed.\n"); + return -1; + } + if (strncmp(ctx, CONTEXT, strlen(CONTEXT)) != 0) { + fail("Wrong SELinux context %s expected %s\n", ctx, CONTEXT); + return -1; + } + memset(ctx, 0, 1024); + + test_daemon(); + test_waitsig(); + + /* Read out the socket label again */ + + if (fgetxattr(sk, "security.selinux", ctx, 1024) == -1) { + fail("Reading xattr 'security.selinux' failed.\n"); + return -1; + } + if (strncmp(ctx, CONTEXT, strlen(CONTEXT)) != 0) { + fail("Wrong SELinux context %s expected %s\n", ctx, CONTEXT); + return -1; + } + +#ifdef USING_SOCKCREATE + if (check_sockcreate()) + return -1; +#else + if (check_sockcreate_empty()) + return -1; +#endif + + pass(); + + return 0; +} diff --git a/test/zdtm/static/selinux01.checkskip b/test/zdtm/static/selinux01.checkskip new file mode 120000 index 000000000..e8a172479 --- /dev/null +++ b/test/zdtm/static/selinux01.checkskip @@ -0,0 +1 @@ +selinux00.checkskip \ No newline at end of file diff --git a/test/zdtm/static/selinux01.desc b/test/zdtm/static/selinux01.desc new file mode 120000 index 000000000..2d2961a76 --- /dev/null +++ b/test/zdtm/static/selinux01.desc @@ -0,0 +1 @@ +selinux00.desc \ No newline at end of file diff --git a/test/zdtm/static/selinux01.hook b/test/zdtm/static/selinux01.hook new file mode 120000 index 000000000..dd7ed6bb3 --- /dev/null +++ b/test/zdtm/static/selinux01.hook @@ -0,0 +1 @@ +selinux00.hook \ No newline at end of file diff --git a/test/zdtm/static/selinux02.c b/test/zdtm/static/selinux02.c new file mode 120000 index 000000000..570267785 --- /dev/null +++ b/test/zdtm/static/selinux02.c @@ -0,0 +1 @@ +selinux01.c \ No newline at end of file diff --git a/test/zdtm/static/selinux02.checkskip b/test/zdtm/static/selinux02.checkskip new file mode 120000 index 000000000..2696e6e3d --- /dev/null +++ b/test/zdtm/static/selinux02.checkskip @@ -0,0 +1 @@ +selinux01.checkskip \ No newline at end of file diff --git a/test/zdtm/static/selinux02.desc b/test/zdtm/static/selinux02.desc new file mode 120000 index 000000000..9c6802c4d --- /dev/null +++ b/test/zdtm/static/selinux02.desc @@ -0,0 +1 @@ +selinux01.desc \ No newline at end of file diff --git a/test/zdtm/static/selinux02.hook b/test/zdtm/static/selinux02.hook new file mode 120000 index 000000000..e3ea0a6c8 --- /dev/null +++ b/test/zdtm/static/selinux02.hook @@ -0,0 +1 @@ +selinux01.hook \ No newline at end of file From 26d21236250b1951e9a0aa2a92e48710dde22d6e Mon Sep 17 00:00:00 2001 From: Dmitry Safonov Date: Thu, 2 May 2019 02:34:41 +0100 Subject: [PATCH 084/249] zdtm/ia32: fcntl() wrapper for old glibc(s) A bit nasty, but does the job to run ofd tests on glibc < v2.28. Other way would be to update glibc on Travis-CI ia32 tests, but I thought someone might want to run the tests outside Travis-CI. Signed-off-by: Dmitry Safonov Signed-off-by: Andrei Vagin --- test/zdtm/static/file_locks06.c | 2 +- test/zdtm/static/file_locks07.c | 2 +- test/zdtm/static/file_locks08.c | 2 +- test/zdtm/static/ofd_file_locks.c | 60 +++++++++++++++++++++++++++++-- test/zdtm/static/ofd_file_locks.h | 1 + 5 files changed, 62 insertions(+), 5 deletions(-) diff --git a/test/zdtm/static/file_locks06.c b/test/zdtm/static/file_locks06.c index 9bc70c47d..780fb07ea 100644 --- a/test/zdtm/static/file_locks06.c +++ b/test/zdtm/static/file_locks06.c @@ -26,7 +26,7 @@ int init_lock(int *fd, struct flock *lck) lck->l_len = 0; lck->l_pid = 0; - if (fcntl(*fd, F_OFD_SETLK, lck) < 0) { + if (zdtm_fcntl(*fd, F_OFD_SETLK, lck) < 0) { pr_perror("Can't set ofd lock"); return -1; } diff --git a/test/zdtm/static/file_locks07.c b/test/zdtm/static/file_locks07.c index b36f23011..2fe169fcf 100644 --- a/test/zdtm/static/file_locks07.c +++ b/test/zdtm/static/file_locks07.c @@ -45,7 +45,7 @@ int init_file_locks(void) } for (i = 0; i < FILE_NUM; ++i) - if (fcntl(fds[i], F_OFD_SETLKW, &lcks[i]) < 0) { + if (zdtm_fcntl(fds[i], F_OFD_SETLKW, &lcks[i]) < 0) { pr_perror("Can't set ofd lock"); return -1; } diff --git a/test/zdtm/static/file_locks08.c b/test/zdtm/static/file_locks08.c index 2d25b4b09..fea8d9e7e 100644 --- a/test/zdtm/static/file_locks08.c +++ b/test/zdtm/static/file_locks08.c @@ -28,7 +28,7 @@ int init_file_lock(int *fd, struct flock *lck) lck->l_len = 0; /* lock whole file */ lck->l_pid = 0; /* should be 0 for ofd lock */ - if (fcntl(*fd, F_OFD_SETLKW, lck) < 0) { + if (zdtm_fcntl(*fd, F_OFD_SETLKW, lck) < 0) { pr_perror("Can't set ofd lock"); return -1; } diff --git a/test/zdtm/static/ofd_file_locks.c b/test/zdtm/static/ofd_file_locks.c index c4a633625..5b19532f8 100644 --- a/test/zdtm/static/ofd_file_locks.c +++ b/test/zdtm/static/ofd_file_locks.c @@ -86,7 +86,7 @@ int check_lock_exists(const char *filename, struct flock *lck) if (lck->l_type == F_RDLCK) { /* check, that there is no write lock */ - ret = fcntl(fd, F_OFD_GETLK, lck); + ret = zdtm_fcntl(fd, F_OFD_GETLK, lck); if (ret) { pr_err("fcntl failed (%i)\n", ret); goto out; @@ -99,7 +99,7 @@ int check_lock_exists(const char *filename, struct flock *lck) /* check, that lock is set */ lck->l_type = F_WRLCK; - ret = fcntl(fd, F_OFD_GETLK, lck); + ret = zdtm_fcntl(fd, F_OFD_GETLK, lck); if (ret) { pr_err("fcntl failed (%i)\n", ret); goto out; @@ -136,3 +136,59 @@ int check_file_lock_restored(int pid, int fd, struct flock *lck) } return 0; } + +/* + * fcntl() wrapper for ofd locks. + * + * Kernel requires ia32 processes to use fcntl64() syscall for ofd: + * COMPAT_SYSCALL_DEFINE3(fcntl, [..]) + * { + * switch (cmd) { + * case F_GETLK64: + * case F_SETLK64: + * case F_SETLKW64: + * case F_OFD_GETLK: + * case F_OFD_SETLK: + * case F_OFD_SETLKW: + * return -EINVAL; + * } + * + * Glibc does all the needed wraps for fcntl(), but only from v2.28. + * To make ofd tests run on the older glibc's - provide zdtm wrap. + * + * Note: we don't need the wraps in CRIU itself as parasite/restorer + * run in 64-bit mode as long as possible, including the time to play + * with ofd (and they are dumped from CRIU). + */ +int zdtm_fcntl(int fd, int cmd, struct flock *f) +{ +#if defined(__i386__) +#ifndef __NR_fcntl64 +# define __NR_fcntl64 221 +#endif + struct flock64 f64 = {}; + int ret; + + switch (cmd) { + case F_OFD_SETLK: + case F_OFD_SETLKW: + f64.l_type = f->l_type; + f64.l_whence = f->l_whence; + f64.l_start = f->l_start; + f64.l_len = f->l_len; + f64.l_pid = f->l_pid; + return syscall(__NR_fcntl64, fd, cmd, &f64); + case F_OFD_GETLK: + ret = syscall(__NR_fcntl64, fd, cmd, &f64); + f->l_type = f64.l_type; + f->l_whence = f64.l_whence; + f->l_start = f64.l_start; + f->l_len = f64.l_len; + f->l_pid = f64.l_pid; + return ret; + default: + break; + } +#endif + return fcntl(fd, cmd, f); +} diff --git a/test/zdtm/static/ofd_file_locks.h b/test/zdtm/static/ofd_file_locks.h index 6978446df..1b206a238 100644 --- a/test/zdtm/static/ofd_file_locks.h +++ b/test/zdtm/static/ofd_file_locks.h @@ -16,5 +16,6 @@ extern int check_lock_exists(const char *filename, struct flock *lck); extern int check_file_lock_restored(int pid, int fd, struct flock *lck); +extern int zdtm_fcntl(int fd, int cmd, struct flock *f); #endif /* ZDTM_OFD_FILE_LOCKS_H_ */ From b45f621ec550502d13992f80ffa0d5cd6027e409 Mon Sep 17 00:00:00 2001 From: Dmitry Safonov Date: Thu, 2 May 2019 02:34:42 +0100 Subject: [PATCH 085/249] zdtm/vdso/ia32: Use uint64_t for /proc/self/maps Add some comments to state things those might be not obvious. Signed-off-by: Dmitry Safonov Signed-off-by: Andrei Vagin --- test/zdtm/static/vdso-proxy.c | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/test/zdtm/static/vdso-proxy.c b/test/zdtm/static/vdso-proxy.c index 2381127b7..66d6741f4 100644 --- a/test/zdtm/static/vdso-proxy.c +++ b/test/zdtm/static/vdso-proxy.c @@ -1,3 +1,4 @@ +#include #include #include @@ -8,6 +9,10 @@ const char *test_author = "Dmitry Safonov "; #define BUILD_BUG_ON(condition) ((void)sizeof(char[1 - 2*!!(condition)])) #define VDSO_BAD_ADDR (-1ul) +/* + * Use constant MAX_VMAS - to minimize the risk of allocating a new + * mapping or changing the size of existent VMA with realloc() + */ #define MAX_VMAS 80 #define BUF_SIZE 1024 @@ -18,8 +23,13 @@ const char *test_author = "Dmitry Safonov "; * Also previous vdso/vvar vma should still be present after C/R. */ struct vm_area { - unsigned long start; - unsigned long end; + /* + * Intentionally use 64bit integer to make sure that it's possible + * to parse mappings >4Gb - those might appear on ia32 + * that's restored by x86_64 CRIU ¯\(°_o)/¯ + */ + uint64_t start; + uint64_t end; bool is_vvar_or_vdso; }; @@ -43,11 +53,12 @@ static int parse_maps(struct vm_area *vmas) if (fgets(buf, BUF_SIZE, maps) == NULL) break; - v->start = strtoul(buf, &end, 16); - v->end = strtoul(end + 1, NULL, 16); + v->start = strtoull(buf, &end, 16); + v->end = strtoull(end + 1, NULL, 16); v->is_vvar_or_vdso |= strstr(buf, "[vdso]") != NULL; v->is_vvar_or_vdso |= strstr(buf, "[vvar]") != NULL; - test_msg("[NOTE]\tVMA: [%#lx, %#lx]\n", v->start, v->end); + test_msg("[NOTE]\tVMA: [%#" PRIx64 ", %#" PRIx64 "]\n", + v->start, v->end); } if (i == MAX_VMAS) { @@ -88,7 +99,7 @@ static int check_vvar_vdso(struct vm_area *before, struct vm_area *after) continue; if (cmp < 0) {/* Lost mapping */ - test_msg("[NOTE]\tLost mapping: %#lx-%#lx\n", + test_msg("[NOTE]\tLost mapping: %#" PRIx64 "-%#" PRIx64 "\n", before[i].start, before[i].end); j--; if (before[i].is_vvar_or_vdso) { @@ -98,7 +109,7 @@ static int check_vvar_vdso(struct vm_area *before, struct vm_area *after) continue; } - test_msg("[NOTE]\tNew mapping appeared: %#lx-%#lx\n", + test_msg("[NOTE]\tNew mapping appeared: %#" PRIx64 "-%#" PRIx64 "\n", after[j].start, after[j].end); i--; } From 59c85752bdcbf9848d0dbc2b06f53d16e0dac3b9 Mon Sep 17 00:00:00 2001 From: Dmitry Safonov Date: Thu, 2 May 2019 02:34:43 +0100 Subject: [PATCH 086/249] zdtm/vdso/ia32: Ignore vsyscall page appear Not a major bummer. On the other side, it's also becomes less important as it seems that distribution switches from LEGACY_VSYSCALL_EMULATE to LEGACY_VSYSCALL_NONE (by security reasons). Might be not worth fixing at all in the end. Signed-off-by: Dmitry Safonov Signed-off-by: Andrei Vagin --- test/zdtm/static/vdso-proxy.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/test/zdtm/static/vdso-proxy.c b/test/zdtm/static/vdso-proxy.c index 66d6741f4..ecb71e892 100644 --- a/test/zdtm/static/vdso-proxy.c +++ b/test/zdtm/static/vdso-proxy.c @@ -8,7 +8,7 @@ const char *test_doc = "Compare mappings before/after C/R for vdso/vvar presence const char *test_author = "Dmitry Safonov "; #define BUILD_BUG_ON(condition) ((void)sizeof(char[1 - 2*!!(condition)])) -#define VDSO_BAD_ADDR (-1ul) +#define VSYSCALL_START 0xffffffffff600000ULL /* * Use constant MAX_VMAS - to minimize the risk of allocating a new * mapping or changing the size of existent VMA with realloc() @@ -55,6 +55,18 @@ static int parse_maps(struct vm_area *vmas) v->start = strtoull(buf, &end, 16); v->end = strtoull(end + 1, NULL, 16); + +#if defined(__i386__) + /* + * XXX: ia32 is being restored from x86_64 and leaves + * emulated vsyscall "mapping". Hopefully, will be done + * per-process, ignore for now. + */ + if (v->start == VSYSCALL_START) { + i--; + continue; + } +#endif v->is_vvar_or_vdso |= strstr(buf, "[vdso]") != NULL; v->is_vvar_or_vdso |= strstr(buf, "[vvar]") != NULL; test_msg("[NOTE]\tVMA: [%#" PRIx64 ", %#" PRIx64 "]\n", From 4aa952102b30d0df5e5d9e2f42951d9874afb5ea Mon Sep 17 00:00:00 2001 From: Dmitry Safonov Date: Thu, 2 May 2019 02:34:44 +0100 Subject: [PATCH 087/249] x86/compel/infect: Be verbose on remote mmap failure Error-case print missing. Signed-off-by: Dmitry Safonov Signed-off-by: Andrei Vagin --- compel/arch/x86/src/lib/infect.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/compel/arch/x86/src/lib/infect.c b/compel/arch/x86/src/lib/infect.c index 0737e07a3..11e7f4c91 100644 --- a/compel/arch/x86/src/lib/infect.c +++ b/compel/arch/x86/src/lib/infect.c @@ -375,10 +375,13 @@ void *remote_mmap(struct parasite_ctl *ctl, if (err < 0) return NULL; + if (map == -EACCES && (prot & PROT_WRITE) && (prot & PROT_EXEC)) { + pr_warn("mmap(PROT_WRITE | PROT_EXEC) failed for %d, " + "check selinux execmem policy\n", ctl->rpid); + return NULL; + } if (IS_ERR_VALUE(map)) { - if (map == -EACCES && (prot & PROT_WRITE) && (prot & PROT_EXEC)) - pr_warn("mmap(PROT_WRITE | PROT_EXEC) failed for %d, " - "check selinux execmem policy\n", ctl->rpid); + pr_err("remote mmap() failed: %s\n", strerror(-map)); return NULL; } From e847a5205fa402e17b6b55aca6266ac6e446f0ba Mon Sep 17 00:00:00 2001 From: Dmitry Safonov Date: Thu, 2 May 2019 02:34:45 +0100 Subject: [PATCH 088/249] zdtm/thread-bomb: Limit stack size in thread-bomb ia32 thread-bomb test failed when compel refused to seize the test, trying to mmap() in remote process and getting ENOMEM. It turns to be true - remote process thread-bomb was filled with 8Mb mappings created by pthread_create() (the default stack size). So, that 1024 * 8Mb is a bit too much to place in 4Gb. Fix the test on 32-bit platforms by using much smaller stack. Also check the return value of pthread_create(). Signed-off-by: Dmitry Safonov Signed-off-by: Andrei Vagin --- test/zdtm/transition/thread-bomb.c | 27 ++++++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/test/zdtm/transition/thread-bomb.c b/test/zdtm/transition/thread-bomb.c index 0b794ef2e..6621b18ed 100644 --- a/test/zdtm/transition/thread-bomb.c +++ b/test/zdtm/transition/thread-bomb.c @@ -11,6 +11,10 @@ #define exit_group(code) \ syscall(__NR_exit_group, code) +static pthread_attr_t attr; +/* Having in mind setup with 64 Kb large pages */ +static const size_t stack_size = 64 * 1024; + static void *thread_fn(void *arg) { pthread_t t, p, *self; @@ -24,14 +28,27 @@ static void *thread_fn(void *arg) self = malloc(sizeof(*self)); *self = pthread_self(); - pthread_create(&t, NULL, thread_fn, self); + pthread_create(&t, &attr, thread_fn, self); return NULL; } int main(int argc, char **argv) { - char *val; int max_nr = 1024, i; + char *val; + int err; + + err = pthread_attr_init(&attr); + if (err) { + pr_err("pthread_attr_init(): %d\n", err); + exit(1); + } + + err = pthread_attr_setstacksize(&attr, stack_size); + if (err) { + pr_err("pthread_attr_setstacksize(): %d\n", err); + exit(1); + } val = getenv("ZDTM_THREAD_BOMB"); if (val) @@ -43,7 +60,11 @@ int main(int argc, char **argv) for (i = 0; i < max_nr; i++) { pthread_t p; - pthread_create(&p, NULL, thread_fn, NULL); + err = pthread_create(&p, &attr, thread_fn, NULL); + if (err) { + pr_err("pthread_create(): %d\n", err); + exit(1); + } } test_daemon(); From 42e5c04f6cdb53172d2d024fe9722a9b7682665a Mon Sep 17 00:00:00 2001 From: Dmitry Safonov Date: Wed, 1 May 2019 14:59:44 +0100 Subject: [PATCH 089/249] cgroup: Add "ignore" mode for --manage-cgroups Since commit 6c572bee8f10 ("cgroup: Set "soft" mode by default") it become impossible to set ignore mode at all. Provide a user option to do that. Cc: Cyrill Gorcunov Signed-off-by: Dmitry Safonov Signed-off-by: Andrei Vagin --- Documentation/criu.txt | 2 ++ criu/config.c | 2 ++ criu/crtools.c | 3 ++- 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/Documentation/criu.txt b/Documentation/criu.txt index 414c9bb2d..6111c3baf 100644 --- a/Documentation/criu.txt +++ b/Documentation/criu.txt @@ -440,6 +440,8 @@ The 'mode' may be one of the following: *strict*::: Restore all cgroups and their properties from the scratch, requiring them to not present in the system. + *ignore*::: Don't deal with cgroups and pretend that they don't exist. + *--cgroup-root* ['controller'*:*]/'newroot':: Change the root cgroup the controller will be installed into. No controller means that root is the default for all controllers not specified. diff --git a/criu/config.c b/criu/config.c index f587b6ba2..1474625ea 100644 --- a/criu/config.c +++ b/criu/config.c @@ -368,6 +368,8 @@ static int parse_manage_cgroups(struct cr_options *opts, const char *optarg) opts->manage_cgroups = CG_MODE_FULL; } else if (!strcmp(optarg, "strict")) { opts->manage_cgroups = CG_MODE_STRICT; + } else if (!strcmp(optarg, "ignore")) { + opts->manage_cgroups = CG_MODE_IGNORE; } else goto Esyntax; diff --git a/criu/crtools.c b/criu/crtools.c index fbc707b04..791db6067 100644 --- a/criu/crtools.c +++ b/criu/crtools.c @@ -368,7 +368,8 @@ int main(int argc, char *argv[], char *envp[]) " --irmap-scan-path FILE\n" " add a path the irmap hints to scan\n" " --manage-cgroups [m] dump/restore process' cgroups; argument can be one of\n" -" 'none', 'props', 'soft' (default), 'full' or 'strict'\n" +" 'none', 'props', 'soft' (default), 'full', 'strict'\n" +" or 'ignore'\n" " --cgroup-root [controller:]/newroot\n" " on dump: change the root for the controller that will\n" " be dumped. By default, only the paths with tasks in\n" From c5a2eed24cceda0ce2328668a146877c7f148e89 Mon Sep 17 00:00:00 2001 From: Harshavardhan Unnibhavi Date: Sun, 7 Apr 2019 14:29:53 +0530 Subject: [PATCH 090/249] test/exhaustive: Replace map by list comprehension Fixes #331. https://github.com/checkpoint-restore/criu/issues/331 Signed-off-by: Harshavardhan Unnibhavi Signed-off-by: Andrei Vagin --- test/exhaustive/unix.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/exhaustive/unix.py b/test/exhaustive/unix.py index 6b1ed85d8..41053bd0d 100755 --- a/test/exhaustive/unix.py +++ b/test/exhaustive/unix.py @@ -415,7 +415,7 @@ def clone(self): # one in which. At the same time really different states # shouldn't map to the same string. def describe(self): - sks = map(lambda x: x.describe(self), self.sockets) + sks = [x.describe(self) for x in self.sockets] sks = sorted(sks) return '_'.join(sks) From a82fec8344ed631d9275aa04c9af75240155f2ce Mon Sep 17 00:00:00 2001 From: guoqd Date: Mon, 22 Apr 2019 14:40:11 +0800 Subject: [PATCH 091/249] [coredump]: correct the parsing of reg_files from files.img Fixes #679 --- coredump/criu_coredump/coredump.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/coredump/criu_coredump/coredump.py b/coredump/criu_coredump/coredump.py index 963e8c61b..2b0c37f1a 100644 --- a/coredump/criu_coredump/coredump.py +++ b/coredump/criu_coredump/coredump.py @@ -163,7 +163,8 @@ def __call__(self, imgs_dir): self.mms[pid] = self._img_open_and_strip("mm", True, pid) self.pagemaps[pid] = self._img_open_and_strip("pagemap", False, pid) - self.reg_files = self._img_open_and_strip("reg-files", False) + files = self._img_open_and_strip("files", False) + self.reg_files = [ x["reg"] for x in files if x["type"]=="REG" ] for pid in self.pstree: self.coredumps[pid] = self._gen_coredump(pid) From 6c695d02d30a47c6dff27c25d5967d427d97898b Mon Sep 17 00:00:00 2001 From: Adrian Reber Date: Fri, 3 May 2019 06:27:51 +0000 Subject: [PATCH 092/249] lsm: fix compiler error 'unused-result' Reading out the xattr 'security.selinux' of checkpointed sockets with fscanf() works (at least in theory) without checking the result of fscanf(). There are, however, multiple CI failures when ignoring the return value of fscanf(). This adds ferror() to check if the stream has an actual error or if '-1' just mean EOF. Handle all errors of fscanf() // Andrei Signed-off-by: Adrian Reber Signed-off-by: Andrei Vagin --- criu/lsm.c | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/criu/lsm.c b/criu/lsm.c index ef6ba112b..9c9ac7f80 100644 --- a/criu/lsm.c +++ b/criu/lsm.c @@ -33,8 +33,8 @@ static int apparmor_get_label(pid_t pid, char **profile_name) return -1; if (fscanf(f, "%ms", profile_name) != 1) { - fclose(f); pr_perror("err scanfing"); + fclose(f); return -1; } @@ -111,19 +111,23 @@ static int selinux_get_label(pid_t pid, char **output) static int selinux_get_sockcreate_label(pid_t pid, char **output) { FILE *f; + int ret; f = fopen_proc(pid, "attr/sockcreate"); if (!f) return -1; - fscanf(f, "%ms", output); - /* - * No need to check the result of fscanf(). If there is something - * in /proc/PID/attr/sockcreate it will be copied to *output. If - * there is nothing it will stay NULL. So whatever fscanf() does - * it should be correct. - */ - + ret = fscanf(f, "%ms", output); + if (ret == -1 && errno != 0) { + pr_perror("Unable to parse /proc/%d/attr/sockcreate", pid); + /* + * Only if the error indicator is set it is a real error. + * -1 could also be EOF, which would mean that sockcreate + * was just empty, which is the most common case. + */ + fclose(f); + return -1; + } fclose(f); return 0; } From bb6ce09cfd5f4b282db7e095880a78d83ca52269 Mon Sep 17 00:00:00 2001 From: Andrei Vagin Date: Sat, 4 May 2019 20:01:52 -0700 Subject: [PATCH 093/249] lsm: don't reset socket contex if SELinux is disabled Fixes #693 --- criu/lsm.c | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/criu/lsm.c b/criu/lsm.c index 9c9ac7f80..592113839 100644 --- a/criu/lsm.c +++ b/criu/lsm.c @@ -134,7 +134,15 @@ static int selinux_get_sockcreate_label(pid_t pid, char **output) int reset_setsockcreatecon() { - return setsockcreatecon_raw(NULL); + /* Currently this only works for SELinux. */ + if (kdat.lsm != LSMTYPE__SELINUX) + return 0; + + if (setsockcreatecon_raw(NULL)) { + pr_perror("Unable to reset socket SELinux context"); + return -1; + } + return 0; } int run_setsockcreatecon(FdinfoEntry *e) @@ -147,7 +155,11 @@ int run_setsockcreatecon(FdinfoEntry *e) ctx = e->xattr_security_selinux; /* Writing to the FD using fsetxattr() did not work for some reason. */ - return setsockcreatecon_raw(ctx); + if (setsockcreatecon_raw(ctx)) { + pr_perror("Unable to set the %s socket SELinux context", ctx); + return -1; + } + return 0; } int dump_xattr_security_selinux(int fd, FdinfoEntry *e) From cc8ee9d06aca217088be37632e8af8c0997111ed Mon Sep 17 00:00:00 2001 From: Adrian Reber Date: Sat, 4 May 2019 15:27:32 +0200 Subject: [PATCH 094/249] lsm: fix compiler error on Fedora 30 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This fixes following compiler error: criu/lsm.c: In function ‘dump_xattr_security_selinux’: criu/include/log.h:51:2: error: ‘%s’ directive argument is null [-Werror=format-overflow=] 51 | print_on_level(LOG_ERROR, \ | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 52 | "Error (%s:%d): " LOG_PREFIX fmt, \ | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 53 | __FILE__, __LINE__, ##__VA_ARGS__) | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ criu/lsm.c:166:3: note: in expansion of macro ‘pr_err’ 166 | pr_err("Reading xattr %s to FD %d failed\n", ctx, fd); | ^~~~~~ Signed-off-by: Adrian Reber --- criu/lsm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/criu/lsm.c b/criu/lsm.c index 592113839..420585ba4 100644 --- a/criu/lsm.c +++ b/criu/lsm.c @@ -175,7 +175,7 @@ int dump_xattr_security_selinux(int fd, FdinfoEntry *e) /* Get the size of the xattr. */ len = fgetxattr(fd, "security.selinux", ctx, 0); if (len == -1) { - pr_err("Reading xattr %s to FD %d failed\n", ctx, fd); + pr_err("Reading xattr security.selinux from FD %d failed\n", fd); return -1; } From 70d2ab37bb57c8a97b960d84f9378d4fbf31544e Mon Sep 17 00:00:00 2001 From: Radostin Stoyanov Date: Sat, 4 May 2019 19:53:55 +0100 Subject: [PATCH 095/249] zdtm: Simplify string to boolean conversion The built-in bool() function returns a boolean value by converting the input using standard truth testing procedure. https://docs.python.org/3/library/functions.html#bool Signed-off-by: Radostin Stoyanov --- test/zdtm.py | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/test/zdtm.py b/test/zdtm.py index e6325d5f5..44fa92ee3 100755 --- a/test/zdtm.py +++ b/test/zdtm.py @@ -940,24 +940,24 @@ def __init__(self, opts): self.__dump_path = None self.__iter = 0 self.__prev_dump_iter = None - self.__page_server = (opts['page_server'] and True or False) - self.__remote_lazy_pages = (opts['remote_lazy_pages'] and True or False) + self.__page_server = bool(opts['page_server']) + self.__remote_lazy_pages = bool(opts['remote_lazy_pages']) self.__lazy_pages = (self.__remote_lazy_pages or - opts['lazy_pages'] and True or False) - self.__lazy_migrate = (opts['lazy_migrate'] and True or False) - self.__restore_sibling = (opts['sibling'] and True or False) - self.__join_ns = (opts['join_ns'] and True or False) - self.__empty_ns = (opts['empty_ns'] and True or False) - self.__fault = (opts['fault']) + bool(opts['lazy_pages'])) + self.__lazy_migrate = bool(opts['lazy_migrate']) + self.__restore_sibling = bool(opts['sibling']) + self.__join_ns = bool(opts['join_ns']) + self.__empty_ns = bool(opts['empty_ns']) + self.__fault = opts['fault'] self.__script = opts['script'] - self.__sat = (opts['sat'] and True or False) - self.__dedup = (opts['dedup'] and True or False) - self.__mdedup = (opts['noauto_dedup'] and True or False) - self.__user = (opts['user'] and True or False) - self.__leave_stopped = (opts['stop'] and True or False) - self.__remote = (opts['remote'] and True or False) + self.__sat = bool(opts['sat']) + self.__dedup = bool(opts['dedup']) + self.__mdedup = bool(opts['noauto_dedup']) + self.__user = bool(opts['user']) + self.__leave_stopped = bool(opts['stop']) + self.__remote = bool(opts['remote']) self.__criu = (opts['rpc'] and criu_rpc or criu_cli) - self.__show_stats = (opts['show_stats'] and True or False) + self.__show_stats = bool(opts['show_stats']) self.__lazy_pages_p = None self.__page_server_p = None self.__dump_process = None From 929a18334e8d0d42139cc28342463ef62be2e914 Mon Sep 17 00:00:00 2001 From: Radostin Stoyanov Date: Tue, 16 Apr 2019 17:16:46 +0100 Subject: [PATCH 096/249] sk-inet: restore SO_BROADCAST option Inet sockets may have broadcasting capability enabled. The SO_BROADCAST option is used to enable this feature. It is a Boolean flag option, which is defined, fetched, and set with the int data type. During checkpoint, CRIU should detect the state of this flag, and during restore, it should be set appropriately. Fixes #673 Reported-by: @dubukuangye Signed-off-by: Radostin Stoyanov --- criu/sk-inet.c | 4 ++++ criu/sockets.c | 9 +++++++++ images/sk-opts.proto | 1 + 3 files changed, 14 insertions(+) diff --git a/criu/sk-inet.c b/criu/sk-inet.c index ca5c9bf2c..ebae53113 100644 --- a/criu/sk-inet.c +++ b/criu/sk-inet.c @@ -742,6 +742,10 @@ static int post_open_inet_sk(struct file_desc *d, int sk) if (!val && restore_opt(sk, SOL_SOCKET, SO_REUSEPORT, &val)) return -1; + val = ii->ie->opts->so_broadcast; + if (!val && restore_opt(sk, SOL_SOCKET, SO_BROADCAST, &val)) + return -1; + return 0; } diff --git a/criu/sockets.c b/criu/sockets.c index 7f7453ca1..312b55c6d 100644 --- a/criu/sockets.c +++ b/criu/sockets.c @@ -566,6 +566,11 @@ int restore_socket_opts(int sk, SkOptsEntry *soe) pr_debug("\tset no_check for socket\n"); ret |= restore_opt(sk, SOL_SOCKET, SO_NO_CHECK, &val); } + if (soe->has_so_broadcast && soe->so_broadcast) { + val = 1; + pr_debug("\tset broadcast for socket\n"); + ret |= restore_opt(sk, SOL_SOCKET, SO_BROADCAST, &val); + } tv.tv_sec = soe->so_snd_tmo_sec; tv.tv_usec = soe->so_snd_tmo_usec; @@ -647,6 +652,10 @@ int dump_socket_opts(int sk, SkOptsEntry *soe) soe->has_so_no_check = true; soe->so_no_check = val ? true : false; + ret |= dump_opt(sk, SOL_SOCKET, SO_BROADCAST, &val); + soe->has_so_broadcast = true; + soe->so_broadcast = val ? true : false; + ret |= dump_bound_dev(sk, soe); ret |= dump_socket_filter(sk, soe); diff --git a/images/sk-opts.proto b/images/sk-opts.proto index af61975e9..c93ec5fd5 100644 --- a/images/sk-opts.proto +++ b/images/sk-opts.proto @@ -22,6 +22,7 @@ message sk_opts_entry { repeated fixed64 so_filter = 16; optional bool so_reuseport = 17; + optional bool so_broadcast = 18; } enum sk_shutdown { From 93d57da6a87ae5df9923aef76fe56f587c0cb3c8 Mon Sep 17 00:00:00 2001 From: Radostin Stoyanov Date: Thu, 25 Apr 2019 12:13:19 +0100 Subject: [PATCH 097/249] zdtm: Add UDP broadcast test Signed-off-by: Radostin Stoyanov --- test/zdtm/static/Makefile | 1 + test/zdtm/static/socket_udp-broadcast.c | 47 +++++++++++++++++++++++++ 2 files changed, 48 insertions(+) create mode 100644 test/zdtm/static/socket_udp-broadcast.c diff --git a/test/zdtm/static/Makefile b/test/zdtm/static/Makefile index 1ffaa9039..7799c0b0a 100644 --- a/test/zdtm/static/Makefile +++ b/test/zdtm/static/Makefile @@ -30,6 +30,7 @@ TST_NOFILE := \ socket_listen6 \ socket_listen4v6 \ socket_udp \ + socket_udp-broadcast \ socket_udp-corked \ socket6_udp \ socket_udp_shutdown \ diff --git a/test/zdtm/static/socket_udp-broadcast.c b/test/zdtm/static/socket_udp-broadcast.c new file mode 100644 index 000000000..a5fb55444 --- /dev/null +++ b/test/zdtm/static/socket_udp-broadcast.c @@ -0,0 +1,47 @@ +#include +#include + +#include "zdtmtst.h" + +const char *test_doc = "test checkpoint/restore of SO_BROADCAST\n"; +const char *test_author = "Radostin Stoyanov \n"; + +/* Description: + * Create UDP socket, set SO_BROADCAST and verify its value after restore. + */ + +int main(int argc, char **argv) +{ + int sockfd; + int val; + socklen_t len = sizeof(val); + + test_init(argc, argv); + + sockfd = socket(AF_INET, SOCK_DGRAM, 0); + if (sockfd < 0) { + pr_perror("Can't create socket"); + return 1; + } + + if (setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, &(int){ 1 }, len)) { + pr_perror("setsockopt"); + return 1; + } + + test_daemon(); + test_waitsig(); + + if (getsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, &val, &len)) { + pr_perror("getsockopt"); + return 1; + } + + if (len != sizeof(val) || val != 1) { + fail("SO_BROADCAST not set"); + return 1; + } + + pass(); + return 0; +} From 518b04f3327a69377f2c13102eee385e018084c9 Mon Sep 17 00:00:00 2001 From: Radostin Stoyanov Date: Sun, 21 Apr 2019 07:48:41 +0100 Subject: [PATCH 098/249] util: cr_daemon: Drop keep_fd argument When running lazy-pages in daemon mode, file descriptor 3 is reused after fork to 'protect' the opened UNIX socket. However, fd 3 happens to correspond to the opened image directory. Thus, when this file descriptor is overwritten CRIU fails with the following error: $ criu lazy-pages -D --page-server \ --address --port -d ... (06.835596) Error (criu/image.c:470): Unable to open pagemap-1.img: Not a directory (06.835855) Error (criu/uffd.c:773): uffd: 1-7: Failed to open pagemap The need for keep_fd is really only necessary if the file descriptor we would like to 'protect' is 0, 1 or 2. Assuming that the standard file descriptors STDIN, STDOUT and STDERR are open this hack is unnecessary. Signed-off-by: Radostin Stoyanov --- criu/include/util.h | 2 +- criu/uffd.c | 2 +- criu/util.c | 14 ++------------ 3 files changed, 4 insertions(+), 14 deletions(-) diff --git a/criu/include/util.h b/criu/include/util.h index 136ca14ca..a14be7229 100644 --- a/criu/include/util.h +++ b/criu/include/util.h @@ -176,7 +176,7 @@ extern int is_anon_link_type(char *link, char *type); extern int cr_system(int in, int out, int err, char *cmd, char *const argv[], unsigned flags); extern int cr_system_userns(int in, int out, int err, char *cmd, char *const argv[], unsigned flags, int userns_pid); -extern int cr_daemon(int nochdir, int noclose, int *keep_fd, int close_fd); +extern int cr_daemon(int nochdir, int noclose, int close_fd); extern int close_status_fd(void); extern int is_root_user(void); diff --git a/criu/uffd.c b/criu/uffd.c index e437f1f63..6699cb14a 100644 --- a/criu/uffd.c +++ b/criu/uffd.c @@ -1427,7 +1427,7 @@ int cr_lazy_pages(bool daemon) return -1; if (daemon) { - ret = cr_daemon(1, 0, &lazy_sk, -1); + ret = cr_daemon(1, 0, -1); if (ret == -1) { pr_err("Can't run in the background\n"); return -1; diff --git a/criu/util.c b/criu/util.c index 65ca3ac41..41b6915b7 100644 --- a/criu/util.c +++ b/criu/util.c @@ -665,7 +665,7 @@ int close_status_fd(void) return close_safe(&opts.status_fd); } -int cr_daemon(int nochdir, int noclose, int *keep_fd, int close_fd) +int cr_daemon(int nochdir, int noclose, int close_fd) { int pid; @@ -688,16 +688,6 @@ int cr_daemon(int nochdir, int noclose, int *keep_fd, int close_fd) if (close_fd != -1) close(close_fd); - if ((*keep_fd != -1) && (*keep_fd != 3)) { - fd = dup2(*keep_fd, 3); - if (fd < 0) { - pr_perror("Dup2 failed"); - return -1; - } - close(*keep_fd); - *keep_fd = fd; - } - fd = open("/dev/null", O_RDWR); if (fd < 0) { pr_perror("Can't open /dev/null"); @@ -1093,7 +1083,7 @@ int run_tcp_server(bool daemon_mode, int *ask, int cfd, int sk) socklen_t clen = sizeof(caddr); if (daemon_mode) { - ret = cr_daemon(1, 0, ask, cfd); + ret = cr_daemon(1, 0, cfd); if (ret == -1) { pr_err("Can't run in the background\n"); goto out; From 92ad98043e0b1a56f834511dd23124cedbc14eda Mon Sep 17 00:00:00 2001 From: Radostin Stoyanov Date: Wed, 1 May 2019 19:41:34 +0100 Subject: [PATCH 099/249] config: Exit with error if ps-socket is std fd In daemon mode the standard file descriptors 0, 1 and 2 will be closed and ps-socket should not be one of them. Signed-off-by: Radostin Stoyanov --- criu/config.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/criu/config.c b/criu/config.c index 1474625ea..24f0b3385 100644 --- a/criu/config.c +++ b/criu/config.c @@ -847,9 +847,16 @@ int check_options() return 1; } - if (opts.ps_socket != -1 && (opts.addr || opts.port)) - pr_warn("Using --address or --port in " - "combination with --ps-socket is obsolete\n"); + if (opts.ps_socket != -1) { + if (opts.addr || opts.port) + pr_warn("Using --address or --port in " + "combination with --ps-socket is obsolete\n"); + if (opts.ps_socket <= STDERR_FILENO && opts.daemon_mode) { + pr_err("Standard file descriptors will be closed" + " in daemon mode\n"); + return 1; + } + } if (check_namespace_opts()) { pr_err("Error: namespace flags conflict\n"); From 970198a4ecee4424cc65c4d849b258e67fbecaa7 Mon Sep 17 00:00:00 2001 From: Radostin Stoyanov Date: Fri, 5 Apr 2019 14:06:44 +0100 Subject: [PATCH 100/249] crtools: Print err messages from check_options() When check_options() exits with an error (return value != 0) the logging is not yet initialised, and therefore the error messages are not printed out. Since this affects only command-line usage, and only when check_options() reports an error, flush the early log messages to STDERR. Signed-off-by: Radostin Stoyanov --- criu/crtools.c | 4 +++- criu/include/log.h | 2 ++ criu/log.c | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/criu/crtools.c b/criu/crtools.c index 791db6067..aacdc9843 100644 --- a/criu/crtools.c +++ b/criu/crtools.c @@ -96,8 +96,10 @@ int main(int argc, char *argv[], char *envp[]) return cr_service_work(atoi(argv[2])); } - if (check_options()) + if (check_options()) { + flush_early_log_buffer(STDERR_FILENO); return 1; + } if (opts.imgs_dir == NULL) SET_CHAR_OPTS(imgs_dir, "."); diff --git a/criu/include/log.h b/criu/include/log.h index 797be1bb2..15787b09f 100644 --- a/criu/include/log.h +++ b/criu/include/log.h @@ -30,6 +30,8 @@ extern void print_on_level(unsigned int loglevel, const char *format, ...) # define LOG_PREFIX #endif +void flush_early_log_buffer(int fd); + #define print_once(loglevel, fmt, ...) \ do { \ static bool __printed; \ diff --git a/criu/log.c b/criu/log.c index edd2511ce..1e43f663d 100644 --- a/criu/log.c +++ b/criu/log.c @@ -170,7 +170,7 @@ struct early_log_hdr { uint16_t len; }; -static void flush_early_log_buffer(int fd) +void flush_early_log_buffer(int fd) { unsigned int pos = 0; int ret; From 4699aa17e22d23a1839f59e27d4e8ad0dd0d25ab Mon Sep 17 00:00:00 2001 From: Mike Rapoport Date: Tue, 14 May 2019 11:34:22 +0300 Subject: [PATCH 101/249] lazy-pages: fix stack detection The commit 5432a964dcc7 ("lazy-pages: don't mark current stack page as lazy") tried to make the pages surrounding the stack pointers non-lazy. Unfortunately, it used a wrong mask for the detection. Fix it. Signed-off-by: Mike Rapoport Signed-off-by: Andrei Vagin --- criu/mem.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/criu/mem.c b/criu/mem.c index 8015a7e4e..df87ed5b0 100644 --- a/criu/mem.c +++ b/criu/mem.c @@ -149,7 +149,7 @@ static bool is_stack(struct pstree_item *item, unsigned long vaddr) for (i = 0; i < item->nr_threads; i++) { uint64_t sp = dmpi(item)->thread_sp[i]; - if (!((sp ^ vaddr) & PAGE_MASK)) + if (!((sp ^ vaddr) & ~PAGE_MASK)) return true; } From 2e7c03360153ac035cdc7c3bd0eae8bdbf953f7a Mon Sep 17 00:00:00 2001 From: Dmitry Safonov Date: Fri, 17 May 2019 23:53:00 +0100 Subject: [PATCH 102/249] build/pie: Add comments to build files And drop a stale comment that doesn't clearify anything. Reviewed-by: Cyrill Gorcunov Signed-off-by: Dmitry Safonov Signed-off-by: Andrei Vagin --- criu/pie/Makefile | 4 ++++ criu/pie/Makefile.library | 12 ++++++------ 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/criu/pie/Makefile b/criu/pie/Makefile index 24f97ea0d..739191308 100644 --- a/criu/pie/Makefile +++ b/criu/pie/Makefile @@ -1,3 +1,7 @@ +# Recipes to compile PIEs: parastie and restorer +# Compel will deal with converting the result binaries +# to a C array to be used in CRIU. + target := parasite restorer CFLAGS := $(filter-out -pg $(CFLAGS-GCOV) $(CFLAGS-ASAN),$(CFLAGS)) diff --git a/criu/pie/Makefile.library b/criu/pie/Makefile.library index f268b5ded..a48a0ea4c 100644 --- a/criu/pie/Makefile.library +++ b/criu/pie/Makefile.library @@ -1,3 +1,9 @@ +# PIE library is a static library that's going to be linked into +# *both* CRIU binary and PIEs (parasite/restorer). +# Please, make sure that you're including here only objects +# those will be used in CRIU too. For objects files only for PIEs +# edit their separate recipes criu/pie/Makefile + lib-name := pie.lib.a CFLAGS += -fno-stack-protector -DCR_NOGLIBC -fpie @@ -27,12 +33,6 @@ ifeq ($(SRCARCH),x86) CFLAGS_util-vdso-elf32.o += -DCONFIG_VDSO_32 endif -# -# We can't provide proper mount implementation -# in parasite code -- it requires run-time rellocation -# applications, which is not the target of the -# project. -# CFLAGS := $(filter-out -pg $(CFLAGS-GCOV) $(CFLAGS-ASAN),$(CFLAGS)) asflags-y := -D__ASSEMBLY__ From 0961bc4452d526f63aeadd6797f9ef1868ead5f2 Mon Sep 17 00:00:00 2001 From: Dmitry Safonov Date: Fri, 17 May 2019 23:53:01 +0100 Subject: [PATCH 103/249] arm/build: Move -marm cflag to CFLAGS_PIE I don't want to see CFLAGS redefined per-architecture in PIE makefiles in couple of places. Clean it up. The only expected per-arch ifdeffery should be object files. Also add a big comment about -marm vs -mthumb[2] Reviewed-by: Cyrill Gorcunov Signed-off-by: Dmitry Safonov Signed-off-by: Andrei Vagin --- Makefile | 7 +++++++ criu/pie/Makefile | 4 ---- criu/pie/Makefile.library | 5 ----- 3 files changed, 7 insertions(+), 9 deletions(-) diff --git a/Makefile b/Makefile index 38887da99..50948787a 100644 --- a/Makefile +++ b/Makefile @@ -46,6 +46,13 @@ ifeq ($(ARCH),arm) endif PROTOUFIX := y + # For simplicity - compile code in Arm mode without interwork. + # We could choose Thumb mode as default instead - but a dirty + # experiment shows that with 90Kb PIEs Thumb code doesn't save + # even one page. So, let's stick so far to Arm mode as it's more + # universal around all different Arm variations, until someone + # will find any use for Thumb mode. -dima + CFLAGS_PIE := -marm endif ifeq ($(ARCH),aarch64) diff --git a/criu/pie/Makefile b/criu/pie/Makefile index 739191308..c9e8a3d82 100644 --- a/criu/pie/Makefile +++ b/criu/pie/Makefile @@ -17,10 +17,6 @@ ifneq ($(filter-out clean mrproper,$(MAKECMDGOALS)),) compel_plugins := $(shell $(COMPEL_BIN) plugins) endif -ifeq ($(SRCARCH),arm) - ccflags-y += -marm -endif - asflags-y += -D__ASSEMBLY__ LDS := compel/arch/$(SRCARCH)/scripts/compel-pack.lds.S diff --git a/criu/pie/Makefile.library b/criu/pie/Makefile.library index a48a0ea4c..b7918438b 100644 --- a/criu/pie/Makefile.library +++ b/criu/pie/Makefile.library @@ -38,8 +38,3 @@ CFLAGS := $(filter-out -pg $(CFLAGS-GCOV) $(CFLAGS-ASAN),$(CFLAGS)) asflags-y := -D__ASSEMBLY__ ccflags-y += $(COMPEL_UAPI_INCLUDES) ccflags-y += $(CFLAGS_PIE) - -ifeq ($(SRCARCH),arm) - ccflags-y += -marm -endif - From 7acce8c27b710570e17bd39a7512c881798648d7 Mon Sep 17 00:00:00 2001 From: Dmitry Safonov Date: Fri, 17 May 2019 23:53:02 +0100 Subject: [PATCH 104/249] build: Move __ASSEMBLY__ define to the top Makefile __ASSEMBLY__ is used to guard C-related code in headers from asm-compatible defines. We actually want every .S file to be assembled with -D__ASSEMBLY__ not to burst with C in asm file. Move __ASSEMBLY__ from all local asflags to top Makefile's AFLAGS. Reviewed-by: Cyrill Gorcunov Signed-off-by: Dmitry Safonov Signed-off-by: Andrei Vagin --- Makefile | 3 ++- compel/plugins/Makefile | 2 +- criu/arch/aarch64/Makefile | 1 - criu/arch/arm/Makefile | 1 - criu/arch/x86/Makefile | 2 +- criu/pie/Makefile | 2 -- criu/pie/Makefile.library | 1 - 7 files changed, 4 insertions(+), 8 deletions(-) diff --git a/Makefile b/Makefile index 50948787a..84fc0841f 100644 --- a/Makefile +++ b/Makefile @@ -129,9 +129,10 @@ ifeq ($(GMON),1) export GMON GMONLDOPT endif +AFLAGS += -D__ASSEMBLY__ CFLAGS += $(USERCFLAGS) $(WARNINGS) $(DEFINES) -iquote include/ HOSTCFLAGS += $(WARNINGS) $(DEFINES) -iquote include/ -export CFLAGS USERCLFAGS HOSTCFLAGS +export AFLAGS CFLAGS USERCLFAGS HOSTCFLAGS # Default target all: criu lib crit diff --git a/compel/plugins/Makefile b/compel/plugins/Makefile index 60b78473c..8f44ba86d 100644 --- a/compel/plugins/Makefile +++ b/compel/plugins/Makefile @@ -29,7 +29,7 @@ asflags-y += -iquote $(PLUGIN_ARCH_DIR) # General flags for assembly asflags-y += -fpie -Wstrict-prototypes -asflags-y += -D__ASSEMBLY__ -nostdlib -fomit-frame-pointer +asflags-y += -nostdlib -fomit-frame-pointer asflags-y += -fno-stack-protector ldflags-y += -z noexecstack diff --git a/criu/arch/aarch64/Makefile b/criu/arch/aarch64/Makefile index 49ef6a480..fd721d12f 100644 --- a/criu/arch/aarch64/Makefile +++ b/criu/arch/aarch64/Makefile @@ -3,7 +3,6 @@ builtin-name := crtools.built-in.o ccflags-y += -iquote $(obj)/include -iquote criu/include ccflags-y += -iquote include ccflags-y += $(COMPEL_UAPI_INCLUDES) -asflags-y += -D__ASSEMBLY__ ldflags-y += -r obj-y += cpu.o diff --git a/criu/arch/arm/Makefile b/criu/arch/arm/Makefile index d01c69a16..5142fbe12 100644 --- a/criu/arch/arm/Makefile +++ b/criu/arch/arm/Makefile @@ -4,7 +4,6 @@ ccflags-y += -iquote $(obj)/include ccflags-y += -iquote criu/include -iquote include ccflags-y += $(COMPEL_UAPI_INCLUDES) -asflags-y += -D__ASSEMBLY__ ldflags-y += -r -z noexecstack obj-y += cpu.o diff --git a/criu/arch/x86/Makefile b/criu/arch/x86/Makefile index 20a40e4ae..ca92a241c 100644 --- a/criu/arch/x86/Makefile +++ b/criu/arch/x86/Makefile @@ -5,7 +5,7 @@ ccflags-y += -iquote criu/include -iquote include ccflags-y += $(COMPEL_UAPI_INCLUDES) asflags-y += -Wstrict-prototypes -asflags-y += -D__ASSEMBLY__ -nostdlib -fomit-frame-pointer +asflags-y += -nostdlib -fomit-frame-pointer asflags-y += -iquote $(obj)/include ldflags-y += -r -z noexecstack diff --git a/criu/pie/Makefile b/criu/pie/Makefile index c9e8a3d82..5c0606786 100644 --- a/criu/pie/Makefile +++ b/criu/pie/Makefile @@ -17,8 +17,6 @@ ifneq ($(filter-out clean mrproper,$(MAKECMDGOALS)),) compel_plugins := $(shell $(COMPEL_BIN) plugins) endif -asflags-y += -D__ASSEMBLY__ - LDS := compel/arch/$(SRCARCH)/scripts/compel-pack.lds.S restorer-obj-y += ./$(ARCH_DIR)/restorer.o diff --git a/criu/pie/Makefile.library b/criu/pie/Makefile.library index b7918438b..577497f5a 100644 --- a/criu/pie/Makefile.library +++ b/criu/pie/Makefile.library @@ -35,6 +35,5 @@ endif CFLAGS := $(filter-out -pg $(CFLAGS-GCOV) $(CFLAGS-ASAN),$(CFLAGS)) -asflags-y := -D__ASSEMBLY__ ccflags-y += $(COMPEL_UAPI_INCLUDES) ccflags-y += $(CFLAGS_PIE) From 3485e37312006b48d44ea823c5058c524a611123 Mon Sep 17 00:00:00 2001 From: Dmitry Safonov Date: Fri, 17 May 2019 23:53:03 +0100 Subject: [PATCH 105/249] make: Don't export ccflags-y As far as I know, difference between CFLAGS and ccflags-y in kernel is that CFLAGS are global and exported and ccflags-y are per-Makefile. So, exporting ccflags-y should be omitted. While at it, remove COMPEL_UAPI_INCLUDES - they're added to CFLAGS straight away and exported to sub-makes, so no-one need to include them twice. Also, remove from sub-Makefiles -iquote(s) for includes those are already added in criu/Makefile Reviewed-by: Cyrill Gorcunov Signed-off-by: Dmitry Safonov Signed-off-by: Andrei Vagin --- criu/Makefile | 24 ++++++++++-------------- criu/Makefile.crtools | 1 - criu/arch/aarch64/Makefile | 3 --- criu/arch/arm/Makefile | 4 ---- criu/arch/ppc64/Makefile | 3 --- criu/arch/s390/Makefile | 3 --- criu/arch/x86/Makefile | 4 ---- criu/pie/Makefile | 1 - criu/pie/Makefile.library | 2 -- 9 files changed, 10 insertions(+), 35 deletions(-) diff --git a/criu/Makefile b/criu/Makefile index 797878176..1e9a16789 100644 --- a/criu/Makefile +++ b/criu/Makefile @@ -7,9 +7,8 @@ PIE_DIR := criu/pie export ARCH_DIR PIE_DIR ifeq ($(filter clean mrproper,$(MAKECMDGOALS)),) - COMPEL_UAPI_INCLUDES := $(shell $(COMPEL_BIN) includes) - export COMPEL_UAPI_INCLUDES - COMPEL_LIBS := $(shell $(COMPEL_BIN) --static libs) + CFLAGS += $(shell $(COMPEL_BIN) includes) + COMPEL_LIBS := $(shell $(COMPEL_BIN) --static libs) endif # @@ -20,17 +19,14 @@ CONFIG-DEFINES += -DUSER_CONFIG_DIR='".criu/"' # # General flags. -ccflags-y += -fno-strict-aliasing -ccflags-y += -iquote criu/include -ccflags-y += -iquote include -ccflags-y += -iquote images -ccflags-y += -iquote $(ARCH_DIR)/include -ccflags-y += -iquote . -ccflags-y += $(shell pkg-config --cflags libnl-3.0) -ccflags-y += $(COMPEL_UAPI_INCLUDES) -ccflags-y += $(CONFIG-DEFINES) - -export ccflags-y +CFLAGS += -fno-strict-aliasing +CFLAGS += -iquote criu/include +CFLAGS += -iquote include +CFLAGS += -iquote images +CFLAGS += -iquote $(ARCH_DIR)/include +CFLAGS += -iquote . +CFLAGS += $(shell pkg-config --cflags libnl-3.0) +CFLAGS += $(CONFIG-DEFINES) ifeq ($(GMON),1) CFLAGS += -pg diff --git a/criu/Makefile.crtools b/criu/Makefile.crtools index b598f0ef5..140436abf 100644 --- a/criu/Makefile.crtools +++ b/criu/Makefile.crtools @@ -1,5 +1,4 @@ ccflags-y += -iquote criu/$(ARCH) -ccflags-y += $(COMPEL_UAPI_INCLUDES) CFLAGS_REMOVE_clone-noasan.o += $(CFLAGS-ASAN) CFLAGS_kerndat.o += -DKDAT_MAGIC_2=${shell echo $${SOURCE_DATE_EPOCH:-$$(date +%s)}} -DKDAT_RUNDIR=\"$(RUNDIR)\" ldflags-y += -r diff --git a/criu/arch/aarch64/Makefile b/criu/arch/aarch64/Makefile index fd721d12f..b26487367 100644 --- a/criu/arch/aarch64/Makefile +++ b/criu/arch/aarch64/Makefile @@ -1,8 +1,5 @@ builtin-name := crtools.built-in.o -ccflags-y += -iquote $(obj)/include -iquote criu/include -ccflags-y += -iquote include -ccflags-y += $(COMPEL_UAPI_INCLUDES) ldflags-y += -r obj-y += cpu.o diff --git a/criu/arch/arm/Makefile b/criu/arch/arm/Makefile index 5142fbe12..b111e5959 100644 --- a/criu/arch/arm/Makefile +++ b/criu/arch/arm/Makefile @@ -1,9 +1,5 @@ builtin-name := crtools.built-in.o -ccflags-y += -iquote $(obj)/include -ccflags-y += -iquote criu/include -iquote include -ccflags-y += $(COMPEL_UAPI_INCLUDES) - ldflags-y += -r -z noexecstack obj-y += cpu.o diff --git a/criu/arch/ppc64/Makefile b/criu/arch/ppc64/Makefile index ff0a71207..f37337f74 100644 --- a/criu/arch/ppc64/Makefile +++ b/criu/arch/ppc64/Makefile @@ -1,8 +1,5 @@ builtin-name := crtools.built-in.o -ccflags-y += -iquote $(obj)/include -ccflags-y += -iquote criu/include -iquote include -ccflags-y += $(COMPEL_UAPI_INCLUDES) ldflags-y += -r obj-y += cpu.o diff --git a/criu/arch/s390/Makefile b/criu/arch/s390/Makefile index ff0a71207..f37337f74 100644 --- a/criu/arch/s390/Makefile +++ b/criu/arch/s390/Makefile @@ -1,8 +1,5 @@ builtin-name := crtools.built-in.o -ccflags-y += -iquote $(obj)/include -ccflags-y += -iquote criu/include -iquote include -ccflags-y += $(COMPEL_UAPI_INCLUDES) ldflags-y += -r obj-y += cpu.o diff --git a/criu/arch/x86/Makefile b/criu/arch/x86/Makefile index ca92a241c..618e85bb3 100644 --- a/criu/arch/x86/Makefile +++ b/criu/arch/x86/Makefile @@ -1,9 +1,5 @@ builtin-name := crtools.built-in.o -ccflags-y += -iquote $(obj)/include -ccflags-y += -iquote criu/include -iquote include -ccflags-y += $(COMPEL_UAPI_INCLUDES) - asflags-y += -Wstrict-prototypes asflags-y += -nostdlib -fomit-frame-pointer asflags-y += -iquote $(obj)/include diff --git a/criu/pie/Makefile b/criu/pie/Makefile index 5c0606786..526e4e1ad 100644 --- a/criu/pie/Makefile +++ b/criu/pie/Makefile @@ -5,7 +5,6 @@ target := parasite restorer CFLAGS := $(filter-out -pg $(CFLAGS-GCOV) $(CFLAGS-ASAN),$(CFLAGS)) -ccflags-y += $(COMPEL_UAPI_INCLUDES) ccflags-y += $(CFLAGS_PIE) ccflags-y += -DCR_NOGLIBC ccflags-y += -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=0 diff --git a/criu/pie/Makefile.library b/criu/pie/Makefile.library index 577497f5a..467dfd6b6 100644 --- a/criu/pie/Makefile.library +++ b/criu/pie/Makefile.library @@ -34,6 +34,4 @@ ifeq ($(SRCARCH),x86) endif CFLAGS := $(filter-out -pg $(CFLAGS-GCOV) $(CFLAGS-ASAN),$(CFLAGS)) - -ccflags-y += $(COMPEL_UAPI_INCLUDES) ccflags-y += $(CFLAGS_PIE) From 5d3b42f38965613f8700548dd52b8d57f97842aa Mon Sep 17 00:00:00 2001 From: Dmitry Safonov Date: Fri, 17 May 2019 23:53:04 +0100 Subject: [PATCH 106/249] Makefile.crtools: Remove bogus ccflags-y There ain't even such path in sources. Reviewed-by: Cyrill Gorcunov Signed-off-by: Dmitry Safonov Signed-off-by: Andrei Vagin --- criu/Makefile.crtools | 1 - 1 file changed, 1 deletion(-) diff --git a/criu/Makefile.crtools b/criu/Makefile.crtools index 140436abf..d24bb174c 100644 --- a/criu/Makefile.crtools +++ b/criu/Makefile.crtools @@ -1,4 +1,3 @@ -ccflags-y += -iquote criu/$(ARCH) CFLAGS_REMOVE_clone-noasan.o += $(CFLAGS-ASAN) CFLAGS_kerndat.o += -DKDAT_MAGIC_2=${shell echo $${SOURCE_DATE_EPOCH:-$$(date +%s)}} -DKDAT_RUNDIR=\"$(RUNDIR)\" ldflags-y += -r From df0548cd0ca03b37fc3626ee31598d412899a1f1 Mon Sep 17 00:00:00 2001 From: Dmitry Safonov Date: Fri, 17 May 2019 23:53:05 +0100 Subject: [PATCH 107/249] build: Use cflags from compel for pie.lib.a As pie.lib.a linked also to PIEs - we need to use missing flags as -nostdlib and -fomit-frame-pointer. Reviewed-by: Cyrill Gorcunov Signed-off-by: Dmitry Safonov Signed-off-by: Andrei Vagin --- criu/Makefile | 1 + criu/pie/Makefile | 1 - criu/pie/Makefile.library | 2 +- 3 files changed, 2 insertions(+), 2 deletions(-) diff --git a/criu/Makefile b/criu/Makefile index 1e9a16789..3de6eb217 100644 --- a/criu/Makefile +++ b/criu/Makefile @@ -9,6 +9,7 @@ export ARCH_DIR PIE_DIR ifeq ($(filter clean mrproper,$(MAKECMDGOALS)),) CFLAGS += $(shell $(COMPEL_BIN) includes) COMPEL_LIBS := $(shell $(COMPEL_BIN) --static libs) + CFLAGS_PIE += $(shell $(COMPEL_BIN) cflags) endif # diff --git a/criu/pie/Makefile b/criu/pie/Makefile index 526e4e1ad..35aa78bd3 100644 --- a/criu/pie/Makefile +++ b/criu/pie/Makefile @@ -11,7 +11,6 @@ ccflags-y += -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=0 ccflags-y += -Wp,-U_FORTIFY_SOURCE -Wp,-D_FORTIFY_SOURCE=0 ifneq ($(filter-out clean mrproper,$(MAKECMDGOALS)),) - CFLAGS += $(shell $(COMPEL_BIN) cflags) LDFLAGS += $(shell $(COMPEL_BIN) ldflags) compel_plugins := $(shell $(COMPEL_BIN) plugins) endif diff --git a/criu/pie/Makefile.library b/criu/pie/Makefile.library index 467dfd6b6..2d11ad923 100644 --- a/criu/pie/Makefile.library +++ b/criu/pie/Makefile.library @@ -6,7 +6,7 @@ lib-name := pie.lib.a -CFLAGS += -fno-stack-protector -DCR_NOGLIBC -fpie +CFLAGS += -DCR_NOGLIBC lib-y += util.o From 24e90c82d0a2c66e44b6e6f869f9c734445925da Mon Sep 17 00:00:00 2001 From: Dmitry Safonov Date: Fri, 17 May 2019 23:53:06 +0100 Subject: [PATCH 108/249] compel: Don't use CFLAGS_PIE for libcompel.so It's needed for PIEs, but not for the library. It comes earlier than commit 61e6c01d0964, but I don't see the point. Regardles, I'm a bit afraid to break s390, hopefully testing covers the platform. This and the next one "make: Move CR_NOGLIBC into CFLAGS_PIE" should be reverted/dropped from criu-dev if they turn to be breaking something. Cc: Michael Holzheu Reviewed-by: Cyrill Gorcunov Signed-off-by: Dmitry Safonov Signed-off-by: Andrei Vagin --- compel/Makefile | 1 - 1 file changed, 1 deletion(-) diff --git a/compel/Makefile b/compel/Makefile index 45736f29e..de9318c42 100644 --- a/compel/Makefile +++ b/compel/Makefile @@ -11,7 +11,6 @@ ccflags-y += -iquote compel/arch/$(ARCH)/src/lib/include ccflags-y += -iquote compel/include ccflags-y += -fno-strict-aliasing ccflags-y += -fPIC -ccflags-y += $(CFLAGS_PIE) ldflags-y += -r # From bd808f5d657fba8a225090b4ce0b93da86ce9905 Mon Sep 17 00:00:00 2001 From: Dmitry Safonov Date: Fri, 17 May 2019 23:53:07 +0100 Subject: [PATCH 109/249] make: Move CR_NOGLIBC into CFLAGS_PIE Lesser duplication, cleaner Makefiles. Reviewed-by: Cyrill Gorcunov Signed-off-by: Dmitry Safonov Signed-off-by: Andrei Vagin --- Makefile | 2 ++ compel/plugins/Makefile | 2 +- criu/pie/Makefile | 1 - criu/pie/Makefile.library | 2 -- 4 files changed, 3 insertions(+), 4 deletions(-) diff --git a/Makefile b/Makefile index 84fc0841f..cee8a42c9 100644 --- a/Makefile +++ b/Makefile @@ -85,6 +85,8 @@ ifeq ($(ARCH),s390) DEFINES := -DCONFIG_S390 CFLAGS_PIE := -fno-optimize-sibling-calls endif + +CFLAGS_PIE += -DCR_NOGLIBC export CFLAGS_PIE LDARCH ?= $(SRCARCH) diff --git a/compel/plugins/Makefile b/compel/plugins/Makefile index 8f44ba86d..a326e2a66 100644 --- a/compel/plugins/Makefile +++ b/compel/plugins/Makefile @@ -1,5 +1,5 @@ CFLAGS := $(filter-out -pg $(CFLAGS-GCOV) $(CFLAGS-ASAN),$(CFLAGS)) -CFLAGS += -DCR_NOGLIBC -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=0 +CFLAGS += -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=0 CFLAGS += -Wp,-U_FORTIFY_SOURCE -Wp,-D_FORTIFY_SOURCE=0 PLUGIN_ARCH_DIR := compel/arch/$(ARCH)/plugins diff --git a/criu/pie/Makefile b/criu/pie/Makefile index 35aa78bd3..bb65f8908 100644 --- a/criu/pie/Makefile +++ b/criu/pie/Makefile @@ -6,7 +6,6 @@ target := parasite restorer CFLAGS := $(filter-out -pg $(CFLAGS-GCOV) $(CFLAGS-ASAN),$(CFLAGS)) ccflags-y += $(CFLAGS_PIE) -ccflags-y += -DCR_NOGLIBC ccflags-y += -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=0 ccflags-y += -Wp,-U_FORTIFY_SOURCE -Wp,-D_FORTIFY_SOURCE=0 diff --git a/criu/pie/Makefile.library b/criu/pie/Makefile.library index 2d11ad923..423c782aa 100644 --- a/criu/pie/Makefile.library +++ b/criu/pie/Makefile.library @@ -6,8 +6,6 @@ lib-name := pie.lib.a -CFLAGS += -DCR_NOGLIBC - lib-y += util.o ifeq ($(VDSO),y) From 48880e3ff6f943b4a32e967a295d64992d1dd210 Mon Sep 17 00:00:00 2001 From: Dmitry Safonov Date: Fri, 17 May 2019 23:53:08 +0100 Subject: [PATCH 110/249] criu/ia32: Consolidate compat vdso and move to arch/x86 Do the cleanup that was long pending by XXX :) Reviewed-by: Cyrill Gorcunov Signed-off-by: Dmitry Safonov Signed-off-by: Andrei Vagin --- criu/arch/x86/include/asm/restorer.h | 8 -------- criu/arch/x86/include/asm/vdso.h | 24 ++++++++++++++++++++++++ criu/include/util-vdso.h | 13 ++----------- criu/pie/parasite-vdso.c | 14 ++------------ 4 files changed, 28 insertions(+), 31 deletions(-) diff --git a/criu/arch/x86/include/asm/restorer.h b/criu/arch/x86/include/asm/restorer.h index 3c43ce688..25559b57c 100644 --- a/criu/arch/x86/include/asm/restorer.h +++ b/criu/arch/x86/include/asm/restorer.h @@ -72,14 +72,6 @@ static inline int set_compat_robust_list(uint32_t head_ptr, uint32_t len) : "r"(ret) \ : "memory") -#ifndef ARCH_MAP_VDSO_32 -# define ARCH_MAP_VDSO_32 0x2002 -#endif - -#ifndef ARCH_MAP_VDSO_64 -# define ARCH_MAP_VDSO_64 0x2003 -#endif - static inline void __setup_sas_compat(struct ucontext_ia32* uc, ThreadSasEntry *sas) { diff --git a/criu/arch/x86/include/asm/vdso.h b/criu/arch/x86/include/asm/vdso.h index d6c2f1b8c..ae893b8d7 100644 --- a/criu/arch/x86/include/asm/vdso.h +++ b/criu/arch/x86/include/asm/vdso.h @@ -23,5 +23,29 @@ "__kernel_sigreturn", \ "__kernel_rt_sigreturn" +#ifndef ARCH_MAP_VDSO_32 +# define ARCH_MAP_VDSO_32 0x2002 +#endif + +#ifndef ARCH_MAP_VDSO_64 +# define ARCH_MAP_VDSO_64 0x2003 +#endif + +#if defined(CONFIG_COMPAT) && !defined(__ASSEMBLY__) +struct vdso_symtable; +extern int vdso_fill_symtable(uintptr_t mem, size_t size, + struct vdso_symtable *t); +extern int vdso_fill_symtable_compat(uintptr_t mem, size_t size, + struct vdso_symtable *t); + +static inline int __vdso_fill_symtable(uintptr_t mem, size_t size, + struct vdso_symtable *t, bool compat_vdso) +{ + if (compat_vdso) + return vdso_fill_symtable_compat(mem, size, t); + else + return vdso_fill_symtable(mem, size, t); +} +#endif #endif /* __CR_ASM_VDSO_H__ */ diff --git a/criu/include/util-vdso.h b/criu/include/util-vdso.h index 05b8326f5..c74360c87 100644 --- a/criu/include/util-vdso.h +++ b/criu/include/util-vdso.h @@ -75,6 +75,8 @@ struct vdso_maps { #define ELF_ST_BIND ELF32_ST_BIND #endif +# define vdso_fill_symtable vdso_fill_symtable_compat + #else /* CONFIG_VDSO_32 */ #define Ehdr_t Elf64_Ehdr @@ -92,17 +94,6 @@ struct vdso_maps { #endif /* CONFIG_VDSO_32 */ -#if defined(CONFIG_VDSO_32) -# define vdso_fill_symtable vdso_fill_symtable_compat -#endif - extern int vdso_fill_symtable(uintptr_t mem, size_t size, struct vdso_symtable *t); -#if defined(CONFIG_X86_64) && defined(CONFIG_COMPAT) -#ifndef ARCH_MAP_VDSO_32 -# define ARCH_MAP_VDSO_32 0x2002 -#endif -extern int vdso_fill_symtable_compat(uintptr_t mem, size_t size, - struct vdso_symtable *t); -#endif #endif /* __CR_UTIL_VDSO_H__ */ diff --git a/criu/pie/parasite-vdso.c b/criu/pie/parasite-vdso.c index 8072c11f7..dc73fb53e 100644 --- a/criu/pie/parasite-vdso.c +++ b/criu/pie/parasite-vdso.c @@ -74,18 +74,8 @@ int vdso_do_park(struct vdso_maps *rt, unsigned long park_at, return ret; } -/* XXX: move in arch/ */ -#if defined(CONFIG_X86_64) && defined(CONFIG_COMPAT) -int __vdso_fill_symtable(uintptr_t mem, size_t size, - struct vdso_symtable *t, bool compat_vdso) -{ - if (compat_vdso) - return vdso_fill_symtable_compat(mem, size, t); - else - return vdso_fill_symtable(mem, size, t); -} -#else -int __vdso_fill_symtable(uintptr_t mem, size_t size, +#ifndef CONFIG_COMPAT +static int __vdso_fill_symtable(uintptr_t mem, size_t size, struct vdso_symtable *t, bool __always_unused compat_vdso) { return vdso_fill_symtable(mem, size, t); From beeda670987b383aa4182b996e5c22aa43541765 Mon Sep 17 00:00:00 2001 From: Dmitry Safonov Date: Fri, 17 May 2019 23:53:09 +0100 Subject: [PATCH 111/249] build/criu/pie: Move trampolines to restorer-obj-y We don't need patching vdso neither in parasite nor in criu. Move it to restorer-only objects. Note that we need filling symtables everywhere (kdat/parasite/restorer), this change doesn't move util-vdso.o which has vdso_fill_symtable(). [those files ask for a proper rename, but it's not directly related to the change, so yet TODO] Reviewed-by: Cyrill Gorcunov Signed-off-by: Dmitry Safonov Signed-off-by: Andrei Vagin --- criu/pie/Makefile | 12 ++++++++++++ criu/pie/Makefile.library | 10 +--------- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/criu/pie/Makefile b/criu/pie/Makefile index bb65f8908..bdff44816 100644 --- a/criu/pie/Makefile +++ b/criu/pie/Makefile @@ -25,6 +25,18 @@ ifeq ($(ARCH),x86) endif endif +ifeq ($(VDSO),y) + restorer-obj-y += parasite-vdso.o ./$(ARCH_DIR)/vdso-pie.o + + ifeq ($(SRCARCH),aarch64) + restorer-obj-y += ./$(ARCH_DIR)/intraprocedure.o + endif + + ifeq ($(SRCARCH),ppc64) + restorer-obj-y += ./$(ARCH_DIR)/vdso-trampoline.o + endif +endif + define gen-pie-rules $(1)-obj-y += $(1).o $(1)-obj-e += pie.lib.a diff --git a/criu/pie/Makefile.library b/criu/pie/Makefile.library index 423c782aa..0a33a8861 100644 --- a/criu/pie/Makefile.library +++ b/criu/pie/Makefile.library @@ -9,15 +9,7 @@ lib-name := pie.lib.a lib-y += util.o ifeq ($(VDSO),y) - lib-y += util-vdso.o parasite-vdso.o ./$(ARCH_DIR)/vdso-pie.o - - ifeq ($(SRCARCH),aarch64) - lib-y += ./$(ARCH_DIR)/intraprocedure.o - endif - - ifeq ($(SRCARCH),ppc64) - lib-y += ./$(ARCH_DIR)/vdso-trampoline.o - endif + lib-y += util-vdso.o endif ifeq ($(SRCARCH),ppc64) From 5a52e34655636a8f00a664cf42716e1439ea2ef0 Mon Sep 17 00:00:00 2001 From: Zhang Ning Date: Tue, 16 Apr 2019 15:45:05 +0800 Subject: [PATCH 112/249] x86/crtools: do not error when YMM is missing for Intel Apollo Lake SOC, its cpuinfo and fpu features: cpu: x86_family 6 x86_vendor_id GenuineIntel x86_model_id Intel(R) Celeron(R) CPU J3455 @ 1.50GHz cpu: fpu: xfeatures_mask 0x11 xsave_size 1088 xsave_size_max 1088 xsaves_size 704 cpu: fpu: x87 floating point registers xstate_offsets 0 / 0 xstate_sizes 160 / 160 this CPU doesn't have AVX registers, YMM feature. when CRIU runs on this CPU, it will report dump error: Dumping GP/FPU registers for 4888 Error (criu/arch/x86/crtools.c:362): x86: Corruption in XFEATURE_YMM area (expected 64 but 0 obtained) Error (criu/cr-dump.c:1278): Can't infect (pid: 4888) with parasite that's because x86/crtools.c will still valid YMM xsave frame, thus fail to dump. bypass unsupported feature, to make CRIU runs this kinds of CPUs. Cc: Chen Hu Signed-off-by: Zhang Ning Acked-by: Cyrill Gorcunov Signed-off-by: Andrei Vagin --- criu/arch/x86/crtools.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/criu/arch/x86/crtools.c b/criu/arch/x86/crtools.c index ee016da00..efc23e5fe 100644 --- a/criu/arch/x86/crtools.c +++ b/criu/arch/x86/crtools.c @@ -354,7 +354,7 @@ static bool valid_xsave_frame(CoreEntry *core) }; for (i = 0; i < ARRAY_SIZE(features); i++) { - if (!features[i].ptr && i > 0) + if (!features[i].ptr) continue; if (features[i].expected > features[i].obtained) { From b5d29bbf7265177bdf4deae9cf1106bac3ec27cd Mon Sep 17 00:00:00 2001 From: Adrian Reber Date: Wed, 15 May 2019 06:11:23 +0000 Subject: [PATCH 113/249] autofs: fix coverity RESOURCE_LEAK criu-3.12/criu/autofs.c:114: leaked_storage: Variable "path" going out of scope leaks the storage it points to. criu-3.12/criu/autofs.c:254: leaked_storage: Variable "opts" going out of scope leaks the storage it points to. criu-3.12/criu/autofs.c:719: leaked_storage: Variable "path" going out of scope leaks the storage it points to. criu-3.12/criu/autofs.c:980: leaked_storage: Variable "img" going out of scope leaks the storage it points to. Signed-off-by: Adrian Reber --- criu/autofs.c | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/criu/autofs.c b/criu/autofs.c index 576edef68..a2dc60ffc 100644 --- a/criu/autofs.c +++ b/criu/autofs.c @@ -110,8 +110,10 @@ static int autofs_kernel_pipe_alive(int pgrp, int fd, int ino) return -1; if (stat(path, &buf) < 0) { - if (errno == ENOENT) + if (errno == ENOENT) { + xfree(path); return 0; + } pr_perror("Failed to stat %s", path); return -1; } @@ -208,6 +210,7 @@ static int parse_options(char *options, AutofsEntry *entry, long *pipe_ino) { char **opts; int nr_opts, i; + int parse_error = 0; entry->fd = AUTOFS_OPT_UNKNOWN; entry->timeout = AUTOFS_OPT_UNKNOWN; @@ -250,14 +253,19 @@ static int parse_options(char *options, AutofsEntry *entry, long *pipe_ino) else if (!strncmp(opt, "gid=", strlen("gid="))) err = xatoi(opt + strlen("gid="), &entry->gid); - if (err) - return -1; + if (err) { + parse_error = 1; + break; + } } for (i = 0; i < nr_opts; i++) xfree(opts[i]); xfree(opts); + if (parse_error) + return -1; + if (entry->fd == AUTOFS_OPT_UNKNOWN) { pr_err("Failed to find fd option\n"); return -1; @@ -716,6 +724,7 @@ static int autofs_create_dentries(const struct mount_info *mi, char *mnt_path) return -1; if (mkdir(path, 0555) < 0) { pr_perror("Failed to create autofs dentry %s", path); + free(path); return -1; } free(path); @@ -967,6 +976,7 @@ static int autofs_add_mount_info(struct pprep_head *ph) static int autofs_restore_entry(struct mount_info *mi, AutofsEntry **entry) { struct cr_img *img; + int ret; img = open_image(CR_FD_AUTOFS, O_RSTR, mi->s_dev); if (!img) @@ -976,10 +986,11 @@ static int autofs_restore_entry(struct mount_info *mi, AutofsEntry **entry) return -1; } - if (pb_read_one_eof(img, entry, PB_AUTOFS) < 0) - return -1; + ret = pb_read_one_eof(img, entry, PB_AUTOFS); close_image(img); + if (ret < 0) + return -1; return 0; } From 7c86982df04300547747390fb100913ed4e93343 Mon Sep 17 00:00:00 2001 From: Adrian Reber Date: Wed, 15 May 2019 06:55:26 +0000 Subject: [PATCH 114/249] cgroup: fix clang 'free released memory' criu-3.12/criu/cgroup.c:927:2: warning: Attempt to free released memory Signed-off-by: Adrian Reber --- criu/cgroup.c | 1 + 1 file changed, 1 insertion(+) diff --git a/criu/cgroup.c b/criu/cgroup.c index 22e722acf..332c79fb9 100644 --- a/criu/cgroup.c +++ b/criu/cgroup.c @@ -818,6 +818,7 @@ static int dump_controllers(CgroupEntry *cg) if (ce->n_dirs > 0) if (dump_cg_dirs(&cur->heads, cur->n_heads, &ce->dirs, 0) < 0) { xfree(cg->controllers); + cg->controllers = NULL; return -1; } cg->controllers[i++] = ce++; From 8c4dbb3ceb4b59befed057c8989bfb104a41a137 Mon Sep 17 00:00:00 2001 From: Adrian Reber Date: Wed, 15 May 2019 06:09:31 +0000 Subject: [PATCH 115/249] compel: fix clang 'value stored is never read' criu-3.12/compel/src/lib/infect.c:276:3: warning: Value stored to 'ret' is never read Signed-off-by: Adrian Reber --- compel/src/lib/infect.c | 1 - 1 file changed, 1 deletion(-) diff --git a/compel/src/lib/infect.c b/compel/src/lib/infect.c index 09c2c53f9..f0bcaf334 100644 --- a/compel/src/lib/infect.c +++ b/compel/src/lib/infect.c @@ -273,7 +273,6 @@ int compel_wait_task(int pid, int ppid, goto err; } - ret = 0; if (free_status) free_status(pid, ss, data); goto try_again; From 703a4acca45d274d622c59c0435b7c6607a5609e Mon Sep 17 00:00:00 2001 From: Adrian Reber Date: Tue, 14 May 2019 18:11:26 +0000 Subject: [PATCH 116/249] cr-service: fix coverity STRING_OVERFLOW criu-3.12/criu/cr-service.c:1305: fixed_size_dest: You might overrun the 108-character fixed-size string "server_addr.sun_path" by copying "opts.addr" without checking the length. Signed-off-by: Adrian Reber --- criu/cr-service.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/criu/cr-service.c b/criu/cr-service.c index 1f1e5fa86..52b86bb05 100644 --- a/criu/cr-service.c +++ b/criu/cr-service.c @@ -1302,7 +1302,8 @@ int cr_service(bool daemon_mode) SET_CHAR_OPTS(addr, CR_DEFAULT_SERVICE_ADDRESS); } - strcpy(server_addr.sun_path, opts.addr); + strncpy(server_addr.sun_path, opts.addr, + sizeof(server_addr.sun_path) - 1); server_addr_len = strlen(server_addr.sun_path) + sizeof(server_addr.sun_family); From f0523a38014c8cceb865d32cc30e74ffacc8495f Mon Sep 17 00:00:00 2001 From: Adrian Reber Date: Wed, 15 May 2019 07:23:27 +0000 Subject: [PATCH 117/249] cr-service: fix clang 'dereference of a null pointer' criu-3.12/criu/cr-service.c:933:7: warning: Access to field 'keep_open' results in a dereference of a null pointer (loaded from variable 'msg') Signed-off-by: Adrian Reber --- criu/cr-service.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/criu/cr-service.c b/criu/cr-service.c index 52b86bb05..cb76de4f4 100644 --- a/criu/cr-service.c +++ b/criu/cr-service.c @@ -1156,7 +1156,7 @@ int cr_service_work(int sk) CriuReq *msg = 0; more: - if (recv_criu_msg(sk, &msg) == -1) { + if (recv_criu_msg(sk, &msg) != 0) { pr_perror("Can't recv request"); goto err; } From d32dfc232aea71ce5f3116d612e560a4a5ce711b Mon Sep 17 00:00:00 2001 From: Adrian Reber Date: Wed, 15 May 2019 07:42:35 +0000 Subject: [PATCH 118/249] files: fix coverity RESOURCE_LEAK criu-3.12/criu/files.c:1250: leaked_storage: Variable "dir" going out of scope leaks the storage it points to. Signed-off-by: Adrian Reber --- criu/files.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/criu/files.c b/criu/files.c index 38b12ee4f..ffdaa459f 100644 --- a/criu/files.c +++ b/criu/files.c @@ -1247,6 +1247,8 @@ int close_old_fds(void) ret = sscanf(de->d_name, "%d", &fd); if (ret != 1) { pr_err("Can't parse %s\n", de->d_name); + closedir(dir); + close_pid_proc(); return -1; } From 8f3b86ae842d4b8da99f32ec416ac3db9a9ddb43 Mon Sep 17 00:00:00 2001 From: Adrian Reber Date: Tue, 14 May 2019 18:01:31 +0000 Subject: [PATCH 119/249] files-reg: fix coverity NULL_RETURNS criu-3.12/criu/files-reg.c:1574: dereference: Dereferencing "rmi", which is known to be "NULL". criu-3.12/criu/files-reg.c:1582: dereference: Dereferencing "tmi", which is known to be "NULL". Signed-off-by: Adrian Reber --- criu/files-reg.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/criu/files-reg.c b/criu/files-reg.c index b7e043841..d982126c0 100644 --- a/criu/files-reg.c +++ b/criu/files-reg.c @@ -1594,6 +1594,9 @@ static int rfi_remap(struct reg_file_info *rfi, int *level) } mi = lookup_mnt_id(rfi->rfe->mnt_id); + if (mi == NULL) + return -1; + if (rfi->rfe->mnt_id == rfi->remap->rmnt_id) { /* Both links on the same mount point */ tmi = mi; @@ -1603,6 +1606,8 @@ static int rfi_remap(struct reg_file_info *rfi, int *level) } rmi = lookup_mnt_id(rfi->remap->rmnt_id); + if (rmi == NULL) + return -1; /* * Find the common bind-mount. We know that one mount point was From 2eb6716a3e382a66b2ca4c3a19710a5430e22e6a Mon Sep 17 00:00:00 2001 From: Adrian Reber Date: Wed, 15 May 2019 08:06:55 +0000 Subject: [PATCH 120/249] image: fix clang 'dereference of a null pointer' criu-3.12/criu/include/image.h:129:9: warning: Dereference of null pointer Signed-off-by: Adrian Reber --- criu/include/image.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/criu/include/image.h b/criu/include/image.h index 48ba3ec00..2baa39496 100644 --- a/criu/include/image.h +++ b/criu/include/image.h @@ -133,6 +133,8 @@ extern int open_image_lazy(struct cr_img *img); static inline int img_raw_fd(struct cr_img *img) { + if (!img) + return -1; if (lazy_image(img) && open_image_lazy(img)) return -1; From 3b7fa180af3bf7001afa9d18ff663298bfc55e8b Mon Sep 17 00:00:00 2001 From: Adrian Reber Date: Tue, 14 May 2019 16:51:50 +0000 Subject: [PATCH 121/249] log: fix coverity OVERRUN This fixes a coverity buffer overflow warning: criu-3.12/criu/log.c:344: overrun-local: Overrunning array of 1024 bytes at byte offset 1031 by dereferencing pointer "early_log_buffer + early_log_buf_off + log_size". [Note: The source code implementation of the function has been overridden by a builtin model.] Signed-off-by: Adrian Reber --- criu/log.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/criu/log.c b/criu/log.c index 1e43f663d..8bdf83534 100644 --- a/criu/log.c +++ b/criu/log.c @@ -320,7 +320,7 @@ static void early_vprint(const char *format, unsigned int loglevel, va_list para unsigned int log_size = 0; struct early_log_hdr *hdr; - if (early_log_buf_off >= EARLY_LOG_BUF_LEN) + if ((early_log_buf_off + sizeof(hdr)) >= EARLY_LOG_BUF_LEN) return; /* Save loglevel */ From d3f2fb9f21120cfd601377b934c58466bd1d902e Mon Sep 17 00:00:00 2001 From: Adrian Reber Date: Tue, 14 May 2019 16:55:20 +0000 Subject: [PATCH 122/249] libcriu: fix coverity RESOURCE_LEAK criu-3.12/lib/c/criu.c:255: leaked_storage: Variable "rpc" going out of scope leaks the storage it points to. Signed-off-by: Adrian Reber --- lib/c/criu.c | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/c/criu.c b/lib/c/criu.c index 9e36a9795..c7a96b82e 100644 --- a/lib/c/criu.c +++ b/lib/c/criu.c @@ -252,6 +252,7 @@ int criu_local_init_opts(criu_opts **o) if (opts == NULL) { perror("Can't allocate memory for criu opts"); criu_local_free_opts(opts); + free(rpc); return -1; } From 1dc85028e955a9834f344a189e54be5952630631 Mon Sep 17 00:00:00 2001 From: Adrian Reber Date: Wed, 15 May 2019 14:24:37 +0000 Subject: [PATCH 123/249] lib/c: fix coverity DEADCODE criu-3.12/lib/c/criu.c:869: dead_error_line: Execution cannot reach this statement: "free(ptr);". criu-3.12/lib/c/criu.c:906: dead_error_line: Execution cannot reach this statement: "free(ptr);". Signed-off-by: Adrian Reber --- lib/c/criu.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/lib/c/criu.c b/lib/c/criu.c index c7a96b82e..17d5c3983 100644 --- a/lib/c/criu.c +++ b/lib/c/criu.c @@ -866,8 +866,6 @@ int criu_local_add_enable_fs(criu_opts *opts, const char *fs) err: if (str) free(str); - if (ptr) - free(ptr); return -ENOMEM; } @@ -903,8 +901,6 @@ int criu_local_add_skip_mnt(criu_opts *opts, const char *mnt) err: if (str) free(str); - if (ptr) - free(ptr); return -ENOMEM; } From 5f78a1af6daba2df97891506b16cc4e03a8c615b Mon Sep 17 00:00:00 2001 From: Adrian Reber Date: Wed, 15 May 2019 05:49:30 +0000 Subject: [PATCH 124/249] lsm: fix clang 'Use of memory after it is freed' criu-3.12/criu/lsm.c:257:3: warning: Use of memory after it is freed Signed-off-by: Adrian Reber --- criu/lsm.c | 1 + 1 file changed, 1 insertion(+) diff --git a/criu/lsm.c b/criu/lsm.c index 420585ba4..9d7e55c11 100644 --- a/criu/lsm.c +++ b/criu/lsm.c @@ -89,6 +89,7 @@ static int selinux_get_label(pid_t pid, char **output) if (!pos) { pr_err("Invalid selinux context %s\n", (char *)ctx); xfree(*output); + *output = NULL; goto err; } From d95c04d47a16b32f39e1ad803981d606342ee6dc Mon Sep 17 00:00:00 2001 From: Adrian Reber Date: Wed, 15 May 2019 09:39:55 +0000 Subject: [PATCH 125/249] mem: fix coverity RESOURCE_LEAK criu-3.12/criu/mem.c:597:3: warning: Value stored to 'ret' is never read criu-3.12/criu/mem.c:632: leaked_storage: Variable "img" going out of scope leaks the storage it points to. Signed-off-by: Adrian Reber --- criu/mem.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/criu/mem.c b/criu/mem.c index df87ed5b0..f79e04cc4 100644 --- a/criu/mem.c +++ b/criu/mem.c @@ -594,7 +594,6 @@ int prepare_mm_pid(struct pstree_item *i) if (!vma) break; - ret = 0; ri->vmas.nr++; if (!img) vma->e = ri->mm->vmas[vn++]; @@ -603,6 +602,7 @@ int prepare_mm_pid(struct pstree_item *i) if (ret <= 0) { xfree(vma); close_image(img); + img = NULL; break; } } @@ -629,6 +629,8 @@ int prepare_mm_pid(struct pstree_item *i) break; } + if (img) + close_image(img); return ret; } From 38487fad5f83faa6c7ef16b429ab285f8c27babc Mon Sep 17 00:00:00 2001 From: Adrian Reber Date: Wed, 15 May 2019 09:47:17 +0000 Subject: [PATCH 126/249] net: fix coverity RESOURCE_LEAK criu-3.12/criu/net.c:2043: overwrite_var: Overwriting "img" in "img = open_image_at(-1, CR_FD_IP6TABLES, 0UL, pid)" leaks the storage that "img" points to. Signed-off-by: Adrian Reber --- criu/net.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/criu/net.c b/criu/net.c index b9f6669c3..fe9b51add 100644 --- a/criu/net.c +++ b/criu/net.c @@ -1941,13 +1941,13 @@ static int restore_ip_dump(int type, int pid, char *cmd) if (written < n) { pr_perror("Failed to write to tmpfile " "[written: %d; total: %d]", written, n); - return -1; + goto close; } } if (fseek(tmp_file, 0, SEEK_SET)) { pr_perror("Failed to set file position to beginning of tmpfile"); - return -1; + goto close; } if (img) { @@ -1955,6 +1955,7 @@ static int restore_ip_dump(int type, int pid, char *cmd) close_image(img); } +close: if(fclose(tmp_file)) { pr_perror("Failed to close tmpfile"); } @@ -2059,6 +2060,7 @@ static inline int restore_iptables(int pid) return -1; if (empty_image(img)) { ret = 0; + close_image(img); goto ipt6; } From 2cabbab7d24d637be423643d0b33805c6bf90371 Mon Sep 17 00:00:00 2001 From: Adrian Reber Date: Wed, 15 May 2019 11:57:05 +0000 Subject: [PATCH 127/249] pagemap: fix clang 'free released memory' criu-3.12/criu/pagemap.c:460:2: warning: Attempt to free released memory Signed-off-by: Adrian Reber --- criu/pagemap.c | 1 + 1 file changed, 1 insertion(+) diff --git a/criu/pagemap.c b/criu/pagemap.c index 8d2e16320..070240e72 100644 --- a/criu/pagemap.c +++ b/criu/pagemap.c @@ -490,6 +490,7 @@ static void free_pagemaps(struct page_read *pr) pagemap_entry__free_unpacked(pr->pmes[i], NULL); xfree(pr->pmes); + pr->pmes = NULL; } static void advance_piov(struct page_read_iov *piov, ssize_t len) From 57f66e8ff7dc58627f5d005ac692dac0dc0f6ce3 Mon Sep 17 00:00:00 2001 From: Adrian Reber Date: Wed, 15 May 2019 11:59:05 +0000 Subject: [PATCH 128/249] pagemap: fix coverity FORWARD_NULL criu-3.12/criu/pagemap.c:694: var_deref_model: Passing "pr" to "free_pagemaps", which dereferences null "pr->pmes" Signed-off-by: Adrian Reber --- criu/pagemap.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/criu/pagemap.c b/criu/pagemap.c index 070240e72..a19969b2f 100644 --- a/criu/pagemap.c +++ b/criu/pagemap.c @@ -738,11 +738,13 @@ static int init_pagemaps(struct page_read *pr) pr->nr_pmes++; if (pr->nr_pmes >= nr_pmes) { + PagemapEntry **new; nr_pmes += nr_realloc; - pr->pmes = xrealloc(pr->pmes, + new = xrealloc(pr->pmes, nr_pmes * sizeof(*pr->pmes)); - if (!pr->pmes) + if (!new) goto free_pagemaps; + pr->pmes = new; } } From ae7bd0868874f0b654e3650417dac3a2ec6434ad Mon Sep 17 00:00:00 2001 From: Adrian Reber Date: Wed, 15 May 2019 11:48:14 +0000 Subject: [PATCH 129/249] page-xfer: fix clang 'value is never read' criu-3.12/criu/page-xfer.c:988:3: warning: Value stored to 'ret' is never read Signed-off-by: Adrian Reber --- criu/page-xfer.c | 1 - 1 file changed, 1 deletion(-) diff --git a/criu/page-xfer.c b/criu/page-xfer.c index e0ee58c0f..1876c0ec6 100644 --- a/criu/page-xfer.c +++ b/criu/page-xfer.c @@ -1007,7 +1007,6 @@ int cr_page_server(bool daemon_mode, bool lazy_dump, int cfd) return -1; if (opts.ps_socket != -1) { - ret = 0; ask = opts.ps_socket; pr_info("Re-using ps socket %d\n", ask); goto no_server; From b83c049081e175bfc93f99896a7fb992d8d67b72 Mon Sep 17 00:00:00 2001 From: Adrian Reber Date: Wed, 15 May 2019 12:02:40 +0000 Subject: [PATCH 130/249] pie/restorer: fix clang 'value is never read' criu-3.12/criu/pie/restorer.c:1514:2: warning: Value stored to 'ret' is never read Signed-off-by: Adrian Reber --- criu/pie/restorer.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/criu/pie/restorer.c b/criu/pie/restorer.c index 4f42605a0..f2db115ff 100644 --- a/criu/pie/restorer.c +++ b/criu/pie/restorer.c @@ -1511,8 +1511,6 @@ long __export_restore_task(struct task_restore_args *args) } } - ret = 0; - /* * Tune up the task fields. */ From 7d5154b2decfefa529ee9e76c2f4011b043a1c67 Mon Sep 17 00:00:00 2001 From: Adrian Reber Date: Wed, 15 May 2019 12:17:35 +0000 Subject: [PATCH 131/249] proc_parse: fix coverity RESOURCE_LEAK criu-3.12/criu/proc_parse.c:2280: leaked_storage: Variable "dir" going out of scope leaks the storage it points to. Signed-off-by: Adrian Reber Signed-off-by: Andrei Vagin --- criu/proc_parse.c | 1 + 1 file changed, 1 insertion(+) diff --git a/criu/proc_parse.c b/criu/proc_parse.c index 1a5722eaf..3d852d755 100644 --- a/criu/proc_parse.c +++ b/criu/proc_parse.c @@ -2277,6 +2277,7 @@ int parse_threads(int pid, struct pid **_t, int *_n) tmp = xrealloc(t, nr * sizeof(struct pid)); if (!tmp) { xfree(t); + closedir(dir); return -1; } t = tmp; From a9b31852cd8c8ff21c03b85f76ad415ebdd39602 Mon Sep 17 00:00:00 2001 From: Adrian Reber Date: Tue, 14 May 2019 16:56:39 +0000 Subject: [PATCH 132/249] sk-inet: fix coverity RESOURCE_LEAK criu-3.12/criu/sk-inet.c:822: leaked_handle: Handle variable "sk" going out of scope leaks the handle. Signed-off-by: Adrian Reber --- criu/sk-inet.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/criu/sk-inet.c b/criu/sk-inet.c index ebae53113..fed3181f0 100644 --- a/criu/sk-inet.c +++ b/criu/sk-inet.c @@ -823,7 +823,7 @@ static int open_inet_sk(struct file_desc *d, int *new_fd) } if (reset_setsockcreatecon()) - return -1; + goto err; if (ie->v6only) { if (restore_opt(sk, SOL_IPV6, IPV6_V6ONLY, &yes) == -1) From ab23469995b5bcef288155c032dbea4eacba08a0 Mon Sep 17 00:00:00 2001 From: Adrian Reber Date: Wed, 15 May 2019 12:28:45 +0000 Subject: [PATCH 133/249] sk-inet: fix clang 'potential memory leak' criu-3.12/criu/sk-inet.c:581:2: warning: Potential leak of memory pointed to by 'ie.ifname' Signed-off-by: Adrian Reber --- criu/sk-inet.c | 1 + 1 file changed, 1 insertion(+) diff --git a/criu/sk-inet.c b/criu/sk-inet.c index fed3181f0..90ab492ed 100644 --- a/criu/sk-inet.c +++ b/criu/sk-inet.c @@ -579,6 +579,7 @@ static int do_dump_one_inet_fd(int lfd, u32 id, const struct fd_parms *p, int fa release_skopts(&skopts); xfree(ie.src_addr); xfree(ie.dst_addr); + xfree(ie.ifname); return err; } From ef4840a125470d18416fcfd0ec2c6461e4e2d058 Mon Sep 17 00:00:00 2001 From: Adrian Reber Date: Wed, 15 May 2019 12:33:40 +0000 Subject: [PATCH 134/249] sk-queue: fix clang 'potential memory leak' criu-3.12/criu/sk-queue.c:272:6: warning: Potential leak of memory pointed to by 'pe.scm' Signed-off-by: Adrian Reber --- criu/sk-queue.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/criu/sk-queue.c b/criu/sk-queue.c index fdf610170..776eb5aaf 100644 --- a/criu/sk-queue.c +++ b/criu/sk-queue.c @@ -273,6 +273,8 @@ int dump_sk_queue(int sock_fd, int sock_id) pr_perror("setsockopt failed on restore"); ret = -1; } + if (pe.scm) + release_cmsg(&pe); err_brk: xfree(data); return ret; From 399a241011603dcae9a761308b5110e777b5b261 Mon Sep 17 00:00:00 2001 From: Adrian Reber Date: Wed, 15 May 2019 12:38:32 +0000 Subject: [PATCH 135/249] sk-unix: fix clang 'value is never read' criu-3.12/criu/sk-unix.c:1545:5: warning: Value stored to 'ret' is never read Signed-off-by: Adrian Reber --- criu/sk-unix.c | 1 - 1 file changed, 1 deletion(-) diff --git a/criu/sk-unix.c b/criu/sk-unix.c index 26123515c..35a6befa7 100644 --- a/criu/sk-unix.c +++ b/criu/sk-unix.c @@ -1542,7 +1542,6 @@ static int bind_on_deleted(int sk, struct unix_sk_info *ui) pos = strrchr(path, '/')) { *pos = '\0'; if (rmdir(path)) { - ret = - errno; pr_perror("ghost: Can't remove directory %s on id %#x ino %d", path, ui->ue->id, ui->ue->ino); return -1; From 9aecd273bd8ca9883c326c710abb0695f72007b8 Mon Sep 17 00:00:00 2001 From: Adrian Reber Date: Wed, 15 May 2019 12:40:50 +0000 Subject: [PATCH 136/249] sk-unix: fix coverity RESOURCE_LEAK criu-3.12/criu/sk-unix.c:1893: leaked_handle: Handle variable "sk" going out of scope leaks the handle. Signed-off-by: Adrian Reber --- criu/sk-unix.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/criu/sk-unix.c b/criu/sk-unix.c index 35a6befa7..c339ccf56 100644 --- a/criu/sk-unix.c +++ b/criu/sk-unix.c @@ -1888,13 +1888,16 @@ static int open_unixsk_standalone(struct unix_sk_info *ui, int *new_fd) } } - if (bind_unix_sk(sk, ui)) + if (bind_unix_sk(sk, ui)) { + close(sk); return -1; + } if (ui->ue->state == TCP_LISTEN) { pr_info("\tPutting %d into listen state\n", ui->ue->ino); if (listen(sk, ui->ue->backlog) < 0) { pr_perror("Can't make usk listen"); + close(sk); return -1; } ui->listen = 1; From ee40a11b3e9facf3d8c601b3b68bf956615922e8 Mon Sep 17 00:00:00 2001 From: Adrian Reber Date: Tue, 14 May 2019 18:22:29 +0000 Subject: [PATCH 137/249] util: fix coverity FORWARD_NULL criu-3.12/criu/util.c:505: var_deref_model: Passing null pointer "dir" to "dirfd", which dereferences it. (The dereference is assumed on the basis of the 'nonnull' parameter attribute.) Signed-off-by: Adrian Reber --- criu/util.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/criu/util.c b/criu/util.c index 41b6915b7..3f6bdde7f 100644 --- a/criu/util.c +++ b/criu/util.c @@ -522,8 +522,10 @@ static int close_fds(int minfd) int fd, ret, dfd; dir = opendir("/proc/self/fd"); - if (dir == NULL) + if (dir == NULL) { pr_perror("Can't open /proc/self/fd"); + return -1; + } dfd = dirfd(dir); while ((de = readdir(dir))) { From 720550616d270dd0b3f942337d9a04420428ab73 Mon Sep 17 00:00:00 2001 From: Adrian Reber Date: Wed, 15 May 2019 07:59:23 +0000 Subject: [PATCH 138/249] image: fix coverity RESOURCE_LEAK criu-3.12/criu/image.c:103: leaked_storage: Variable "img" going out of scope leaks the storage it points to. Signed-off-by: Adrian Reber --- criu/image.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/criu/image.c b/criu/image.c index 173ca21ad..78947ab5f 100644 --- a/criu/image.c +++ b/criu/image.c @@ -93,6 +93,7 @@ int check_img_inventory(void) int write_img_inventory(InventoryEntry *he) { struct cr_img *img; + int ret; pr_info("Writing image inventory (version %u)\n", CRTOOLS_IMAGES_V1); @@ -100,11 +101,12 @@ int write_img_inventory(InventoryEntry *he) if (!img) return -1; - if (pb_write_one(img, he, PB_INVENTORY) < 0) - return -1; + ret = pb_write_one(img, he, PB_INVENTORY); xfree(he->root_ids); close_image(img); + if (ret < 0) + return -1; return 0; } From c49bd0db72079a64a9ab31497931c005e963bf50 Mon Sep 17 00:00:00 2001 From: Adrian Reber Date: Wed, 15 May 2019 12:36:49 +0000 Subject: [PATCH 139/249] sk-unix: fix coverity NULL_RETURNS criu-3.12/criu/sk-unix.c:1225: dereference: Dereferencing "ns", which is known to be "NULL". Signed-off-by: Adrian Reber --- criu/sk-unix.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/criu/sk-unix.c b/criu/sk-unix.c index c339ccf56..f0620e676 100644 --- a/criu/sk-unix.c +++ b/criu/sk-unix.c @@ -1208,14 +1208,14 @@ static int prep_unix_sk_cwd(struct unix_sk_info *ui, int *prev_cwd_fd, if (prev_root_fd && (root_ns_mask & CLONE_NEWNS)) { if (ui->ue->mnt_id >= 0) { ns = lookup_nsid_by_mnt_id(ui->ue->mnt_id); - if (ns == NULL) - goto err; } else { if (root == NULL) root = lookup_ns_by_id(root_item->ids->mnt_ns_id, &mnt_ns_desc); ns = root; } + if (ns == NULL) + goto err; *prev_root_fd = open("/", O_RDONLY); if (*prev_root_fd < 0) { pr_perror("Can't open current root"); From 22c23bb63351c3914f2a9c55cf7fb3cb09a4f3a8 Mon Sep 17 00:00:00 2001 From: Radostin Stoyanov Date: Sat, 18 May 2019 16:26:53 +0100 Subject: [PATCH 140/249] python: Drop six dependency From the python-six module is used only six.string_types in the is_string() function. An alternative solution is to use basestring with additional if statement for Python 3 compatibility. This change avoids the dependency on the six module. However, this module is required by junit_xml and it is not listed as a dependency in the CentOS 7 package python2-junit_xml. Signed-off-by: Radostin Stoyanov --- lib/py/images/pb2dict.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/lib/py/images/pb2dict.py b/lib/py/images/pb2dict.py index 18d4c68eb..4e2c171d5 100644 --- a/lib/py/images/pb2dict.py +++ b/lib/py/images/pb2dict.py @@ -4,7 +4,7 @@ from ipaddress import IPv6Address import socket import collections -import os, six +import os # pb2dict and dict2pb are methods to convert pb to/from dict. # Inspired by: @@ -216,7 +216,10 @@ def get_bytes_dec(field): return decode_base64 def is_string(value): - return isinstance(value, six.string_types) + # Python 3 compatibility + if not hasattr(__builtins__, "basestring"): + basestring = (str, bytes) + return isinstance(value, basestring) def _pb2dict_cast(field, value, pretty = False, is_hex = False): if not is_hex: From c06b4956c8eee199a593bb4807ec7813452928e9 Mon Sep 17 00:00:00 2001 From: Radostin Stoyanov Date: Wed, 22 May 2019 15:50:30 +0100 Subject: [PATCH 141/249] make: Use asciidoctor by default The final release of asciidoc was on Sep 29, 2017 and the development is continued under asciidoctor. With commit 0493724 (Documentation: Allow to use asciidoctor for formatting man pages) was added support for this tool by introducing USE_ASCIIDOCTOR. However, using asciidoctor by default might be a better option. With this change CRIU will use asciidoctor if installed. Otherwise, it will fallback to asciidoc. Signed-off-by: Radostin Stoyanov --- scripts/build/Dockerfile.alpine | 2 +- scripts/build/Dockerfile.centos | 2 +- scripts/build/Dockerfile.fedora.tmpl | 2 +- scripts/nmk/scripts/tools.mk | 2 ++ scripts/travis/travis-tests | 2 +- 5 files changed, 6 insertions(+), 4 deletions(-) diff --git a/scripts/build/Dockerfile.alpine b/scripts/build/Dockerfile.alpine index aab6184d7..a91e01637 100644 --- a/scripts/build/Dockerfile.alpine +++ b/scripts/build/Dockerfile.alpine @@ -36,7 +36,7 @@ RUN apk add \ bash \ go \ e2fsprogs \ - asciidoc xmlto + asciidoctor # The rpc test cases are running as user #1000, let's add the user RUN adduser -u 1000 -D test diff --git a/scripts/build/Dockerfile.centos b/scripts/build/Dockerfile.centos index d8e70ac47..2ed3a2db9 100644 --- a/scripts/build/Dockerfile.centos +++ b/scripts/build/Dockerfile.centos @@ -32,7 +32,7 @@ RUN yum install -y \ which \ e2fsprogs \ python2-pip \ - asciidoc xmlto + rubygem-asciidoctor COPY . /criu WORKDIR /criu diff --git a/scripts/build/Dockerfile.fedora.tmpl b/scripts/build/Dockerfile.fedora.tmpl index 616b9ec42..22ebaed9c 100644 --- a/scripts/build/Dockerfile.fedora.tmpl +++ b/scripts/build/Dockerfile.fedora.tmpl @@ -33,7 +33,7 @@ RUN dnf install -y \ tar \ which \ e2fsprogs \ - asciidoc xmlto \ + rubygem-asciidoctor \ kmod # Replace coreutils-single with "traditional" coreutils diff --git a/scripts/nmk/scripts/tools.mk b/scripts/nmk/scripts/tools.mk index 8620ded7c..ce3d85dea 100644 --- a/scripts/nmk/scripts/tools.mk +++ b/scripts/nmk/scripts/tools.mk @@ -35,6 +35,8 @@ CTAGS := ctags export RM HOSTLD LD HOSTCC CC CPP AS AR STRIP OBJCOPY OBJDUMP export NM SH MAKE MKDIR AWK PERL PYTHON SH CSCOPE +export USE_ASCIIDOCTOR ?= $(shell which asciidoctor 2>/dev/null) + # # Footer. ____nmk_defined__tools = y diff --git a/scripts/travis/travis-tests b/scripts/travis/travis-tests index 01a2659f6..47ff199cf 100755 --- a/scripts/travis/travis-tests +++ b/scripts/travis/travis-tests @@ -4,7 +4,7 @@ set -x -e TRAVIS_PKGS="protobuf-c-compiler libprotobuf-c0-dev libaio-dev libprotobuf-dev protobuf-compiler libcap-dev libnl-3-dev gcc-multilib gdb bash python-protobuf - libnet-dev util-linux asciidoc xmlto libnl-route-3-dev" + libnet-dev util-linux asciidoctor libnl-route-3-dev" travis_prep () { [ -n "$SKIP_TRAVIS_PREP" ] && return From d17109254ac473a01cbd1ec7e00a0beda9c0579a Mon Sep 17 00:00:00 2001 From: Pavel Emelianov Date: Thu, 23 May 2019 08:58:04 +0000 Subject: [PATCH 142/249] mem: Update stats for overflow page pipes Since commit b5dff62e we skipped updating dump stats for pages that overflowed the page-pipe and thus got flushed in "chunk" mode. Signed-off-by: Pavel Emelyanov Acked-by: Cyrill Gorcunov Signed-off-by: Andrei Vagin --- criu/mem.c | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/criu/mem.c b/criu/mem.c index f79e04cc4..b1d13188b 100644 --- a/criu/mem.c +++ b/criu/mem.c @@ -170,13 +170,14 @@ static int generate_iovs(struct pstree_item *item, struct vma_area *vma, struct u64 *at = &map[PAGE_PFN(*off)]; unsigned long pfn, nr_to_scan; unsigned long pages[3] = {}; + int ret = 0; nr_to_scan = (vma_area_len(vma) - *off) / PAGE_SIZE; for (pfn = 0; pfn < nr_to_scan; pfn++) { unsigned long vaddr; unsigned int ppb_flags = 0; - int ret; + int st; if (!should_dump_page(vma->e, at[pfn])) continue; @@ -195,19 +196,22 @@ static int generate_iovs(struct pstree_item *item, struct vma_area *vma, struct if (has_parent && page_in_parent(at[pfn] & PME_SOFT_DIRTY)) { ret = page_pipe_add_hole(pp, vaddr, PP_HOLE_PARENT); - pages[0]++; + st = 0; } else { ret = page_pipe_add_page(pp, vaddr, ppb_flags); if (ppb_flags & PPB_LAZY && opts.lazy_pages) - pages[1]++; + st = 1; else - pages[2]++; + st = 2; } if (ret) { - *off += pfn * PAGE_SIZE; - return ret; + /* Do not do pfn++, just bail out */ + pr_debug("Pagemap full\n"); + break; } + + pages[st]++; } *off += pfn * PAGE_SIZE; @@ -219,7 +223,7 @@ static int generate_iovs(struct pstree_item *item, struct vma_area *vma, struct pr_info("Pagemap generated: %lu pages (%lu lazy) %lu holes\n", pages[2] + pages[1], pages[1], pages[0]); - return 0; + return ret; } static struct parasite_dump_pages_args *prep_dump_pages_args(struct parasite_ctl *ctl, From 0e7f01bc41c96080301609ab231284d122661d60 Mon Sep 17 00:00:00 2001 From: Pavel Emelianov Date: Thu, 23 May 2019 08:58:35 +0000 Subject: [PATCH 143/249] shmem: Save pages stats too Shmem pages are written in the same set of images as regular pages are, but stats for those are not collected. Fix this, but keep the counts separate to have more info. Signed-off-by: Pavel Emelyanov Acked-by: Cyrill Gorcunov Signed-off-by: Andrei Vagin --- criu/include/stats.h | 4 ++++ criu/shmem.c | 17 +++++++++++++++-- criu/stats.c | 7 +++++++ images/stats.proto | 4 ++++ 4 files changed, 30 insertions(+), 2 deletions(-) diff --git a/criu/include/stats.h b/criu/include/stats.h index 07690b8ea..bab9a0507 100644 --- a/criu/include/stats.h +++ b/criu/include/stats.h @@ -29,6 +29,10 @@ enum { CNT_PAGE_PIPES, CNT_PAGE_PIPE_BUFS, + CNT_SHPAGES_SCANNED, + CNT_SHPAGES_SKIPPED_PARENT, + CNT_SHPAGES_WRITTEN, + DUMP_CNT_NR_STATS, }; diff --git a/criu/shmem.c b/criu/shmem.c index a3c7c5caf..03b088f26 100644 --- a/criu/shmem.c +++ b/criu/shmem.c @@ -11,6 +11,7 @@ #include "image.h" #include "cr_options.h" #include "kerndat.h" +#include "stats.h" #include "page-pipe.h" #include "page-xfer.h" #include "rst-malloc.h" @@ -676,6 +677,7 @@ static int do_dump_one_shmem(int fd, void *addr, struct shmem_info *si) struct page_xfer xfer; int err, ret = -1; unsigned long pfn, nrpages, next_data_pnf = 0, next_hole_pfn = 0; + unsigned long pages[2] = {}; nrpages = (si->size + PAGE_SIZE - 1) / PAGE_SIZE; @@ -693,6 +695,7 @@ static int do_dump_one_shmem(int fd, void *addr, struct shmem_info *si) unsigned int pgstate = PST_DIRTY; bool use_mc = true; unsigned long pgaddr; + int st = -1; if (pfn >= next_hole_pfn && next_data_segment(fd, pfn, &next_data_pnf, &next_hole_pfn)) @@ -714,10 +717,13 @@ static int do_dump_one_shmem(int fd, void *addr, struct shmem_info *si) again: if (pgstate == PST_ZERO) ret = 0; - else if (xfer.parent && page_in_parent(pgstate == PST_DIRTY)) + else if (xfer.parent && page_in_parent(pgstate == PST_DIRTY)) { ret = page_pipe_add_hole(pp, pgaddr, PP_HOLE_PARENT); - else + st = 0; + } else { ret = page_pipe_add_page(pp, pgaddr, 0); + st = 1; + } if (ret == -EAGAIN) { ret = dump_pages(pp, &xfer); @@ -727,8 +733,15 @@ static int do_dump_one_shmem(int fd, void *addr, struct shmem_info *si) goto again; } else if (ret) goto err_xfer; + + if (st >= 0) + pages[st]++; } + cnt_add(CNT_SHPAGES_SCANNED, nrpages); + cnt_add(CNT_SHPAGES_SKIPPED_PARENT, pages[0]); + cnt_add(CNT_SHPAGES_WRITTEN, pages[1]); + ret = dump_pages(pp, &xfer); err_xfer: diff --git a/criu/stats.c b/criu/stats.c index 64679b134..a64383542 100644 --- a/criu/stats.c +++ b/criu/stats.c @@ -165,6 +165,13 @@ void write_stats(int what) ds_entry.page_pipe_bufs = dstats->counts[CNT_PAGE_PIPE_BUFS]; ds_entry.has_page_pipe_bufs = true; + ds_entry.shpages_scanned = dstats->counts[CNT_SHPAGES_SCANNED]; + ds_entry.has_shpages_scanned = true; + ds_entry.shpages_skipped_parent = dstats->counts[CNT_SHPAGES_SKIPPED_PARENT]; + ds_entry.has_shpages_skipped_parent = true; + ds_entry.shpages_written = dstats->counts[CNT_SHPAGES_WRITTEN]; + ds_entry.has_shpages_written = true; + name = "dump"; } else if (what == RESTORE_STATS) { stats.restore = &rs_entry; diff --git a/images/stats.proto b/images/stats.proto index d76503441..68d2f1bbb 100644 --- a/images/stats.proto +++ b/images/stats.proto @@ -16,6 +16,10 @@ message dump_stats_entry { required uint64 pages_lazy = 9; optional uint64 page_pipes = 10; optional uint64 page_pipe_bufs = 11; + + optional uint64 shpages_scanned = 12; + optional uint64 shpages_skipped_parent = 13; + optional uint64 shpages_written = 14; } message restore_stats_entry { From 81607db80b1f955268012c7137f3eb23434e767b Mon Sep 17 00:00:00 2001 From: Pavel Emelianov Date: Thu, 23 May 2019 08:59:05 +0000 Subject: [PATCH 144/249] zdtm: Check pages stats after dump After dump command -- verify that the amount of bytes counted in stats-dump matches the real sizes of pages-*.img files. Signed-off-by: Pavel Emelyanov Acked-by: Cyrill Gorcunov Signed-off-by: Andrei Vagin --- test/zdtm.py | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/test/zdtm.py b/test/zdtm.py index 44fa92ee3..9f483693d 100755 --- a/test/zdtm.py +++ b/test/zdtm.py @@ -1099,13 +1099,32 @@ def __criu_act(self, action, opts = [], log = None, nowait = False): else: raise test_fail_exc("CRIU %s" % action) + def __stats_file(self, action): + return os.path.join(self.__ddir(), "stats-%s" % action) + def show_stats(self, action): if not self.__show_stats: return - subprocess.Popen([self.__crit_bin, "show", - os.path.join(self.__dump_path, - str(self.__iter), "stats-%s" % action)]).wait() + subprocess.Popen([self.__crit_bin, "show", self.__stats_file(action)]).wait() + + def check_pages_counts(self): + stats_written = -1 + with open(self.__stats_file("dump"), 'rb') as stfile: + stats = crpc.images.load(stfile) + stent = stats['entries'][0]['dump'] + stats_written = int(stent['shpages_written']) + int(stent['pages_written']) + + real_written = 0 + for f in os.listdir(self.__ddir()): + if f.startswith('pages-'): + real_written += os.path.getsize(os.path.join(self.__ddir(), f)) + + r_pages = real_written / 4096 + r_off = real_written % 4096 + if (stats_written != r_pages) or (r_off != 0): + print("ERROR: bad page counts, stats = %d real = %d(%d)" % (stats_written, r_pages, r_off)) + raise test_fail_exc("page counts mismatch") def dump(self, action, opts = []): self.__iter += 1 @@ -1174,6 +1193,7 @@ def dump(self, action, opts = []): self.__criu_act("dedup", opts = []) self.show_stats("dump") + self.check_pages_counts() if self.__leave_stopped: pstree_check_stopped(self.__test.getpid()) From 05d28c424990376c9da8b51979e22c2a42eb3158 Mon Sep 17 00:00:00 2001 From: Pavel Emelianov Date: Thu, 23 May 2019 08:59:26 +0000 Subject: [PATCH 145/249] stats: Make dstats shmem Dumping shmem segments causing stats "pages written" counter to mismatch the real pages* sizes. This is due to ipcns' dumping happens in another process and the relevant shmem dumping counters remain in its address space. Signed-off-by: Pavel Emelyanov Signed-off-by: Andrei Vagin --- criu/stats.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/criu/stats.c b/criu/stats.c index a64383542..7410b5ced 100644 --- a/criu/stats.c +++ b/criu/stats.c @@ -201,7 +201,15 @@ void write_stats(int what) int init_stats(int what) { if (what == DUMP_STATS) { - dstats = xzalloc(sizeof(*dstats)); + /* + * Dumping happens via one process most of the time, + * so we are typically OK with the plain malloc, but + * when dumping namespaces we fork() a separate process + * for it and when it goes and dumps shmem segments + * it will alter the CNT_SHPAGES_ counters, so we need + * to have them in shmem. + */ + dstats = shmalloc(sizeof(*dstats)); return dstats ? 0 : -1; } From aad606a988b7c3996b36741c0da7753f97f524ab Mon Sep 17 00:00:00 2001 From: Andrei Vagin Date: Tue, 21 May 2019 00:13:27 -0700 Subject: [PATCH 146/249] util: use F_DUPFD when we don't want to overwrite an existing descriptor Right now we use fcntl(F_GETFD) to check whether a target descriptor is used and then we call dup2(). Actually, we can do this for one system call. Cc: Cyrill Gorcunov Signed-off-by: Andrei Vagin Reviewed-by: Cyrill Gorcunov Signed-off-by: Andrei Vagin --- criu/include/servicefd.h | 11 ----------- criu/servicefd.c | 37 +++++++++++++++++++++++-------------- criu/util.c | 18 +++++++++--------- 3 files changed, 32 insertions(+), 34 deletions(-) diff --git a/criu/include/servicefd.h b/criu/include/servicefd.h index 7be472cf4..986c46af5 100644 --- a/criu/include/servicefd.h +++ b/criu/include/servicefd.h @@ -35,17 +35,6 @@ struct pstree_item; extern bool sfds_protected; -#define sfd_verify_target(_type, _old_fd, _new_fd) \ - ({ \ - int __ret = 0; \ - if (fcntl(_new_fd, F_GETFD) != -1 && errno != EBADF) { \ - pr_err("%s busy target %d -> %d\n", \ - sfd_type_name(_type), _old_fd, _new_fd); \ - __ret = -1; \ - } \ - __ret; \ - }) - extern const char *sfd_type_name(enum sfd_type type); extern int init_service_fd(void); extern int get_service_fd(enum sfd_type type); diff --git a/criu/servicefd.c b/criu/servicefd.c index 82147921c..dc423895b 100644 --- a/criu/servicefd.c +++ b/criu/servicefd.c @@ -153,6 +153,7 @@ static void sfds_protection_bug(enum sfd_type type) int install_service_fd(enum sfd_type type, int fd) { int sfd = __get_service_fd(type, service_fd_id); + int tmp; BUG_ON((int)type <= SERVICE_FD_MIN || (int)type >= SERVICE_FD_MAX); if (sfds_protected && !test_bit(type, sfd_map)) @@ -166,16 +167,19 @@ int install_service_fd(enum sfd_type type, int fd) return fd; } - if (!test_bit(type, sfd_map)) { - if (sfd_verify_target(type, fd, sfd)) - return -1; - } - - if (dup3(fd, sfd, O_CLOEXEC) != sfd) { + if (!test_bit(type, sfd_map)) + tmp = fcntl(fd, F_DUPFD, sfd); + else + tmp = dup3(fd, sfd, O_CLOEXEC); + if (tmp < 0) { pr_perror("%s dup %d -> %d failed", sfd_type_name(type), fd, sfd); close(fd); return -1; + } else if (tmp != sfd) { + pr_err("%s busy target %d -> %d\n", sfd_type_name(type), fd, sfd); + close(fd); + return -1; } set_bit(type, sfd_map); @@ -201,25 +205,30 @@ int close_service_fd(enum sfd_type type) return 0; } -static void move_service_fd(struct pstree_item *me, int type, int new_id, int new_base) +static int move_service_fd(struct pstree_item *me, int type, int new_id, int new_base) { int old = get_service_fd(type); int new = new_base - type - SERVICE_FD_MAX * new_id; int ret; if (old < 0) - return; + return 0; if (!test_bit(type, sfd_map)) - sfd_verify_target(type, old, new); - - ret = dup2(old, new); + ret = fcntl(old, F_DUPFD, new); + else + ret = dup2(old, new); if (ret == -1) { - if (errno != EBADF) - pr_perror("%s unable to clone %d->%d", - sfd_type_name(type), old, new); + pr_perror("%s unable to clone %d->%d", + sfd_type_name(type), old, new); + return -1; + } else if (ret != new) { + pr_err("%s busy target %d -> %d\n", sfd_type_name(type), old, new); + return -1; } else if (!(rsti(me)->clone_flags & CLONE_FILES)) close(old); + + return 0; } static int choose_service_fd_base(struct pstree_item *me) diff --git a/criu/util.c b/criu/util.c index 3f6bdde7f..31cdee1ff 100644 --- a/criu/util.c +++ b/criu/util.c @@ -229,19 +229,19 @@ int reopen_fd_as_safe(char *file, int line, int new_fd, int old_fd, bool allow_r int tmp; if (old_fd != new_fd) { - if (!allow_reuse_fd) { - if (fcntl(new_fd, F_GETFD) != -1 || errno != EBADF) { - pr_err("fd %d already in use (called at %s:%d)\n", - new_fd, file, line); - return -1; - } - } - - tmp = dup2(old_fd, new_fd); + if (!allow_reuse_fd) + tmp = fcntl(old_fd, F_DUPFD, new_fd); + else + tmp = dup2(old_fd, new_fd); if (tmp < 0) { pr_perror("Dup %d -> %d failed (called at %s:%d)", old_fd, new_fd, file, line); return tmp; + } else if (tmp != new_fd) { + close(tmp); + pr_err("fd %d already in use (called at %s:%d)\n", + new_fd, file, line); + return -1; } /* Just to have error message if failed */ From 5be8ab0328bcfdc3cc07f4d2380e371680f2ec46 Mon Sep 17 00:00:00 2001 From: Andrei Vagin Date: Sun, 26 May 2019 20:53:57 -0700 Subject: [PATCH 147/249] test/s390: add a new patch to xtables libraries Signed-off-by: Andrei Vagin --- test/zdtm/static/socket-tcp-reseted.desc | 6 +++--- test/zdtm/static/socket-tcp-syn-sent.desc | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/test/zdtm/static/socket-tcp-reseted.desc b/test/zdtm/static/socket-tcp-reseted.desc index c0e83aad7..94425b44e 100644 --- a/test/zdtm/static/socket-tcp-reseted.desc +++ b/test/zdtm/static/socket-tcp-reseted.desc @@ -1,8 +1,8 @@ { 'deps': [ '/bin/sh', '/sbin/iptables', - '/usr/lib64/xtables/libxt_tcp.so|/lib/xtables/libxt_tcp.so|/usr/lib/powerpc64le-linux-gnu/xtables/libxt_tcp.so|/usr/lib/x86_64-linux-gnu/xtables/libxt_tcp.so|/usr/lib/xtables/libxt_tcp.so', - '/usr/lib64/xtables/libxt_standard.so|/lib/xtables/libxt_standard.so|/usr/lib/powerpc64le-linux-gnu/xtables/libxt_standard.so|/usr/lib/x86_64-linux-gnu/xtables/libxt_standard.so|/usr/lib/xtables/libxt_standard.so', - '/usr/lib64/xtables/libipt_REJECT.so|/lib/xtables/libipt_REJECT.so|/usr/lib/powerpc64le-linux-gnu/xtables/libipt_REJECT.so|/usr/lib/x86_64-linux-gnu/xtables/libipt_REJECT.so|/usr/lib/xtables/libipt_REJECT.so', + '/usr/lib64/xtables/libxt_tcp.so|/lib/xtables/libxt_tcp.so|/usr/lib/powerpc64le-linux-gnu/xtables/libxt_tcp.so|/usr/lib/x86_64-linux-gnu/xtables/libxt_tcp.so|/usr/lib/xtables/libxt_tcp.so|/usr/lib/s390x-linux-gnu/xtables/libxt_tcp.so', + '/usr/lib64/xtables/libxt_standard.so|/lib/xtables/libxt_standard.so|/usr/lib/powerpc64le-linux-gnu/xtables/libxt_standard.so|/usr/lib/x86_64-linux-gnu/xtables/libxt_standard.so|/usr/lib/xtables/libxt_standard.so|/usr/lib/s390x-linux-gnu/xtables/libxt_standard.so', + '/usr/lib64/xtables/libipt_REJECT.so|/lib/xtables/libipt_REJECT.so|/usr/lib/powerpc64le-linux-gnu/xtables/libipt_REJECT.so|/usr/lib/x86_64-linux-gnu/xtables/libipt_REJECT.so|/usr/lib/xtables/libipt_REJECT.so|/usr/lib/s390x-linux-gnu/xtables/libipt_REJECT.so', ], 'opts': '--tcp-established', 'flags': 'suid nouser samens', diff --git a/test/zdtm/static/socket-tcp-syn-sent.desc b/test/zdtm/static/socket-tcp-syn-sent.desc index c5d1faa0e..b9f3d5e6d 100644 --- a/test/zdtm/static/socket-tcp-syn-sent.desc +++ b/test/zdtm/static/socket-tcp-syn-sent.desc @@ -1,7 +1,7 @@ { 'deps': [ '/bin/sh', '/sbin/iptables', - '/usr/lib64/xtables/libxt_tcp.so|/lib/xtables/libxt_tcp.so|/usr/lib/powerpc64le-linux-gnu/xtables/libxt_tcp.so|/usr/lib/x86_64-linux-gnu/xtables/libxt_tcp.so|/usr/lib/xtables/libxt_tcp.so', - '/usr/lib64/xtables/libxt_standard.so|/lib/xtables/libxt_standard.so|/usr/lib/powerpc64le-linux-gnu/xtables/libxt_standard.so|/usr/lib/x86_64-linux-gnu/xtables/libxt_standard.so|/usr/lib/xtables/libxt_standard.so', + '/usr/lib64/xtables/libxt_tcp.so|/lib/xtables/libxt_tcp.so|/usr/lib/powerpc64le-linux-gnu/xtables/libxt_tcp.so|/usr/lib/x86_64-linux-gnu/xtables/libxt_tcp.so|/usr/lib/xtables/libxt_tcp.so|/usr/lib/s390x-linux-gnu/xtables/libxt_tcp.so', + '/usr/lib64/xtables/libxt_standard.so|/lib/xtables/libxt_standard.so|/usr/lib/powerpc64le-linux-gnu/xtables/libxt_standard.so|/usr/lib/x86_64-linux-gnu/xtables/libxt_standard.so|/usr/lib/xtables/libxt_standard.so|/usr/lib/s390x-linux-gnu/xtables/libxt_standard.so', ], 'opts': '--tcp-established', 'flags': 'suid nouser samens', From ef4f0315c799b0449c4ecb88d16b4359e463bb13 Mon Sep 17 00:00:00 2001 From: Radostin Stoyanov Date: Sun, 26 May 2019 19:34:15 +0100 Subject: [PATCH 148/249] aarch64: Remove stack pointer from clobber list Since gcc version 9.1 was added the restriction that the clobber list of an inline assembly should not contain the stack pointer register. https://gcc.gnu.org/git/?p=gcc.git;a=commit;h=9d1cdb749a1 In commit 901f5d4 have been fixed most of the build failures related to this gcc restriction. In this patch is resolved a build error that occurs only on aarch64. Signed-off-by: Radostin Stoyanov --- criu/arch/aarch64/include/asm/restore.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/criu/arch/aarch64/include/asm/restore.h b/criu/arch/aarch64/include/asm/restore.h index 2345a579a..3d794ffb5 100644 --- a/criu/arch/aarch64/include/asm/restore.h +++ b/criu/arch/aarch64/include/asm/restore.h @@ -15,7 +15,7 @@ : "r"(new_sp), \ "r"(restore_task_exec_start), \ "r"(task_args) \ - : "sp", "x0", "memory") + : "x0", "memory") static inline void core_get_tls(CoreEntry *pcore, tls_t *ptls) { From 08bdda78317697ef689d4bfd764d7a81228ce687 Mon Sep 17 00:00:00 2001 From: Pavel Emelianov Date: Tue, 28 May 2019 08:47:51 +0000 Subject: [PATCH 149/249] zdtm: Check stats file presence before reading In some cases the stats-dump file can be missing, so do not crash the whole zdtm.py in this case. https://ci.openvz.org/job/CRIU/job/criu-live-migration/job/criu-dev/2362/console Signed-off-by: Pavel Emelyanov --- test/zdtm.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test/zdtm.py b/test/zdtm.py index 9f483693d..0adc22c39 100755 --- a/test/zdtm.py +++ b/test/zdtm.py @@ -1109,6 +1109,9 @@ def show_stats(self, action): subprocess.Popen([self.__crit_bin, "show", self.__stats_file(action)]).wait() def check_pages_counts(self): + if not os.access(self.__stats_file("dump"), os.R_OK): + return + stats_written = -1 with open(self.__stats_file("dump"), 'rb') as stfile: stats = crpc.images.load(stfile) From a835a42f14470c50e2e2bba137fa04323ba75cc8 Mon Sep 17 00:00:00 2001 From: Radostin Stoyanov Date: Tue, 28 May 2019 08:24:58 +0100 Subject: [PATCH 150/249] pb2dict: Fix is_string() check for Python 2 In the __main__ module, __builtins__ is the built-in module builtins. In any other module, __builtins__ is an alias for the dictionary of the builtins module itself. [1] Thus, hasattr(__builtins__, "basestring") would only work in __main__ module. Since pb2dict is part of pycriu and is intended to be called by modules other than __main__, we can assume that __builtins__ would always be a dictionary (not a module). In Python 2, basestring is a superclass for str and unicode. [2] However, the assignment statement creates a variable basestring in the local scope of the function is_string() which, in Python 2, causes a failure with UnboundLocalError. In order to mitigate this issue the local variable name has been changed to string_types. Fixes #708 [1] https://docs.python.org/2/reference/executionmodel.html#builtins-and-restricted-execution [2] https://docs.python.org/2/library/functions.html#basestring Signed-off-by: Radostin Stoyanov --- lib/py/images/pb2dict.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/py/images/pb2dict.py b/lib/py/images/pb2dict.py index 4e2c171d5..af14db24d 100644 --- a/lib/py/images/pb2dict.py +++ b/lib/py/images/pb2dict.py @@ -217,9 +217,11 @@ def get_bytes_dec(field): def is_string(value): # Python 3 compatibility - if not hasattr(__builtins__, "basestring"): - basestring = (str, bytes) - return isinstance(value, basestring) + if "basestring" in __builtins__: + string_types = basestring + else: + string_types = (str, bytes) + return isinstance(value, string_types) def _pb2dict_cast(field, value, pretty = False, is_hex = False): if not is_hex: From c7a49921dd58fa852c79adff91529507737c0b06 Mon Sep 17 00:00:00 2001 From: Radostin Stoyanov Date: Wed, 29 May 2019 11:58:10 +0100 Subject: [PATCH 151/249] pb2dict: Resolve Python 2/3 compatibility issues In Python 3, bytes has only a decode() method, and string has only an encode() method. [1] The modules quopri and base64 from the Python Standard Library perform quoted-printable transport encoding and decoding with both Python 2 [2] and Python 3 [3]. [1] https://docs.python.org/3/howto/pyporting.html#text-versus-binary-data [2] https://docs.python.org/2/library/quopri.html [3] https://docs.python.org/3/library/quopri.html Signed-off-by: Radostin Stoyanov --- lib/py/images/pb2dict.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/lib/py/images/pb2dict.py b/lib/py/images/pb2dict.py index af14db24d..c4ce736e8 100644 --- a/lib/py/images/pb2dict.py +++ b/lib/py/images/pb2dict.py @@ -5,6 +5,12 @@ import socket import collections import os +import base64 +import quopri + +if "encodebytes" not in dir(base64): + base64.encodebytes = base64.encodestring + base64.decodebytes = base64.decodestring # pb2dict and dict2pb are methods to convert pb to/from dict. # Inspired by: @@ -189,14 +195,14 @@ def encode_dev(field, value): return dev[0] << kern_minorbits | dev[1] def encode_base64(value): - return value.encode('base64') + return base64.encodebytes(value) def decode_base64(value): - return value.decode('base64') + return base64.decodebytes(value) def encode_unix(value): - return value.encode('quopri') + return quopri.encodestring(value) def decode_unix(value): - return value.decode('quopri') + return quopri.decodestring(value) encode = { 'unix_name': encode_unix } decode = { 'unix_name': decode_unix } From 7627c14ad4b2bae3d69fa5ad07c2599dc89bcdbf Mon Sep 17 00:00:00 2001 From: Dmitry Safonov Date: Wed, 29 May 2019 17:15:04 +0100 Subject: [PATCH 152/249] cr-check: Add check for mremap() of special mappings During restore any VMA that's a subject to ASLR should be moved at the same address as was on a checkpoint. Previously, ports to non-x86 architectures had problems with VDSO mremap(). On those platforms kernel needs "landing" for return to userspace in some cases. Usually, vdso provides this landing and finishes restoring of registers. That's `int80_landing_pad` on ia32. On arm64/arm32 it's sigtrap for SA_RESTORER - to proceed after signal processing. That's why kernel needs to track the position of landing. On modern kernels for platform we support it's already done - however, for older kernels some patches needs to be backported for C/R. Provide the checks for mremap() of special VMAs: that CRIU has suitable kernel to work on and if we'll have some new platforms - that kernel tracks the position of landing. Signed-off-by: Dmitry Safonov --- criu/cr-check.c | 174 +++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 173 insertions(+), 1 deletion(-) diff --git a/criu/cr-check.c b/criu/cr-check.c index e24668305..75a665cfb 100644 --- a/criu/cr-check.c +++ b/criu/cr-check.c @@ -582,7 +582,7 @@ static pid_t fork_and_ptrace_attach(int (*child_setup)(void)) return pid; } -static int check_ptrace_peeksiginfo() +static int check_ptrace_peeksiginfo(void) { struct ptrace_peeksiginfo_args arg; siginfo_t siginfo; @@ -611,6 +611,177 @@ static int check_ptrace_peeksiginfo() return ret; } +struct special_mapping { + const char *name; + void *addr; + size_t size; +}; + +static int parse_special_maps(struct special_mapping *vmas, size_t nr) +{ + FILE *maps; + char buf[256]; + int ret = 0; + + maps = fopen_proc(PROC_SELF, "maps"); + if (!maps) + return -1; + + while (fgets(buf, sizeof(buf), maps)) { + unsigned long start, end; + int r, tail; + size_t i; + + r = sscanf(buf, "%lx-%lx %*s %*s %*s %*s %n\n", + &start, &end, &tail); + if (r != 2) { + fclose(maps); + pr_err("Bad maps format %d.%d (%s)\n", r, tail, buf + tail); + return -1; + } + + for (i = 0; i < nr; i++) { + if (strcmp(buf + tail, vmas[i].name) != 0) + continue; + if (vmas[i].addr != MAP_FAILED) { + pr_err("Special mapping meet twice: %s\n", vmas[i].name); + ret = -1; + goto out; + } + vmas[i].addr = (void *)start; + vmas[i].size = end - start; + } + } + +out: + fclose(maps); + return ret; +} + +static void dummy_sighandler(int sig) +{ +} + +/* + * The idea of test is checking if the kernel correctly tracks positions + * of special_mappings: vdso/vvar/sigpage/... + * Per-architecture commits added handling for mremap() somewhere between + * v4.8...v4.14. If the kernel doesn't have one of those patches, + * a process will crash after receiving a signal (we use SIGUSR1 for + * the test here). That's because after processing a signal the kernel + * needs a "landing" to return to userspace, which is based on vdso/sigpage. + * If the kernel doesn't track the position of mapping - we land in the void. + * And we definitely mremap() support by the fact that those special_mappings + * are subjects for ASLR. (See #288 as a reference) + */ +static void check_special_mapping_mremap_child(struct special_mapping *vmas, + size_t nr) +{ + size_t i, parking_size = 0; + void *parking_lot; + pid_t self = getpid(); + + for (i = 0; i < nr; i++) { + if (vmas[i].addr != MAP_FAILED) + parking_size += vmas[i].size; + } + + if (signal(SIGUSR1, dummy_sighandler) == SIG_ERR) { + pr_perror("signal() failed"); + exit(1); + } + + parking_lot = mmap(NULL, parking_size, PROT_NONE, + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + if (parking_lot == MAP_FAILED) { + pr_perror("mmap(%zu) failed", parking_size); + exit(1); + } + + for (i = 0; i < nr; i++) { + unsigned long ret; + + if (vmas[i].addr == MAP_FAILED) + continue; + + ret = syscall(__NR_mremap, (unsigned long)vmas[i].addr, + vmas[i].size, vmas[i].size, + MREMAP_FIXED | MREMAP_MAYMOVE, + (unsigned long)parking_lot); + if (ret != (unsigned long)parking_lot) + syscall(__NR_exit, 1); + parking_lot += vmas[i].size; + } + + syscall(__NR_kill, self, SIGUSR1); + syscall(__NR_exit, 0); +} + +static int check_special_mapping_mremap(void) +{ + struct special_mapping special_vmas[] = { + { + .name = "[vvar]\n", + .addr = MAP_FAILED, + }, + { + .name = "[vdso]\n", + .addr = MAP_FAILED, + }, + { + .name = "[sigpage]\n", + .addr = MAP_FAILED, + }, + /* XXX: { .name = "[uprobes]\n" }, */ + /* + * Not subjects for ASLR, skipping: + * { .name = "[vectors]\n", }, + * { .name = "[vsyscall]\n" }, + */ + }; + size_t vmas_nr = ARRAY_SIZE(special_vmas); + pid_t child; + int stat; + + if (parse_special_maps(special_vmas, vmas_nr)) + return -1; + + child = fork(); + if (child < 0) { + pr_perror("%s(): failed to fork()", __func__); + return -1; + } + + if (child == 0) + check_special_mapping_mremap_child(special_vmas, vmas_nr); + + if (waitpid(child, &stat, 0) != child) { + if (errno == ECHILD) { + pr_err("BUG: Someone waited for the child already\n"); + return -1; + } + /* Probably, we're interrupted with a signal - cleanup */ + pr_err("Failed to wait for a child %d\n", errno); + kill(child, SIGKILL); + return -1; + } + + if (WIFSIGNALED(stat)) { + pr_err("Child killed by signal %d\n", WTERMSIG(stat)); + pr_err("Your kernel probably lacks the support for mremapping special mappings\n"); + return -1; + } else if (WIFEXITED(stat)) { + if (WEXITSTATUS(stat) == 0) + return 0; + pr_err("Child exited with %d\n", WEXITSTATUS(stat)); + return -1; + } + + pr_err("BUG: waitpid() returned stat=%d\n", stat); + /* We're not killing the child here - it's predestined to die anyway. */ + return -1; +} + static int check_ptrace_suspend_seccomp(void) { pid_t pid; @@ -1172,6 +1343,7 @@ int cr_check(void) CHECK_CAT1(check_ipc()); CHECK_CAT1(check_sigqueuinfo()); CHECK_CAT1(check_ptrace_peeksiginfo()); + CHECK_CAT1(check_special_mapping_mremap()); /* * Category 2 - required for specific cases. From debd2308a6d05ef859f77c6d34a454c6ef8806a7 Mon Sep 17 00:00:00 2001 From: Dmitry Safonov Date: Wed, 29 May 2019 17:15:05 +0100 Subject: [PATCH 153/249] vdso/arm: Add vdso symbols from kernel Signed-off-by: Dmitry Safonov --- criu/arch/arm/include/asm/vdso.h | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 criu/arch/arm/include/asm/vdso.h diff --git a/criu/arch/arm/include/asm/vdso.h b/criu/arch/arm/include/asm/vdso.h new file mode 100644 index 000000000..cf9d500be --- /dev/null +++ b/criu/arch/arm/include/asm/vdso.h @@ -0,0 +1,17 @@ +#ifndef __CR_ASM_VDSO_H__ +#define __CR_ASM_VDSO_H__ + +#include "asm/int.h" +#include "asm-generic/vdso.h" + +/* This definition is used in pie/util-vdso.c to initialize the vdso symbol + * name string table 'vdso_symbols' + * + * Poke from kernel file arch/arm/vdso/vdso.lds.S + */ +#define VDSO_SYMBOL_MAX 2 +#define ARCH_VDSO_SYMBOLS \ + "__vdso_clock_gettime", \ + "__vdso_gettimeofday" + +#endif /* __CR_ASM_VDSO_H__ */ From 60298f899c1616dcb300c96339d05d6a9449e864 Mon Sep 17 00:00:00 2001 From: Dmitry Safonov Date: Wed, 29 May 2019 17:15:06 +0100 Subject: [PATCH 154/249] parasite-vdso: Add ugly casts for arm32 criu/pie/parasite-vdso.c: In function 'remap_rt_vdso': criu/pie/parasite-vdso.c:144:17: error: cast to pointer from integer of different size [-Werror=int-to-pointer-cast] if (sys_munmap((void *)vma_vdso->start, vma_entry_len(vma_vdso))) { ^ criu/pie/parasite-vdso.c:154:17: error: cast to pointer from integer of different size [-Werror=int-to-pointer-cast] if (sys_munmap((void *)vma_vvar->start, vma_entry_len(vma_vvar))) { ^ cc1: all warnings being treated as errors Signed-off-by: Dmitry Safonov --- criu/pie/parasite-vdso.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/criu/pie/parasite-vdso.c b/criu/pie/parasite-vdso.c index dc73fb53e..90e20f767 100644 --- a/criu/pie/parasite-vdso.c +++ b/criu/pie/parasite-vdso.c @@ -127,11 +127,17 @@ static int remap_rt_vdso(VmaEntry *vma_vdso, VmaEntry *vma_vvar, { unsigned long rt_vvar_addr = vdso_rt_parked_at; unsigned long rt_vdso_addr = vdso_rt_parked_at; + void *remap_addr; int ret; pr_info("Runtime vdso/vvar matches dumpee, remap inplace\n"); - if (sys_munmap((void *)vma_vdso->start, vma_entry_len(vma_vdso))) { + /* + * Ugly casts for 32bit platforms, which don't like uint64_t + * cast to (void *) + */ + remap_addr = (void *)(uintptr_t)vma_vdso->start; + if (sys_munmap(remap_addr, vma_entry_len(vma_vdso))) { pr_err("Failed to unmap dumpee vdso\n"); return -1; } @@ -141,7 +147,8 @@ static int remap_rt_vdso(VmaEntry *vma_vdso, VmaEntry *vma_vvar, vma_vdso->start, sym_rt->vdso_size); } - if (sys_munmap((void *)vma_vvar->start, vma_entry_len(vma_vvar))) { + remap_addr = (void *)(uintptr_t)vma_vvar->start; + if (sys_munmap(remap_addr, vma_entry_len(vma_vvar))) { pr_err("Failed to unmap dumpee vvar\n"); return -1; } From 77e54cc8280db8ccbb1e59107d4e0fcf6b7e97c4 Mon Sep 17 00:00:00 2001 From: Dmitry Safonov Date: Wed, 29 May 2019 17:15:07 +0100 Subject: [PATCH 155/249] vdso/arm32: Add vdso trampoline support Signed-off-by: Dmitry Safonov --- criu/arch/arm/vdso-pie.c | 58 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 criu/arch/arm/vdso-pie.c diff --git a/criu/arch/arm/vdso-pie.c b/criu/arch/arm/vdso-pie.c new file mode 100644 index 000000000..0ec8bd9a8 --- /dev/null +++ b/criu/arch/arm/vdso-pie.c @@ -0,0 +1,58 @@ +#include + +#include "asm/types.h" + +#include +#include +#include "parasite-vdso.h" +#include "log.h" +#include "common/bug.h" + +#ifdef LOG_PREFIX +# undef LOG_PREFIX +#endif +#define LOG_PREFIX "vdso: " + +static void insert_trampoline(uintptr_t from, uintptr_t to) +{ + struct { + uint32_t ldr_pc; + uint32_t imm32; + uint32_t guards; + } __packed jmp = { + .ldr_pc = 0xe51ff004, /* ldr pc, [pc, #-4] */ + .imm32 = to, + .guards = 0xe1200070, /* bkpt 0x0000 */ + }; + void *iflush_start = (void *)from; + void *iflush_end = iflush_start + sizeof(jmp); + + memcpy((void *)from, &jmp, sizeof(jmp)); + + __builtin___clear_cache(iflush_start, iflush_end); +} + +int vdso_redirect_calls(unsigned long base_to, unsigned long base_from, + struct vdso_symtable *sto, struct vdso_symtable *sfrom, + bool compat_vdso) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(sto->symbols); i++) { + uintptr_t from, to; + + if (vdso_symbol_empty(&sfrom->symbols[i])) + continue; + + pr_debug("jmp: %lx/%lx -> %lx/%lx (index %d)\n", + base_from, sfrom->symbols[i].offset, + base_to, sto->symbols[i].offset, i); + + from = base_from + sfrom->symbols[i].offset; + to = base_to + sto->symbols[i].offset; + + insert_trampoline(from, to); + } + + return 0; +} From ff9dd16d095fe26ef630b0227ef37c818f95daeb Mon Sep 17 00:00:00 2001 From: Dmitry Safonov <0x7f454c46@gmail.com> Date: Wed, 29 May 2019 17:15:08 +0100 Subject: [PATCH 156/249] arm: Build {pie-, }util-vdso with CONFIG_VDSO_32 Signed-off-by: Dmitry Safonov <0x7f454c46@gmail.com> --- Makefile | 2 +- criu/Makefile.crtools | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index cee8a42c9..475d4abaf 100644 --- a/Makefile +++ b/Makefile @@ -35,7 +35,7 @@ endif # Architecture specific options. ifeq ($(ARCH),arm) ARMV := $(shell echo $(UNAME-M) | sed -nr 's/armv([[:digit:]]).*/\1/p; t; i7') - DEFINES := -DCONFIG_ARMV$(ARMV) + DEFINES := -DCONFIG_ARMV$(ARMV) -DCONFIG_VDSO_32 ifeq ($(ARMV),6) USERCFLAGS += -march=armv6 diff --git a/criu/Makefile.crtools b/criu/Makefile.crtools index d24bb174c..23dd6d664 100644 --- a/criu/Makefile.crtools +++ b/criu/Makefile.crtools @@ -90,7 +90,7 @@ obj-y += servicefd.o ifeq ($(VDSO),y) obj-y += pie-util-vdso.o obj-y += vdso.o -obj-y += pie-util-vdso-elf32.o +obj-$(CONFIG_COMPAT) += pie-util-vdso-elf32.o CFLAGS_pie-util-vdso-elf32.o += -DCONFIG_VDSO_32 obj-$(CONFIG_COMPAT) += vdso-compat.o CFLAGS_REMOVE_vdso-compat.o += $(CFLAGS-ASAN) $(CFLAGS-GCOV) From 7d90ec22bf05318cb9a186352d41240daa8ce737 Mon Sep 17 00:00:00 2001 From: Dmitry Safonov <0x7f454c46@gmail.com> Date: Wed, 29 May 2019 17:15:09 +0100 Subject: [PATCH 157/249] arm: Provide aeabi helpers in ARM format We're building PIEs in arm format rather than in thumb. Copy helpers from libgcc, provide a proper define and link them into blobs. Also substitute tabs by spaces, how it should have been in pie/Makefile - tabs are for recipes. Fixes: LINK criu/pie/parasite.built-in.o criu/pie/pie.lib.a(util-vdso.o): In function `elf_hash': /criu/criu/pie/util-vdso.c:61: undefined reference to `__aeabi_uidivmod' /criu/scripts/nmk/scripts/build.mk:209: recipe for target 'criu/pie/parasite.built-in.o' failed Signed-off-by: Dmitry Safonov <0x7f454c46@gmail.com> --- criu/arch/arm/aeabi-helpers.S | 96 +++++++++++++ criu/arch/arm/uidiv.S | 186 -------------------------- criu/pie/Makefile | 4 +- criu/pie/Makefile.library | 4 + include/common/arch/arm/asm/linkage.h | 4 + 5 files changed, 106 insertions(+), 188 deletions(-) create mode 100644 criu/arch/arm/aeabi-helpers.S delete mode 100644 criu/arch/arm/uidiv.S diff --git a/criu/arch/arm/aeabi-helpers.S b/criu/arch/arm/aeabi-helpers.S new file mode 100644 index 000000000..ea8561d48 --- /dev/null +++ b/criu/arch/arm/aeabi-helpers.S @@ -0,0 +1,96 @@ +/* + * Code borrowed from gcc, arm/lib1funcs.S + * and adapted to CRIU macros. + */ + +#if defined(__thumb__) +/* + * We don't support compiling PIEs in Thumb mode, + * see top Makefile for details (ARM CFLAGS_PIE section). +*/ +#error Unsupported Thumb mode +#endif + +#include "common/asm/linkage.h" + +#define RET bx lr +#define RETc(x) bx##x lr +#define LSYM(x) .x + +.macro do_it cond, suffix="" +.endm + +.macro ARM_DIV2_ORDER divisor, order + clz \order, \divisor + rsb \order, \order, #31 +.endm + +.macro ARM_DIV_BODY dividend, divisor, result, curbit + clz \curbit, \dividend + clz \result, \divisor + sub \curbit, \result, \curbit + rsbs \curbit, \curbit, #31 + addne \curbit, \curbit, \curbit, lsl #1 + mov \result, #0 + addne pc, pc, \curbit, lsl #2 + nop + .set shift, 32 + .rept 32 + .set shift, shift - 1 + cmp \dividend, \divisor, lsl #shift + adc \result, \result, \result + subcs \dividend, \dividend, \divisor, lsl #shift + .endr +.endm + +/* + * XXX: as an optimization add udiv instruction based version. + * It's possible to check if CPU supports the instruction by + * reading Instruction Set Attribute Register (ID_ISAR0) + * and checking fields "Divide_instrs". + */ +ENTRY(__aeabi_uidiv) + /* Note: if called via udivsi3_skip_div0_test, this will unnecessarily + check for division-by-zero a second time. */ +LSYM(udivsi3_skip_div0_test): + subs r2, r1, #1 + do_it eq + RETc(eq) + bcc LSYM(Ldiv0) + cmp r0, r1 + bls 11f + tst r1, r2 + beq 12f + + ARM_DIV_BODY r0, r1, r2, r3 + + mov r0, r2 + RET + +11: do_it eq, e + moveq r0, #1 + movne r0, #0 + RET + +12: ARM_DIV2_ORDER r1, r2 + + mov r0, r0, lsr r2 + RET + +LSYM(Ldiv0): + .byte 0xf0, 0x01, 0xf0, 0xe7 @ the instruction UDF #32 generates the signal SIGTRAP in Linux + +END(__aeabi_uidiv) +ALIAS(__udivsi3, __aeabi_uidiv) + +ENTRY(__aeabi_uidivmod) + cmp r1, #0 + beq LSYM(Ldiv0) + stmfd sp!, { r0, r1, lr } + bl LSYM(udivsi3_skip_div0_test) + ldmfd sp!, { r1, r2, lr } + mul r3, r2, r0 + sub r1, r1, r3 + RET +END(__aeabi_uidivmod) +ALIAS(__umodsi3, __aeabi_uidiv) diff --git a/criu/arch/arm/uidiv.S b/criu/arch/arm/uidiv.S deleted file mode 100644 index e77f6100c..000000000 --- a/criu/arch/arm/uidiv.S +++ /dev/null @@ -1,186 +0,0 @@ -.globl __aeabi_uidiv - -work .req r4 @ XXXX is this safe ? -dividend .req r0 -divisor .req r1 -overdone .req r2 -result .req r2 -curbit .req r3 - -#define LSYM(x) x - -.macro THUMB_DIV_MOD_BODY modulo - @ Load the constant 0x10000000 into our work register. - mov work, #1 - lsl work, #28 -LSYM(Loop1): - @ Unless the divisor is very big, shift it up in multiples of - @ four bits, since this is the amount of unwinding in the main - @ division loop. Continue shifting until the divisor is - @ larger than the dividend. - cmp divisor, work - bhs LSYM(Lbignum) - cmp divisor, dividend - bhs LSYM(Lbignum) - lsl divisor, #4 - lsl curbit, #4 - b LSYM(Loop1) -LSYM(Lbignum): - @ Set work to 0x80000000 - lsl work, #3 -LSYM(Loop2): - @ For very big divisors, we must shift it a bit at a time, or - @ we will be in danger of overflowing. - cmp divisor, work - bhs LSYM(Loop3) - cmp divisor, dividend - bhs LSYM(Loop3) - lsl divisor, #1 - lsl curbit, #1 - b LSYM(Loop2) -LSYM(Loop3): - @ Test for possible subtractions ... - .if \modulo - @ ... On the final pass, this may subtract too much from the dividend, - @ so keep track of which subtractions are done, we can fix them up - @ afterwards. - mov overdone, #0 - cmp dividend, divisor - blo LSYM(Lover1) - sub dividend, dividend, divisor -LSYM(Lover1): - lsr work, divisor, #1 - cmp dividend, work - blo LSYM(Lover2) - sub dividend, dividend, work - mov ip, curbit - mov work, #1 - ror curbit, work - orr overdone, curbit - mov curbit, ip -LSYM(Lover2): - lsr work, divisor, #2 - cmp dividend, work - blo LSYM(Lover3) - sub dividend, dividend, work - mov ip, curbit - mov work, #2 - ror curbit, work - orr overdone, curbit - mov curbit, ip -LSYM(Lover3): - lsr work, divisor, #3 - cmp dividend, work - blo LSYM(Lover4) - sub dividend, dividend, work - mov ip, curbit - mov work, #3 - ror curbit, work - orr overdone, curbit - mov curbit, ip -LSYM(Lover4): - mov ip, curbit - .else - @ ... and note which bits are done in the result. On the final pass, - @ this may subtract too much from the dividend, but the result will be ok, - @ since the "bit" will have been shifted out at the bottom. - cmp dividend, divisor - blo LSYM(Lover1) - sub dividend, dividend, divisor - orr result, result, curbit -LSYM(Lover1): - lsr work, divisor, #1 - cmp dividend, work - blo LSYM(Lover2) - sub dividend, dividend, work - lsr work, curbit, #1 - orr result, work -LSYM(Lover2): - lsr work, divisor, #2 - cmp dividend, work - blo LSYM(Lover3) - sub dividend, dividend, work - lsr work, curbit, #2 - orr result, work -LSYM(Lover3): - lsr work, divisor, #3 - cmp dividend, work - blo LSYM(Lover4) - sub dividend, dividend, work - lsr work, curbit, #3 - orr result, work -LSYM(Lover4): - .endif - - cmp dividend, #0 @ Early termination? - beq LSYM(Lover5) - lsr curbit, #4 @ No, any more bits to do? - beq LSYM(Lover5) - lsr divisor, #4 - b LSYM(Loop3) -LSYM(Lover5): - .if \modulo - @ Any subtractions that we should not have done will be recorded in - @ the top three bits of "overdone". Exactly which were not needed - @ are governed by the position of the bit, stored in ip. - mov work, #0xe - lsl work, #28 - and overdone, work - beq LSYM(Lgot_result) - - @ If we terminated early, because dividend became zero, then the - @ bit in ip will not be in the bottom nibble, and we should not - @ perform the additions below. We must test for this though - @ (rather relying upon the TSTs to prevent the additions) since - @ the bit in ip could be in the top two bits which might then match - @ with one of the smaller RORs. - mov curbit, ip - mov work, #0x7 - tst curbit, work - beq LSYM(Lgot_result) - - mov curbit, ip - mov work, #3 - ror curbit, work - tst overdone, curbit - beq LSYM(Lover6) - lsr work, divisor, #3 - add dividend, work -LSYM(Lover6): - mov curbit, ip - mov work, #2 - ror curbit, work - tst overdone, curbit - beq LSYM(Lover7) - lsr work, divisor, #2 - add dividend, work -LSYM(Lover7): - mov curbit, ip - mov work, #1 - ror curbit, work - tst overdone, curbit - beq LSYM(Lgot_result) - lsr work, divisor, #1 - add dividend, work - .endif -LSYM(Lgot_result): -.endm - - - .thumb - .text - -__aeabi_uidiv: - mov curbit, #1 - mov result, #0 - - push { work } - cmp dividend, divisor - blo LSYM(Lgot_result) - - THUMB_DIV_MOD_BODY 0 - - mov r0, result - pop { work } - - bx lr diff --git a/criu/pie/Makefile b/criu/pie/Makefile index bdff44816..ade186346 100644 --- a/criu/pie/Makefile +++ b/criu/pie/Makefile @@ -10,8 +10,8 @@ ccflags-y += -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=0 ccflags-y += -Wp,-U_FORTIFY_SOURCE -Wp,-D_FORTIFY_SOURCE=0 ifneq ($(filter-out clean mrproper,$(MAKECMDGOALS)),) - LDFLAGS += $(shell $(COMPEL_BIN) ldflags) - compel_plugins := $(shell $(COMPEL_BIN) plugins) + LDFLAGS += $(shell $(COMPEL_BIN) ldflags) + compel_plugins := $(shell $(COMPEL_BIN) plugins) endif LDS := compel/arch/$(SRCARCH)/scripts/compel-pack.lds.S diff --git a/criu/pie/Makefile.library b/criu/pie/Makefile.library index 0a33a8861..2d2d1faf1 100644 --- a/criu/pie/Makefile.library +++ b/criu/pie/Makefile.library @@ -23,5 +23,9 @@ ifeq ($(SRCARCH),x86) CFLAGS_util-vdso-elf32.o += -DCONFIG_VDSO_32 endif +ifeq ($(SRCARCH),arm) + lib-y += ./$(ARCH_DIR)/aeabi-helpers.o +endif + CFLAGS := $(filter-out -pg $(CFLAGS-GCOV) $(CFLAGS-ASAN),$(CFLAGS)) ccflags-y += $(CFLAGS_PIE) diff --git a/include/common/arch/arm/asm/linkage.h b/include/common/arch/arm/asm/linkage.h index 738064233..a93898be5 100644 --- a/include/common/arch/arm/asm/linkage.h +++ b/include/common/arch/arm/asm/linkage.h @@ -19,6 +19,10 @@ #define END(sym) \ .size sym, . - sym +#define ALIAS(sym_new, sym_old) \ + .globl sym_new; \ + .set sym_new, sym_old + #endif /* __ASSEMBLY__ */ #endif /* __CR_LINKAGE_H__ */ From 7e4055e1059bc64d3fce346da2e8bbcc6e40912f Mon Sep 17 00:00:00 2001 From: Dmitry Safonov Date: Wed, 29 May 2019 17:15:10 +0100 Subject: [PATCH 158/249] arm/pie: Provide __clear_cache() After patching code - we need to flush CPU cache, it's done with __builtin___clear_cache(). As we don't link to libgcc, provide a helper that wraps ARM-specific syscall. Fixes: LINK criu/pie/restorer.built-in.o ld: ./criu/arch/arm/vdso-pie.o: in function `insert_trampoline': /root/criu/criu/arch/arm/vdso-pie.c:32: undefined reference to `__clear_cache' Signed-off-by: Dmitry Safonov --- compel/arch/arm/plugins/std/syscalls/syscall.def | 1 + criu/arch/arm/pie-cacheflush.c | 7 +++++++ criu/pie/Makefile.library | 1 + 3 files changed, 9 insertions(+) create mode 100644 criu/arch/arm/pie-cacheflush.c diff --git a/compel/arch/arm/plugins/std/syscalls/syscall.def b/compel/arch/arm/plugins/std/syscalls/syscall.def index bcd61d4a1..653a7539b 100644 --- a/compel/arch/arm/plugins/std/syscalls/syscall.def +++ b/compel/arch/arm/plugins/std/syscalls/syscall.def @@ -110,3 +110,4 @@ gettimeofday 169 78 (struct timeval *tv, struct timezone *tz) preadv_raw 69 361 (int fd, struct iovec *iov, unsigned long nr, unsigned long pos_l, unsigned long pos_h) userfaultfd 282 388 (int flags) fallocate 47 352 (int fd, int mode, loff_t offset, loff_t len) +cacheflush ! 983042 (void *start, void *end, int flags) diff --git a/criu/arch/arm/pie-cacheflush.c b/criu/arch/arm/pie-cacheflush.c new file mode 100644 index 000000000..e6fd71f1e --- /dev/null +++ b/criu/arch/arm/pie-cacheflush.c @@ -0,0 +1,7 @@ +#include + +/* That's __builtin___clear_cache() to flush CPU cache */ +void __clear_cache(void *start, void *end) +{ + sys_cacheflush(start, end, 0); +} diff --git a/criu/pie/Makefile.library b/criu/pie/Makefile.library index 2d2d1faf1..b1ac600c6 100644 --- a/criu/pie/Makefile.library +++ b/criu/pie/Makefile.library @@ -25,6 +25,7 @@ endif ifeq ($(SRCARCH),arm) lib-y += ./$(ARCH_DIR)/aeabi-helpers.o + lib-y += ./$(ARCH_DIR)/pie-cacheflush.o endif CFLAGS := $(filter-out -pg $(CFLAGS-GCOV) $(CFLAGS-ASAN),$(CFLAGS)) From 009f15441ad3fb0bf02f83da1ea20ea38196e101 Mon Sep 17 00:00:00 2001 From: Dmitry Safonov Date: Wed, 29 May 2019 17:15:11 +0100 Subject: [PATCH 159/249] rt-vdso: Be verbose if !blobs_matches() (00.251007) pie: 4: vdso: Runtime vdso mismatches dumpee, generate proxy And I want to know why :) Signed-off-by: Dmitry Safonov --- criu/pie/parasite-vdso.c | 29 +++++++++++++++++++++++++---- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/criu/pie/parasite-vdso.c b/criu/pie/parasite-vdso.c index 90e20f767..00bc2bffa 100644 --- a/criu/pie/parasite-vdso.c +++ b/criu/pie/parasite-vdso.c @@ -95,23 +95,44 @@ static int __vdso_fill_symtable(uintptr_t mem, size_t size, static bool blobs_matches(VmaEntry *vdso_img, VmaEntry *vvar_img, struct vdso_symtable *sym_img, struct vdso_symtable *sym_rt) { + unsigned long vdso_size = vma_entry_len(vdso_img); + unsigned long rt_vdso_size = sym_rt->vdso_size; size_t i; - if (vma_entry_len(vdso_img) != sym_rt->vdso_size) + if (vdso_size != rt_vdso_size) { + pr_info("size differs: %lx != %lx (rt)\n", + vdso_size, rt_vdso_size); return false; + } for (i = 0; i < ARRAY_SIZE(sym_img->symbols); i++) { - if (sym_img->symbols[i].offset != sym_rt->symbols[i].offset) + unsigned long sym_offset = sym_img->symbols[i].offset; + unsigned long rt_sym_offset = sym_rt->symbols[i].offset; + char *sym_name = sym_img->symbols[i].name; + + if (sym_offset != rt_sym_offset) { + pr_info("[%zu]`%s` offset differs: %lx != %lx (rt)\n", + i, sym_name, sym_offset, rt_sym_offset); return false; + } } if (vvar_img && sym_rt->vvar_size != VVAR_BAD_SIZE) { bool vdso_firstly = (vvar_img->start > vdso_img->start); + unsigned long vvar_size = vma_entry_len(vvar_img); + unsigned long rt_vvar_size = sym_rt->vvar_size; - if (sym_rt->vvar_size != vma_entry_len(vvar_img)) + if (vvar_size != rt_vvar_size) { + pr_info("vvar size differs: %lx != %lx (rt)\n", + vdso_size, rt_vdso_size); return false; + } - return (vdso_firstly == sym_rt->vdso_before_vvar); + if (vdso_firstly != sym_rt->vdso_before_vvar) { + pr_info("[%s] pair has different order\n", + vdso_firstly ? "vdso/vvar" : "vvar/vdso"); + return false; + } } return true; From c02c907bb0b78e8ae5d8d48271c95e4dd4ff28c1 Mon Sep 17 00:00:00 2001 From: Dmitry Safonov Date: Wed, 29 May 2019 17:15:12 +0100 Subject: [PATCH 160/249] x86/vdso: Don't insert trampolines in vsyscall The patch "util-vdso: Check chain for STN_UNDEF" fixed an issue about not discovering present symbols on vdso. While it's a good and a proper fix, as the result __kernel_vsyscall started being patched. Which in result broke zdtm trampoline test on ia32. So, let's omit patching vsyscall while #512 issue is not fixed. We might actually refrain patching it for long time as it doesn't access vvar, so there is little sense in doing patching. Signed-off-by: Dmitry Safonov --- criu/arch/x86/include/asm/vdso.h | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/criu/arch/x86/include/asm/vdso.h b/criu/arch/x86/include/asm/vdso.h index ae893b8d7..046db2336 100644 --- a/criu/arch/x86/include/asm/vdso.h +++ b/criu/arch/x86/include/asm/vdso.h @@ -12,17 +12,38 @@ * This is a minimal amount of symbols * we should support at the moment. */ -#define VDSO_SYMBOL_MAX 7 +#define VDSO_SYMBOL_MAX 6 +/* + * XXX: we don't patch __kernel_vsyscall as it's too small: + * + * byte *before* *after* + * 0x0 push %ecx mov $[rt-vdso],%eax + * 0x1 push %edx ^ + * 0x2 push %ebp ^ + * 0x3 mov %esp,%ebp ^ + * 0x5 sysenter jmp *%eax + * 0x7 int $0x80 int3 + * 0x9 pop %ebp int3 + * 0xa pop %edx int3 + * 0xb pop %ecx pop %ecx + * 0xc ret ret + * + * As restarting a syscall is quite likely after restore, + * the patched version quitly crashes. + * vsyscall will be patched again when addressing: + * https://github.com/checkpoint-restore/criu/issues/512 + */ #define ARCH_VDSO_SYMBOLS \ "__vdso_clock_gettime", \ "__vdso_getcpu", \ "__vdso_gettimeofday", \ "__vdso_time", \ - "__kernel_vsyscall", \ "__kernel_sigreturn", \ "__kernel_rt_sigreturn" +/* "__kernel_vsyscall", */ + #ifndef ARCH_MAP_VDSO_32 # define ARCH_MAP_VDSO_32 0x2002 #endif From 9d8a63e8e9453765110396a10a0c22fefee19ef9 Mon Sep 17 00:00:00 2001 From: Dmitry Safonov Date: Wed, 29 May 2019 17:15:13 +0100 Subject: [PATCH 161/249] util-vdso: Check chain for STN_UNDEF Rather than chain[chain] != STN_UNDEF. Seems like, on !ARM32 vdso there are more symbols and less chance to hit this "feature". Fixes parsing of __vdso_clock_gettime symbol on v5.1 arm kernel. Signed-off-by: Dmitry Safonov --- criu/pie/util-vdso.c | 2 +- test/zdtm/static/vdso01.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/criu/pie/util-vdso.c b/criu/pie/util-vdso.c index 6213df9a4..104da0633 100644 --- a/criu/pie/util-vdso.c +++ b/criu/pie/util-vdso.c @@ -242,7 +242,7 @@ static void parse_elf_symbols(uintptr_t mem, size_t size, Phdr_t *load, const char * symbol = vdso_symbols[i]; k = elf_hash((const unsigned char *)symbol); - for (j = bucket[k % nbucket]; j < nchain && chain[j] != STN_UNDEF; j = chain[j]) { + for (j = bucket[k % nbucket]; j < nchain && j != STN_UNDEF; j = chain[j]) { addr = mem + dyn_symtab->d_un.d_ptr - load->p_vaddr; Sym_t *sym; char *name; diff --git a/test/zdtm/static/vdso01.c b/test/zdtm/static/vdso01.c index be733663c..269688c5e 100644 --- a/test/zdtm/static/vdso01.c +++ b/test/zdtm/static/vdso01.c @@ -246,7 +246,7 @@ static int vdso_fill_symtable(char *mem, size_t size, struct vdso_symtable *t) for (i = 0; i < ARRAY_SIZE(vdso_symbols); i++) { k = elf_hash((const unsigned char *)vdso_symbols[i]); - for (j = bucket[k % nbucket]; j < nchain && chain[j] != STN_UNDEF; j = chain[j]) { + for (j = bucket[k % nbucket]; j < nchain && j != STN_UNDEF; j = chain[j]) { Sym_t *sym = (void *)&mem[dyn_symtab->d_un.d_ptr - load->p_vaddr]; char *name; From 916ac8e6d447cb6f216748e7f92d21a30c853dbb Mon Sep 17 00:00:00 2001 From: Dmitry Safonov Date: Wed, 29 May 2019 17:15:14 +0100 Subject: [PATCH 162/249] pie/build: Add CFLAGS_PIE to CFLAGS There is a little difference between ccflags-y and CFLAGS, except the local/global visibility: nmk adds $(CFLAGS) to nmk-asflags and assembles using them, but without ccflags-y. The other possible way would be adding asflags-y with CFLAGS_PIE, but I'm not convinced - let's update CFLAGS for the time being. Signed-off-by: Dmitry Safonov --- criu/pie/Makefile | 2 +- criu/pie/Makefile.library | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/criu/pie/Makefile b/criu/pie/Makefile index ade186346..47443c26b 100644 --- a/criu/pie/Makefile +++ b/criu/pie/Makefile @@ -5,7 +5,7 @@ target := parasite restorer CFLAGS := $(filter-out -pg $(CFLAGS-GCOV) $(CFLAGS-ASAN),$(CFLAGS)) -ccflags-y += $(CFLAGS_PIE) +CFLAGS += $(CFLAGS_PIE) ccflags-y += -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=0 ccflags-y += -Wp,-U_FORTIFY_SOURCE -Wp,-D_FORTIFY_SOURCE=0 diff --git a/criu/pie/Makefile.library b/criu/pie/Makefile.library index b1ac600c6..d802417de 100644 --- a/criu/pie/Makefile.library +++ b/criu/pie/Makefile.library @@ -29,4 +29,4 @@ ifeq ($(SRCARCH),arm) endif CFLAGS := $(filter-out -pg $(CFLAGS-GCOV) $(CFLAGS-ASAN),$(CFLAGS)) -ccflags-y += $(CFLAGS_PIE) +CFLAGS += $(CFLAGS_PIE) From f237bc59936e4db60a23de4c3f41f4234d068a5b Mon Sep 17 00:00:00 2001 From: Dmitry Safonov Date: Wed, 29 May 2019 17:15:15 +0100 Subject: [PATCH 163/249] criu/vdso: Purge CONFIG_VDSO Vigorously remove the config ifdef. The config option *never* had any excuse to exist: - for x86 we were grand - for ppc64/arm64 patches to support mremap() on vdso were long ago accepted, but regardless - it's not possible to disable CONFIG_VDSO for those platforms in kernel - for s390 - patches were mainstreamed not that long ago, but it's not possible to disable the kernel config - for arm32 it's possible to disable the kernel config, but kernel returns to userspace historically through sigpage, not vdso. That's the only platform that criu disallows to have CONFIG_VDSO=y in kernel, but that's just meaningles. A kernel patch for sigpage mremap() has gone into v4.13: commit 280e87e98c09 ("ARM: 8683/1: ARM32: Support mremap() for sigpage/vDSO"). So, removing the config was long-lived item on my TODO list that bligted arm32 users and made changes to vdso more complex by all "needed" iffdeferry. Get rid of it with fire. Fixes: #446 Signed-off-by: Dmitry Safonov --- Makefile | 6 +----- Makefile.config | 4 ---- criu/Makefile.crtools | 3 --- criu/cr-restore.c | 6 ------ criu/include/kerndat.h | 4 ---- criu/include/parasite-vdso.h | 9 --------- criu/include/restorer.h | 2 -- criu/include/vdso.h | 13 ------------- criu/mem.c | 3 +-- criu/pie/Makefile | 15 ++++++--------- criu/pie/Makefile.library | 9 +-------- criu/pie/parasite.c | 8 -------- criu/pie/restorer.c | 8 -------- criu/proc_parse.c | 14 -------------- 14 files changed, 9 insertions(+), 95 deletions(-) diff --git a/Makefile b/Makefile index 475d4abaf..09cf2406a 100644 --- a/Makefile +++ b/Makefile @@ -56,19 +56,16 @@ ifeq ($(ARCH),arm) endif ifeq ($(ARCH),aarch64) - VDSO := y DEFINES := -DCONFIG_AARCH64 endif ifeq ($(ARCH),ppc64) LDARCH := powerpc:common64 - VDSO := y DEFINES := -DCONFIG_PPC64 -D__SANE_USERSPACE_TYPES__ endif ifeq ($(ARCH),x86) LDARCH := i386:x86-64 - VDSO := y DEFINES := -DCONFIG_X86_64 endif @@ -81,7 +78,6 @@ endif ifeq ($(ARCH),s390) ARCH := s390 SRCARCH := s390 - VDSO := y DEFINES := -DCONFIG_S390 CFLAGS_PIE := -fno-optimize-sibling-calls endif @@ -90,7 +86,7 @@ CFLAGS_PIE += -DCR_NOGLIBC export CFLAGS_PIE LDARCH ?= $(SRCARCH) -export LDARCH VDSO +export LDARCH export PROTOUFIX DEFINES # diff --git a/Makefile.config b/Makefile.config index a853705b3..008a82289 100644 --- a/Makefile.config +++ b/Makefile.config @@ -57,10 +57,6 @@ $(CONFIG_HEADER): scripts/feature-tests.mak $(CONFIG_FILE) $(Q) echo '' >> $$@ $(call map,gen-feature-test,$(FEATURES_LIST)) $(Q) cat $(CONFIG_FILE) | sed -n -e '/^[^#]/s/^/#define CONFIG_/p' >> $$@ -ifeq ($$(VDSO),y) - $(Q) echo '#define CONFIG_VDSO' >> $$@ - $(Q) echo '' >> $$@ -endif $(Q) echo '#endif /* __CR_CONFIG_H__ */' >> $$@ endef diff --git a/criu/Makefile.crtools b/criu/Makefile.crtools index 23dd6d664..6591621ed 100644 --- a/criu/Makefile.crtools +++ b/criu/Makefile.crtools @@ -86,15 +86,12 @@ obj-y += fdstore.o obj-y += uffd.o obj-y += config.o obj-y += servicefd.o - -ifeq ($(VDSO),y) obj-y += pie-util-vdso.o obj-y += vdso.o obj-$(CONFIG_COMPAT) += pie-util-vdso-elf32.o CFLAGS_pie-util-vdso-elf32.o += -DCONFIG_VDSO_32 obj-$(CONFIG_COMPAT) += vdso-compat.o CFLAGS_REMOVE_vdso-compat.o += $(CFLAGS-ASAN) $(CFLAGS-GCOV) -endif PROTOBUF_GEN := scripts/protobuf-gen.sh diff --git a/criu/cr-restore.c b/criu/cr-restore.c index b6274affb..ecfee1296 100644 --- a/criu/cr-restore.c +++ b/criu/cr-restore.c @@ -3231,10 +3231,8 @@ static int sigreturn_restore(pid_t pid, struct task_restore_args *task_args, uns struct thread_restore_args *thread_args; struct restore_mem_zone *mz; -#ifdef CONFIG_VDSO struct vdso_maps vdso_maps_rt; unsigned long vdso_rt_size = 0; -#endif struct vm_area_list self_vmas; struct vm_area_list *vmas = &rsti(current)->vmas; @@ -3285,7 +3283,6 @@ static int sigreturn_restore(pid_t pid, struct task_restore_args *task_args, uns pr_info("%d threads require %ldK of memory\n", current->nr_threads, KBYTES(task_args->bootstrap_len)); -#ifdef CONFIG_VDSO if (core_is_compat(core)) vdso_maps_rt = vdso_maps_compat; else @@ -3297,7 +3294,6 @@ static int sigreturn_restore(pid_t pid, struct task_restore_args *task_args, uns if (vdso_rt_size && vdso_maps_rt.sym.vvar_size) vdso_rt_size += ALIGN(vdso_maps_rt.sym.vvar_size, PAGE_SIZE); task_args->bootstrap_len += vdso_rt_size; -#endif /* * Restorer is a blob (code + args) that will get mapped in some @@ -3512,7 +3508,6 @@ static int sigreturn_restore(pid_t pid, struct task_restore_args *task_args, uns } -#ifdef CONFIG_VDSO /* * Restorer needs own copy of vdso parameters. Runtime * vdso must be kept non intersecting with anything else, @@ -3524,7 +3519,6 @@ static int sigreturn_restore(pid_t pid, struct task_restore_args *task_args, uns task_args->vdso_maps_rt = vdso_maps_rt; task_args->vdso_rt_size = vdso_rt_size; task_args->can_map_vdso = kdat.can_map_vdso; -#endif new_sp = restorer_stack(task_args->t->mz); diff --git a/criu/include/kerndat.h b/criu/include/kerndat.h index 2740dd3b1..75e2130b2 100644 --- a/criu/include/kerndat.h +++ b/criu/include/kerndat.h @@ -5,9 +5,7 @@ #include "int.h" #include "common/config.h" #include "asm/kerndat.h" -#ifdef CONFIG_VDSO #include "util-vdso.h" -#endif struct stat; @@ -61,11 +59,9 @@ struct kerndat_s { bool has_thp_disable; bool can_map_vdso; bool vdso_hint_reliable; -#ifdef CONFIG_VDSO struct vdso_symtable vdso_sym; #ifdef CONFIG_COMPAT struct vdso_symtable vdso_sym_compat; -#endif #endif bool has_nsid; bool has_link_nsid; diff --git a/criu/include/parasite-vdso.h b/criu/include/parasite-vdso.h index 6667fe5c4..3cf67bbb3 100644 --- a/criu/include/parasite-vdso.h +++ b/criu/include/parasite-vdso.h @@ -2,9 +2,6 @@ #define __CR_PARASITE_VDSO_H__ #include "common/config.h" - -#ifdef CONFIG_VDSO - #include "util-vdso.h" #include "images/vma.pb-c.h" @@ -95,10 +92,4 @@ extern int vdso_redirect_calls(unsigned long base_to, unsigned long base_from, struct vdso_symtable *to, struct vdso_symtable *from, bool compat_vdso); -#else /* CONFIG_VDSO */ -#define vdso_do_park(sym_rt, park_at, park_size) (0) -#define vdso_map_compat(map_at) (0) - -#endif /* CONFIG_VDSO */ - #endif /* __CR_PARASITE_VDSO_H__ */ diff --git a/criu/include/restorer.h b/criu/include/restorer.h index b83e9130c..effbc3655 100644 --- a/criu/include/restorer.h +++ b/criu/include/restorer.h @@ -207,11 +207,9 @@ struct task_restore_args { bool can_map_vdso; bool auto_dedup; -#ifdef CONFIG_VDSO unsigned long vdso_rt_size; struct vdso_maps vdso_maps_rt; /* runtime vdso symbols */ unsigned long vdso_rt_parked_at; /* safe place to keep vdso */ -#endif void **breakpoint; enum faults fault_strategy; diff --git a/criu/include/vdso.h b/criu/include/vdso.h index 1719f3fb7..fd30772b4 100644 --- a/criu/include/vdso.h +++ b/criu/include/vdso.h @@ -5,9 +5,6 @@ #include #include "common/config.h" - -#ifdef CONFIG_VDSO - #include "util-vdso.h" extern struct vdso_maps vdso_maps; @@ -26,14 +23,4 @@ extern void compat_vdso_helper(struct vdso_maps *native, int pipe_fd, int err_fd, void *vdso_buf, size_t buf_size); #endif -#else /* CONFIG_VDSO */ - -#define vdso_init_dump() (0) -#define vdso_init_restore() (0) -#define kerndat_vdso_fill_symtable() (0) -#define kerndat_vdso_preserves_hint() (0) -#define parasite_fixup_vdso(ctl, pid, vma_area_list) (0) - -#endif /* CONFIG_VDSO */ - #endif /* __CR_VDSO_H__ */ diff --git a/criu/mem.c b/criu/mem.c index b1d13188b..6a1a87a1e 100644 --- a/criu/mem.c +++ b/criu/mem.c @@ -101,7 +101,6 @@ static inline bool __page_in_parent(bool dirty) bool should_dump_page(VmaEntry *vmae, u64 pme) { -#ifdef CONFIG_VDSO /* * vDSO area must be always dumped because on restore * we might need to generate a proxy. @@ -117,7 +116,7 @@ bool should_dump_page(VmaEntry *vmae, u64 pme) */ if (vma_entry_is(vmae, VMA_AREA_VVAR)) return false; -#endif + /* * Optimisation for private mapping pages, that haven't * yet being COW-ed diff --git a/criu/pie/Makefile b/criu/pie/Makefile index 47443c26b..1ad456f43 100644 --- a/criu/pie/Makefile +++ b/criu/pie/Makefile @@ -16,6 +16,7 @@ endif LDS := compel/arch/$(SRCARCH)/scripts/compel-pack.lds.S +restorer-obj-y += parasite-vdso.o ./$(ARCH_DIR)/vdso-pie.o restorer-obj-y += ./$(ARCH_DIR)/restorer.o ifeq ($(ARCH),x86) @@ -25,16 +26,12 @@ ifeq ($(ARCH),x86) endif endif -ifeq ($(VDSO),y) - restorer-obj-y += parasite-vdso.o ./$(ARCH_DIR)/vdso-pie.o - - ifeq ($(SRCARCH),aarch64) - restorer-obj-y += ./$(ARCH_DIR)/intraprocedure.o - endif +ifeq ($(SRCARCH),aarch64) + restorer-obj-y += ./$(ARCH_DIR)/intraprocedure.o +endif - ifeq ($(SRCARCH),ppc64) - restorer-obj-y += ./$(ARCH_DIR)/vdso-trampoline.o - endif +ifeq ($(SRCARCH),ppc64) + restorer-obj-y += ./$(ARCH_DIR)/vdso-trampoline.o endif define gen-pie-rules diff --git a/criu/pie/Makefile.library b/criu/pie/Makefile.library index d802417de..658c8a4eb 100644 --- a/criu/pie/Makefile.library +++ b/criu/pie/Makefile.library @@ -7,14 +7,7 @@ lib-name := pie.lib.a lib-y += util.o - -ifeq ($(VDSO),y) - lib-y += util-vdso.o -endif - -ifeq ($(SRCARCH),ppc64) - lib-y += ./$(ARCH_DIR)/misc.o -endif +lib-y += util-vdso.o ifeq ($(SRCARCH),x86) ifeq ($(CONFIG_COMPAT),y) diff --git a/criu/pie/parasite.c b/criu/pie/parasite.c index c32e31384..01bacd311 100644 --- a/criu/pie/parasite.c +++ b/criu/pie/parasite.c @@ -573,7 +573,6 @@ static int parasite_dump_tty(struct parasite_tty_args *args) #undef __tty_ioctl } -#ifdef CONFIG_VDSO static int parasite_check_vdso_mark(struct parasite_vdso_vma_entry *args) { struct vdso_mark *m = (void *)args->start; @@ -609,13 +608,6 @@ static int parasite_check_vdso_mark(struct parasite_vdso_vma_entry *args) return 0; } -#else -static inline int parasite_check_vdso_mark(struct parasite_vdso_vma_entry *args) -{ - pr_err("Unexpected VDSO check command\n"); - return -1; -} -#endif static int parasite_dump_cgroup(struct parasite_dump_cgroup_args *args) { diff --git a/criu/pie/restorer.c b/criu/pie/restorer.c index f2db115ff..513be74e0 100644 --- a/criu/pie/restorer.c +++ b/criu/pie/restorer.c @@ -1081,11 +1081,7 @@ static void restore_posix_timers(struct task_restore_args *args) * sys_munmap must not return here. The control process must * trap us on the exit from sys_munmap. */ -#ifdef CONFIG_VDSO unsigned long vdso_rt_size = 0; -#else -#define vdso_rt_size (0) -#endif void *bootstrap_start = NULL; unsigned int bootstrap_len = 0; @@ -1259,9 +1255,7 @@ long __export_restore_task(struct task_restore_args *args) bootstrap_start = args->bootstrap_start; bootstrap_len = args->bootstrap_len; -#ifdef CONFIG_VDSO vdso_rt_size = args->vdso_rt_size; -#endif fi_strategy = args->fault_strategy; @@ -1446,7 +1440,6 @@ long __export_restore_task(struct task_restore_args *args) sys_close(args->vma_ios_fd); -#ifdef CONFIG_VDSO /* * Proxify vDSO. */ @@ -1454,7 +1447,6 @@ long __export_restore_task(struct task_restore_args *args) args->vmas, args->vmas_n, args->compatible_mode, fault_injected(FI_VDSO_TRAMPOLINES))) goto core_restore_end; -#endif /* * Walk though all VMAs again to drop PROT_WRITE diff --git a/criu/proc_parse.c b/criu/proc_parse.c index 3d852d755..f6ebb1fd6 100644 --- a/criu/proc_parse.c +++ b/criu/proc_parse.c @@ -502,7 +502,6 @@ int parse_self_maps_lite(struct vm_area_list *vms) return -1; } -#ifdef CONFIG_VDSO static inline int handle_vdso_vma(struct vma_area *vma) { vma->e->status |= VMA_AREA_REGULAR; @@ -518,19 +517,6 @@ static inline int handle_vvar_vma(struct vma_area *vma) vma->e->status |= VMA_AREA_VVAR; return 0; } -#else -static inline int handle_vdso_vma(struct vma_area *vma) -{ - pr_warn_once("Found vDSO area without support\n"); - return -1; -} - -static inline int handle_vvar_vma(struct vma_area *vma) -{ - pr_warn_once("Found VVAR area without support\n"); - return -1; -} -#endif static int handle_vma(pid_t pid, struct vma_area *vma_area, const char *file_path, DIR *map_files_dir, From 187096eaa6e8315026996de86967f281d2d6631f Mon Sep 17 00:00:00 2001 From: Radostin Stoyanov Date: Sat, 18 May 2019 09:28:36 +0100 Subject: [PATCH 164/249] zdtm: Fix memory and resource leaks These errors were found by Cppcheck 1.84 Signed-off-by: Radostin Stoyanov --- test/zdtm/static/autofs.c | 11 ++++++----- test/zdtm/static/caps00.c | 1 + test/zdtm/static/packet_sock_mmap.c | 1 + test/zdtm/static/s390x_runtime_instr.c | 2 ++ test/zdtm/static/sock_peercred.c | 18 +++++++++++------- test/zdtm/static/unlink_multiple_largefiles.c | 10 +++++++--- test/zdtm/static/vdso-proxy.c | 8 ++++---- test/zdtm/transition/lazy-thp.c | 2 ++ 8 files changed, 34 insertions(+), 19 deletions(-) diff --git a/test/zdtm/static/autofs.c b/test/zdtm/static/autofs.c index f74bc35ac..4360f90f0 100644 --- a/test/zdtm/static/autofs.c +++ b/test/zdtm/static/autofs.c @@ -312,7 +312,7 @@ static int autofs_open_mount(int devid, const char *mountpoint) { struct autofs_dev_ioctl *param; size_t size; - int fd; + int ret; size = sizeof(struct autofs_dev_ioctl) + strlen(mountpoint) + 1; param = malloc(size); @@ -325,13 +325,14 @@ static int autofs_open_mount(int devid, const char *mountpoint) if (ioctl(autofs_dev, AUTOFS_DEV_IOCTL_OPENMOUNT, param) < 0) { pr_perror("failed to open autofs mount %s", mountpoint); - return -errno; + ret = -errno; + goto out; } - fd = param->ioctlfd; + ret = param->ioctlfd; +out: free(param); - - return fd; + return ret; } static int autofs_report_result(int token, int devid, const char *mountpoint, diff --git a/test/zdtm/static/caps00.c b/test/zdtm/static/caps00.c index 62484c4f4..7a256c08a 100644 --- a/test/zdtm/static/caps00.c +++ b/test/zdtm/static/caps00.c @@ -47,6 +47,7 @@ int main(int argc, char **argv) if (f) { if (fscanf(f, "%d", &cap_last_cap) != 1) { pr_perror("Unable to read cal_last_cap"); + fclose(f); return 1; } fclose(f); diff --git a/test/zdtm/static/packet_sock_mmap.c b/test/zdtm/static/packet_sock_mmap.c index edf96c66e..2a82950bc 100644 --- a/test/zdtm/static/packet_sock_mmap.c +++ b/test/zdtm/static/packet_sock_mmap.c @@ -47,6 +47,7 @@ static void check_map_is_there(unsigned long addr, int sk) sscanf(line, "%lx-%*x %*s %*s %x:%x %d %*s", &start, &maj, &min, &ino); if ((start == addr) && ss.st_dev == makedev(maj, min) && ss.st_ino == ino) { pass(); + fclose(f); return; } } diff --git a/test/zdtm/static/s390x_runtime_instr.c b/test/zdtm/static/s390x_runtime_instr.c index 6be32c3c1..e0a5742d9 100644 --- a/test/zdtm/static/s390x_runtime_instr.c +++ b/test/zdtm/static/s390x_runtime_instr.c @@ -147,9 +147,11 @@ int main(int argc, char **argv) test_waitsig(); skip("RI not supported"); pass(); + free(buf); return 0; } fail("Fail with error %d", errno); + free(buf); return -1; } /* Set buffer for RI */ diff --git a/test/zdtm/static/sock_peercred.c b/test/zdtm/static/sock_peercred.c index e681ecec9..069cc52f7 100644 --- a/test/zdtm/static/sock_peercred.c +++ b/test/zdtm/static/sock_peercred.c @@ -67,6 +67,7 @@ int main(int argc, char **argv) socklen_t len; char *stack; pid_t pid; + int exit_code = 1; test_init(argc, argv); @@ -78,7 +79,7 @@ int main(int argc, char **argv) stack = malloc(2 * STACK_SIZE); if (!stack) { pr_err("malloc\n"); - return 1; + goto out; } /* Find unused fd */ @@ -89,18 +90,18 @@ int main(int argc, char **argv) if (fd == INT_MAX) { pr_err("INT_MAX happens...\n"); - return 1; + goto out; } pid = clone(child_func, stack + STACK_SIZE, CLONE_FILES|SIGCHLD, (void *)(unsigned long)fd); if (pid == -1) { pr_perror("clone"); - return 1; + goto out; } if (wait(&status) == -1 || status) { pr_perror("wait error: status=%d\n", status); - return 1; + goto out; } test_daemon(); @@ -109,15 +110,18 @@ int main(int argc, char **argv) len = sizeof(ucred); if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &ucred, &len) < 0) { fail("Can't getsockopt()"); - return 1; + goto out; } if (ucred.pid != pid || ucred.gid != getuid() + UID_INC || ucred.gid != getgid() + GID_INC) { fail("Wrong pid, uid or gid\n"); - return 1; + goto out; } pass(); - return 0; + exit_code = 0; + out: + free(stack); + return exit_code; } diff --git a/test/zdtm/static/unlink_multiple_largefiles.c b/test/zdtm/static/unlink_multiple_largefiles.c index a87093439..7cf628606 100644 --- a/test/zdtm/static/unlink_multiple_largefiles.c +++ b/test/zdtm/static/unlink_multiple_largefiles.c @@ -31,7 +31,7 @@ void create_check_pattern(char *buf, size_t count, unsigned char seed) struct fiemap *read_fiemap(int fd) { test_msg("Obtaining fiemap for fd %d\n", fd); - struct fiemap *fiemap; + struct fiemap *fiemap, *tmp; int extents_size; fiemap = malloc(sizeof(struct fiemap)); @@ -48,16 +48,19 @@ struct fiemap *read_fiemap(int fd) if (ioctl(fd, FS_IOC_FIEMAP, fiemap) < 0) { pr_perror("FIEMAP ioctl failed"); + free(fiemap); return NULL; } extents_size = sizeof(struct fiemap_extent) * fiemap->fm_mapped_extents; - fiemap = realloc(fiemap,sizeof(struct fiemap) + extents_size); - if (fiemap == NULL) { + tmp = realloc(fiemap, sizeof(struct fiemap) + extents_size); + if (tmp == NULL) { + free(fiemap); pr_perror("Cannot resize fiemap"); return NULL; } + fiemap = tmp; memset(fiemap->fm_extents, 0, extents_size); fiemap->fm_extent_count = fiemap->fm_mapped_extents; @@ -65,6 +68,7 @@ struct fiemap *read_fiemap(int fd) if (ioctl(fd, FS_IOC_FIEMAP, fiemap) < 0) { pr_perror("fiemap ioctl() failed"); + free(fiemap); return NULL; } test_msg("Debugkillme: %x\n", fiemap->fm_mapped_extents); diff --git a/test/zdtm/static/vdso-proxy.c b/test/zdtm/static/vdso-proxy.c index ecb71e892..2946eb790 100644 --- a/test/zdtm/static/vdso-proxy.c +++ b/test/zdtm/static/vdso-proxy.c @@ -73,13 +73,13 @@ static int parse_maps(struct vm_area *vmas) v->start, v->end); } - if (i == MAX_VMAS) { - pr_err("Number of VMAs is bigger than reserved array's size\n"); + if (fclose(maps)) { + pr_err("Failed to close maps file: %m\n"); return -1; } - if (fclose(maps)) { - pr_err("Failed to close maps file: %m\n"); + if (i == MAX_VMAS) { + pr_err("Number of VMAs is bigger than reserved array's size\n"); return -1; } diff --git a/test/zdtm/transition/lazy-thp.c b/test/zdtm/transition/lazy-thp.c index f7af41446..a0cf33041 100644 --- a/test/zdtm/transition/lazy-thp.c +++ b/test/zdtm/transition/lazy-thp.c @@ -57,5 +57,7 @@ int main(int argc, char ** argv) } pass(); + free(org); + free(mem); return 0; } From d5065a5b3390a368ac7f229bb4aff0adbe244a61 Mon Sep 17 00:00:00 2001 From: Uchio Kondo Date: Fri, 1 Mar 2019 18:48:06 +0900 Subject: [PATCH 165/249] c-lib: Support to build a static archive Signed-off-by: Uchio Kondo --- lib/Makefile | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/Makefile b/lib/Makefile index 94632848e..0c9841071 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -1,4 +1,5 @@ CRIU_SO := libcriu.so +CRIU_A := libcriu.a UAPI_HEADERS := lib/c/criu.h images/rpc.proto # @@ -19,8 +20,12 @@ ldflags-so += -lprotobuf-c lib/c/$(CRIU_SO): lib/c/built-in.o $(call msg-link, $@) $(Q) $(CC) -shared $(cflags-so) -o $@ $^ $(ldflags-so) $(LDFLAGS) +lib/c/$(CRIU_A): lib/c/built-in.o + $(call msg-link, $@) + $(Q) $(AR) rcs $@ $^ lib-c: lib/c/$(CRIU_SO) -.PHONY: lib-c +lib-a: lib/c/$(CRIU_A) +.PHONY: lib-c lib-a # # Python bindings. From fc3f90c0a2c3f0d2a0ee74378a9f45ae505f6638 Mon Sep 17 00:00:00 2001 From: Uchio Kondo Date: Fri, 8 Mar 2019 15:08:32 +0900 Subject: [PATCH 166/249] c-lib: Add lib-a into all-y targets Signed-off-by: Uchio Kondo --- lib/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Makefile b/lib/Makefile index 0c9841071..7b95956ab 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -6,7 +6,7 @@ UAPI_HEADERS := lib/c/criu.h images/rpc.proto # File to keep track of files installed by setup.py CRIT_SETUP_FILES := lib/.crit-setup.files -all-y += lib-c lib-py +all-y += lib-c lib-a lib-py # # C language bindings. From cd8ba0d474318836273121dd5fc67b96ed9ace32 Mon Sep 17 00:00:00 2001 From: Uchio Kondo Date: Wed, 29 May 2019 13:48:17 +0900 Subject: [PATCH 167/249] Add CRIU_A to cleanup target - This patch is from the comment by Radostin Stoyanov @rst0git Signed-off-by: Uchio Kondo --- lib/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Makefile b/lib/Makefile index 7b95956ab..4a131e88e 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -42,7 +42,7 @@ clean-lib: $(Q) $(MAKE) $(build)=lib/py clean .PHONY: clean-lib clean: clean-lib -cleanup-y += lib/c/$(CRIU_SO) lib/c/criu.pc +cleanup-y += lib/c/$(CRIU_SO) lib/c/$(CRIU_A) lib/c/criu.pc mrproper: clean install: lib-c lib-py crit/crit lib/c/criu.pc.in From d6ae83a05b55bf397f65b16adc856c05d7deffcd Mon Sep 17 00:00:00 2001 From: Uchio Kondo Date: Fri, 7 Jun 2019 17:11:02 +0900 Subject: [PATCH 168/249] c-lib: Install and uninstall libcriu.a Signed-off-by: Uchio Kondo --- lib/Makefile | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/Makefile b/lib/Makefile index 4a131e88e..67c50b95a 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -45,12 +45,13 @@ clean: clean-lib cleanup-y += lib/c/$(CRIU_SO) lib/c/$(CRIU_A) lib/c/criu.pc mrproper: clean -install: lib-c lib-py crit/crit lib/c/criu.pc.in +install: lib-c lib-a lib-py crit/crit lib/c/criu.pc.in $(E) " INSTALL " lib $(Q) mkdir -p $(DESTDIR)$(LIBDIR) $(Q) install -m 755 lib/c/$(CRIU_SO) $(DESTDIR)$(LIBDIR)/$(CRIU_SO).$(CRIU_SO_VERSION_MAJOR).$(CRIU_SO_VERSION_MINOR) $(Q) ln -fns $(CRIU_SO).$(CRIU_SO_VERSION_MAJOR).$(CRIU_SO_VERSION_MINOR) $(DESTDIR)$(LIBDIR)/$(CRIU_SO).$(CRIU_SO_VERSION_MAJOR) $(Q) ln -fns $(CRIU_SO).$(CRIU_SO_VERSION_MAJOR).$(CRIU_SO_VERSION_MINOR) $(DESTDIR)$(LIBDIR)/$(CRIU_SO) + $(Q) install -m 755 lib/c/$(CRIU_A) $(DESTDIR)$(LIBDIR)/$(CRIU_A) $(Q) mkdir -p $(DESTDIR)$(INCLUDEDIR)/criu/ $(Q) install -m 644 $(UAPI_HEADERS) $(DESTDIR)$(INCLUDEDIR)/criu/ $(E) " INSTALL " pkgconfig/criu.pc @@ -65,6 +66,7 @@ uninstall: $(E) " UNINSTALL" $(CRIU_SO) $(Q) $(RM) $(addprefix $(DESTDIR)$(LIBDIR)/,$(CRIU_SO).$(CRIU_SO_VERSION_MAJOR)) $(Q) $(RM) $(addprefix $(DESTDIR)$(LIBDIR)/,$(CRIU_SO)) + $(Q) $(RM) $(addprefix $(DESTDIR)$(LIBDIR)/,$(CRIU_A)) $(Q) $(RM) $(addprefix $(DESTDIR)$(LIBDIR)/,$(CRIU_SO).$(CRIU_SO_VERSION_MAJOR).$(CRIU_SO_VERSION_MINOR)) $(Q) $(RM) $(addprefix $(DESTDIR)$(INCLUDEDIR)/criu/,$(notdir $(UAPI_HEADERS))) $(E) " UNINSTALL" pkgconfig/criu.pc From 0f89561a0114784d30bdbdcc9c1447c7a3d58468 Mon Sep 17 00:00:00 2001 From: Cyrill Gorcunov Date: Tue, 4 Jun 2019 10:11:28 +0300 Subject: [PATCH 169/249] fsnotify: More precious error handling - make sure the alloc_openable is not failed with memory error, so that we should not lookup via irmap - irmap lookup should provide us a copy of the path instead of reference to irmap entry https://github.com/checkpoint-restore/criu/issues/698 Signed-off-by: Cyrill Gorcunov Reviewed-by: Dmitry Safonov <0x7f454c46@gmail.com> Signed-off-by: Andrei Vagin --- criu/fsnotify.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/criu/fsnotify.c b/criu/fsnotify.c index ed8a67a21..09093c0be 100644 --- a/criu/fsnotify.c +++ b/criu/fsnotify.c @@ -175,7 +175,7 @@ static char *alloc_openable(unsigned int s_dev, unsigned long i_ino, FhEntry *f_ if (st.st_ino == i_ino) { path = xstrdup(buf); if (path == NULL) - goto err; + return ERR_PTR(-ENOMEM); if (root_ns_mask & CLONE_NEWNS) { f_handle->has_mnt_id = true; f_handle->mnt_id = m->mnt_id; @@ -227,8 +227,8 @@ static int open_handle(unsigned int s_dev, unsigned long i_ino, int check_open_handle(unsigned int s_dev, unsigned long i_ino, FhEntry *f_handle) { + char *path, *irmap_path; int fd = -1; - char *path; if (fault_injected(FI_CHECK_OPEN_HANDLE)) { fd = -1; @@ -262,6 +262,8 @@ int check_open_handle(unsigned int s_dev, unsigned long i_ino, path = alloc_openable(s_dev, i_ino, f_handle); if (!IS_ERR_OR_NULL(path)) goto out; + else if (IS_ERR(path) && PTR_ERR(path) == -ENOMEM) + goto err; if ((mi->fstype->code == FSTYPE__TMPFS) || (mi->fstype->code == FSTYPE__DEVTMPFS)) { @@ -284,11 +286,14 @@ int check_open_handle(unsigned int s_dev, unsigned long i_ino, } pr_warn("\tHandle 0x%x:0x%lx cannot be opened\n", s_dev, i_ino); - path = irmap_lookup(s_dev, i_ino); - if (!path) { + irmap_path = irmap_lookup(s_dev, i_ino); + if (!irmap_path) { pr_err("\tCan't dump that handle\n"); return -1; } + path = xstrdup(irmap_path); + if (!path) + goto err; out: pr_debug("\tDumping %s as path for handle\n", path); f_handle->path = path; From 9e1922e3d5a4f102e1ee28d52e651177fe30fb5d Mon Sep 17 00:00:00 2001 From: Radostin Stoyanov Date: Sun, 31 Mar 2019 11:43:16 +0100 Subject: [PATCH 170/249] make: config -- Link with GnuTLS There are two notable open-source libraries that provide TLS implementation - OpenSSL and GnuTLS. The license of OpenSSL is incompatible with CRIU's license, and threfore GnuTLS is the recommended choice. GnuTLS offers an API to access secure communication protocols. These protocols provide privacy over insecure lines, and are designed to prevent eavesdropping, tampering or message forgery. Signed-off-by: Radostin Stoyanov --- Makefile.config | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Makefile.config b/Makefile.config index 008a82289..6f7324069 100644 --- a/Makefile.config +++ b/Makefile.config @@ -15,6 +15,14 @@ ifeq ($(call pkg-config-check,libselinux),y) FEATURE_DEFINES += -DCONFIG_HAS_SELINUX endif +ifeq ($(NO_GNUTLS)x$(call pkg-config-check,gnutls),xy) + LIBS_FEATURES += -lgnutls + export CONFIG_GNUTLS := y + FEATURE_DEFINES += -DCONFIG_GNUTLS +else + $(info Note: Building without GnuTLS support) +endif + export LIBS += $(LIBS_FEATURES) CONFIG_FILE = .config From 14673b2b19c0112d98ed6985e9090bd36f981f5b Mon Sep 17 00:00:00 2001 From: Radostin Stoyanov Date: Sun, 31 Mar 2019 12:05:22 +0100 Subject: [PATCH 171/249] page-xfer: Add TLS support with X509 certificates This commit adds Transport Layer Security (TLS) support for remote page-server connections. The following command-line options are introduced with this commit: --tls-cacert FILE Trust certificates signed only by this CA --tls-cacrl FILE CA certificate revocation list --tls-cert FILE TLS certificate --tls-key FILE TLS private key --tls Use TLS to secure remote connections The default PKI locations are: CA certificate /etc/pki/CA/cacert.pem CA revocation list /etc/pki/CA/cacrl.pem Client/server certificate /etc/pki/criu/cert.pem Client/server private key /etc/pki/criu/private/key.pem The files cacert.pem and cacrl.pem are optional. If they are not present, and not explicitly specified with a command-line option, CRIU will use only the system's trusted CAs to verify the remote peer's identity. This implies that if a CA certificate is specified using "--tls-cacert" only this CA will be used for verification. If CA certificate (cacert.pem) is not present, certificate revocation list (cacrl.pem) will be ignored. Both (client and server) sides require a private key and certificate. When the "--tls" option is specified, a TLS handshake (key exchange) will be performed immediately after the remote TCP connection has been accepted. X.509 certificates can be generated as follows: -------------------------%<------------------------- # Generate CA key and certificate echo -ne "ca\ncert_signing_key" > temp certtool --generate-privkey > cakey.pem certtool --generate-self-signed \ --template temp \ --load-privkey cakey.pem \ --outfile cacert.pem # Generate server key and certificate echo -ne "cn=$HOSTNAME\nencryption_key\nsigning_key" > temp certtool --generate-privkey > key.pem certtool --generate-certificate \ --template temp \ --load-privkey key.pem \ --load-ca-certificate cacert.pem \ --load-ca-privkey cakey.pem \ --outfile cert.pem rm temp mkdir -p /etc/pki/CA mkdir -p /etc/pki/criu/private mv cacert.pem /etc/pki/CA/ mv cert.pem /etc/pki/criu/ mv key.pem /etc/pki/criu/private -------------------------%<------------------------- Usage Example: Page-server: [src]# criu page-server -D --port --tls [dst]# criu dump --page-server --address --port \ -t -D --tls Lazy migration: [src]# criu dump --lazy-pages --port -t -D --tls [dst]# criu lazy-pages --page-server --address --port \ -D --tls [dst]# criu restore -D --lazy-pages Signed-off-by: Radostin Stoyanov --- Documentation/criu.txt | 27 +++ Makefile | 1 + criu/Makefile | 3 +- criu/Makefile.crtools | 1 + criu/config.c | 24 +++ criu/crtools.c | 5 + criu/include/cr_options.h | 5 + criu/include/tls.h | 26 +++ criu/page-xfer.c | 119 +++++++++---- criu/tls.c | 366 ++++++++++++++++++++++++++++++++++++++ criu/uffd.c | 3 + 11 files changed, 546 insertions(+), 34 deletions(-) create mode 100644 criu/include/tls.h create mode 100644 criu/tls.c diff --git a/Documentation/criu.txt b/Documentation/criu.txt index 6111c3baf..94fc5428a 100644 --- a/Documentation/criu.txt +++ b/Documentation/criu.txt @@ -594,6 +594,33 @@ Launches *criu* in page server mode. remote *lazy-pages* daemon to request memory pages in random order. +*--tls-cacert* 'file':: + Specifies the path to a trusted Certificate Authority (CA) certificate + file to be used for verification of a client or server certificate. + The 'file' must be in PEM format. When this option is used only the + specified CA is used for verification. Otherwise, the system's trusted CAs + and, if present, '/etc/pki/CA/cacert.pem' will be used. + +*--tls-cacrl* 'file':: + Specifies a path to a Certificate Revocation List (CRL) 'file' which + contains a list of revoked certificates that should no longer be trusted. + The 'file' must be in PEM format. When this option is not specified, the + file, if present, '/etc/pki/CA/cacrl.pem' will be used. + +*--tls-cert* 'file':: + Specifies a path to a file that contains a X.509 certificate to present + to the remote entity. The 'file' must be in PEM format. When this option + is not specified, the default location ('/etc/pki/criu/cert.pem') will be + used. + +*--tls-key* 'file':: + Specifies a path to a file that contains TLS private key. The 'file' must + be in PEM format. When this option is not the default location + ('/etc/pki/criu/private/key.pem') will be used. + +*--tls*:: + Use TLS to secure remote connections. + *lazy-pages* ~~~~~~~~~~~~ Launches *criu* in lazy-pages daemon mode. diff --git a/Makefile b/Makefile index 09cf2406a..9d83862d1 100644 --- a/Makefile +++ b/Makefile @@ -193,6 +193,7 @@ include Makefile.config else # To clean all files, enable make/build options here export CONFIG_COMPAT := y +export CONFIG_GNUTLS := y endif # diff --git a/criu/Makefile b/criu/Makefile index 3de6eb217..4134e5052 100644 --- a/criu/Makefile +++ b/criu/Makefile @@ -1,5 +1,5 @@ # here is a workaround for a bug in libnl-3: -# 6a8d90f5fec4 "attr: Allow attribute type 0" +# 6a8d90f5fec4 "attr: Allow attribute type 0" WRAPFLAGS += -Wl,--wrap=nla_parse,--wrap=nlmsg_parse ARCH_DIR := criu/arch/$(SRCARCH) @@ -14,6 +14,7 @@ endif # # Configuration file paths +CONFIG-DEFINES += -DSYSCONFDIR='"/etc"' CONFIG-DEFINES += -DGLOBAL_CONFIG_DIR='"/etc/criu/"' CONFIG-DEFINES += -DDEFAULT_CONFIG_FILENAME='"default.conf"' CONFIG-DEFINES += -DUSER_CONFIG_DIR='".criu/"' diff --git a/criu/Makefile.crtools b/criu/Makefile.crtools index 6591621ed..d19ff8123 100644 --- a/criu/Makefile.crtools +++ b/criu/Makefile.crtools @@ -76,6 +76,7 @@ obj-y += string.o obj-y += sysctl.o obj-y += sysfs_parse.o obj-y += timerfd.o +obj-$(CONFIG_GNUTLS) += tls.o obj-y += tty.o obj-y += tun.o obj-y += util.o diff --git a/criu/config.c b/criu/config.c index 24f0b3385..7903e44da 100644 --- a/criu/config.c +++ b/criu/config.c @@ -511,6 +511,11 @@ int parse_options(int argc, char **argv, bool *usage_error, BOOL_OPT("remote", &opts.remote), { "config", required_argument, 0, 1089}, { "no-default-config", no_argument, 0, 1090}, + { "tls-cacert", required_argument, 0, 1092}, + { "tls-cacrl", required_argument, 0, 1093}, + { "tls-cert", required_argument, 0, 1094}, + { "tls-key", required_argument, 0, 1095}, + BOOL_OPT("tls", &opts.tls), { }, }; @@ -797,6 +802,18 @@ int parse_options(int argc, char **argv, bool *usage_error, case 1091: opts.ps_socket = atoi(optarg); break; + case 1092: + SET_CHAR_OPTS(tls_cacert, optarg); + break; + case 1093: + SET_CHAR_OPTS(tls_cacrl, optarg); + break; + case 1094: + SET_CHAR_OPTS(tls_cert, optarg); + break; + case 1095: + SET_CHAR_OPTS(tls_key, optarg); + break; case 'V': pr_msg("Version: %s\n", CRIU_VERSION); if (strcmp(CRIU_GITID, "0")) @@ -858,6 +875,13 @@ int check_options() } } +#ifndef CONFIG_GNUTLS + if (opts.tls) { + pr_err("CRIU was built without TLS support\n"); + return 1; + } +#endif + if (check_namespace_opts()) { pr_err("Error: namespace flags conflict\n"); return 1; diff --git a/criu/crtools.c b/criu/crtools.c index aacdc9843..99ec14468 100644 --- a/criu/crtools.c +++ b/criu/crtools.c @@ -450,6 +450,11 @@ int main(int argc, char *argv[], char *envp[]) " -d|--daemon run in the background after creating socket\n" " --status-fd FD write \\0 to the FD and close it once process is ready\n" " to handle requests\n" +" --tls-cacert FILE trust certificates signed only by this CA\n" +" --tls-cacrl FILE path to CA certificate revocation list file\n" +" --tls-cert FILE path to TLS certificate file\n" +" --tls-key FILE path to TLS private key file\n" +" --tls use TLS to secure remote connection\n" "\n" "Configuration file options:\n" " --config FILEPATH pass a specific configuration file\n" diff --git a/criu/include/cr_options.h b/criu/include/cr_options.h index 8ddbf2341..18e43f043 100644 --- a/criu/include/cr_options.h +++ b/criu/include/cr_options.h @@ -139,6 +139,11 @@ struct cr_options { pid_t tree_id; int log_level; char *imgs_dir; + char *tls_cacert; + char *tls_cacrl; + char *tls_cert; + char *tls_key; + int tls; }; extern struct cr_options opts; diff --git a/criu/include/tls.h b/criu/include/tls.h new file mode 100644 index 000000000..aa2517887 --- /dev/null +++ b/criu/include/tls.h @@ -0,0 +1,26 @@ +#ifndef __CR_TLS_H__ +#define __CR_TLS_H__ + +# ifdef CONFIG_GNUTLS + +int tls_x509_init(int sockfd, bool is_server); +void tls_terminate_session(); + +ssize_t tls_send(const void *buf, size_t len, int flags); +ssize_t tls_recv(void *buf, size_t len, int flags); + +int tls_send_data_from_fd(int fd, unsigned long len); +int tls_recv_data_to_fd(int fd, unsigned long len); + +# else /* CONFIG_GNUTLS */ + +#define tls_x509_init(sockfd, is_server) (0) +#define tls_send(buf, len, flags) (-1) +#define tls_recv(buf, len, flags) (-1) +#define tls_send_data_from_fd(fd, len) (-1) +#define tls_recv_data_to_fd(fd, len) (-1) +#define tls_terminate_session() + +#endif /* CONFIG_HAS_GNUTLS */ + +#endif /* __CR_TLS_H__ */ diff --git a/criu/page-xfer.c b/criu/page-xfer.c index 1876c0ec6..9cdffd8d0 100644 --- a/criu/page-xfer.c +++ b/criu/page-xfer.c @@ -22,6 +22,7 @@ #include "rst_info.h" #include "stats.h" #include "img-remote.h" +#include "tls.h" static int page_server_sk = -1; @@ -129,13 +130,22 @@ static inline u32 decode_ps_flags(u32 cmd) return cmd >> PS_CMD_BITS; } +static inline int __send(int sk, const void *buf, size_t sz, int fl) +{ + return opts.tls ? tls_send(buf, sz, fl) : send(sk, buf, sz, fl); +} + +static inline int __recv(int sk, void *buf, size_t sz, int fl) +{ + return opts.tls ? tls_recv(buf, sz, fl) : recv(sk, buf, sz, fl); +} + static inline int send_psi_flags(int sk, struct page_server_iov *pi, int flags) { - if (send(sk, pi, sizeof(*pi), flags) != sizeof(*pi)) { + if (__send(sk, pi, sizeof(*pi), flags) != sizeof(*pi)) { pr_perror("Can't send PSI %d to server", pi->cmd); return -1; } - return 0; } @@ -150,17 +160,28 @@ static int write_pages_to_server(struct page_xfer *xfer, { ssize_t ret, left = len; - pr_debug("Splicing %lu bytes / %lu pages into socket\n", len, len / PAGE_SIZE); + if (opts.tls) { + pr_debug("Sending %lu bytes / %lu pages\n", + len, len / PAGE_SIZE); - while (left > 0) { - ret = splice(p, NULL, xfer->sk, NULL, left, SPLICE_F_MOVE); - if (ret < 0) { - pr_perror("Can't write pages to socket"); + if (tls_send_data_from_fd(p, len)) return -1; - } + } else { + pr_debug("Splicing %lu bytes / %lu pages into socket\n", + len, len / PAGE_SIZE); + + while (left > 0) { + ret = splice(p, NULL, xfer->sk, NULL, left, + SPLICE_F_MOVE); + if (ret < 0) { + pr_perror("Can't write pages to socket"); + return -1; + } - pr_debug("\tSpliced: %lu bytes sent\n", (unsigned long)ret); - left -= ret; + pr_debug("\tSpliced: %lu bytes sent\n", + (unsigned long)ret); + left -= ret; + } } return 0; @@ -206,7 +227,7 @@ static int open_page_server_xfer(struct page_xfer *xfer, int fd_type, unsigned l /* Push the command NOW */ tcp_nodelay(xfer->sk, true); - if (read(xfer->sk, &has_parent, 1) != 1) { + if (__recv(xfer->sk, &has_parent, 1, 0) != 1) { pr_perror("The page server doesn't answer"); return -1; } @@ -561,7 +582,7 @@ static int page_server_check_parent(int sk, struct page_server_iov *pi) if (ret < 0) return -1; - if (write(sk, &ret, sizeof(ret)) != sizeof(ret)) { + if (__send(sk, &ret, sizeof(ret), 0) != sizeof(ret)) { pr_perror("Unable to send response"); return -1; } @@ -582,7 +603,7 @@ static int check_parent_server_xfer(int fd_type, unsigned long img_id) tcp_nodelay(page_server_sk, true); - if (read(page_server_sk, &has_parent, sizeof(int)) != sizeof(int)) { + if (__recv(page_server_sk, &has_parent, sizeof(int), 0) != sizeof(int)) { pr_perror("The page server doesn't answer"); return -1; } @@ -646,8 +667,7 @@ static int page_server_open(int sk, struct page_server_iov *pi) if (sk >= 0) { char has_parent = !!cxfer.loc_xfer.parent; - - if (write(sk, &has_parent, 1) != 1) { + if (__send(sk, &has_parent, 1, 0) != 1) { pr_perror("Unable to send response"); close_page_xfer(&cxfer.loc_xfer); return -1; @@ -706,14 +726,23 @@ static int page_server_add(int sk, struct page_server_iov *pi, u32 flags) return -1; } - chunk = splice(sk, NULL, cxfer.p[1], NULL, chunk, SPLICE_F_MOVE | SPLICE_F_NONBLOCK); - if (chunk < 0) { - pr_perror("Can't read from socket"); - return -1; - } - if (chunk == 0) { - pr_err("A socket was closed unexpectedly\n"); - return -1; + if (opts.tls) { + if(tls_recv_data_to_fd(cxfer.p[1], chunk)) { + pr_err("Can't read from socket\n"); + return -1; + } + } else { + chunk = splice(sk, NULL, cxfer.p[1], NULL, chunk, + SPLICE_F_MOVE | SPLICE_F_NONBLOCK); + + if (chunk < 0) { + pr_perror("Can't read from socket"); + return -1; + } + if (chunk == 0) { + pr_err("A socket was closed unexpectedly\n"); + return -1; + } } if (lxfer->write_pages(lxfer, cxfer.p[0], chunk)) @@ -755,9 +784,16 @@ static int page_server_get_pages(int sk, struct page_server_iov *pi) return -1; len = pi->nr_pages * PAGE_SIZE; - ret = splice(pipe_read_dest.p[0], NULL, sk, NULL, len, SPLICE_F_MOVE); - if (ret != len) - return -1; + + if (opts.tls) { + if (tls_send_data_from_fd(pipe_read_dest.p[0], len)) + return -1; + } else { + ret = splice(pipe_read_dest.p[0], NULL, sk, NULL, len, + SPLICE_F_MOVE); + if (ret != len) + return -1; + } tcp_nodelay(sk, true); @@ -795,7 +831,7 @@ static int page_server_serve(int sk) struct page_server_iov pi; u32 cmd; - ret = recv(sk, &pi, sizeof(pi), MSG_WAITALL); + ret = __recv(sk, &pi, sizeof(pi), MSG_WAITALL); if (!ret) break; @@ -845,7 +881,7 @@ static int page_server_serve(int sk) * An answer must be sent back to inform another side, * that all data were received */ - if (write(sk, &status, sizeof(status)) != sizeof(status)) { + if (__send(sk, &status, sizeof(status), 0) != sizeof(status)) { pr_perror("Can't send the final package"); ret = -1; } @@ -878,14 +914,15 @@ static int page_server_serve(int sk) * Wait when a remote side closes the connection * to avoid TIME_WAIT bucket */ - if (read(sk, &c, sizeof(c)) != 0) { pr_perror("Unexpected data"); ret = -1; } } + tls_terminate_session(); page_server_close(); + pr_info("Session over\n"); close(sk); @@ -1033,6 +1070,11 @@ int cr_page_server(bool daemon_mode, bool lazy_dump, int cfd) if (ret != 0) return ret > 0 ? 0 : -1; + if (tls_x509_init(ask, true)) { + close(sk); + return -1; + } + if (ask >= 0) ret = page_server_serve(ask); @@ -1056,6 +1098,11 @@ static int connect_to_page_server(void) page_server_sk = setup_tcp_client(); if (page_server_sk == -1) return -1; + + if (tls_x509_init(page_server_sk, false)) { + close(page_server_sk); + return -1; + } out: /* * CORK the socket at the very beginning. As per ANK @@ -1098,14 +1145,16 @@ int disconnect_from_page_server(void) if (send_psi(page_server_sk, &pi)) goto out; - if (read(page_server_sk, &status, sizeof(status)) != sizeof(status)) { + if (__recv(page_server_sk, &status, sizeof(status), 0) != sizeof(status)) { pr_perror("The page server doesn't answer"); goto out; } ret = 0; out: + tls_terminate_session(); close_safe(&page_server_sk); + return ret ? : status; } @@ -1182,10 +1231,14 @@ static int page_server_read(struct ps_async_read *ar, int flags) need = ar->goal - ar->rb; } - ret = recv(page_server_sk, buf, need, flags); + ret = __recv(page_server_sk, buf, need, flags); if (ret < 0) { - pr_perror("Error reading async data from page server"); - return -1; + if (flags == MSG_DONTWAIT && (errno == EAGAIN || errno == EINTR)) { + ret = 0; + } else { + pr_perror("Error reading data from page server"); + return -1; + } } ar->rb += ret; diff --git a/criu/tls.c b/criu/tls.c new file mode 100644 index 000000000..a6bf25db4 --- /dev/null +++ b/criu/tls.c @@ -0,0 +1,366 @@ +#include +#include +#include +#include + +#include + +#include "cr_options.h" +#include "xmalloc.h" + +/* Compatability with GnuTLS verson <3.5 */ +#ifndef GNUTLS_E_CERTIFICATE_VERIFICATION_ERROR +# define GNUTLS_E_CERTIFICATE_VERIFICATION_ERROR GNUTLS_E_CERTIFICATE_ERROR +#endif + +#undef LOG_PREFIX +#define LOG_PREFIX "tls: " + +#define CRIU_PKI_DIR SYSCONFDIR "/pki" +#define CRIU_CACERT CRIU_PKI_DIR "/CA/cacert.pem" +#define CRIU_CACRL CRIU_PKI_DIR "/CA/cacrl.pem" +#define CRIU_CERT CRIU_PKI_DIR "/criu/cert.pem" +#define CRIU_KEY CRIU_PKI_DIR "/criu/private/key.pem" + +#define SPLICE_BUF_SZ_MAX (PIPE_BUF * 100) + +#define tls_perror(msg, ret) pr_err("%s: %s\n", msg, gnutls_strerror(ret)) + +static gnutls_session_t session; +static gnutls_certificate_credentials_t x509_cred; +static int tls_sk = -1; +static int tls_sk_flags = 0; + +void tls_terminate_session() +{ + int ret; + + if (!opts.tls) + return; + + if (session) { + do { + /* don't wait for peer to close connection */ + ret = gnutls_bye(session, GNUTLS_SHUT_WR); + } while(ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED); + gnutls_deinit(session); + } + + tls_sk = -1; + if (x509_cred) + gnutls_certificate_free_credentials(x509_cred); +} + +ssize_t tls_send(const void *buf, size_t len, int flags) +{ + int ret; + + tls_sk_flags = flags; + ret = gnutls_record_send(session, buf, len); + tls_sk_flags = 0; + + if (ret < 0) { + switch(ret) { + case GNUTLS_E_AGAIN: + errno = EAGAIN; + break; + case GNUTLS_E_INTERRUPTED: + errno = EINTR; + break; + case GNUTLS_E_UNEXPECTED_PACKET_LENGTH: + errno = ENOMSG; + break; + default: + tls_perror("Failed to send data", ret); + errno = EIO; + break; + } + } + + return ret; +} + +/* + * Read data from a file descriptor, then encrypt and send it with GnuTLS. + * This function is used for cases when we would otherwise use splice() + * to transfer data from PIPE to TCP socket. + */ +int tls_send_data_from_fd(int fd, unsigned long len) +{ + ssize_t copied; + unsigned long buf_size = min(len, (unsigned long)SPLICE_BUF_SZ_MAX); + void *buf = xmalloc(buf_size); + + if (!buf) + return -1; + + while (len > 0) { + int ret, sent; + + copied = read(fd, buf, min(len, buf_size)); + if (copied <= 0) { + pr_perror("Can't read from pipe"); + goto err; + } + + for(sent = 0; sent < copied; sent += ret) { + ret = tls_send((buf + sent), (copied - sent), 0); + if (ret < 0) { + tls_perror("Failed sending data", ret); + goto err; + } + } + len -= copied; + } +err: + xfree(buf); + return (len > 0); +} + +ssize_t tls_recv(void *buf, size_t len, int flags) +{ + int ret; + + tls_sk_flags = flags; + ret = gnutls_record_recv(session, buf, len); + tls_sk_flags = 0; + + /* Check if there are any data to receive in the gnutls buffers. */ + if (flags == MSG_DONTWAIT + && (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED)) { + size_t pending = gnutls_record_check_pending(session); + if (pending > 0) { + pr_debug("Receiving pending data (%zu bytes)\n", pending); + ret = gnutls_record_recv(session, buf, len); + } + } + + if (ret < 0) { + switch (ret) { + case GNUTLS_E_AGAIN: + errno = EAGAIN; + break; + case GNUTLS_E_INTERRUPTED: + errno = EINTR; + break; + default: + tls_perror("Failed receiving data", ret); + errno = EIO; + break; + } + ret = -1; + } + + return ret; +} + +/* + * Read and decrypt data with GnuTLS, then write it to a file descriptor. + * This function is used for cases when we would otherwise use splice() + * to transfer data from a TCP socket to a PIPE. + */ +int tls_recv_data_to_fd(int fd, unsigned long len) +{ + gnutls_packet_t packet; + + while (len > 0) { + int ret, w; + gnutls_datum_t pdata; + + ret = gnutls_record_recv_packet(session, &packet); + if (ret == 0) { + pr_info("Connection closed by peer\n"); + break; + } else if (ret < 0) { + tls_perror("Received corrupted data", ret); + break; + } + + gnutls_packet_get(packet, &pdata, NULL); + for(w = 0; w < pdata.size; w += ret) { + ret = write(fd, (pdata.data + w), (pdata.size - w)); + if (ret < 0) { + pr_perror("Failed writing to fd"); + goto err; + } + } + len -= pdata.size; + } +err: + gnutls_packet_deinit(packet); + return (len > 0); +} + +static inline void tls_handshake_verification_status_print(int ret, unsigned status) +{ + gnutls_datum_t out; + int type = gnutls_certificate_type_get(session); + + if (!gnutls_certificate_verification_status_print(status, type, &out, 0)) + pr_err("%s\n", out.data); + + gnutls_free(out.data); +} + +static int tls_x509_verify_peer_cert(void) +{ + int ret; + unsigned status; + + ret = gnutls_certificate_verify_peers3(session, opts.addr, &status); + if (ret != GNUTLS_E_SUCCESS) { + tls_perror("Unable to verify TLS peer", ret); + return -1; + } + + if (status != 0) { + pr_err("Invalid certificate\n"); + tls_handshake_verification_status_print( + GNUTLS_E_CERTIFICATE_VERIFICATION_ERROR, status); + return -1; + } + + return 0; +} + +static int tls_handshake() +{ + int ret = -1; + while (ret != GNUTLS_E_SUCCESS) { + ret = gnutls_handshake(session); + if (gnutls_error_is_fatal(ret)) { + tls_perror("TLS handshake failed", ret); + return -1; + } + } + pr_info("TLS handshake completed\n"); + return 0; +} + +static int tls_x509_setup_creds() +{ + int ret; + char *cacert = CRIU_CACERT; + char *cacrl = CRIU_CACRL; + char *cert = CRIU_CERT; + char *key = CRIU_KEY; + gnutls_x509_crt_fmt_t pem = GNUTLS_X509_FMT_PEM; + + if (opts.tls_cacert) + cacert = opts.tls_cacert; + if (opts.tls_cacrl) + cacrl = opts.tls_cacrl; + if (opts.tls_cert) + cert = opts.tls_cert; + if (opts.tls_key) + key = opts.tls_key; + + ret = gnutls_certificate_allocate_credentials(&x509_cred); + if (ret != GNUTLS_E_SUCCESS) { + tls_perror("Failed to allocate x509 credentials", ret); + return -1; + } + + if (!opts.tls_cacert) { + ret = gnutls_certificate_set_x509_system_trust(x509_cred); + if (ret < 0) { + tls_perror("Failed to load default trusted CAs", ret); + return -1; + } + } + + ret = gnutls_certificate_set_x509_trust_file(x509_cred, cacert, pem); + if (ret == 0) { + pr_info("No trusted CA certificates added (%s)\n", cacert); + if (opts.tls_cacert) + return -1; + } + + if (!access(cacrl, R_OK)) { + ret = gnutls_certificate_set_x509_crl_file(x509_cred, cacrl, pem); + if (ret < 0) { + tls_perror("Can't set certificate revocation list", ret); + return -1; + } + } else if (opts.tls_cacrl) { + pr_perror("Can't read certificate revocation list %s", cacrl); + return -1; + } + + ret = gnutls_certificate_set_x509_key_file(x509_cred, cert, key, pem); + if (ret != GNUTLS_E_SUCCESS) { + tls_perror("Failed to set certificate/private key pair", ret); + return -1; + } + + return 0; +} + +static ssize_t _tls_push_cb(void *p, const void* data, size_t sz) +{ + int fd = *(int *)(p); + return send(fd, data, sz, tls_sk_flags); +} + +static ssize_t _tls_pull_cb(void *p, void* data, size_t sz) +{ + int fd = *(int *)(p); + return recv(fd, data, sz, tls_sk_flags); +} + +static int tls_x509_setup_session(unsigned int flags) +{ + int ret; + + ret = gnutls_init(&session, flags); + if (ret != GNUTLS_E_SUCCESS) { + tls_perror("Failed to initialize session", ret); + return -1; + } + + ret = gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, x509_cred); + if (ret != GNUTLS_E_SUCCESS) { + tls_perror("Failed to set session credentials", ret); + return -1; + } + + ret = gnutls_set_default_priority(session); + if (ret != GNUTLS_E_SUCCESS) { + tls_perror("Failed to set priority", ret); + return -1; + } + + gnutls_transport_set_ptr(session, &tls_sk); + gnutls_transport_set_push_function(session, _tls_push_cb); + gnutls_transport_set_pull_function(session, _tls_pull_cb); + + if (flags == GNUTLS_SERVER) { + /* Require client certificate */ + gnutls_certificate_server_set_request(session, GNUTLS_CERT_REQUIRE); + /* Do not advertise trusted CAs to the client */ + gnutls_certificate_send_x509_rdn_sequence(session, 1); + } + + return 0; +} + +int tls_x509_init(int sockfd, bool is_server) +{ + if (!opts.tls) + return 0; + + tls_sk = sockfd; + if (tls_x509_setup_creds()) + goto err; + if (tls_x509_setup_session(is_server ? GNUTLS_SERVER : GNUTLS_CLIENT)) + goto err; + if (tls_handshake()) + goto err; + if (tls_x509_verify_peer_cert()) + goto err; + + return 0; +err: + tls_terminate_session(); + return -1; +} diff --git a/criu/uffd.c b/criu/uffd.c index 6699cb14a..5c1e32184 100644 --- a/criu/uffd.c +++ b/criu/uffd.c @@ -37,6 +37,7 @@ #include "page-xfer.h" #include "common/lock.h" #include "rst-malloc.h" +#include "tls.h" #include "fdstore.h" #include "util.h" @@ -1469,5 +1470,7 @@ int cr_lazy_pages(bool daemon) ret = handle_requests(epollfd, events, nr_fds); + tls_terminate_session(); + return ret; } From ff2cdf8f4ed6a5b96af60429bae096451686642e Mon Sep 17 00:00:00 2001 From: Radostin Stoyanov Date: Mon, 6 May 2019 14:12:36 +0100 Subject: [PATCH 172/249] tls: Add --tls-no-cn-verify option By default, CRIU will verify the certificate of a server (with gnutls_certificate_verify_peers3()) by providing the value specified with "--address" as a hostname. As part of the verification process, this value will be compared against the common name (CN) included in the TLS certificate of the server. If the CN doesn't match the TLS handshake will be terminated and CRIU will exit with an error. Although, this is an important feature that is used to mitigate MITM attacks, a user might need to disable such hostname verification for a particular use case or testing purposes. For instance, this option is needed when the common name included in the certificate corresponds to the server's domain name and an IP address is being used to establish connection. Signed-off-by: Radostin Stoyanov --- criu/config.c | 1 + criu/crtools.c | 1 + criu/include/cr_options.h | 1 + criu/tls.c | 6 +++++- 4 files changed, 8 insertions(+), 1 deletion(-) diff --git a/criu/config.c b/criu/config.c index 7903e44da..3a54afd4b 100644 --- a/criu/config.c +++ b/criu/config.c @@ -516,6 +516,7 @@ int parse_options(int argc, char **argv, bool *usage_error, { "tls-cert", required_argument, 0, 1094}, { "tls-key", required_argument, 0, 1095}, BOOL_OPT("tls", &opts.tls), + {"tls-no-cn-verify", no_argument, &opts.tls_no_cn_verify, true}, { }, }; diff --git a/criu/crtools.c b/criu/crtools.c index 99ec14468..97a6d6d6c 100644 --- a/criu/crtools.c +++ b/criu/crtools.c @@ -455,6 +455,7 @@ int main(int argc, char *argv[], char *envp[]) " --tls-cert FILE path to TLS certificate file\n" " --tls-key FILE path to TLS private key file\n" " --tls use TLS to secure remote connection\n" +" --tls-no-cn-verify do not verify common name in server certificate\n" "\n" "Configuration file options:\n" " --config FILEPATH pass a specific configuration file\n" diff --git a/criu/include/cr_options.h b/criu/include/cr_options.h index 18e43f043..c519c740d 100644 --- a/criu/include/cr_options.h +++ b/criu/include/cr_options.h @@ -144,6 +144,7 @@ struct cr_options { char *tls_cert; char *tls_key; int tls; + int tls_no_cn_verify; }; extern struct cr_options opts; diff --git a/criu/tls.c b/criu/tls.c index a6bf25db4..db9cc4f5a 100644 --- a/criu/tls.c +++ b/criu/tls.c @@ -206,8 +206,12 @@ static int tls_x509_verify_peer_cert(void) { int ret; unsigned status; + const char *hostname = NULL; - ret = gnutls_certificate_verify_peers3(session, opts.addr, &status); + if (!opts.tls_no_cn_verify) + hostname = opts.addr; + + ret = gnutls_certificate_verify_peers3(session, hostname, &status); if (ret != GNUTLS_E_SUCCESS) { tls_perror("Unable to verify TLS peer", ret); return -1; From ca34cb1cbb75bf350e8479d68af1366945957db3 Mon Sep 17 00:00:00 2001 From: Radostin Stoyanov Date: Thu, 4 Apr 2019 00:44:22 +0100 Subject: [PATCH 173/249] rpc: Add support for TLS options Signed-off-by: Radostin Stoyanov --- criu/cr-service.c | 13 +++++++++++++ images/rpc.proto | 6 ++++++ 2 files changed, 19 insertions(+) diff --git a/criu/cr-service.c b/criu/cr-service.c index cb76de4f4..0938db02b 100644 --- a/criu/cr-service.c +++ b/criu/cr-service.c @@ -608,6 +608,19 @@ static int setup_opts_from_req(int sk, CriuOpts *req) goto err; } + if (req->tls_cacert) + SET_CHAR_OPTS(tls_cacert, req->tls_cacert); + if (req->tls_cacrl) + SET_CHAR_OPTS(tls_cacrl, req->tls_cacrl); + if (req->tls_cert) + SET_CHAR_OPTS(tls_cert, req->tls_cert); + if (req->tls_key) + SET_CHAR_OPTS(tls_key, req->tls_key); + if (req->tls) + opts.tls = req->tls; + if (req->tls_no_cn_verify) + opts.tls_no_cn_verify = req->tls_no_cn_verify; + if (req->has_auto_ext_mnt) opts.autodetect_ext_mounts = req->auto_ext_mnt; diff --git a/images/rpc.proto b/images/rpc.proto index 16c5d5028..15e677a77 100644 --- a/images/rpc.proto +++ b/images/rpc.proto @@ -114,6 +114,12 @@ message criu_opts { optional string config_file = 51; optional bool tcp_close = 52; optional string lsm_profile = 53; + optional string tls_cacert = 54; + optional string tls_cacrl = 55; + optional string tls_cert = 56; + optional string tls_key = 57; + optional bool tls = 58; + optional bool tls_no_cn_verify = 59; /* optional bool check_mounts = 128; */ } From 028666d2315632cf7512dc0104e7fb391698e001 Mon Sep 17 00:00:00 2001 From: Radostin Stoyanov Date: Wed, 8 May 2019 21:09:33 +0100 Subject: [PATCH 174/249] zdtm: Add --tls option Signed-off-by: Radostin Stoyanov --- test/pki/cacert.pem | 23 ++++++ test/pki/cert.pem | 24 ++++++ test/pki/key.pem | 182 ++++++++++++++++++++++++++++++++++++++++++++ test/zdtm.py | 23 ++++-- 4 files changed, 246 insertions(+), 6 deletions(-) create mode 100644 test/pki/cacert.pem create mode 100644 test/pki/cert.pem create mode 100644 test/pki/key.pem diff --git a/test/pki/cacert.pem b/test/pki/cacert.pem new file mode 100644 index 000000000..2f8706616 --- /dev/null +++ b/test/pki/cacert.pem @@ -0,0 +1,23 @@ +-----BEGIN CERTIFICATE----- +MIID0TCCAjmgAwIBAgIUWzgmx9p7y7mkrNptGX9+0acjpa4wDQYJKoZIhvcNAQEL +BQAwADAeFw0xOTA1MDYxMjAzMDJaFw0yMDA1MDUxMjAzMDJaMAAwggGiMA0GCSqG +SIb3DQEBAQUAA4IBjwAwggGKAoIBgQD0p0lJUlq917GmJuCBeP2eLNd1/MUg1ojy +s7rrpinPYtLZqqquUhp32lfQtt3uJLjkhTrseZd86zWi3SMZlGs8zGGmKfqg0vaG +BXIgpEIr5C0wU9995kL9A6LS+eFZR6vJQETO5T22tjponoqEPOXeU8VaiC9jNipC +uFJT0wyC0bKIo+TUn573kxsGMt8jMOv0tc/okUlH16UAsYrmN7kWzgkWTJPddB7S +v5a9ibpPkbh+wrIGK5A6V5hTZ8U1wz2bE6/Xp+qjsD2R3jeU6f1tDvc8FZilabQy +Rmbxggucl1G3Ulo6Nvor1lhog72eZlHZujzf/5e/aMiZ7Br6plZ1/WTwtNgoCw6A +rgpLDraasQohiK6opYs2rr7uuiQxPLLVWE/RryXwUEoPXzxaf782XtXxkB0UhGvz +y2JBxCVPn7uUGuyEYywjTjI2UFvsMcXnMiQ4WaAfMbAmrBWM7EQ4b7VpD2c+OZkQ +J/AJeg85/ovTAtHPjhPP+0a9hnirktkCAwEAAaNDMEEwDwYDVR0TAQH/BAUwAwEB +/zAPBgNVHQ8BAf8EBQMDBwQAMB0GA1UdDgQWBBQOg6AA8Qu/m/O/II5spzYsTnsn +pjANBgkqhkiG9w0BAQsFAAOCAYEA1KKtw+ILKOg1AhGwgPsJXAWZoSIt7hdLaJ3P +WGyTWHLKKoJiGlLj3bSsJcMmMO+UwHBH9lmXrOWe/xcOvob2H+7dXbddQ0pX1wzK +KJKzSG35FZ2BfuSn5oEDtRsHnN2Ikc5MYz1a+F4w2tVL/Hcxld+oSAodDlCbGoe+ +0MkI5f1KhdAw00l/5IO7xPOcThjHw+nB5/cZTQ+l4zLWCWaXkor4IAEq/plPcdX1 +uoLSj3JruLz7/ts/EgG+ARAzXQrJ+LM2hdPB1NiaVxFq7MSWM6FybUdmMYgbP5s4 +RMNqI/M+bU9K5LRySDaiPhDXUoVULuqG1a23GQwXLOjF0JbrUQewfAaTO7TaPFh1 +lr25j9Fc9/gcXZjvLl+CEIv6P/haGOwySCTCks0F5bDehbLjZStPmugcnJflXdBn +lzoejlw2rePojQMlffQsaRGmmhj0beU4WQBfGACcZQB8GFNxQB8aynf0CK7Dvvb0 +9c9y4k0gHL7RxeLoQfq+smzKm+Eo +-----END CERTIFICATE----- diff --git a/test/pki/cert.pem b/test/pki/cert.pem new file mode 100644 index 000000000..a0946ee41 --- /dev/null +++ b/test/pki/cert.pem @@ -0,0 +1,24 @@ +-----BEGIN CERTIFICATE----- +MIIEAzCCAmugAwIBAgIUKV6zLC//OJDnmOYBuIG1Gvmv+V4wDQYJKoZIhvcNAQEL +BQAwADAeFw0xOTA1MDYxMjAzMDJaFw0yMDA1MDUxMjAzMDJaMBQxEjAQBgNVBAMT +CWxvY2FsaG9zdDCCAaIwDQYJKoZIhvcNAQEBBQADggGPADCCAYoCggGBANX1nv4J +U8+TEb2bWej5O2nOowpw2zSYTDAQ1oyAvV3P99Y6GZCuVZ1uT/7DWat0uRpcdmNi +HvownkO4VmDZdVqgiK1eHzY5YBJ7hBVDs3tpWNuN7eJPjnskNmJqKQ6l9rxYl/au +781T+tdtHp1ATtToMgVJxWaUx5lrpEJdmYc8Y6GpAA42D+rI3o4Sll3mI5rPCk16 +QY5dT2lnL2HuCKzM2bjWat6b3lMpfNz3A/blU9E/462Zxr/yKK/0yy3SBZhYzrrQ +1/erjIpm4I0sakHIOexM1AQliFiowFzVvr/paiXApWGOcuBJVIbmPI/bEGuTh0nr +3pmiF0YrkDCRhargElYcz64KQ9IxPFCKcKjkMnFPjTStZ7rcMyqKvGczqFaM5a6c +9gIn2ieUrVZ38yvtI5Lo/uxZ5IjXqB1Fdg4xi2tyf9WMHKy2tydBr9bTjfQRXfNT +/Zm3woDXOYsHzj+Sf6ntLVCkO1fnczw03fPRV03/uVRa5mPGyyj9xdPBqwIDAQAB +o2EwXzAMBgNVHRMBAf8EAjAAMA8GA1UdDwEB/wQFAwMHoAAwHQYDVR0OBBYEFEtF +ELehnIjLzoh/W51TGm2B00QAMB8GA1UdIwQYMBaAFA6DoADxC7+b878gjmynNixO +eyemMA0GCSqGSIb3DQEBCwUAA4IBgQA17NZCaiCgD9P4GPWgdVNkWUrmc8itJuIB +z3c9RdJGduxb9W/D7IW//D3hIblOPorNi3+08kO/IRMGah874MDCprMNppk94WGj +Kgqi/rsxq+rT6bcZXxMrcOIg0j2EvTPIgPh7trd8nHVWxNT/hvFClDtBJ2ssL2Tz +76EA7smDCUsfdzFJ2Xvk95fSTL49nfT2j9N/YoLaBQtCIxWAVZHKiCF2K+yXufHz +B/9UlXwsPJfqxM75dYWXFEqvhNf08YRHT1e1GRrybNGrNKF864KbLsnASdK4N5wu +sK9vZJ7VkLDQz+YpZkbm+UgOYK/BY3M8IX+F+WngV+43fr6Wh89TSgD7acEBvQTm +q1y9FipRvz0my7fwBh6UlYDja6/3yw6/YfN7uMFGsOOSgpNDCrMLqesf8l1HdQUF +VaVJyDjgFswV9KykAeJK2KU8QI7TGHv9soW60sr97DgUtCh4a6OPXLt79Ji3RSNw +MbU54JnpnfmMAj/0suDymdrJWv8EJKc= +-----END CERTIFICATE----- diff --git a/test/pki/key.pem b/test/pki/key.pem new file mode 100644 index 000000000..eda1aa761 --- /dev/null +++ b/test/pki/key.pem @@ -0,0 +1,182 @@ +Public Key Info: + Public Key Algorithm: RSA + Key Security Level: High (3072 bits) + +modulus: + 00:d5:f5:9e:fe:09:53:cf:93:11:bd:9b:59:e8:f9:3b + 69:ce:a3:0a:70:db:34:98:4c:30:10:d6:8c:80:bd:5d + cf:f7:d6:3a:19:90:ae:55:9d:6e:4f:fe:c3:59:ab:74 + b9:1a:5c:76:63:62:1e:fa:30:9e:43:b8:56:60:d9:75 + 5a:a0:88:ad:5e:1f:36:39:60:12:7b:84:15:43:b3:7b + 69:58:db:8d:ed:e2:4f:8e:7b:24:36:62:6a:29:0e:a5 + f6:bc:58:97:f6:ae:ef:cd:53:fa:d7:6d:1e:9d:40:4e + d4:e8:32:05:49:c5:66:94:c7:99:6b:a4:42:5d:99:87 + 3c:63:a1:a9:00:0e:36:0f:ea:c8:de:8e:12:96:5d:e6 + 23:9a:cf:0a:4d:7a:41:8e:5d:4f:69:67:2f:61:ee:08 + ac:cc:d9:b8:d6:6a:de:9b:de:53:29:7c:dc:f7:03:f6 + e5:53:d1:3f:e3:ad:99:c6:bf:f2:28:af:f4:cb:2d:d2 + 05:98:58:ce:ba:d0:d7:f7:ab:8c:8a:66:e0:8d:2c:6a + 41:c8:39:ec:4c:d4:04:25:88:58:a8:c0:5c:d5:be:bf + e9:6a:25:c0:a5:61:8e:72:e0:49:54:86:e6:3c:8f:db + 10:6b:93:87:49:eb:de:99:a2:17:46:2b:90:30:91:85 + aa:e0:12:56:1c:cf:ae:0a:43:d2:31:3c:50:8a:70:a8 + e4:32:71:4f:8d:34:ad:67:ba:dc:33:2a:8a:bc:67:33 + a8:56:8c:e5:ae:9c:f6:02:27:da:27:94:ad:56:77:f3 + 2b:ed:23:92:e8:fe:ec:59:e4:88:d7:a8:1d:45:76:0e + 31:8b:6b:72:7f:d5:8c:1c:ac:b6:b7:27:41:af:d6:d3 + 8d:f4:11:5d:f3:53:fd:99:b7:c2:80:d7:39:8b:07:ce + 3f:92:7f:a9:ed:2d:50:a4:3b:57:e7:73:3c:34:dd:f3 + d1:57:4d:ff:b9:54:5a:e6:63:c6:cb:28:fd:c5:d3:c1 + ab: + +public exponent: + 01:00:01: + +private exponent: + 1e:38:b0:79:7f:85:c8:17:24:f5:5c:41:29:e8:32:5d + 32:a3:d2:f0:b7:f5:c8:e1:52:14:be:c9:5f:d1:df:b3 + 65:75:6c:05:7a:6b:35:8a:a4:2f:46:73:ff:71:79:6e + 3f:eb:f9:88:f6:2e:1b:f6:cc:14:12:b0:98:c3:7e:91 + 0b:85:e2:bf:1d:b7:82:09:30:f3:23:68:01:85:13:94 + 80:c9:9a:55:94:96:da:30:48:a0:29:ec:86:da:1b:d5 + 2b:2b:74:63:92:b8:2a:8f:87:29:f0:ae:d7:55:63:0d + 2d:b3:0b:0e:2d:84:dc:d5:08:b5:ac:a0:f7:29:9d:71 + 89:3d:27:6a:eb:96:f5:4e:9b:8a:dc:14:82:0a:c7:5c + 16:1c:d2:7e:b9:1b:13:69:d8:b2:b1:b1:7e:aa:a9:ad + 06:ce:66:0e:5b:50:10:42:2a:0a:fd:29:14:f7:09:63 + c1:20:18:5f:27:81:46:12:8c:b8:f4:89:a6:3d:55:a1 + d4:64:fc:f2:db:d7:9c:f5:be:f7:9d:88:5c:6d:36:a4 + 4b:ea:c5:e3:ea:32:81:6b:f3:47:b5:35:d5:c4:1a:b2 + ae:12:9d:19:a3:ec:a4:af:41:7e:5e:34:9d:f5:bc:b9 + 1f:a3:c2:32:b4:fc:95:a7:7a:54:04:e2:d6:4e:10:2f + 66:68:8b:3b:20:ea:05:db:2e:72:01:11:e7:7c:f8:72 + 0f:60:be:f1:27:19:ad:3a:6f:e9:70:56:3a:86:6e:46 + 0d:e3:55:31:66:77:09:84:48:b9:25:4b:c3:26:70:12 + ca:a4:5f:c6:3d:6a:e5:db:4d:63:04:b8:09:07:c9:30 + 85:08:9d:77:40:26:60:da:10:c2:53:d2:00:0d:9e:d9 + d5:71:06:30:eb:fb:f7:3f:82:1f:b3:9a:f3:4d:24:86 + 2e:94:fd:06:9e:dc:26:68:fa:64:c3:f9:fa:08:c4:b2 + ec:7a:f5:55:c5:10:b5:e2:2d:de:ba:04:30:10:5b:99 + + +prime1: + 00:fb:d1:47:9d:9e:73:f8:1e:09:21:fd:89:16:05:56 + af:a5:cf:52:d5:cd:f7:26:18:d1:84:3a:36:65:0b:a2 + cd:f9:b8:99:c0:c7:ef:00:c9:2f:c9:92:1a:1d:3d:86 + 58:3b:b1:be:d4:8c:c6:1b:df:ba:ee:87:aa:d1:22:47 + 18:bd:de:01:0f:0d:cb:ac:d0:48:a4:f4:93:e2:a6:cb + b5:b7:f5:f5:72:dd:ec:ac:13:e8:3d:62:23:54:ac:52 + ff:ee:9a:e1:7f:b0:ae:3b:41:38:d8:39:2b:40:ef:25 + 81:50:b0:98:db:f8:40:16:6e:1a:41:79:22:90:58:99 + 80:c2:0d:ba:b5:d3:54:ec:28:33:e4:b0:58:ea:de:61 + a1:b7:30:0b:9d:dc:73:62:c2:07:d3:75:91:48:49:dd + be:cf:b2:90:95:8f:29:6c:6f:f6:68:cb:cf:d5:24:a3 + d7:37:81:1b:34:3b:af:9a:48:52:af:53:7c:f7:32:a2 + 3f: + +prime2: + 00:d9:83:5e:be:0a:ea:0b:d9:66:63:56:3b:9e:44:aa + 46:6d:8d:6c:10:81:4b:de:19:5d:2c:16:7e:30:7c:ad + 23:9a:89:53:cc:18:e8:e8:51:2b:79:35:d0:67:7d:9e + 8f:be:ea:63:5e:14:c0:6b:ba:02:6c:4a:da:07:70:9d + 14:fa:be:1e:40:47:50:6f:f2:5a:87:9e:b6:b1:b8:55 + 2c:b6:a2:e3:b0:24:ba:ea:9b:55:87:8b:4b:cf:40:4a + 25:b4:89:cf:9e:76:ca:79:4a:f4:74:b7:ee:cf:6c:8f + cb:e3:3d:9e:86:3b:44:b7:70:ec:05:0c:68:ce:d6:c3 + a2:ec:e6:11:d6:2f:f7:80:26:a9:5c:aa:b9:a6:33:84 + a9:00:43:cf:72:07:8a:91:59:a2:b1:de:79:07:6b:81 + 67:a5:c2:4b:fd:29:8a:1a:96:66:57:66:d4:37:9a:98 + 69:d1:19:24:53:b1:a4:54:68:1e:8c:2b:b4:93:19:ed + 95: + +coefficient: + 00:90:9a:7f:6f:14:a8:bc:79:3f:25:e5:62:f9:5d:29 + 78:a4:78:8e:7a:e4:8a:62:8a:7f:9c:ae:75:95:fe:ee + 1a:99:53:40:01:76:29:7d:48:85:28:a2:2a:9f:0f:10 + 8c:19:6a:36:6b:e1:ac:a2:07:b9:72:5c:b9:a6:20:bb + 8f:cb:f5:ea:dd:3f:0e:ab:9d:c1:57:7e:7b:96:f9:da + b0:52:3c:3f:62:94:e7:5c:04:9e:ac:60:cd:4d:ec:7e + 68:d3:fb:2a:b4:02:f0:0e:be:37:bc:2a:f8:6e:8d:31 + b5:38:67:00:9e:67:9f:71:d0:88:36:32:69:4b:20:73 + eb:a1:d9:bc:72:c2:7e:39:1a:36:cc:c1:45:a2:14:37 + e6:ca:db:4d:0b:5b:68:a4:ff:b7:7b:b1:db:2f:70:27 + a1:6c:31:3f:c0:c3:23:04:b0:7a:e2:0d:21:ba:5a:80 + 52:c1:a1:2b:57:72:20:b6:ed:b1:e8:3b:95:88:81:90 + 5d: + +exp1: + 00:ef:ce:66:20:01:44:b9:35:89:46:f8:56:33:45:54 + 3f:23:6d:23:9a:7e:71:6d:b3:56:db:50:40:7a:cb:b0 + f7:ec:67:52:ec:96:b9:d1:8a:c6:5a:74:2b:30:4b:66 + 03:e2:9d:2b:78:e8:b2:c4:da:b3:fe:f1:ed:c7:09:98 + a1:44:37:05:d5:1b:33:2a:58:93:c5:9b:30:b6:38:57 + 68:af:4e:a8:b7:02:06:9f:fc:b9:3e:b3:95:a7:ce:0f + a0:b0:ce:88:0e:7c:e7:ff:7f:e6:2d:6b:8b:f8:63:85 + d8:f7:49:a5:d8:5d:3a:52:e1:f9:58:fe:8d:de:de:b1 + 18:40:34:a8:e8:fc:df:33:a2:39:81:00:3b:3d:38:17 + cb:d4:53:09:cd:04:a2:51:9b:2b:ae:c1:98:60:3a:0f + d4:e5:a0:4c:36:51:46:86:80:bd:2d:21:62:c3:bd:07 + d6:2d:82:62:b0:c4:62:3f:4f:be:86:3e:c0:93:fc:81 + 2b: + +exp2: + 11:e4:73:93:b0:74:26:3b:60:e7:c4:fd:2c:7c:bb:81 + 05:9b:ff:8a:b0:08:1c:a1:fb:7f:17:ee:93:70:7e:11 + 92:b1:bf:39:e7:c6:a8:ed:9c:64:e1:1f:5e:93:ff:ca + 15:4b:54:97:35:9f:ca:7c:c7:9c:3e:e0:06:82:a5:f9 + 46:d3:02:cc:08:d1:be:13:b2:8c:bb:6a:8d:dd:fa:eb + ad:ae:62:8a:67:cb:14:67:68:b6:b8:a7:a8:c9:c2:0f + ad:f5:34:25:f5:e1:9b:ee:a5:83:40:6a:1d:97:f1:90 + 35:06:29:97:23:22:f8:f0:0a:0a:34:46:1e:d5:9d:cc + 36:2e:8a:c3:12:b9:0a:4a:a3:dd:e2:91:58:f1:9d:f5 + 04:f7:8f:05:f3:46:db:c4:02:d5:1c:d6:d9:dc:67:0d + ae:9d:f8:00:40:3d:83:08:62:2c:c8:61:a6:9d:49:f2 + 52:67:fe:0c:00:6d:e3:1f:99:7b:b0:50:af:55:0f:ad + + + +Public Key PIN: + pin-sha256:EiqPFBPoLKkCzVlK8KoKYGQT/LSo7/0iLg/I7nKt1/0= +Public Key ID: + sha256:122a8f1413e82ca902cd594af0aa0a606413fcb4a8effd222e0fc8ee72add7fd + sha1:4b4510b7a19c88cbce887f5b9d531a6d81d34400 + +-----BEGIN RSA PRIVATE KEY----- +MIIG5AIBAAKCAYEA1fWe/glTz5MRvZtZ6Pk7ac6jCnDbNJhMMBDWjIC9Xc/31joZ +kK5VnW5P/sNZq3S5Glx2Y2Ie+jCeQ7hWYNl1WqCIrV4fNjlgEnuEFUOze2lY243t +4k+OeyQ2YmopDqX2vFiX9q7vzVP6120enUBO1OgyBUnFZpTHmWukQl2ZhzxjoakA +DjYP6sjejhKWXeYjms8KTXpBjl1PaWcvYe4IrMzZuNZq3pveUyl83PcD9uVT0T/j +rZnGv/Ior/TLLdIFmFjOutDX96uMimbgjSxqQcg57EzUBCWIWKjAXNW+v+lqJcCl +YY5y4ElUhuY8j9sQa5OHSevemaIXRiuQMJGFquASVhzPrgpD0jE8UIpwqOQycU+N +NK1nutwzKoq8ZzOoVozlrpz2AifaJ5StVnfzK+0jkuj+7FnkiNeoHUV2DjGLa3J/ +1YwcrLa3J0Gv1tON9BFd81P9mbfCgNc5iwfOP5J/qe0tUKQ7V+dzPDTd89FXTf+5 +VFrmY8bLKP3F08GrAgMBAAECggGAHjiweX+FyBck9VxBKegyXTKj0vC39cjhUhS+ +yV/R37NldWwFems1iqQvRnP/cXluP+v5iPYuG/bMFBKwmMN+kQuF4r8dt4IJMPMj +aAGFE5SAyZpVlJbaMEigKeyG2hvVKyt0Y5K4Ko+HKfCu11VjDS2zCw4thNzVCLWs +oPcpnXGJPSdq65b1TpuK3BSCCsdcFhzSfrkbE2nYsrGxfqqprQbOZg5bUBBCKgr9 +KRT3CWPBIBhfJ4FGEoy49ImmPVWh1GT88tvXnPW+952IXG02pEvqxePqMoFr80e1 +NdXEGrKuEp0Zo+ykr0F+XjSd9by5H6PCMrT8lad6VATi1k4QL2Zoizsg6gXbLnIB +Eed8+HIPYL7xJxmtOm/pcFY6hm5GDeNVMWZ3CYRIuSVLwyZwEsqkX8Y9auXbTWME +uAkHyTCFCJ13QCZg2hDCU9IADZ7Z1XEGMOv79z+CH7Oa800khi6U/Qae3CZo+mTD ++foIxLLsevVVxRC14i3eugQwEFuZAoHBAPvRR52ec/geCSH9iRYFVq+lz1LVzfcm +GNGEOjZlC6LN+biZwMfvAMkvyZIaHT2GWDuxvtSMxhvfuu6HqtEiRxi93gEPDcus +0Eik9JPipsu1t/X1ct3srBPoPWIjVKxS/+6a4X+wrjtBONg5K0DvJYFQsJjb+EAW +bhpBeSKQWJmAwg26tdNU7Cgz5LBY6t5hobcwC53cc2LCB9N1kUhJ3b7PspCVjyls +b/Zoy8/VJKPXN4EbNDuvmkhSr1N89zKiPwKBwQDZg16+CuoL2WZjVjueRKpGbY1s +EIFL3hldLBZ+MHytI5qJU8wY6OhRK3k10Gd9no++6mNeFMBrugJsStoHcJ0U+r4e +QEdQb/Jah562sbhVLLai47AkuuqbVYeLS89ASiW0ic+edsp5SvR0t+7PbI/L4z2e +hjtEt3DsBQxoztbDouzmEdYv94AmqVyquaYzhKkAQ89yB4qRWaKx3nkHa4FnpcJL +/SmKGpZmV2bUN5qYadEZJFOxpFRoHowrtJMZ7ZUCgcEA785mIAFEuTWJRvhWM0VU +PyNtI5p+cW2zVttQQHrLsPfsZ1LslrnRisZadCswS2YD4p0reOiyxNqz/vHtxwmY +oUQ3BdUbMypYk8WbMLY4V2ivTqi3Agaf/Lk+s5Wnzg+gsM6IDnzn/3/mLWuL+GOF +2PdJpdhdOlLh+Vj+jd7esRhANKjo/N8zojmBADs9OBfL1FMJzQSiUZsrrsGYYDoP +1OWgTDZRRoaAvS0hYsO9B9YtgmKwxGI/T76GPsCT/IErAoHAEeRzk7B0Jjtg58T9 +LHy7gQWb/4qwCByh+38X7pNwfhGSsb8558ao7Zxk4R9ek//KFUtUlzWfynzHnD7g +BoKl+UbTAswI0b4Tsoy7ao3d+uutrmKKZ8sUZ2i2uKeoycIPrfU0JfXhm+6lg0Bq +HZfxkDUGKZcjIvjwCgo0Rh7Vncw2LorDErkKSqPd4pFY8Z31BPePBfNG28QC1RzW +2dxnDa6d+ABAPYMIYizIYaadSfJSZ/4MAG3jH5l7sFCvVQ+tAoHBAJCaf28UqLx5 +PyXlYvldKXikeI565Ipiin+crnWV/u4amVNAAXYpfUiFKKIqnw8QjBlqNmvhrKIH +uXJcuaYgu4/L9erdPw6rncFXfnuW+dqwUjw/YpTnXASerGDNTex+aNP7KrQC8A6+ +N7wq+G6NMbU4ZwCeZ59x0Ig2MmlLIHProdm8csJ+ORo2zMFFohQ35srbTQtbaKT/ +t3ux2y9wJ6FsMT/AwyMEsHriDSG6WoBSwaErV3Igtu2x6DuViIGQXQ== +-----END RSA PRIVATE KEY----- diff --git a/test/zdtm.py b/test/zdtm.py index 0adc22c39..57eb68a49 100755 --- a/test/zdtm.py +++ b/test/zdtm.py @@ -961,6 +961,7 @@ def __init__(self, opts): self.__lazy_pages_p = None self.__page_server_p = None self.__dump_process = None + self.__tls = self.__tls_options() if opts['tls'] else [] self.__criu_bin = opts['criu_bin'] self.__crit_bin = opts['crit_bin'] @@ -1009,6 +1010,13 @@ def cleanup(self): print("Removing %s" % self.__dump_path) shutil.rmtree(self.__dump_path) + def __tls_options(self): + pki_dir = os.path.dirname(os.path.abspath(__file__)) + "/pki" + return ["--tls", "--tls-no-cn-verify", + "--tls-key", pki_dir + "/key.pem", + "--tls-cert", pki_dir + "/cert.pem", + "--tls-cacert", pki_dir + "/cacert.pem"] + def __ddir(self): return os.path.join(self.__dump_path, "%d" % self.__iter) @@ -1142,12 +1150,12 @@ def dump(self, action, opts = []): if self.__page_server: print("Adding page server") - ps_opts = ["--port", "12345"] + ps_opts = ["--port", "12345"] + self.__tls if self.__dedup: ps_opts += ["--auto-dedup"] self.__page_server_p = self.__criu_act("page-server", opts = ps_opts, nowait = True) - a_opts += ["--page-server", "--address", "127.0.0.1", "--port", "12345"] + a_opts += ["--page-server", "--address", "127.0.0.1", "--port", "12345"] + self.__tls a_opts += self.__test.getdopts() @@ -1189,7 +1197,7 @@ def dump(self, action, opts = []): nowait = False if self.__lazy_migrate and action == "dump": - a_opts += ["--lazy-pages", "--port", "12345"] + a_opts += ["--lazy-pages", "--port", "12345"] + self.__tls nowait = True self.__dump_process = self.__criu_act(action, opts = a_opts + opts, nowait = nowait) if self.__mdedup and self.__iter > 1: @@ -1237,10 +1245,12 @@ def restore(self): if self.__lazy_pages or self.__lazy_migrate: lp_opts = [] if self.__remote_lazy_pages or self.__lazy_migrate: - lp_opts += ['--page-server', "--port", "12345", "--address", "127.0.0.1"] + lp_opts += ["--page-server", "--port", "12345", + "--address", "127.0.0.1"] + self.__tls + if self.__remote_lazy_pages: ps_opts = ["--pidfile", "ps.pid", - "--port", "12345", "--lazy-pages"] + "--port", "12345", "--lazy-pages"] + self.__tls self.__page_server_p = self.__criu_act("page-server", opts = ps_opts, nowait = True) self.__lazy_pages_p = self.__criu_act("lazy-pages", opts = lp_opts, nowait = True) r_opts += ["--lazy-pages"] @@ -1741,7 +1751,7 @@ def run_test(self, name, desc, flavor): nd = ('nocr', 'norst', 'pre', 'iters', 'page_server', 'sibling', 'stop', 'empty_ns', 'fault', 'keep_img', 'report', 'snaps', 'sat', 'script', 'rpc', 'lazy_pages', 'join_ns', 'dedup', 'sbs', 'freezecg', 'user', 'dry_run', 'noauto_dedup', - 'remote_lazy_pages', 'show_stats', 'lazy_migrate', 'remote', + 'remote_lazy_pages', 'show_stats', 'lazy_migrate', 'remote', 'tls', 'criu_bin', 'crit_bin') arg = repr((name, desc, flavor, {d: self.__opts[d] for d in nd})) @@ -2306,6 +2316,7 @@ def clean_stuff(opts): rp.add_argument("--lazy-pages", help = "restore pages on demand", action = 'store_true') rp.add_argument("--lazy-migrate", help = "restore pages on demand", action = 'store_true') rp.add_argument("--remote-lazy-pages", help = "simulate lazy migration", action = 'store_true') +rp.add_argument("--tls", help = "use TLS for migration", action = 'store_true') rp.add_argument("--title", help = "A test suite title", default = "criu") rp.add_argument("--show-stats", help = "Show criu statistics", action = 'store_true') rp.add_argument("--criu-bin", help = "Path to criu binary", default = '../criu/criu') From 3953b333292ded4792cc5ba1f722f1873cef77dd Mon Sep 17 00:00:00 2001 From: Radostin Stoyanov Date: Wed, 8 May 2019 21:45:39 +0100 Subject: [PATCH 175/249] travis: Enable TLS testing Signed-off-by: Radostin Stoyanov --- scripts/build/Dockerfile.alpine | 1 + scripts/build/Dockerfile.centos | 1 + scripts/build/Dockerfile.fedora.tmpl | 1 + scripts/build/Dockerfile.tmpl | 2 ++ scripts/travis/travis-tests | 5 +++-- 5 files changed, 8 insertions(+), 2 deletions(-) diff --git a/scripts/build/Dockerfile.alpine b/scripts/build/Dockerfile.alpine index a91e01637..c71a3901f 100644 --- a/scripts/build/Dockerfile.alpine +++ b/scripts/build/Dockerfile.alpine @@ -9,6 +9,7 @@ RUN apk update && apk add \ ccache \ coreutils \ git \ + gnutls-dev \ libaio-dev \ libcap-dev \ libnet-dev \ diff --git a/scripts/build/Dockerfile.centos b/scripts/build/Dockerfile.centos index 2ed3a2db9..2ce40b179 100644 --- a/scripts/build/Dockerfile.centos +++ b/scripts/build/Dockerfile.centos @@ -9,6 +9,7 @@ RUN yum install -y \ findutils \ gcc \ git \ + gnutls-devel \ iproute \ iptables \ libaio-devel \ diff --git a/scripts/build/Dockerfile.fedora.tmpl b/scripts/build/Dockerfile.fedora.tmpl index 22ebaed9c..965309623 100644 --- a/scripts/build/Dockerfile.fedora.tmpl +++ b/scripts/build/Dockerfile.fedora.tmpl @@ -6,6 +6,7 @@ RUN dnf install -y \ findutils \ gcc \ git \ + gnutls-devel \ iproute \ iptables \ libaio-devel \ diff --git a/scripts/build/Dockerfile.tmpl b/scripts/build/Dockerfile.tmpl index bdfdf713a..4378ba149 100644 --- a/scripts/build/Dockerfile.tmpl +++ b/scripts/build/Dockerfile.tmpl @@ -12,6 +12,8 @@ RUN apt-get update && apt-get install -y \ iptables \ libaio-dev \ libcap-dev \ + libgnutls28-dev \ + libgnutls30 \ libnl-3-dev \ libprotobuf-c0-dev \ libprotobuf-dev \ diff --git a/scripts/travis/travis-tests b/scripts/travis/travis-tests index 47ff199cf..664f723e9 100755 --- a/scripts/travis/travis-tests +++ b/scripts/travis/travis-tests @@ -2,8 +2,8 @@ set -x -e TRAVIS_PKGS="protobuf-c-compiler libprotobuf-c0-dev libaio-dev - libprotobuf-dev protobuf-compiler libcap-dev - libnl-3-dev gcc-multilib gdb bash python-protobuf + libgnutls28-dev libgnutls30 libprotobuf-dev protobuf-compiler + libcap-dev libnl-3-dev gcc-multilib gdb bash python-protobuf libnet-dev util-linux asciidoctor libnl-route-3-dev" travis_prep () { @@ -125,6 +125,7 @@ LAZY_TESTS=.*\(maps0\|uffd-events\|lazy-thp\|futex\|fork\).* ./test/zdtm.py run -p 2 -T $LAZY_TESTS --lazy-pages $LAZY_EXCLUDE $ZDTM_OPTS ./test/zdtm.py run -p 2 -T $LAZY_TESTS --remote-lazy-pages $LAZY_EXCLUDE $ZDTM_OPTS +./test/zdtm.py run -p 2 -T $LAZY_TESTS --remote-lazy-pages --tls $LAZY_EXCLUDE $ZDTM_OPTS bash ./test/jenkins/criu-fault.sh bash ./test/jenkins/criu-fcg.sh From 316193d70ddd40ac838bb20306c6c052b8873d4a Mon Sep 17 00:00:00 2001 From: Radostin Stoyanov Date: Thu, 27 Jun 2019 15:51:50 +0100 Subject: [PATCH 176/249] zdtm: Refactor seccomp_filter_{threads,tsync} As discusses on the musl mailing list [1] when libc api is used to create a POSIX thread, and this thread is killed by seccomp, this breaks a fundamental assumption the C runtime relies on, causing any libc call (i.e. pthread_join) after the kill to have undefined behaviour. In order to work around the issue we could use SECCOMP_RET_ERRNO instead of SECCOMP_RET_KILL. This filter will set a magic value to user space as errno without executing the system call. [1] https://www.openwall.com/lists/musl/2019/06/26/7 Rresolves #725 Signed-off-by: Radostin Stoyanov --- test/zdtm/static/seccomp_filter_threads.c | 26 +++++++++++++++-------- test/zdtm/static/seccomp_filter_tsync.c | 23 ++++++++++---------- 2 files changed, 29 insertions(+), 20 deletions(-) diff --git a/test/zdtm/static/seccomp_filter_threads.c b/test/zdtm/static/seccomp_filter_threads.c index b3fa6089d..63ea4b5bd 100644 --- a/test/zdtm/static/seccomp_filter_threads.c +++ b/test/zdtm/static/seccomp_filter_threads.c @@ -39,6 +39,8 @@ static long sys_gettid(void) { return syscall(__NR_gettid); } static futex_t *wait_rdy; static futex_t *wait_run; +static int magic = 1234; + int get_seccomp_mode(pid_t pid) { FILE *f; @@ -70,7 +72,7 @@ int filter_syscall(int syscall_nr, unsigned int flags) struct sock_filter filter[] = { BPF_STMT(BPF_LD+BPF_W+BPF_ABS, offsetof(struct seccomp_data, nr)), BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, syscall_nr, 0, 1), - BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_KILL), + BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_ERRNO | (SECCOMP_RET_DATA & magic)), BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_ALLOW), }; @@ -87,9 +89,9 @@ int filter_syscall(int syscall_nr, unsigned int flags) return 0; } -void tigger_ptrace(void) { ptrace(PTRACE_TRACEME); } -void trigger_prctl(void) { prctl(PR_SET_PDEATHSIG, 9, 0, 0, 0); } -void trigger_mincore(void) { mincore(NULL, 0, NULL); } +int tigger_ptrace(void) { return ptrace(PTRACE_TRACEME); } +int trigger_prctl(void) { return prctl(PR_SET_PDEATHSIG, 9, 0, 0, 0); } +int trigger_mincore(void) { return mincore(NULL, 0, NULL); } #define gen_param(__syscall_nr, __trigger) \ { \ @@ -101,7 +103,7 @@ void trigger_mincore(void) { mincore(NULL, 0, NULL); } struct { char *syscall_name; unsigned int syscall_nr; - void (*trigger)(void); + int (*trigger)(void); } pthread_seccomp_params[] = { gen_param(__NR_ptrace, tigger_ptrace), gen_param(__NR_prctl, trigger_prctl), @@ -112,6 +114,7 @@ struct { void *thread_main(void *arg) { + int ret; size_t nr = (long) arg; if (filter_syscall(pthread_seccomp_params[nr].syscall_nr, 0) < 0) @@ -128,10 +131,12 @@ void *thread_main(void *arg) nr, pthread_seccomp_params[nr].syscall_name, sys_gettid()); - pthread_seccomp_params[nr].trigger(); + ret = pthread_seccomp_params[nr].trigger(); + if (ret == -1 && errno == magic) + return (void *)0; test_msg("Abnormal exit %zu thread %lu\n", nr, sys_gettid()); - pthread_exit((void *)1); + return (void *)1; } int main(int argc, char ** argv) @@ -167,7 +172,7 @@ int main(int argc, char ** argv) if (pid == 0) { pthread_t thread[ARRAY_SIZE(pthread_seccomp_params)]; - void *p = NULL; + void *ret; zdtm_seccomp = 1; @@ -180,10 +185,13 @@ int main(int argc, char ** argv) for (i = 0; i < ARRAY_SIZE(pthread_seccomp_params); i++) { test_msg("Waiting thread %zu\n", i); - if (pthread_join(thread[i], &p) != 0) { + if (pthread_join(thread[i], &ret) != 0) { pr_perror("pthread_join"); exit(1); } + + if (ret != 0) + syscall(__NR_exit, 1); } syscall(__NR_exit, 0); diff --git a/test/zdtm/static/seccomp_filter_tsync.c b/test/zdtm/static/seccomp_filter_tsync.c index 9b4742ba1..e374f0aff 100644 --- a/test/zdtm/static/seccomp_filter_tsync.c +++ b/test/zdtm/static/seccomp_filter_tsync.c @@ -34,6 +34,8 @@ const char *test_author = "Tycho Andersen "; pthread_mutex_t getpid_wait; +static int magic = 1234; + int get_seccomp_mode(pid_t pid) { FILE *f; @@ -65,7 +67,7 @@ int filter_syscall(int syscall_nr, unsigned int flags) struct sock_filter filter[] = { BPF_STMT(BPF_LD+BPF_W+BPF_ABS, offsetof(struct seccomp_data, nr)), BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, syscall_nr, 0, 1), - BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_KILL), + BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_ERRNO | (SECCOMP_RET_DATA & magic)), BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_ALLOW), }; @@ -84,14 +86,19 @@ int filter_syscall(int syscall_nr, unsigned int flags) void *wait_and_getpid(void *arg) { + int ret; + pthread_mutex_lock(&getpid_wait); pthread_mutex_unlock(&getpid_wait); pthread_mutex_destroy(&getpid_wait); - /* we expect the tg to get killed by the seccomp filter that was - * installed via TSYNC */ - ptrace(PTRACE_TRACEME); - pthread_exit((void *)1); + /* we expect seccomp to exit with + * an error and set errno = magic */ + ret = ptrace(PTRACE_TRACEME); + if (ret == -1 && errno == magic) + return (void *)0; + + return ((void *)1); } int main(int argc, char ** argv) @@ -159,12 +166,6 @@ int main(int argc, char ** argv) exit(1); } - /* Here we're abusing pthread exit slightly: if the thread gets - * to call pthread_exit, the value of p is one, but if it gets - * killed pthread_join doesn't set a value since the thread - * didn't, so the value is null; we exit 0 to indicate success - * as usual. - */ syscall(__NR_exit, p); } From d3c5f8ccf3885cf0b005c5c3b67b335d9834656c Mon Sep 17 00:00:00 2001 From: Pavel Emelyanov Date: Thu, 27 Jun 2019 13:36:56 +0300 Subject: [PATCH 177/249] py: Reformat everything into pep8 style As discussed on the mailing list, current .py files formatting does not conform to the world standard, so we should better reformat it. For this the yapf tool is used. The command I used was yapf -i $(find -name *.py) Signed-off-by: Pavel Emelyanov --- coredump/criu_coredump/coredump.py | 1335 ++++----- coredump/criu_coredump/elf.py | 1055 ++++--- lib/py/cli.py | 602 ++-- lib/py/criu.py | 433 +-- lib/py/images/images.py | 861 +++--- lib/py/images/pb2dict.py | 594 ++-- scripts/crit-setup.py | 19 +- scripts/magic-gen.py | 108 +- soccr/test/run.py | 20 +- test/check_actions.py | 41 +- test/crit-recode.py | 100 +- test/exhaustive/pipe.py | 412 +-- test/exhaustive/unix.py | 1323 ++++----- test/inhfd/fifo.py | 52 +- test/inhfd/pipe.py | 14 +- test/inhfd/socket.py | 14 +- test/inhfd/tty.py | 35 +- test/others/ext-tty/run.py | 27 +- test/others/mounts/mounts.py | 47 +- test/others/rpc/config_file.py | 255 +- test/others/rpc/errno.py | 182 +- test/others/rpc/ps_test.py | 76 +- test/others/rpc/read.py | 2 +- test/others/rpc/restore-loop.py | 37 +- test/others/rpc/test.py | 75 +- test/others/rpc/version.py | 34 +- test/others/shell-job/run.py | 13 +- test/zdtm.py | 4237 +++++++++++++++------------- 28 files changed, 6293 insertions(+), 5710 deletions(-) diff --git a/coredump/criu_coredump/coredump.py b/coredump/criu_coredump/coredump.py index 2b0c37f1a..9b2c6c60c 100644 --- a/coredump/criu_coredump/coredump.py +++ b/coredump/criu_coredump/coredump.py @@ -36,795 +36,802 @@ # Some memory-related constants PAGESIZE = 4096 status = { - "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_FORCE_READ" : 1 << 4, - "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_AREA_UNSUPP" : 1 << 31 + "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_FORCE_READ": 1 << 4, + "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_AREA_UNSUPP": 1 << 31 } -prot = { - "PROT_READ" : 0x1, - "PROT_WRITE" : 0x2, - "PROT_EXEC" : 0x4 -} +prot = {"PROT_READ": 0x1, "PROT_WRITE": 0x2, "PROT_EXEC": 0x4} + class elf_note: - nhdr = None # Elf_Nhdr; - owner = None # i.e. CORE or LINUX; - data = None # Ctypes structure with note data; + nhdr = None # Elf_Nhdr; + owner = None # i.e. CORE or LINUX; + data = None # Ctypes structure with note data; class coredump: - """ + """ A class to keep elf core dump components inside and functions to properly write them to file. """ - ehdr = None # Elf ehdr; - phdrs = [] # Array of Phdrs; - notes = [] # Array of elf_notes; - vmas = [] # Array of BytesIO with memory content; - # FIXME keeping all vmas in memory is a bad idea; + ehdr = None # Elf ehdr; + phdrs = [] # Array of Phdrs; + notes = [] # Array of elf_notes; + vmas = [] # Array of BytesIO with memory content; - def write(self, f): - """ + # FIXME keeping all vmas in memory is a bad idea; + + def write(self, f): + """ Write core dump to file f. """ - buf = io.BytesIO() - buf.write(self.ehdr) + buf = io.BytesIO() + buf.write(self.ehdr) - for phdr in self.phdrs: - buf.write(phdr) + for phdr in self.phdrs: + buf.write(phdr) - for note in self.notes: - buf.write(note.nhdr) - buf.write(note.owner) - buf.write("\0"*(8-len(note.owner))) - buf.write(note.data) + for note in self.notes: + buf.write(note.nhdr) + buf.write(note.owner) + buf.write("\0" * (8 - len(note.owner))) + buf.write(note.data) - offset = ctypes.sizeof(elf.Elf64_Ehdr()) - offset += (len(self.vmas) + 1)*ctypes.sizeof(elf.Elf64_Phdr()) + offset = ctypes.sizeof(elf.Elf64_Ehdr()) + offset += (len(self.vmas) + 1) * ctypes.sizeof(elf.Elf64_Phdr()) - filesz = 0 - for note in self.notes: - filesz += ctypes.sizeof(note.nhdr) + ctypes.sizeof(note.data) + 8 + filesz = 0 + for note in self.notes: + filesz += ctypes.sizeof(note.nhdr) + ctypes.sizeof(note.data) + 8 - note_align = PAGESIZE - ((offset + filesz) % PAGESIZE) + note_align = PAGESIZE - ((offset + filesz) % PAGESIZE) - if note_align == PAGESIZE: - note_align = 0 + if note_align == PAGESIZE: + note_align = 0 - if note_align != 0: - scratch = (ctypes.c_char * note_align)() - ctypes.memset(ctypes.addressof(scratch), 0, ctypes.sizeof(scratch)) - buf.write(scratch) + if note_align != 0: + scratch = (ctypes.c_char * note_align)() + ctypes.memset(ctypes.addressof(scratch), 0, ctypes.sizeof(scratch)) + buf.write(scratch) - for vma in self.vmas: - buf.write(vma.data) + for vma in self.vmas: + buf.write(vma.data) - buf.seek(0) - f.write(buf.read()) + buf.seek(0) + f.write(buf.read()) class coredump_generator: - """ + """ Generate core dump from criu images. """ - coredumps = {} # coredumps by pid; + coredumps = {} # coredumps by pid; - pstree = {} # process info by pid; - cores = {} # cores by pid; - mms = {} # mm by pid; - reg_files = None # reg-files; - pagemaps = {} # pagemap by pid; + pstree = {} # process info by pid; + cores = {} # cores by pid; + mms = {} # mm by pid; + reg_files = None # reg-files; + pagemaps = {} # pagemap by pid; - def _img_open_and_strip(self, name, single = False, pid = None): - """ + def _img_open_and_strip(self, name, single=False, pid=None): + """ Load criu image and strip it from magic and redundant list. """ - path = self._imgs_dir + "/" + name - if pid: - path += "-"+str(pid) - path += ".img" + path = self._imgs_dir + "/" + name + if pid: + path += "-" + str(pid) + path += ".img" - with open(path) as f: - img = images.load(f) + with open(path) as f: + img = images.load(f) - if single: - return img["entries"][0] - else: - return img["entries"] + if single: + return img["entries"][0] + else: + return img["entries"] - - def __call__(self, imgs_dir): - """ + def __call__(self, imgs_dir): + """ Parse criu images stored in directory imgs_dir to fill core dumps. """ - self._imgs_dir = imgs_dir - pstree = self._img_open_and_strip("pstree") + self._imgs_dir = imgs_dir + pstree = self._img_open_and_strip("pstree") - for p in pstree: - pid = p['pid'] + for p in pstree: + pid = p['pid'] - self.pstree[pid] = p - for tid in p['threads']: - self.cores[tid] = self._img_open_and_strip("core", True, tid) - self.mms[pid] = self._img_open_and_strip("mm", True, pid) - self.pagemaps[pid] = self._img_open_and_strip("pagemap", False, pid) + self.pstree[pid] = p + for tid in p['threads']: + self.cores[tid] = self._img_open_and_strip("core", True, tid) + self.mms[pid] = self._img_open_and_strip("mm", True, pid) + self.pagemaps[pid] = self._img_open_and_strip( + "pagemap", False, pid) - files = self._img_open_and_strip("files", False) - self.reg_files = [ x["reg"] for x in files if x["type"]=="REG" ] + files = self._img_open_and_strip("files", False) + self.reg_files = [x["reg"] for x in files if x["type"] == "REG"] - for pid in self.pstree: - self.coredumps[pid] = self._gen_coredump(pid) + for pid in self.pstree: + self.coredumps[pid] = self._gen_coredump(pid) - return self.coredumps + return self.coredumps - - def write(self, coredumps_dir, pid = None): - """ + def write(self, coredumps_dir, pid=None): + """ Write core dumpt to cores_dir directory. Specify pid to choose core dump of only one process. """ - for p in self.coredumps: - if pid and p != pid: - continue - with open(coredumps_dir+"/"+"core."+str(p), 'w+') as f: - self.coredumps[p].write(f) + for p in self.coredumps: + if pid and p != pid: + continue + with open(coredumps_dir + "/" + "core." + str(p), 'w+') as f: + self.coredumps[p].write(f) - def _gen_coredump(self, pid): - """ + def _gen_coredump(self, pid): + """ Generate core dump for pid. """ - cd = coredump() + cd = coredump() - # Generate everything backwards so it is easier to calculate offset. - cd.vmas = self._gen_vmas(pid) - cd.notes = self._gen_notes(pid) - cd.phdrs = self._gen_phdrs(pid, cd.notes, cd.vmas) - cd.ehdr = self._gen_ehdr(pid, cd.phdrs) + # Generate everything backwards so it is easier to calculate offset. + cd.vmas = self._gen_vmas(pid) + cd.notes = self._gen_notes(pid) + cd.phdrs = self._gen_phdrs(pid, cd.notes, cd.vmas) + cd.ehdr = self._gen_ehdr(pid, cd.phdrs) - return cd + return cd - def _gen_ehdr(self, pid, phdrs): - """ + def _gen_ehdr(self, pid, phdrs): + """ Generate elf header for process pid with program headers phdrs. """ - ehdr = elf.Elf64_Ehdr() - - ctypes.memset(ctypes.addressof(ehdr), 0, ctypes.sizeof(ehdr)) - ehdr.e_ident[elf.EI_MAG0] = elf.ELFMAG0 - ehdr.e_ident[elf.EI_MAG1] = elf.ELFMAG1 - ehdr.e_ident[elf.EI_MAG2] = elf.ELFMAG2 - ehdr.e_ident[elf.EI_MAG3] = elf.ELFMAG3 - ehdr.e_ident[elf.EI_CLASS] = elf.ELFCLASS64 - ehdr.e_ident[elf.EI_DATA] = elf.ELFDATA2LSB - ehdr.e_ident[elf.EI_VERSION] = elf.EV_CURRENT - - ehdr.e_type = elf.ET_CORE - ehdr.e_machine = elf.EM_X86_64 - ehdr.e_version = elf.EV_CURRENT - ehdr.e_phoff = ctypes.sizeof(elf.Elf64_Ehdr()) - ehdr.e_ehsize = ctypes.sizeof(elf.Elf64_Ehdr()) - ehdr.e_phentsize = ctypes.sizeof(elf.Elf64_Phdr()) - #FIXME Case len(phdrs) > PN_XNUM should be handled properly. - # See fs/binfmt_elf.c from linux kernel. - ehdr.e_phnum = len(phdrs) - - return ehdr - - def _gen_phdrs(self, pid, notes, vmas): - """ + ehdr = elf.Elf64_Ehdr() + + ctypes.memset(ctypes.addressof(ehdr), 0, ctypes.sizeof(ehdr)) + ehdr.e_ident[elf.EI_MAG0] = elf.ELFMAG0 + ehdr.e_ident[elf.EI_MAG1] = elf.ELFMAG1 + ehdr.e_ident[elf.EI_MAG2] = elf.ELFMAG2 + ehdr.e_ident[elf.EI_MAG3] = elf.ELFMAG3 + ehdr.e_ident[elf.EI_CLASS] = elf.ELFCLASS64 + ehdr.e_ident[elf.EI_DATA] = elf.ELFDATA2LSB + ehdr.e_ident[elf.EI_VERSION] = elf.EV_CURRENT + + ehdr.e_type = elf.ET_CORE + ehdr.e_machine = elf.EM_X86_64 + ehdr.e_version = elf.EV_CURRENT + ehdr.e_phoff = ctypes.sizeof(elf.Elf64_Ehdr()) + ehdr.e_ehsize = ctypes.sizeof(elf.Elf64_Ehdr()) + ehdr.e_phentsize = ctypes.sizeof(elf.Elf64_Phdr()) + #FIXME Case len(phdrs) > PN_XNUM should be handled properly. + # See fs/binfmt_elf.c from linux kernel. + ehdr.e_phnum = len(phdrs) + + return ehdr + + def _gen_phdrs(self, pid, notes, vmas): + """ Generate program headers for process pid. """ - phdrs = [] + phdrs = [] - offset = ctypes.sizeof(elf.Elf64_Ehdr()) - offset += (len(vmas) + 1)*ctypes.sizeof(elf.Elf64_Phdr()) + offset = ctypes.sizeof(elf.Elf64_Ehdr()) + offset += (len(vmas) + 1) * ctypes.sizeof(elf.Elf64_Phdr()) - filesz = 0 - for note in notes: - filesz += ctypes.sizeof(note.nhdr) + ctypes.sizeof(note.data) + 8 + filesz = 0 + for note in notes: + filesz += ctypes.sizeof(note.nhdr) + ctypes.sizeof(note.data) + 8 - # PT_NOTE - phdr = elf.Elf64_Phdr() - ctypes.memset(ctypes.addressof(phdr), 0, ctypes.sizeof(phdr)) - phdr.p_type = elf.PT_NOTE - phdr.p_offset = offset - phdr.p_filesz = filesz + # PT_NOTE + phdr = elf.Elf64_Phdr() + ctypes.memset(ctypes.addressof(phdr), 0, ctypes.sizeof(phdr)) + phdr.p_type = elf.PT_NOTE + phdr.p_offset = offset + phdr.p_filesz = filesz - phdrs.append(phdr) + phdrs.append(phdr) - note_align = PAGESIZE - ((offset + filesz) % PAGESIZE) + note_align = PAGESIZE - ((offset + filesz) % PAGESIZE) - if note_align == PAGESIZE: - note_align = 0 + if note_align == PAGESIZE: + note_align = 0 - offset += note_align + offset += note_align - # VMA phdrs + # VMA phdrs - for vma in vmas: - offset += filesz - filesz = vma.filesz - phdr = elf.Elf64_Phdr() - ctypes.memset(ctypes.addressof(phdr), 0, ctypes.sizeof(phdr)) - phdr.p_type = elf.PT_LOAD - phdr.p_align = PAGESIZE - phdr.p_paddr = 0 - phdr.p_offset = offset - phdr.p_vaddr = vma.start - phdr.p_memsz = vma.memsz - phdr.p_filesz = vma.filesz - phdr.p_flags = vma.flags + for vma in vmas: + offset += filesz + filesz = vma.filesz + phdr = elf.Elf64_Phdr() + ctypes.memset(ctypes.addressof(phdr), 0, ctypes.sizeof(phdr)) + phdr.p_type = elf.PT_LOAD + phdr.p_align = PAGESIZE + phdr.p_paddr = 0 + phdr.p_offset = offset + phdr.p_vaddr = vma.start + phdr.p_memsz = vma.memsz + phdr.p_filesz = vma.filesz + phdr.p_flags = vma.flags - phdrs.append(phdr) + phdrs.append(phdr) - return phdrs + return phdrs - def _gen_prpsinfo(self, pid): - """ + def _gen_prpsinfo(self, pid): + """ Generate NT_PRPSINFO note for process pid. """ - pstree = self.pstree[pid] - core = self.cores[pid] - - prpsinfo = elf.elf_prpsinfo() - ctypes.memset(ctypes.addressof(prpsinfo), 0, ctypes.sizeof(prpsinfo)) - - # FIXME TASK_ALIVE means that it is either running or sleeping, need to - # teach criu to distinguish them. - TASK_ALIVE = 0x1 - # XXX A bit of confusion here, as in ps "dead" and "zombie" - # state are two separate states, and we use TASK_DEAD for zombies. - TASK_DEAD = 0x2 - TASK_STOPPED = 0x3 - if core["tc"]["task_state"] == TASK_ALIVE: - prpsinfo.pr_state = 0 - if core["tc"]["task_state"] == TASK_DEAD: - prpsinfo.pr_state = 4 - if core["tc"]["task_state"] == TASK_STOPPED: - prpsinfo.pr_state = 3 - # Don't even ask me why it is so, just borrowed from linux - # source and made pr_state match. - prpsinfo.pr_sname = '.' if prpsinfo.pr_state > 5 else "RSDTZW"[prpsinfo.pr_state] - prpsinfo.pr_zomb = 1 if prpsinfo.pr_state == 4 else 0 - prpsinfo.pr_nice = core["thread_core"]["sched_prio"] if "sched_prio" in core["thread_core"] else 0 - prpsinfo.pr_flag = core["tc"]["flags"] - prpsinfo.pr_uid = core["thread_core"]["creds"]["uid"] - prpsinfo.pr_gid = core["thread_core"]["creds"]["gid"] - prpsinfo.pr_pid = pid - prpsinfo.pr_ppid = pstree["ppid"] - prpsinfo.pr_pgrp = pstree["pgid"] - prpsinfo.pr_sid = pstree["sid"] - prpsinfo.pr_fname = core["tc"]["comm"] - prpsinfo.pr_psargs = self._gen_cmdline(pid) - - nhdr = elf.Elf64_Nhdr() - nhdr.n_namesz = 5 - nhdr.n_descsz = ctypes.sizeof(elf.elf_prpsinfo()) - nhdr.n_type = elf.NT_PRPSINFO - - note = elf_note() - note.data = prpsinfo - note.owner = "CORE" - note.nhdr = nhdr - - return note - - def _gen_prstatus(self, pid, tid): - """ + pstree = self.pstree[pid] + core = self.cores[pid] + + prpsinfo = elf.elf_prpsinfo() + ctypes.memset(ctypes.addressof(prpsinfo), 0, ctypes.sizeof(prpsinfo)) + + # FIXME TASK_ALIVE means that it is either running or sleeping, need to + # teach criu to distinguish them. + TASK_ALIVE = 0x1 + # XXX A bit of confusion here, as in ps "dead" and "zombie" + # state are two separate states, and we use TASK_DEAD for zombies. + TASK_DEAD = 0x2 + TASK_STOPPED = 0x3 + if core["tc"]["task_state"] == TASK_ALIVE: + prpsinfo.pr_state = 0 + if core["tc"]["task_state"] == TASK_DEAD: + prpsinfo.pr_state = 4 + if core["tc"]["task_state"] == TASK_STOPPED: + prpsinfo.pr_state = 3 + # Don't even ask me why it is so, just borrowed from linux + # source and made pr_state match. + prpsinfo.pr_sname = '.' if prpsinfo.pr_state > 5 else "RSDTZW" [ + prpsinfo.pr_state] + prpsinfo.pr_zomb = 1 if prpsinfo.pr_state == 4 else 0 + prpsinfo.pr_nice = core["thread_core"][ + "sched_prio"] if "sched_prio" in core["thread_core"] else 0 + prpsinfo.pr_flag = core["tc"]["flags"] + prpsinfo.pr_uid = core["thread_core"]["creds"]["uid"] + prpsinfo.pr_gid = core["thread_core"]["creds"]["gid"] + prpsinfo.pr_pid = pid + prpsinfo.pr_ppid = pstree["ppid"] + prpsinfo.pr_pgrp = pstree["pgid"] + prpsinfo.pr_sid = pstree["sid"] + prpsinfo.pr_fname = core["tc"]["comm"] + prpsinfo.pr_psargs = self._gen_cmdline(pid) + + nhdr = elf.Elf64_Nhdr() + nhdr.n_namesz = 5 + nhdr.n_descsz = ctypes.sizeof(elf.elf_prpsinfo()) + nhdr.n_type = elf.NT_PRPSINFO + + note = elf_note() + note.data = prpsinfo + note.owner = "CORE" + note.nhdr = nhdr + + return note + + def _gen_prstatus(self, pid, tid): + """ Generate NT_PRSTATUS note for thread tid of process pid. """ - core = self.cores[tid] - regs = core["thread_info"]["gpregs"] - pstree = self.pstree[pid] - - prstatus = elf.elf_prstatus() - - ctypes.memset(ctypes.addressof(prstatus), 0, ctypes.sizeof(prstatus)) - - #FIXME setting only some of the fields for now. Revisit later. - prstatus.pr_pid = tid - prstatus.pr_ppid = pstree["ppid"] - prstatus.pr_pgrp = pstree["pgid"] - prstatus.pr_sid = pstree["sid"] - - prstatus.pr_reg.r15 = regs["r15"] - prstatus.pr_reg.r14 = regs["r14"] - prstatus.pr_reg.r13 = regs["r13"] - prstatus.pr_reg.r12 = regs["r12"] - prstatus.pr_reg.rbp = regs["bp"] - prstatus.pr_reg.rbx = regs["bx"] - prstatus.pr_reg.r11 = regs["r11"] - prstatus.pr_reg.r10 = regs["r10"] - prstatus.pr_reg.r9 = regs["r9"] - prstatus.pr_reg.r8 = regs["r8"] - prstatus.pr_reg.rax = regs["ax"] - prstatus.pr_reg.rcx = regs["cx"] - prstatus.pr_reg.rdx = regs["dx"] - prstatus.pr_reg.rsi = regs["si"] - prstatus.pr_reg.rdi = regs["di"] - prstatus.pr_reg.orig_rax = regs["orig_ax"] - prstatus.pr_reg.rip = regs["ip"] - prstatus.pr_reg.cs = regs["cs"] - prstatus.pr_reg.eflags = regs["flags"] - prstatus.pr_reg.rsp = regs["sp"] - prstatus.pr_reg.ss = regs["ss"] - prstatus.pr_reg.fs_base = regs["fs_base"] - prstatus.pr_reg.gs_base = regs["gs_base"] - prstatus.pr_reg.ds = regs["ds"] - prstatus.pr_reg.es = regs["es"] - prstatus.pr_reg.fs = regs["fs"] - prstatus.pr_reg.gs = regs["gs"] - - nhdr = elf.Elf64_Nhdr() - nhdr.n_namesz = 5 - nhdr.n_descsz = ctypes.sizeof(elf.elf_prstatus()) - nhdr.n_type = elf.NT_PRSTATUS - - note = elf_note() - note.data = prstatus - note.owner = "CORE" - note.nhdr = nhdr - - return note - - def _gen_fpregset(self, pid, tid): - """ + core = self.cores[tid] + regs = core["thread_info"]["gpregs"] + pstree = self.pstree[pid] + + prstatus = elf.elf_prstatus() + + ctypes.memset(ctypes.addressof(prstatus), 0, ctypes.sizeof(prstatus)) + + #FIXME setting only some of the fields for now. Revisit later. + prstatus.pr_pid = tid + prstatus.pr_ppid = pstree["ppid"] + prstatus.pr_pgrp = pstree["pgid"] + prstatus.pr_sid = pstree["sid"] + + prstatus.pr_reg.r15 = regs["r15"] + prstatus.pr_reg.r14 = regs["r14"] + prstatus.pr_reg.r13 = regs["r13"] + prstatus.pr_reg.r12 = regs["r12"] + prstatus.pr_reg.rbp = regs["bp"] + prstatus.pr_reg.rbx = regs["bx"] + prstatus.pr_reg.r11 = regs["r11"] + prstatus.pr_reg.r10 = regs["r10"] + prstatus.pr_reg.r9 = regs["r9"] + prstatus.pr_reg.r8 = regs["r8"] + prstatus.pr_reg.rax = regs["ax"] + prstatus.pr_reg.rcx = regs["cx"] + prstatus.pr_reg.rdx = regs["dx"] + prstatus.pr_reg.rsi = regs["si"] + prstatus.pr_reg.rdi = regs["di"] + prstatus.pr_reg.orig_rax = regs["orig_ax"] + prstatus.pr_reg.rip = regs["ip"] + prstatus.pr_reg.cs = regs["cs"] + prstatus.pr_reg.eflags = regs["flags"] + prstatus.pr_reg.rsp = regs["sp"] + prstatus.pr_reg.ss = regs["ss"] + prstatus.pr_reg.fs_base = regs["fs_base"] + prstatus.pr_reg.gs_base = regs["gs_base"] + prstatus.pr_reg.ds = regs["ds"] + prstatus.pr_reg.es = regs["es"] + prstatus.pr_reg.fs = regs["fs"] + prstatus.pr_reg.gs = regs["gs"] + + nhdr = elf.Elf64_Nhdr() + nhdr.n_namesz = 5 + nhdr.n_descsz = ctypes.sizeof(elf.elf_prstatus()) + nhdr.n_type = elf.NT_PRSTATUS + + note = elf_note() + note.data = prstatus + note.owner = "CORE" + note.nhdr = nhdr + + return note + + def _gen_fpregset(self, pid, tid): + """ Generate NT_FPREGSET note for thread tid of process pid. """ - core = self.cores[tid] - regs = core["thread_info"]["fpregs"] - - fpregset = elf.elf_fpregset_t() - ctypes.memset(ctypes.addressof(fpregset), 0, ctypes.sizeof(fpregset)) - - fpregset.cwd = regs["cwd"] - fpregset.swd = regs["swd"] - fpregset.ftw = regs["twd"] - fpregset.fop = regs["fop"] - fpregset.rip = regs["rip"] - fpregset.rdp = regs["rdp"] - fpregset.mxcsr = regs["mxcsr"] - fpregset.mxcr_mask = regs["mxcsr_mask"] - fpregset.st_space = (ctypes.c_uint * len(regs["st_space"]))(*regs["st_space"]) - fpregset.xmm_space = (ctypes.c_uint * len(regs["xmm_space"]))(*regs["xmm_space"]) - #fpregset.padding = regs["padding"] unused - - nhdr = elf.Elf64_Nhdr() - nhdr.n_namesz = 5 - nhdr.n_descsz = ctypes.sizeof(elf.elf_fpregset_t()) - nhdr.n_type = elf.NT_FPREGSET - - note = elf_note() - note.data = fpregset - note.owner = "CORE" - note.nhdr = nhdr - - return note - - def _gen_x86_xstate(self, pid, tid): - """ + core = self.cores[tid] + regs = core["thread_info"]["fpregs"] + + fpregset = elf.elf_fpregset_t() + ctypes.memset(ctypes.addressof(fpregset), 0, ctypes.sizeof(fpregset)) + + fpregset.cwd = regs["cwd"] + fpregset.swd = regs["swd"] + fpregset.ftw = regs["twd"] + fpregset.fop = regs["fop"] + fpregset.rip = regs["rip"] + fpregset.rdp = regs["rdp"] + fpregset.mxcsr = regs["mxcsr"] + fpregset.mxcr_mask = regs["mxcsr_mask"] + fpregset.st_space = (ctypes.c_uint * len(regs["st_space"]))( + *regs["st_space"]) + fpregset.xmm_space = (ctypes.c_uint * len(regs["xmm_space"]))( + *regs["xmm_space"]) + #fpregset.padding = regs["padding"] unused + + nhdr = elf.Elf64_Nhdr() + nhdr.n_namesz = 5 + nhdr.n_descsz = ctypes.sizeof(elf.elf_fpregset_t()) + nhdr.n_type = elf.NT_FPREGSET + + note = elf_note() + note.data = fpregset + note.owner = "CORE" + note.nhdr = nhdr + + return note + + def _gen_x86_xstate(self, pid, tid): + """ Generate NT_X86_XSTATE note for thread tid of process pid. """ - core = self.cores[tid] - fpregs = core["thread_info"]["fpregs"] - - data = elf.elf_xsave_struct() - ctypes.memset(ctypes.addressof(data), 0, ctypes.sizeof(data)) - - data.i387.cwd = fpregs["cwd"] - data.i387.swd = fpregs["swd"] - data.i387.twd = fpregs["twd"] - data.i387.fop = fpregs["fop"] - data.i387.rip = fpregs["rip"] - data.i387.rdp = fpregs["rdp"] - data.i387.mxcsr = fpregs["mxcsr"] - data.i387.mxcsr_mask = fpregs["mxcsr_mask"] - data.i387.st_space = (ctypes.c_uint * len(fpregs["st_space"]))(*fpregs["st_space"]) - data.i387.xmm_space = (ctypes.c_uint * len(fpregs["xmm_space"]))(*fpregs["xmm_space"]) - - if "xsave" in fpregs: - data.xsave_hdr.xstate_bv = fpregs["xsave"]["xstate_bv"] - data.ymmh.ymmh_space = (ctypes.c_uint * len(fpregs["xsave"]["ymmh_space"]))(*fpregs["xsave"]["ymmh_space"]) - - nhdr = elf.Elf64_Nhdr() - nhdr.n_namesz = 6 - nhdr.n_descsz = ctypes.sizeof(data) - nhdr.n_type = elf.NT_X86_XSTATE - - note = elf_note() - note.data = data - note.owner = "LINUX" - note.nhdr = nhdr - - return note - - def _gen_siginfo(self, pid, tid): - """ + core = self.cores[tid] + fpregs = core["thread_info"]["fpregs"] + + data = elf.elf_xsave_struct() + ctypes.memset(ctypes.addressof(data), 0, ctypes.sizeof(data)) + + data.i387.cwd = fpregs["cwd"] + data.i387.swd = fpregs["swd"] + data.i387.twd = fpregs["twd"] + data.i387.fop = fpregs["fop"] + data.i387.rip = fpregs["rip"] + data.i387.rdp = fpregs["rdp"] + data.i387.mxcsr = fpregs["mxcsr"] + data.i387.mxcsr_mask = fpregs["mxcsr_mask"] + data.i387.st_space = (ctypes.c_uint * len(fpregs["st_space"]))( + *fpregs["st_space"]) + data.i387.xmm_space = (ctypes.c_uint * len(fpregs["xmm_space"]))( + *fpregs["xmm_space"]) + + if "xsave" in fpregs: + data.xsave_hdr.xstate_bv = fpregs["xsave"]["xstate_bv"] + data.ymmh.ymmh_space = (ctypes.c_uint * + len(fpregs["xsave"]["ymmh_space"]))( + *fpregs["xsave"]["ymmh_space"]) + + nhdr = elf.Elf64_Nhdr() + nhdr.n_namesz = 6 + nhdr.n_descsz = ctypes.sizeof(data) + nhdr.n_type = elf.NT_X86_XSTATE + + note = elf_note() + note.data = data + note.owner = "LINUX" + note.nhdr = nhdr + + return note + + def _gen_siginfo(self, pid, tid): + """ Generate NT_SIGINFO note for thread tid of process pid. """ - siginfo = elf.siginfo_t() - # FIXME zeroify everything for now - ctypes.memset(ctypes.addressof(siginfo), 0, ctypes.sizeof(siginfo)) + siginfo = elf.siginfo_t() + # FIXME zeroify everything for now + ctypes.memset(ctypes.addressof(siginfo), 0, ctypes.sizeof(siginfo)) - nhdr = elf.Elf64_Nhdr() - nhdr.n_namesz = 5 - nhdr.n_descsz = ctypes.sizeof(elf.siginfo_t()) - nhdr.n_type = elf.NT_SIGINFO + nhdr = elf.Elf64_Nhdr() + nhdr.n_namesz = 5 + nhdr.n_descsz = ctypes.sizeof(elf.siginfo_t()) + nhdr.n_type = elf.NT_SIGINFO - note = elf_note() - note.data = siginfo - note.owner = "CORE" - note.nhdr = nhdr + note = elf_note() + note.data = siginfo + note.owner = "CORE" + note.nhdr = nhdr - return note + return note - def _gen_auxv(self, pid): - """ + def _gen_auxv(self, pid): + """ Generate NT_AUXV note for thread tid of process pid. """ - mm = self.mms[pid] - num_auxv = len(mm["mm_saved_auxv"])/2 + mm = self.mms[pid] + num_auxv = len(mm["mm_saved_auxv"]) / 2 - class elf_auxv(ctypes.Structure): - _fields_ = [("auxv", elf.Elf64_auxv_t*num_auxv)] + class elf_auxv(ctypes.Structure): + _fields_ = [("auxv", elf.Elf64_auxv_t * num_auxv)] - auxv = elf_auxv() - for i in range(num_auxv): - auxv.auxv[i].a_type = mm["mm_saved_auxv"][i] - auxv.auxv[i].a_val = mm["mm_saved_auxv"][i+1] + auxv = elf_auxv() + for i in range(num_auxv): + auxv.auxv[i].a_type = mm["mm_saved_auxv"][i] + auxv.auxv[i].a_val = mm["mm_saved_auxv"][i + 1] - nhdr = elf.Elf64_Nhdr() - nhdr.n_namesz = 5 - nhdr.n_descsz = ctypes.sizeof(elf_auxv()) - nhdr.n_type = elf.NT_AUXV + nhdr = elf.Elf64_Nhdr() + nhdr.n_namesz = 5 + nhdr.n_descsz = ctypes.sizeof(elf_auxv()) + nhdr.n_type = elf.NT_AUXV - note = elf_note() - note.data = auxv - note.owner = "CORE" - note.nhdr = nhdr + note = elf_note() + note.data = auxv + note.owner = "CORE" + note.nhdr = nhdr - return note + return note - def _gen_files(self, pid): - """ + def _gen_files(self, pid): + """ Generate NT_FILE note for process pid. """ - mm = self.mms[pid] - - class mmaped_file_info: - start = None - end = None - file_ofs = None - name = None - - infos = [] - for vma in mm["vmas"]: - if vma["shmid"] == 0: - # shmid == 0 means that it is not a file - continue - - shmid = vma["shmid"] - size = vma["end"] - vma["start"] - off = vma["pgoff"]/PAGESIZE - - files = self.reg_files - fname = filter(lambda x: x["id"] == shmid, files)[0]["name"] - - info = mmaped_file_info() - info.start = vma["start"] - info.end = vma["end"] - info.file_ofs = off - info.name = fname - - infos.append(info) - - # /* - # * Format of NT_FILE note: - # * - # * long count -- how many files are mapped - # * long page_size -- units for file_ofs - # * array of [COUNT] elements of - # * long start - # * long end - # * long file_ofs - # * followed by COUNT filenames in ASCII: "FILE1" NUL "FILE2" NUL... - # */ - fields = [] - fields.append(("count", ctypes.c_long)) - fields.append(("page_size", ctypes.c_long)) - for i in range(len(infos)): - fields.append(("start"+str(i), ctypes.c_long)) - fields.append(("end"+str(i), ctypes.c_long)) - fields.append(("file_ofs"+str(i), ctypes.c_long)) - for i in range(len(infos)): - fields.append(("name"+str(i), ctypes.c_char*(len(infos[i].name)+1))) - - class elf_files(ctypes.Structure): - _fields_ = fields - - data = elf_files() - data.count = len(infos) - data.page_size = PAGESIZE - for i in range(len(infos)): - info = infos[i] - setattr(data, "start"+str(i), info.start) - setattr(data, "end"+str(i), info.end) - setattr(data, "file_ofs"+str(i), info.file_ofs) - setattr(data, "name"+str(i), info.name) - - nhdr = elf.Elf64_Nhdr() - - nhdr.n_namesz = 5#XXX strlen + 1 - nhdr.n_descsz = ctypes.sizeof(elf_files()) - nhdr.n_type = elf.NT_FILE - - note = elf_note() - note.nhdr = nhdr - note.owner = "CORE" - note.data = data - - return note - - def _gen_thread_notes(self, pid, tid): - notes = [] - - notes.append(self._gen_prstatus(pid, tid)) - notes.append(self._gen_fpregset(pid, tid)) - notes.append(self._gen_x86_xstate(pid, tid)) - notes.append(self._gen_siginfo(pid, tid)) - - return notes - - def _gen_notes(self, pid): - """ + mm = self.mms[pid] + + class mmaped_file_info: + start = None + end = None + file_ofs = None + name = None + + infos = [] + for vma in mm["vmas"]: + if vma["shmid"] == 0: + # shmid == 0 means that it is not a file + continue + + shmid = vma["shmid"] + size = vma["end"] - vma["start"] + off = vma["pgoff"] / PAGESIZE + + files = self.reg_files + fname = filter(lambda x: x["id"] == shmid, files)[0]["name"] + + info = mmaped_file_info() + info.start = vma["start"] + info.end = vma["end"] + info.file_ofs = off + info.name = fname + + infos.append(info) + + # /* + # * Format of NT_FILE note: + # * + # * long count -- how many files are mapped + # * long page_size -- units for file_ofs + # * array of [COUNT] elements of + # * long start + # * long end + # * long file_ofs + # * followed by COUNT filenames in ASCII: "FILE1" NUL "FILE2" NUL... + # */ + fields = [] + fields.append(("count", ctypes.c_long)) + fields.append(("page_size", ctypes.c_long)) + for i in range(len(infos)): + fields.append(("start" + str(i), ctypes.c_long)) + fields.append(("end" + str(i), ctypes.c_long)) + fields.append(("file_ofs" + str(i), ctypes.c_long)) + for i in range(len(infos)): + fields.append( + ("name" + str(i), ctypes.c_char * (len(infos[i].name) + 1))) + + class elf_files(ctypes.Structure): + _fields_ = fields + + data = elf_files() + data.count = len(infos) + data.page_size = PAGESIZE + for i in range(len(infos)): + info = infos[i] + setattr(data, "start" + str(i), info.start) + setattr(data, "end" + str(i), info.end) + setattr(data, "file_ofs" + str(i), info.file_ofs) + setattr(data, "name" + str(i), info.name) + + nhdr = elf.Elf64_Nhdr() + + nhdr.n_namesz = 5 #XXX strlen + 1 + nhdr.n_descsz = ctypes.sizeof(elf_files()) + nhdr.n_type = elf.NT_FILE + + note = elf_note() + note.nhdr = nhdr + note.owner = "CORE" + note.data = data + + return note + + def _gen_thread_notes(self, pid, tid): + notes = [] + + notes.append(self._gen_prstatus(pid, tid)) + notes.append(self._gen_fpregset(pid, tid)) + notes.append(self._gen_x86_xstate(pid, tid)) + notes.append(self._gen_siginfo(pid, tid)) + + return notes + + def _gen_notes(self, pid): + """ Generate notes for core dump of process pid. """ - notes = [] + notes = [] - notes.append(self._gen_prpsinfo(pid)) + notes.append(self._gen_prpsinfo(pid)) - threads = self.pstree[pid]["threads"] + threads = self.pstree[pid]["threads"] - # Main thread first - notes += self._gen_thread_notes(pid, pid) + # Main thread first + notes += self._gen_thread_notes(pid, pid) - # Then other threads - for tid in threads: - if tid == pid: - continue + # Then other threads + for tid in threads: + if tid == pid: + continue - notes += self._gen_thread_notes(pid, tid) + notes += self._gen_thread_notes(pid, tid) - notes.append(self._gen_auxv(pid)) - notes.append(self._gen_files(pid)) + notes.append(self._gen_auxv(pid)) + notes.append(self._gen_files(pid)) - return notes + return notes - def _get_page(self, pid, page_no): - """ + def _get_page(self, pid, page_no): + """ Try to find memory page page_no in pages.img image for process pid. """ - pagemap = self.pagemaps[pid] - - # First entry is pagemap_head, we will need it later to open - # proper pages.img. - pages_id = pagemap[0]["pages_id"] - off = 0# in pages - for m in pagemap[1:]: - found = False - for i in range(m["nr_pages"]): - if m["vaddr"] + i*PAGESIZE == page_no*PAGESIZE: - found = True - break - off += 1 - - if not found: - continue - - if "in_parent" in m and m["in_parent"] == True: - ppid = self.pstree[pid]["ppid"] - return self._get_page(ppid, page_no) - else: - with open(self._imgs_dir+"/"+"pages-"+str(pages_id)+".img") as f: - f.seek(off*PAGESIZE) - return f.read(PAGESIZE) - - return None - - def _gen_mem_chunk(self, pid, vma, size): - """ + pagemap = self.pagemaps[pid] + + # First entry is pagemap_head, we will need it later to open + # proper pages.img. + pages_id = pagemap[0]["pages_id"] + off = 0 # in pages + for m in pagemap[1:]: + found = False + for i in range(m["nr_pages"]): + if m["vaddr"] + i * PAGESIZE == page_no * PAGESIZE: + found = True + break + off += 1 + + if not found: + continue + + if "in_parent" in m and m["in_parent"] == True: + ppid = self.pstree[pid]["ppid"] + return self._get_page(ppid, page_no) + else: + with open(self._imgs_dir + "/" + "pages-" + str(pages_id) + + ".img") as f: + f.seek(off * PAGESIZE) + return f.read(PAGESIZE) + + return None + + def _gen_mem_chunk(self, pid, vma, size): + """ Obtain vma contents for process pid. """ - f = None - - if size == 0: - return "" - - if vma["status"] & status["VMA_AREA_VVAR"]: - #FIXME this is what gdb does, as vvar vma - # is not readable from userspace? - return "\0"*size - elif vma["status"] & status["VMA_AREA_VSYSCALL"]: - #FIXME need to dump it with criu or read from - # current process. - return "\0"*size - - if vma["status"] & status["VMA_FILE_SHARED"] or \ - vma["status"] & status["VMA_FILE_PRIVATE"]: - # Open file before iterating vma pages - shmid = vma["shmid"] - off = vma["pgoff"] - - files = self.reg_files - fname = filter(lambda x: x["id"] == shmid, files)[0]["name"] - - f = open(fname) - f.seek(off) - - start = vma["start"] - end = vma["start"] + size - - # Split requested memory chunk into pages, so it could be - # pictured as: - # - # "----" -- part of page with memory outside of our vma; - # "XXXX" -- memory from our vma; - # - # Start page Pages in the middle End page - # [-----XXXXX]...[XXXXXXXXXX][XXXXXXXXXX]...[XXX-------] - # - # Each page could be found in pages.img or in a standalone - # file described by shmid field in vma entry and - # corresponding entry in reg-files.img. - # For VMA_FILE_PRIVATE vma, unchanged pages are taken from - # a file, and changed ones -- from pages.img. - # Finally, if no page is found neither in pages.img nor - # in file, hole in inserted -- a page filled with zeroes. - start_page = start/PAGESIZE - end_page = end/PAGESIZE - - buf = "" - for page_no in range(start_page, end_page+1): - page = None - - # Search for needed page in pages.img and reg-files.img - # and choose appropriate. - page_mem = self._get_page(pid, page_no) - - if f != None: - page = f.read(PAGESIZE) - - if page_mem != None: - # Page from pages.img has higher priority - # than one from maped file on disk. - page = page_mem - - if page == None: - # Hole - page = PAGESIZE*"\0" - - # If it is a start or end page, we need to read - # only part of it. - if page_no == start_page: - n_skip = start - page_no*PAGESIZE - if start_page == end_page: - n_read = size - else: - n_read = PAGESIZE - n_skip - elif page_no == end_page: - n_skip = 0 - n_read = end - page_no*PAGESIZE - else: - n_skip = 0 - n_read = PAGESIZE - - buf += page[n_skip : n_skip + n_read] - - # Don't forget to close file. - if f != None: - f.close() - - return buf - - def _gen_cmdline(self, pid): - """ + f = None + + if size == 0: + return "" + + if vma["status"] & status["VMA_AREA_VVAR"]: + #FIXME this is what gdb does, as vvar vma + # is not readable from userspace? + return "\0" * size + elif vma["status"] & status["VMA_AREA_VSYSCALL"]: + #FIXME need to dump it with criu or read from + # current process. + return "\0" * size + + if vma["status"] & status["VMA_FILE_SHARED"] or \ + vma["status"] & status["VMA_FILE_PRIVATE"]: + # Open file before iterating vma pages + shmid = vma["shmid"] + off = vma["pgoff"] + + files = self.reg_files + fname = filter(lambda x: x["id"] == shmid, files)[0]["name"] + + f = open(fname) + f.seek(off) + + start = vma["start"] + end = vma["start"] + size + + # Split requested memory chunk into pages, so it could be + # pictured as: + # + # "----" -- part of page with memory outside of our vma; + # "XXXX" -- memory from our vma; + # + # Start page Pages in the middle End page + # [-----XXXXX]...[XXXXXXXXXX][XXXXXXXXXX]...[XXX-------] + # + # Each page could be found in pages.img or in a standalone + # file described by shmid field in vma entry and + # corresponding entry in reg-files.img. + # For VMA_FILE_PRIVATE vma, unchanged pages are taken from + # a file, and changed ones -- from pages.img. + # Finally, if no page is found neither in pages.img nor + # in file, hole in inserted -- a page filled with zeroes. + start_page = start / PAGESIZE + end_page = end / PAGESIZE + + buf = "" + for page_no in range(start_page, end_page + 1): + page = None + + # Search for needed page in pages.img and reg-files.img + # and choose appropriate. + page_mem = self._get_page(pid, page_no) + + if f != None: + page = f.read(PAGESIZE) + + if page_mem != None: + # Page from pages.img has higher priority + # than one from maped file on disk. + page = page_mem + + if page == None: + # Hole + page = PAGESIZE * "\0" + + # If it is a start or end page, we need to read + # only part of it. + if page_no == start_page: + n_skip = start - page_no * PAGESIZE + if start_page == end_page: + n_read = size + else: + n_read = PAGESIZE - n_skip + elif page_no == end_page: + n_skip = 0 + n_read = end - page_no * PAGESIZE + else: + n_skip = 0 + n_read = PAGESIZE + + buf += page[n_skip:n_skip + n_read] + + # Don't forget to close file. + if f != None: + f.close() + + return buf + + def _gen_cmdline(self, pid): + """ Generate full command with arguments. """ - mm = self.mms[pid] + mm = self.mms[pid] - vma = {} - vma["start"] = mm["mm_arg_start"] - vma["end"] = mm["mm_arg_end"] - # Dummy flags and status. - vma["flags"] = 0 - vma["status"] = 0 - size = vma["end"] - vma["start"] + vma = {} + vma["start"] = mm["mm_arg_start"] + vma["end"] = mm["mm_arg_end"] + # Dummy flags and status. + vma["flags"] = 0 + vma["status"] = 0 + size = vma["end"] - vma["start"] - chunk = self._gen_mem_chunk(pid, vma, size) + chunk = self._gen_mem_chunk(pid, vma, size) - # Replace all '\0's with spaces. - return chunk.replace('\0', ' ') + # Replace all '\0's with spaces. + return chunk.replace('\0', ' ') - def _get_vma_dump_size(self, vma): - """ + def _get_vma_dump_size(self, vma): + """ Calculate amount of vma to put into core dump. """ - if vma["status"] & status["VMA_AREA_VVAR"] or \ - vma["status"] & status["VMA_AREA_VSYSCALL"] or \ - vma["status"] & status["VMA_AREA_VDSO"]: - size = vma["end"] - vma["start"] - elif vma["prot"] == 0: - size = 0 - elif vma["prot"] & prot["PROT_READ"] and \ - vma["prot"] & prot["PROT_EXEC"]: - size = PAGESIZE - elif vma["status"] & status["VMA_ANON_SHARED"] or \ - vma["status"] & status["VMA_FILE_SHARED"] or \ - vma["status"] & status["VMA_ANON_PRIVATE"] or \ - vma["status"] & status["VMA_FILE_PRIVATE"]: - size = vma["end"] - vma["start"] - else: - size = 0 - - return size - - def _get_vma_flags(self, vma): - """ + if vma["status"] & status["VMA_AREA_VVAR"] or \ + vma["status"] & status["VMA_AREA_VSYSCALL"] or \ + vma["status"] & status["VMA_AREA_VDSO"]: + size = vma["end"] - vma["start"] + elif vma["prot"] == 0: + size = 0 + elif vma["prot"] & prot["PROT_READ"] and \ + vma["prot"] & prot["PROT_EXEC"]: + size = PAGESIZE + elif vma["status"] & status["VMA_ANON_SHARED"] or \ + vma["status"] & status["VMA_FILE_SHARED"] or \ + vma["status"] & status["VMA_ANON_PRIVATE"] or \ + vma["status"] & status["VMA_FILE_PRIVATE"]: + size = vma["end"] - vma["start"] + else: + size = 0 + + return size + + def _get_vma_flags(self, vma): + """ Convert vma flags int elf flags. """ - flags = 0 + flags = 0 - if vma['prot'] & prot["PROT_READ"]: - flags = flags | elf.PF_R + if vma['prot'] & prot["PROT_READ"]: + flags = flags | elf.PF_R - if vma['prot'] & prot["PROT_WRITE"]: - flags = flags | elf.PF_W + if vma['prot'] & prot["PROT_WRITE"]: + flags = flags | elf.PF_W - if vma['prot'] & prot["PROT_EXEC"]: - flags = flags | elf.PF_X + if vma['prot'] & prot["PROT_EXEC"]: + flags = flags | elf.PF_X - return flags + return flags - def _gen_vmas(self, pid): - """ + def _gen_vmas(self, pid): + """ Generate vma contents for core dump for process pid. """ - mm = self.mms[pid] + mm = self.mms[pid] - class vma_class: - data = None - filesz = None - memsz = None - flags = None - start = None + class vma_class: + data = None + filesz = None + memsz = None + flags = None + start = None - vmas = [] - for vma in mm["vmas"]: - size = self._get_vma_dump_size(vma) + vmas = [] + for vma in mm["vmas"]: + size = self._get_vma_dump_size(vma) - chunk = self._gen_mem_chunk(pid, vma, size) + chunk = self._gen_mem_chunk(pid, vma, size) - v = vma_class() - v.filesz = self._get_vma_dump_size(vma) - v.data = self._gen_mem_chunk(pid, vma, v.filesz) - v.memsz = vma["end"] - vma["start"] - v.start = vma["start"] - v.flags = self._get_vma_flags(vma) + v = vma_class() + v.filesz = self._get_vma_dump_size(vma) + v.data = self._gen_mem_chunk(pid, vma, v.filesz) + v.memsz = vma["end"] - vma["start"] + v.start = vma["start"] + v.flags = self._get_vma_flags(vma) - vmas.append(v) + vmas.append(v) - return vmas + return vmas diff --git a/coredump/criu_coredump/elf.py b/coredump/criu_coredump/elf.py index 1da06a6fd..65da583c3 100644 --- a/coredump/criu_coredump/elf.py +++ b/coredump/criu_coredump/elf.py @@ -1,526 +1,685 @@ # Define structures and constants for generating elf file. import ctypes -Elf64_Half = ctypes.c_uint16 # typedef uint16_t Elf64_Half; -Elf64_Word = ctypes.c_uint32 # typedef uint32_t Elf64_Word; -Elf64_Addr = ctypes.c_uint64 # typedef uint64_t Elf64_Addr; -Elf64_Off = ctypes.c_uint64 # typedef uint64_t Elf64_Off; -Elf64_Xword = ctypes.c_uint64 # typedef uint64_t Elf64_Xword; +Elf64_Half = ctypes.c_uint16 # typedef uint16_t Elf64_Half; +Elf64_Word = ctypes.c_uint32 # typedef uint32_t Elf64_Word; +Elf64_Addr = ctypes.c_uint64 # typedef uint64_t Elf64_Addr; +Elf64_Off = ctypes.c_uint64 # typedef uint64_t Elf64_Off; +Elf64_Xword = ctypes.c_uint64 # typedef uint64_t Elf64_Xword; # Elf64_Ehdr related constants. # e_ident size. -EI_NIDENT = 16 # #define EI_NIDENT (16) +EI_NIDENT = 16 # #define EI_NIDENT (16) -EI_MAG0 = 0 # #define EI_MAG0 0 /* File identification byte 0 index */ -ELFMAG0 = 0x7f # #define ELFMAG0 0x7f /* Magic number byte 0 */ +EI_MAG0 = 0 # #define EI_MAG0 0 /* File identification byte 0 index */ +ELFMAG0 = 0x7f # #define ELFMAG0 0x7f /* Magic number byte 0 */ -EI_MAG1 = 1 # #define EI_MAG1 1 /* File identification byte 1 index */ -ELFMAG1 = ord('E') # #define ELFMAG1 'E' /* Magic number byte 1 */ +EI_MAG1 = 1 # #define EI_MAG1 1 /* File identification byte 1 index */ +ELFMAG1 = ord( + 'E') # #define ELFMAG1 'E' /* Magic number byte 1 */ -EI_MAG2 = 2 # #define EI_MAG2 2 /* File identification byte 2 index */ -ELFMAG2 = ord('L') # #define ELFMAG2 'L' /* Magic number byte 2 */ +EI_MAG2 = 2 # #define EI_MAG2 2 /* File identification byte 2 index */ +ELFMAG2 = ord( + 'L') # #define ELFMAG2 'L' /* Magic number byte 2 */ -EI_MAG3 = 3 # #define EI_MAG3 3 /* File identification byte 3 index */ -ELFMAG3 = ord('F') # #define ELFMAG3 'F' /* Magic number byte 3 */ +EI_MAG3 = 3 # #define EI_MAG3 3 /* File identification byte 3 index */ +ELFMAG3 = ord( + 'F') # #define ELFMAG3 'F' /* Magic number byte 3 */ -EI_CLASS = 4 # #define EI_CLASS 4 /* File class byte index */ +EI_CLASS = 4 # #define EI_CLASS 4 /* File class byte index */ -EI_DATA = 5 # #define EI_DATA 5 /* Data encoding byte index */ +EI_DATA = 5 # #define EI_DATA 5 /* Data encoding byte index */ -EI_VERSION = 6 # #define EI_VERSION 6 /* File version byte index */ +EI_VERSION = 6 # #define EI_VERSION 6 /* File version byte index */ -ELFDATA2LSB = 1 # #define ELFDATA2LSB 1 /* 2's complement, little endian */ +ELFDATA2LSB = 1 # #define ELFDATA2LSB 1 /* 2's complement, little endian */ -ELFCLASS64 = 2 # #define ELFCLASS64 2 /* 64-bit objects */ +ELFCLASS64 = 2 # #define ELFCLASS64 2 /* 64-bit objects */ # Legal values for e_type (object file type). -ET_CORE = 4 # #define ET_CORE 4 /* Core file */ +ET_CORE = 4 # #define ET_CORE 4 /* Core file */ # Legal values for e_machine (architecture). -EM_X86_64 = 62 # #define EM_X86_64 62 /* AMD x86-64 architecture */ +EM_X86_64 = 62 # #define EM_X86_64 62 /* AMD x86-64 architecture */ # Legal values for e_version (version). -EV_CURRENT = 1 # #define EV_CURRENT 1 /* Current version */ - -class Elf64_Ehdr(ctypes.Structure): # typedef struct - _fields_ = [ # { - ("e_ident", ctypes.c_ubyte*EI_NIDENT), # unsigned char e_ident[EI_NIDENT]; - ("e_type", Elf64_Half), # Elf64_Half e_type; - ("e_machine", Elf64_Half), # Elf64_Half e_machine; - ("e_version", Elf64_Word), # Elf64_Word e_version; - ("e_entry", Elf64_Addr), # Elf64_Addr e_entry; - ("e_phoff", Elf64_Off), # Elf64_Off e_phoff; - ("e_shoff", Elf64_Off), # Elf64_Off e_shoff; - ("e_flags", Elf64_Word), # Elf64_Word e_flags; - ("e_ehsize", Elf64_Half), # Elf64_Half e_ehsize; - ("e_phentsize", Elf64_Half), # Elf64_Half e_phentsize; - ("e_phnum", Elf64_Half), # Elf64_Half e_phnum; - ("e_shentsize", Elf64_Half), # Elf64_Half e_shentsize; - ("e_shnum", Elf64_Half), # Elf64_Half e_shnum; - ("e_shstrndx", Elf64_Half) # Elf64_Half e_shstrndx; - ] # } Elf64_Ehdr; +EV_CURRENT = 1 # #define EV_CURRENT 1 /* Current version */ + + +class Elf64_Ehdr(ctypes.Structure): # typedef struct + _fields_ = [ # { + ("e_ident", + ctypes.c_ubyte * EI_NIDENT), # unsigned char e_ident[EI_NIDENT]; + ("e_type", Elf64_Half), # Elf64_Half e_type; + ("e_machine", Elf64_Half), # Elf64_Half e_machine; + ("e_version", Elf64_Word), # Elf64_Word e_version; + ("e_entry", Elf64_Addr), # Elf64_Addr e_entry; + ("e_phoff", Elf64_Off), # Elf64_Off e_phoff; + ("e_shoff", Elf64_Off), # Elf64_Off e_shoff; + ("e_flags", Elf64_Word), # Elf64_Word e_flags; + ("e_ehsize", Elf64_Half), # Elf64_Half e_ehsize; + ("e_phentsize", Elf64_Half), # Elf64_Half e_phentsize; + ("e_phnum", Elf64_Half), # Elf64_Half e_phnum; + ("e_shentsize", Elf64_Half), # Elf64_Half e_shentsize; + ("e_shnum", Elf64_Half), # Elf64_Half e_shnum; + ("e_shstrndx", Elf64_Half) # Elf64_Half e_shstrndx; + ] # } Elf64_Ehdr; # Elf64_Phdr related constants. # Legal values for p_type (segment type). -PT_LOAD = 1 # #define PT_LOAD 1 /* Loadable program segment */ -PT_NOTE = 4 # #define PT_NOTE 4 /* Auxiliary information */ +PT_LOAD = 1 # #define PT_LOAD 1 /* Loadable program segment */ +PT_NOTE = 4 # #define PT_NOTE 4 /* Auxiliary information */ # Legal values for p_flags (segment flags). -PF_X = 1 # #define PF_X (1 << 0) /* Segment is executable */ -PF_W = 1 << 1 # #define PF_W (1 << 1) /* Segment is writable */ -PF_R = 1 << 2 # #define PF_R (1 << 2) /* Segment is readable */ - -class Elf64_Phdr(ctypes.Structure): # typedef struct - _fields_ = [ # { - ("p_type", Elf64_Word), # Elf64_Word p_type; - ("p_flags", Elf64_Word), # Elf64_Word p_flags; - ("p_offset", Elf64_Off), # Elf64_Off p_offset; - ("p_vaddr", Elf64_Addr), # Elf64_Addr p_vaddr; - ("p_paddr", Elf64_Addr), # Elf64_Addr p_paddr; - ("p_filesz", Elf64_Xword), # Elf64_Xword p_filesz; - ("p_memsz", Elf64_Xword), # Elf64_Xword p_memsz; - ("p_align", Elf64_Xword), # Elf64_Xword p_align; - ] # } Elf64_Phdr; +PF_X = 1 # #define PF_X (1 << 0) /* Segment is executable */ +PF_W = 1 << 1 # #define PF_W (1 << 1) /* Segment is writable */ +PF_R = 1 << 2 # #define PF_R (1 << 2) /* Segment is readable */ + + +class Elf64_Phdr(ctypes.Structure): # typedef struct + _fields_ = [ # { + ("p_type", Elf64_Word), # Elf64_Word p_type; + ("p_flags", Elf64_Word), # Elf64_Word p_flags; + ("p_offset", Elf64_Off), # Elf64_Off p_offset; + ("p_vaddr", Elf64_Addr), # Elf64_Addr p_vaddr; + ("p_paddr", Elf64_Addr), # Elf64_Addr p_paddr; + ("p_filesz", Elf64_Xword), # Elf64_Xword p_filesz; + ("p_memsz", Elf64_Xword), # Elf64_Xword p_memsz; + ("p_align", Elf64_Xword), # Elf64_Xword p_align; + ] # } Elf64_Phdr; # Elf64_auxv_t related constants. + class _Elf64_auxv_t_U(ctypes.Union): - _fields_ = [ - ("a_val", ctypes.c_uint64) - ] - -class Elf64_auxv_t(ctypes.Structure): # typedef struct - _fields_ = [ # { - ("a_type", ctypes.c_uint64), # uint64_t a_type; /* Entry type */ - ("a_un", _Elf64_auxv_t_U) # union - # { - # uint64_t a_val; /* Integer value */ - # /* We use to have pointer elements added here. We cannot do that, - # though, since it does not work when using 32-bit definitions - # on 64-bit platforms and vice versa. */ - # } a_un; - ] # } Elf64_auxv_t; + _fields_ = [("a_val", ctypes.c_uint64)] + + +class Elf64_auxv_t(ctypes.Structure): # typedef struct + _fields_ = [ # { + ("a_type", + ctypes.c_uint64), # uint64_t a_type; /* Entry type */ + ("a_un", _Elf64_auxv_t_U) # union + # { + # uint64_t a_val; /* Integer value */ + # /* We use to have pointer elements added here. We cannot do that, + # though, since it does not work when using 32-bit definitions + # on 64-bit platforms and vice versa. */ + # } a_un; + ] # } Elf64_auxv_t; # Elf64_Nhdr related constants. -NT_PRSTATUS = 1 # #define NT_PRSTATUS 1 /* Contains copy of prstatus struct */ -NT_FPREGSET = 2 # #define NT_FPREGSET 2 /* Contains copy of fpregset struct */ -NT_PRPSINFO = 3 # #define NT_PRPSINFO 3 /* Contains copy of prpsinfo struct */ -NT_AUXV = 6 # #define NT_AUXV 6 /* Contains copy of auxv array */ -NT_SIGINFO = 0x53494749 # #define NT_SIGINFO 0x53494749 /* Contains copy of siginfo_t, +NT_PRSTATUS = 1 # #define NT_PRSTATUS 1 /* Contains copy of prstatus struct */ +NT_FPREGSET = 2 # #define NT_FPREGSET 2 /* Contains copy of fpregset struct */ +NT_PRPSINFO = 3 # #define NT_PRPSINFO 3 /* Contains copy of prpsinfo struct */ +NT_AUXV = 6 # #define NT_AUXV 6 /* Contains copy of auxv array */ +NT_SIGINFO = 0x53494749 # #define NT_SIGINFO 0x53494749 /* Contains copy of siginfo_t, # size might increase */ -NT_FILE = 0x46494c45 # #define NT_FILE 0x46494c45 /* Contains information about mapped +NT_FILE = 0x46494c45 # #define NT_FILE 0x46494c45 /* Contains information about mapped # files */ -NT_X86_XSTATE = 0x202 # #define NT_X86_XSTATE 0x202 /* x86 extended state using xsave */ +NT_X86_XSTATE = 0x202 # #define NT_X86_XSTATE 0x202 /* x86 extended state using xsave */ + -class Elf64_Nhdr(ctypes.Structure): # typedef struct - _fields_ = [ # { - ("n_namesz", Elf64_Word), # Elf64_Word n_namesz; /* Length of the note's name. */ - ("n_descsz", Elf64_Word), # Elf64_Word n_descsz; /* Length of the note's descriptor. */ - ("n_type", Elf64_Word), # Elf64_Word n_type; /* Type of the note. */ - ] # } Elf64_Nhdr; +class Elf64_Nhdr(ctypes.Structure): # typedef struct + _fields_ = [ # { + ( + "n_namesz", Elf64_Word + ), # Elf64_Word n_namesz; /* Length of the note's name. */ + ( + "n_descsz", Elf64_Word + ), # Elf64_Word n_descsz; /* Length of the note's descriptor. */ + ("n_type", Elf64_Word + ), # Elf64_Word n_type; /* Type of the note. */ + ] # } Elf64_Nhdr; # Elf64_Shdr related constants. -class Elf64_Shdr(ctypes.Structure): # typedef struct - _fields_ = [ # { - ("sh_name", Elf64_Word), # Elf64_Word sh_name; /* Section name (string tbl index) */ - ("sh_type", Elf64_Word), # Elf64_Word sh_type; /* Section type */ - ("sh_flags", Elf64_Xword), # Elf64_Xword sh_flags; /* Section flags */ - ("sh_addr", Elf64_Addr), # Elf64_Addr sh_addr; /* Section virtual addr at execution */ - ("sh_offset", Elf64_Off), # Elf64_Off sh_offset; /* Section file offset */ - ("sh_size", Elf64_Xword), # Elf64_Xword sh_size; /* Section size in bytes */ - ("sh_link", Elf64_Word), # Elf64_Word sh_link; /* Link to another section */ - ("sh_info", Elf64_Word), # Elf64_Word sh_info; /* Additional section information */ - ("sh_addralign",Elf64_Xword), # Elf64_Xword sh_addralign; /* Section alignment */ - ("sh_entsize", Elf64_Xword) # Elf64_Xword sh_entsize; /* Entry size if section holds table */ - ] # } Elf64_Shdr; + +class Elf64_Shdr(ctypes.Structure): # typedef struct + _fields_ = [ # { + ( + "sh_name", Elf64_Word + ), # Elf64_Word sh_name; /* Section name (string tbl index) */ + ("sh_type", Elf64_Word + ), # Elf64_Word sh_type; /* Section type */ + ("sh_flags", Elf64_Xword + ), # Elf64_Xword sh_flags; /* Section flags */ + ( + "sh_addr", Elf64_Addr + ), # Elf64_Addr sh_addr; /* Section virtual addr at execution */ + ( + "sh_offset", Elf64_Off + ), # Elf64_Off sh_offset; /* Section file offset */ + ( + "sh_size", Elf64_Xword + ), # Elf64_Xword sh_size; /* Section size in bytes */ + ( + "sh_link", Elf64_Word + ), # Elf64_Word sh_link; /* Link to another section */ + ( + "sh_info", Elf64_Word + ), # Elf64_Word sh_info; /* Additional section information */ + ("sh_addralign", Elf64_Xword + ), # Elf64_Xword sh_addralign; /* Section alignment */ + ( + "sh_entsize", Elf64_Xword + ) # Elf64_Xword sh_entsize; /* Entry size if section holds table */ + ] # } Elf64_Shdr; # elf_prstatus related constants. + # Signal info. -class elf_siginfo(ctypes.Structure): # struct elf_siginfo - _fields_ = [ # { - ("si_signo", ctypes.c_int), # int si_signo; /* Signal number. */ - ("si_code", ctypes.c_int), # int si_code; /* Extra code. */ - ("si_errno", ctypes.c_int) # int si_errno; /* Errno. */ - ] # }; +class elf_siginfo(ctypes.Structure): # struct elf_siginfo + _fields_ = [ # { + ("si_signo", ctypes.c_int + ), # int si_signo; /* Signal number. */ + ("si_code", ctypes.c_int + ), # int si_code; /* Extra code. */ + ("si_errno", ctypes.c_int + ) # int si_errno; /* Errno. */ + ] # }; + # A time value that is accurate to the nearest # microsecond but also has a range of years. -class timeval(ctypes.Structure): # struct timeval - _fields_ = [ # { - ("tv_sec", ctypes.c_long), # __time_t tv_sec; /* Seconds. */ - ("tv_usec", ctypes.c_long) # __suseconds_t tv_usec; /* Microseconds. */ - ] # }; - -class user_regs_struct(ctypes.Structure): # struct user_regs_struct - _fields_ = [ # { - ("r15", ctypes.c_ulonglong), # __extension__ unsigned long long int r15; - ("r14", ctypes.c_ulonglong), # __extension__ unsigned long long int r14; - ("r13", ctypes.c_ulonglong), # __extension__ unsigned long long int r13; - ("r12", ctypes.c_ulonglong), # __extension__ unsigned long long int r12; - ("rbp", ctypes.c_ulonglong), # __extension__ unsigned long long int rbp; - ("rbx", ctypes.c_ulonglong), # __extension__ unsigned long long int rbx; - ("r11", ctypes.c_ulonglong), # __extension__ unsigned long long int r11; - ("r10", ctypes.c_ulonglong), # __extension__ unsigned long long int r10; - ("r9", ctypes.c_ulonglong), # __extension__ unsigned long long int r9; - ("r8", ctypes.c_ulonglong), # __extension__ unsigned long long int r8; - ("rax", ctypes.c_ulonglong), # __extension__ unsigned long long int rax; - ("rcx", ctypes.c_ulonglong), # __extension__ unsigned long long int rcx; - ("rdx", ctypes.c_ulonglong), # __extension__ unsigned long long int rdx; - ("rsi", ctypes.c_ulonglong), # __extension__ unsigned long long int rsi; - ("rdi", ctypes.c_ulonglong), # __extension__ unsigned long long int rdi; - ("orig_rax", ctypes.c_ulonglong), # __extension__ unsigned long long int orig_rax; - ("rip", ctypes.c_ulonglong), # __extension__ unsigned long long int rip; - ("cs", ctypes.c_ulonglong), # __extension__ unsigned long long int cs; - ("eflags", ctypes.c_ulonglong), # __extension__ unsigned long long int eflags; - ("rsp", ctypes.c_ulonglong), # __extension__ unsigned long long int rsp; - ("ss", ctypes.c_ulonglong), # __extension__ unsigned long long int ss; - ("fs_base", ctypes.c_ulonglong), # __extension__ unsigned long long int fs_base; - ("gs_base", ctypes.c_ulonglong), # __extension__ unsigned long long int gs_base; - ("ds", ctypes.c_ulonglong), # __extension__ unsigned long long int ds; - ("es", ctypes.c_ulonglong), # __extension__ unsigned long long int es; - ("fs", ctypes.c_ulonglong), # __extension__ unsigned long long int fs; - ("gs", ctypes.c_ulonglong) # __extension__ unsigned long long int gs; - ] # }; +class timeval(ctypes.Structure): # struct timeval + _fields_ = [ # { + ("tv_sec", + ctypes.c_long), # __time_t tv_sec; /* Seconds. */ + ("tv_usec", ctypes.c_long + ) # __suseconds_t tv_usec; /* Microseconds. */ + ] # }; + + +class user_regs_struct(ctypes.Structure): # struct user_regs_struct + _fields_ = [ # { + ("r15", + ctypes.c_ulonglong), # __extension__ unsigned long long int r15; + ("r14", + ctypes.c_ulonglong), # __extension__ unsigned long long int r14; + ("r13", + ctypes.c_ulonglong), # __extension__ unsigned long long int r13; + ("r12", + ctypes.c_ulonglong), # __extension__ unsigned long long int r12; + ("rbp", + ctypes.c_ulonglong), # __extension__ unsigned long long int rbp; + ("rbx", + ctypes.c_ulonglong), # __extension__ unsigned long long int rbx; + ("r11", + ctypes.c_ulonglong), # __extension__ unsigned long long int r11; + ("r10", + ctypes.c_ulonglong), # __extension__ unsigned long long int r10; + ("r9", + ctypes.c_ulonglong), # __extension__ unsigned long long int r9; + ("r8", + ctypes.c_ulonglong), # __extension__ unsigned long long int r8; + ("rax", + ctypes.c_ulonglong), # __extension__ unsigned long long int rax; + ("rcx", + ctypes.c_ulonglong), # __extension__ unsigned long long int rcx; + ("rdx", + ctypes.c_ulonglong), # __extension__ unsigned long long int rdx; + ("rsi", + ctypes.c_ulonglong), # __extension__ unsigned long long int rsi; + ("rdi", + ctypes.c_ulonglong), # __extension__ unsigned long long int rdi; + ("orig_rax", ctypes.c_ulonglong + ), # __extension__ unsigned long long int orig_rax; + ("rip", + ctypes.c_ulonglong), # __extension__ unsigned long long int rip; + ("cs", + ctypes.c_ulonglong), # __extension__ unsigned long long int cs; + ("eflags", + ctypes.c_ulonglong), # __extension__ unsigned long long int eflags; + ("rsp", + ctypes.c_ulonglong), # __extension__ unsigned long long int rsp; + ("ss", + ctypes.c_ulonglong), # __extension__ unsigned long long int ss; + ("fs_base", ctypes.c_ulonglong + ), # __extension__ unsigned long long int fs_base; + ("gs_base", ctypes.c_ulonglong + ), # __extension__ unsigned long long int gs_base; + ("ds", + ctypes.c_ulonglong), # __extension__ unsigned long long int ds; + ("es", + ctypes.c_ulonglong), # __extension__ unsigned long long int es; + ("fs", + ctypes.c_ulonglong), # __extension__ unsigned long long int fs; + ("gs", ctypes.c_ulonglong + ) # __extension__ unsigned long long int gs; + ] # }; + #elf_greg_t = ctypes.c_ulonglong #ELF_NGREG = ctypes.sizeof(user_regs_struct)/ctypes.sizeof(elf_greg_t) #elf_gregset_t = elf_greg_t*ELF_NGREG elf_gregset_t = user_regs_struct -class elf_prstatus(ctypes.Structure): # struct elf_prstatus - _fields_ = [ # { - ("pr_info", elf_siginfo), # struct elf_siginfo pr_info; /* Info associated with signal. */ - ("pr_cursig", ctypes.c_short), # short int pr_cursig; /* Current signal. */ - ("pr_sigpend", ctypes.c_ulong), # unsigned long int pr_sigpend; /* Set of pending signals. */ - ("pr_sighold", ctypes.c_ulong), # unsigned long int pr_sighold; /* Set of held signals. */ - ("pr_pid", ctypes.c_int), # __pid_t pr_pid; - ("pr_ppid", ctypes.c_int), # __pid_t pr_ppid; - ("pr_pgrp", ctypes.c_int), # __pid_t pr_pgrp; - ("pr_sid", ctypes.c_int), # __pid_t pr_sid; - ("pr_utime", timeval), # struct timeval pr_utime; /* User time. */ - ("pr_stime", timeval), # struct timeval pr_stime; /* System time. */ - ("pr_cutime", timeval), # struct timeval pr_cutime; /* Cumulative user time. */ - ("pr_cstime", timeval), # struct timeval pr_cstime; /* Cumulative system time. */ - ("pr_reg", elf_gregset_t), # elf_gregset_t pr_reg; /* GP registers. */ - ("pr_fpvalid", ctypes.c_int) # int pr_fpvalid; /* True if math copro being used. */ - ] # }; + +class elf_prstatus(ctypes.Structure): # struct elf_prstatus + _fields_ = [ # { + ( + "pr_info", elf_siginfo + ), # struct elf_siginfo pr_info; /* Info associated with signal. */ + ("pr_cursig", ctypes.c_short + ), # short int pr_cursig; /* Current signal. */ + ( + "pr_sigpend", ctypes.c_ulong + ), # unsigned long int pr_sigpend; /* Set of pending signals. */ + ( + "pr_sighold", ctypes.c_ulong + ), # unsigned long int pr_sighold; /* Set of held signals. */ + ("pr_pid", ctypes.c_int), # __pid_t pr_pid; + ("pr_ppid", ctypes.c_int), # __pid_t pr_ppid; + ("pr_pgrp", ctypes.c_int), # __pid_t pr_pgrp; + ("pr_sid", ctypes.c_int), # __pid_t pr_sid; + ("pr_utime", + timeval), # struct timeval pr_utime; /* User time. */ + ("pr_stime", timeval + ), # struct timeval pr_stime; /* System time. */ + ( + "pr_cutime", timeval + ), # struct timeval pr_cutime; /* Cumulative user time. */ + ( + "pr_cstime", timeval + ), # struct timeval pr_cstime; /* Cumulative system time. */ + ("pr_reg", elf_gregset_t + ), # elf_gregset_t pr_reg; /* GP registers. */ + ( + "pr_fpvalid", ctypes.c_int + ) # int pr_fpvalid; /* True if math copro being used. */ + ] # }; # elf_prpsinfo related constants. -ELF_PRARGSZ = 80 # #define ELF_PRARGSZ (80) /* Number of chars for args. */ - -class elf_prpsinfo(ctypes.Structure): # struct elf_prpsinfo - _fields_ = [ # { - ("pr_state", ctypes.c_byte), # char pr_state; /* Numeric process state. */ - ("pr_sname", ctypes.c_char), # char pr_sname; /* Char for pr_state. */ - ("pr_zomb", ctypes.c_byte), # char pr_zomb; /* Zombie. */ - ("pr_nice", ctypes.c_byte), # char pr_nice; /* Nice val. */ - ("pr_flag", ctypes.c_ulong), # unsigned long int pr_flag; /* Flags. */ - # #if __WORDSIZE == 32 - # unsigned short int pr_uid; - # unsigned short int pr_gid; - # #else - ("pr_uid", ctypes.c_uint), # unsigned int pr_uid; - ("pr_gid", ctypes.c_uint), # unsigned int pr_gid; - # #endif - ("pr_pid", ctypes.c_int), # int pr_pid, pr_ppid, pr_pgrp, pr_sid; - ("pr_ppid", ctypes.c_int), - ("pr_pgrp", ctypes.c_int), - ("pr_sid", ctypes.c_int), - # /* Lots missing */ - ("pr_fname", ctypes.c_char*16), # char pr_fname[16]; /* Filename of executable. */ - ("pr_psargs", ctypes.c_char*ELF_PRARGSZ) # char pr_psargs[ELF_PRARGSZ]; /* Initial part of arg list. */ - ] # }; - - -class user_fpregs_struct(ctypes.Structure): # struct user_fpregs_struct - _fields_ = [ # { - ("cwd", ctypes.c_ushort), # unsigned short int cwd; - ("swd", ctypes.c_ushort), # unsigned short int swd; - ("ftw", ctypes.c_ushort), # unsigned short int ftw; - ("fop", ctypes.c_ushort), # unsigned short int fop; - ("rip", ctypes.c_ulonglong), # __extension__ unsigned long long int rip; - ("rdp", ctypes.c_ulonglong), # __extension__ unsigned long long int rdp; - ("mxcsr", ctypes.c_uint), # unsigned int mxcsr; - ("mxcr_mask", ctypes.c_uint), # unsigned int mxcr_mask; - ("st_space", ctypes.c_uint*32), # unsigned int st_space[32]; /* 8*16 bytes for each FP-reg = 128 bytes */ - ("xmm_space", ctypes.c_uint*64), # unsigned int xmm_space[64]; /* 16*16 bytes for each XMM-reg = 256 bytes */ - ("padding", ctypes.c_uint*24), # unsigned int padding[24]; - ] # }; +ELF_PRARGSZ = 80 # #define ELF_PRARGSZ (80) /* Number of chars for args. */ + + +class elf_prpsinfo(ctypes.Structure): # struct elf_prpsinfo + _fields_ = [ # { + ( + "pr_state", ctypes.c_byte + ), # char pr_state; /* Numeric process state. */ + ( + "pr_sname", ctypes.c_char + ), # char pr_sname; /* Char for pr_state. */ + ("pr_zomb", ctypes.c_byte + ), # char pr_zomb; /* Zombie. */ + ("pr_nice", ctypes.c_byte + ), # char pr_nice; /* Nice val. */ + ("pr_flag", ctypes.c_ulong + ), # unsigned long int pr_flag; /* Flags. */ + # #if __WORDSIZE == 32 + # unsigned short int pr_uid; + # unsigned short int pr_gid; + # #else + ("pr_uid", ctypes.c_uint), # unsigned int pr_uid; + ("pr_gid", ctypes.c_uint), # unsigned int pr_gid; + # #endif + ("pr_pid", ctypes.c_int), # int pr_pid, pr_ppid, pr_pgrp, pr_sid; + ("pr_ppid", ctypes.c_int), + ("pr_pgrp", ctypes.c_int), + ("pr_sid", ctypes.c_int), + # /* Lots missing */ + ( + "pr_fname", ctypes.c_char * 16 + ), # char pr_fname[16]; /* Filename of executable. */ + ( + "pr_psargs", ctypes.c_char * ELF_PRARGSZ + ) # char pr_psargs[ELF_PRARGSZ]; /* Initial part of arg list. */ + ] # }; + + +class user_fpregs_struct(ctypes.Structure): # struct user_fpregs_struct + _fields_ = [ # { + ("cwd", ctypes.c_ushort), # unsigned short int cwd; + ("swd", ctypes.c_ushort), # unsigned short int swd; + ("ftw", ctypes.c_ushort), # unsigned short int ftw; + ("fop", ctypes.c_ushort), # unsigned short int fop; + ("rip", + ctypes.c_ulonglong), # __extension__ unsigned long long int rip; + ("rdp", + ctypes.c_ulonglong), # __extension__ unsigned long long int rdp; + ("mxcsr", ctypes.c_uint), # unsigned int mxcsr; + ("mxcr_mask", ctypes.c_uint), # unsigned int mxcr_mask; + ( + "st_space", ctypes.c_uint * 32 + ), # unsigned int st_space[32]; /* 8*16 bytes for each FP-reg = 128 bytes */ + ( + "xmm_space", ctypes.c_uint * 64 + ), # unsigned int xmm_space[64]; /* 16*16 bytes for each XMM-reg = 256 bytes */ + ("padding", + ctypes.c_uint * 24), # unsigned int padding[24]; + ] # }; elf_fpregset_t = user_fpregs_struct - # siginfo_t related constants. -_SI_MAX_SIZE = 128 -_SI_PAD_SIZE = (_SI_MAX_SIZE/ctypes.sizeof(ctypes.c_int)) - 4 +_SI_MAX_SIZE = 128 +_SI_PAD_SIZE = (_SI_MAX_SIZE / ctypes.sizeof(ctypes.c_int)) - 4 - # /* kill(). */ -class _siginfo_t_U_kill(ctypes.Structure): # struct - _fields_ = [ # { - ("si_pid", ctypes.c_int), # __pid_t si_pid; /* Sending process ID. */ - ("si_uid", ctypes.c_uint) # __uid_t si_uid; /* Real user ID of sending process. */ - ] # } _kill; +# /* kill(). */ +class _siginfo_t_U_kill(ctypes.Structure): # struct + _fields_ = [ # { + ("si_pid", ctypes.c_int + ), # __pid_t si_pid; /* Sending process ID. */ + ( + "si_uid", ctypes.c_uint + ) # __uid_t si_uid; /* Real user ID of sending process. */ + ] # } _kill; # Type for data associated with a signal. -class sigval_t(ctypes.Union): # typedef union sigval - _fields_ = [ # { - ("sival_int", ctypes.c_int), # int sival_int; - ("sical_ptr", ctypes.c_void_p), # void *sival_ptr; - ] # } sigval_t; - - # /* POSIX.1b timers. */ -class _siginfo_t_U_timer(ctypes.Structure): # struct - _fields_ = [ # { - ("si_tid", ctypes.c_int), # int si_tid; /* Timer ID. */ - ("si_overrun", ctypes.c_int), # int si_overrun; /* Overrun count. */ - ("si_sigval", sigval_t) # sigval_t si_sigval; /* Signal value. */ - ] # } _timer; - - - # /* POSIX.1b signals. */ -class _siginfo_t_U_rt(ctypes.Structure): # struct - _fields_ = [ # { - ("si_pid", ctypes.c_int), # __pid_t si_pid; /* Sending process ID. */ - ("si_uid", ctypes.c_uint), # __uid_t si_uid; /* Real user ID of sending process. */ - ("si_sigval", sigval_t) # sigval_t si_sigval; /* Signal value. */ - ] # } _rt; - - - # /* SIGCHLD. */ -class _siginfo_t_U_sigchld(ctypes.Structure): # struct - _fields_ = [ # { - ("si_pid", ctypes.c_int), # __pid_t si_pid; /* Which child. */ - ("si_uid", ctypes.c_uint), # __uid_t si_uid; /* Real user ID of sending process. */ - ("si_status", ctypes.c_int), # int si_status; /* Exit value or signal. */ - ("si_utime", ctypes.c_long), # __sigchld_clock_t si_utime; - ("si_stime", ctypes.c_long) # __sigchld_clock_t si_stime; - ] # } _sigchld; - - # /* SIGILL, SIGFPE, SIGSEGV, SIGBUS. */ -class _siginfo_t_U_sigfault(ctypes.Structure): # struct - _fields_ = [ # { - ("si_addr", ctypes.c_void_p), # void *si_addr; /* Faulting insn/memory ref. */ - ("si_addr_lsb", ctypes.c_short) # short int si_addr_lsb; /* Valid LSB of the reported address. */ - ] # } _sigfault; - - # /* SIGPOLL. */ -class _siginfo_t_U_sigpoll(ctypes.Structure): # struct - _fields_ = [ # { - ("si_band", ctypes.c_long), # long int si_band; /* Band event for SIGPOLL. */ - ("si_fd", ctypes.c_int) # int si_fd; - ] # } _sigpoll; - - - # /* SIGSYS. */ -class _siginfo_t_U_sigsys(ctypes.Structure): # struct - _fields_ = [ # { - ("_call_addr", ctypes.c_void_p), # void *_call_addr; /* Calling user insn. */ - ("_syscall", ctypes.c_int), # int _syscall; /* Triggering system call number. */ - ("_arch", ctypes.c_uint) # unsigned int _arch; /* AUDIT_ARCH_* of syscall. */ - ] # } _sigsys; - - -class _siginfo_t_U(ctypes.Union): # union - _fields_ = [ # { - ("_pad", ctypes.c_int*_SI_PAD_SIZE), # int _pad[__SI_PAD_SIZE]; - # - # /* kill(). */ - ("_kill", _siginfo_t_U_kill), # struct - # { - # __pid_t si_pid; /* Sending process ID. */ - # __uid_t si_uid; /* Real user ID of sending process. */ - # } _kill; - # - # /* POSIX.1b timers. */ - ("_timer", _siginfo_t_U_timer), # struct - # { - # int si_tid; /* Timer ID. */ - # int si_overrun; /* Overrun count. */ - # sigval_t si_sigval; /* Signal value. */ - # } _timer; - # - # /* POSIX.1b signals. */ - ("_rt", _siginfo_t_U_rt), # struct - # { - # __pid_t si_pid; /* Sending process ID. */ - # __uid_t si_uid; /* Real user ID of sending process. */ - # sigval_t si_sigval; /* Signal value. */ - # } _rt; - # - # /* SIGCHLD. */ - ("_sigchld", _siginfo_t_U_sigchld), # struct - # { - # __pid_t si_pid; /* Which child. */ - # __uid_t si_uid; /* Real user ID of sending process. */ - # int si_status; /* Exit value or signal. */ - # __sigchld_clock_t si_utime; - # __sigchld_clock_t si_stime; - # } _sigchld; - # - # /* SIGILL, SIGFPE, SIGSEGV, SIGBUS. */ - ("_sigfault", _siginfo_t_U_sigfault), # struct - # { - # void *si_addr; /* Faulting insn/memory ref. */ - # short int si_addr_lsb; /* Valid LSB of the reported address. */ - # } _sigfault; - # - # /* SIGPOLL. */ - ("_sigpoll", _siginfo_t_U_sigpoll), # struct - # { - # long int si_band; /* Band event for SIGPOLL. */ - # int si_fd; - # } _sigpoll; - # - # /* SIGSYS. */ - ("_sigsys", _siginfo_t_U_sigpoll) # struct - # { - # void *_call_addr; /* Calling user insn. */ - # int _syscall; /* Triggering system call number. */ - # unsigned int _arch; /* AUDIT_ARCH_* of syscall. */ - # } _sigsys; - ] # } _sifields; - -class siginfo_t(ctypes.Structure): # typedef struct - _fields_ = [ # { - ("si_signo", ctypes.c_int), # int si_signo; /* Signal number. */ - ("si_errno", ctypes.c_int), # int si_errno; /* If non-zero, an errno value associated with - # this signal, as defined in . */ - ("si_code", ctypes.c_int), # int si_code; /* Signal code. */ - # - ("_sifields", _siginfo_t_U) # union - # { - # int _pad[__SI_PAD_SIZE]; - # - # /* kill(). */ - # struct - # { - # __pid_t si_pid; /* Sending process ID. */ - # __uid_t si_uid; /* Real user ID of sending process. */ - # } _kill; - # - # /* POSIX.1b timers. */ - # struct - # { - # int si_tid; /* Timer ID. */ - # int si_overrun; /* Overrun count. */ - # sigval_t si_sigval; /* Signal value. */ - # } _timer; - # - # /* POSIX.1b signals. */ - # struct - # { - # __pid_t si_pid; /* Sending process ID. */ - # __uid_t si_uid; /* Real user ID of sending process. */ - # sigval_t si_sigval; /* Signal value. */ - # } _rt; - # - # /* SIGCHLD. */ - # struct - # { - # __pid_t si_pid; /* Which child. */ - # __uid_t si_uid; /* Real user ID of sending process. */ - # int si_status; /* Exit value or signal. */ - # __sigchld_clock_t si_utime; - # __sigchld_clock_t si_stime; - # } _sigchld; - # - # /* SIGILL, SIGFPE, SIGSEGV, SIGBUS. */ - # struct - # { - # void *si_addr; /* Faulting insn/memory ref. */ - # short int si_addr_lsb; /* Valid LSB of the reported address. */ - # } _sigfault; - # - # /* SIGPOLL. */ - # struct - # { - # long int si_band; /* Band event for SIGPOLL. */ - # int si_fd; - # } _sigpoll; - # - # /* SIGSYS. */ - # struct - # { - # void *_call_addr; /* Calling user insn. */ - # int _syscall; /* Triggering system call number. */ - # unsigned int _arch; /* AUDIT_ARCH_* of syscall. */ - # } _sigsys; - # } _sifields; - ] # } siginfo_t __SI_ALIGNMENT; +class sigval_t(ctypes.Union): # typedef union sigval + _fields_ = [ # { + ("sival_int", ctypes.c_int), # int sival_int; + ("sical_ptr", ctypes.c_void_p), # void *sival_ptr; + ] # } sigval_t; + + + # /* POSIX.1b timers. */ +class _siginfo_t_U_timer(ctypes.Structure): # struct + _fields_ = [ # { + ("si_tid", + ctypes.c_int), # int si_tid; /* Timer ID. */ + ("si_overrun", ctypes.c_int + ), # int si_overrun; /* Overrun count. */ + ("si_sigval", sigval_t + ) # sigval_t si_sigval; /* Signal value. */ + ] # } _timer; + + + # /* POSIX.1b signals. */ +class _siginfo_t_U_rt(ctypes.Structure): # struct + _fields_ = [ # { + ("si_pid", ctypes.c_int + ), # __pid_t si_pid; /* Sending process ID. */ + ( + "si_uid", ctypes.c_uint + ), # __uid_t si_uid; /* Real user ID of sending process. */ + ("si_sigval", sigval_t + ) # sigval_t si_sigval; /* Signal value. */ + ] # } _rt; + + + # /* SIGCHLD. */ +class _siginfo_t_U_sigchld(ctypes.Structure): # struct + _fields_ = [ # { + ("si_pid", + ctypes.c_int), # __pid_t si_pid; /* Which child. */ + ( + "si_uid", ctypes.c_uint + ), # __uid_t si_uid; /* Real user ID of sending process. */ + ("si_status", ctypes.c_int + ), # int si_status; /* Exit value or signal. */ + ("si_utime", ctypes.c_long), # __sigchld_clock_t si_utime; + ("si_stime", ctypes.c_long) # __sigchld_clock_t si_stime; + ] # } _sigchld; + + + # /* SIGILL, SIGFPE, SIGSEGV, SIGBUS. */ +class _siginfo_t_U_sigfault(ctypes.Structure): # struct + _fields_ = [ # { + ("si_addr", ctypes.c_void_p + ), # void *si_addr; /* Faulting insn/memory ref. */ + ( + "si_addr_lsb", ctypes.c_short + ) # short int si_addr_lsb; /* Valid LSB of the reported address. */ + ] # } _sigfault; + + + # /* SIGPOLL. */ +class _siginfo_t_U_sigpoll(ctypes.Structure): # struct + _fields_ = [ # { + ("si_band", ctypes.c_long + ), # long int si_band; /* Band event for SIGPOLL. */ + ("si_fd", ctypes.c_int) # int si_fd; + ] # } _sigpoll; + + + # /* SIGSYS. */ +class _siginfo_t_U_sigsys(ctypes.Structure): # struct + _fields_ = [ # { + ("_call_addr", ctypes.c_void_p + ), # void *_call_addr; /* Calling user insn. */ + ( + "_syscall", ctypes.c_int + ), # int _syscall; /* Triggering system call number. */ + ("_arch", ctypes.c_uint + ) # unsigned int _arch; /* AUDIT_ARCH_* of syscall. */ + ] # } _sigsys; + + +class _siginfo_t_U(ctypes.Union): # union + _fields_ = [ # { + ("_pad", + ctypes.c_int * _SI_PAD_SIZE), # int _pad[__SI_PAD_SIZE]; + # + # /* kill(). */ + ("_kill", _siginfo_t_U_kill), # struct + # { + # __pid_t si_pid; /* Sending process ID. */ + # __uid_t si_uid; /* Real user ID of sending process. */ + # } _kill; + # + # /* POSIX.1b timers. */ + ("_timer", _siginfo_t_U_timer), # struct + # { + # int si_tid; /* Timer ID. */ + # int si_overrun; /* Overrun count. */ + # sigval_t si_sigval; /* Signal value. */ + # } _timer; + # + # /* POSIX.1b signals. */ + ("_rt", _siginfo_t_U_rt), # struct + # { + # __pid_t si_pid; /* Sending process ID. */ + # __uid_t si_uid; /* Real user ID of sending process. */ + # sigval_t si_sigval; /* Signal value. */ + # } _rt; + # + # /* SIGCHLD. */ + ("_sigchld", _siginfo_t_U_sigchld), # struct + # { + # __pid_t si_pid; /* Which child. */ + # __uid_t si_uid; /* Real user ID of sending process. */ + # int si_status; /* Exit value or signal. */ + # __sigchld_clock_t si_utime; + # __sigchld_clock_t si_stime; + # } _sigchld; + # + # /* SIGILL, SIGFPE, SIGSEGV, SIGBUS. */ + ("_sigfault", _siginfo_t_U_sigfault), # struct + # { + # void *si_addr; /* Faulting insn/memory ref. */ + # short int si_addr_lsb; /* Valid LSB of the reported address. */ + # } _sigfault; + # + # /* SIGPOLL. */ + ("_sigpoll", _siginfo_t_U_sigpoll), # struct + # { + # long int si_band; /* Band event for SIGPOLL. */ + # int si_fd; + # } _sigpoll; + # + # /* SIGSYS. */ + ("_sigsys", _siginfo_t_U_sigpoll) # struct + # { + # void *_call_addr; /* Calling user insn. */ + # int _syscall; /* Triggering system call number. */ + # unsigned int _arch; /* AUDIT_ARCH_* of syscall. */ + # } _sigsys; + ] # } _sifields; + + +class siginfo_t(ctypes.Structure): # typedef struct + _fields_ = [ # { + ("si_signo", ctypes.c_int + ), # int si_signo; /* Signal number. */ + ( + "si_errno", ctypes.c_int + ), # int si_errno; /* If non-zero, an errno value associated with + # this signal, as defined in . */ + ("si_code", ctypes.c_int + ), # int si_code; /* Signal code. */ + # + ("_sifields", _siginfo_t_U) # union + # { + # int _pad[__SI_PAD_SIZE]; + # + # /* kill(). */ + # struct + # { + # __pid_t si_pid; /* Sending process ID. */ + # __uid_t si_uid; /* Real user ID of sending process. */ + # } _kill; + # + # /* POSIX.1b timers. */ + # struct + # { + # int si_tid; /* Timer ID. */ + # int si_overrun; /* Overrun count. */ + # sigval_t si_sigval; /* Signal value. */ + # } _timer; + # + # /* POSIX.1b signals. */ + # struct + # { + # __pid_t si_pid; /* Sending process ID. */ + # __uid_t si_uid; /* Real user ID of sending process. */ + # sigval_t si_sigval; /* Signal value. */ + # } _rt; + # + # /* SIGCHLD. */ + # struct + # { + # __pid_t si_pid; /* Which child. */ + # __uid_t si_uid; /* Real user ID of sending process. */ + # int si_status; /* Exit value or signal. */ + # __sigchld_clock_t si_utime; + # __sigchld_clock_t si_stime; + # } _sigchld; + # + # /* SIGILL, SIGFPE, SIGSEGV, SIGBUS. */ + # struct + # { + # void *si_addr; /* Faulting insn/memory ref. */ + # short int si_addr_lsb; /* Valid LSB of the reported address. */ + # } _sigfault; + # + # /* SIGPOLL. */ + # struct + # { + # long int si_band; /* Band event for SIGPOLL. */ + # int si_fd; + # } _sigpoll; + # + # /* SIGSYS. */ + # struct + # { + # void *_call_addr; /* Calling user insn. */ + # int _syscall; /* Triggering system call number. */ + # unsigned int _arch; /* AUDIT_ARCH_* of syscall. */ + # } _sigsys; + # } _sifields; + ] # } siginfo_t __SI_ALIGNMENT; # xsave related. -class ymmh_struct(ctypes.Structure): # struct ymmh_struct { - _fields_ = [ - ("ymmh_space", 64*ctypes.c_uint) # u32 ymmh_space[64]; - ] # } __packed; - - -class xsave_hdr_struct(ctypes.Structure): # struct xsave_hdr_struct { - _fields_ = [ - ("xstate_bv", ctypes.c_ulonglong), # u64 xstate_bv; - ("reserved1", ctypes.c_ulonglong*2), # u64 reserved1[2]; - ("reserved2", ctypes.c_ulonglong*5) # u64 reserved2[5]; - ] # } __packed; - - -class i387_fxsave_struct(ctypes.Structure): # struct i387_fxsave_struct { - _fields_ = [ - ("cwd", ctypes.c_ushort), # u16 cwd; /* Control Word */ - ("swd", ctypes.c_ushort), # u16 swd; /* Status Word */ - ("twd", ctypes.c_ushort), # u16 twd; /* Tag Word */ - ("fop", ctypes.c_ushort), # u16 fop; /* Last Instruction Opcode */ - # union { - # struct { - ("rip", ctypes.c_ulonglong), # u64 rip; /* Instruction Pointer */ - ("rdp", ctypes.c_ulonglong), # u64 rdp; /* Data Pointer */ - # }; - # struct { - # u32 fip; /* FPU IP Offset */ - # u32 fcs; /* FPU IP Selector */ - # u32 foo; /* FPU Operand Offset */ - # u32 fos; /* FPU Operand Selector */ - # }; - # }; - ("mxcsr", ctypes.c_uint), # u32 mxcsr; /* MXCSR Register State */ - ("mxcsr_mask", ctypes.c_uint), # u32 mxcsr_mask; /* MXCSR Mask */ - # - # /* 8*16 bytes for each FP-reg = 128 bytes */ - ("st_space", ctypes.c_uint*32), # u32 st_space[32]; -# - # /* 16*16 bytes for each XMM-reg = 256 bytes */ - ("xmm_space", ctypes.c_uint*64), # u32 xmm_space[64]; - # - ("padding", ctypes.c_uint*12), # u32 padding[12]; - # - # union { - ("padding1", ctypes.c_uint*12) # u32 padding1[12]; - # u32 sw_reserved[12]; - # }; - # - ] # } __aligned(16); - - -class elf_xsave_struct(ctypes.Structure): # struct xsave_struct { - _fields_ = [ - ("i387", i387_fxsave_struct), # struct i387_fxsave_struct i387; - ("xsave_hdr", xsave_hdr_struct), # struct xsave_hdr_struct xsave_hdr; - ("ymmh", ymmh_struct) # struct ymmh_struct ymmh; - ] # } __aligned(FP_MIN_ALIGN_BYTES) __packed; + +class ymmh_struct(ctypes.Structure): # struct ymmh_struct { + _fields_ = [("ymmh_space", 64 * ctypes.c_uint + ) # u32 ymmh_space[64]; + ] # } __packed; + + +class xsave_hdr_struct(ctypes.Structure): # struct xsave_hdr_struct { + _fields_ = [ + ("xstate_bv", ctypes.c_ulonglong + ), # u64 xstate_bv; + ("reserved1", ctypes.c_ulonglong * + 2), # u64 reserved1[2]; + ("reserved2", ctypes.c_ulonglong * 5 + ) # u64 reserved2[5]; + ] # } __packed; + + +class i387_fxsave_struct(ctypes.Structure): # struct i387_fxsave_struct { + _fields_ = [ + ( + "cwd", ctypes.c_ushort + ), # u16 cwd; /* Control Word */ + ( + "swd", ctypes.c_ushort + ), # u16 swd; /* Status Word */ + ( + "twd", ctypes.c_ushort + ), # u16 twd; /* Tag Word */ + ( + "fop", ctypes.c_ushort + ), # u16 fop; /* Last Instruction Opcode */ + # union { + # struct { + ( + "rip", ctypes.c_ulonglong + ), # u64 rip; /* Instruction Pointer */ + ( + "rdp", ctypes.c_ulonglong + ), # u64 rdp; /* Data Pointer */ + # }; + # struct { + # u32 fip; /* FPU IP Offset */ + # u32 fcs; /* FPU IP Selector */ + # u32 foo; /* FPU Operand Offset */ + # u32 fos; /* FPU Operand Selector */ + # }; + # }; + ( + "mxcsr", ctypes.c_uint + ), # u32 mxcsr; /* MXCSR Register State */ + ( + "mxcsr_mask", ctypes.c_uint + ), # u32 mxcsr_mask; /* MXCSR Mask */ + # + # /* 8*16 bytes for each FP-reg = 128 bytes */ + ("st_space", ctypes.c_uint * 32 + ), # u32 st_space[32]; + # + # /* 16*16 bytes for each XMM-reg = 256 bytes */ + ("xmm_space", ctypes.c_uint * 64 + ), # u32 xmm_space[64]; + # + ("padding", ctypes.c_uint * 12 + ), # u32 padding[12]; + # + # union { + ("padding1", ctypes.c_uint * 12 + ) # u32 padding1[12]; + # u32 sw_reserved[12]; + # }; + # + ] # } __aligned(16); + + +class elf_xsave_struct(ctypes.Structure): # struct xsave_struct { + _fields_ = [ + ("i387", + i387_fxsave_struct), # struct i387_fxsave_struct i387; + ("xsave_hdr", xsave_hdr_struct + ), # struct xsave_hdr_struct xsave_hdr; + ("ymmh", ymmh_struct) # struct ymmh_struct ymmh; + ] # } __aligned(FP_MIN_ALIGN_BYTES) __packed; diff --git a/lib/py/cli.py b/lib/py/cli.py index abaf0720c..da343022e 100755 --- a/lib/py/cli.py +++ b/lib/py/cli.py @@ -6,337 +6,409 @@ import pycriu + def inf(opts): - if opts['in']: - return open(opts['in'], 'rb') - else: - return sys.stdin + if opts['in']: + return open(opts['in'], 'rb') + else: + return sys.stdin + def outf(opts): - if opts['out']: - return open(opts['out'], 'w+') - else: - return sys.stdout + if opts['out']: + return open(opts['out'], 'w+') + else: + return sys.stdout + def dinf(opts, name): - return open(os.path.join(opts['dir'], name)) + return open(os.path.join(opts['dir'], name)) + def decode(opts): - indent = None + indent = None + + try: + img = pycriu.images.load(inf(opts), opts['pretty'], opts['nopl']) + except pycriu.images.MagicException as exc: + print("Unknown magic %#x.\n"\ + "Maybe you are feeding me an image with "\ + "raw data(i.e. pages.img)?" % exc.magic, file=sys.stderr) + sys.exit(1) - try: - img = pycriu.images.load(inf(opts), opts['pretty'], opts['nopl']) - except pycriu.images.MagicException as exc: - print("Unknown magic %#x.\n"\ - "Maybe you are feeding me an image with "\ - "raw data(i.e. pages.img)?" % exc.magic, file=sys.stderr) - sys.exit(1) + if opts['pretty']: + indent = 4 - if opts['pretty']: - indent = 4 + f = outf(opts) + json.dump(img, f, indent=indent) + if f == sys.stdout: + f.write("\n") - f = outf(opts) - json.dump(img, f, indent=indent) - if f == sys.stdout: - f.write("\n") def encode(opts): - img = json.load(inf(opts)) - pycriu.images.dump(img, outf(opts)) + img = json.load(inf(opts)) + pycriu.images.dump(img, outf(opts)) + def info(opts): - infs = pycriu.images.info(inf(opts)) - json.dump(infs, sys.stdout, indent = 4) - print() + infs = pycriu.images.info(inf(opts)) + json.dump(infs, sys.stdout, indent=4) + print() + def get_task_id(p, val): - return p[val] if val in p else p['ns_' + val][0] + return p[val] if val in p else p['ns_' + val][0] + + # # Explorers # + class ps_item: - def __init__(self, p, core): - self.pid = get_task_id(p, 'pid') - self.ppid = p['ppid'] - self.p = p - self.core = core - self.kids = [] - -def show_ps(p, opts, depth = 0): - print("%7d%7d%7d %s%s" % (p.pid, get_task_id(p.p, 'pgid'), get_task_id(p.p, 'sid'), - ' ' * (4 * depth), p.core['tc']['comm'])) - for kid in p.kids: - show_ps(kid, opts, depth + 1) + def __init__(self, p, core): + self.pid = get_task_id(p, 'pid') + self.ppid = p['ppid'] + self.p = p + self.core = core + self.kids = [] + + +def show_ps(p, opts, depth=0): + print("%7d%7d%7d %s%s" % + (p.pid, get_task_id(p.p, 'pgid'), get_task_id(p.p, 'sid'), ' ' * + (4 * depth), p.core['tc']['comm'])) + for kid in p.kids: + show_ps(kid, opts, depth + 1) + def explore_ps(opts): - pss = { } - ps_img = pycriu.images.load(dinf(opts, 'pstree.img')) - for p in ps_img['entries']: - core = pycriu.images.load(dinf(opts, 'core-%d.img' % get_task_id(p, 'pid'))) - ps = ps_item(p, core['entries'][0]) - pss[ps.pid] = ps - - # Build tree - psr = None - for pid in pss: - p = pss[pid] - if p.ppid == 0: - psr = p - continue - - pp = pss[p.ppid] - pp.kids.append(p) - - print("%7s%7s%7s %s" % ('PID', 'PGID', 'SID', 'COMM')) - show_ps(psr, opts) + pss = {} + ps_img = pycriu.images.load(dinf(opts, 'pstree.img')) + for p in ps_img['entries']: + core = pycriu.images.load( + dinf(opts, 'core-%d.img' % get_task_id(p, 'pid'))) + ps = ps_item(p, core['entries'][0]) + pss[ps.pid] = ps + + # Build tree + psr = None + for pid in pss: + p = pss[pid] + if p.ppid == 0: + psr = p + continue + + pp = pss[p.ppid] + pp.kids.append(p) + + print("%7s%7s%7s %s" % ('PID', 'PGID', 'SID', 'COMM')) + show_ps(psr, opts) + files_img = None + def ftype_find_in_files(opts, ft, fid): - global files_img + global files_img - if files_img is None: - try: - files_img = pycriu.images.load(dinf(opts, "files.img"))['entries'] - except: - files_img = [] + if files_img is None: + try: + files_img = pycriu.images.load(dinf(opts, "files.img"))['entries'] + except: + files_img = [] - if len(files_img) == 0: - return None + if len(files_img) == 0: + return None - for f in files_img: - if f['id'] == fid: - return f + for f in files_img: + if f['id'] == fid: + return f - return None + return None def ftype_find_in_image(opts, ft, fid, img): - f = ftype_find_in_files(opts, ft, fid) - if f: - return f[ft['field']] + f = ftype_find_in_files(opts, ft, fid) + if f: + return f[ft['field']] + + if ft['img'] == None: + ft['img'] = pycriu.images.load(dinf(opts, img))['entries'] + for f in ft['img']: + if f['id'] == fid: + return f + return None - if ft['img'] == None: - ft['img'] = pycriu.images.load(dinf(opts, img))['entries'] - for f in ft['img']: - if f['id'] == fid: - return f - return None def ftype_reg(opts, ft, fid): - rf = ftype_find_in_image(opts, ft, fid, 'reg-files.img') - return rf and rf['name'] or 'unknown path' + rf = ftype_find_in_image(opts, ft, fid, 'reg-files.img') + return rf and rf['name'] or 'unknown path' + def ftype_pipe(opts, ft, fid): - p = ftype_find_in_image(opts, ft, fid, 'pipes.img') - return p and 'pipe[%d]' % p['pipe_id'] or 'pipe[?]' + p = ftype_find_in_image(opts, ft, fid, 'pipes.img') + return p and 'pipe[%d]' % p['pipe_id'] or 'pipe[?]' + def ftype_unix(opts, ft, fid): - ux = ftype_find_in_image(opts, ft, fid, 'unixsk.img') - if not ux: - return 'unix[?]' + ux = ftype_find_in_image(opts, ft, fid, 'unixsk.img') + if not ux: + return 'unix[?]' + + n = ux['name'] and ' %s' % ux['name'] or '' + return 'unix[%d (%d)%s]' % (ux['ino'], ux['peer'], n) - n = ux['name'] and ' %s' % ux['name'] or '' - return 'unix[%d (%d)%s]' % (ux['ino'], ux['peer'], n) file_types = { - 'REG': {'get': ftype_reg, 'img': None, 'field': 'reg'}, - 'PIPE': {'get': ftype_pipe, 'img': None, 'field': 'pipe'}, - 'UNIXSK': {'get': ftype_unix, 'img': None, 'field': 'usk'}, + 'REG': { + 'get': ftype_reg, + 'img': None, + 'field': 'reg' + }, + 'PIPE': { + 'get': ftype_pipe, + 'img': None, + 'field': 'pipe' + }, + 'UNIXSK': { + 'get': ftype_unix, + 'img': None, + 'field': 'usk' + }, } + def ftype_gen(opts, ft, fid): - return '%s.%d' % (ft['typ'], fid) + return '%s.%d' % (ft['typ'], fid) -files_cache = { } -def get_file_str(opts, fd): - key = (fd['type'], fd['id']) - f = files_cache.get(key, None) - if not f: - ft = file_types.get(fd['type'], {'get': ftype_gen, 'typ': fd['type']}) - f = ft['get'](opts, ft, fd['id']) - files_cache[key] = f +files_cache = {} - return f -def explore_fds(opts): - ps_img = pycriu.images.load(dinf(opts, 'pstree.img')) - for p in ps_img['entries']: - pid = get_task_id(p, 'pid') - idi = pycriu.images.load(dinf(opts, 'ids-%s.img' % pid)) - fdt = idi['entries'][0]['files_id'] - fdi = pycriu.images.load(dinf(opts, 'fdinfo-%d.img' % fdt)) +def get_file_str(opts, fd): + key = (fd['type'], fd['id']) + f = files_cache.get(key, None) + if not f: + ft = file_types.get(fd['type'], {'get': ftype_gen, 'typ': fd['type']}) + f = ft['get'](opts, ft, fd['id']) + files_cache[key] = f + + return f - print("%d" % pid) - for fd in fdi['entries']: - print("\t%7d: %s" % (fd['fd'], get_file_str(opts, fd))) - fdi = pycriu.images.load(dinf(opts, 'fs-%d.img' % pid))['entries'][0] - print("\t%7s: %s" % ('cwd', get_file_str(opts, {'type': 'REG', 'id': fdi['cwd_id']}))) - print("\t%7s: %s" % ('root', get_file_str(opts, {'type': 'REG', 'id': fdi['root_id']}))) +def explore_fds(opts): + ps_img = pycriu.images.load(dinf(opts, 'pstree.img')) + for p in ps_img['entries']: + pid = get_task_id(p, 'pid') + idi = pycriu.images.load(dinf(opts, 'ids-%s.img' % pid)) + fdt = idi['entries'][0]['files_id'] + fdi = pycriu.images.load(dinf(opts, 'fdinfo-%d.img' % fdt)) + + print("%d" % pid) + for fd in fdi['entries']: + print("\t%7d: %s" % (fd['fd'], get_file_str(opts, fd))) + + fdi = pycriu.images.load(dinf(opts, 'fs-%d.img' % pid))['entries'][0] + print("\t%7s: %s" % + ('cwd', get_file_str(opts, { + 'type': 'REG', + 'id': fdi['cwd_id'] + }))) + print("\t%7s: %s" % + ('root', get_file_str(opts, { + 'type': 'REG', + 'id': fdi['root_id'] + }))) class vma_id: - def __init__(self): - self.__ids = {} - self.__last = 1 + def __init__(self): + self.__ids = {} + self.__last = 1 + + def get(self, iid): + ret = self.__ids.get(iid, None) + if not ret: + ret = self.__last + self.__last += 1 + self.__ids[iid] = ret - def get(self, iid): - ret = self.__ids.get(iid, None) - if not ret: - ret = self.__last - self.__last += 1 - self.__ids[iid] = ret + return ret - return ret def explore_mems(opts): - ps_img = pycriu.images.load(dinf(opts, 'pstree.img')) - vids = vma_id() - for p in ps_img['entries']: - pid = get_task_id(p, 'pid') - mmi = pycriu.images.load(dinf(opts, 'mm-%d.img' % pid))['entries'][0] - - print("%d" % pid) - print("\t%-36s %s" % ('exe', get_file_str(opts, {'type': 'REG', 'id': mmi['exe_file_id']}))) - - for vma in mmi['vmas']: - st = vma['status'] - if st & (1 << 10): - fn = ' ' + 'ips[%lx]' % vids.get(vma['shmid']) - elif st & (1 << 8): - fn = ' ' + 'shmem[%lx]' % vids.get(vma['shmid']) - elif st & (1 << 11): - fn = ' ' + 'packet[%lx]' % vids.get(vma['shmid']) - elif st & ((1 << 6) | (1 << 7)): - fn = ' ' + get_file_str(opts, {'type': 'REG', 'id': vma['shmid']}) - if vma['pgoff']: - fn += ' + %#lx' % vma['pgoff'] - if st & (1 << 7): - fn += ' (s)' - elif st & (1 << 1): - fn = ' [stack]' - elif st & (1 << 2): - fn = ' [vsyscall]' - elif st & (1 << 3): - fn = ' [vdso]' - elif vma['flags'] & 0x0100: # growsdown - fn = ' [stack?]' - else: - fn = '' - - if not st & (1 << 0): - fn += ' *' - - prot = vma['prot'] & 0x1 and 'r' or '-' - prot += vma['prot'] & 0x2 and 'w' or '-' - prot += vma['prot'] & 0x4 and 'x' or '-' - - astr = '%08lx-%08lx' % (vma['start'], vma['end']) - print("\t%-36s%s%s" % (astr, prot, fn)) + ps_img = pycriu.images.load(dinf(opts, 'pstree.img')) + vids = vma_id() + for p in ps_img['entries']: + pid = get_task_id(p, 'pid') + mmi = pycriu.images.load(dinf(opts, 'mm-%d.img' % pid))['entries'][0] + + print("%d" % pid) + print("\t%-36s %s" % ('exe', + get_file_str(opts, { + 'type': 'REG', + 'id': mmi['exe_file_id'] + }))) + + for vma in mmi['vmas']: + st = vma['status'] + if st & (1 << 10): + fn = ' ' + 'ips[%lx]' % vids.get(vma['shmid']) + elif st & (1 << 8): + fn = ' ' + 'shmem[%lx]' % vids.get(vma['shmid']) + elif st & (1 << 11): + fn = ' ' + 'packet[%lx]' % vids.get(vma['shmid']) + elif st & ((1 << 6) | (1 << 7)): + fn = ' ' + get_file_str(opts, { + 'type': 'REG', + 'id': vma['shmid'] + }) + if vma['pgoff']: + fn += ' + %#lx' % vma['pgoff'] + if st & (1 << 7): + fn += ' (s)' + elif st & (1 << 1): + fn = ' [stack]' + elif st & (1 << 2): + fn = ' [vsyscall]' + elif st & (1 << 3): + fn = ' [vdso]' + elif vma['flags'] & 0x0100: # growsdown + fn = ' [stack?]' + else: + fn = '' + + if not st & (1 << 0): + fn += ' *' + + prot = vma['prot'] & 0x1 and 'r' or '-' + prot += vma['prot'] & 0x2 and 'w' or '-' + prot += vma['prot'] & 0x4 and 'x' or '-' + + astr = '%08lx-%08lx' % (vma['start'], vma['end']) + print("\t%-36s%s%s" % (astr, prot, fn)) def explore_rss(opts): - ps_img = pycriu.images.load(dinf(opts, 'pstree.img')) - for p in ps_img['entries']: - pid = get_task_id(p, 'pid') - vmas = pycriu.images.load(dinf(opts, 'mm-%d.img' % pid))['entries'][0]['vmas'] - pms = pycriu.images.load(dinf(opts, 'pagemap-%d.img' % pid))['entries'] - - print("%d" % pid) - vmi = 0 - pvmi = -1 - for pm in pms[1:]: - pstr = '\t%lx / %-8d' % (pm['vaddr'], pm['nr_pages']) - while vmas[vmi]['end'] <= pm['vaddr']: - vmi += 1 - - pme = pm['vaddr'] + (pm['nr_pages'] << 12) - vstr = '' - while vmas[vmi]['start'] < pme: - vma = vmas[vmi] - if vmi == pvmi: - vstr += ' ~' - else: - vstr += ' %08lx / %-8d' % (vma['start'], (vma['end'] - vma['start'])>>12) - if vma['status'] & ((1 << 6) | (1 << 7)): - vstr += ' ' + get_file_str(opts, {'type': 'REG', 'id': vma['shmid']}) - pvmi = vmi - vstr += '\n\t%23s' % '' - vmi += 1 - - vmi -= 1 - - print('%-24s%s' % (pstr, vstr)) - - - -explorers = { 'ps': explore_ps, 'fds': explore_fds, 'mems': explore_mems, 'rss': explore_rss } + ps_img = pycriu.images.load(dinf(opts, 'pstree.img')) + for p in ps_img['entries']: + pid = get_task_id(p, 'pid') + vmas = pycriu.images.load(dinf(opts, 'mm-%d.img' % + pid))['entries'][0]['vmas'] + pms = pycriu.images.load(dinf(opts, 'pagemap-%d.img' % pid))['entries'] + + print("%d" % pid) + vmi = 0 + pvmi = -1 + for pm in pms[1:]: + pstr = '\t%lx / %-8d' % (pm['vaddr'], pm['nr_pages']) + while vmas[vmi]['end'] <= pm['vaddr']: + vmi += 1 + + pme = pm['vaddr'] + (pm['nr_pages'] << 12) + vstr = '' + while vmas[vmi]['start'] < pme: + vma = vmas[vmi] + if vmi == pvmi: + vstr += ' ~' + else: + vstr += ' %08lx / %-8d' % ( + vma['start'], (vma['end'] - vma['start']) >> 12) + if vma['status'] & ((1 << 6) | (1 << 7)): + vstr += ' ' + get_file_str(opts, { + 'type': 'REG', + 'id': vma['shmid'] + }) + pvmi = vmi + vstr += '\n\t%23s' % '' + vmi += 1 + + vmi -= 1 + + print('%-24s%s' % (pstr, vstr)) + + +explorers = { + 'ps': explore_ps, + 'fds': explore_fds, + 'mems': explore_mems, + 'rss': explore_rss +} + def explore(opts): - explorers[opts['what']](opts) + explorers[opts['what']](opts) + def main(): - desc = 'CRiu Image Tool' - parser = argparse.ArgumentParser(description=desc, - formatter_class=argparse.RawTextHelpFormatter) - - subparsers = parser.add_subparsers(help='Use crit CMD --help for command-specific help') - - # Decode - decode_parser = subparsers.add_parser('decode', - help = 'convert criu image from binary type to json') - decode_parser.add_argument('--pretty', - help = 'Multiline with indents and some numerical fields in field-specific format', - action = 'store_true') - decode_parser.add_argument('-i', - '--in', - help = 'criu image in binary format to be decoded (stdin by default)') - decode_parser.add_argument('-o', - '--out', - help = 'where to put criu image in json format (stdout by default)') - decode_parser.set_defaults(func=decode, nopl=False) - - # Encode - encode_parser = subparsers.add_parser('encode', - help = 'convert criu image from json type to binary') - encode_parser.add_argument('-i', - '--in', - help = 'criu image in json format to be encoded (stdin by default)') - encode_parser.add_argument('-o', - '--out', - help = 'where to put criu image in binary format (stdout by default)') - encode_parser.set_defaults(func=encode) - - # Info - info_parser = subparsers.add_parser('info', - help = 'show info about image') - info_parser.add_argument("in") - info_parser.set_defaults(func=info) - - # Explore - x_parser = subparsers.add_parser('x', help = 'explore image dir') - x_parser.add_argument('dir') - x_parser.add_argument('what', choices = [ 'ps', 'fds', 'mems', 'rss']) - x_parser.set_defaults(func=explore) - - # Show - show_parser = subparsers.add_parser('show', - help = "convert criu image from binary to human-readable json") - show_parser.add_argument("in") - show_parser.add_argument('--nopl', help = 'do not show entry payload (if exists)', action = 'store_true') - show_parser.set_defaults(func=decode, pretty=True, out=None) - - opts = vars(parser.parse_args()) - - if not opts: - sys.stderr.write(parser.format_usage()) - sys.stderr.write("crit: error: too few arguments\n") - sys.exit(1) - - opts["func"](opts) + desc = 'CRiu Image Tool' + parser = argparse.ArgumentParser( + description=desc, formatter_class=argparse.RawTextHelpFormatter) + + subparsers = parser.add_subparsers( + help='Use crit CMD --help for command-specific help') + + # Decode + decode_parser = subparsers.add_parser( + 'decode', help='convert criu image from binary type to json') + decode_parser.add_argument( + '--pretty', + help= + 'Multiline with indents and some numerical fields in field-specific format', + action='store_true') + decode_parser.add_argument( + '-i', + '--in', + help='criu image in binary format to be decoded (stdin by default)') + decode_parser.add_argument( + '-o', + '--out', + help='where to put criu image in json format (stdout by default)') + decode_parser.set_defaults(func=decode, nopl=False) + + # Encode + encode_parser = subparsers.add_parser( + 'encode', help='convert criu image from json type to binary') + encode_parser.add_argument( + '-i', + '--in', + help='criu image in json format to be encoded (stdin by default)') + encode_parser.add_argument( + '-o', + '--out', + help='where to put criu image in binary format (stdout by default)') + encode_parser.set_defaults(func=encode) + + # Info + info_parser = subparsers.add_parser('info', help='show info about image') + info_parser.add_argument("in") + info_parser.set_defaults(func=info) + + # Explore + x_parser = subparsers.add_parser('x', help='explore image dir') + x_parser.add_argument('dir') + x_parser.add_argument('what', choices=['ps', 'fds', 'mems', 'rss']) + x_parser.set_defaults(func=explore) + + # Show + show_parser = subparsers.add_parser( + 'show', help="convert criu image from binary to human-readable json") + show_parser.add_argument("in") + show_parser.add_argument('--nopl', + help='do not show entry payload (if exists)', + action='store_true') + show_parser.set_defaults(func=decode, pretty=True, out=None) + + opts = vars(parser.parse_args()) + + if not opts: + sys.stderr.write(parser.format_usage()) + sys.stderr.write("crit: error: too few arguments\n") + sys.exit(1) + + opts["func"](opts) + if __name__ == '__main__': - main() + main() diff --git a/lib/py/criu.py b/lib/py/criu.py index de1a214a3..d94fea9e1 100644 --- a/lib/py/criu.py +++ b/lib/py/criu.py @@ -8,325 +8,336 @@ import pycriu.rpc_pb2 as 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): - """ + 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 + pass - def disconnect(self): - """ + def disconnect(self): + """ Disconnect from criu. """ - pass + 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) + def __init__(self, sk_path): + self.comm_type = self.COMM_SK + self.comm = sk_path - return self.sk + def connect(self, daemon): + self.sk = socket.socket(socket.AF_UNIX, socket.SOCK_SEQPACKET) + self.sk.connect(self.comm) - def disconnect(self): - self.sk.close() + return self.sk + + def disconnect(self): + self.sk.close() class _criu_comm_fd(_criu_comm): - """ + """ Communication 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) + 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 - return self.sk + def disconnect(self): + self.sk.close() - 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) - flags = fcntl.fcntl(css[0], fcntl.F_GETFD) - fcntl.fcntl(css[0], 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() - else: - if daemon: - os.waitpid(p, 0) - - 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) + + 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) + flags = fcntl.fcntl(css[0], fcntl.F_GETFD) + fcntl.fcntl(css[0], 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() + else: + if daemon: + os.waitpid(p, 0) + + 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 + typ = None + _str = None - def __str__(self): - return self._str + 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) + + 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 __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), ) + def _gen_error_str(self): + s = "%s failed: " % (rpc.criu_req_type.Name(self.typ), ) - if self.typ != self.resp_typ: - s += "Unexpected response type %d: " % (self.resp_typ, ) + if self.typ != self.resp_typ: + s += "Unexpected response type %d: " % (self.resp_typ, ) - s += "Error(%d): " % (self.errno, ) + s += "Error(%d): " % (self.errno, ) - if self.errno == errno.EBADRQC: - s += "Bad options" + 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.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" + if self.typ == rpc.RESTORE: + if self.errno == errno.EEXIST: + s += "Process with requested pid already exists" - s += "Unknown" + s += "Unknown" - return s + return s class criu: - """ + """ Call criu through RPC. """ - opts = None #CRIU options in pb format + opts = None #CRIU options in pb format - _comm = None #Communication method + _comm = None #Communication method - def __init__(self): - self.use_binary('criu') - self.opts = rpc.criu_opts() - self.sk = None + def __init__(self): + self.use_binary('criu') + self.opts = rpc.criu_opts() + self.sk = None - def use_sk(self, sk_name): - """ + 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) + self._comm = _criu_comm_sk(sk_name) - def use_fd(self, fd): - """ + def use_fd(self, fd): + """ Access criu using provided fd. """ - self._comm = _criu_comm_fd(fd) + self._comm = _criu_comm_fd(fd) - def use_binary(self, bin_name): - """ + def use_binary(self, bin_name): + """ Access criu by execing it using provided path to criu binary. """ - self._comm = _criu_comm_bin(bin_name) + self._comm = _criu_comm_bin(bin_name) - def _send_req_and_recv_resp(self, req): - """ + 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 + # 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: - if not self.sk: - s = self._comm.connect(daemon) - else: - s = self.sk + try: + if not self.sk: + s = self._comm.connect(daemon) + else: + s = self.sk - if req.keep_open: - self.sk = s + if req.keep_open: + self.sk = s - s.send(req.SerializeToString()) + s.send(req.SerializeToString()) - buf = s.recv(len(s.recv(1, socket.MSG_TRUNC | socket.MSG_PEEK))) + buf = s.recv(len(s.recv(1, socket.MSG_TRUNC | socket.MSG_PEEK))) - if not req.keep_open: - self._comm.disconnect() + if not req.keep_open: + self._comm.disconnect() - resp = rpc.criu_resp() - resp.ParseFromString(buf) - except Exception as e: - raise CRIUExceptionInternal(req.type, str(e)) + resp = rpc.criu_resp() + resp.ParseFromString(buf) + except Exception as e: + raise CRIUExceptionInternal(req.type, str(e)) - return resp + return resp - def check(self): - """ + def check(self): + """ Checks whether the kernel support is up-to-date. """ - req = rpc.criu_req() - req.type = rpc.CHECK + req = rpc.criu_req() + req.type = rpc.CHECK - resp = self._send_req_and_recv_resp(req) + resp = self._send_req_and_recv_resp(req) - if not resp.success: - raise CRIUExceptionExternal(req.type, resp.type, resp.cr_errno) + if not resp.success: + raise CRIUExceptionExternal(req.type, resp.type, resp.cr_errno) - def dump(self): - """ + def dump(self): + """ Checkpoint a process/tree identified by opts.pid. """ - req = rpc.criu_req() - req.type = rpc.DUMP - req.opts.MergeFrom(self.opts) + req = rpc.criu_req() + req.type = rpc.DUMP + req.opts.MergeFrom(self.opts) - resp = self._send_req_and_recv_resp(req) + resp = self._send_req_and_recv_resp(req) - if not resp.success: - raise CRIUExceptionExternal(req.type, resp.type, resp.cr_errno) + if not resp.success: + raise CRIUExceptionExternal(req.type, resp.type, resp.cr_errno) - return resp.dump + return resp.dump - def pre_dump(self): - """ + def pre_dump(self): + """ Checkpoint a process/tree identified by opts.pid. """ - req = rpc.criu_req() - req.type = rpc.PRE_DUMP - req.opts.MergeFrom(self.opts) + req = rpc.criu_req() + req.type = rpc.PRE_DUMP + req.opts.MergeFrom(self.opts) - resp = self._send_req_and_recv_resp(req) + resp = self._send_req_and_recv_resp(req) - if not resp.success: - raise CRIUExceptionExternal(req.type, resp.type, resp.cr_errno) + if not resp.success: + raise CRIUExceptionExternal(req.type, resp.type, resp.cr_errno) - return resp.dump + return resp.dump - def restore(self): - """ + def restore(self): + """ Restore a process/tree. """ - req = rpc.criu_req() - req.type = rpc.RESTORE - req.opts.MergeFrom(self.opts) + req = rpc.criu_req() + req.type = rpc.RESTORE + req.opts.MergeFrom(self.opts) - resp = self._send_req_and_recv_resp(req) + resp = self._send_req_and_recv_resp(req) - if not resp.success: - raise CRIUExceptionExternal(req.type, resp.type, resp.cr_errno) + if not resp.success: + raise CRIUExceptionExternal(req.type, resp.type, resp.cr_errno) - return resp.restore + return resp.restore - def page_server_chld(self): - req = rpc.criu_req() - req.type = rpc.PAGE_SERVER_CHLD - req.opts.MergeFrom(self.opts) - req.keep_open = True + def page_server_chld(self): + req = rpc.criu_req() + req.type = rpc.PAGE_SERVER_CHLD + req.opts.MergeFrom(self.opts) + req.keep_open = True - resp = self._send_req_and_recv_resp(req) + resp = self._send_req_and_recv_resp(req) - if not resp.success: - raise CRIUExceptionExternal(req.type, resp.type, resp.cr_errno) + if not resp.success: + raise CRIUExceptionExternal(req.type, resp.type, resp.cr_errno) - return resp.ps + return resp.ps - def wait_pid(self, pid): - req = rpc.criu_req() - req.type = rpc.WAIT_PID - req.pid = pid + def wait_pid(self, pid): + req = rpc.criu_req() + req.type = rpc.WAIT_PID + req.pid = pid - resp = self._send_req_and_recv_resp(req) + resp = self._send_req_and_recv_resp(req) - if not resp.success: - raise CRIUExceptionExternal(req.type, resp.type, resp.cr_errno) + if not resp.success: + raise CRIUExceptionExternal(req.type, resp.type, resp.cr_errno) - return resp.status + return resp.status diff --git a/lib/py/images/images.py b/lib/py/images/images.py index 7a9b9da6e..28c6d9e1f 100644 --- a/lib/py/images/images.py +++ b/lib/py/images/images.py @@ -48,8 +48,8 @@ from . import pb2dict if "encodebytes" not in dir(base64): - base64.encodebytes = base64.encodestring - base64.decodebytes = base64.decodestring + base64.encodebytes = base64.encodestring + base64.decodebytes = base64.decodestring # # Predefined hardcoded constants @@ -57,233 +57,241 @@ sizeof_u32 = 4 sizeof_u64 = 8 + # A helper for rounding -def round_up(x,y): - return (((x - 1) | (y - 1)) + 1) +def round_up(x, y): + return (((x - 1) | (y - 1)) + 1) + class MagicException(Exception): - def __init__(self, magic): - self.magic = magic + 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): - """ + + def __init__(self, payload, extra_handler=None): + """ Sets payload class and extra handler class. """ - self.payload = payload - self.extra_handler = extra_handler + self.payload = payload + self.extra_handler = extra_handler - def load(self, f, pretty = False, no_payload = False): - """ + def load(self, f, pretty=False, no_payload=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 - pbuff = self.payload() - buf = f.read(4) - if buf == b'': - break - size, = struct.unpack('i', buf) - pbuff.ParseFromString(f.read(size)) - entry = pb2dict.pb2dict(pbuff, pretty) - - # Read extra - if self.extra_handler: - if no_payload: - def human_readable(num): - for unit in ['','K','M','G','T','P','E','Z']: - if num < 1024.0: - if int(num) == num: - return "%d%sB" % (num, unit) - else: - return "%.1f%sB" % (num, unit) - num /= 1024.0 - return "%.1fYB" % num - - pl_size = self.extra_handler.skip(f, pbuff) - entry['extra'] = '... <%s>' % human_readable(pl_size) - else: - entry['extra'] = self.extra_handler.load(f, pbuff) - - entries.append(entry) - - return entries - - def loads(self, s, pretty = False): - """ + entries = [] + + while True: + entry = {} + + # Read payload + pbuff = self.payload() + buf = f.read(4) + if buf == b'': + break + size, = struct.unpack('i', buf) + pbuff.ParseFromString(f.read(size)) + entry = pb2dict.pb2dict(pbuff, pretty) + + # Read extra + if self.extra_handler: + if no_payload: + + def human_readable(num): + for unit in ['', 'K', 'M', 'G', 'T', 'P', 'E', 'Z']: + if num < 1024.0: + if int(num) == num: + return "%d%sB" % (num, unit) + else: + return "%.1f%sB" % (num, unit) + num /= 1024.0 + return "%.1fYB" % num + + pl_size = self.extra_handler.skip(f, pbuff) + entry['extra'] = '... <%s>' % human_readable(pl_size) + else: + entry['extra'] = self.extra_handler.load(f, pbuff) + + 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) + f = io.BytesIO(s) + return self.load(f, pretty) - def dump(self, entries, f): - """ + 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 - pbuff = self.payload() - pb2dict.dict2pb(entry, pbuff) - pb_str = pbuff.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, pbuff) - - def dumps(self, entries): - """ + for entry in entries: + extra = entry.pop('extra', None) + + # Write payload + pbuff = self.payload() + pb2dict.dict2pb(entry, pbuff) + pb_str = pbuff.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, pbuff) + + 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() + f = io.BytesIO('') + self.dump(entries, f) + return f.read() - def count(self, f): - """ + def count(self, f): + """ Counts the number of top-level object in the image file """ - entries = 0 + entries = 0 + + while True: + buf = f.read(4) + if buf == '': + break + size, = struct.unpack('i', buf) + f.seek(size, 1) + entries += 1 - while True: - buf = f.read(4) - if buf == '': - break - size, = struct.unpack('i', buf) - f.seek(size, 1) - entries += 1 + return entries - 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, no_payload = False): - entries = [] - pbuff = pb.pagemap_head() - while True: - buf = f.read(4) - if buf == b'': - break - size, = struct.unpack('i', buf) - pbuff.ParseFromString(f.read(size)) - entries.append(pb2dict.pb2dict(pbuff, pretty)) + def load(self, f, pretty=False, no_payload=False): + entries = [] + + pbuff = pb.pagemap_head() + while True: + buf = f.read(4) + if buf == b'': + break + size, = struct.unpack('i', buf) + pbuff.ParseFromString(f.read(size)) + entries.append(pb2dict.pb2dict(pbuff, pretty)) + + pbuff = pb.pagemap_entry() - pbuff = pb.pagemap_entry() + return entries - return entries + def loads(self, s, pretty=False): + f = io.BytesIO(s) + return self.load(f, pretty) - def loads(self, s, pretty = False): - f = io.BytesIO(s) - return self.load(f, pretty) + def dump(self, entries, f): + pbuff = pb.pagemap_head() + for item in entries: + pb2dict.dict2pb(item, pbuff) + pb_str = pbuff.SerializeToString() + size = len(pb_str) + f.write(struct.pack('i', size)) + f.write(pb_str) - def dump(self, entries, f): - pbuff = pb.pagemap_head() - for item in entries: - pb2dict.dict2pb(item, pbuff) - pb_str = pbuff.SerializeToString() - size = len(pb_str) - f.write(struct.pack('i', size)) - f.write(pb_str) + pbuff = pb.pagemap_entry() - pbuff = pb.pagemap_entry() + def dumps(self, entries): + f = io.BytesIO('') + self.dump(entries, f) + return f.read() - 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 - def count(self, f): - return entry_handler(None).count(f) - 1 # Special handler for ghost-file.img class ghost_file_handler: - def load(self, f, pretty = False, no_payload = False): - entries = [] - - gf = pb.ghost_file_entry() - buf = f.read(4) - size, = struct.unpack('i', buf) - gf.ParseFromString(f.read(size)) - g_entry = pb2dict.pb2dict(gf, pretty) - - if gf.chunks: - entries.append(g_entry) - while True: - gc = pb.ghost_chunk_entry() - buf = f.read(4) - if buf == '': - break - size, = struct.unpack('i', buf) - gc.ParseFromString(f.read(size)) - entry = pb2dict.pb2dict(gc, pretty) - if no_payload: - f.seek(gc.len, os.SEEK_CUR) - else: - entry['extra'] = base64.encodebytes(f.read(gc.len)) - entries.append(entry) - else: - if no_payload: - f.seek(0, os.SEEK_END) - else: - g_entry['extra'] = base64.encodebytes(f.read()) - entries.append(g_entry) - - return entries - - def loads(self, s, pretty = False): - f = io.BytesIO(s) - return self.load(f, pretty) - - def dump(self, entries, f): - pbuff = pb.ghost_file_entry() - item = entries.pop(0) - pb2dict.dict2pb(item, pbuff) - pb_str = pbuff.SerializeToString() - size = len(pb_str) - f.write(struct.pack('i', size)) - f.write(pb_str) - - if pbuff.chunks: - for item in entries: - pbuff = pb.ghost_chunk_entry() - pb2dict.dict2pb(item, pbuff) - pb_str = pbuff.SerializeToString() - size = len(pb_str) - f.write(struct.pack('i', size)) - f.write(pb_str) - f.write(base64.decodebytes(item['extra'])) - else: - f.write(base64.decodebytes(item['extra'])) - - def dumps(self, entries): - f = io.BytesIO('') - self.dump(entries, f) - return f.read() + def load(self, f, pretty=False, no_payload=False): + entries = [] + + gf = pb.ghost_file_entry() + buf = f.read(4) + size, = struct.unpack('i', buf) + gf.ParseFromString(f.read(size)) + g_entry = pb2dict.pb2dict(gf, pretty) + + if gf.chunks: + entries.append(g_entry) + while True: + gc = pb.ghost_chunk_entry() + buf = f.read(4) + if buf == '': + break + size, = struct.unpack('i', buf) + gc.ParseFromString(f.read(size)) + entry = pb2dict.pb2dict(gc, pretty) + if no_payload: + f.seek(gc.len, os.SEEK_CUR) + else: + entry['extra'] = base64.encodebytes(f.read(gc.len)) + entries.append(entry) + else: + if no_payload: + f.seek(0, os.SEEK_END) + else: + g_entry['extra'] = base64.encodebytes(f.read()) + entries.append(g_entry) + + return entries + + def loads(self, s, pretty=False): + f = io.BytesIO(s) + return self.load(f, pretty) + + def dump(self, entries, f): + pbuff = pb.ghost_file_entry() + item = entries.pop(0) + pb2dict.dict2pb(item, pbuff) + pb_str = pbuff.SerializeToString() + size = len(pb_str) + f.write(struct.pack('i', size)) + f.write(pb_str) + + if pbuff.chunks: + for item in entries: + pbuff = pb.ghost_chunk_entry() + pb2dict.dict2pb(item, pbuff) + pb_str = pbuff.SerializeToString() + size = len(pb_str) + f.write(struct.pack('i', size)) + f.write(pb_str) + f.write(base64.decodebytes(item['extra'])) + else: + f.write(base64.decodebytes(item['extra'])) + + def dumps(self, entries): + f = io.BytesIO('') + self.dump(entries, f) + return f.read() # In following extra handlers we use base64 encoding @@ -293,304 +301,317 @@ def dumps(self, entries): # 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 base64.encodebytes(data) + def load(self, f, pload): + size = pload.bytes + data = f.read(size) + return base64.encodebytes(data) - def dump(self, extra, f, pload): - data = base64.decodebytes(extra) - f.write(data) + def dump(self, extra, f, pload): + data = base64.decodebytes(extra) + f.write(data) + + def skip(self, f, pload): + f.seek(pload.bytes, os.SEEK_CUR) + return pload.bytes - def skip(self, f, pload): - f.seek(pload.bytes, os.SEEK_CUR) - return pload.bytes class sk_queues_extra_handler: - def load(self, f, pload): - size = pload.length - data = f.read(size) - return base64.encodebytes(data) + def load(self, f, pload): + size = pload.length + data = f.read(size) + return base64.encodebytes(data) - def dump(self, extra, f, _unused): - data = base64.decodebytes(extra) - f.write(data) + def dump(self, extra, f, _unused): + data = base64.decodebytes(extra) + f.write(data) - def skip(self, f, pload): - f.seek(pload.length, os.SEEK_CUR) - return pload.length + def skip(self, f, pload): + f.seek(pload.length, os.SEEK_CUR) + return pload.length class tcp_stream_extra_handler: - def load(self, f, pbuff): - d = {} + def load(self, f, pbuff): + d = {} + + inq = f.read(pbuff.inq_len) + outq = f.read(pbuff.outq_len) - inq = f.read(pbuff.inq_len) - outq = f.read(pbuff.outq_len) + d['inq'] = base64.encodebytes(inq) + d['outq'] = base64.encodebytes(outq) - d['inq'] = base64.encodebytes(inq) - d['outq'] = base64.encodebytes(outq) + return d - return d + def dump(self, extra, f, _unused): + inq = base64.decodebytes(extra['inq']) + outq = base64.decodebytes(extra['outq']) - def dump(self, extra, f, _unused): - inq = base64.decodebytes(extra['inq']) - outq = base64.decodebytes(extra['outq']) + f.write(inq) + f.write(outq) - f.write(inq) - f.write(outq) + def skip(self, f, pbuff): + f.seek(0, os.SEEK_END) + return pbuff.inq_len + pbuff.outq_len - def skip(self, f, pbuff): - f.seek(0, os.SEEK_END) - return pbuff.inq_len + pbuff.outq_len class ipc_sem_set_handler: - def load(self, f, pbuff): - entry = pb2dict.pb2dict(pbuff) - 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, pbuff): - entry = pb2dict.pb2dict(pbuff) - 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)) - - def skip(self, f, pbuff): - entry = pb2dict.pb2dict(pbuff) - size = sizeof_u16 * entry['nsems'] - f.seek(round_up(size, sizeof_u64), os.SEEK_CUR) - return size + def load(self, f, pbuff): + entry = pb2dict.pb2dict(pbuff) + 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, pbuff): + entry = pb2dict.pb2dict(pbuff) + 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)) + + def skip(self, f, pbuff): + entry = pb2dict.pb2dict(pbuff) + size = sizeof_u16 * entry['nsems'] + f.seek(round_up(size, sizeof_u64), os.SEEK_CUR) + return size + class ipc_msg_queue_handler: - def load(self, f, pbuff): - entry = pb2dict.pb2dict(pbuff) - messages = [] - for x in range (0, entry['qnum']): - buf = f.read(4) - if buf == '': - break - size, = struct.unpack('i', buf) - msg = pb.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(base64.encodebytes(data)) - return messages - - def dump(self, extra, f, pbuff): - entry = pb2dict.pb2dict(pbuff) - for i in range (0, len(extra), 2): - msg = pb.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 = base64.decodebytes(extra[i + 1]) - f.write(data[:msg.msize]) - f.write('\0' * (rounded - msg.msize)) - - def skip(self, f, pbuff): - entry = pb2dict.pb2dict(pbuff) - pl_len = 0 - for x in range (0, entry['qnum']): - buf = f.read(4) - if buf == '': - break - size, = struct.unpack('i', buf) - msg = pb.ipc_msg() - msg.ParseFromString(f.read(size)) - rounded = round_up(msg.msize, sizeof_u64) - f.seek(rounded, os.SEEK_CUR) - pl_len += size + msg.msize - - return pl_len + def load(self, f, pbuff): + entry = pb2dict.pb2dict(pbuff) + messages = [] + for x in range(0, entry['qnum']): + buf = f.read(4) + if buf == '': + break + size, = struct.unpack('i', buf) + msg = pb.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(base64.encodebytes(data)) + return messages + + def dump(self, extra, f, pbuff): + entry = pb2dict.pb2dict(pbuff) + for i in range(0, len(extra), 2): + msg = pb.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 = base64.decodebytes(extra[i + 1]) + f.write(data[:msg.msize]) + f.write('\0' * (rounded - msg.msize)) + + def skip(self, f, pbuff): + entry = pb2dict.pb2dict(pbuff) + pl_len = 0 + for x in range(0, entry['qnum']): + buf = f.read(4) + if buf == '': + break + size, = struct.unpack('i', buf) + msg = pb.ipc_msg() + msg.ParseFromString(f.read(size)) + rounded = round_up(msg.msize, sizeof_u64) + f.seek(rounded, os.SEEK_CUR) + pl_len += size + msg.msize + + return pl_len + class ipc_shm_handler: - def load(self, f, pbuff): - entry = pb2dict.pb2dict(pbuff) - size = entry['size'] - data = f.read(size) - rounded = round_up(size, sizeof_u32) - f.seek(rounded - size, 1) - return base64.encodebytes(data) - - def dump(self, extra, f, pbuff): - entry = pb2dict.pb2dict(pbuff) - size = entry['size'] - data = base64.decodebytes(extra) - rounded = round_up(size, sizeof_u32) - f.write(data[:size]) - f.write('\0' * (rounded - size)) - - def skip(self, f, pbuff): - entry = pb2dict.pb2dict(pbuff) - size = entry['size'] - rounded = round_up(size, sizeof_u32) - f.seek(rounded, os.SEEK_CUR) - return size + def load(self, f, pbuff): + entry = pb2dict.pb2dict(pbuff) + size = entry['size'] + data = f.read(size) + rounded = round_up(size, sizeof_u32) + f.seek(rounded - size, 1) + return base64.encodebytes(data) + + def dump(self, extra, f, pbuff): + entry = pb2dict.pb2dict(pbuff) + size = entry['size'] + data = base64.decodebytes(extra) + rounded = round_up(size, sizeof_u32) + f.write(data[:size]) + f.write('\0' * (rounded - size)) + + def skip(self, f, pbuff): + entry = pb2dict.pb2dict(pbuff) + size = entry['size'] + rounded = round_up(size, sizeof_u32) + f.seek(rounded, os.SEEK_CUR) + return size handlers = { - 'INVENTORY' : entry_handler(pb.inventory_entry), - 'CORE' : entry_handler(pb.core_entry), - 'IDS' : entry_handler(pb.task_kobj_ids_entry), - 'CREDS' : entry_handler(pb.creds_entry), - 'UTSNS' : entry_handler(pb.utsns_entry), - 'IPC_VAR' : entry_handler(pb.ipc_var_entry), - 'FS' : entry_handler(pb.fs_entry), - 'GHOST_FILE' : ghost_file_handler(), - 'MM' : entry_handler(pb.mm_entry), - 'CGROUP' : entry_handler(pb.cgroup_entry), - 'TCP_STREAM' : entry_handler(pb.tcp_stream_entry, tcp_stream_extra_handler()), - 'STATS' : entry_handler(pb.stats_entry), - 'PAGEMAP' : pagemap_handler(), # Special one - 'PSTREE' : entry_handler(pb.pstree_entry), - 'REG_FILES' : entry_handler(pb.reg_file_entry), - 'NS_FILES' : entry_handler(pb.ns_file_entry), - 'EVENTFD_FILE' : entry_handler(pb.eventfd_file_entry), - 'EVENTPOLL_FILE' : entry_handler(pb.eventpoll_file_entry), - 'EVENTPOLL_TFD' : entry_handler(pb.eventpoll_tfd_entry), - 'SIGNALFD' : entry_handler(pb.signalfd_entry), - 'TIMERFD' : entry_handler(pb.timerfd_entry), - 'INOTIFY_FILE' : entry_handler(pb.inotify_file_entry), - 'INOTIFY_WD' : entry_handler(pb.inotify_wd_entry), - 'FANOTIFY_FILE' : entry_handler(pb.fanotify_file_entry), - 'FANOTIFY_MARK' : entry_handler(pb.fanotify_mark_entry), - 'VMAS' : entry_handler(pb.vma_entry), - 'PIPES' : entry_handler(pb.pipe_entry), - 'FIFO' : entry_handler(pb.fifo_entry), - 'SIGACT' : entry_handler(pb.sa_entry), - 'NETLINK_SK' : entry_handler(pb.netlink_sk_entry), - 'REMAP_FPATH' : entry_handler(pb.remap_file_path_entry), - 'MNTS' : entry_handler(pb.mnt_entry), - 'TTY_FILES' : entry_handler(pb.tty_file_entry), - 'TTY_INFO' : entry_handler(pb.tty_info_entry), - 'TTY_DATA' : entry_handler(pb.tty_data_entry), - 'RLIMIT' : entry_handler(pb.rlimit_entry), - 'TUNFILE' : entry_handler(pb.tunfile_entry), - 'EXT_FILES' : entry_handler(pb.ext_file_entry), - 'IRMAP_CACHE' : entry_handler(pb.irmap_cache_entry), - 'FILE_LOCKS' : entry_handler(pb.file_lock_entry), - 'FDINFO' : entry_handler(pb.fdinfo_entry), - 'UNIXSK' : entry_handler(pb.unix_sk_entry), - 'INETSK' : entry_handler(pb.inet_sk_entry), - 'PACKETSK' : entry_handler(pb.packet_sock_entry), - 'ITIMERS' : entry_handler(pb.itimer_entry), - 'POSIX_TIMERS' : entry_handler(pb.posix_timer_entry), - 'NETDEV' : entry_handler(pb.net_device_entry), - 'PIPES_DATA' : entry_handler(pb.pipe_data_entry, pipes_data_extra_handler()), - 'FIFO_DATA' : entry_handler(pb.pipe_data_entry, pipes_data_extra_handler()), - 'SK_QUEUES' : entry_handler(pb.sk_packet_entry, sk_queues_extra_handler()), - 'IPCNS_SHM' : entry_handler(pb.ipc_shm_entry, ipc_shm_handler()), - 'IPCNS_SEM' : entry_handler(pb.ipc_sem_entry, ipc_sem_set_handler()), - 'IPCNS_MSG' : entry_handler(pb.ipc_msg_entry, ipc_msg_queue_handler()), - 'NETNS' : entry_handler(pb.netns_entry), - 'USERNS' : entry_handler(pb.userns_entry), - 'SECCOMP' : entry_handler(pb.seccomp_entry), - 'AUTOFS' : entry_handler(pb.autofs_entry), - 'FILES' : entry_handler(pb.file_entry), - 'CPUINFO' : entry_handler(pb.cpuinfo_entry), - } + 'INVENTORY': entry_handler(pb.inventory_entry), + 'CORE': entry_handler(pb.core_entry), + 'IDS': entry_handler(pb.task_kobj_ids_entry), + 'CREDS': entry_handler(pb.creds_entry), + 'UTSNS': entry_handler(pb.utsns_entry), + 'IPC_VAR': entry_handler(pb.ipc_var_entry), + 'FS': entry_handler(pb.fs_entry), + 'GHOST_FILE': ghost_file_handler(), + 'MM': entry_handler(pb.mm_entry), + 'CGROUP': entry_handler(pb.cgroup_entry), + 'TCP_STREAM': entry_handler(pb.tcp_stream_entry, + tcp_stream_extra_handler()), + 'STATS': entry_handler(pb.stats_entry), + 'PAGEMAP': pagemap_handler(), # Special one + 'PSTREE': entry_handler(pb.pstree_entry), + 'REG_FILES': entry_handler(pb.reg_file_entry), + 'NS_FILES': entry_handler(pb.ns_file_entry), + 'EVENTFD_FILE': entry_handler(pb.eventfd_file_entry), + 'EVENTPOLL_FILE': entry_handler(pb.eventpoll_file_entry), + 'EVENTPOLL_TFD': entry_handler(pb.eventpoll_tfd_entry), + 'SIGNALFD': entry_handler(pb.signalfd_entry), + 'TIMERFD': entry_handler(pb.timerfd_entry), + 'INOTIFY_FILE': entry_handler(pb.inotify_file_entry), + 'INOTIFY_WD': entry_handler(pb.inotify_wd_entry), + 'FANOTIFY_FILE': entry_handler(pb.fanotify_file_entry), + 'FANOTIFY_MARK': entry_handler(pb.fanotify_mark_entry), + 'VMAS': entry_handler(pb.vma_entry), + 'PIPES': entry_handler(pb.pipe_entry), + 'FIFO': entry_handler(pb.fifo_entry), + 'SIGACT': entry_handler(pb.sa_entry), + 'NETLINK_SK': entry_handler(pb.netlink_sk_entry), + 'REMAP_FPATH': entry_handler(pb.remap_file_path_entry), + 'MNTS': entry_handler(pb.mnt_entry), + 'TTY_FILES': entry_handler(pb.tty_file_entry), + 'TTY_INFO': entry_handler(pb.tty_info_entry), + 'TTY_DATA': entry_handler(pb.tty_data_entry), + 'RLIMIT': entry_handler(pb.rlimit_entry), + 'TUNFILE': entry_handler(pb.tunfile_entry), + 'EXT_FILES': entry_handler(pb.ext_file_entry), + 'IRMAP_CACHE': entry_handler(pb.irmap_cache_entry), + 'FILE_LOCKS': entry_handler(pb.file_lock_entry), + 'FDINFO': entry_handler(pb.fdinfo_entry), + 'UNIXSK': entry_handler(pb.unix_sk_entry), + 'INETSK': entry_handler(pb.inet_sk_entry), + 'PACKETSK': entry_handler(pb.packet_sock_entry), + 'ITIMERS': entry_handler(pb.itimer_entry), + 'POSIX_TIMERS': entry_handler(pb.posix_timer_entry), + 'NETDEV': entry_handler(pb.net_device_entry), + 'PIPES_DATA': entry_handler(pb.pipe_data_entry, + pipes_data_extra_handler()), + 'FIFO_DATA': entry_handler(pb.pipe_data_entry, pipes_data_extra_handler()), + 'SK_QUEUES': entry_handler(pb.sk_packet_entry, sk_queues_extra_handler()), + 'IPCNS_SHM': entry_handler(pb.ipc_shm_entry, ipc_shm_handler()), + 'IPCNS_SEM': entry_handler(pb.ipc_sem_entry, ipc_sem_set_handler()), + 'IPCNS_MSG': entry_handler(pb.ipc_msg_entry, ipc_msg_queue_handler()), + 'NETNS': entry_handler(pb.netns_entry), + 'USERNS': entry_handler(pb.userns_entry), + 'SECCOMP': entry_handler(pb.seccomp_entry), + 'AUTOFS': entry_handler(pb.autofs_entry), + 'FILES': entry_handler(pb.file_entry), + 'CPUINFO': entry_handler(pb.cpuinfo_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)) + # 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: + 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) + try: + handler = handlers[m] + except: + raise Exception("No handler found for image with magic " + m) - return m, handler + return m, handler -def load(f, pretty = False, no_payload = False): - """ + +def load(f, pretty=False, no_payload=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 = {} + image = {} - m, handler = __rhandler(f) + m, handler = __rhandler(f) - image['magic'] = m - image['entries'] = handler.load(f, pretty, no_payload) + image['magic'] = m + image['entries'] = handler.load(f, pretty, no_payload) + + return image - return image def info(f): - res = {} + res = {} - m, handler = __rhandler(f) + m, handler = __rhandler(f) - res['magic'] = m - res['count'] = handler.count(f) + res['magic'] = m + res['count'] = handler.count(f) - return res + return res -def loads(s, pretty = False): - """ + +def loads(s, pretty=False): + """ Same as load(), but takes a string. """ - f = io.BytesIO(s) - return load(f, pretty) + 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']] + 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'])) - # 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)) - f.write(struct.pack('i', magic_val)) + try: + handler = handlers[m] + except: + raise Exception("No handler found for image with such magic") - try: - handler = handlers[m] - except: - raise Exception("No handler found for image with such magic") + handler.dump(img['entries'], f) - handler.dump(img['entries'], f) def dumps(img): - """ + """ Same as dump(), but takes only an image and returns a string. """ - f = io.BytesIO(b'') - dump(img, f) - return f.getvalue() + f = io.BytesIO(b'') + dump(img, f) + return f.getvalue() diff --git a/lib/py/images/pb2dict.py b/lib/py/images/pb2dict.py index c4ce736e8..8825f9897 100644 --- a/lib/py/images/pb2dict.py +++ b/lib/py/images/pb2dict.py @@ -9,8 +9,8 @@ import quopri if "encodebytes" not in dir(base64): - base64.encodebytes = base64.encodestring - base64.decodebytes = base64.decodestring + base64.encodebytes = base64.encodestring + base64.decodebytes = base64.decodestring # pb2dict and dict2pb are methods to convert pb to/from dict. # Inspired by: @@ -29,350 +29,400 @@ # enums to string value too. (i.e. "march : x86_64" is better then # "march : 1"). - _basic_cast = { - FD.TYPE_FIXED64 : int, - FD.TYPE_FIXED32 : int, - FD.TYPE_SFIXED64 : int, - FD.TYPE_SFIXED32 : int, - - FD.TYPE_INT64 : int, - FD.TYPE_UINT64 : int, - FD.TYPE_SINT64 : int, - - FD.TYPE_INT32 : int, - FD.TYPE_UINT32 : int, - FD.TYPE_SINT32 : int, - - FD.TYPE_BOOL : bool, - - FD.TYPE_STRING : str + FD.TYPE_FIXED64: int, + FD.TYPE_FIXED32: int, + FD.TYPE_SFIXED64: int, + FD.TYPE_SFIXED32: int, + FD.TYPE_INT64: int, + FD.TYPE_UINT64: int, + FD.TYPE_SINT64: int, + FD.TYPE_INT32: int, + FD.TYPE_UINT32: int, + FD.TYPE_SINT32: int, + FD.TYPE_BOOL: bool, + FD.TYPE_STRING: str } + def _marked_as_hex(field): - return field.GetOptions().Extensions[opts_pb2.criu].hex + return field.GetOptions().Extensions[opts_pb2.criu].hex + def _marked_as_ip(field): - return field.GetOptions().Extensions[opts_pb2.criu].ipadd + return field.GetOptions().Extensions[opts_pb2.criu].ipadd + def _marked_as_flags(field): - return field.GetOptions().Extensions[opts_pb2.criu].flags + return field.GetOptions().Extensions[opts_pb2.criu].flags + def _marked_as_dev(field): - return field.GetOptions().Extensions[opts_pb2.criu].dev + return field.GetOptions().Extensions[opts_pb2.criu].dev + def _marked_as_odev(field): - return field.GetOptions().Extensions[opts_pb2.criu].odev + return field.GetOptions().Extensions[opts_pb2.criu].odev + def _marked_as_dict(field): - return field.GetOptions().Extensions[opts_pb2.criu].dict + return field.GetOptions().Extensions[opts_pb2.criu].dict + def _custom_conv(field): - return field.GetOptions().Extensions[opts_pb2.criu].conv + return field.GetOptions().Extensions[opts_pb2.criu].conv + mmap_prot_map = [ - ('PROT_READ', 0x1), - ('PROT_WRITE', 0x2), - ('PROT_EXEC', 0x4), + ('PROT_READ', 0x1), + ('PROT_WRITE', 0x2), + ('PROT_EXEC', 0x4), ] mmap_flags_map = [ - ('MAP_SHARED', 0x1), - ('MAP_PRIVATE', 0x2), - ('MAP_ANON', 0x20), - ('MAP_GROWSDOWN', 0x0100), + ('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), + ('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', 0o1), - ('O_RDWR', 0o2), - ('O_APPEND', 0o2000), - ('O_DIRECT', 0o40000), - ('O_LARGEFILE', 0o100000), + ('O_WRONLY', 0o1), + ('O_RDWR', 0o2), + ('O_APPEND', 0o2000), + ('O_DIRECT', 0o40000), + ('O_LARGEFILE', 0o100000), ] pmap_flags_map = [ - ('PE_PARENT', 1 << 0), - ('PE_LAZY', 1 << 1), - ('PE_PRESENT', 1 << 2), + ('PE_PARENT', 1 << 0), + ('PE_LAZY', 1 << 1), + ('PE_PRESENT', 1 << 2), ] flags_maps = { - 'mmap.prot' : mmap_prot_map, - 'mmap.flags' : mmap_flags_map, - 'mmap.status' : mmap_status_map, - 'rfile.flags' : rfile_flags_map, - 'pmap.flags' : pmap_flags_map, + 'mmap.prot': mmap_prot_map, + 'mmap.flags': mmap_flags_map, + 'mmap.status': mmap_status_map, + 'rfile.flags': rfile_flags_map, + 'pmap.flags': pmap_flags_map, } gen_maps = { - 'task_state' : { 1: 'Alive', 3: 'Zombie', 6: 'Stopped' }, + 'task_state': { + 1: 'Alive', + 3: 'Zombie', + 6: 'Stopped' + }, } sk_maps = { - 'family' : { 1: 'UNIX', - 2: 'INET', - 10: 'INET6', - 16: 'NETLINK', - 17: 'PACKET' }, - 'type' : { 1: 'STREAM', - 2: 'DGRAM', - 3: 'RAW', - 5: 'SEQPACKET', - 10: 'PACKET' }, - 'state' : { 1: 'ESTABLISHED', - 2: 'SYN_SENT', - 3: 'SYN_RECV', - 4: 'FIN_WAIT1', - 5: 'FIN_WAIT2', - 6: 'TIME_WAIT', - 7: 'CLOSE', - 8: 'CLOSE_WAIT', - 9: 'LAST_ACK', - 10: 'LISTEN' }, - 'proto' : { 0: 'IP', - 6: 'TCP', - 17: 'UDP', - 136: 'UDPLITE' }, + 'family': { + 1: 'UNIX', + 2: 'INET', + 10: 'INET6', + 16: 'NETLINK', + 17: 'PACKET' + }, + 'type': { + 1: 'STREAM', + 2: 'DGRAM', + 3: 'RAW', + 5: 'SEQPACKET', + 10: 'PACKET' + }, + 'state': { + 1: 'ESTABLISHED', + 2: 'SYN_SENT', + 3: 'SYN_RECV', + 4: 'FIN_WAIT1', + 5: 'FIN_WAIT2', + 6: 'TIME_WAIT', + 7: 'CLOSE', + 8: 'CLOSE_WAIT', + 9: 'LAST_ACK', + 10: 'LISTEN' + }, + 'proto': { + 0: 'IP', + 6: 'TCP', + 17: 'UDP', + 136: 'UDPLITE' + }, } -gen_rmaps = { k: {v2:k2 for k2,v2 in list(v.items())} for k,v in list(gen_maps.items()) } -sk_rmaps = { k: {v2:k2 for k2,v2 in list(v.items())} for k,v in list(sk_maps.items()) } +gen_rmaps = { + k: {v2: k2 + for k2, v2 in list(v.items())} + for k, v in list(gen_maps.items()) +} +sk_rmaps = { + k: {v2: k2 + for k2, v2 in list(v.items())} + for k, v in list(sk_maps.items()) +} dict_maps = { - 'gen' : ( gen_maps, gen_rmaps ), - 'sk' : ( sk_maps, sk_rmaps ), + 'gen': (gen_maps, gen_rmaps), + 'sk': (sk_maps, sk_rmaps), } + def map_flags(value, flags_map): - bs = [x[0] for x in [x for x in flags_map if value & x[1]]] - value &= ~sum([x[1] for x in flags_map]) - if value: - bs.append("0x%x" % value) - return " | ".join(bs) + bs = [x[0] for x in [x for x in flags_map if value & x[1]]] + value &= ~sum([x[1] for x in flags_map]) + if value: + bs.append("0x%x" % value) + return " | ".join(bs) + def unmap_flags(value, flags_map): - if value == '': - return 0 + if value == '': + return 0 - bd = dict(flags_map) - return sum([int(str(bd.get(x, x)), 0) for x in [x.strip() for x in value.split('|')]]) + bd = dict(flags_map) + return sum([ + int(str(bd.get(x, x)), 0) + for x in [x.strip() for x in value.split('|')] + ]) + + +kern_minorbits = 20 # This is how kernel encodes dev_t in new format -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)) + 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 = [int(x) for x in value.split(':')] - if _marked_as_odev(field): - return os.makedev(dev[0], dev[1]) - else: - return dev[0] << kern_minorbits | dev[1] + dev = [int(x) for x in value.split(':')] + if _marked_as_odev(field): + return os.makedev(dev[0], dev[1]) + else: + return dev[0] << kern_minorbits | dev[1] + def encode_base64(value): - return base64.encodebytes(value) + return base64.encodebytes(value) + + def decode_base64(value): - return base64.decodebytes(value) + return base64.decodebytes(value) + def encode_unix(value): - return quopri.encodestring(value) + return quopri.encodestring(value) + + def decode_unix(value): - return quopri.decodestring(value) + return quopri.decodestring(value) + + +encode = {'unix_name': encode_unix} +decode = {'unix_name': decode_unix} -encode = { 'unix_name': encode_unix } -decode = { 'unix_name': decode_unix } def get_bytes_enc(field): - c = _custom_conv(field) - if c: - return encode[c] - else: - return encode_base64 + c = _custom_conv(field) + if c: + return encode[c] + else: + return encode_base64 + def get_bytes_dec(field): - c = _custom_conv(field) - if c: - return decode[c] - else: - return decode_base64 + c = _custom_conv(field) + if c: + return decode[c] + else: + return decode_base64 + def is_string(value): - # Python 3 compatibility - if "basestring" in __builtins__: - string_types = basestring - else: - string_types = (str, bytes) - return isinstance(value, string_types) - -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 get_bytes_enc(field)(value) - 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): - 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) - - dct = _marked_as_dict(field) - if dct: - return dict_maps[dct][0][field.name].get(value, cast(value)) - - return cast(value) - else: - raise Exception("Field(%s) has unsupported type %d" % (field.name, field.type)) - -def pb2dict(pb, pretty = False, is_hex = False): - """ + # Python 3 compatibility + if "basestring" in __builtins__: + string_types = basestring + else: + string_types = (str, bytes) + return isinstance(value, string_types) + + +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 get_bytes_enc(field)(value) + 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): + 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) + + dct = _marked_as_dict(field) + if dct: + return dict_maps[dct][0][field.name].get(value, cast(value)) + + 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 = 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 = 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 + 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 = 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 = 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 get_bytes_dec(field)(value) - 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) 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) - - dct = _marked_as_dict(field) - if dct: - ret = dict_maps[dct][1][field.name].get(value, None) - if ret == None: - ret = cast(value, 0) - return ret - - # 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)) + # 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 get_bytes_dec(field)(value) + 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) 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) + + dct = _marked_as_dict(field) + if dct: + ret = dict_maps[dct][1][field.name].get(value, None) + if ret == None: + ret = cast(value, 0) + return ret + + # 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 = ip_address(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 + 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 = ip_address(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/scripts/crit-setup.py b/scripts/crit-setup.py index 60fef6a07..06681cd30 100644 --- a/scripts/crit-setup.py +++ b/scripts/crit-setup.py @@ -1,12 +1,11 @@ from distutils.core import setup -setup(name = "crit", - version = "0.0.1", - description = "CRiu Image Tool", - author = "CRIU team", - author_email = "criu@openvz.org", - url = "https://github.com/xemul/criu", - package_dir = {'pycriu': 'lib/py'}, - packages = ["pycriu", "pycriu.images"], - scripts = ["crit/crit"] - ) +setup(name="crit", + version="0.0.1", + description="CRiu Image Tool", + author="CRIU team", + author_email="criu@openvz.org", + url="https://github.com/xemul/criu", + package_dir={'pycriu': 'lib/py'}, + packages=["pycriu", "pycriu.images"], + scripts=["crit/crit"]) diff --git a/scripts/magic-gen.py b/scripts/magic-gen.py index 7088f634d..3d9777735 100755 --- a/scripts/magic-gen.py +++ b/scripts/magic-gen.py @@ -1,61 +1,63 @@ #!/bin/env python2 import sys + # This program parses criu magic.h file and produces # magic.py with all *_MAGIC constants except RAW and V1. def main(argv): - if len(argv) != 3: - print("Usage: magic-gen.py path/to/image.h path/to/magic.py") - exit(1) - - magic_c_header = argv[1] - magic_py = argv[2] - - out = open(magic_py, 'w+') - - # all_magic is used to parse constructions like: - # #define PAGEMAP_MAGIC 0x56084025 - # #define SHMEM_PAGEMAP_MAGIC PAGEMAP_MAGIC - all_magic = {} - # and magic is used to store only unique magic. - magic = {} - - f = open(magic_c_header, 'r') - for line in f: - split = line.split() - - if len(split) < 3: - continue - - if not '#define' in split[0]: - continue - - key = split[1] - value = split[2] - - if value in all_magic: - value = all_magic[value] - else: - magic[key] = value - - all_magic[key] = value - - out.write('#Autogenerated. Do not edit!\n') - out.write('by_name = {}\n') - out.write('by_val = {}\n') - for k,v in list(magic.items()): - # We don't need RAW or V1 magic, because - # they can't be used to identify images. - if v == '0x0' or v == '1' or k == '0x0' or v == '1': - continue - if k.endswith("_MAGIC"): - # Just cutting _MAGIC suffix - k = k[:-6] - v = int(v, 16) - out.write("by_name['"+ k +"'] = "+ str(v) +"\n") - out.write("by_val["+ str(v) +"] = '"+ k +"'\n") - f.close() - out.close() + if len(argv) != 3: + print("Usage: magic-gen.py path/to/image.h path/to/magic.py") + exit(1) + + magic_c_header = argv[1] + magic_py = argv[2] + + out = open(magic_py, 'w+') + + # all_magic is used to parse constructions like: + # #define PAGEMAP_MAGIC 0x56084025 + # #define SHMEM_PAGEMAP_MAGIC PAGEMAP_MAGIC + all_magic = {} + # and magic is used to store only unique magic. + magic = {} + + f = open(magic_c_header, 'r') + for line in f: + split = line.split() + + if len(split) < 3: + continue + + if not '#define' in split[0]: + continue + + key = split[1] + value = split[2] + + if value in all_magic: + value = all_magic[value] + else: + magic[key] = value + + all_magic[key] = value + + out.write('#Autogenerated. Do not edit!\n') + out.write('by_name = {}\n') + out.write('by_val = {}\n') + for k, v in list(magic.items()): + # We don't need RAW or V1 magic, because + # they can't be used to identify images. + if v == '0x0' or v == '1' or k == '0x0' or v == '1': + continue + if k.endswith("_MAGIC"): + # Just cutting _MAGIC suffix + k = k[:-6] + v = int(v, 16) + out.write("by_name['" + k + "'] = " + str(v) + "\n") + out.write("by_val[" + str(v) + "] = '" + k + "'\n") + f.close() + out.close() + if __name__ == "__main__": - main(sys.argv) + main(sys.argv) diff --git a/soccr/test/run.py b/soccr/test/run.py index a25c29263..446584a71 100644 --- a/soccr/test/run.py +++ b/soccr/test/run.py @@ -13,17 +13,17 @@ dport = os.getenv("TCP_DPORT", "54321") print(sys.argv[1]) -args = [sys.argv[1], - "--addr", src, "--port", sport, "--seq", "555", - "--next", - "--addr", dst, "--port", dport, "--seq", "666", - "--reverse", "--", "./tcp-test.py"] +args = [ + sys.argv[1], "--addr", src, "--port", sport, "--seq", "555", "--next", + "--addr", dst, "--port", dport, "--seq", "666", "--reverse", "--", + "./tcp-test.py" +] -p1 = Popen(args + ["dst"], stdout = PIPE, stdin = PIPE) +p1 = Popen(args + ["dst"], stdout=PIPE, stdin=PIPE) -args.remove("--reverse"); +args.remove("--reverse") -p2 = Popen(args + ["src"], stdout = PIPE, stdin = PIPE) +p2 = Popen(args + ["src"], stdout=PIPE, stdin=PIPE) p1.stdout.read(5) p2.stdout.read(5) @@ -42,7 +42,7 @@ if str2 != eval(s): print("FAIL", repr(str2), repr(s)) - sys.exit(5); + sys.exit(5) s = p1.stdout.read() m = hashlib.md5() @@ -52,7 +52,7 @@ s = p2.stdout.read() if str1 != eval(s): print("FAIL", repr(str1), s) - sys.exit(5); + sys.exit(5) if p1.wait(): sys.exit(1) diff --git a/test/check_actions.py b/test/check_actions.py index 0e3daf178..ae909e668 100755 --- a/test/check_actions.py +++ b/test/check_actions.py @@ -4,37 +4,38 @@ import os actions = set(['pre-dump', 'pre-restore', 'post-dump', 'setup-namespaces', \ - 'post-setup-namespaces', 'post-restore', 'post-resume', \ - 'network-lock', 'network-unlock' ]) + 'post-setup-namespaces', 'post-restore', 'post-resume', \ + 'network-lock', 'network-unlock' ]) errors = [] af = os.path.dirname(os.path.abspath(__file__)) + '/actions_called.txt' for act in open(af): - act = act.strip().split() - act.append('EMPTY') - act.append('EMPTY') + act = act.strip().split() + act.append('EMPTY') + act.append('EMPTY') - if act[0] == 'EMPTY': - raise Exception("Error in test, bogus actions line") + if act[0] == 'EMPTY': + raise Exception("Error in test, bogus actions line") - if act[1] == 'EMPTY': - errors.append('Action %s misses CRTOOLS_IMAGE_DIR' % act[0]) + if act[1] == 'EMPTY': + errors.append('Action %s misses CRTOOLS_IMAGE_DIR' % act[0]) - if act[0] in ('post-dump', 'setup-namespaces', 'post-setup-namespaces', \ - 'post-restore', 'post-resume', 'network-lock', 'network-unlock'): - if act[2] == 'EMPTY': - errors.append('Action %s misses CRTOOLS_INIT_PID' % act[0]) - elif not act[2].isdigit() or int(act[2]) == 0: - errors.append('Action %s PID is not number (%s)' % (act[0], act[2])) + if act[0] in ('post-dump', 'setup-namespaces', 'post-setup-namespaces', \ + 'post-restore', 'post-resume', 'network-lock', 'network-unlock'): + if act[2] == 'EMPTY': + errors.append('Action %s misses CRTOOLS_INIT_PID' % act[0]) + elif not act[2].isdigit() or int(act[2]) == 0: + errors.append('Action %s PID is not number (%s)' % + (act[0], act[2])) - actions -= set([act[0]]) + actions -= set([act[0]]) if actions: - errors.append('Not all actions called: %r' % actions) + errors.append('Not all actions called: %r' % actions) if errors: - for x in errors: - print(x) - sys.exit(1) + for x in errors: + print(x) + sys.exit(1) print('PASS') diff --git a/test/crit-recode.py b/test/crit-recode.py index 441f7757e..a7dcc7272 100755 --- a/test/crit-recode.py +++ b/test/crit-recode.py @@ -6,70 +6,72 @@ import os import subprocess -find = subprocess.Popen(['find', 'test/dump/', '-size', '+0', '-name', '*.img'], - stdout = subprocess.PIPE) +find = subprocess.Popen( + ['find', 'test/dump/', '-size', '+0', '-name', '*.img'], + stdout=subprocess.PIPE) test_pass = True + def recode_and_check(imgf, o_img, pretty): - try: - pb = pycriu.images.loads(o_img, pretty) - except pycriu.images.MagicException as me: - print("%s magic %x error" % (imgf, me.magic)) - return False - except Exception as e: - print("%s %sdecode fails: %s" % (imgf, pretty and 'pretty ' or '', e)) - return False + try: + pb = pycriu.images.loads(o_img, pretty) + except pycriu.images.MagicException as me: + print("%s magic %x error" % (imgf, me.magic)) + return False + except Exception as e: + print("%s %sdecode fails: %s" % (imgf, pretty and 'pretty ' or '', e)) + return False - try: - r_img = pycriu.images.dumps(pb) - except Exception as e: - r_img = pycriu.images.dumps(pb) - print("%s %s encode fails: %s" % (imgf, pretty and 'pretty ' or '', e)) - return False + try: + r_img = pycriu.images.dumps(pb) + except Exception as e: + r_img = pycriu.images.dumps(pb) + print("%s %s encode fails: %s" % (imgf, pretty and 'pretty ' or '', e)) + return False - if o_img != r_img: - print("%s %s recode mismatch" % (imgf, pretty and 'pretty ' or '')) - return False + if o_img != r_img: + print("%s %s recode mismatch" % (imgf, pretty and 'pretty ' or '')) + return False - return True + return True for imgf in find.stdout.readlines(): - imgf = imgf.strip() - imgf_b = os.path.basename(imgf) + imgf = imgf.strip() + imgf_b = os.path.basename(imgf) - if imgf_b.startswith(b'pages-'): - continue - if imgf_b.startswith(b'iptables-'): - continue - if imgf_b.startswith(b'ip6tables-'): - continue - if imgf_b.startswith(b'route-'): - continue - if imgf_b.startswith(b'route6-'): - continue - if imgf_b.startswith(b'ifaddr-'): - continue - if imgf_b.startswith(b'tmpfs-'): - continue - if imgf_b.startswith(b'netns-ct-'): - continue - if imgf_b.startswith(b'netns-exp-'): - continue - if imgf_b.startswith(b'rule-'): - continue + if imgf_b.startswith(b'pages-'): + continue + if imgf_b.startswith(b'iptables-'): + continue + if imgf_b.startswith(b'ip6tables-'): + continue + if imgf_b.startswith(b'route-'): + continue + if imgf_b.startswith(b'route6-'): + continue + if imgf_b.startswith(b'ifaddr-'): + continue + if imgf_b.startswith(b'tmpfs-'): + continue + if imgf_b.startswith(b'netns-ct-'): + continue + if imgf_b.startswith(b'netns-exp-'): + continue + if imgf_b.startswith(b'rule-'): + continue - o_img = open(imgf.decode(), "rb").read() - if not recode_and_check(imgf, o_img, False): - test_pass = False - if not recode_and_check(imgf, o_img, True): - test_pass = False + o_img = open(imgf.decode(), "rb").read() + if not recode_and_check(imgf, o_img, False): + test_pass = False + if not recode_and_check(imgf, o_img, True): + test_pass = False find.wait() if not test_pass: - print("FAIL") - sys.exit(1) + print("FAIL") + sys.exit(1) print("PASS") diff --git a/test/exhaustive/pipe.py b/test/exhaustive/pipe.py index 17e065800..fdadc480c 100755 --- a/test/exhaustive/pipe.py +++ b/test/exhaustive/pipe.py @@ -8,125 +8,127 @@ import sys import subprocess -criu_bin='../../criu/criu' +criu_bin = '../../criu/criu' + def mix(nr_tasks, nr_pipes): - # Returned is the list of combinations. - # Each combination is the lists of pipe descriptors. - # Each pipe descriptor is a 2-elemtn tuple, that contains values - # for R and W ends of pipes, each being a bit-field denoting in - # which tasks the respective end should be opened or not. + # Returned is the list of combinations. + # Each combination is the lists of pipe descriptors. + # Each pipe descriptor is a 2-elemtn tuple, that contains values + # for R and W ends of pipes, each being a bit-field denoting in + # which tasks the respective end should be opened or not. - # First -- make a full set of combinations for a single pipe. - max_idx = 1 << nr_tasks - pipe_mix = [[(r, w)] for r in range(0, max_idx) for w in range(0, max_idx)] + # First -- make a full set of combinations for a single pipe. + max_idx = 1 << nr_tasks + pipe_mix = [[(r, w)] for r in range(0, max_idx) for w in range(0, max_idx)] - # Now, for every pipe throw another one into the game making - # all possible combinations of what was seen before with the - # newbie. - pipes_mix = pipe_mix - for t in range(1, nr_pipes): - pipes_mix = [ o + n for o in pipes_mix for n in pipe_mix ] + # Now, for every pipe throw another one into the game making + # all possible combinations of what was seen before with the + # newbie. + pipes_mix = pipe_mix + for t in range(1, nr_pipes): + pipes_mix = [o + n for o in pipes_mix for n in pipe_mix] - return pipes_mix + return pipes_mix # Called by a test sub-process. It just closes the not needed ends # of pipes and sleeps waiting for death. def make_pipes(task_nr, nr_pipes, pipes, comb, status_pipe): - print('\t\tMake pipes for %d' % task_nr) - # We need to make sure that pipes have their - # ends according to comb for task_nr + print('\t\tMake pipes for %d' % task_nr) + # We need to make sure that pipes have their + # ends according to comb for task_nr - for i in range(0, nr_pipes): - # Read end - if not (comb[i][0] & (1 << task_nr)): - os.close(pipes[i][0]) - # Write end - if not (comb[i][1] & (1 << task_nr)): - os.close(pipes[i][1]) + for i in range(0, nr_pipes): + # Read end + if not (comb[i][0] & (1 << task_nr)): + os.close(pipes[i][0]) + # Write end + if not (comb[i][1] & (1 << task_nr)): + os.close(pipes[i][1]) - os.write(status_pipe, '0') - os.close(status_pipe) - while True: - time.sleep(100) + os.write(status_pipe, '0') + os.close(status_pipe) + while True: + time.sleep(100) def get_pipe_ino(pid, fd): - try: - return os.stat('/proc/%d/fd/%d' % (pid, fd)).st_ino - except: - return None + try: + return os.stat('/proc/%d/fd/%d' % (pid, fd)).st_ino + except: + return None def get_pipe_rw(pid, fd): - for l in open('/proc/%d/fdinfo/%d' % (pid, fd)): - if l.startswith('flags:'): - f = l.split(None, 1)[1][-2] - if f == '0': - return 0 # Read - elif f == '1': - return 1 # Write - break + for l in open('/proc/%d/fdinfo/%d' % (pid, fd)): + if l.startswith('flags:'): + f = l.split(None, 1)[1][-2] + if f == '0': + return 0 # Read + elif f == '1': + return 1 # Write + break - raise Exception('Unexpected fdinfo contents') + raise Exception('Unexpected fdinfo contents') def check_pipe_y(pid, fd, rw, inos): - ino = get_pipe_ino(pid, fd) - if ino == None: - return 'missing ' - if not inos.has_key(fd): - inos[fd] = ino - elif inos[fd] != ino: - return 'wrong ' - mod = get_pipe_rw(pid, fd) - if mod != rw: - return 'badmode ' - return None + ino = get_pipe_ino(pid, fd) + if ino == None: + return 'missing ' + if not inos.has_key(fd): + inos[fd] = ino + elif inos[fd] != ino: + return 'wrong ' + mod = get_pipe_rw(pid, fd) + if mod != rw: + return 'badmode ' + return None def check_pipe_n(pid, fd): - ino = get_pipe_ino(pid, fd) - if ino == None: - return None - else: - return 'present ' + ino = get_pipe_ino(pid, fd) + if ino == None: + return None + else: + return 'present ' def check_pipe_end(kids, fd, comb, rw, inos): - t_nr = 0 - for t_pid in kids: - if comb & (1 << t_nr): - res = check_pipe_y(t_pid, fd, rw, inos) - else: - res = check_pipe_n(t_pid, fd) - if res != None: - return res + 'kid(%d)' % t_nr - t_nr += 1 - return None + t_nr = 0 + for t_pid in kids: + if comb & (1 << t_nr): + res = check_pipe_y(t_pid, fd, rw, inos) + else: + res = check_pipe_n(t_pid, fd) + if res != None: + return res + 'kid(%d)' % t_nr + t_nr += 1 + return None def check_pipe(kids, fds, comb, inos): - for e in (0, 1): # 0 == R, 1 == W, see get_pipe_rw() - res = check_pipe_end(kids, fds[e], comb[e], e, inos) - if res != None: - return res + 'end(%d)' % e - return None + for e in (0, 1): # 0 == R, 1 == W, see get_pipe_rw() + res = check_pipe_end(kids, fds[e], comb[e], e, inos) + if res != None: + return res + 'end(%d)' % e + return None + def check_pipes(kids, pipes, comb): - # Kids contain pids - # Pipes contain pipe FDs - # Comb contain list of pairs of bits for RW ends - p_nr = 0 - p_inos = {} - for p_fds in pipes: - res = check_pipe(kids, p_fds, comb[p_nr], p_inos) - if res != None: - return res + 'pipe(%d)' % p_nr - p_nr += 1 + # Kids contain pids + # Pipes contain pipe FDs + # Comb contain list of pairs of bits for RW ends + p_nr = 0 + p_inos = {} + for p_fds in pipes: + res = check_pipe(kids, p_fds, comb[p_nr], p_inos) + if res != None: + return res + 'pipe(%d)' % p_nr + p_nr += 1 - return None + return None # Run by test main process. It opens pipes, then forks kids that @@ -134,128 +136,134 @@ def check_pipes(kids, pipes, comb): # and waits for a signal (unix socket message) to start checking # the kids' FD tables. def make_comb(comb, opts, status_pipe): - print('\tMake pipes') - # 1st -- make needed pipes - pipes = [] - for p in range(0, opts.pipes): - pipes.append(os.pipe()) - - # Fork the kids that'll make pipes - kc_pipe = os.pipe() - kids = [] - for t in range(0, opts.tasks): - pid = os.fork() - if pid == 0: - os.close(status_pipe) - os.close(kc_pipe[0]) - make_pipes(t, opts.pipes, pipes, comb, kc_pipe[1]) - sys.exit(1) - kids.append(pid) - - os.close(kc_pipe[1]) - for p in pipes: - os.close(p[0]) - os.close(p[1]) - - # Wait for kids to get ready - k_res = '' - while True: - v = os.read(kc_pipe[0], 16) - if v == '': - break - k_res += v - os.close(kc_pipe[0]) - - ex_code = 1 - if k_res == '0' * opts.tasks: - print('\tWait for C/R') - cmd_sk = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM, 0) - cmd_sk.bind('\0CRIUPCSK') - - # Kids are ready, so is socket for kicking us. Notify the - # parent task that we are good to go. - os.write(status_pipe, '0') - os.close(status_pipe) - v = cmd_sk.recv(16) - if v == '0': - print('\tCheck pipes') - res = check_pipes(kids, pipes, comb) - if res == None: - ex_code = 0 - else: - print('\tFAIL %s' % res) - - # Just kill kids, all checks are done by us, we don't need'em any more - for t in kids: - os.kill(t, signal.SIGKILL) - os.waitpid(t, 0) - - return ex_code + print('\tMake pipes') + # 1st -- make needed pipes + pipes = [] + for p in range(0, opts.pipes): + pipes.append(os.pipe()) + + # Fork the kids that'll make pipes + kc_pipe = os.pipe() + kids = [] + for t in range(0, opts.tasks): + pid = os.fork() + if pid == 0: + os.close(status_pipe) + os.close(kc_pipe[0]) + make_pipes(t, opts.pipes, pipes, comb, kc_pipe[1]) + sys.exit(1) + kids.append(pid) + + os.close(kc_pipe[1]) + for p in pipes: + os.close(p[0]) + os.close(p[1]) + + # Wait for kids to get ready + k_res = '' + while True: + v = os.read(kc_pipe[0], 16) + if v == '': + break + k_res += v + os.close(kc_pipe[0]) + + ex_code = 1 + if k_res == '0' * opts.tasks: + print('\tWait for C/R') + cmd_sk = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM, 0) + cmd_sk.bind('\0CRIUPCSK') + + # Kids are ready, so is socket for kicking us. Notify the + # parent task that we are good to go. + os.write(status_pipe, '0') + os.close(status_pipe) + v = cmd_sk.recv(16) + if v == '0': + print('\tCheck pipes') + res = check_pipes(kids, pipes, comb) + if res == None: + ex_code = 0 + else: + print('\tFAIL %s' % res) + + # Just kill kids, all checks are done by us, we don't need'em any more + for t in kids: + os.kill(t, signal.SIGKILL) + os.waitpid(t, 0) + + return ex_code def cr_test(pid): - print('C/R test') - img_dir = 'pimg_%d' % pid - try: - os.mkdir(img_dir) - subprocess.check_call([criu_bin, 'dump', '-t', '%d' % pid, '-D', img_dir, '-o', 'dump.log', '-v4', '-j']) - except: - print('`- dump fail') - return False - - try: - os.waitpid(pid, 0) - subprocess.check_call([criu_bin, 'restore', '-D', img_dir, '-o', 'rst.log', '-v4', '-j', '-d', '-S']) - except: - print('`- restore fail') - return False - - return True + print('C/R test') + img_dir = 'pimg_%d' % pid + try: + os.mkdir(img_dir) + subprocess.check_call([ + criu_bin, 'dump', '-t', + '%d' % pid, '-D', img_dir, '-o', 'dump.log', '-v4', '-j' + ]) + except: + print('`- dump fail') + return False + + try: + os.waitpid(pid, 0) + subprocess.check_call([ + criu_bin, 'restore', '-D', img_dir, '-o', 'rst.log', '-v4', '-j', + '-d', '-S' + ]) + except: + print('`- restore fail') + return False + + return True def run(comb, opts): - print('Checking %r' % comb) - cpipe = os.pipe() - pid = os.fork() - if pid == 0: - os.close(cpipe[0]) - ret = make_comb(comb, opts, cpipe[1]) - sys.exit(ret) - - # Wait for the main process to get ready - os.close(cpipe[1]) - res = os.read(cpipe[0], 16) - os.close(cpipe[0]) - - if res == '0': - res = cr_test(pid) - - print('Wake up test') - s = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM, 0) - if res: - res = '0' - else: - res = 'X' - try: - # Kick the test to check its state - s.sendto(res, '\0CRIUPCSK') - except: - # Restore might have failed or smth else happened - os.kill(pid, signal.SIGKILL) - s.close() - - # Wait for the guy to exit and get the result (PASS/FAIL) - p, st = os.waitpid(pid, 0) - if os.WIFEXITED(st): - st = os.WEXITSTATUS(st) - - print('Done (%d, pid == %d)' % (st, pid)) - return st == 0 + print('Checking %r' % comb) + cpipe = os.pipe() + pid = os.fork() + if pid == 0: + os.close(cpipe[0]) + ret = make_comb(comb, opts, cpipe[1]) + sys.exit(ret) + + # Wait for the main process to get ready + os.close(cpipe[1]) + res = os.read(cpipe[0], 16) + os.close(cpipe[0]) + + if res == '0': + res = cr_test(pid) + + print('Wake up test') + s = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM, 0) + if res: + res = '0' + else: + res = 'X' + try: + # Kick the test to check its state + s.sendto(res, '\0CRIUPCSK') + except: + # Restore might have failed or smth else happened + os.kill(pid, signal.SIGKILL) + s.close() + + # Wait for the guy to exit and get the result (PASS/FAIL) + p, st = os.waitpid(pid, 0) + if os.WIFEXITED(st): + st = os.WEXITSTATUS(st) + + print('Done (%d, pid == %d)' % (st, pid)) + return st == 0 p = argparse.ArgumentParser("CRIU test suite") -p.add_argument("--tasks", help = "Number of tasks", default = '2') -p.add_argument("--pipes", help = "Number of pipes", default = '2') +p.add_argument("--tasks", help="Number of tasks", default='2') +p.add_argument("--pipes", help="Number of pipes", default='2') opts = p.parse_args() opts.tasks = int(opts.tasks) opts.pipes = int(opts.pipes) @@ -263,8 +271,8 @@ def run(comb, opts): pipe_combs = mix(opts.tasks, opts.pipes) for comb in pipe_combs: - if not run(comb, opts): - print('FAIL') - break + if not run(comb, opts): + print('FAIL') + break else: - print('PASS') + print('PASS') diff --git a/test/exhaustive/unix.py b/test/exhaustive/unix.py index 41053bd0d..98dbbb7b0 100755 --- a/test/exhaustive/unix.py +++ b/test/exhaustive/unix.py @@ -9,11 +9,11 @@ import fcntl import stat -criu_bin='../../criu/criu' +criu_bin = '../../criu/criu' sk_type_s = { - socket.SOCK_STREAM: "S", - socket.SOCK_DGRAM: "D", + socket.SOCK_STREAM: "S", + socket.SOCK_DGRAM: "D", } # Actions that can be done by test. Actions are not only syscall @@ -25,721 +25,739 @@ # - do() method, that actually does what's required # - show() method to return the string description of what's done + def mk_socket(st, typ): - st.sk_id += 1 - sk = sock(st.sk_id, typ) - st.add_socket(sk) - return sk + st.sk_id += 1 + sk = sock(st.sk_id, typ) + st.add_socket(sk) + return sk + class act_socket: - def __init__(self, typ): - self.typ = typ + def __init__(self, typ): + self.typ = typ - def act(self, st): - sk = mk_socket(st, self.typ) - self.sk_id = sk.sk_id + def act(self, st): + sk = mk_socket(st, self.typ) + self.sk_id = sk.sk_id - def do(self, st): - sk = socket.socket(socket.AF_UNIX, self.typ, 0) - st.real_sockets[self.sk_id] = sk + def do(self, st): + sk = socket.socket(socket.AF_UNIX, self.typ, 0) + st.real_sockets[self.sk_id] = sk - def show(self): - return 'socket(%s) = %d' % (sk_type_s[self.typ], self.sk_id) + def show(self): + return 'socket(%s) = %d' % (sk_type_s[self.typ], self.sk_id) class act_close: - def __init__(self, sk_id): - self.sk_id = sk_id + def __init__(self, sk_id): + self.sk_id = sk_id - def act(self, st): - sk = st.get_socket(self.sk_id) - st.del_socket(sk) - for ic in sk.icons: - sk = st.get_socket(ic) - st.del_socket(sk) + def act(self, st): + sk = st.get_socket(self.sk_id) + st.del_socket(sk) + for ic in sk.icons: + sk = st.get_socket(ic) + st.del_socket(sk) - def do(self, st): - sk = st.real_sockets.pop(self.sk_id) - sk.close() + def do(self, st): + sk = st.real_sockets.pop(self.sk_id) + sk.close() - def show(self): - return 'close(%d)' % self.sk_id + def show(self): + return 'close(%d)' % self.sk_id class act_listen: - def __init__(self, sk_id): - self.sk_id = sk_id + def __init__(self, sk_id): + self.sk_id = sk_id - def act(self, st): - sk = st.get_socket(self.sk_id) - sk.listen = True + def act(self, st): + sk = st.get_socket(self.sk_id) + sk.listen = True - def do(self, st): - sk = st.real_sockets[self.sk_id] - sk.listen(10) + def do(self, st): + sk = st.real_sockets[self.sk_id] + sk.listen(10) - def show(self): - return 'listen(%d)' % self.sk_id + def show(self): + return 'listen(%d)' % self.sk_id class act_bind: - def __init__(self, sk_id, name_id): - self.sk_id = sk_id - self.name_id = name_id + def __init__(self, sk_id, name_id): + self.sk_id = sk_id + self.name_id = name_id - def act(self, st): - sk = st.get_socket(self.sk_id) - sk.name = self.name_id + def act(self, st): + sk = st.get_socket(self.sk_id) + sk.name = self.name_id - def do(self, st): - sk = st.real_sockets[self.sk_id] - sk.bind(sock.real_name_for(self.name_id)) + def do(self, st): + sk = st.real_sockets[self.sk_id] + sk.bind(sock.real_name_for(self.name_id)) - def show(self): - return 'bind(%d, $name-%d)' % (self.sk_id, self.name_id) + def show(self): + return 'bind(%d, $name-%d)' % (self.sk_id, self.name_id) class act_connect: - def __init__(self, sk_id, listen_sk_id): - self.sk_id = sk_id - self.lsk_id = listen_sk_id - - def act(self, st): - sk = st.get_socket(self.sk_id) - if st.sk_type == socket.SOCK_STREAM: - lsk = st.get_socket(self.lsk_id) - psk = mk_socket(st, socket.SOCK_STREAM) - psk.visible = False - sk.peer = psk.sk_id - psk.peer = sk.sk_id - psk.name = lsk.name - lsk.icons.append(psk.sk_id) - lsk.icons_seq += 1 - else: - sk.peer = self.lsk_id - psk = st.get_socket(self.lsk_id) - psk.icons_seq += 1 - - def do(self, st): - sk = st.real_sockets[self.sk_id] - sk.connect(sock.real_name_for(self.lsk_id)) - - def show(self): - return 'connect(%d, $name-%d)' % (self.sk_id, self.lsk_id) + def __init__(self, sk_id, listen_sk_id): + self.sk_id = sk_id + self.lsk_id = listen_sk_id + + def act(self, st): + sk = st.get_socket(self.sk_id) + if st.sk_type == socket.SOCK_STREAM: + lsk = st.get_socket(self.lsk_id) + psk = mk_socket(st, socket.SOCK_STREAM) + psk.visible = False + sk.peer = psk.sk_id + psk.peer = sk.sk_id + psk.name = lsk.name + lsk.icons.append(psk.sk_id) + lsk.icons_seq += 1 + else: + sk.peer = self.lsk_id + psk = st.get_socket(self.lsk_id) + psk.icons_seq += 1 + + def do(self, st): + sk = st.real_sockets[self.sk_id] + sk.connect(sock.real_name_for(self.lsk_id)) + + def show(self): + return 'connect(%d, $name-%d)' % (self.sk_id, self.lsk_id) class act_accept: - def __init__(self, sk_id): - self.sk_id = sk_id + def __init__(self, sk_id): + self.sk_id = sk_id - def act(self, st): - lsk = st.get_socket(self.sk_id) - iid = lsk.icons.pop(0) - nsk = st.get_socket(iid) - nsk.visible = True - self.nsk_id = nsk.sk_id + def act(self, st): + lsk = st.get_socket(self.sk_id) + iid = lsk.icons.pop(0) + nsk = st.get_socket(iid) + nsk.visible = True + self.nsk_id = nsk.sk_id - def do(self, st): - sk = st.real_sockets[self.sk_id] - nsk, ai = sk.accept() - if self.nsk_id in st.real_sockets: - raise Exception("SK ID conflict") - st.real_sockets[self.nsk_id] = nsk + def do(self, st): + sk = st.real_sockets[self.sk_id] + nsk, ai = sk.accept() + if self.nsk_id in st.real_sockets: + raise Exception("SK ID conflict") + st.real_sockets[self.nsk_id] = nsk - def show(self): - return 'accept(%d) = %d' % (self.sk_id, self.nsk_id) + def show(self): + return 'accept(%d) = %d' % (self.sk_id, self.nsk_id) class act_sendmsg: - def __init__(self, sk_id, to_id): - self.sk_id = sk_id - self.to_id = to_id - self.direct_send = None - - def act(self, st): - sk = st.get_socket(self.sk_id) - msg = (sk.sk_id, sk.outseq) - self.msg_id = sk.outseq - sk.outseq += 1 - psk = st.get_socket(self.to_id) - psk.inqueue.append(msg) - self.direct_send = (sk.peer == psk.sk_id) - - def do(self, st): - sk = st.real_sockets[self.sk_id] - msgv = act_sendmsg.msgval(self.msg_id) - if self.direct_send: - sk.send(msgv) - else: - sk.sendto(msgv, sock.real_name_for(self.to_id)) - - def show(self): - return 'send(%d, %d, $message-%d)' % (self.sk_id, self.to_id, self.msg_id) - - @staticmethod - def msgval(msgid, pref = ''): - return '%sMSG%d' % (pref, msgid) + def __init__(self, sk_id, to_id): + self.sk_id = sk_id + self.to_id = to_id + self.direct_send = None + + def act(self, st): + sk = st.get_socket(self.sk_id) + msg = (sk.sk_id, sk.outseq) + self.msg_id = sk.outseq + sk.outseq += 1 + psk = st.get_socket(self.to_id) + psk.inqueue.append(msg) + self.direct_send = (sk.peer == psk.sk_id) + + def do(self, st): + sk = st.real_sockets[self.sk_id] + msgv = act_sendmsg.msgval(self.msg_id) + if self.direct_send: + sk.send(msgv) + else: + sk.sendto(msgv, sock.real_name_for(self.to_id)) + + def show(self): + return 'send(%d, %d, $message-%d)' % (self.sk_id, self.to_id, + self.msg_id) + + @staticmethod + def msgval(msgid, pref=''): + return '%sMSG%d' % (pref, msgid) + # # Description of a socket # class sock: - def __init__(self, sk_id, sock_type): - # ID of a socket. Since states and sockets are cloned - # while we scan the tree of states the only valid way - # to address a socket is to find one by ID. - self.sk_id = sk_id - # The socket.SOCK_FOO value - self.sk_type = sock_type - # Sockets that haven't yet been accept()-ed are in the - # state, but user cannot operate on them. Also this - # invisibility contributes to state description since - # connection to not accepted socket is not the same - # as connection to accepted one. - self.visible = True - # The listen() was called. - self.listen = False - # The bind() was called. Also set by accept(), the name - # inherits from listener. - self.name = None - # The connect() was called. Set on two sockets when the - # connect() is called. - self.peer = None - # Progress on accepting connections. Used to check when - # it's OK to close the socket (see comment below). - self.icons_seq = 0 - # List of IDs of sockets that can be accept()-ed - self.icons = [] - # Number to generate message contents. - self.outseq = 0 - # Incoming queue of messages. - self.inqueue = [] - - def clone(self): - sk = sock(self.sk_id, self.sk_type) - sk.visible = self.visible - sk.listen = self.listen - sk.name = self.name - sk.peer = self.peer - sk.icons_seq = self.icons_seq - sk.icons = list(self.icons) - sk.outseq = self.outseq - sk.inqueue = list(self.inqueue) - return sk - - def get_actions(self, st): - if not self.visible: - return [] - - if st.sk_type == socket.SOCK_STREAM: - return self.get_stream_actions(st) - else: - return self.get_dgram_actions(st) - - def get_send_action(self, to, st): - # However, if peer has a message from us at - # the queue tail, sending a new one doesn't - # really make sense - want_msg = True - if len(to.inqueue) != 0: - lmsg = to.inqueue[-1] - if lmsg[0] == self.sk_id: - want_msg = False - if want_msg: - return [ act_sendmsg(self.sk_id, to.sk_id) ] - else: - return [ ] - - def get_stream_actions(self, st): - act_list = [] - - # Any socket can be closed, but closing a socket - # that hasn't contributed to some new states is - # just waste of time, so we close only connected - # sockets or listeners that has at least one - # incoming connection pendig or served - - if self.listen: - if self.icons: - act_list.append(act_accept(self.sk_id)) - if self.icons_seq: - act_list.append(act_close(self.sk_id)) - elif self.peer: - act_list.append(act_close(self.sk_id)) - # Connected sockets can send and receive messages - # But receiving seem not to produce any new states, - # so only sending - # Also sending to a closed socket doesn't work - psk = st.get_socket(self.peer, True) - if psk: - act_list += self.get_send_action(psk, st) - else: - for psk in st.sockets: - if psk.listen and psk.name: - act_list.append(act_connect(self.sk_id, psk.sk_id)) - - # Listen on not-bound socket is prohibited as - # well as binding a listening socket - if not self.name: - # TODO: support for file paths (see real_name_for) - # TODO: these names can overlap each other - act_list.append(act_bind(self.sk_id, self.sk_id)) - else: - act_list.append(act_listen(self.sk_id)) - - return act_list - - def get_dgram_actions(self, st): - act_list = [] - - # Dgram socket can bind at any time - if not self.name: - act_list.append(act_bind(self.sk_id, self.sk_id)) - - # Can connect to peer-less sockets - for psk in st.sockets: - if psk == self: - continue - if psk.peer != None and psk.peer != self.sk_id: - # Peer by someone else, can do nothing - continue - - # Peer-less psk or having us as peer - # We can connect to or send messages - if psk.name and self.peer != psk.sk_id: - act_list.append(act_connect(self.sk_id, psk.sk_id)) - - if psk.name or self.peer == psk.sk_id: - act_list += self.get_send_action(psk, st) - - if self.outseq != 0 or self.icons_seq != 0: - act_list.append(act_close(self.sk_id)) - - return act_list - - @staticmethod - def name_of(sk): - if not sk: - return 'X' - elif not sk.visible: - return 'H' - elif sk.name: - return 'B' - else: - return 'A' - - @staticmethod - def real_name_for(sk_id): - return "\0" + "CRSK%d" % sk_id - - # The describe() generates a string that represents - # a state of a socket. Called by state.describe(), see - # comment there about what description is. - def describe(self, st): - dsc = '%s' % sk_type_s[self.sk_type] - dsc += sock.name_of(self) - - if self.listen: - dsc += 'L' - if self.peer: - psk = st.get_socket(self.peer, True) - dsc += '-C%s' % sock.name_of(psk) - if self.icons: - i_dsc = '' - for c in self.icons: - psk = st.get_socket(c) - psk = st.get_socket(psk.peer, True) - i_dsc += sock.name_of(psk) - dsc += '-I%s' % i_dsc - if self.inqueue: - froms = set() - for m in self.inqueue: - froms.add(m[0]) - q_dsc = '' - for f in froms: - fsk = st.get_socket(f, True) - q_dsc += sock.name_of(fsk) - dsc += '-M%s' % q_dsc - return dsc + def __init__(self, sk_id, sock_type): + # ID of a socket. Since states and sockets are cloned + # while we scan the tree of states the only valid way + # to address a socket is to find one by ID. + self.sk_id = sk_id + # The socket.SOCK_FOO value + self.sk_type = sock_type + # Sockets that haven't yet been accept()-ed are in the + # state, but user cannot operate on them. Also this + # invisibility contributes to state description since + # connection to not accepted socket is not the same + # as connection to accepted one. + self.visible = True + # The listen() was called. + self.listen = False + # The bind() was called. Also set by accept(), the name + # inherits from listener. + self.name = None + # The connect() was called. Set on two sockets when the + # connect() is called. + self.peer = None + # Progress on accepting connections. Used to check when + # it's OK to close the socket (see comment below). + self.icons_seq = 0 + # List of IDs of sockets that can be accept()-ed + self.icons = [] + # Number to generate message contents. + self.outseq = 0 + # Incoming queue of messages. + self.inqueue = [] + + def clone(self): + sk = sock(self.sk_id, self.sk_type) + sk.visible = self.visible + sk.listen = self.listen + sk.name = self.name + sk.peer = self.peer + sk.icons_seq = self.icons_seq + sk.icons = list(self.icons) + sk.outseq = self.outseq + sk.inqueue = list(self.inqueue) + return sk + + def get_actions(self, st): + if not self.visible: + return [] + + if st.sk_type == socket.SOCK_STREAM: + return self.get_stream_actions(st) + else: + return self.get_dgram_actions(st) + + def get_send_action(self, to, st): + # However, if peer has a message from us at + # the queue tail, sending a new one doesn't + # really make sense + want_msg = True + if len(to.inqueue) != 0: + lmsg = to.inqueue[-1] + if lmsg[0] == self.sk_id: + want_msg = False + if want_msg: + return [act_sendmsg(self.sk_id, to.sk_id)] + else: + return [] + + def get_stream_actions(self, st): + act_list = [] + + # Any socket can be closed, but closing a socket + # that hasn't contributed to some new states is + # just waste of time, so we close only connected + # sockets or listeners that has at least one + # incoming connection pendig or served + + if self.listen: + if self.icons: + act_list.append(act_accept(self.sk_id)) + if self.icons_seq: + act_list.append(act_close(self.sk_id)) + elif self.peer: + act_list.append(act_close(self.sk_id)) + # Connected sockets can send and receive messages + # But receiving seem not to produce any new states, + # so only sending + # Also sending to a closed socket doesn't work + psk = st.get_socket(self.peer, True) + if psk: + act_list += self.get_send_action(psk, st) + else: + for psk in st.sockets: + if psk.listen and psk.name: + act_list.append(act_connect(self.sk_id, psk.sk_id)) + + # Listen on not-bound socket is prohibited as + # well as binding a listening socket + if not self.name: + # TODO: support for file paths (see real_name_for) + # TODO: these names can overlap each other + act_list.append(act_bind(self.sk_id, self.sk_id)) + else: + act_list.append(act_listen(self.sk_id)) + + return act_list + + def get_dgram_actions(self, st): + act_list = [] + + # Dgram socket can bind at any time + if not self.name: + act_list.append(act_bind(self.sk_id, self.sk_id)) + + # Can connect to peer-less sockets + for psk in st.sockets: + if psk == self: + continue + if psk.peer != None and psk.peer != self.sk_id: + # Peer by someone else, can do nothing + continue + + # Peer-less psk or having us as peer + # We can connect to or send messages + if psk.name and self.peer != psk.sk_id: + act_list.append(act_connect(self.sk_id, psk.sk_id)) + + if psk.name or self.peer == psk.sk_id: + act_list += self.get_send_action(psk, st) + + if self.outseq != 0 or self.icons_seq != 0: + act_list.append(act_close(self.sk_id)) + + return act_list + + @staticmethod + def name_of(sk): + if not sk: + return 'X' + elif not sk.visible: + return 'H' + elif sk.name: + return 'B' + else: + return 'A' + + @staticmethod + def real_name_for(sk_id): + return "\0" + "CRSK%d" % sk_id + + # The describe() generates a string that represents + # a state of a socket. Called by state.describe(), see + # comment there about what description is. + def describe(self, st): + dsc = '%s' % sk_type_s[self.sk_type] + dsc += sock.name_of(self) + + if self.listen: + dsc += 'L' + if self.peer: + psk = st.get_socket(self.peer, True) + dsc += '-C%s' % sock.name_of(psk) + if self.icons: + i_dsc = '' + for c in self.icons: + psk = st.get_socket(c) + psk = st.get_socket(psk.peer, True) + i_dsc += sock.name_of(psk) + dsc += '-I%s' % i_dsc + if self.inqueue: + froms = set() + for m in self.inqueue: + froms.add(m[0]) + q_dsc = '' + for f in froms: + fsk = st.get_socket(f, True) + q_dsc += sock.name_of(fsk) + dsc += '-M%s' % q_dsc + return dsc class state: - def __init__(self, max_sockets, sk_type): - self.sockets = [] - self.sk_id = 0 - self.steps = [] - self.real_sockets = {} - self.sockets_left = max_sockets - self.sk_type = sk_type - - def add_socket(self, sk): - self.sockets.append(sk) - - def del_socket(self, sk): - self.sockets.remove(sk) - - def get_socket(self, sk_id, can_be_null = False): - for sk in self.sockets: - if sk.sk_id == sk_id: - return sk - - if not can_be_null: - raise Exception("%d socket not in list" % sk_id) - - return None - - def get_actions(self): - act_list = [] - - # Any socket in the state we can change it - for sk in self.sockets: - act_list += sk.get_actions(self) - - if self.sockets_left > 0: - act_list.append(act_socket(self.sk_type)) - self.sockets_left -= 1 - - return act_list - - def clone(self): - nst = state(self.sockets_left, self.sk_type) - for sk in self.sockets: - nst.sockets.append(sk.clone()) - nst.sk_id = self.sk_id - nst.steps = list(self.steps) - return nst - - # Generates textual description of a state. Different states - # may have same descriptions, e.g. if we have two sockets and - # only one of them is in listen state, we don't care which - # one in which. At the same time really different states - # shouldn't map to the same string. - def describe(self): - sks = [x.describe(self) for x in self.sockets] - sks = sorted(sks) - return '_'.join(sks) + def __init__(self, max_sockets, sk_type): + self.sockets = [] + self.sk_id = 0 + self.steps = [] + self.real_sockets = {} + self.sockets_left = max_sockets + self.sk_type = sk_type + + def add_socket(self, sk): + self.sockets.append(sk) + + def del_socket(self, sk): + self.sockets.remove(sk) + + def get_socket(self, sk_id, can_be_null=False): + for sk in self.sockets: + if sk.sk_id == sk_id: + return sk + + if not can_be_null: + raise Exception("%d socket not in list" % sk_id) + + return None + + def get_actions(self): + act_list = [] + + # Any socket in the state we can change it + for sk in self.sockets: + act_list += sk.get_actions(self) + + if self.sockets_left > 0: + act_list.append(act_socket(self.sk_type)) + self.sockets_left -= 1 + + return act_list + + def clone(self): + nst = state(self.sockets_left, self.sk_type) + for sk in self.sockets: + nst.sockets.append(sk.clone()) + nst.sk_id = self.sk_id + nst.steps = list(self.steps) + return nst + + # Generates textual description of a state. Different states + # may have same descriptions, e.g. if we have two sockets and + # only one of them is in listen state, we don't care which + # one in which. At the same time really different states + # shouldn't map to the same string. + def describe(self): + sks = [x.describe(self) for x in self.sockets] + sks = sorted(sks) + return '_'.join(sks) def set_nonblock(sk): - fd = sk.fileno() - flags = fcntl.fcntl(fd, fcntl.F_GETFL) - fcntl.fcntl(fd, fcntl.F_SETFL, flags | os.O_NONBLOCK) - -CHK_FAIL_UNKNOWN = 10 -CHK_FAIL_SOCKET = 11 -CHK_FAIL_STAT = 12 -CHK_FAIL_LISTEN = 13 -CHK_FAIL_NAME = 14 -CHK_FAIL_ACCEPT = 15 -CHK_FAIL_RECV_0 = 16 -CHK_FAIL_RECV_MIX = 17 -CHK_FAIL_CONNECT = 18 -CHK_FAIL_CONNECT2 = 19 -CHK_FAIL_KILLED = 20 -CHK_FAIL_DUMP = 21 -CHK_FAIL_RESTORE = 22 - -CHK_PASS = 42 + fd = sk.fileno() + flags = fcntl.fcntl(fd, fcntl.F_GETFL) + fcntl.fcntl(fd, fcntl.F_SETFL, flags | os.O_NONBLOCK) + + +CHK_FAIL_UNKNOWN = 10 +CHK_FAIL_SOCKET = 11 +CHK_FAIL_STAT = 12 +CHK_FAIL_LISTEN = 13 +CHK_FAIL_NAME = 14 +CHK_FAIL_ACCEPT = 15 +CHK_FAIL_RECV_0 = 16 +CHK_FAIL_RECV_MIX = 17 +CHK_FAIL_CONNECT = 18 +CHK_FAIL_CONNECT2 = 19 +CHK_FAIL_KILLED = 20 +CHK_FAIL_DUMP = 21 +CHK_FAIL_RESTORE = 22 + +CHK_PASS = 42 fail_desc = { - CHK_FAIL_UNKNOWN: 'Aliens invaded the test', - CHK_FAIL_LISTEN: 'Listen state lost on restore', - CHK_FAIL_NAME: 'Name lost on restore', - CHK_FAIL_ACCEPT: 'Incoming connection lost on restore', - CHK_FAIL_RECV_0: 'Message lost on restore', - CHK_FAIL_RECV_MIX: 'Message misorder on restore', - CHK_FAIL_CONNECT: 'Connectivity broken on restore', - CHK_FAIL_CONNECT2: 'Connectivity broken the hard way on restore', - CHK_FAIL_KILLED: 'Test process died unexpectedly', - CHK_FAIL_DUMP: 'Cannot dump', - CHK_FAIL_RESTORE: 'Cannot restore', + CHK_FAIL_UNKNOWN: 'Aliens invaded the test', + CHK_FAIL_LISTEN: 'Listen state lost on restore', + CHK_FAIL_NAME: 'Name lost on restore', + CHK_FAIL_ACCEPT: 'Incoming connection lost on restore', + CHK_FAIL_RECV_0: 'Message lost on restore', + CHK_FAIL_RECV_MIX: 'Message misorder on restore', + CHK_FAIL_CONNECT: 'Connectivity broken on restore', + CHK_FAIL_CONNECT2: 'Connectivity broken the hard way on restore', + CHK_FAIL_KILLED: 'Test process died unexpectedly', + CHK_FAIL_DUMP: 'Cannot dump', + CHK_FAIL_RESTORE: 'Cannot restore', } + def chk_real_state(st): - # Before enything else -- check that we still have - # all the sockets at hands - for sk in st.sockets: - if not sk.visible: - continue - - # In theory we can have key-not-found exception here, - # but this has nothing to do with sockets restore, - # since it's just bytes in memory, so ... we assume - # that we have object here and just check for it in - # the fdtable - rsk = st.real_sockets[sk.sk_id] - try: - s_st = os.fstat(rsk.fileno()) - except: - print('FAIL: Socket %d lost' % sk.sk_id) - return CHK_FAIL_SOCKET - if not stat.S_ISSOCK(s_st.st_mode): - print('FAIL: Not a socket %d at %d' % (sk.sk_id, rsk.fileno())) - return CHK_FAIL_STAT - - # First -- check the listen states and names - for sk in st.sockets: - if not sk.visible: - continue - - rsk = st.real_sockets[sk.sk_id] - r_listen = rsk.getsockopt(socket.SOL_SOCKET, socket.SO_ACCEPTCONN) - if (sk.listen and r_listen == 0) or (not sk.listen and r_listen == 1): - print("FAIL: Socket %d listen %d, expected %d" - % (sk.sk_id, r_listen, sk.listen and 1 or 0)) - return CHK_FAIL_LISTEN - - if sk.name: - r_name = rsk.getsockname() - w_name = sock.real_name_for(sk.name) - if r_name != w_name: - print('FAIL: Socket %d name mismatch [%s], want [%s]' - % (sk.sk_id, r_name, w_name)) - return CHK_FAIL_NAME - - # Second -- check (accept) pending connections - for sk in st.sockets: - if not sk.listen: - continue - - rsk = st.real_sockets[sk.sk_id] - set_nonblock(rsk) - - while sk.icons: - # Do act_accept to change the state properly - # and not write the code twice - acc = act_accept(sk.sk_id) - acc.act(st) - try: - acc.do(st) - except: - print('FAIL: Cannot accept pending connection for %d' % sk.sk_id) - return CHK_FAIL_ACCEPT - - print(' `- did %s' % acc.show()) - - # Third -- check inqueues - for sk in st.sockets: - if not sk.inqueue: - continue - - rsk = st.real_sockets[sk.sk_id] - set_nonblock(rsk) - - while sk.inqueue: - msg = sk.inqueue.pop(0) - try: - r_msg, m_from = rsk.recvfrom(128) - except: - print('FAIL: No message in queue for %d' % sk.sk_id) - return CHK_FAIL_RECV_0 - - w_msg = act_sendmsg.msgval(msg[1]) - if r_msg != w_msg: - print('FAIL: Message misorder: %s want %s (from %d)' - %(r_msg, w_msg, msg[0])) - return CHK_FAIL_RECV_MIX - - # TODO -- check sender - print(' `- recvd %d.%d msg %s -> %d' - % (msg[0], msg[1], m_from, sk.sk_id)) - - # Finally, after all sockets are visible and all inqueues are - # drained -- check the sockets connectivity - for sk in st.sockets: - if not sk.peer: - continue - - # Closed connection with one peer alive. Cannot check. - if not sk.peer in st.real_sockets: - continue - - rsk = st.real_sockets[sk.sk_id] - psk = st.real_sockets[sk.peer] - set_nonblock(psk) - msgv = act_sendmsg.msgval(3 * sk.sk_id + 5 * sk.peer, 'C') # just random - - try: - rsk.send(msgv) - rmsg = psk.recv(128) - except: - print('FAIL: Connectivity %d -> %d lost' % (sk.sk_id, sk.peer)) - return CHK_FAIL_CONNECT - - # If sockets are not connected the recv above - # would generate exception and the check would - # fail. But just in case we've screwed the queues - # the hard way -- also check for the message being - # delivered for real - if rmsg != msgv: - print('FAIL: Connectivity %d -> %d not verified' - % (sk.sk_id, sk.peer)) - return CHK_FAIL_CONNECT2 - - print(' `- checked %d -> %d with %s' % (sk.sk_id, sk.peer, msgv)) - - return CHK_PASS + # Before enything else -- check that we still have + # all the sockets at hands + for sk in st.sockets: + if not sk.visible: + continue + + # In theory we can have key-not-found exception here, + # but this has nothing to do with sockets restore, + # since it's just bytes in memory, so ... we assume + # that we have object here and just check for it in + # the fdtable + rsk = st.real_sockets[sk.sk_id] + try: + s_st = os.fstat(rsk.fileno()) + except: + print('FAIL: Socket %d lost' % sk.sk_id) + return CHK_FAIL_SOCKET + if not stat.S_ISSOCK(s_st.st_mode): + print('FAIL: Not a socket %d at %d' % (sk.sk_id, rsk.fileno())) + return CHK_FAIL_STAT + + # First -- check the listen states and names + for sk in st.sockets: + if not sk.visible: + continue + + rsk = st.real_sockets[sk.sk_id] + r_listen = rsk.getsockopt(socket.SOL_SOCKET, socket.SO_ACCEPTCONN) + if (sk.listen and r_listen == 0) or (not sk.listen and r_listen == 1): + print("FAIL: Socket %d listen %d, expected %d" % + (sk.sk_id, r_listen, sk.listen and 1 or 0)) + return CHK_FAIL_LISTEN + + if sk.name: + r_name = rsk.getsockname() + w_name = sock.real_name_for(sk.name) + if r_name != w_name: + print('FAIL: Socket %d name mismatch [%s], want [%s]' % + (sk.sk_id, r_name, w_name)) + return CHK_FAIL_NAME + + # Second -- check (accept) pending connections + for sk in st.sockets: + if not sk.listen: + continue + + rsk = st.real_sockets[sk.sk_id] + set_nonblock(rsk) + + while sk.icons: + # Do act_accept to change the state properly + # and not write the code twice + acc = act_accept(sk.sk_id) + acc.act(st) + try: + acc.do(st) + except: + print('FAIL: Cannot accept pending connection for %d' % + sk.sk_id) + return CHK_FAIL_ACCEPT + + print(' `- did %s' % acc.show()) + + # Third -- check inqueues + for sk in st.sockets: + if not sk.inqueue: + continue + + rsk = st.real_sockets[sk.sk_id] + set_nonblock(rsk) + + while sk.inqueue: + msg = sk.inqueue.pop(0) + try: + r_msg, m_from = rsk.recvfrom(128) + except: + print('FAIL: No message in queue for %d' % sk.sk_id) + return CHK_FAIL_RECV_0 + + w_msg = act_sendmsg.msgval(msg[1]) + if r_msg != w_msg: + print('FAIL: Message misorder: %s want %s (from %d)' % + (r_msg, w_msg, msg[0])) + return CHK_FAIL_RECV_MIX + + # TODO -- check sender + print(' `- recvd %d.%d msg %s -> %d' % + (msg[0], msg[1], m_from, sk.sk_id)) + + # Finally, after all sockets are visible and all inqueues are + # drained -- check the sockets connectivity + for sk in st.sockets: + if not sk.peer: + continue + + # Closed connection with one peer alive. Cannot check. + if not sk.peer in st.real_sockets: + continue + + rsk = st.real_sockets[sk.sk_id] + psk = st.real_sockets[sk.peer] + set_nonblock(psk) + msgv = act_sendmsg.msgval(3 * sk.sk_id + 5 * sk.peer, + 'C') # just random + + try: + rsk.send(msgv) + rmsg = psk.recv(128) + except: + print('FAIL: Connectivity %d -> %d lost' % (sk.sk_id, sk.peer)) + return CHK_FAIL_CONNECT + + # If sockets are not connected the recv above + # would generate exception and the check would + # fail. But just in case we've screwed the queues + # the hard way -- also check for the message being + # delivered for real + if rmsg != msgv: + print('FAIL: Connectivity %d -> %d not verified' % + (sk.sk_id, sk.peer)) + return CHK_FAIL_CONNECT2 + + print(' `- checked %d -> %d with %s' % (sk.sk_id, sk.peer, msgv)) + + return CHK_PASS def chk_state(st, opts): - print("Will check state") - - sigsk_name = "\0" + "CRSIGSKC" - signal_sk = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM, 0) - signal_sk.bind(sigsk_name) - - # FIXME Ideally call to criu should be performed by the run_state's - # pid!=0 branch, but for simplicity we fork the kid which has the - # same set of sockets we do, then dump it. Then restore and notify - # via dgram socket to check its state. Current task still has all - # the same sockets :) so we close them not to produce bind() name - # conflicts on restore - - pid = os.fork() - if pid == 0: - msg = signal_sk.recv(64) - ret = chk_real_state(st) - sys.exit(ret) - - signal_sk.close() - for rsk in st.real_sockets.values(): - rsk.close() - - print("`- dump") - img_path = "sti_" + st.describe() - try: - os.mkdir(img_path) - subprocess.check_call([criu_bin, "dump", "-t", "%d" % pid, "-D", img_path, "-v4", "-o", "dump.log", "-j"]) - except: - print("Dump failed") - os.kill(pid, signal.SIGKILL) - return CHK_FAIL_DUMP - - print("`- restore") - try: - os.waitpid(pid, 0) - subprocess.check_call([criu_bin, "restore", "-D", img_path, "-v4", "-o", "rst.log", "-j", "-d", "-S"]) - except: - print("Restore failed") - return CHK_FAIL_RESTORE - - print("`- check") - signal_sk = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM, 0) - try: - signal_sk.sendto('check', sigsk_name) - except: - # Probably the peer has died before us or smth else went wrong - os.kill(pid, signal.SIGKILL) - - wp, status = os.waitpid(pid, 0) - if os.WIFEXITED(status): - status = os.WEXITSTATUS(status) - if status != CHK_PASS: - print("`- exited with %d" % status) - return status - elif os.WIFSIGNALED(status): - status = os.WTERMSIG(status) - print("`- killed with %d" % status) - return CHK_FAIL_KILLED - else: - return CHK_FAIL_UNKNOWN - - return CHK_PASS + print("Will check state") + + sigsk_name = "\0" + "CRSIGSKC" + signal_sk = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM, 0) + signal_sk.bind(sigsk_name) + + # FIXME Ideally call to criu should be performed by the run_state's + # pid!=0 branch, but for simplicity we fork the kid which has the + # same set of sockets we do, then dump it. Then restore and notify + # via dgram socket to check its state. Current task still has all + # the same sockets :) so we close them not to produce bind() name + # conflicts on restore + + pid = os.fork() + if pid == 0: + msg = signal_sk.recv(64) + ret = chk_real_state(st) + sys.exit(ret) + + signal_sk.close() + for rsk in st.real_sockets.values(): + rsk.close() + + print("`- dump") + img_path = "sti_" + st.describe() + try: + os.mkdir(img_path) + subprocess.check_call([ + criu_bin, "dump", "-t", + "%d" % pid, "-D", img_path, "-v4", "-o", "dump.log", "-j" + ]) + except: + print("Dump failed") + os.kill(pid, signal.SIGKILL) + return CHK_FAIL_DUMP + + print("`- restore") + try: + os.waitpid(pid, 0) + subprocess.check_call([ + criu_bin, "restore", "-D", img_path, "-v4", "-o", "rst.log", "-j", + "-d", "-S" + ]) + except: + print("Restore failed") + return CHK_FAIL_RESTORE + + print("`- check") + signal_sk = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM, 0) + try: + signal_sk.sendto('check', sigsk_name) + except: + # Probably the peer has died before us or smth else went wrong + os.kill(pid, signal.SIGKILL) + + wp, status = os.waitpid(pid, 0) + if os.WIFEXITED(status): + status = os.WEXITSTATUS(status) + if status != CHK_PASS: + print("`- exited with %d" % status) + return status + elif os.WIFSIGNALED(status): + status = os.WTERMSIG(status) + print("`- killed with %d" % status) + return CHK_FAIL_KILLED + else: + return CHK_FAIL_UNKNOWN + + return CHK_PASS def run_state(st, opts): - print("Will run state") - pid = os.fork() - if pid != 0: - wpid, status = os.wait() - if os.WIFEXITED(status): - status = os.WEXITSTATUS(status) - elif os.WIFSIGNALED(status): - status = CHK_FAIL_KILLED - else: - status = CHK_FAIL_UNKNOWN - return status - - # Try the states in subprocess so that once - # it exits the created sockets are removed - for step in st.steps: - step.do(st) - - if not opts.run: - ret = chk_state(st, opts) - else: - ret = chk_real_state(st) - - sys.exit(ret) - - -def proceed(st, seen, failed, opts, depth = 0): - desc = st.describe() - if not desc: - pass - elif not desc in seen: - # When scanning the tree we run and try only states that - # differ, but don't stop tree traversal on them. This is - # because sometimes we can get into the already seen state - # using less steps and it's better to proceed as we have - # depth to move forward and generate more states. - seen[desc] = len(st.steps) - print('%s' % desc) - for s in st.steps: - print('\t%s' % s.show()) - - if not opts.gen: - ret = run_state(st, opts) - if ret != CHK_PASS: - failed.add((desc, ret)) - if not opts.keep: - return False - else: - # Don't even proceed with this state if we've already - # seen one but get there with less steps - seen_score = seen[desc] - if len(st.steps) > seen_score: - return True - else: - seen[desc] = len(st.steps) - - if depth >= opts.depth: - return True - - actions = st.get_actions() - for act in actions: - nst = st.clone() - act.act(nst) - nst.steps.append(act) - if not proceed(nst, seen, failed, opts, depth + 1): - return False - - return True + print("Will run state") + pid = os.fork() + if pid != 0: + wpid, status = os.wait() + if os.WIFEXITED(status): + status = os.WEXITSTATUS(status) + elif os.WIFSIGNALED(status): + status = CHK_FAIL_KILLED + else: + status = CHK_FAIL_UNKNOWN + return status + + # Try the states in subprocess so that once + # it exits the created sockets are removed + for step in st.steps: + step.do(st) + + if not opts.run: + ret = chk_state(st, opts) + else: + ret = chk_real_state(st) + + sys.exit(ret) + + +def proceed(st, seen, failed, opts, depth=0): + desc = st.describe() + if not desc: + pass + elif not desc in seen: + # When scanning the tree we run and try only states that + # differ, but don't stop tree traversal on them. This is + # because sometimes we can get into the already seen state + # using less steps and it's better to proceed as we have + # depth to move forward and generate more states. + seen[desc] = len(st.steps) + print('%s' % desc) + for s in st.steps: + print('\t%s' % s.show()) + + if not opts.gen: + ret = run_state(st, opts) + if ret != CHK_PASS: + failed.add((desc, ret)) + if not opts.keep: + return False + else: + # Don't even proceed with this state if we've already + # seen one but get there with less steps + seen_score = seen[desc] + if len(st.steps) > seen_score: + return True + else: + seen[desc] = len(st.steps) + + if depth >= opts.depth: + return True + + actions = st.get_actions() + for act in actions: + nst = st.clone() + act.act(nst) + nst.steps.append(act) + if not proceed(nst, seen, failed, opts, depth + 1): + return False + + return True p = argparse.ArgumentParser("CRIU test suite") -p.add_argument("--depth", help = "Depth of generated tree", default = '8') -p.add_argument("--sockets", help = "Maximum number of sockets", default = '1') -p.add_argument("--dgram", help = "Use SOCK_DGRAM sockets", action = 'store_true') -p.add_argument("--stream", help = "Use SOCK_STREAM sockets", action = 'store_true') -p.add_argument("--gen", help = "Only generate and show states", action = 'store_true') -p.add_argument("--run", help = "Run the states, but don't C/R", action = 'store_true') -p.add_argument("--keep", help = "Don't stop on error", action = 'store_true') +p.add_argument("--depth", help="Depth of generated tree", default='8') +p.add_argument("--sockets", help="Maximum number of sockets", default='1') +p.add_argument("--dgram", help="Use SOCK_DGRAM sockets", action='store_true') +p.add_argument("--stream", help="Use SOCK_STREAM sockets", action='store_true') +p.add_argument("--gen", + help="Only generate and show states", + action='store_true') +p.add_argument("--run", + help="Run the states, but don't C/R", + action='store_true') +p.add_argument("--keep", help="Don't stop on error", action='store_true') opts = p.parse_args() opts.depth = int(opts.depth) # XXX: does it make any sense to mix two types in one go? if opts.stream and opts.dgram: - print('Choose only one type') - sys.exit(1) + print('Choose only one type') + sys.exit(1) if opts.stream: - sk_type = socket.SOCK_STREAM + sk_type = socket.SOCK_STREAM elif opts.dgram: - sk_type = socket.SOCK_DGRAM + sk_type = socket.SOCK_DGRAM else: - print('Choose some type') - sys.exit(1) + print('Choose some type') + sys.exit(1) st = state(int(opts.sockets), sk_type) seen = {} @@ -747,8 +765,9 @@ def proceed(st, seen, failed, opts, depth = 0): proceed(st, seen, failed, opts) if len(failed) == 0: - print('PASS (%d states)' % len(seen)) + print('PASS (%d states)' % len(seen)) else: - print('FAIL %d/%d' % (len(failed), len(seen))) - for f in failed: - print("\t%-50s: %s" % (f[0], fail_desc.get(f[1], 'unknown reason %d' % f[1]))) + print('FAIL %d/%d' % (len(failed), len(seen))) + for f in failed: + print("\t%-50s: %s" % + (f[0], fail_desc.get(f[1], 'unknown reason %d' % f[1]))) diff --git a/test/inhfd/fifo.py b/test/inhfd/fifo.py index 64e5f8f13..2d20e4dbf 100755 --- a/test/inhfd/fifo.py +++ b/test/inhfd/fifo.py @@ -5,35 +5,35 @@ def create_fds(): - tdir = tempfile.mkdtemp("zdtm.inhfd.XXXXXX") - if os.system("mount -t tmpfs zdtm.inhfd %s" % tdir) != 0: - raise Exception("Unable to mount tmpfs") - tfifo = os.path.join(tdir, "test_fifo") - os.mkfifo(tfifo) - fd2 = open(tfifo, "w+b", buffering=0) - fd1 = open(tfifo, "rb") - os.system("umount -l %s" % tdir) - os.rmdir(tdir) - - mnt_id = -1 - with open("/proc/self/fdinfo/%d" % fd1.fileno()) as f: - for line in f: - line = line.split() - if line[0] == "mnt_id:": - mnt_id = int(line[1]) - break - else: - raise Exception("Unable to find mnt_id") - - global id_str - id_str = "file[%x:%x]" % (mnt_id, os.fstat(fd1.fileno()).st_ino) - - return [(fd2, fd1)] + tdir = tempfile.mkdtemp("zdtm.inhfd.XXXXXX") + if os.system("mount -t tmpfs zdtm.inhfd %s" % tdir) != 0: + raise Exception("Unable to mount tmpfs") + tfifo = os.path.join(tdir, "test_fifo") + os.mkfifo(tfifo) + fd2 = open(tfifo, "w+b", buffering=0) + fd1 = open(tfifo, "rb") + os.system("umount -l %s" % tdir) + os.rmdir(tdir) + + mnt_id = -1 + with open("/proc/self/fdinfo/%d" % fd1.fileno()) as f: + for line in f: + line = line.split() + if line[0] == "mnt_id:": + mnt_id = int(line[1]) + break + else: + raise Exception("Unable to find mnt_id") + + global id_str + id_str = "file[%x:%x]" % (mnt_id, os.fstat(fd1.fileno()).st_ino) + + return [(fd2, fd1)] def filename(pipef): - return id_str + return id_str def dump_opts(sockf): - return ["--external", id_str] + return ["--external", id_str] diff --git a/test/inhfd/pipe.py b/test/inhfd/pipe.py index 318dc862d..8d8318d5b 100755 --- a/test/inhfd/pipe.py +++ b/test/inhfd/pipe.py @@ -2,16 +2,16 @@ def create_fds(): - pipes = [] - for i in range(10): - (fd1, fd2) = os.pipe() - pipes.append((os.fdopen(fd2, "wb"), os.fdopen(fd1, "rb"))) - return pipes + pipes = [] + for i in range(10): + (fd1, fd2) = os.pipe() + pipes.append((os.fdopen(fd2, "wb"), os.fdopen(fd1, "rb"))) + return pipes def filename(pipef): - return 'pipe:[%d]' % os.fstat(pipef.fileno()).st_ino + return 'pipe:[%d]' % os.fstat(pipef.fileno()).st_ino def dump_opts(sockf): - return [] + return [] diff --git a/test/inhfd/socket.py b/test/inhfd/socket.py index feba0e0c6..9cea16ffb 100755 --- a/test/inhfd/socket.py +++ b/test/inhfd/socket.py @@ -3,19 +3,19 @@ def create_fds(): - (sk1, sk2) = socket.socketpair(socket.AF_UNIX, socket.SOCK_STREAM) - (sk3, sk4) = socket.socketpair(socket.AF_UNIX, socket.SOCK_STREAM) - return [(sk1.makefile("wb"), sk2.makefile("rb")), - (sk3.makefile("wb"), sk4.makefile("rb"))] + (sk1, sk2) = socket.socketpair(socket.AF_UNIX, socket.SOCK_STREAM) + (sk3, sk4) = socket.socketpair(socket.AF_UNIX, socket.SOCK_STREAM) + return [(sk1.makefile("wb"), sk2.makefile("rb")), + (sk3.makefile("wb"), sk4.makefile("rb"))] def __sock_ino(sockf): - return os.fstat(sockf.fileno()).st_ino + return os.fstat(sockf.fileno()).st_ino def filename(sockf): - return 'socket:[%d]' % __sock_ino(sockf) + return 'socket:[%d]' % __sock_ino(sockf) def dump_opts(sockf): - return ['--external', 'unix[%d]' % __sock_ino(sockf)] + return ['--external', 'unix[%d]' % __sock_ino(sockf)] diff --git a/test/inhfd/tty.py b/test/inhfd/tty.py index ae76a96d4..c11a57117 100755 --- a/test/inhfd/tty.py +++ b/test/inhfd/tty.py @@ -4,34 +4,33 @@ import pty import termios - ctl = False def child_prep(fd): - global ctl - if ctl: - return - ctl = True - fcntl.ioctl(fd.fileno(), termios.TIOCSCTTY, 1) + global ctl + if ctl: + return + ctl = True + fcntl.ioctl(fd.fileno(), termios.TIOCSCTTY, 1) def create_fds(): - ttys = [] - for i in range(10): - (fd1, fd2) = pty.openpty() - newattr = termios.tcgetattr(fd1) - newattr[3] &= ~termios.ICANON & ~termios.ECHO - termios.tcsetattr(fd1, termios.TCSADRAIN, newattr) - ttys.append((os.fdopen(fd1, "wb"), os.fdopen(fd2, "rb"))) - return ttys + ttys = [] + for i in range(10): + (fd1, fd2) = pty.openpty() + newattr = termios.tcgetattr(fd1) + newattr[3] &= ~termios.ICANON & ~termios.ECHO + termios.tcsetattr(fd1, termios.TCSADRAIN, newattr) + ttys.append((os.fdopen(fd1, "wb"), os.fdopen(fd2, "rb"))) + return ttys def filename(pipef): - st = os.fstat(pipef.fileno()) - return 'tty[%x:%x]' % (st.st_rdev, st.st_dev) + st = os.fstat(pipef.fileno()) + return 'tty[%x:%x]' % (st.st_rdev, st.st_dev) def dump_opts(sockf): - st = os.fstat(sockf.fileno()) - return "--external", 'tty[%x:%x]' % (st.st_rdev, st.st_dev) + st = os.fstat(sockf.fileno()) + return "--external", 'tty[%x:%x]' % (st.st_rdev, st.st_dev) diff --git a/test/others/ext-tty/run.py b/test/others/ext-tty/run.py index f44b1d946..b1dcb4a5a 100755 --- a/test/others/ext-tty/run.py +++ b/test/others/ext-tty/run.py @@ -5,32 +5,41 @@ master, slave = pty.openpty() p = subprocess.Popen(["setsid", "--ctty", "sleep", "10000"], - stdin = slave, stdout = slave, stderr = slave, close_fds = True) + stdin=slave, + stdout=slave, + stderr=slave, + close_fds=True) st = os.stat("/proc/self/fd/%d" % slave) ttyid = "tty[%x:%x]" % (st.st_rdev, st.st_dev) os.close(slave) time.sleep(1) -ret = subprocess.Popen(["../../../criu/criu", "dump", "-t", str(p.pid), "-v4", "--external", ttyid]).wait() +ret = subprocess.Popen([ + "../../../criu/criu", "dump", "-t", + str(p.pid), "-v4", "--external", ttyid +]).wait() if ret: - sys.exit(ret) + sys.exit(ret) p.wait() -new_master, slave = pty.openpty() # get another pty pair +new_master, slave = pty.openpty() # get another pty pair os.close(master) ttyid = "fd[%d]:tty[%x:%x]" % (slave, st.st_rdev, st.st_dev) -ret = subprocess.Popen(["../../../criu/criu", "restore", "-v4", "--inherit-fd", ttyid, "--restore-sibling", "--restore-detach"]).wait() +ret = subprocess.Popen([ + "../../../criu/criu", "restore", "-v4", "--inherit-fd", ttyid, + "--restore-sibling", "--restore-detach" +]).wait() if ret: - sys.exit(ret) + sys.exit(ret) os.close(slave) -os.waitpid(-1, os.WNOHANG) # is the process alive +os.waitpid(-1, os.WNOHANG) # is the process alive os.close(new_master) _, status = os.wait() if not os.WIFSIGNALED(status) or os.WTERMSIG(status) != signal.SIGHUP: - print(status) - sys.exit(1) + print(status) + sys.exit(1) print("PASS") diff --git a/test/others/mounts/mounts.py b/test/others/mounts/mounts.py index dc65ba45c..70b0be5fa 100755 --- a/test/others/mounts/mounts.py +++ b/test/others/mounts/mounts.py @@ -1,31 +1,36 @@ import os import tempfile, random + def mount(src, dst, shared, private, slave): - cmd = "mount" - if shared: - cmd += " --make-shared" - if private: - cmd += " --make-private" - if slave: - cmd += " --make-slave" - if src: - cmd += " --bind '%s' '%s'" % (src, dst) - else: - cmd += " -t tmpfs none '%s'" % (dst) + cmd = "mount" + if shared: + cmd += " --make-shared" + if private: + cmd += " --make-private" + if slave: + cmd += " --make-slave" + if src: + cmd += " --bind '%s' '%s'" % (src, dst) + else: + cmd += " -t tmpfs none '%s'" % (dst) + + print(cmd) + ret = os.system(cmd) + if ret: + print("failed") - print(cmd) - ret = os.system(cmd) - if ret: - print("failed") -root = tempfile.mkdtemp(prefix = "root.mount", dir = "/tmp") +root = tempfile.mkdtemp(prefix="root.mount", dir="/tmp") mount(None, root, 1, 0, 0) mounts = [root] for i in range(10): - dstdir = random.choice(mounts) - dst = tempfile.mkdtemp(prefix = "mount", dir = dstdir) - src = random.choice(mounts + [None]) - mount(src, dst, random.randint(0,100) > 50, random.randint(0,100) > 90, random.randint(0,100) > 50) - mounts.append(dst) + dstdir = random.choice(mounts) + dst = tempfile.mkdtemp(prefix="mount", dir=dstdir) + src = random.choice(mounts + [None]) + mount(src, dst, + random.randint(0, 100) > 50, + random.randint(0, 100) > 90, + random.randint(0, 100) > 50) + mounts.append(dst) diff --git a/test/others/rpc/config_file.py b/test/others/rpc/config_file.py index 23a06615f..3579ac76f 100755 --- a/test/others/rpc/config_file.py +++ b/test/others/rpc/config_file.py @@ -14,169 +14,174 @@ def setup_swrk(): - print('Connecting to CRIU in swrk mode.') - css = socket.socketpair(socket.AF_UNIX, socket.SOCK_SEQPACKET) - swrk = subprocess.Popen(['./criu', "swrk", "%d" % css[0].fileno()]) - css[0].close() - return swrk, css[1] + print('Connecting to CRIU in swrk mode.') + css = socket.socketpair(socket.AF_UNIX, socket.SOCK_SEQPACKET) + swrk = subprocess.Popen(['./criu', "swrk", "%d" % css[0].fileno()]) + css[0].close() + return swrk, css[1] def setup_config_file(content): - # Creating a temporary file which will be used as configuration file. - fd, path = mkstemp() + # Creating a temporary file which will be used as configuration file. + fd, path = mkstemp() - with os.fdopen(fd, 'w') as f: - f.write(content) + with os.fdopen(fd, 'w') as f: + f.write(content) - os.environ['CRIU_CONFIG_FILE'] = path + os.environ['CRIU_CONFIG_FILE'] = path - return path + return path def cleanup_config_file(path): - if os.environ.get('CRIU_CONFIG_FILE', None) is not None: - del os.environ['CRIU_CONFIG_FILE'] - os.unlink(path) + if os.environ.get('CRIU_CONFIG_FILE', None) is not None: + del os.environ['CRIU_CONFIG_FILE'] + os.unlink(path) def cleanup_output(path): - for f in (does_not_exist, log_file): - f = os.path.join(path, f) - if os.access(f, os.F_OK): - os.unlink(f) + for f in (does_not_exist, log_file): + f = os.path.join(path, f) + if os.access(f, os.F_OK): + os.unlink(f) def setup_criu_dump_request(): - # Create criu msg, set it's type to dump request - # and set dump options. Checkout more options in protobuf/rpc.proto - req = rpc.criu_req() - req.type = rpc.DUMP - req.opts.leave_running = True - req.opts.log_level = 4 - req.opts.log_file = log_file - req.opts.images_dir_fd = os.open(args['dir'], os.O_DIRECTORY) - # Not necessary, just for testing - req.opts.tcp_established = True - req.opts.shell_job = True - return req + # Create criu msg, set it's type to dump request + # and set dump options. Checkout more options in protobuf/rpc.proto + req = rpc.criu_req() + req.type = rpc.DUMP + req.opts.leave_running = True + req.opts.log_level = 4 + req.opts.log_file = log_file + req.opts.images_dir_fd = os.open(args['dir'], os.O_DIRECTORY) + # Not necessary, just for testing + req.opts.tcp_established = True + req.opts.shell_job = True + return req def do_rpc(s, req): - # Send request - s.send(req.SerializeToString()) + # Send request + s.send(req.SerializeToString()) - # Recv response - resp = rpc.criu_resp() - MAX_MSG_SIZE = 1024 - resp.ParseFromString(s.recv(MAX_MSG_SIZE)) + # Recv response + resp = rpc.criu_resp() + MAX_MSG_SIZE = 1024 + resp.ParseFromString(s.recv(MAX_MSG_SIZE)) - s.close() - return resp + s.close() + return resp def test_broken_configuration_file(): - # Testing RPC configuration file mode with a broken configuration file. - # This should fail - content = 'hopefully-this-option-will-never=exist' - path = setup_config_file(content) - swrk, s = setup_swrk() - s.close() - # This test is only about detecting wrong configuration files. - # If we do not sleep it might happen that we kill CRIU before - # it parses the configuration file. A short sleep makes sure - # that the configuration file has been parsed. Hopefully. - # (I am sure this will fail horribly at some point) - time.sleep(0.3) - swrk.kill() - return_code = swrk.wait() - # delete temporary file again - cleanup_config_file(path) - if return_code != 1: - print('FAIL: CRIU should have returned 1 instead of %d' % return_code) - sys.exit(-1) + # Testing RPC configuration file mode with a broken configuration file. + # This should fail + content = 'hopefully-this-option-will-never=exist' + path = setup_config_file(content) + swrk, s = setup_swrk() + s.close() + # This test is only about detecting wrong configuration files. + # If we do not sleep it might happen that we kill CRIU before + # it parses the configuration file. A short sleep makes sure + # that the configuration file has been parsed. Hopefully. + # (I am sure this will fail horribly at some point) + time.sleep(0.3) + swrk.kill() + return_code = swrk.wait() + # delete temporary file again + cleanup_config_file(path) + if return_code != 1: + print('FAIL: CRIU should have returned 1 instead of %d' % return_code) + sys.exit(-1) def search_in_log_file(log, message): - with open(os.path.join(args['dir'], log)) as f: - if message not in f.read(): - print('FAIL: Missing the expected error message (%s) in the log file' % message) - sys.exit(-1) + with open(os.path.join(args['dir'], log)) as f: + if message not in f.read(): + print( + 'FAIL: Missing the expected error message (%s) in the log file' + % message) + sys.exit(-1) def check_results(resp, log): - # Check if the specified log file exists - if not os.path.isfile(os.path.join(args['dir'], log)): - print('FAIL: Expected log file %s does not exist' % log) - sys.exit(-1) - # Dump should have failed with: 'The criu itself is within dumped tree' - if resp.type != rpc.DUMP: - print('FAIL: Unexpected msg type %r' % resp.type) - sys.exit(-1) - if 'The criu itself is within dumped tree' not in resp.cr_errmsg: - print('FAIL: Missing the expected error message in RPC response') - sys.exit(-1) - # Look into the log file for the same message - search_in_log_file(log, 'The criu itself is within dumped tree') + # Check if the specified log file exists + if not os.path.isfile(os.path.join(args['dir'], log)): + print('FAIL: Expected log file %s does not exist' % log) + sys.exit(-1) + # Dump should have failed with: 'The criu itself is within dumped tree' + if resp.type != rpc.DUMP: + print('FAIL: Unexpected msg type %r' % resp.type) + sys.exit(-1) + if 'The criu itself is within dumped tree' not in resp.cr_errmsg: + print('FAIL: Missing the expected error message in RPC response') + sys.exit(-1) + # Look into the log file for the same message + search_in_log_file(log, 'The criu itself is within dumped tree') def test_rpc_without_configuration_file(): - # Testing without configuration file - # Just doing a dump and checking for the logfile - req = setup_criu_dump_request() - _, s = setup_swrk() - resp = do_rpc(s, req) - s.close() - check_results(resp, log_file) + # Testing without configuration file + # Just doing a dump and checking for the logfile + req = setup_criu_dump_request() + _, s = setup_swrk() + resp = do_rpc(s, req) + s.close() + check_results(resp, log_file) def test_rpc_with_configuration_file(): - # Testing with configuration file - # Just doing a dump and checking for the logfile - - # Setting a different log file via configuration file - # This should not work as RPC settings overwrite configuration - # file settings in the default configuration. - log = does_not_exist - content = 'log-file ' + log + '\n' - content += 'no-tcp-established\nno-shell-job' - path = setup_config_file(content) - req = setup_criu_dump_request() - _, s = setup_swrk() - do_rpc(s, req) - s.close() - cleanup_config_file(path) - # Check if the specified log file exists - # It should not as configuration files do not overwrite RPC values. - if os.path.isfile(os.path.join(args['dir'], log)): - print('FAIL: log file %s should not exist' % log) - sys.exit(-1) + # Testing with configuration file + # Just doing a dump and checking for the logfile + + # Setting a different log file via configuration file + # This should not work as RPC settings overwrite configuration + # file settings in the default configuration. + log = does_not_exist + content = 'log-file ' + log + '\n' + content += 'no-tcp-established\nno-shell-job' + path = setup_config_file(content) + req = setup_criu_dump_request() + _, s = setup_swrk() + do_rpc(s, req) + s.close() + cleanup_config_file(path) + # Check if the specified log file exists + # It should not as configuration files do not overwrite RPC values. + if os.path.isfile(os.path.join(args['dir'], log)): + print('FAIL: log file %s should not exist' % log) + sys.exit(-1) def test_rpc_with_configuration_file_overwriting_rpc(): - # Testing with configuration file - # Just doing a dump and checking for the logfile - - # Setting a different log file via configuration file - # This should not work as RPC settings overwrite configuration - # file settings in the default configuration. - log = does_not_exist - content = 'log-file ' + log + '\n' - content += 'no-tcp-established\nno-shell-job' - path = setup_config_file(content) - # Only set the configuration file via RPC; - # not via environment variable - del os.environ['CRIU_CONFIG_FILE'] - req = setup_criu_dump_request() - req.opts.config_file = path - _, s = setup_swrk() - resp = do_rpc(s, req) - s.close() - cleanup_config_file(path) - check_results(resp, log) - - -parser = argparse.ArgumentParser(description="Test config files using CRIU RPC") -parser.add_argument('dir', type = str, help = "Directory where CRIU images should be placed") + # Testing with configuration file + # Just doing a dump and checking for the logfile + + # Setting a different log file via configuration file + # This should not work as RPC settings overwrite configuration + # file settings in the default configuration. + log = does_not_exist + content = 'log-file ' + log + '\n' + content += 'no-tcp-established\nno-shell-job' + path = setup_config_file(content) + # Only set the configuration file via RPC; + # not via environment variable + del os.environ['CRIU_CONFIG_FILE'] + req = setup_criu_dump_request() + req.opts.config_file = path + _, s = setup_swrk() + resp = do_rpc(s, req) + s.close() + cleanup_config_file(path) + check_results(resp, log) + + +parser = argparse.ArgumentParser( + description="Test config files using CRIU RPC") +parser.add_argument('dir', + type=str, + help="Directory where CRIU images should be placed") args = vars(parser.parse_args()) diff --git a/test/others/rpc/errno.py b/test/others/rpc/errno.py index ee9e90d8c..49cb622de 100755 --- a/test/others/rpc/errno.py +++ b/test/others/rpc/errno.py @@ -6,130 +6,136 @@ import argparse parser = argparse.ArgumentParser(description="Test errno reported by CRIU RPC") -parser.add_argument('socket', type = str, help = "CRIU service socket") -parser.add_argument('dir', type = str, help = "Directory where CRIU images should be placed") +parser.add_argument('socket', type=str, help="CRIU service socket") +parser.add_argument('dir', + type=str, + help="Directory where CRIU images should be placed") args = vars(parser.parse_args()) + # Prepare dir for images class test: - def __init__(self): - self.imgs_fd = os.open(args['dir'], os.O_DIRECTORY) - self.s = -1 - self._MAX_MSG_SIZE = 1024 + def __init__(self): + self.imgs_fd = os.open(args['dir'], os.O_DIRECTORY) + self.s = -1 + self._MAX_MSG_SIZE = 1024 + + def connect(self): + self.s = socket.socket(socket.AF_UNIX, socket.SOCK_SEQPACKET) + self.s.connect(args['socket']) - def connect(self): - self.s = socket.socket(socket.AF_UNIX, socket.SOCK_SEQPACKET) - self.s.connect(args['socket']) + def get_base_req(self): + req = rpc.criu_req() + req.opts.log_level = 4 + req.opts.images_dir_fd = self.imgs_fd + return req - def get_base_req(self): - req = rpc.criu_req() - req.opts.log_level = 4 - req.opts.images_dir_fd = self.imgs_fd - return req + def send_req(self, req): + self.connect() + self.s.send(req.SerializeToString()) - def send_req(self, req): - self.connect() - self.s.send(req.SerializeToString()) + def recv_resp(self): + resp = rpc.criu_resp() + resp.ParseFromString(self.s.recv(self._MAX_MSG_SIZE)) + return resp - def recv_resp(self): - resp = rpc.criu_resp() - resp.ParseFromString(self.s.recv(self._MAX_MSG_SIZE)) - return resp + def check_resp(self, resp, typ, err): + if resp.type != typ: + raise Exception('Unexpected responce type ' + str(resp.type)) - def check_resp(self, resp, typ, err): - if resp.type != typ: - raise Exception('Unexpected responce type ' + str(resp.type)) + if resp.success: + raise Exception('Unexpected success = True') - if resp.success: - raise Exception('Unexpected success = True') + if err and resp.cr_errno != err: + raise Exception('Unexpected cr_errno ' + str(resp.cr_errno)) - if err and resp.cr_errno != err: - raise Exception('Unexpected cr_errno ' + str(resp.cr_errno)) + def no_process(self): + print('Try to dump unexisting process') + # Get pid of non-existing process. + # Suppose max_pid is not taken by any process. + with open("/proc/sys/kernel/pid_max", "r") as f: + pid = int(f.readline()) + try: + os.kill(pid, 0) + except OSError: + pass + else: + raise Exception('max pid is taken') - def no_process(self): - print('Try to dump unexisting process') - # Get pid of non-existing process. - # Suppose max_pid is not taken by any process. - with open("/proc/sys/kernel/pid_max", "r") as f: - pid = int(f.readline()) - try: - os.kill(pid, 0) - except OSError: - pass - else: - raise Exception('max pid is taken') + # Ask criu to dump non-existing process. + req = self.get_base_req() + req.type = rpc.DUMP + req.opts.pid = pid - # Ask criu to dump non-existing process. - req = self.get_base_req() - req.type = rpc.DUMP - req.opts.pid = pid + self.send_req(req) + resp = self.recv_resp() - self.send_req(req) - resp = self.recv_resp() + self.check_resp(resp, rpc.DUMP, errno.ESRCH) - self.check_resp(resp, rpc.DUMP, errno.ESRCH) + print('Success') - print('Success') + def process_exists(self): + print( + 'Try to restore process which pid is already taken by other process' + ) - def process_exists(self): - print('Try to restore process which pid is already taken by other process') + # Perform self-dump + req = self.get_base_req() + req.type = rpc.DUMP + req.opts.leave_running = True - # Perform self-dump - req = self.get_base_req() - req.type = rpc.DUMP - req.opts.leave_running = True + self.send_req(req) + resp = self.recv_resp() - self.send_req(req) - resp = self.recv_resp() + if resp.success != True: + raise Exception('Self-dump failed') - if resp.success != True: - raise Exception('Self-dump failed') + # Ask to restore process from images of ourselves + req = self.get_base_req() + req.type = rpc.RESTORE - # Ask to restore process from images of ourselves - req = self.get_base_req() - req.type = rpc.RESTORE + self.send_req(req) + resp = self.recv_resp() - self.send_req(req) - resp = self.recv_resp() + self.check_resp(resp, rpc.RESTORE, errno.EEXIST) - self.check_resp(resp, rpc.RESTORE, errno.EEXIST) + print('Success') - print('Success') + def bad_options(self): + print('Try to send criu invalid opts') - def bad_options(self): - print('Try to send criu invalid opts') + # Subdirs are not allowed in log_file + req = self.get_base_req() + req.type = rpc.DUMP + req.opts.log_file = "../file.log" - # Subdirs are not allowed in log_file - req = self.get_base_req() - req.type = rpc.DUMP - req.opts.log_file = "../file.log" + self.send_req(req) + resp = self.recv_resp() - self.send_req(req) - resp = self.recv_resp() + self.check_resp(resp, rpc.DUMP, errno.EBADRQC) - self.check_resp(resp, rpc.DUMP, errno.EBADRQC) + print('Success') - print('Success') + def bad_request(self): + print('Try to send criu invalid request type') - def bad_request(self): - print('Try to send criu invalid request type') + req = self.get_base_req() + req.type = rpc.NOTIFY - req = self.get_base_req() - req.type = rpc.NOTIFY + self.send_req(req) + resp = self.recv_resp() - self.send_req(req) - resp = self.recv_resp() + self.check_resp(resp, rpc.EMPTY, None) - self.check_resp(resp, rpc.EMPTY, None) + print('Success') - print('Success') + def run(self): + self.no_process() + self.process_exists() + self.bad_options() + self.bad_request() - def run(self): - self.no_process() - self.process_exists() - self.bad_options() - self.bad_request() t = test() t.run() diff --git a/test/others/rpc/ps_test.py b/test/others/rpc/ps_test.py index 1872120fc..d16efd3f6 100755 --- a/test/others/rpc/ps_test.py +++ b/test/others/rpc/ps_test.py @@ -5,8 +5,10 @@ import argparse parser = argparse.ArgumentParser(description="Test page-server using CRIU RPC") -parser.add_argument('socket', type = str, help = "CRIU service socket") -parser.add_argument('dir', type = str, help = "Directory where CRIU images should be placed") +parser.add_argument('socket', type=str, help="CRIU service socket") +parser.add_argument('dir', + type=str, + help="Directory where CRIU images should be placed") args = vars(parser.parse_args()) @@ -16,45 +18,45 @@ # Start page-server print('Starting page-server') -req = rpc.criu_req() -req.type = rpc.PAGE_SERVER -req.opts.log_file = 'page-server.log' -req.opts.log_level = 4 -req.opts.images_dir_fd = os.open(args['dir'], os.O_DIRECTORY) +req = rpc.criu_req() +req.type = rpc.PAGE_SERVER +req.opts.log_file = 'page-server.log' +req.opts.log_level = 4 +req.opts.images_dir_fd = os.open(args['dir'], os.O_DIRECTORY) s.send(req.SerializeToString()) -resp = rpc.criu_resp() +resp = rpc.criu_resp() MAX_MSG_SIZE = 1024 resp.ParseFromString(s.recv(MAX_MSG_SIZE)) if resp.type != rpc.PAGE_SERVER: - print('Unexpected msg type') - sys.exit(1) + print('Unexpected msg type') + sys.exit(1) else: - if resp.success: - # check if pid even exists - try: - os.kill(resp.ps.pid, 0) - except OSError as err: - if err.errno == errno.ESRCH: - print('No process with page-server pid %d' %(resp.ps.pid)) - else: - print('Can\'t check that process %d exists' %(resp.ps.pid)) - sys.exit(1) - print('Success, page-server pid %d started on port %u' %(resp.ps.pid, resp.ps.port)) - else: - print('Failed to start page-server') - sys.exit(1) - + if resp.success: + # check if pid even exists + try: + os.kill(resp.ps.pid, 0) + except OSError as err: + if err.errno == errno.ESRCH: + print('No process with page-server pid %d' % (resp.ps.pid)) + else: + print('Can\'t check that process %d exists' % (resp.ps.pid)) + sys.exit(1) + print('Success, page-server pid %d started on port %u' % + (resp.ps.pid, resp.ps.port)) + else: + print('Failed to start page-server') + sys.exit(1) # Perform self-dump print('Dumping myself using page-server') -req.type = rpc.DUMP -req.opts.ps.port = resp.ps.port -req.opts.ps.address = "127.0.0.1" -req.opts.log_file = 'dump.log' -req.opts.leave_running = True +req.type = rpc.DUMP +req.opts.ps.port = resp.ps.port +req.opts.ps.address = "127.0.0.1" +req.opts.log_file = 'dump.log' +req.opts.leave_running = True s.close() s = socket.socket(socket.AF_UNIX, socket.SOCK_SEQPACKET) @@ -64,11 +66,11 @@ resp.ParseFromString(s.recv(MAX_MSG_SIZE)) if resp.type != rpc.DUMP: - print('Unexpected msg type') - sys.exit(1) + print('Unexpected msg type') + sys.exit(1) else: - if resp.success: - print('Success') - else: - print('Fail') - sys.exit(1) + if resp.success: + print('Success') + else: + print('Fail') + sys.exit(1) diff --git a/test/others/rpc/read.py b/test/others/rpc/read.py index bbf69b6cb..ff7e5c1a0 100644 --- a/test/others/rpc/read.py +++ b/test/others/rpc/read.py @@ -12,6 +12,6 @@ f.close() if r == '\0': - sys.exit(0) + sys.exit(0) sys.exit(-1) diff --git a/test/others/rpc/restore-loop.py b/test/others/rpc/restore-loop.py index ce5786a56..c81567426 100755 --- a/test/others/rpc/restore-loop.py +++ b/test/others/rpc/restore-loop.py @@ -4,9 +4,12 @@ import rpc_pb2 as rpc import argparse -parser = argparse.ArgumentParser(description="Test ability to restore a process from images using CRIU RPC") -parser.add_argument('socket', type = str, help = "CRIU service socket") -parser.add_argument('dir', type = str, help = "Directory where CRIU images could be found") +parser = argparse.ArgumentParser( + description="Test ability to restore a process from images using CRIU RPC") +parser.add_argument('socket', type=str, help="CRIU service socket") +parser.add_argument('dir', + type=str, + help="Directory where CRIU images could be found") args = vars(parser.parse_args()) @@ -16,30 +19,30 @@ # Create criu msg, set it's type to dump request # and set dump options. Checkout more options in protobuf/rpc.proto -req = rpc.criu_req() -req.type = rpc.RESTORE -req.opts.images_dir_fd = os.open(args['dir'], os.O_DIRECTORY) +req = rpc.criu_req() +req.type = rpc.RESTORE +req.opts.images_dir_fd = os.open(args['dir'], os.O_DIRECTORY) # As the dumped process is running with setsid this should not # be necessary. There seems to be a problem for this testcase # in combination with alpine's setsid. # The dump is now done with -j and the restore also. -req.opts.shell_job = True +req.opts.shell_job = True # Send request s.send(req.SerializeToString()) # Recv response -resp = rpc.criu_resp() -MAX_MSG_SIZE = 1024 +resp = rpc.criu_resp() +MAX_MSG_SIZE = 1024 resp.ParseFromString(s.recv(MAX_MSG_SIZE)) if resp.type != rpc.RESTORE: - print('Unexpected msg type') - sys.exit(-1) + print('Unexpected msg type') + sys.exit(-1) else: - if resp.success: - print('Restore success') - else: - print('Restore fail') - sys.exit(-1) - print("PID of the restored program is %d\n" %(resp.restore.pid)) + if resp.success: + print('Restore success') + else: + print('Restore fail') + sys.exit(-1) + print("PID of the restored program is %d\n" % (resp.restore.pid)) diff --git a/test/others/rpc/test.py b/test/others/rpc/test.py index 0addbaedc..9a35e0e97 100755 --- a/test/others/rpc/test.py +++ b/test/others/rpc/test.py @@ -4,9 +4,12 @@ import rpc_pb2 as rpc import argparse -parser = argparse.ArgumentParser(description="Test dump/restore using CRIU RPC") -parser.add_argument('socket', type = str, help = "CRIU service socket") -parser.add_argument('dir', type = str, help = "Directory where CRIU images should be placed") +parser = argparse.ArgumentParser( + description="Test dump/restore using CRIU RPC") +parser.add_argument('socket', type=str, help="CRIU service socket") +parser.add_argument('dir', + type=str, + help="Directory where CRIU images should be placed") args = vars(parser.parse_args()) @@ -16,32 +19,32 @@ # Create criu msg, set it's type to dump request # and set dump options. Checkout more options in protobuf/rpc.proto -req = rpc.criu_req() -req.type = rpc.DUMP -req.opts.leave_running = True -req.opts.log_level = 4 -req.opts.images_dir_fd = os.open(args['dir'], os.O_DIRECTORY) +req = rpc.criu_req() +req.type = rpc.DUMP +req.opts.leave_running = True +req.opts.log_level = 4 +req.opts.images_dir_fd = os.open(args['dir'], os.O_DIRECTORY) # Send request s.send(req.SerializeToString()) # Recv response -resp = rpc.criu_resp() -MAX_MSG_SIZE = 1024 +resp = rpc.criu_resp() +MAX_MSG_SIZE = 1024 resp.ParseFromString(s.recv(MAX_MSG_SIZE)) if resp.type != rpc.DUMP: - print('Unexpected msg type') - sys.exit(-1) + print('Unexpected msg type') + sys.exit(-1) else: - if resp.success: - print('Success') - else: - print('Fail') - sys.exit(-1) + if resp.success: + print('Success') + else: + print('Fail') + sys.exit(-1) - if resp.dump.restored: - print('Restored') + if resp.dump.restored: + print('Restored') # Connect to service socket s = socket.socket(socket.AF_UNIX, socket.SOCK_SEQPACKET) @@ -61,21 +64,21 @@ resp.ParseFromString(s.recv(MAX_MSG_SIZE)) if resp.type != rpc.VERSION: - print('RPC: Unexpected msg type') - sys.exit(-1) + print('RPC: Unexpected msg type') + sys.exit(-1) else: - if resp.success: - print('RPC: Success') - print('CRIU major %d' % resp.version.major_number) - print('CRIU minor %d' % resp.version.minor_number) - if resp.version.HasField('gitid'): - print('CRIU gitid %s' % resp.version.gitid) - if resp.version.HasField('sublevel'): - print('CRIU sublevel %s' % resp.version.sublevel) - if resp.version.HasField('extra'): - print('CRIU extra %s' % resp.version.extra) - if resp.version.HasField('name'): - print('CRIU name %s' % resp.version.name) - else: - print('Fail') - sys.exit(-1) + if resp.success: + print('RPC: Success') + print('CRIU major %d' % resp.version.major_number) + print('CRIU minor %d' % resp.version.minor_number) + if resp.version.HasField('gitid'): + print('CRIU gitid %s' % resp.version.gitid) + if resp.version.HasField('sublevel'): + print('CRIU sublevel %s' % resp.version.sublevel) + if resp.version.HasField('extra'): + print('CRIU extra %s' % resp.version.extra) + if resp.version.HasField('name'): + print('CRIU name %s' % resp.version.name) + else: + print('Fail') + sys.exit(-1) diff --git a/test/others/rpc/version.py b/test/others/rpc/version.py index 247bc466d..f978c6c37 100755 --- a/test/others/rpc/version.py +++ b/test/others/rpc/version.py @@ -27,21 +27,21 @@ resp.ParseFromString(s.recv(MAX_MSG_SIZE)) if resp.type != rpc.VERSION: - print('RPC: Unexpected msg type') - sys.exit(-1) + print('RPC: Unexpected msg type') + sys.exit(-1) else: - if resp.success: - print('RPC: Success') - print('CRIU major %d' % resp.version.major_number) - print('CRIU minor %d' % resp.version.minor_number) - if resp.version.HasField('gitid'): - print('CRIU gitid %s' % resp.version.gitid) - if resp.version.HasField('sublevel'): - print('CRIU sublevel %s' % resp.version.sublevel) - if resp.version.HasField('extra'): - print('CRIU extra %s' % resp.version.extra) - if resp.version.HasField('name'): - print('CRIU name %s' % resp.version.name) - else: - print('Fail') - sys.exit(-1) + if resp.success: + print('RPC: Success') + print('CRIU major %d' % resp.version.major_number) + print('CRIU minor %d' % resp.version.minor_number) + if resp.version.HasField('gitid'): + print('CRIU gitid %s' % resp.version.gitid) + if resp.version.HasField('sublevel'): + print('CRIU sublevel %s' % resp.version.sublevel) + if resp.version.HasField('extra'): + print('CRIU extra %s' % resp.version.extra) + if resp.version.HasField('name'): + print('CRIU name %s' % resp.version.name) + else: + print('Fail') + sys.exit(-1) diff --git a/test/others/shell-job/run.py b/test/others/shell-job/run.py index 4f4dfadef..bd5c42509 100755 --- a/test/others/shell-job/run.py +++ b/test/others/shell-job/run.py @@ -6,15 +6,17 @@ os.chdir(os.getcwd()) + def create_pty(): - (fd1, fd2) = pty.openpty() - return (os.fdopen(fd1, "w+"), os.fdopen(fd2, "w+")) + (fd1, fd2) = pty.openpty() + return (os.fdopen(fd1, "w+"), os.fdopen(fd2, "w+")) + if not os.access("work", os.X_OK): os.mkdir("work", 0755) open("running", "w").close() -m,s = create_pty() +m, s = create_pty() p = os.pipe() pr = os.fdopen(p[0], "r") pw = os.fdopen(p[1], "w") @@ -46,14 +48,15 @@ def create_pty(): os.wait() os.unlink("running") -m,s = create_pty() +m, s = create_pty() cpid = os.fork() if cpid == 0: os.setsid() fcntl.ioctl(m.fileno(), termios.TIOCSCTTY, 1) cmd = [cr_bin, "restore", "-j", "-D", "work", "-v"] print("Run: %s" % " ".join(cmd)) - ret = subprocess.Popen([cr_bin, "restore", "-j", "-D", "work", "-v"]).wait() + ret = subprocess.Popen([cr_bin, "restore", "-j", "-D", "work", + "-v"]).wait() if ret != 0: sys.exit(1) sys.exit(0) diff --git a/test/zdtm.py b/test/zdtm.py index 57eb68a49..bac7abe70 100755 --- a/test/zdtm.py +++ b/test/zdtm.py @@ -31,26 +31,26 @@ def alarm(*args): - print("==== ALARM ====") + print("==== ALARM ====") signal.signal(signal.SIGALRM, alarm) def traceit(f, e, a): - if e == "line": - lineno = f.f_lineno - fil = f.f_globals["__file__"] - if fil.endswith("zdtm.py"): - global prev_line - line = linecache.getline(fil, lineno) - if line == prev_line: - print(" ...") - else: - prev_line = line - print("+%4d: %s" % (lineno, line.rstrip())) + if e == "line": + lineno = f.f_lineno + fil = f.f_globals["__file__"] + if fil.endswith("zdtm.py"): + global prev_line + line = linecache.getline(fil, lineno) + if line == prev_line: + print(" ...") + else: + prev_line = line + print("+%4d: %s" % (lineno, line.rstrip())) - return traceit + return traceit # Root dir for ns and uns flavors. All tests @@ -59,17 +59,17 @@ def traceit(f, e, a): def clean_tests_root(): - global tests_root - if tests_root and tests_root[0] == os.getpid(): - os.rmdir(tests_root[1]) + global tests_root + if tests_root and tests_root[0] == os.getpid(): + os.rmdir(tests_root[1]) def make_tests_root(): - global tests_root - if not tests_root: - tests_root = (os.getpid(), tempfile.mkdtemp("", "criu-root-", "/tmp")) - atexit.register(clean_tests_root) - return tests_root[1] + global tests_root + if not tests_root: + tests_root = (os.getpid(), tempfile.mkdtemp("", "criu-root-", "/tmp")) + atexit.register(clean_tests_root) + return tests_root[1] # Report generation @@ -78,60 +78,61 @@ def make_tests_root(): def init_report(path): - global report_dir - report_dir = path - if not os.access(report_dir, os.F_OK): - os.makedirs(report_dir) + global report_dir + report_dir = path + if not os.access(report_dir, os.F_OK): + os.makedirs(report_dir) def add_to_report(path, tgt_name): - global report_dir - if report_dir: - tgt_path = os.path.join(report_dir, tgt_name) - att = 0 - while os.access(tgt_path, os.F_OK): - tgt_path = os.path.join(report_dir, tgt_name + ".%d" % att) - att += 1 - - ignore = shutil.ignore_patterns('*.socket') - if os.path.isdir(path): - shutil.copytree(path, tgt_path, ignore = ignore) - else: - if not os.path.exists(os.path.dirname(tgt_path)): - os.mkdir(os.path.dirname(tgt_path)) - shutil.copy2(path, tgt_path) + global report_dir + if report_dir: + tgt_path = os.path.join(report_dir, tgt_name) + att = 0 + while os.access(tgt_path, os.F_OK): + tgt_path = os.path.join(report_dir, tgt_name + ".%d" % att) + att += 1 + + ignore = shutil.ignore_patterns('*.socket') + if os.path.isdir(path): + shutil.copytree(path, tgt_path, ignore=ignore) + else: + if not os.path.exists(os.path.dirname(tgt_path)): + os.mkdir(os.path.dirname(tgt_path)) + shutil.copy2(path, tgt_path) def add_to_output(path): - global report_dir - if not report_dir: - return + global report_dir + if not report_dir: + return - output_path = os.path.join(report_dir, "output") - with open(path, "r") as fdi, open(output_path, "a") as fdo: - for line in fdi: - fdo.write(line) + output_path = os.path.join(report_dir, "output") + with open(path, "r") as fdi, open(output_path, "a") as fdo: + for line in fdi: + fdo.write(line) prev_crash_reports = set(glob.glob("/tmp/zdtm-core-*.txt")) def check_core_files(): - reports = set(glob.glob("/tmp/zdtm-core-*.txt")) - prev_crash_reports - if not reports: - return False + reports = set(glob.glob("/tmp/zdtm-core-*.txt")) - prev_crash_reports + if not reports: + return False - while subprocess.Popen(r"ps axf | grep 'abrt\.sh'", shell = True).wait() == 0: - time.sleep(1) + while subprocess.Popen(r"ps axf | grep 'abrt\.sh'", + shell=True).wait() == 0: + time.sleep(1) - for i in reports: - add_to_report(i, os.path.basename(i)) - print_sep(i) - with open(i, "r") as report: - print(report.read()) - print_sep(i) + for i in reports: + add_to_report(i, os.path.basename(i)) + print_sep(i) + with open(i, "r") as report: + print(report.read()) + print_sep(i) - return True + return True # Arch we run on @@ -146,148 +147,161 @@ def check_core_files(): class host_flavor: - def __init__(self, opts): - self.name = "host" - self.ns = False - self.root = None + def __init__(self, opts): + self.name = "host" + self.ns = False + self.root = None - def init(self, l_bins, x_bins): - pass + def init(self, l_bins, x_bins): + pass - def fini(self): - pass + def fini(self): + pass - @staticmethod - def clean(): - pass + @staticmethod + def clean(): + pass class ns_flavor: - __root_dirs = ["/bin", "/sbin", "/etc", "/lib", "/lib64", "/dev", "/dev/pts", "/dev/net", "/tmp", "/usr", "/proc", "/run"] - - def __init__(self, opts): - self.name = "ns" - self.ns = True - self.uns = False - self.root = make_tests_root() - self.root_mounted = False - - def __copy_one(self, fname): - tfname = self.root + fname - if not os.access(tfname, os.F_OK): - # Copying should be atomic as tests can be - # run in parallel - try: - os.makedirs(self.root + os.path.dirname(fname)) - except OSError as e: - if e.errno != errno.EEXIST: - raise - dst = tempfile.mktemp(".tso", "", self.root + os.path.dirname(fname)) - shutil.copy2(fname, dst) - os.rename(dst, tfname) - - def __copy_libs(self, binary): - ldd = subprocess.Popen(["ldd", binary], stdout = subprocess.PIPE) - xl = re.compile(r'^(linux-gate.so|linux-vdso(64)?.so|not a dynamic|.*\s*ldd\s)') - - # This Mayakovsky-style code gets list of libraries a binary - # needs minus vdso and gate .so-s - libs = map(lambda x: x[1] == '=>' and x[2] or x[0], - map(lambda x: str(x).split(), - filter(lambda x: not xl.match(x), - map(lambda x: str(x).strip(), - filter(lambda x: str(x).startswith('\t'), ldd.stdout.read().decode('ascii').splitlines()))))) - - ldd.wait() - - for lib in libs: - if not os.access(lib, os.F_OK): - raise test_fail_exc("Can't find lib %s required by %s" % (lib, binary)) - self.__copy_one(lib) - - def __mknod(self, name, rdev = None): - name = "/dev/" + name - if not rdev: - if not os.access(name, os.F_OK): - print("Skipping %s at root" % name) - return - else: - rdev = os.stat(name).st_rdev - - name = self.root + name - os.mknod(name, stat.S_IFCHR, rdev) - os.chmod(name, 0o666) - - def __construct_root(self): - for dir in self.__root_dirs: - os.mkdir(self.root + dir) - os.chmod(self.root + dir, 0o777) - - for ldir in ["/bin", "/sbin", "/lib", "/lib64"]: - os.symlink(".." + ldir, self.root + "/usr" + ldir) - - self.__mknod("tty", os.makedev(5, 0)) - self.__mknod("null", os.makedev(1, 3)) - self.__mknod("net/tun") - self.__mknod("rtc") - self.__mknod("autofs", os.makedev(10, 235)) - - def __copy_deps(self, deps): - for d in deps.split('|'): - if os.access(d, os.F_OK): - self.__copy_one(d) - self.__copy_libs(d) - return - raise test_fail_exc("Deps check %s failed" % deps) - - def init(self, l_bins, x_bins): - subprocess.check_call(["mount", "--make-slave", "--bind", ".", self.root]) - self.root_mounted = True - - if not os.access(self.root + "/.constructed", os.F_OK): - with open(os.path.abspath(__file__)) as o: - fcntl.flock(o, fcntl.LOCK_EX) - if not os.access(self.root + "/.constructed", os.F_OK): - print("Construct root for %s" % l_bins[0]) - self.__construct_root() - os.mknod(self.root + "/.constructed", stat.S_IFREG | 0o600) - - for b in l_bins: - self.__copy_libs(b) - for b in x_bins: - self.__copy_deps(b) - - def fini(self): - if self.root_mounted: - subprocess.check_call(["./umount2", self.root]) - self.root_mounted = False - - @staticmethod - def clean(): - for d in ns_flavor.__root_dirs: - p = './' + d - print('Remove %s' % p) - if os.access(p, os.F_OK): - shutil.rmtree('./' + d) - - if os.access('./.constructed', os.F_OK): - os.unlink('./.constructed') + __root_dirs = [ + "/bin", "/sbin", "/etc", "/lib", "/lib64", "/dev", "/dev/pts", + "/dev/net", "/tmp", "/usr", "/proc", "/run" + ] + + def __init__(self, opts): + self.name = "ns" + self.ns = True + self.uns = False + self.root = make_tests_root() + self.root_mounted = False + + def __copy_one(self, fname): + tfname = self.root + fname + if not os.access(tfname, os.F_OK): + # Copying should be atomic as tests can be + # run in parallel + try: + os.makedirs(self.root + os.path.dirname(fname)) + except OSError as e: + if e.errno != errno.EEXIST: + raise + dst = tempfile.mktemp(".tso", "", + self.root + os.path.dirname(fname)) + shutil.copy2(fname, dst) + os.rename(dst, tfname) + + def __copy_libs(self, binary): + ldd = subprocess.Popen(["ldd", binary], stdout=subprocess.PIPE) + xl = re.compile( + r'^(linux-gate.so|linux-vdso(64)?.so|not a dynamic|.*\s*ldd\s)') + + # This Mayakovsky-style code gets list of libraries a binary + # needs minus vdso and gate .so-s + libs = map( + lambda x: x[1] == '=>' and x[2] or x[0], + map( + lambda x: str(x).split(), + filter( + lambda x: not xl.match(x), + map( + lambda x: str(x).strip(), + filter(lambda x: str(x).startswith('\t'), + ldd.stdout.read().decode( + 'ascii').splitlines()))))) + + ldd.wait() + + for lib in libs: + if not os.access(lib, os.F_OK): + raise test_fail_exc("Can't find lib %s required by %s" % + (lib, binary)) + self.__copy_one(lib) + + def __mknod(self, name, rdev=None): + name = "/dev/" + name + if not rdev: + if not os.access(name, os.F_OK): + print("Skipping %s at root" % name) + return + else: + rdev = os.stat(name).st_rdev + + name = self.root + name + os.mknod(name, stat.S_IFCHR, rdev) + os.chmod(name, 0o666) + + def __construct_root(self): + for dir in self.__root_dirs: + os.mkdir(self.root + dir) + os.chmod(self.root + dir, 0o777) + + for ldir in ["/bin", "/sbin", "/lib", "/lib64"]: + os.symlink(".." + ldir, self.root + "/usr" + ldir) + + self.__mknod("tty", os.makedev(5, 0)) + self.__mknod("null", os.makedev(1, 3)) + self.__mknod("net/tun") + self.__mknod("rtc") + self.__mknod("autofs", os.makedev(10, 235)) + + def __copy_deps(self, deps): + for d in deps.split('|'): + if os.access(d, os.F_OK): + self.__copy_one(d) + self.__copy_libs(d) + return + raise test_fail_exc("Deps check %s failed" % deps) + + def init(self, l_bins, x_bins): + subprocess.check_call( + ["mount", "--make-slave", "--bind", ".", self.root]) + self.root_mounted = True + + if not os.access(self.root + "/.constructed", os.F_OK): + with open(os.path.abspath(__file__)) as o: + fcntl.flock(o, fcntl.LOCK_EX) + if not os.access(self.root + "/.constructed", os.F_OK): + print("Construct root for %s" % l_bins[0]) + self.__construct_root() + os.mknod(self.root + "/.constructed", stat.S_IFREG | 0o600) + + for b in l_bins: + self.__copy_libs(b) + for b in x_bins: + self.__copy_deps(b) + + def fini(self): + if self.root_mounted: + subprocess.check_call(["./umount2", self.root]) + self.root_mounted = False + + @staticmethod + def clean(): + for d in ns_flavor.__root_dirs: + p = './' + d + print('Remove %s' % p) + if os.access(p, os.F_OK): + shutil.rmtree('./' + d) + + if os.access('./.constructed', os.F_OK): + os.unlink('./.constructed') class userns_flavor(ns_flavor): - def __init__(self, opts): - ns_flavor.__init__(self, opts) - self.name = "userns" - self.uns = True + def __init__(self, opts): + ns_flavor.__init__(self, opts) + self.name = "userns" + self.uns = True - def init(self, l_bins, x_bins): - # To be able to create roots_yard in CRIU - os.chmod(".", os.stat(".").st_mode | 0o077) - ns_flavor.init(self, l_bins, x_bins) + def init(self, l_bins, x_bins): + # To be able to create roots_yard in CRIU + os.chmod(".", os.stat(".").st_mode | 0o077) + ns_flavor.init(self, l_bins, x_bins) - @staticmethod - def clean(): - pass + @staticmethod + def clean(): + pass flavors = {'h': host_flavor, 'ns': ns_flavor, 'uns': userns_flavor} @@ -299,47 +313,47 @@ def clean(): def encode_flav(f): - return sorted(flavors.keys()).index(f) + 128 + return sorted(flavors.keys()).index(f) + 128 def decode_flav(i): - return flavors_codes.get(i - 128, "unknown") + return flavors_codes.get(i - 128, "unknown") def tail(path): - p = subprocess.Popen(['tail', '-n1', path], - stdout = subprocess.PIPE) - out = p.stdout.readline() - p.wait() - return out.decode() + p = subprocess.Popen(['tail', '-n1', path], stdout=subprocess.PIPE) + out = p.stdout.readline() + p.wait() + return out.decode() def rpidfile(path): - with open(path) as fd: - return fd.readline().strip() + with open(path) as fd: + return fd.readline().strip() -def wait_pid_die(pid, who, tmo = 30): - stime = 0.1 - while stime < tmo: - try: - os.kill(int(pid), 0) - except OSError as e: - if e.errno != errno.ESRCH: - print(e) - break +def wait_pid_die(pid, who, tmo=30): + stime = 0.1 + while stime < tmo: + try: + os.kill(int(pid), 0) + except OSError as e: + if e.errno != errno.ESRCH: + print(e) + break - print("Wait for %s(%d) to die for %f" % (who, pid, stime)) - time.sleep(stime) - stime *= 2 - else: - subprocess.Popen(["ps", "-p", str(pid)]).wait() - subprocess.Popen(["ps", "axf", str(pid)]).wait() - raise test_fail_exc("%s die" % who) + print("Wait for %s(%d) to die for %f" % (who, pid, stime)) + time.sleep(stime) + stime *= 2 + else: + subprocess.Popen(["ps", "-p", str(pid)]).wait() + subprocess.Popen(["ps", "axf", str(pid)]).wait() + raise test_fail_exc("%s die" % who) def test_flag(tdesc, flag): - return flag in tdesc.get('flags', '').split() + return flag in tdesc.get('flags', '').split() + # # Exception thrown when something inside the test goes wrong, @@ -349,16 +363,17 @@ def test_flag(tdesc, flag): class test_fail_exc(Exception): - def __init__(self, step): - self.step = step + def __init__(self, step): + self.step = step - def __str__(self): - return str(self.step) + def __str__(self): + return str(self.step) class test_fail_expected_exc(Exception): - def __init__(self, cr_action): - self.cr_action = cr_action + def __init__(self, cr_action): + self.cr_action = cr_action + # # A test from zdtm/ directory. @@ -366,418 +381,440 @@ def __init__(self, cr_action): class zdtm_test: - def __init__(self, name, desc, flavor, freezer): - self.__name = name - self.__desc = desc - self.__freezer = None - self.__make_action('cleanout') - self.__pid = 0 - self.__flavor = flavor - self.__freezer = freezer - self._bins = [name] - self._env = {} - self._deps = desc.get('deps', []) - self.auto_reap = True - self.__timeout = int(self.__desc.get('timeout') or 30) - - def __make_action(self, act, env = None, root = None): - sys.stdout.flush() # Not to let make's messages appear before ours - tpath = self.__name + '.' + act - s_args = ['make', '--no-print-directory', - '-C', os.path.dirname(tpath), - os.path.basename(tpath)] - - if env: - env = dict(os.environ, **env) - - s = subprocess.Popen(s_args, env = env, cwd = root, close_fds = True, - preexec_fn = self.__freezer and self.__freezer.attach or None) - if act == "pid": - try_run_hook(self, ["--post-start"]) - if s.wait(): - raise test_fail_exc(str(s_args)) - - if self.__freezer: - self.__freezer.freeze() - - def __pidfile(self): - return self.__name + '.pid' - - def __wait_task_die(self): - wait_pid_die(int(self.__pid), self.__name, self.__timeout) - - def __add_wperms(self): - # Add write perms for .out and .pid files - for b in self._bins: - p = os.path.dirname(b) - os.chmod(p, os.stat(p).st_mode | 0o222) - - def start(self): - self.__flavor.init(self._bins, self._deps) - - print("Start test") - - env = self._env - if not self.__freezer.kernel: - env['ZDTM_THREAD_BOMB'] = "5" - - if test_flag(self.__desc, 'pre-dump-notify'): - env['ZDTM_NOTIFY_FDIN'] = "100" - env['ZDTM_NOTIFY_FDOUT'] = "101" - - if not test_flag(self.__desc, 'suid'): - # Numbers should match those in criu - env['ZDTM_UID'] = "18943" - env['ZDTM_GID'] = "58467" - env['ZDTM_GROUPS'] = "27495 48244" - self.__add_wperms() - else: - print("Test is SUID") - - if self.__flavor.ns: - env['ZDTM_NEWNS'] = "1" - env['ZDTM_ROOT'] = self.__flavor.root - env['PATH'] = "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" - - if self.__flavor.uns: - env['ZDTM_USERNS'] = "1" - self.__add_wperms() - if os.getenv("GCOV"): - criu_dir = os.path.dirname(os.getcwd()) - criu_dir_r = "%s%s" % (self.__flavor.root, criu_dir) - - env['ZDTM_CRIU'] = os.path.dirname(os.getcwd()) - subprocess.check_call(["mkdir", "-p", criu_dir_r]) - - self.__make_action('pid', env, self.__flavor.root) - - try: - os.kill(int(self.getpid()), 0) - except Exception as e: - raise test_fail_exc("start: %s" % e) - - if not self.static(): - # Wait less than a second to give the test chance to - # move into some semi-random state - time.sleep(random.random()) - - def kill(self, sig = signal.SIGKILL): - self.__freezer.thaw() - if self.__pid: - print("Send the %d signal to %s" % (sig, self.__pid)) - os.kill(int(self.__pid), sig) - self.gone(sig == signal.SIGKILL) - - self.__flavor.fini() - - def pre_dump_notify(self): - env = self._env - - if 'ZDTM_NOTIFY_FDIN' not in env: - return - - if self.__pid == 0: - self.getpid() - - notify_fdout_path = "/proc/%s/fd/%s" % (self.__pid, env['ZDTM_NOTIFY_FDOUT']) - notify_fdin_path = "/proc/%s/fd/%s" % (self.__pid, env['ZDTM_NOTIFY_FDIN']) - - print("Send pre-dump notify to %s" % (self.__pid)) - with open(notify_fdout_path, "rb") as fdout: - with open(notify_fdin_path, "wb") as fdin: - fdin.write(struct.pack("i", 0)) - fdin.flush() - print("Wait pre-dump notify reply") - ret = struct.unpack('i', fdout.read(4)) - print("Completed pre-dump notify with %d" % (ret)) - - def stop(self): - self.__freezer.thaw() - self.getpid() # Read the pid from pidfile back - self.kill(signal.SIGTERM) - - res = tail(self.__name + '.out') - if 'PASS' not in list(map(lambda s: s.strip(), res.split())): - if os.access(self.__name + '.out.inprogress', os.F_OK): - print_sep(self.__name + '.out.inprogress') - with open(self.__name + '.out.inprogress') as fd: - print(fd.read()) - print_sep(self.__name + '.out.inprogress') - raise test_fail_exc("result check") - - def getpid(self): - if self.__pid == 0: - self.__pid = rpidfile(self.__pidfile()) - - return self.__pid - - def getname(self): - return self.__name - - def __getcropts(self): - opts = self.__desc.get('opts', '').split() + ["--pidfile", os.path.realpath(self.__pidfile())] - if self.__flavor.ns: - opts += ["--root", self.__flavor.root] - if test_flag(self.__desc, 'crlib'): - opts += ["-L", os.path.dirname(os.path.realpath(self.__name)) + '/lib'] - return opts - - def getdopts(self): - return self.__getcropts() + self.__freezer.getdopts() + self.__desc.get('dopts', '').split() - - def getropts(self): - return self.__getcropts() + self.__freezer.getropts() + self.__desc.get('ropts', '').split() - - def unlink_pidfile(self): - self.__pid = 0 - os.unlink(self.__pidfile()) - - def gone(self, force = True): - if not self.auto_reap: - pid, status = os.waitpid(int(self.__pid), 0) - if pid != int(self.__pid): - raise test_fail_exc("kill pid mess") - - self.__wait_task_die() - self.__pid = 0 - if force: - os.unlink(self.__pidfile()) - - def print_output(self): - if os.access(self.__name + '.out', os.R_OK): - print("Test output: " + "=" * 32) - with open(self.__name + '.out') as output: - print(output.read()) - print(" <<< " + "=" * 32) - - def static(self): - return self.__name.split('/')[1] == 'static' - - def ns(self): - return self.__flavor.ns - - def blocking(self): - return test_flag(self.__desc, 'crfail') - - @staticmethod - def available(): - if not os.access("umount2", os.X_OK): - subprocess.check_call(["make", "umount2"]) - if not os.access("zdtm_ct", os.X_OK): - subprocess.check_call(["make", "zdtm_ct"]) - if not os.access("zdtm/lib/libzdtmtst.a", os.F_OK): - subprocess.check_call(["make", "-C", "zdtm/"]) - subprocess.check_call(["flock", "zdtm_mount_cgroups.lock", "./zdtm_mount_cgroups"]) - - @staticmethod - def cleanup(): - subprocess.check_call(["flock", "zdtm_mount_cgroups.lock", "./zdtm_umount_cgroups"]) + def __init__(self, name, desc, flavor, freezer): + self.__name = name + self.__desc = desc + self.__freezer = None + self.__make_action('cleanout') + self.__pid = 0 + self.__flavor = flavor + self.__freezer = freezer + self._bins = [name] + self._env = {} + self._deps = desc.get('deps', []) + self.auto_reap = True + self.__timeout = int(self.__desc.get('timeout') or 30) + + def __make_action(self, act, env=None, root=None): + sys.stdout.flush() # Not to let make's messages appear before ours + tpath = self.__name + '.' + act + s_args = [ + 'make', '--no-print-directory', '-C', + os.path.dirname(tpath), + os.path.basename(tpath) + ] + + if env: + env = dict(os.environ, **env) + + s = subprocess.Popen( + s_args, + env=env, + cwd=root, + close_fds=True, + preexec_fn=self.__freezer and self.__freezer.attach or None) + if act == "pid": + try_run_hook(self, ["--post-start"]) + if s.wait(): + raise test_fail_exc(str(s_args)) + + if self.__freezer: + self.__freezer.freeze() + + def __pidfile(self): + return self.__name + '.pid' + + def __wait_task_die(self): + wait_pid_die(int(self.__pid), self.__name, self.__timeout) + + def __add_wperms(self): + # Add write perms for .out and .pid files + for b in self._bins: + p = os.path.dirname(b) + os.chmod(p, os.stat(p).st_mode | 0o222) + + def start(self): + self.__flavor.init(self._bins, self._deps) + + print("Start test") + + env = self._env + if not self.__freezer.kernel: + env['ZDTM_THREAD_BOMB'] = "5" + + if test_flag(self.__desc, 'pre-dump-notify'): + env['ZDTM_NOTIFY_FDIN'] = "100" + env['ZDTM_NOTIFY_FDOUT'] = "101" + + if not test_flag(self.__desc, 'suid'): + # Numbers should match those in criu + env['ZDTM_UID'] = "18943" + env['ZDTM_GID'] = "58467" + env['ZDTM_GROUPS'] = "27495 48244" + self.__add_wperms() + else: + print("Test is SUID") + + if self.__flavor.ns: + env['ZDTM_NEWNS'] = "1" + env['ZDTM_ROOT'] = self.__flavor.root + env['PATH'] = "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" + + if self.__flavor.uns: + env['ZDTM_USERNS'] = "1" + self.__add_wperms() + if os.getenv("GCOV"): + criu_dir = os.path.dirname(os.getcwd()) + criu_dir_r = "%s%s" % (self.__flavor.root, criu_dir) + + env['ZDTM_CRIU'] = os.path.dirname(os.getcwd()) + subprocess.check_call(["mkdir", "-p", criu_dir_r]) + + self.__make_action('pid', env, self.__flavor.root) + + try: + os.kill(int(self.getpid()), 0) + except Exception as e: + raise test_fail_exc("start: %s" % e) + + if not self.static(): + # Wait less than a second to give the test chance to + # move into some semi-random state + time.sleep(random.random()) + + def kill(self, sig=signal.SIGKILL): + self.__freezer.thaw() + if self.__pid: + print("Send the %d signal to %s" % (sig, self.__pid)) + os.kill(int(self.__pid), sig) + self.gone(sig == signal.SIGKILL) + + self.__flavor.fini() + + def pre_dump_notify(self): + env = self._env + + if 'ZDTM_NOTIFY_FDIN' not in env: + return + + if self.__pid == 0: + self.getpid() + + notify_fdout_path = "/proc/%s/fd/%s" % (self.__pid, + env['ZDTM_NOTIFY_FDOUT']) + notify_fdin_path = "/proc/%s/fd/%s" % (self.__pid, + env['ZDTM_NOTIFY_FDIN']) + + print("Send pre-dump notify to %s" % (self.__pid)) + with open(notify_fdout_path, "rb") as fdout: + with open(notify_fdin_path, "wb") as fdin: + fdin.write(struct.pack("i", 0)) + fdin.flush() + print("Wait pre-dump notify reply") + ret = struct.unpack('i', fdout.read(4)) + print("Completed pre-dump notify with %d" % (ret)) + + def stop(self): + self.__freezer.thaw() + self.getpid() # Read the pid from pidfile back + self.kill(signal.SIGTERM) + + res = tail(self.__name + '.out') + if 'PASS' not in list(map(lambda s: s.strip(), res.split())): + if os.access(self.__name + '.out.inprogress', os.F_OK): + print_sep(self.__name + '.out.inprogress') + with open(self.__name + '.out.inprogress') as fd: + print(fd.read()) + print_sep(self.__name + '.out.inprogress') + raise test_fail_exc("result check") + + def getpid(self): + if self.__pid == 0: + self.__pid = rpidfile(self.__pidfile()) + + return self.__pid + + def getname(self): + return self.__name + + def __getcropts(self): + opts = self.__desc.get('opts', '').split() + [ + "--pidfile", os.path.realpath(self.__pidfile()) + ] + if self.__flavor.ns: + opts += ["--root", self.__flavor.root] + if test_flag(self.__desc, 'crlib'): + opts += [ + "-L", + os.path.dirname(os.path.realpath(self.__name)) + '/lib' + ] + return opts + + def getdopts(self): + return self.__getcropts() + self.__freezer.getdopts( + ) + self.__desc.get('dopts', '').split() + + def getropts(self): + return self.__getcropts() + self.__freezer.getropts( + ) + self.__desc.get('ropts', '').split() + + def unlink_pidfile(self): + self.__pid = 0 + os.unlink(self.__pidfile()) + + def gone(self, force=True): + if not self.auto_reap: + pid, status = os.waitpid(int(self.__pid), 0) + if pid != int(self.__pid): + raise test_fail_exc("kill pid mess") + + self.__wait_task_die() + self.__pid = 0 + if force: + os.unlink(self.__pidfile()) + + def print_output(self): + if os.access(self.__name + '.out', os.R_OK): + print("Test output: " + "=" * 32) + with open(self.__name + '.out') as output: + print(output.read()) + print(" <<< " + "=" * 32) + + def static(self): + return self.__name.split('/')[1] == 'static' + + def ns(self): + return self.__flavor.ns + + def blocking(self): + return test_flag(self.__desc, 'crfail') + + @staticmethod + def available(): + if not os.access("umount2", os.X_OK): + subprocess.check_call(["make", "umount2"]) + if not os.access("zdtm_ct", os.X_OK): + subprocess.check_call(["make", "zdtm_ct"]) + if not os.access("zdtm/lib/libzdtmtst.a", os.F_OK): + subprocess.check_call(["make", "-C", "zdtm/"]) + subprocess.check_call( + ["flock", "zdtm_mount_cgroups.lock", "./zdtm_mount_cgroups"]) + + @staticmethod + def cleanup(): + subprocess.check_call( + ["flock", "zdtm_mount_cgroups.lock", "./zdtm_umount_cgroups"]) def load_module_from_file(name, path): - if sys.version_info[0] == 3 and sys.version_info[1] >= 5: - import importlib.util - spec = importlib.util.spec_from_file_location(name, path) - mod = importlib.util.module_from_spec(spec) - spec.loader.exec_module(mod) - else: - import imp - mod = imp.load_source(name, path) - return mod + if sys.version_info[0] == 3 and sys.version_info[1] >= 5: + import importlib.util + spec = importlib.util.spec_from_file_location(name, path) + mod = importlib.util.module_from_spec(spec) + spec.loader.exec_module(mod) + else: + import imp + mod = imp.load_source(name, path) + return mod class inhfd_test: - def __init__(self, name, desc, flavor, freezer): - self.__name = os.path.basename(name) - print("Load %s" % name) - self.__fdtyp = load_module_from_file(self.__name, name) - self.__peer_pid = 0 - self.__files = None - self.__peer_file_names = [] - self.__dump_opts = [] - self.__messages = {} - - def __get_message(self, i): - m = self.__messages.get(i, None) - if not m: - m = b"".join([random.choice(string.ascii_letters).encode() for _ in range(10)]) + b"%06d" % i - self.__messages[i] = m - return m - - def start(self): - self.__files = self.__fdtyp.create_fds() - - # Check FDs returned for inter-connection - i = 0 - for my_file, peer_file in self.__files: - msg = self.__get_message(i) - my_file.write(msg) - my_file.flush() - data = peer_file.read(len(msg)) - if data != msg: - raise test_fail_exc("FDs screwup: %r %r" % (msg, data)) - i += 1 - - start_pipe = os.pipe() - self.__peer_pid = os.fork() - if self.__peer_pid == 0: - os.setsid() - - for _, peer_file in self.__files: - getattr(self.__fdtyp, "child_prep", lambda fd: None)(peer_file) - - try: - os.unlink(self.__name + ".out") - except Exception as e: - print(e) - fd = os.open(self.__name + ".out", os.O_WRONLY | os.O_APPEND | os.O_CREAT) - os.dup2(fd, 1) - os.dup2(fd, 2) - os.close(fd) - fd = os.open("/dev/null", os.O_RDONLY) - os.dup2(fd, 0) - for my_file, _ in self.__files: - my_file.close() - os.close(start_pipe[0]) - os.close(start_pipe[1]) - i = 0 - for _, peer_file in self.__files: - msg = self.__get_message(i) - my_file.close() - try: - data = peer_file.read(16) - except Exception as e: - print("Unable to read a peer file: %s" % e) - sys.exit(1) - - if data != msg: - print("%r %r" % (data, msg)) - i += 1 - sys.exit(data == msg and 42 or 2) - - os.close(start_pipe[1]) - os.read(start_pipe[0], 12) - os.close(start_pipe[0]) - - for _, peer_file in self.__files: - self.__peer_file_names.append(self.__fdtyp.filename(peer_file)) - self.__dump_opts += self.__fdtyp.dump_opts(peer_file) - - self.__fds = set(os.listdir("/proc/%s/fd" % self.__peer_pid)) - - def stop(self): - fds = set(os.listdir("/proc/%s/fd" % self.__peer_pid)) - if fds != self.__fds: - raise test_fail_exc("File descriptors mismatch: %s %s" % (fds, self.__fds)) - i = 0 - for my_file, _ in self.__files: - msg = self.__get_message(i) - my_file.write(msg) - my_file.flush() - i += 1 - pid, status = os.waitpid(self.__peer_pid, 0) - with open(self.__name + ".out") as output: - print(output.read()) - self.__peer_pid = 0 - if not os.WIFEXITED(status) or os.WEXITSTATUS(status) != 42: - raise test_fail_exc("test failed with %d" % status) - - def kill(self): - if self.__peer_pid: - os.kill(self.__peer_pid, signal.SIGKILL) - - def getname(self): - return self.__name - - def getpid(self): - return "%s" % self.__peer_pid - - def gone(self, force = True): - os.waitpid(self.__peer_pid, 0) - wait_pid_die(self.__peer_pid, self.__name) - self.__files = None - - def getdopts(self): - return self.__dump_opts - - def getropts(self): - self.__files = self.__fdtyp.create_fds() - ropts = ["--restore-sibling"] - for i in range(len(self.__files)): - my_file, peer_file = self.__files[i] - fd = peer_file.fileno() - fdflags = fcntl.fcntl(fd, fcntl.F_GETFD) & ~fcntl.FD_CLOEXEC - fcntl.fcntl(fd, fcntl.F_SETFD, fdflags) - peer_file_name = self.__peer_file_names[i] - ropts.extend(["--inherit-fd", "fd[%d]:%s" % (fd, peer_file_name)]) - return ropts - - def print_output(self): - pass - - def static(self): - return True - - def blocking(self): - return False - - @staticmethod - def available(): - pass - - @staticmethod - def cleanup(): - pass + def __init__(self, name, desc, flavor, freezer): + self.__name = os.path.basename(name) + print("Load %s" % name) + self.__fdtyp = load_module_from_file(self.__name, name) + self.__peer_pid = 0 + self.__files = None + self.__peer_file_names = [] + self.__dump_opts = [] + self.__messages = {} + + def __get_message(self, i): + m = self.__messages.get(i, None) + if not m: + m = b"".join([ + random.choice(string.ascii_letters).encode() for _ in range(10) + ]) + b"%06d" % i + self.__messages[i] = m + return m + + def start(self): + self.__files = self.__fdtyp.create_fds() + + # Check FDs returned for inter-connection + i = 0 + for my_file, peer_file in self.__files: + msg = self.__get_message(i) + my_file.write(msg) + my_file.flush() + data = peer_file.read(len(msg)) + if data != msg: + raise test_fail_exc("FDs screwup: %r %r" % (msg, data)) + i += 1 + + start_pipe = os.pipe() + self.__peer_pid = os.fork() + if self.__peer_pid == 0: + os.setsid() + + for _, peer_file in self.__files: + getattr(self.__fdtyp, "child_prep", lambda fd: None)(peer_file) + + try: + os.unlink(self.__name + ".out") + except Exception as e: + print(e) + fd = os.open(self.__name + ".out", + os.O_WRONLY | os.O_APPEND | os.O_CREAT) + os.dup2(fd, 1) + os.dup2(fd, 2) + os.close(fd) + fd = os.open("/dev/null", os.O_RDONLY) + os.dup2(fd, 0) + for my_file, _ in self.__files: + my_file.close() + os.close(start_pipe[0]) + os.close(start_pipe[1]) + i = 0 + for _, peer_file in self.__files: + msg = self.__get_message(i) + my_file.close() + try: + data = peer_file.read(16) + except Exception as e: + print("Unable to read a peer file: %s" % e) + sys.exit(1) + + if data != msg: + print("%r %r" % (data, msg)) + i += 1 + sys.exit(data == msg and 42 or 2) + + os.close(start_pipe[1]) + os.read(start_pipe[0], 12) + os.close(start_pipe[0]) + + for _, peer_file in self.__files: + self.__peer_file_names.append(self.__fdtyp.filename(peer_file)) + self.__dump_opts += self.__fdtyp.dump_opts(peer_file) + + self.__fds = set(os.listdir("/proc/%s/fd" % self.__peer_pid)) + + def stop(self): + fds = set(os.listdir("/proc/%s/fd" % self.__peer_pid)) + if fds != self.__fds: + raise test_fail_exc("File descriptors mismatch: %s %s" % + (fds, self.__fds)) + i = 0 + for my_file, _ in self.__files: + msg = self.__get_message(i) + my_file.write(msg) + my_file.flush() + i += 1 + pid, status = os.waitpid(self.__peer_pid, 0) + with open(self.__name + ".out") as output: + print(output.read()) + self.__peer_pid = 0 + if not os.WIFEXITED(status) or os.WEXITSTATUS(status) != 42: + raise test_fail_exc("test failed with %d" % status) + + def kill(self): + if self.__peer_pid: + os.kill(self.__peer_pid, signal.SIGKILL) + + def getname(self): + return self.__name + + def getpid(self): + return "%s" % self.__peer_pid + + def gone(self, force=True): + os.waitpid(self.__peer_pid, 0) + wait_pid_die(self.__peer_pid, self.__name) + self.__files = None + + def getdopts(self): + return self.__dump_opts + + def getropts(self): + self.__files = self.__fdtyp.create_fds() + ropts = ["--restore-sibling"] + for i in range(len(self.__files)): + my_file, peer_file = self.__files[i] + fd = peer_file.fileno() + fdflags = fcntl.fcntl(fd, fcntl.F_GETFD) & ~fcntl.FD_CLOEXEC + fcntl.fcntl(fd, fcntl.F_SETFD, fdflags) + peer_file_name = self.__peer_file_names[i] + ropts.extend(["--inherit-fd", "fd[%d]:%s" % (fd, peer_file_name)]) + return ropts + + def print_output(self): + pass + + def static(self): + return True + + def blocking(self): + return False + + @staticmethod + def available(): + pass + + @staticmethod + def cleanup(): + pass class groups_test(zdtm_test): - def __init__(self, name, desc, flavor, freezer): - zdtm_test.__init__(self, 'zdtm/lib/groups', desc, flavor, freezer) - if flavor.ns: - self.__real_name = name - with open(name) as fd: - self.__subs = map(lambda x: x.strip(), fd.readlines()) - print("Subs:\n%s" % '\n'.join(self.__subs)) - else: - self.__real_name = '' - self.__subs = [] - - self._bins += self.__subs - self._deps += get_test_desc('zdtm/lib/groups')['deps'] - self._env = {'ZDTM_TESTS': self.__real_name} - - def __get_start_cmd(self, name): - tdir = os.path.dirname(name) - tname = os.path.basename(name) - - s_args = ['make', '--no-print-directory', '-C', tdir] - subprocess.check_call(s_args + [tname + '.cleanout']) - s = subprocess.Popen(s_args + ['--dry-run', tname + '.pid'], stdout = subprocess.PIPE) - cmd = s.stdout.readlines().pop().strip() - s.wait() - - return 'cd /' + tdir + ' && ' + cmd - - def start(self): - if (self.__subs): - with open(self.__real_name + '.start', 'w') as f: - for test in self.__subs: - cmd = self.__get_start_cmd(test) - f.write(cmd + '\n') - - with open(self.__real_name + '.stop', 'w') as f: - for test in self.__subs: - f.write('kill -TERM `cat /%s.pid`\n' % test) - - zdtm_test.start(self) - - def stop(self): - zdtm_test.stop(self) - - for test in self.__subs: - res = tail(test + '.out') - if 'PASS' not in res.split(): - raise test_fail_exc("sub %s result check" % test) + def __init__(self, name, desc, flavor, freezer): + zdtm_test.__init__(self, 'zdtm/lib/groups', desc, flavor, freezer) + if flavor.ns: + self.__real_name = name + with open(name) as fd: + self.__subs = map(lambda x: x.strip(), fd.readlines()) + print("Subs:\n%s" % '\n'.join(self.__subs)) + else: + self.__real_name = '' + self.__subs = [] + + self._bins += self.__subs + self._deps += get_test_desc('zdtm/lib/groups')['deps'] + self._env = {'ZDTM_TESTS': self.__real_name} + + def __get_start_cmd(self, name): + tdir = os.path.dirname(name) + tname = os.path.basename(name) + + s_args = ['make', '--no-print-directory', '-C', tdir] + subprocess.check_call(s_args + [tname + '.cleanout']) + s = subprocess.Popen(s_args + ['--dry-run', tname + '.pid'], + stdout=subprocess.PIPE) + cmd = s.stdout.readlines().pop().strip() + s.wait() + + return 'cd /' + tdir + ' && ' + cmd + + def start(self): + if (self.__subs): + with open(self.__real_name + '.start', 'w') as f: + for test in self.__subs: + cmd = self.__get_start_cmd(test) + f.write(cmd + '\n') + + with open(self.__real_name + '.stop', 'w') as f: + for test in self.__subs: + f.write('kill -TERM `cat /%s.pid`\n' % test) + + zdtm_test.start(self) + + def stop(self): + zdtm_test.stop(self) + + for test in self.__subs: + res = tail(test + '.out') + if 'PASS' not in res.split(): + raise test_fail_exc("sub %s result check" % test) test_classes = {'zdtm': zdtm_test, 'inhfd': inhfd_test, 'groups': groups_test} @@ -790,517 +827,573 @@ def stop(self): class criu_cli: - @staticmethod - def run(action, args, criu_bin, fault = None, strace = [], preexec = None, nowait = False): - env = dict(os.environ, ASAN_OPTIONS = "log_path=asan.log:disable_coredump=0:detect_leaks=0") - - if fault: - print("Forcing %s fault" % fault) - env['CRIU_FAULT'] = fault - - cr = subprocess.Popen(strace + [criu_bin, action, "--no-default-config"] + args, - env = env, close_fds = False, preexec_fn = preexec) - if nowait: - return cr - return cr.wait() + @staticmethod + def run(action, + args, + criu_bin, + fault=None, + strace=[], + preexec=None, + nowait=False): + env = dict( + os.environ, + ASAN_OPTIONS="log_path=asan.log:disable_coredump=0:detect_leaks=0") + + if fault: + print("Forcing %s fault" % fault) + env['CRIU_FAULT'] = fault + + cr = subprocess.Popen(strace + + [criu_bin, action, "--no-default-config"] + args, + env=env, + close_fds=False, + preexec_fn=preexec) + if nowait: + return cr + return cr.wait() class criu_rpc_process: - def wait(self): - return self.criu.wait_pid(self.pid) + def wait(self): + return self.criu.wait_pid(self.pid) - def terminate(self): - os.kill(self.pid, signal.SIGTERM) + def terminate(self): + os.kill(self.pid, signal.SIGTERM) class criu_rpc: - @staticmethod - def __set_opts(criu, args, ctx): - while len(args) != 0: - arg = args.pop(0) - if arg == '-v4': - criu.opts.log_level = 4 - continue - if arg == '-o': - criu.opts.log_file = args.pop(0) - continue - if arg == '-D': - criu.opts.images_dir_fd = os.open(args.pop(0), os.O_DIRECTORY) - ctx['imgd'] = criu.opts.images_dir_fd - continue - if arg == '-t': - criu.opts.pid = int(args.pop(0)) - continue - if arg == '--pidfile': - ctx['pidf'] = args.pop(0) - continue - if arg == '--timeout': - criu.opts.timeout = int(args.pop(0)) - continue - if arg == '--restore-detached': - # Set by service by default - ctx['rd'] = True - continue - if arg == '--root': - criu.opts.root = args.pop(0) - continue - if arg == '--external': - criu.opts.external.append(args.pop(0)) - continue - if arg == '--status-fd': - fd = int(args.pop(0)) - os.write(fd, b"\0") - fcntl.fcntl(fd, fcntl.F_SETFD, fcntl.FD_CLOEXEC) - continue - if arg == '--port': - criu.opts.ps.port = int(args.pop(0)) - continue - if arg == '--address': - criu.opts.ps.address = args.pop(0) - continue - if arg == '--page-server': - continue - if arg == '--prev-images-dir': - criu.opts.parent_img = args.pop(0) - continue - if arg == '--track-mem': - criu.opts.track_mem = True - continue - if arg == '--tcp-established': - criu.opts.tcp_established = True - continue - if arg == '--restore-sibling': - criu.opts.rst_sibling = True - continue - if arg == "--inherit-fd": - inhfd = criu.opts.inherit_fd.add() - key = args.pop(0) - fd, key = key.split(":", 1) - inhfd.fd = int(fd[3:-1]) - inhfd.key = key - continue - - raise test_fail_exc('RPC for %s required' % arg) - - @staticmethod - def run(action, args, criu_bin, fault = None, strace = [], preexec = None, nowait = False): - if fault: - raise test_fail_exc('RPC and FAULT not supported') - if strace: - raise test_fail_exc('RPC and SAT not supported') - if preexec: - raise test_fail_exc('RPC and PREEXEC not supported') - - ctx = {} # Object used to keep info untill action is done - criu = crpc.criu() - criu.use_binary(criu_bin) - criu_rpc.__set_opts(criu, args, ctx) - p = None - - try: - if action == 'dump': - criu.dump() - elif action == 'pre-dump': - criu.pre_dump() - elif action == 'restore': - if 'rd' not in ctx: - raise test_fail_exc('RPC Non-detached restore is impossible') - - res = criu.restore() - pidf = ctx.get('pidf') - if pidf: - with open(pidf, 'w') as fd: - fd.write('%d\n' % res.pid) - elif action == "page-server": - res = criu.page_server_chld() - p = criu_rpc_process() - p.pid = res.pid - p.criu = criu - else: - raise test_fail_exc('RPC for %s required' % action) - except crpc.CRIUExceptionExternal as e: - print("Fail", e) - ret = -1 - else: - ret = 0 - - imgd = ctx.get('imgd') - if imgd: - os.close(imgd) - - if nowait and ret == 0: - return p - - return ret + @staticmethod + def __set_opts(criu, args, ctx): + while len(args) != 0: + arg = args.pop(0) + if arg == '-v4': + criu.opts.log_level = 4 + continue + if arg == '-o': + criu.opts.log_file = args.pop(0) + continue + if arg == '-D': + criu.opts.images_dir_fd = os.open(args.pop(0), os.O_DIRECTORY) + ctx['imgd'] = criu.opts.images_dir_fd + continue + if arg == '-t': + criu.opts.pid = int(args.pop(0)) + continue + if arg == '--pidfile': + ctx['pidf'] = args.pop(0) + continue + if arg == '--timeout': + criu.opts.timeout = int(args.pop(0)) + continue + if arg == '--restore-detached': + # Set by service by default + ctx['rd'] = True + continue + if arg == '--root': + criu.opts.root = args.pop(0) + continue + if arg == '--external': + criu.opts.external.append(args.pop(0)) + continue + if arg == '--status-fd': + fd = int(args.pop(0)) + os.write(fd, b"\0") + fcntl.fcntl(fd, fcntl.F_SETFD, fcntl.FD_CLOEXEC) + continue + if arg == '--port': + criu.opts.ps.port = int(args.pop(0)) + continue + if arg == '--address': + criu.opts.ps.address = args.pop(0) + continue + if arg == '--page-server': + continue + if arg == '--prev-images-dir': + criu.opts.parent_img = args.pop(0) + continue + if arg == '--track-mem': + criu.opts.track_mem = True + continue + if arg == '--tcp-established': + criu.opts.tcp_established = True + continue + if arg == '--restore-sibling': + criu.opts.rst_sibling = True + continue + if arg == "--inherit-fd": + inhfd = criu.opts.inherit_fd.add() + key = args.pop(0) + fd, key = key.split(":", 1) + inhfd.fd = int(fd[3:-1]) + inhfd.key = key + continue + + raise test_fail_exc('RPC for %s required' % arg) + + @staticmethod + def run(action, + args, + criu_bin, + fault=None, + strace=[], + preexec=None, + nowait=False): + if fault: + raise test_fail_exc('RPC and FAULT not supported') + if strace: + raise test_fail_exc('RPC and SAT not supported') + if preexec: + raise test_fail_exc('RPC and PREEXEC not supported') + + ctx = {} # Object used to keep info untill action is done + criu = crpc.criu() + criu.use_binary(criu_bin) + criu_rpc.__set_opts(criu, args, ctx) + p = None + + try: + if action == 'dump': + criu.dump() + elif action == 'pre-dump': + criu.pre_dump() + elif action == 'restore': + if 'rd' not in ctx: + raise test_fail_exc( + 'RPC Non-detached restore is impossible') + + res = criu.restore() + pidf = ctx.get('pidf') + if pidf: + with open(pidf, 'w') as fd: + fd.write('%d\n' % res.pid) + elif action == "page-server": + res = criu.page_server_chld() + p = criu_rpc_process() + p.pid = res.pid + p.criu = criu + else: + raise test_fail_exc('RPC for %s required' % action) + except crpc.CRIUExceptionExternal as e: + print("Fail", e) + ret = -1 + else: + ret = 0 + + imgd = ctx.get('imgd') + if imgd: + os.close(imgd) + + if nowait and ret == 0: + return p + + return ret class criu: - def __init__(self, opts): - self.__test = None - self.__dump_path = None - self.__iter = 0 - self.__prev_dump_iter = None - self.__page_server = bool(opts['page_server']) - self.__remote_lazy_pages = bool(opts['remote_lazy_pages']) - self.__lazy_pages = (self.__remote_lazy_pages or - bool(opts['lazy_pages'])) - self.__lazy_migrate = bool(opts['lazy_migrate']) - self.__restore_sibling = bool(opts['sibling']) - self.__join_ns = bool(opts['join_ns']) - self.__empty_ns = bool(opts['empty_ns']) - self.__fault = opts['fault'] - self.__script = opts['script'] - self.__sat = bool(opts['sat']) - self.__dedup = bool(opts['dedup']) - self.__mdedup = bool(opts['noauto_dedup']) - self.__user = bool(opts['user']) - self.__leave_stopped = bool(opts['stop']) - self.__remote = bool(opts['remote']) - self.__criu = (opts['rpc'] and criu_rpc or criu_cli) - self.__show_stats = bool(opts['show_stats']) - self.__lazy_pages_p = None - self.__page_server_p = None - self.__dump_process = None - self.__tls = self.__tls_options() if opts['tls'] else [] - self.__criu_bin = opts['criu_bin'] - self.__crit_bin = opts['crit_bin'] - - def fini(self): - if self.__lazy_migrate: - ret = self.__dump_process.wait() - if self.__lazy_pages_p: - ret = self.__lazy_pages_p.wait() - grep_errors(os.path.join(self.__ddir(), "lazy-pages.log")) - self.__lazy_pages_p = None - if ret: - raise test_fail_exc("criu lazy-pages exited with %s" % ret) - if self.__page_server_p: - ret = self.__page_server_p.wait() - grep_errors(os.path.join(self.__ddir(), "page-server.log")) - self.__page_server_p = None - if ret: - raise test_fail_exc("criu page-server exited with %s" % ret) - if self.__dump_process: - ret = self.__dump_process.wait() - grep_errors(os.path.join(self.__ddir(), "dump.log")) - self.__dump_process = None - if ret: - raise test_fail_exc("criu dump exited with %s" % ret) - return - - def logs(self): - return self.__dump_path - - def set_test(self, test): - self.__test = test - self.__dump_path = "dump/" + test.getname() + "/" + test.getpid() - if os.path.exists(self.__dump_path): - for i in range(100): - newpath = self.__dump_path + "." + str(i) - if not os.path.exists(newpath): - os.rename(self.__dump_path, newpath) - break - else: - raise test_fail_exc("couldn't find dump dir %s" % self.__dump_path) - - os.makedirs(self.__dump_path) - - def cleanup(self): - if self.__dump_path: - print("Removing %s" % self.__dump_path) - shutil.rmtree(self.__dump_path) - - def __tls_options(self): - pki_dir = os.path.dirname(os.path.abspath(__file__)) + "/pki" - return ["--tls", "--tls-no-cn-verify", - "--tls-key", pki_dir + "/key.pem", - "--tls-cert", pki_dir + "/cert.pem", - "--tls-cacert", pki_dir + "/cacert.pem"] - - def __ddir(self): - return os.path.join(self.__dump_path, "%d" % self.__iter) - - def set_user_id(self): - # Numbers should match those in zdtm_test - os.setresgid(58467, 58467, 58467) - os.setresuid(18943, 18943, 18943) - - def __criu_act(self, action, opts = [], log = None, nowait = False): - if not log: - log = action + ".log" - - s_args = ["-o", log, "-D", self.__ddir(), "-v4"] + opts - - with open(os.path.join(self.__ddir(), action + '.cropt'), 'w') as f: - f.write(' '.join(s_args) + '\n') - - print("Run criu " + action) - - strace = [] - if self.__sat: - fname = os.path.join(self.__ddir(), action + '.strace') - print_fname(fname, 'strace') - strace = ["strace", "-o", fname, '-T'] - if action == 'restore': - strace += ['-f'] - s_args += ['--action-script', os.getcwd() + '/../scripts/fake-restore.sh'] - - if self.__script: - s_args += ['--action-script', self.__script] - - if action == "restore": - preexec = None - else: - preexec = self.__user and self.set_user_id or None - - __ddir = self.__ddir() - - status_fds = None - if nowait: - status_fds = os.pipe() - fd = status_fds[1] - fdflags = fcntl.fcntl(fd, fcntl.F_GETFD) - fcntl.fcntl(fd, fcntl.F_SETFD, fdflags & ~fcntl.FD_CLOEXEC) - s_args += ["--status-fd", str(fd)] - - with open("/proc/sys/kernel/ns_last_pid") as ns_last_pid_fd: - ns_last_pid = ns_last_pid_fd.read() - - ret = self.__criu.run(action, s_args, self.__criu_bin, self.__fault, strace, preexec, nowait) - - if nowait: - os.close(status_fds[1]) - if os.read(status_fds[0], 1) != b'\0': - ret = ret.wait() - if self.__test.blocking(): - raise test_fail_expected_exc(action) - else: - raise test_fail_exc("criu %s exited with %s" % (action, ret)) - os.close(status_fds[0]) - return ret - - grep_errors(os.path.join(__ddir, log)) - if ret != 0: - if self.__fault and int(self.__fault) < 128: - try_run_hook(self.__test, ["--fault", action]) - if action == "dump": - # create a clean directory for images - os.rename(__ddir, __ddir + ".fail") - os.mkdir(__ddir) - os.chmod(__ddir, 0o777) - else: - # on restore we move only a log file, because we need images - os.rename(os.path.join(__ddir, log), os.path.join(__ddir, log + ".fail")) - # restore ns_last_pid to avoid a case when criu gets - # PID of one of restored processes. - with open("/proc/sys/kernel/ns_last_pid", "w+") as fd: - fd.write(ns_last_pid) - # try again without faults - print("Run criu " + action) - ret = self.__criu.run(action, s_args, self.__criu_bin, False, strace, preexec) - grep_errors(os.path.join(__ddir, log)) - if ret == 0: - return - rst_succeeded = os.access(os.path.join(__ddir, "restore-succeeded"), os.F_OK) - if self.__test.blocking() or (self.__sat and action == 'restore' and rst_succeeded): - raise test_fail_expected_exc(action) - else: - raise test_fail_exc("CRIU %s" % action) - - def __stats_file(self, action): - return os.path.join(self.__ddir(), "stats-%s" % action) - - def show_stats(self, action): - if not self.__show_stats: - return - - subprocess.Popen([self.__crit_bin, "show", self.__stats_file(action)]).wait() - - def check_pages_counts(self): - if not os.access(self.__stats_file("dump"), os.R_OK): - return - - stats_written = -1 - with open(self.__stats_file("dump"), 'rb') as stfile: - stats = crpc.images.load(stfile) - stent = stats['entries'][0]['dump'] - stats_written = int(stent['shpages_written']) + int(stent['pages_written']) - - real_written = 0 - for f in os.listdir(self.__ddir()): - if f.startswith('pages-'): - real_written += os.path.getsize(os.path.join(self.__ddir(), f)) - - r_pages = real_written / 4096 - r_off = real_written % 4096 - if (stats_written != r_pages) or (r_off != 0): - print("ERROR: bad page counts, stats = %d real = %d(%d)" % (stats_written, r_pages, r_off)) - raise test_fail_exc("page counts mismatch") - - def dump(self, action, opts = []): - self.__iter += 1 - os.mkdir(self.__ddir()) - os.chmod(self.__ddir(), 0o777) - - a_opts = ["-t", self.__test.getpid()] - if self.__prev_dump_iter: - a_opts += ["--prev-images-dir", "../%d" % self.__prev_dump_iter, "--track-mem"] - self.__prev_dump_iter = self.__iter - - if self.__page_server: - print("Adding page server") - - ps_opts = ["--port", "12345"] + self.__tls - if self.__dedup: - ps_opts += ["--auto-dedup"] - - self.__page_server_p = self.__criu_act("page-server", opts = ps_opts, nowait = True) - a_opts += ["--page-server", "--address", "127.0.0.1", "--port", "12345"] + self.__tls - - a_opts += self.__test.getdopts() - - if self.__remote: - logdir = os.getcwd() + "/" + self.__dump_path + "/" + str(self.__iter) - print("Adding image cache") - - cache_opts = [self.__criu_bin, "image-cache", "--port", "12345", "-v4", "-o", - logdir + "/image-cache.log", "-D", logdir] - - subprocess.Popen(cache_opts).pid - time.sleep(1) - - print("Adding image proxy") - - proxy_opts = [self.__criu_bin, "image-proxy", "--port", "12345", "--address", - "localhost", "-v4", "-o", logdir + "/image-proxy.log", - "-D", logdir] - - subprocess.Popen(proxy_opts).pid - time.sleep(1) - - a_opts += ["--remote"] - - if self.__dedup: - a_opts += ["--auto-dedup"] - - a_opts += ["--timeout", "10"] - - criu_dir = os.path.dirname(os.getcwd()) - if os.getenv("GCOV"): - a_opts.append('--external') - a_opts.append('mnt[%s]:zdtm' % criu_dir) - - if self.__leave_stopped: - a_opts += ['--leave-stopped'] - if self.__empty_ns: - a_opts += ['--empty-ns', 'net'] - - nowait = False - if self.__lazy_migrate and action == "dump": - a_opts += ["--lazy-pages", "--port", "12345"] + self.__tls - nowait = True - self.__dump_process = self.__criu_act(action, opts = a_opts + opts, nowait = nowait) - if self.__mdedup and self.__iter > 1: - self.__criu_act("dedup", opts = []) - - self.show_stats("dump") - self.check_pages_counts() - - if self.__leave_stopped: - pstree_check_stopped(self.__test.getpid()) - pstree_signal(self.__test.getpid(), signal.SIGKILL) - - if self.__page_server_p: - ret = self.__page_server_p.wait() - grep_errors(os.path.join(self.__ddir(), "page-server.log")) - self.__page_server_p = None - if ret: - raise test_fail_exc("criu page-server exited with %d" % ret) - - def restore(self): - r_opts = [] - if self.__restore_sibling: - r_opts = ["--restore-sibling"] - self.__test.auto_reap = False - r_opts += self.__test.getropts() - if self.__join_ns: - r_opts.append("--join-ns") - r_opts.append("net:%s" % join_ns_file) - if self.__empty_ns: - r_opts += ['--empty-ns', 'net'] - r_opts += ['--action-script', os.getcwd() + '/empty-netns-prep.sh'] - - if self.__remote: - r_opts += ["--remote"] - - if self.__dedup: - r_opts += ["--auto-dedup"] - - self.__prev_dump_iter = None - criu_dir = os.path.dirname(os.getcwd()) - if os.getenv("GCOV"): - r_opts.append('--external') - r_opts.append('mnt[zdtm]:%s' % criu_dir) - - if self.__lazy_pages or self.__lazy_migrate: - lp_opts = [] - if self.__remote_lazy_pages or self.__lazy_migrate: - lp_opts += ["--page-server", "--port", "12345", - "--address", "127.0.0.1"] + self.__tls - - if self.__remote_lazy_pages: - ps_opts = ["--pidfile", "ps.pid", - "--port", "12345", "--lazy-pages"] + self.__tls - self.__page_server_p = self.__criu_act("page-server", opts = ps_opts, nowait = True) - self.__lazy_pages_p = self.__criu_act("lazy-pages", opts = lp_opts, nowait = True) - r_opts += ["--lazy-pages"] - - if self.__leave_stopped: - r_opts += ['--leave-stopped'] - - self.__criu_act("restore", opts = r_opts + ["--restore-detached"]) - self.show_stats("restore") - - if self.__leave_stopped: - pstree_check_stopped(self.__test.getpid()) - pstree_signal(self.__test.getpid(), signal.SIGCONT) - - @staticmethod - def check(feature): - return criu_cli.run("check", ["--no-default-config", "-v0", - "--feature", feature], opts['criu_bin']) == 0 - - @staticmethod - def available(): - if not os.access(opts['criu_bin'], os.X_OK): - print("CRIU binary not found at %s" % opts['criu_bin']) - sys.exit(1) - - def kill(self): - if self.__lazy_pages_p: - self.__lazy_pages_p.terminate() - print("criu lazy-pages exited with %s" % self.__lazy_pages_p.wait()) - grep_errors(os.path.join(self.__ddir(), "lazy-pages.log")) - self.__lazy_pages_p = None - if self.__page_server_p: - self.__page_server_p.terminate() - print("criu page-server exited with %s" % self.__page_server_p.wait()) - grep_errors(os.path.join(self.__ddir(), "page-server.log")) - self.__page_server_p = None - if self.__dump_process: - self.__dump_process.terminate() - print("criu dump exited with %s" % self.__dump_process.wait()) - grep_errors(os.path.join(self.__ddir(), "dump.log")) - self.__dump_process = None + def __init__(self, opts): + self.__test = None + self.__dump_path = None + self.__iter = 0 + self.__prev_dump_iter = None + self.__page_server = bool(opts['page_server']) + self.__remote_lazy_pages = bool(opts['remote_lazy_pages']) + self.__lazy_pages = (self.__remote_lazy_pages + or bool(opts['lazy_pages'])) + self.__lazy_migrate = bool(opts['lazy_migrate']) + self.__restore_sibling = bool(opts['sibling']) + self.__join_ns = bool(opts['join_ns']) + self.__empty_ns = bool(opts['empty_ns']) + self.__fault = opts['fault'] + self.__script = opts['script'] + self.__sat = bool(opts['sat']) + self.__dedup = bool(opts['dedup']) + self.__mdedup = bool(opts['noauto_dedup']) + self.__user = bool(opts['user']) + self.__leave_stopped = bool(opts['stop']) + self.__remote = bool(opts['remote']) + self.__criu = (opts['rpc'] and criu_rpc or criu_cli) + self.__show_stats = bool(opts['show_stats']) + self.__lazy_pages_p = None + self.__page_server_p = None + self.__dump_process = None + self.__tls = self.__tls_options() if opts['tls'] else [] + self.__criu_bin = opts['criu_bin'] + self.__crit_bin = opts['crit_bin'] + + def fini(self): + if self.__lazy_migrate: + ret = self.__dump_process.wait() + if self.__lazy_pages_p: + ret = self.__lazy_pages_p.wait() + grep_errors(os.path.join(self.__ddir(), "lazy-pages.log")) + self.__lazy_pages_p = None + if ret: + raise test_fail_exc("criu lazy-pages exited with %s" % ret) + if self.__page_server_p: + ret = self.__page_server_p.wait() + grep_errors(os.path.join(self.__ddir(), "page-server.log")) + self.__page_server_p = None + if ret: + raise test_fail_exc("criu page-server exited with %s" % ret) + if self.__dump_process: + ret = self.__dump_process.wait() + grep_errors(os.path.join(self.__ddir(), "dump.log")) + self.__dump_process = None + if ret: + raise test_fail_exc("criu dump exited with %s" % ret) + return + + def logs(self): + return self.__dump_path + + def set_test(self, test): + self.__test = test + self.__dump_path = "dump/" + test.getname() + "/" + test.getpid() + if os.path.exists(self.__dump_path): + for i in range(100): + newpath = self.__dump_path + "." + str(i) + if not os.path.exists(newpath): + os.rename(self.__dump_path, newpath) + break + else: + raise test_fail_exc("couldn't find dump dir %s" % + self.__dump_path) + + os.makedirs(self.__dump_path) + + def cleanup(self): + if self.__dump_path: + print("Removing %s" % self.__dump_path) + shutil.rmtree(self.__dump_path) + + def __tls_options(self): + pki_dir = os.path.dirname(os.path.abspath(__file__)) + "/pki" + return [ + "--tls", "--tls-no-cn-verify", "--tls-key", pki_dir + "/key.pem", + "--tls-cert", pki_dir + "/cert.pem", "--tls-cacert", + pki_dir + "/cacert.pem" + ] + + def __ddir(self): + return os.path.join(self.__dump_path, "%d" % self.__iter) + + def set_user_id(self): + # Numbers should match those in zdtm_test + os.setresgid(58467, 58467, 58467) + os.setresuid(18943, 18943, 18943) + + def __criu_act(self, action, opts=[], log=None, nowait=False): + if not log: + log = action + ".log" + + s_args = ["-o", log, "-D", self.__ddir(), "-v4"] + opts + + with open(os.path.join(self.__ddir(), action + '.cropt'), 'w') as f: + f.write(' '.join(s_args) + '\n') + + print("Run criu " + action) + + strace = [] + if self.__sat: + fname = os.path.join(self.__ddir(), action + '.strace') + print_fname(fname, 'strace') + strace = ["strace", "-o", fname, '-T'] + if action == 'restore': + strace += ['-f'] + s_args += [ + '--action-script', + os.getcwd() + '/../scripts/fake-restore.sh' + ] + + if self.__script: + s_args += ['--action-script', self.__script] + + if action == "restore": + preexec = None + else: + preexec = self.__user and self.set_user_id or None + + __ddir = self.__ddir() + + status_fds = None + if nowait: + status_fds = os.pipe() + fd = status_fds[1] + fdflags = fcntl.fcntl(fd, fcntl.F_GETFD) + fcntl.fcntl(fd, fcntl.F_SETFD, fdflags & ~fcntl.FD_CLOEXEC) + s_args += ["--status-fd", str(fd)] + + with open("/proc/sys/kernel/ns_last_pid") as ns_last_pid_fd: + ns_last_pid = ns_last_pid_fd.read() + + ret = self.__criu.run(action, s_args, self.__criu_bin, self.__fault, + strace, preexec, nowait) + + if nowait: + os.close(status_fds[1]) + if os.read(status_fds[0], 1) != b'\0': + ret = ret.wait() + if self.__test.blocking(): + raise test_fail_expected_exc(action) + else: + raise test_fail_exc("criu %s exited with %s" % + (action, ret)) + os.close(status_fds[0]) + return ret + + grep_errors(os.path.join(__ddir, log)) + if ret != 0: + if self.__fault and int(self.__fault) < 128: + try_run_hook(self.__test, ["--fault", action]) + if action == "dump": + # create a clean directory for images + os.rename(__ddir, __ddir + ".fail") + os.mkdir(__ddir) + os.chmod(__ddir, 0o777) + else: + # on restore we move only a log file, because we need images + os.rename(os.path.join(__ddir, log), + os.path.join(__ddir, log + ".fail")) + # restore ns_last_pid to avoid a case when criu gets + # PID of one of restored processes. + with open("/proc/sys/kernel/ns_last_pid", "w+") as fd: + fd.write(ns_last_pid) + # try again without faults + print("Run criu " + action) + ret = self.__criu.run(action, s_args, self.__criu_bin, False, + strace, preexec) + grep_errors(os.path.join(__ddir, log)) + if ret == 0: + return + rst_succeeded = os.access( + os.path.join(__ddir, "restore-succeeded"), os.F_OK) + if self.__test.blocking() or (self.__sat and action == 'restore' + and rst_succeeded): + raise test_fail_expected_exc(action) + else: + raise test_fail_exc("CRIU %s" % action) + + def __stats_file(self, action): + return os.path.join(self.__ddir(), "stats-%s" % action) + + def show_stats(self, action): + if not self.__show_stats: + return + + subprocess.Popen([self.__crit_bin, "show", + self.__stats_file(action)]).wait() + + def check_pages_counts(self): + if not os.access(self.__stats_file("dump"), os.R_OK): + return + + stats_written = -1 + with open(self.__stats_file("dump"), 'rb') as stfile: + stats = crpc.images.load(stfile) + stent = stats['entries'][0]['dump'] + stats_written = int(stent['shpages_written']) + int( + stent['pages_written']) + + real_written = 0 + for f in os.listdir(self.__ddir()): + if f.startswith('pages-'): + real_written += os.path.getsize(os.path.join(self.__ddir(), f)) + + r_pages = real_written / 4096 + r_off = real_written % 4096 + if (stats_written != r_pages) or (r_off != 0): + print("ERROR: bad page counts, stats = %d real = %d(%d)" % + (stats_written, r_pages, r_off)) + raise test_fail_exc("page counts mismatch") + + def dump(self, action, opts=[]): + self.__iter += 1 + os.mkdir(self.__ddir()) + os.chmod(self.__ddir(), 0o777) + + a_opts = ["-t", self.__test.getpid()] + if self.__prev_dump_iter: + a_opts += [ + "--prev-images-dir", + "../%d" % self.__prev_dump_iter, "--track-mem" + ] + self.__prev_dump_iter = self.__iter + + if self.__page_server: + print("Adding page server") + + ps_opts = ["--port", "12345"] + self.__tls + if self.__dedup: + ps_opts += ["--auto-dedup"] + + self.__page_server_p = self.__criu_act("page-server", + opts=ps_opts, + nowait=True) + a_opts += [ + "--page-server", "--address", "127.0.0.1", "--port", "12345" + ] + self.__tls + + a_opts += self.__test.getdopts() + + if self.__remote: + logdir = os.getcwd() + "/" + self.__dump_path + "/" + str( + self.__iter) + print("Adding image cache") + + cache_opts = [ + self.__criu_bin, "image-cache", "--port", "12345", "-v4", "-o", + logdir + "/image-cache.log", "-D", logdir + ] + + subprocess.Popen(cache_opts).pid + time.sleep(1) + + print("Adding image proxy") + + proxy_opts = [ + self.__criu_bin, "image-proxy", "--port", "12345", "--address", + "localhost", "-v4", "-o", logdir + "/image-proxy.log", "-D", + logdir + ] + + subprocess.Popen(proxy_opts).pid + time.sleep(1) + + a_opts += ["--remote"] + + if self.__dedup: + a_opts += ["--auto-dedup"] + + a_opts += ["--timeout", "10"] + + criu_dir = os.path.dirname(os.getcwd()) + if os.getenv("GCOV"): + a_opts.append('--external') + a_opts.append('mnt[%s]:zdtm' % criu_dir) + + if self.__leave_stopped: + a_opts += ['--leave-stopped'] + if self.__empty_ns: + a_opts += ['--empty-ns', 'net'] + + nowait = False + if self.__lazy_migrate and action == "dump": + a_opts += ["--lazy-pages", "--port", "12345"] + self.__tls + nowait = True + self.__dump_process = self.__criu_act(action, + opts=a_opts + opts, + nowait=nowait) + if self.__mdedup and self.__iter > 1: + self.__criu_act("dedup", opts=[]) + + self.show_stats("dump") + self.check_pages_counts() + + if self.__leave_stopped: + pstree_check_stopped(self.__test.getpid()) + pstree_signal(self.__test.getpid(), signal.SIGKILL) + + if self.__page_server_p: + ret = self.__page_server_p.wait() + grep_errors(os.path.join(self.__ddir(), "page-server.log")) + self.__page_server_p = None + if ret: + raise test_fail_exc("criu page-server exited with %d" % ret) + + def restore(self): + r_opts = [] + if self.__restore_sibling: + r_opts = ["--restore-sibling"] + self.__test.auto_reap = False + r_opts += self.__test.getropts() + if self.__join_ns: + r_opts.append("--join-ns") + r_opts.append("net:%s" % join_ns_file) + if self.__empty_ns: + r_opts += ['--empty-ns', 'net'] + r_opts += ['--action-script', os.getcwd() + '/empty-netns-prep.sh'] + + if self.__remote: + r_opts += ["--remote"] + + if self.__dedup: + r_opts += ["--auto-dedup"] + + self.__prev_dump_iter = None + criu_dir = os.path.dirname(os.getcwd()) + if os.getenv("GCOV"): + r_opts.append('--external') + r_opts.append('mnt[zdtm]:%s' % criu_dir) + + if self.__lazy_pages or self.__lazy_migrate: + lp_opts = [] + if self.__remote_lazy_pages or self.__lazy_migrate: + lp_opts += [ + "--page-server", "--port", "12345", "--address", + "127.0.0.1" + ] + self.__tls + + if self.__remote_lazy_pages: + ps_opts = [ + "--pidfile", "ps.pid", "--port", "12345", "--lazy-pages" + ] + self.__tls + self.__page_server_p = self.__criu_act("page-server", + opts=ps_opts, + nowait=True) + self.__lazy_pages_p = self.__criu_act("lazy-pages", + opts=lp_opts, + nowait=True) + r_opts += ["--lazy-pages"] + + if self.__leave_stopped: + r_opts += ['--leave-stopped'] + + self.__criu_act("restore", opts=r_opts + ["--restore-detached"]) + self.show_stats("restore") + + if self.__leave_stopped: + pstree_check_stopped(self.__test.getpid()) + pstree_signal(self.__test.getpid(), signal.SIGCONT) + + @staticmethod + def check(feature): + return criu_cli.run( + "check", ["--no-default-config", "-v0", "--feature", feature], + opts['criu_bin']) == 0 + + @staticmethod + def available(): + if not os.access(opts['criu_bin'], os.X_OK): + print("CRIU binary not found at %s" % opts['criu_bin']) + sys.exit(1) + + def kill(self): + if self.__lazy_pages_p: + self.__lazy_pages_p.terminate() + print("criu lazy-pages exited with %s" % + self.__lazy_pages_p.wait()) + grep_errors(os.path.join(self.__ddir(), "lazy-pages.log")) + self.__lazy_pages_p = None + if self.__page_server_p: + self.__page_server_p.terminate() + print("criu page-server exited with %s" % + self.__page_server_p.wait()) + grep_errors(os.path.join(self.__ddir(), "page-server.log")) + self.__page_server_p = None + if self.__dump_process: + self.__dump_process.terminate() + print("criu dump exited with %s" % self.__dump_process.wait()) + grep_errors(os.path.join(self.__ddir(), "dump.log")) + self.__dump_process = None def try_run_hook(test, args): - hname = test.getname() + '.hook' - if os.access(hname, os.X_OK): - print("Running %s(%s)" % (hname, ', '.join(args))) - hook = subprocess.Popen([hname] + args) - if hook.wait() != 0: - raise test_fail_exc("hook " + " ".join(args)) + hname = test.getname() + '.hook' + if os.access(hname, os.X_OK): + print("Running %s(%s)" % (hname, ', '.join(args))) + hook = subprocess.Popen([hname] + args) + if hook.wait() != 0: + raise test_fail_exc("hook " + " ".join(args)) # @@ -1311,583 +1404,615 @@ def try_run_hook(test, args): def init_sbs(): - if sys.stdout.isatty(): - global do_sbs - do_sbs = True - else: - print("Can't do step-by-step in this runtime") + if sys.stdout.isatty(): + global do_sbs + do_sbs = True + else: + print("Can't do step-by-step in this runtime") def sbs(what): - if do_sbs: - input("Pause at %s. Press Enter to continue." % what) + if do_sbs: + input("Pause at %s. Press Enter to continue." % what) # # Main testing entity -- dump (probably with pre-dumps) and restore # def iter_parm(opt, dflt): - x = ((opt or str(dflt)) + ":0").split(':') - return (range(0, int(x[0])), float(x[1])) + x = ((opt or str(dflt)) + ":0").split(':') + return (range(0, int(x[0])), float(x[1])) def cr(cr_api, test, opts): - if opts['nocr']: - return - - cr_api.set_test(test) - - iters = iter_parm(opts['iters'], 1) - for i in iters[0]: - pres = iter_parm(opts['pre'], 0) - for p in pres[0]: - if opts['snaps']: - cr_api.dump("dump", opts = ["--leave-running", "--track-mem"]) - else: - cr_api.dump("pre-dump") - try_run_hook(test, ["--post-pre-dump"]) - test.pre_dump_notify() - time.sleep(pres[1]) - - sbs('pre-dump') - - os.environ["ZDTM_TEST_PID"] = str(test.getpid()) - if opts['norst']: - try_run_hook(test, ["--pre-dump"]) - cr_api.dump("dump", opts = ["--leave-running"]) - else: - try_run_hook(test, ["--pre-dump"]) - cr_api.dump("dump") - if not opts['lazy_migrate']: - test.gone() - else: - test.unlink_pidfile() - sbs('pre-restore') - try_run_hook(test, ["--pre-restore"]) - cr_api.restore() - os.environ["ZDTM_TEST_PID"] = str(test.getpid()) - os.environ["ZDTM_IMG_DIR"] = cr_api.logs() - try_run_hook(test, ["--post-restore"]) - sbs('post-restore') - - time.sleep(iters[1]) + if opts['nocr']: + return + + cr_api.set_test(test) + + iters = iter_parm(opts['iters'], 1) + for i in iters[0]: + pres = iter_parm(opts['pre'], 0) + for p in pres[0]: + if opts['snaps']: + cr_api.dump("dump", opts=["--leave-running", "--track-mem"]) + else: + cr_api.dump("pre-dump") + try_run_hook(test, ["--post-pre-dump"]) + test.pre_dump_notify() + time.sleep(pres[1]) + + sbs('pre-dump') + + os.environ["ZDTM_TEST_PID"] = str(test.getpid()) + if opts['norst']: + try_run_hook(test, ["--pre-dump"]) + cr_api.dump("dump", opts=["--leave-running"]) + else: + try_run_hook(test, ["--pre-dump"]) + cr_api.dump("dump") + if not opts['lazy_migrate']: + test.gone() + else: + test.unlink_pidfile() + sbs('pre-restore') + try_run_hook(test, ["--pre-restore"]) + cr_api.restore() + os.environ["ZDTM_TEST_PID"] = str(test.getpid()) + os.environ["ZDTM_IMG_DIR"] = cr_api.logs() + try_run_hook(test, ["--post-restore"]) + sbs('post-restore') + + time.sleep(iters[1]) # Additional checks that can be done outside of test process + def get_visible_state(test): - maps = {} - files = {} - mounts = {} - - if not getattr(test, "static", lambda: False)() or \ - not getattr(test, "ns", lambda: False)(): - return ({}, {}, {}) - - r = re.compile('^[0-9]+$') - pids = filter(lambda p: r.match(p), os.listdir("/proc/%s/root/proc/" % test.getpid())) - for pid in pids: - files[pid] = set(os.listdir("/proc/%s/root/proc/%s/fd" % (test.getpid(), pid))) - - cmaps = [[0, 0, ""]] - last = 0 - mapsfd = open("/proc/%s/root/proc/%s/maps" % (test.getpid(), pid)) - for mp in mapsfd: - m = list(map(lambda x: int('0x' + x, 0), mp.split()[0].split('-'))) - - m.append(mp.split()[1]) - - f = "/proc/%s/root/proc/%s/map_files/%s" % (test.getpid(), pid, mp.split()[0]) - if os.access(f, os.F_OK): - st = os.lstat(f) - m.append(oct(st.st_mode)) - - if cmaps[last][1] == m[0] and cmaps[last][2] == m[2]: - cmaps[last][1] = m[1] - else: - cmaps.append(m) - last += 1 - mapsfd.close() - - maps[pid] = set(map(lambda x: '%x-%x %s' % (x[0], x[1], " ".join(x[2:])), cmaps)) - - cmounts = [] - try: - r = re.compile(r"^\S+\s\S+\s\S+\s(\S+)\s(\S+)\s(\S+)\s[^-]*?(shared)?[^-]*?(master)?[^-]*?-") - with open("/proc/%s/root/proc/%s/mountinfo" % (test.getpid(), pid)) as mountinfo: - for m in mountinfo: - cmounts.append(r.match(m).groups()) - except IOError as e: - if e.errno != errno.EINVAL: - raise e - mounts[pid] = cmounts - return files, maps, mounts + maps = {} + files = {} + mounts = {} + + if not getattr(test, "static", lambda: False)() or \ + not getattr(test, "ns", lambda: False)(): + return ({}, {}, {}) + + r = re.compile('^[0-9]+$') + pids = filter(lambda p: r.match(p), + os.listdir("/proc/%s/root/proc/" % test.getpid())) + for pid in pids: + files[pid] = set( + os.listdir("/proc/%s/root/proc/%s/fd" % (test.getpid(), pid))) + + cmaps = [[0, 0, ""]] + last = 0 + mapsfd = open("/proc/%s/root/proc/%s/maps" % (test.getpid(), pid)) + for mp in mapsfd: + m = list(map(lambda x: int('0x' + x, 0), mp.split()[0].split('-'))) + + m.append(mp.split()[1]) + + f = "/proc/%s/root/proc/%s/map_files/%s" % (test.getpid(), pid, + mp.split()[0]) + if os.access(f, os.F_OK): + st = os.lstat(f) + m.append(oct(st.st_mode)) + + if cmaps[last][1] == m[0] and cmaps[last][2] == m[2]: + cmaps[last][1] = m[1] + else: + cmaps.append(m) + last += 1 + mapsfd.close() + + maps[pid] = set( + map(lambda x: '%x-%x %s' % (x[0], x[1], " ".join(x[2:])), cmaps)) + + cmounts = [] + try: + r = re.compile( + r"^\S+\s\S+\s\S+\s(\S+)\s(\S+)\s(\S+)\s[^-]*?(shared)?[^-]*?(master)?[^-]*?-" + ) + with open("/proc/%s/root/proc/%s/mountinfo" % + (test.getpid(), pid)) as mountinfo: + for m in mountinfo: + cmounts.append(r.match(m).groups()) + except IOError as e: + if e.errno != errno.EINVAL: + raise e + mounts[pid] = cmounts + return files, maps, mounts def check_visible_state(test, state, opts): - new = get_visible_state(test) - - for pid in state[0].keys(): - fnew = new[0][pid] - fold = state[0][pid] - if fnew != fold: - print("%s: Old files lost: %s" % (pid, fold - fnew)) - print("%s: New files appeared: %s" % (pid, fnew - fold)) - raise test_fail_exc("fds compare") - - old_maps = state[1][pid] - new_maps = new[1][pid] - if os.getenv("COMPAT_TEST"): - # the vsyscall vma isn't unmapped from x32 processes - vsyscall = u"ffffffffff600000-ffffffffff601000 r-xp" - if vsyscall in new_maps and vsyscall not in old_maps: - new_maps.remove(vsyscall) - if old_maps != new_maps: - print("%s: Old maps lost: %s" % (pid, old_maps - new_maps)) - print("%s: New maps appeared: %s" % (pid, new_maps - old_maps)) - if not opts['fault']: # skip parasite blob - raise test_fail_exc("maps compare") - - old_mounts = state[2][pid] - new_mounts = new[2][pid] - for i in range(len(old_mounts)): - m = old_mounts.pop(0) - if m in new_mounts: - new_mounts.remove(m) - else: - old_mounts.append(m) - if old_mounts or new_mounts: - print("%s: Old mounts lost: %s" % (pid, old_mounts)) - print("%s: New mounts appeared: %s" % (pid, new_mounts)) - raise test_fail_exc("mounts compare") - - if '--link-remap' in test.getdopts(): - import glob - link_remap_list = glob.glob(os.path.dirname(test.getname()) + '/link_remap*') - if link_remap_list: - print("%s: link-remap files left: %s" % (test.getname(), link_remap_list)) - raise test_fail_exc("link remaps left") + new = get_visible_state(test) + + for pid in state[0].keys(): + fnew = new[0][pid] + fold = state[0][pid] + if fnew != fold: + print("%s: Old files lost: %s" % (pid, fold - fnew)) + print("%s: New files appeared: %s" % (pid, fnew - fold)) + raise test_fail_exc("fds compare") + + old_maps = state[1][pid] + new_maps = new[1][pid] + if os.getenv("COMPAT_TEST"): + # the vsyscall vma isn't unmapped from x32 processes + vsyscall = u"ffffffffff600000-ffffffffff601000 r-xp" + if vsyscall in new_maps and vsyscall not in old_maps: + new_maps.remove(vsyscall) + if old_maps != new_maps: + print("%s: Old maps lost: %s" % (pid, old_maps - new_maps)) + print("%s: New maps appeared: %s" % (pid, new_maps - old_maps)) + if not opts['fault']: # skip parasite blob + raise test_fail_exc("maps compare") + + old_mounts = state[2][pid] + new_mounts = new[2][pid] + for i in range(len(old_mounts)): + m = old_mounts.pop(0) + if m in new_mounts: + new_mounts.remove(m) + else: + old_mounts.append(m) + if old_mounts or new_mounts: + print("%s: Old mounts lost: %s" % (pid, old_mounts)) + print("%s: New mounts appeared: %s" % (pid, new_mounts)) + raise test_fail_exc("mounts compare") + + if '--link-remap' in test.getdopts(): + import glob + link_remap_list = glob.glob( + os.path.dirname(test.getname()) + '/link_remap*') + if link_remap_list: + print("%s: link-remap files left: %s" % + (test.getname(), link_remap_list)) + raise test_fail_exc("link remaps left") class noop_freezer: - def __init__(self): - self.kernel = False + def __init__(self): + self.kernel = False - def attach(self): - pass + def attach(self): + pass - def freeze(self): - pass + def freeze(self): + pass - def thaw(self): - pass + def thaw(self): + pass - def getdopts(self): - return [] + def getdopts(self): + return [] - def getropts(self): - return [] + def getropts(self): + return [] class cg_freezer: - def __init__(self, path, state): - self.__path = '/sys/fs/cgroup/freezer/' + path - self.__state = state - self.kernel = True + def __init__(self, path, state): + self.__path = '/sys/fs/cgroup/freezer/' + path + self.__state = state + self.kernel = True - def attach(self): - if not os.access(self.__path, os.F_OK): - os.makedirs(self.__path) - with open(self.__path + '/tasks', 'w') as f: - f.write('0') + def attach(self): + if not os.access(self.__path, os.F_OK): + os.makedirs(self.__path) + with open(self.__path + '/tasks', 'w') as f: + f.write('0') - def __set_state(self, state): - with open(self.__path + '/freezer.state', 'w') as f: - f.write(state) + def __set_state(self, state): + with open(self.__path + '/freezer.state', 'w') as f: + f.write(state) - def freeze(self): - if self.__state.startswith('f'): - self.__set_state('FROZEN') + def freeze(self): + if self.__state.startswith('f'): + self.__set_state('FROZEN') - def thaw(self): - if self.__state.startswith('f'): - self.__set_state('THAWED') + def thaw(self): + if self.__state.startswith('f'): + self.__set_state('THAWED') - def getdopts(self): - return ['--freeze-cgroup', self.__path, '--manage-cgroups'] + def getdopts(self): + return ['--freeze-cgroup', self.__path, '--manage-cgroups'] - def getropts(self): - return ['--manage-cgroups'] + def getropts(self): + return ['--manage-cgroups'] def get_freezer(desc): - if not desc: - return noop_freezer() + if not desc: + return noop_freezer() - fd = desc.split(':') - fr = cg_freezer(path = fd[0], state = fd[1]) - return fr + fd = desc.split(':') + fr = cg_freezer(path=fd[0], state=fd[1]) + return fr def cmp_ns(ns1, match, ns2, msg): - ns1_ino = os.stat(ns1).st_ino - ns2_ino = os.stat(ns2).st_ino - if eval("%r %s %r" % (ns1_ino, match, ns2_ino)): - print("%s match (%r %s %r) fail" % (msg, ns1_ino, match, ns2_ino)) - raise test_fail_exc("%s compare" % msg) + ns1_ino = os.stat(ns1).st_ino + ns2_ino = os.stat(ns2).st_ino + if eval("%r %s %r" % (ns1_ino, match, ns2_ino)): + print("%s match (%r %s %r) fail" % (msg, ns1_ino, match, ns2_ino)) + raise test_fail_exc("%s compare" % msg) def check_joinns_state(t): - cmp_ns("/proc/%s/ns/net" % t.getpid(), "!=", join_ns_file, "join-ns") + cmp_ns("/proc/%s/ns/net" % t.getpid(), "!=", join_ns_file, "join-ns") def pstree_each_pid(root_pid): - f_children_path = "/proc/{0}/task/{0}/children".format(root_pid) - child_pids = [] - try: - with open(f_children_path, "r") as f_children: - pid_line = f_children.readline().strip(" \n") - if pid_line: - child_pids += pid_line.split(" ") - except Exception as e: - print("Unable to read /proc/*/children: %s" % e) - return # process is dead - - yield root_pid - for child_pid in child_pids: - for pid in pstree_each_pid(child_pid): - yield pid + f_children_path = "/proc/{0}/task/{0}/children".format(root_pid) + child_pids = [] + try: + with open(f_children_path, "r") as f_children: + pid_line = f_children.readline().strip(" \n") + if pid_line: + child_pids += pid_line.split(" ") + except Exception as e: + print("Unable to read /proc/*/children: %s" % e) + return # process is dead + + yield root_pid + for child_pid in child_pids: + for pid in pstree_each_pid(child_pid): + yield pid def is_proc_stopped(pid): - def get_thread_status(thread_dir): - try: - with open(os.path.join(thread_dir, "status")) as f_status: - for line in f_status.readlines(): - if line.startswith("State:"): - return line.split(":", 1)[1].strip().split(" ")[0] - except Exception as e: - print("Unable to read a thread status: %s" % e) - pass # process is dead - return None - - def is_thread_stopped(status): - return (status is None) or (status == "T") or (status == "Z") - - tasks_dir = "/proc/%s/task" % pid - thread_dirs = [] - try: - thread_dirs = os.listdir(tasks_dir) - except Exception as e: - print("Unable to read threads: %s" % e) - pass # process is dead - - for thread_dir in thread_dirs: - thread_status = get_thread_status(os.path.join(tasks_dir, thread_dir)) - if not is_thread_stopped(thread_status): - return False - - if not is_thread_stopped(get_thread_status("/proc/%s" % pid)): - return False - - return True + def get_thread_status(thread_dir): + try: + with open(os.path.join(thread_dir, "status")) as f_status: + for line in f_status.readlines(): + if line.startswith("State:"): + return line.split(":", 1)[1].strip().split(" ")[0] + except Exception as e: + print("Unable to read a thread status: %s" % e) + pass # process is dead + return None + + def is_thread_stopped(status): + return (status is None) or (status == "T") or (status == "Z") + + tasks_dir = "/proc/%s/task" % pid + thread_dirs = [] + try: + thread_dirs = os.listdir(tasks_dir) + except Exception as e: + print("Unable to read threads: %s" % e) + pass # process is dead + + for thread_dir in thread_dirs: + thread_status = get_thread_status(os.path.join(tasks_dir, thread_dir)) + if not is_thread_stopped(thread_status): + return False + + if not is_thread_stopped(get_thread_status("/proc/%s" % pid)): + return False + + return True def pstree_check_stopped(root_pid): - for pid in pstree_each_pid(root_pid): - if not is_proc_stopped(pid): - raise test_fail_exc("CRIU --leave-stopped %s" % pid) + for pid in pstree_each_pid(root_pid): + if not is_proc_stopped(pid): + raise test_fail_exc("CRIU --leave-stopped %s" % pid) def pstree_signal(root_pid, signal): - for pid in pstree_each_pid(root_pid): - try: - os.kill(int(pid), signal) - except Exception as e: - print("Unable to kill %d: %s" % (pid, e)) - pass # process is dead + for pid in pstree_each_pid(root_pid): + try: + os.kill(int(pid), signal) + except Exception as e: + print("Unable to kill %d: %s" % (pid, e)) + pass # process is dead def do_run_test(tname, tdesc, flavs, opts): - tcname = tname.split('/')[0] - tclass = test_classes.get(tcname, None) - if not tclass: - print("Unknown test class %s" % tcname) - return - - if opts['report']: - init_report(opts['report']) - if opts['sbs']: - init_sbs() - - fcg = get_freezer(opts['freezecg']) - - for f in flavs: - print_sep("Run %s in %s" % (tname, f)) - if opts['dry_run']: - continue - flav = flavors[f](opts) - t = tclass(tname, tdesc, flav, fcg) - cr_api = criu(opts) - - try: - t.start() - s = get_visible_state(t) - try: - cr(cr_api, t, opts) - except test_fail_expected_exc as e: - if e.cr_action == "dump": - t.stop() - else: - check_visible_state(t, s, opts) - if opts['join_ns']: - check_joinns_state(t) - t.stop() - cr_api.fini() - try_run_hook(t, ["--clean"]) - except test_fail_exc as e: - print_sep("Test %s FAIL at %s" % (tname, e.step), '#') - t.print_output() - t.kill() - cr_api.kill() - try_run_hook(t, ["--clean"]) - if cr_api.logs(): - add_to_report(cr_api.logs(), tname.replace('/', '_') + "_" + f + "/images") - if opts['keep_img'] == 'never': - cr_api.cleanup() - # When option --keep-going not specified this exit - # does two things: exits from subprocess and aborts the - # main script execution on the 1st error met - sys.exit(encode_flav(f)) - else: - if opts['keep_img'] != 'always': - cr_api.cleanup() - print_sep("Test %s PASS" % tname) + tcname = tname.split('/')[0] + tclass = test_classes.get(tcname, None) + if not tclass: + print("Unknown test class %s" % tcname) + return + + if opts['report']: + init_report(opts['report']) + if opts['sbs']: + init_sbs() + + fcg = get_freezer(opts['freezecg']) + + for f in flavs: + print_sep("Run %s in %s" % (tname, f)) + if opts['dry_run']: + continue + flav = flavors[f](opts) + t = tclass(tname, tdesc, flav, fcg) + cr_api = criu(opts) + + try: + t.start() + s = get_visible_state(t) + try: + cr(cr_api, t, opts) + except test_fail_expected_exc as e: + if e.cr_action == "dump": + t.stop() + else: + check_visible_state(t, s, opts) + if opts['join_ns']: + check_joinns_state(t) + t.stop() + cr_api.fini() + try_run_hook(t, ["--clean"]) + except test_fail_exc as e: + print_sep("Test %s FAIL at %s" % (tname, e.step), '#') + t.print_output() + t.kill() + cr_api.kill() + try_run_hook(t, ["--clean"]) + if cr_api.logs(): + add_to_report(cr_api.logs(), + tname.replace('/', '_') + "_" + f + "/images") + if opts['keep_img'] == 'never': + cr_api.cleanup() + # When option --keep-going not specified this exit + # does two things: exits from subprocess and aborts the + # main script execution on the 1st error met + sys.exit(encode_flav(f)) + else: + if opts['keep_img'] != 'always': + cr_api.cleanup() + print_sep("Test %s PASS" % tname) class Launcher: - def __init__(self, opts, nr_tests): - self.__opts = opts - self.__total = nr_tests - self.__runtest = 0 - self.__nr = 0 - self.__max = int(opts['parallel'] or 1) - self.__subs = {} - self.__fail = False - self.__file_report = None - self.__junit_file = None - self.__junit_test_cases = None - self.__failed = [] - self.__nr_skip = 0 - if self.__max > 1 and self.__total > 1: - self.__use_log = True - elif opts['report']: - self.__use_log = True - else: - self.__use_log = False - - if opts['report'] and (opts['keep_going'] or self.__total == 1): - global TestSuite, TestCase - from junit_xml import TestSuite, TestCase - now = datetime.datetime.now() - att = 0 - reportname = os.path.join(report_dir, "criu-testreport.tap") - junitreport = os.path.join(report_dir, "criu-testreport.xml") - while os.access(reportname, os.F_OK) or os.access(junitreport, os.F_OK): - reportname = os.path.join(report_dir, "criu-testreport" + ".%d.tap" % att) - junitreport = os.path.join(report_dir, "criu-testreport" + ".%d.xml" % att) - att += 1 - - self.__junit_file = open(junitreport, 'a') - self.__junit_test_cases = [] - - self.__file_report = open(reportname, 'a') - print(u"TAP version 13", file=self.__file_report) - print(u"# Hardware architecture: " + arch, file=self.__file_report) - print(u"# Timestamp: " + now.strftime("%Y-%m-%d %H:%M") + " (GMT+1)", file=self.__file_report) - print(u"# ", file=self.__file_report) - print(u"1.." + str(nr_tests), file=self.__file_report) - with open("/proc/sys/kernel/tainted") as taintfd: - self.__taint = taintfd.read() - if int(self.__taint, 0) != 0: - print("The kernel is tainted: %r" % self.__taint) - if not opts["ignore_taint"]: - raise Exception("The kernel is tainted: %r" % self.__taint) - - def __show_progress(self, msg): - perc = int(self.__nr * 16 / self.__total) - print("=== Run %d/%d %s %s" % (self.__nr, self.__total, '=' * perc + '-' * (16 - perc), msg)) - - def skip(self, name, reason): - print("Skipping %s (%s)" % (name, reason)) - self.__nr += 1 - self.__runtest += 1 - self.__nr_skip += 1 - - if self.__junit_test_cases is not None: - tc = TestCase(name) - tc.add_skipped_info(reason) - self.__junit_test_cases.append(tc) - if self.__file_report: - testline = u"ok %d - %s # SKIP %s" % (self.__runtest, name, reason) - print(testline, file=self.__file_report) - - def run_test(self, name, desc, flavor): - - if len(self.__subs) >= self.__max: - self.wait() - - with open("/proc/sys/kernel/tainted") as taintfd: - taint = taintfd.read() - if self.__taint != taint: - raise Exception("The kernel is tainted: %r (%r)" % (taint, self.__taint)) - - if test_flag(desc, 'excl'): - self.wait_all() - - self.__nr += 1 - self.__show_progress(name) - - nd = ('nocr', 'norst', 'pre', 'iters', 'page_server', 'sibling', 'stop', 'empty_ns', - 'fault', 'keep_img', 'report', 'snaps', 'sat', 'script', 'rpc', 'lazy_pages', - 'join_ns', 'dedup', 'sbs', 'freezecg', 'user', 'dry_run', 'noauto_dedup', - 'remote_lazy_pages', 'show_stats', 'lazy_migrate', 'remote', 'tls', - 'criu_bin', 'crit_bin') - arg = repr((name, desc, flavor, {d: self.__opts[d] for d in nd})) - - if self.__use_log: - logf = name.replace('/', '_') + ".log" - log = open(logf, "w") - else: - logf = None - log = None - - sub = subprocess.Popen(["./zdtm_ct", "zdtm.py"], - env = dict(os.environ, CR_CT_TEST_INFO = arg), - stdout = log, stderr = subprocess.STDOUT, close_fds = True) - self.__subs[sub.pid] = {'sub': sub, 'log': logf, 'name': name, "start": time.time()} - - if test_flag(desc, 'excl'): - self.wait() - - def __wait_one(self, flags): - pid = -1 - status = -1 - signal.alarm(10) - while True: - try: - pid, status = os.waitpid(0, flags) - except OSError as e: - if e.errno == errno.EINTR: - subprocess.Popen(["ps", "axf"]).wait() - continue - signal.alarm(0) - raise e - else: - break - signal.alarm(0) - - self.__runtest += 1 - if pid != 0: - sub = self.__subs.pop(pid) - tc = None - if self.__junit_test_cases is not None: - tc = TestCase(sub['name'], elapsed_sec=time.time() - sub['start']) - self.__junit_test_cases.append(tc) - if status != 0: - self.__fail = True - failed_flavor = decode_flav(os.WEXITSTATUS(status)) - self.__failed.append([sub['name'], failed_flavor]) - if self.__file_report: - testline = u"not ok %d - %s # flavor %s" % (self.__runtest, sub['name'], failed_flavor) - with open(sub['log']) as sublog: - output = sublog.read() - details = {'output': output} - tc.add_error_info(output = output) - print(testline, file=self.__file_report) - print("%s" % yaml.safe_dump(details, explicit_start=True, - explicit_end=True, default_style='|'), file=self.__file_report) - if sub['log']: - add_to_output(sub['log']) - else: - if self.__file_report: - testline = u"ok %d - %s" % (self.__runtest, sub['name']) - print(testline, file=self.__file_report) - - if sub['log']: - with open(sub['log']) as sublog: - print("%s" % sublog.read().encode('ascii', 'ignore').decode('utf-8')) - os.unlink(sub['log']) - - return True - - return False - - def __wait_all(self): - while self.__subs: - self.__wait_one(0) - - def wait(self): - self.__wait_one(0) - while self.__subs: - if not self.__wait_one(os.WNOHANG): - break - if self.__fail and not opts['keep_going']: - raise test_fail_exc('') - - def wait_all(self): - self.__wait_all() - if self.__fail and not opts['keep_going']: - raise test_fail_exc('') - - def finish(self): - self.__wait_all() - if not opts['fault'] and check_core_files(): - self.__fail = True - if self.__file_report: - ts = TestSuite(opts['title'], self.__junit_test_cases, os.getenv("NODE_NAME")) - self.__junit_file.write(TestSuite.to_xml_string([ts])) - self.__junit_file.close() - self.__file_report.close() - - if opts['keep_going']: - if self.__fail: - print_sep("%d TEST(S) FAILED (TOTAL %d/SKIPPED %d)" - % (len(self.__failed), self.__total, self.__nr_skip), "#") - for failed in self.__failed: - print(" * %s(%s)" % (failed[0], failed[1])) - else: - print_sep("ALL TEST(S) PASSED (TOTAL %d/SKIPPED %d)" - % (self.__total, self.__nr_skip), "#") - - if self.__fail: - print_sep("FAIL", "#") - sys.exit(1) + def __init__(self, opts, nr_tests): + self.__opts = opts + self.__total = nr_tests + self.__runtest = 0 + self.__nr = 0 + self.__max = int(opts['parallel'] or 1) + self.__subs = {} + self.__fail = False + self.__file_report = None + self.__junit_file = None + self.__junit_test_cases = None + self.__failed = [] + self.__nr_skip = 0 + if self.__max > 1 and self.__total > 1: + self.__use_log = True + elif opts['report']: + self.__use_log = True + else: + self.__use_log = False + + if opts['report'] and (opts['keep_going'] or self.__total == 1): + global TestSuite, TestCase + from junit_xml import TestSuite, TestCase + now = datetime.datetime.now() + att = 0 + reportname = os.path.join(report_dir, "criu-testreport.tap") + junitreport = os.path.join(report_dir, "criu-testreport.xml") + while os.access(reportname, os.F_OK) or os.access( + junitreport, os.F_OK): + reportname = os.path.join(report_dir, + "criu-testreport" + ".%d.tap" % att) + junitreport = os.path.join(report_dir, + "criu-testreport" + ".%d.xml" % att) + att += 1 + + self.__junit_file = open(junitreport, 'a') + self.__junit_test_cases = [] + + self.__file_report = open(reportname, 'a') + print(u"TAP version 13", file=self.__file_report) + print(u"# Hardware architecture: " + arch, file=self.__file_report) + print(u"# Timestamp: " + now.strftime("%Y-%m-%d %H:%M") + + " (GMT+1)", + file=self.__file_report) + print(u"# ", file=self.__file_report) + print(u"1.." + str(nr_tests), file=self.__file_report) + with open("/proc/sys/kernel/tainted") as taintfd: + self.__taint = taintfd.read() + if int(self.__taint, 0) != 0: + print("The kernel is tainted: %r" % self.__taint) + if not opts["ignore_taint"]: + raise Exception("The kernel is tainted: %r" % self.__taint) + + def __show_progress(self, msg): + perc = int(self.__nr * 16 / self.__total) + print("=== Run %d/%d %s %s" % + (self.__nr, self.__total, '=' * perc + '-' * (16 - perc), msg)) + + def skip(self, name, reason): + print("Skipping %s (%s)" % (name, reason)) + self.__nr += 1 + self.__runtest += 1 + self.__nr_skip += 1 + + if self.__junit_test_cases is not None: + tc = TestCase(name) + tc.add_skipped_info(reason) + self.__junit_test_cases.append(tc) + if self.__file_report: + testline = u"ok %d - %s # SKIP %s" % (self.__runtest, name, reason) + print(testline, file=self.__file_report) + + def run_test(self, name, desc, flavor): + + if len(self.__subs) >= self.__max: + self.wait() + + with open("/proc/sys/kernel/tainted") as taintfd: + taint = taintfd.read() + if self.__taint != taint: + raise Exception("The kernel is tainted: %r (%r)" % + (taint, self.__taint)) + + if test_flag(desc, 'excl'): + self.wait_all() + + self.__nr += 1 + self.__show_progress(name) + + nd = ('nocr', 'norst', 'pre', 'iters', 'page_server', 'sibling', + 'stop', 'empty_ns', 'fault', 'keep_img', 'report', 'snaps', + 'sat', 'script', 'rpc', 'lazy_pages', 'join_ns', 'dedup', 'sbs', + 'freezecg', 'user', 'dry_run', 'noauto_dedup', + 'remote_lazy_pages', 'show_stats', 'lazy_migrate', 'remote', + 'tls', 'criu_bin', 'crit_bin') + arg = repr((name, desc, flavor, {d: self.__opts[d] for d in nd})) + + if self.__use_log: + logf = name.replace('/', '_') + ".log" + log = open(logf, "w") + else: + logf = None + log = None + + sub = subprocess.Popen(["./zdtm_ct", "zdtm.py"], + env=dict(os.environ, CR_CT_TEST_INFO=arg), + stdout=log, + stderr=subprocess.STDOUT, + close_fds=True) + self.__subs[sub.pid] = { + 'sub': sub, + 'log': logf, + 'name': name, + "start": time.time() + } + + if test_flag(desc, 'excl'): + self.wait() + + def __wait_one(self, flags): + pid = -1 + status = -1 + signal.alarm(10) + while True: + try: + pid, status = os.waitpid(0, flags) + except OSError as e: + if e.errno == errno.EINTR: + subprocess.Popen(["ps", "axf"]).wait() + continue + signal.alarm(0) + raise e + else: + break + signal.alarm(0) + + self.__runtest += 1 + if pid != 0: + sub = self.__subs.pop(pid) + tc = None + if self.__junit_test_cases is not None: + tc = TestCase(sub['name'], + elapsed_sec=time.time() - sub['start']) + self.__junit_test_cases.append(tc) + if status != 0: + self.__fail = True + failed_flavor = decode_flav(os.WEXITSTATUS(status)) + self.__failed.append([sub['name'], failed_flavor]) + if self.__file_report: + testline = u"not ok %d - %s # flavor %s" % ( + self.__runtest, sub['name'], failed_flavor) + with open(sub['log']) as sublog: + output = sublog.read() + details = {'output': output} + tc.add_error_info(output=output) + print(testline, file=self.__file_report) + print("%s" % yaml.safe_dump(details, + explicit_start=True, + explicit_end=True, + default_style='|'), + file=self.__file_report) + if sub['log']: + add_to_output(sub['log']) + else: + if self.__file_report: + testline = u"ok %d - %s" % (self.__runtest, sub['name']) + print(testline, file=self.__file_report) + + if sub['log']: + with open(sub['log']) as sublog: + print("%s" % sublog.read().encode( + 'ascii', 'ignore').decode('utf-8')) + os.unlink(sub['log']) + + return True + + return False + + def __wait_all(self): + while self.__subs: + self.__wait_one(0) + + def wait(self): + self.__wait_one(0) + while self.__subs: + if not self.__wait_one(os.WNOHANG): + break + if self.__fail and not opts['keep_going']: + raise test_fail_exc('') + + def wait_all(self): + self.__wait_all() + if self.__fail and not opts['keep_going']: + raise test_fail_exc('') + + def finish(self): + self.__wait_all() + if not opts['fault'] and check_core_files(): + self.__fail = True + if self.__file_report: + ts = TestSuite(opts['title'], self.__junit_test_cases, + os.getenv("NODE_NAME")) + self.__junit_file.write(TestSuite.to_xml_string([ts])) + self.__junit_file.close() + self.__file_report.close() + + if opts['keep_going']: + if self.__fail: + print_sep( + "%d TEST(S) FAILED (TOTAL %d/SKIPPED %d)" % + (len(self.__failed), self.__total, self.__nr_skip), "#") + for failed in self.__failed: + print(" * %s(%s)" % (failed[0], failed[1])) + else: + print_sep( + "ALL TEST(S) PASSED (TOTAL %d/SKIPPED %d)" % + (self.__total, self.__nr_skip), "#") + + if self.__fail: + print_sep("FAIL", "#") + sys.exit(1) def all_tests(opts): - with open(opts['set'] + '.desc') as fd: - desc = eval(fd.read()) - - files = [] - mask = stat.S_IFREG | stat.S_IXUSR - for d in os.walk(desc['dir']): - for f in d[2]: - fp = os.path.join(d[0], f) - st = os.lstat(fp) - if (st.st_mode & mask) != mask: - continue - if stat.S_IFMT(st.st_mode) in [stat.S_IFLNK, stat.S_IFSOCK]: - continue - files.append(fp) - excl = list(map(lambda x: os.path.join(desc['dir'], x), desc['exclude'])) - tlist = filter(lambda x: - not x.endswith('.checkskip') and - not x.endswith('.hook') and - x not in excl, - map(lambda x: x.strip(), files) - ) - return tlist + with open(opts['set'] + '.desc') as fd: + desc = eval(fd.read()) + + files = [] + mask = stat.S_IFREG | stat.S_IXUSR + for d in os.walk(desc['dir']): + for f in d[2]: + fp = os.path.join(d[0], f) + st = os.lstat(fp) + if (st.st_mode & mask) != mask: + continue + if stat.S_IFMT(st.st_mode) in [stat.S_IFLNK, stat.S_IFSOCK]: + continue + files.append(fp) + excl = list(map(lambda x: os.path.join(desc['dir'], x), desc['exclude'])) + tlist = filter( + lambda x: not x.endswith('.checkskip') and not x.endswith('.hook') and + x not in excl, map(lambda x: x.strip(), files)) + return tlist # Descriptor for abstract test not in list @@ -1895,355 +2020,363 @@ def all_tests(opts): def get_test_desc(tname): - d_path = tname + '.desc' - if os.access(d_path, os.F_OK) and os.path.getsize(d_path) > 0: - with open(d_path) as fd: - return eval(fd.read()) + d_path = tname + '.desc' + if os.access(d_path, os.F_OK) and os.path.getsize(d_path) > 0: + with open(d_path) as fd: + return eval(fd.read()) - return default_test + return default_test def self_checkskip(tname): - chs = tname + '.checkskip' - if os.access(chs, os.X_OK): - ch = subprocess.Popen([chs]) - return not ch.wait() == 0 + chs = tname + '.checkskip' + if os.access(chs, os.X_OK): + ch = subprocess.Popen([chs]) + return not ch.wait() == 0 - return False + return False def print_fname(fname, typ): - print("=[%s]=> %s" % (typ, fname)) + print("=[%s]=> %s" % (typ, fname)) -def print_sep(title, sep = "=", width = 80): - print((" " + title + " ").center(width, sep)) +def print_sep(title, sep="=", width=80): + print((" " + title + " ").center(width, sep)) def print_error(line): - line = line.rstrip() - print(line) - if line.endswith('>'): # combine pie output - return True - return False + line = line.rstrip() + print(line) + if line.endswith('>'): # combine pie output + return True + return False def grep_errors(fname): - first = True - print_next = False - before = [] - with open(fname) as fd: - for l in fd: - before.append(l) - if len(before) > 5: - before.pop(0) - if "Error" in l: - if first: - print_fname(fname, 'log') - print_sep("grep Error", "-", 60) - first = False - for i in before: - print_next = print_error(i) - before = [] - else: - if print_next: - print_next = print_error(l) - before = [] - if not first: - print_sep("ERROR OVER", "-", 60) + first = True + print_next = False + before = [] + with open(fname) as fd: + for l in fd: + before.append(l) + if len(before) > 5: + before.pop(0) + if "Error" in l: + if first: + print_fname(fname, 'log') + print_sep("grep Error", "-", 60) + first = False + for i in before: + print_next = print_error(i) + before = [] + else: + if print_next: + print_next = print_error(l) + before = [] + if not first: + print_sep("ERROR OVER", "-", 60) def run_tests(opts): - excl = None - features = {} - - if opts['pre'] or opts['snaps']: - if not criu.check("mem_dirty_track"): - print("Tracking memory is not available") - return - - if opts['all']: - torun = all_tests(opts) - run_all = True - elif opts['tests']: - r = re.compile(opts['tests']) - torun = filter(lambda x: r.match(x), all_tests(opts)) - run_all = True - elif opts['test']: - torun = opts['test'] - run_all = False - elif opts['from']: - if not os.access(opts['from'], os.R_OK): - print("No such file") - return - - with open(opts['from']) as fd: - torun = map(lambda x: x.strip(), fd) - opts['keep_going'] = False - run_all = True - else: - print("Specify test with -t or -a") - return - - torun = list(torun) - if opts['keep_going'] and len(torun) < 2: - print("[WARNING] Option --keep-going is more useful when running multiple tests") - opts['keep_going'] = False - - if opts['exclude']: - excl = re.compile(".*(" + "|".join(opts['exclude']) + ")") - print("Compiled exclusion list") - - if opts['report']: - init_report(opts['report']) - - if opts['parallel'] and opts['freezecg']: - print("Parallel launch with freezer not supported") - opts['parallel'] = None - - if opts['join_ns']: - if subprocess.Popen(["ip", "netns", "add", "zdtm_netns"]).wait(): - raise Exception("Unable to create a network namespace") - if subprocess.Popen(["ip", "netns", "exec", "zdtm_netns", "ip", "link", "set", "up", "dev", "lo"]).wait(): - raise Exception("ip link set up dev lo") - - if opts['lazy_pages'] or opts['remote_lazy_pages'] or opts['lazy_migrate']: - uffd = criu.check("uffd") - uffd_noncoop = criu.check("uffd-noncoop") - if not uffd: - raise Exception("UFFD is not supported, cannot run with --lazy-pages") - if not uffd_noncoop: - # Most tests will work with 4.3 - 4.11 - print("[WARNING] Non-cooperative UFFD is missing, some tests might spuriously fail") - - launcher = Launcher(opts, len(torun)) - try: - for t in torun: - global arch - - if excl and excl.match(t): - launcher.skip(t, "exclude") - continue - - tdesc = get_test_desc(t) - if tdesc.get('arch', arch) != arch: - launcher.skip(t, "arch %s" % tdesc['arch']) - continue - - if test_flag(tdesc, 'reqrst') and opts['norst']: - launcher.skip(t, "restore stage is required") - continue - - if run_all and test_flag(tdesc, 'noauto'): - launcher.skip(t, "manual run only") - continue - - feat_list = tdesc.get('feature', "") - for feat in feat_list.split(): - if feat not in features: - print("Checking feature %s" % feat) - features[feat] = criu.check(feat) - - if not features[feat]: - launcher.skip(t, "no %s feature" % feat) - feat_list = None - break - if feat_list is None: - continue - - if self_checkskip(t): - launcher.skip(t, "checkskip failed") - continue - - if opts['user']: - if test_flag(tdesc, 'suid'): - launcher.skip(t, "suid test in user mode") - continue - if test_flag(tdesc, 'nouser'): - launcher.skip(t, "criu root prio needed") - continue - - if opts['join_ns']: - if test_flag(tdesc, 'samens'): - launcher.skip(t, "samens test in the same namespace") - continue - - if opts['lazy_pages'] or opts['remote_lazy_pages'] or opts['lazy_migrate']: - if test_flag(tdesc, 'nolazy'): - launcher.skip(t, "lazy pages are not supported") - continue - - if opts['remote_lazy_pages']: - if test_flag(tdesc, 'noremotelazy'): - launcher.skip(t, "remote lazy pages are not supported") - continue - - test_flavs = tdesc.get('flavor', 'h ns uns').split() - opts_flavs = (opts['flavor'] or 'h,ns,uns').split(',') - if opts_flavs != ['best']: - run_flavs = set(test_flavs) & set(opts_flavs) - else: - run_flavs = set([test_flavs.pop()]) - if not criu.check("userns"): - run_flavs -= set(['uns']) - if opts['user']: - # FIXME -- probably uns will make sense - run_flavs -= set(['ns', 'uns']) - - # remove ns and uns flavor in join_ns - if opts['join_ns']: - run_flavs -= set(['ns', 'uns']) - if opts['empty_ns']: - run_flavs -= set(['h']) - - if run_flavs: - launcher.run_test(t, tdesc, run_flavs) - else: - launcher.skip(t, "no flavors") - finally: - launcher.finish() - if opts['join_ns']: - subprocess.Popen(["ip", "netns", "delete", "zdtm_netns"]).wait() + excl = None + features = {} + + if opts['pre'] or opts['snaps']: + if not criu.check("mem_dirty_track"): + print("Tracking memory is not available") + return + + if opts['all']: + torun = all_tests(opts) + run_all = True + elif opts['tests']: + r = re.compile(opts['tests']) + torun = filter(lambda x: r.match(x), all_tests(opts)) + run_all = True + elif opts['test']: + torun = opts['test'] + run_all = False + elif opts['from']: + if not os.access(opts['from'], os.R_OK): + print("No such file") + return + + with open(opts['from']) as fd: + torun = map(lambda x: x.strip(), fd) + opts['keep_going'] = False + run_all = True + else: + print("Specify test with -t or -a") + return + + torun = list(torun) + if opts['keep_going'] and len(torun) < 2: + print( + "[WARNING] Option --keep-going is more useful when running multiple tests" + ) + opts['keep_going'] = False + + if opts['exclude']: + excl = re.compile(".*(" + "|".join(opts['exclude']) + ")") + print("Compiled exclusion list") + + if opts['report']: + init_report(opts['report']) + + if opts['parallel'] and opts['freezecg']: + print("Parallel launch with freezer not supported") + opts['parallel'] = None + + if opts['join_ns']: + if subprocess.Popen(["ip", "netns", "add", "zdtm_netns"]).wait(): + raise Exception("Unable to create a network namespace") + if subprocess.Popen([ + "ip", "netns", "exec", "zdtm_netns", "ip", "link", "set", "up", + "dev", "lo" + ]).wait(): + raise Exception("ip link set up dev lo") + + if opts['lazy_pages'] or opts['remote_lazy_pages'] or opts['lazy_migrate']: + uffd = criu.check("uffd") + uffd_noncoop = criu.check("uffd-noncoop") + if not uffd: + raise Exception( + "UFFD is not supported, cannot run with --lazy-pages") + if not uffd_noncoop: + # Most tests will work with 4.3 - 4.11 + print( + "[WARNING] Non-cooperative UFFD is missing, some tests might spuriously fail" + ) + + launcher = Launcher(opts, len(torun)) + try: + for t in torun: + global arch + + if excl and excl.match(t): + launcher.skip(t, "exclude") + continue + + tdesc = get_test_desc(t) + if tdesc.get('arch', arch) != arch: + launcher.skip(t, "arch %s" % tdesc['arch']) + continue + + if test_flag(tdesc, 'reqrst') and opts['norst']: + launcher.skip(t, "restore stage is required") + continue + + if run_all and test_flag(tdesc, 'noauto'): + launcher.skip(t, "manual run only") + continue + + feat_list = tdesc.get('feature', "") + for feat in feat_list.split(): + if feat not in features: + print("Checking feature %s" % feat) + features[feat] = criu.check(feat) + + if not features[feat]: + launcher.skip(t, "no %s feature" % feat) + feat_list = None + break + if feat_list is None: + continue + + if self_checkskip(t): + launcher.skip(t, "checkskip failed") + continue + + if opts['user']: + if test_flag(tdesc, 'suid'): + launcher.skip(t, "suid test in user mode") + continue + if test_flag(tdesc, 'nouser'): + launcher.skip(t, "criu root prio needed") + continue + + if opts['join_ns']: + if test_flag(tdesc, 'samens'): + launcher.skip(t, "samens test in the same namespace") + continue + + if opts['lazy_pages'] or opts['remote_lazy_pages'] or opts[ + 'lazy_migrate']: + if test_flag(tdesc, 'nolazy'): + launcher.skip(t, "lazy pages are not supported") + continue + + if opts['remote_lazy_pages']: + if test_flag(tdesc, 'noremotelazy'): + launcher.skip(t, "remote lazy pages are not supported") + continue + + test_flavs = tdesc.get('flavor', 'h ns uns').split() + opts_flavs = (opts['flavor'] or 'h,ns,uns').split(',') + if opts_flavs != ['best']: + run_flavs = set(test_flavs) & set(opts_flavs) + else: + run_flavs = set([test_flavs.pop()]) + if not criu.check("userns"): + run_flavs -= set(['uns']) + if opts['user']: + # FIXME -- probably uns will make sense + run_flavs -= set(['ns', 'uns']) + + # remove ns and uns flavor in join_ns + if opts['join_ns']: + run_flavs -= set(['ns', 'uns']) + if opts['empty_ns']: + run_flavs -= set(['h']) + + if run_flavs: + launcher.run_test(t, tdesc, run_flavs) + else: + launcher.skip(t, "no flavors") + finally: + launcher.finish() + if opts['join_ns']: + subprocess.Popen(["ip", "netns", "delete", "zdtm_netns"]).wait() sti_fmt = "%-40s%-10s%s" def show_test_info(t): - tdesc = get_test_desc(t) - flavs = tdesc.get('flavor', '') - return sti_fmt % (t, flavs, tdesc.get('flags', '')) + tdesc = get_test_desc(t) + flavs = tdesc.get('flavor', '') + return sti_fmt % (t, flavs, tdesc.get('flags', '')) def list_tests(opts): - tlist = all_tests(opts) - if opts['info']: - print(sti_fmt % ('Name', 'Flavors', 'Flags')) - tlist = map(lambda x: show_test_info(x), tlist) - print('\n'.join(tlist)) + tlist = all_tests(opts) + if opts['info']: + print(sti_fmt % ('Name', 'Flavors', 'Flags')) + tlist = map(lambda x: show_test_info(x), tlist) + print('\n'.join(tlist)) class group: - def __init__(self, tname, tdesc): - self.__tests = [tname] - self.__desc = tdesc - self.__deps = set() - - def __is_mergeable_desc(self, desc): - # For now make it full match - if self.__desc.get('flags') != desc.get('flags'): - return False - if self.__desc.get('flavor') != desc.get('flavor'): - return False - if self.__desc.get('arch') != desc.get('arch'): - return False - if self.__desc.get('opts') != desc.get('opts'): - return False - if self.__desc.get('feature') != desc.get('feature'): - return False - return True - - def merge(self, tname, tdesc): - if not self.__is_mergeable_desc(tdesc): - return False - - self.__deps |= set(tdesc.get('deps', [])) - self.__tests.append(tname) - return True - - def size(self): - return len(self.__tests) - - # common method to write a "meta" auxiliary script (hook/checkskip) - # which will call all tests' scripts in turn - def __dump_meta(self, fname, ext): - scripts = filter(lambda names: os.access(names[1], os.X_OK), - map(lambda test: (test, test + ext), - self.__tests)) - if scripts: - f = open(fname + ext, "w") - f.write("#!/bin/sh -e\n") - - for test, script in scripts: - f.write("echo 'Running %s for %s'\n" % (ext, test)) - f.write('%s "$@"\n' % script) - - f.write("echo 'All %s scripts OK'\n" % ext) - f.close() - os.chmod(fname + ext, 0o700) - - def dump(self, fname): - f = open(fname, "w") - for t in self.__tests: - f.write(t + '\n') - f.close() - os.chmod(fname, 0o700) - - if len(self.__desc) or len(self.__deps): - f = open(fname + '.desc', "w") - if len(self.__deps): - self.__desc['deps'] = list(self.__deps) - f.write(repr(self.__desc)) - f.close() - - # write "meta" .checkskip and .hook scripts - self.__dump_meta(fname, '.checkskip') - self.__dump_meta(fname, '.hook') + def __init__(self, tname, tdesc): + self.__tests = [tname] + self.__desc = tdesc + self.__deps = set() + + def __is_mergeable_desc(self, desc): + # For now make it full match + if self.__desc.get('flags') != desc.get('flags'): + return False + if self.__desc.get('flavor') != desc.get('flavor'): + return False + if self.__desc.get('arch') != desc.get('arch'): + return False + if self.__desc.get('opts') != desc.get('opts'): + return False + if self.__desc.get('feature') != desc.get('feature'): + return False + return True + + def merge(self, tname, tdesc): + if not self.__is_mergeable_desc(tdesc): + return False + + self.__deps |= set(tdesc.get('deps', [])) + self.__tests.append(tname) + return True + + def size(self): + return len(self.__tests) + + # common method to write a "meta" auxiliary script (hook/checkskip) + # which will call all tests' scripts in turn + def __dump_meta(self, fname, ext): + scripts = filter(lambda names: os.access(names[1], os.X_OK), + map(lambda test: (test, test + ext), self.__tests)) + if scripts: + f = open(fname + ext, "w") + f.write("#!/bin/sh -e\n") + + for test, script in scripts: + f.write("echo 'Running %s for %s'\n" % (ext, test)) + f.write('%s "$@"\n' % script) + + f.write("echo 'All %s scripts OK'\n" % ext) + f.close() + os.chmod(fname + ext, 0o700) + + def dump(self, fname): + f = open(fname, "w") + for t in self.__tests: + f.write(t + '\n') + f.close() + os.chmod(fname, 0o700) + + if len(self.__desc) or len(self.__deps): + f = open(fname + '.desc', "w") + if len(self.__deps): + self.__desc['deps'] = list(self.__deps) + f.write(repr(self.__desc)) + f.close() + + # write "meta" .checkskip and .hook scripts + self.__dump_meta(fname, '.checkskip') + self.__dump_meta(fname, '.hook') def group_tests(opts): - excl = None - groups = [] - pend_groups = [] - maxs = int(opts['max_size']) + excl = None + groups = [] + pend_groups = [] + maxs = int(opts['max_size']) - if not os.access("groups", os.F_OK): - os.mkdir("groups") + if not os.access("groups", os.F_OK): + os.mkdir("groups") - tlist = all_tests(opts) - random.shuffle(tlist) - if opts['exclude']: - excl = re.compile(".*(" + "|".join(opts['exclude']) + ")") - print("Compiled exclusion list") + tlist = all_tests(opts) + random.shuffle(tlist) + if opts['exclude']: + excl = re.compile(".*(" + "|".join(opts['exclude']) + ")") + print("Compiled exclusion list") - for t in tlist: - if excl and excl.match(t): - continue + for t in tlist: + if excl and excl.match(t): + continue - td = get_test_desc(t) + td = get_test_desc(t) - for g in pend_groups: - if g.merge(t, td): - if g.size() == maxs: - pend_groups.remove(g) - groups.append(g) - break - else: - g = group(t, td) - pend_groups.append(g) + for g in pend_groups: + if g.merge(t, td): + if g.size() == maxs: + pend_groups.remove(g) + groups.append(g) + break + else: + g = group(t, td) + pend_groups.append(g) - groups += pend_groups + groups += pend_groups - nr = 0 - suf = opts['name'] or 'group' + nr = 0 + suf = opts['name'] or 'group' - for g in groups: - if maxs > 1 and g.size() == 1: # Not much point in group test for this - continue + for g in groups: + if maxs > 1 and g.size() == 1: # Not much point in group test for this + continue - fn = os.path.join("groups", "%s.%d" % (suf, nr)) - g.dump(fn) - nr += 1 + fn = os.path.join("groups", "%s.%d" % (suf, nr)) + g.dump(fn) + nr += 1 - print("Generated %d group(s)" % nr) + print("Generated %d group(s)" % nr) def clean_stuff(opts): - print("Cleaning %s" % opts['what']) - if opts['what'] == 'nsroot': - for f in flavors: - f = flavors[f] - f.clean() + print("Cleaning %s" % opts['what']) + if opts['what'] == 'nsroot': + for f in flavors: + f = flavors[f] + f.clean() # @@ -2251,104 +2384,168 @@ def clean_stuff(opts): # if 'CR_CT_TEST_INFO' in os.environ: - # Fork here, since we're new pidns init and are supposed to - # collect this namespace's zombies - status = 0 - pid = os.fork() - if pid == 0: - tinfo = eval(os.environ['CR_CT_TEST_INFO']) - do_run_test(tinfo[0], tinfo[1], tinfo[2], tinfo[3]) - else: - while True: - wpid, status = os.wait() - if wpid == pid: - if os.WIFEXITED(status): - status = os.WEXITSTATUS(status) - else: - status = 1 - break - - sys.exit(status) + # Fork here, since we're new pidns init and are supposed to + # collect this namespace's zombies + status = 0 + pid = os.fork() + if pid == 0: + tinfo = eval(os.environ['CR_CT_TEST_INFO']) + do_run_test(tinfo[0], tinfo[1], tinfo[2], tinfo[3]) + else: + while True: + wpid, status = os.wait() + if wpid == pid: + if os.WIFEXITED(status): + status = os.WEXITSTATUS(status) + else: + status = 1 + break + + sys.exit(status) p = argparse.ArgumentParser("CRIU test suite") -p.add_argument("--debug", help = "Print what's being executed", action = 'store_true') -p.add_argument("--set", help = "Which set of tests to use", default = 'zdtm') - -sp = p.add_subparsers(help = "Use --help for list of actions") - -rp = sp.add_parser("run", help = "Run test(s)") -rp.set_defaults(action = run_tests) -rp.add_argument("-a", "--all", action = 'store_true') -rp.add_argument("-t", "--test", help = "Test name", action = 'append') -rp.add_argument("-T", "--tests", help = "Regexp") -rp.add_argument("-F", "--from", help = "From file") -rp.add_argument("-f", "--flavor", help = "Flavor to run") -rp.add_argument("-x", "--exclude", help = "Exclude tests from --all run", action = 'append') - -rp.add_argument("--sibling", help = "Restore tests as siblings", action = 'store_true') -rp.add_argument("--join-ns", help = "Restore tests and join existing namespace", action = 'store_true') -rp.add_argument("--empty-ns", help = "Restore tests in empty net namespace", action = 'store_true') -rp.add_argument("--pre", help = "Do some pre-dumps before dump (n[:pause])") -rp.add_argument("--snaps", help = "Instead of pre-dumps do full dumps", action = 'store_true') -rp.add_argument("--dedup", help = "Auto-deduplicate images on iterations", action = 'store_true') -rp.add_argument("--noauto-dedup", help = "Manual deduplicate images on iterations", action = 'store_true') -rp.add_argument("--nocr", help = "Do not CR anything, just check test works", action = 'store_true') -rp.add_argument("--norst", help = "Don't restore tasks, leave them running after dump", action = 'store_true') -rp.add_argument("--stop", help = "Check that --leave-stopped option stops ps tree.", action = 'store_true') -rp.add_argument("--iters", help = "Do CR cycle several times before check (n[:pause])") -rp.add_argument("--fault", help = "Test fault injection") -rp.add_argument("--sat", help = "Generate criu strace-s for sat tool (restore is fake, images are kept)", action = 'store_true') -rp.add_argument("--sbs", help = "Do step-by-step execution, asking user for keypress to continue", action = 'store_true') -rp.add_argument("--freezecg", help = "Use freeze cgroup (path:state)") -rp.add_argument("--user", help = "Run CRIU as regular user", action = 'store_true') -rp.add_argument("--rpc", help = "Run CRIU via RPC rather than CLI", action = 'store_true') - -rp.add_argument("--page-server", help = "Use page server dump", action = 'store_true') -rp.add_argument("--remote", help = "Use remote option for diskless C/R", action = 'store_true') -rp.add_argument("-p", "--parallel", help = "Run test in parallel") -rp.add_argument("--dry-run", help="Don't run tests, just pretend to", action='store_true') +p.add_argument("--debug", + help="Print what's being executed", + action='store_true') +p.add_argument("--set", help="Which set of tests to use", default='zdtm') + +sp = p.add_subparsers(help="Use --help for list of actions") + +rp = sp.add_parser("run", help="Run test(s)") +rp.set_defaults(action=run_tests) +rp.add_argument("-a", "--all", action='store_true') +rp.add_argument("-t", "--test", help="Test name", action='append') +rp.add_argument("-T", "--tests", help="Regexp") +rp.add_argument("-F", "--from", help="From file") +rp.add_argument("-f", "--flavor", help="Flavor to run") +rp.add_argument("-x", + "--exclude", + help="Exclude tests from --all run", + action='append') + +rp.add_argument("--sibling", + help="Restore tests as siblings", + action='store_true') +rp.add_argument("--join-ns", + help="Restore tests and join existing namespace", + action='store_true') +rp.add_argument("--empty-ns", + help="Restore tests in empty net namespace", + action='store_true') +rp.add_argument("--pre", help="Do some pre-dumps before dump (n[:pause])") +rp.add_argument("--snaps", + help="Instead of pre-dumps do full dumps", + action='store_true') +rp.add_argument("--dedup", + help="Auto-deduplicate images on iterations", + action='store_true') +rp.add_argument("--noauto-dedup", + help="Manual deduplicate images on iterations", + action='store_true') +rp.add_argument("--nocr", + help="Do not CR anything, just check test works", + action='store_true') +rp.add_argument("--norst", + help="Don't restore tasks, leave them running after dump", + action='store_true') +rp.add_argument("--stop", + help="Check that --leave-stopped option stops ps tree.", + action='store_true') +rp.add_argument("--iters", + help="Do CR cycle several times before check (n[:pause])") +rp.add_argument("--fault", help="Test fault injection") +rp.add_argument( + "--sat", + help= + "Generate criu strace-s for sat tool (restore is fake, images are kept)", + action='store_true') +rp.add_argument( + "--sbs", + help="Do step-by-step execution, asking user for keypress to continue", + action='store_true') +rp.add_argument("--freezecg", help="Use freeze cgroup (path:state)") +rp.add_argument("--user", help="Run CRIU as regular user", action='store_true') +rp.add_argument("--rpc", + help="Run CRIU via RPC rather than CLI", + action='store_true') + +rp.add_argument("--page-server", + help="Use page server dump", + action='store_true') +rp.add_argument("--remote", + help="Use remote option for diskless C/R", + action='store_true') +rp.add_argument("-p", "--parallel", help="Run test in parallel") +rp.add_argument("--dry-run", + help="Don't run tests, just pretend to", + action='store_true') rp.add_argument("--script", help="Add script to get notified by criu") -rp.add_argument("-k", "--keep-img", help = "Whether or not to keep images after test", - choices = ['always', 'never', 'failed'], default = 'failed') -rp.add_argument("--report", help = "Generate summary report in directory") -rp.add_argument("--keep-going", help = "Keep running tests in spite of failures", action = 'store_true') -rp.add_argument("--ignore-taint", help = "Don't care about a non-zero kernel taint flag", action = 'store_true') -rp.add_argument("--lazy-pages", help = "restore pages on demand", action = 'store_true') -rp.add_argument("--lazy-migrate", help = "restore pages on demand", action = 'store_true') -rp.add_argument("--remote-lazy-pages", help = "simulate lazy migration", action = 'store_true') -rp.add_argument("--tls", help = "use TLS for migration", action = 'store_true') -rp.add_argument("--title", help = "A test suite title", default = "criu") -rp.add_argument("--show-stats", help = "Show criu statistics", action = 'store_true') -rp.add_argument("--criu-bin", help = "Path to criu binary", default = '../criu/criu') -rp.add_argument("--crit-bin", help = "Path to crit binary", default = '../crit/crit') - -lp = sp.add_parser("list", help = "List tests") -lp.set_defaults(action = list_tests) -lp.add_argument('-i', '--info', help = "Show more info about tests", action = 'store_true') - -gp = sp.add_parser("group", help = "Generate groups") -gp.set_defaults(action = group_tests) -gp.add_argument("-m", "--max-size", help = "Maximum number of tests in group") -gp.add_argument("-n", "--name", help = "Common name for group tests") -gp.add_argument("-x", "--exclude", help = "Exclude tests from --all run", action = 'append') - -cp = sp.add_parser("clean", help = "Clean something") -cp.set_defaults(action = clean_stuff) -cp.add_argument("what", choices = ['nsroot']) +rp.add_argument("-k", + "--keep-img", + help="Whether or not to keep images after test", + choices=['always', 'never', 'failed'], + default='failed') +rp.add_argument("--report", help="Generate summary report in directory") +rp.add_argument("--keep-going", + help="Keep running tests in spite of failures", + action='store_true') +rp.add_argument("--ignore-taint", + help="Don't care about a non-zero kernel taint flag", + action='store_true') +rp.add_argument("--lazy-pages", + help="restore pages on demand", + action='store_true') +rp.add_argument("--lazy-migrate", + help="restore pages on demand", + action='store_true') +rp.add_argument("--remote-lazy-pages", + help="simulate lazy migration", + action='store_true') +rp.add_argument("--tls", help="use TLS for migration", action='store_true') +rp.add_argument("--title", help="A test suite title", default="criu") +rp.add_argument("--show-stats", + help="Show criu statistics", + action='store_true') +rp.add_argument("--criu-bin", + help="Path to criu binary", + default='../criu/criu') +rp.add_argument("--crit-bin", + help="Path to crit binary", + default='../crit/crit') + +lp = sp.add_parser("list", help="List tests") +lp.set_defaults(action=list_tests) +lp.add_argument('-i', + '--info', + help="Show more info about tests", + action='store_true') + +gp = sp.add_parser("group", help="Generate groups") +gp.set_defaults(action=group_tests) +gp.add_argument("-m", "--max-size", help="Maximum number of tests in group") +gp.add_argument("-n", "--name", help="Common name for group tests") +gp.add_argument("-x", + "--exclude", + help="Exclude tests from --all run", + action='append') + +cp = sp.add_parser("clean", help="Clean something") +cp.set_defaults(action=clean_stuff) +cp.add_argument("what", choices=['nsroot']) opts = vars(p.parse_args()) if opts.get('sat', False): - opts['keep_img'] = 'always' + opts['keep_img'] = 'always' if opts['debug']: - sys.settrace(traceit) + sys.settrace(traceit) if opts['action'] == 'run': - criu.available() + criu.available() for tst in test_classes.values(): - tst.available() + tst.available() opts['action'](opts) for tst in test_classes.values(): - tst.cleanup() + tst.cleanup() From 4797cfb408782a2090d33b29462cc185a102c361 Mon Sep 17 00:00:00 2001 From: Pavel Emelyanov Date: Thu, 27 Jun 2019 14:18:38 +0300 Subject: [PATCH 178/249] lint: Print flake8 version before checking Signed-off-by: Pavel Emelyanov --- Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/Makefile b/Makefile index 9d83862d1..0b49364fb 100644 --- a/Makefile +++ b/Makefile @@ -380,6 +380,7 @@ help: .PHONY: help lint: + flake8 --version flake8 --config=scripts/flake8.cfg test/zdtm.py flake8 --config=scripts/flake8.cfg test/inhfd/*.py flake8 --config=scripts/flake8.cfg test/others/rpc/config_file.py From 8117692ce764ae309f50208163c00d367267859e Mon Sep 17 00:00:00 2001 From: Pavel Emelyanov Date: Thu, 27 Jun 2019 14:21:35 +0300 Subject: [PATCH 179/249] zdtm: Two fixes to yapf bombing The lint W503 rule requires operators to be on the first line Signed-off-by: Pavel Emelyanov --- test/zdtm.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/zdtm.py b/test/zdtm.py index bac7abe70..396b52d74 100755 --- a/test/zdtm.py +++ b/test/zdtm.py @@ -997,8 +997,8 @@ def __init__(self, opts): self.__prev_dump_iter = None self.__page_server = bool(opts['page_server']) self.__remote_lazy_pages = bool(opts['remote_lazy_pages']) - self.__lazy_pages = (self.__remote_lazy_pages - or bool(opts['lazy_pages'])) + self.__lazy_pages = (self.__remote_lazy_pages or + bool(opts['lazy_pages'])) self.__lazy_migrate = bool(opts['lazy_migrate']) self.__restore_sibling = bool(opts['sibling']) self.__join_ns = bool(opts['join_ns']) @@ -1167,8 +1167,8 @@ def __criu_act(self, action, opts=[], log=None, nowait=False): return rst_succeeded = os.access( os.path.join(__ddir, "restore-succeeded"), os.F_OK) - if self.__test.blocking() or (self.__sat and action == 'restore' - and rst_succeeded): + if self.__test.blocking() or (self.__sat and action == 'restore' and + rst_succeeded): raise test_fail_expected_exc(action) else: raise test_fail_exc("CRIU %s" % action) From a7f537033cee58b9cfd45bba21afeb4eaf03bf8b Mon Sep 17 00:00:00 2001 From: Pavel Emelyanov Date: Fri, 28 Jun 2019 20:16:09 +0300 Subject: [PATCH 180/249] zdtm: Fix muliline assignment Signed-off-by: Pavel Emelyanov --- test/zdtm.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/test/zdtm.py b/test/zdtm.py index 396b52d74..69be1df5a 100755 --- a/test/zdtm.py +++ b/test/zdtm.py @@ -2456,8 +2456,7 @@ def clean_stuff(opts): rp.add_argument("--fault", help="Test fault injection") rp.add_argument( "--sat", - help= - "Generate criu strace-s for sat tool (restore is fake, images are kept)", + help="Generate criu strace-s for sat tool (restore is fake, images are kept)", action='store_true') rp.add_argument( "--sbs", From 31b3ba1e29e22ed3083d359536659743aecf8cf2 Mon Sep 17 00:00:00 2001 From: Pavel Emelyanov Date: Fri, 28 Jun 2019 20:16:38 +0300 Subject: [PATCH 181/249] flake.cfg: Update to yapf formatting Signed-off-by: Pavel Emelyanov --- scripts/flake8.cfg | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/scripts/flake8.cfg b/scripts/flake8.cfg index 4231e843d..b6a587729 100644 --- a/scripts/flake8.cfg +++ b/scripts/flake8.cfg @@ -1,10 +1,4 @@ [flake8] -# W191 indentation contains tabs -# E128 continuation line under-indented for visual indent # E501 line too long -# E251 unexpected spaces around keyword / parameter equals -# E101 indentation contains mixed spaces and tabs -# E126 continuation line over-indented for hanging indent # W504 line break after binary operator -# E117 over-indented -ignore = W191,E128,E501,E251,E101,E126,W504,E117 +ignore = E501,W504 From 348b1f61e6f806d19670e9a53fc53f7e8639e556 Mon Sep 17 00:00:00 2001 From: Radostin Stoyanov Date: Fri, 28 Jun 2019 20:17:35 +0300 Subject: [PATCH 182/249] py: Fix tabs in code comments These were left by yapf formatter Signed-off-by: Pavel Emelyanov --- coredump/criu_coredump/coredump.py | 130 ++++++++++++++--------------- coredump/criu_coredump/elf.py | 12 +-- lib/py/criu.py | 74 ++++++++-------- lib/py/images/images.py | 92 ++++++++++---------- lib/py/images/pb2dict.py | 12 +-- scripts/magic-gen.py | 4 +- 6 files changed, 162 insertions(+), 162 deletions(-) diff --git a/coredump/criu_coredump/coredump.py b/coredump/criu_coredump/coredump.py index 9b2c6c60c..bc53a7705 100644 --- a/coredump/criu_coredump/coredump.py +++ b/coredump/criu_coredump/coredump.py @@ -9,24 +9,24 @@ # # On my x86_64 systems with fresh kernel ~3.17 core dump looks like: # -# 1) Elf file header; -# 2) PT_NOTE program header describing notes section; -# 3) PT_LOAD program headers for (almost?) each vma; -# 4) NT_PRPSINFO note with elf_prpsinfo inside; -# 5) An array of notes for each thread of the process: -# NT_PRSTATUS note with elf_prstatus inside; -# NT_FPREGSET note with elf_fpregset inside; -# NT_X86_XSTATE note with x86 extended state using xsave; -# NT_SIGINFO note with siginfo_t inside; -# 6) NT_AUXV note with auxv; -# 7) NT_FILE note with mapped files; -# 8) VMAs themselves; +# 1) Elf file header; +# 2) PT_NOTE program header describing notes section; +# 3) PT_LOAD program headers for (almost?) each vma; +# 4) NT_PRPSINFO note with elf_prpsinfo inside; +# 5) An array of notes for each thread of the process: +# NT_PRSTATUS note with elf_prstatus inside; +# NT_FPREGSET note with elf_fpregset inside; +# NT_X86_XSTATE note with x86 extended state using xsave; +# NT_SIGINFO note with siginfo_t inside; +# 6) NT_AUXV note with auxv; +# 7) NT_FILE note with mapped files; +# 8) VMAs themselves; # # Or, you can represent it in less details as: -# 1) Elf file header; -# 2) Program table; -# 3) Notes; -# 4) VMAs contents; +# 1) Elf file header; +# 2) Program table; +# 3) Notes; +# 4) VMAs contents; # import io import elf @@ -65,9 +65,9 @@ class elf_note: class coredump: """ - A class to keep elf core dump components inside and - functions to properly write them to file. - """ + A class to keep elf core dump components inside and + functions to properly write them to file. + """ ehdr = None # Elf ehdr; phdrs = [] # Array of Phdrs; notes = [] # Array of elf_notes; @@ -77,8 +77,8 @@ class coredump: def write(self, f): """ - Write core dump to file f. - """ + Write core dump to file f. + """ buf = io.BytesIO() buf.write(self.ehdr) @@ -117,8 +117,8 @@ def write(self, f): class coredump_generator: """ - Generate core dump from criu images. - """ + Generate core dump from criu images. + """ coredumps = {} # coredumps by pid; pstree = {} # process info by pid; @@ -129,8 +129,8 @@ class coredump_generator: def _img_open_and_strip(self, name, single=False, pid=None): """ - Load criu image and strip it from magic and redundant list. - """ + Load criu image and strip it from magic and redundant list. + """ path = self._imgs_dir + "/" + name if pid: path += "-" + str(pid) @@ -146,8 +146,8 @@ def _img_open_and_strip(self, name, single=False, pid=None): def __call__(self, imgs_dir): """ - Parse criu images stored in directory imgs_dir to fill core dumps. - """ + Parse criu images stored in directory imgs_dir to fill core dumps. + """ self._imgs_dir = imgs_dir pstree = self._img_open_and_strip("pstree") @@ -171,9 +171,9 @@ def __call__(self, imgs_dir): def write(self, coredumps_dir, pid=None): """ - Write core dumpt to cores_dir directory. Specify pid to choose - core dump of only one process. - """ + Write core dumpt to cores_dir directory. Specify pid to choose + core dump of only one process. + """ for p in self.coredumps: if pid and p != pid: continue @@ -182,8 +182,8 @@ def write(self, coredumps_dir, pid=None): def _gen_coredump(self, pid): """ - Generate core dump for pid. - """ + Generate core dump for pid. + """ cd = coredump() # Generate everything backwards so it is easier to calculate offset. @@ -196,8 +196,8 @@ def _gen_coredump(self, pid): def _gen_ehdr(self, pid, phdrs): """ - Generate elf header for process pid with program headers phdrs. - """ + Generate elf header for process pid with program headers phdrs. + """ ehdr = elf.Elf64_Ehdr() ctypes.memset(ctypes.addressof(ehdr), 0, ctypes.sizeof(ehdr)) @@ -223,8 +223,8 @@ def _gen_ehdr(self, pid, phdrs): def _gen_phdrs(self, pid, notes, vmas): """ - Generate program headers for process pid. - """ + Generate program headers for process pid. + """ phdrs = [] offset = ctypes.sizeof(elf.Elf64_Ehdr()) @@ -272,8 +272,8 @@ def _gen_phdrs(self, pid, notes, vmas): def _gen_prpsinfo(self, pid): """ - Generate NT_PRPSINFO note for process pid. - """ + Generate NT_PRPSINFO note for process pid. + """ pstree = self.pstree[pid] core = self.cores[pid] @@ -324,8 +324,8 @@ def _gen_prpsinfo(self, pid): def _gen_prstatus(self, pid, tid): """ - Generate NT_PRSTATUS note for thread tid of process pid. - """ + Generate NT_PRSTATUS note for thread tid of process pid. + """ core = self.cores[tid] regs = core["thread_info"]["gpregs"] pstree = self.pstree[pid] @@ -382,8 +382,8 @@ def _gen_prstatus(self, pid, tid): def _gen_fpregset(self, pid, tid): """ - Generate NT_FPREGSET note for thread tid of process pid. - """ + Generate NT_FPREGSET note for thread tid of process pid. + """ core = self.cores[tid] regs = core["thread_info"]["fpregs"] @@ -402,7 +402,7 @@ def _gen_fpregset(self, pid, tid): *regs["st_space"]) fpregset.xmm_space = (ctypes.c_uint * len(regs["xmm_space"]))( *regs["xmm_space"]) - #fpregset.padding = regs["padding"] unused + #fpregset.padding = regs["padding"] unused nhdr = elf.Elf64_Nhdr() nhdr.n_namesz = 5 @@ -418,8 +418,8 @@ def _gen_fpregset(self, pid, tid): def _gen_x86_xstate(self, pid, tid): """ - Generate NT_X86_XSTATE note for thread tid of process pid. - """ + Generate NT_X86_XSTATE note for thread tid of process pid. + """ core = self.cores[tid] fpregs = core["thread_info"]["fpregs"] @@ -459,8 +459,8 @@ def _gen_x86_xstate(self, pid, tid): def _gen_siginfo(self, pid, tid): """ - Generate NT_SIGINFO note for thread tid of process pid. - """ + Generate NT_SIGINFO note for thread tid of process pid. + """ siginfo = elf.siginfo_t() # FIXME zeroify everything for now ctypes.memset(ctypes.addressof(siginfo), 0, ctypes.sizeof(siginfo)) @@ -479,8 +479,8 @@ def _gen_siginfo(self, pid, tid): def _gen_auxv(self, pid): """ - Generate NT_AUXV note for thread tid of process pid. - """ + Generate NT_AUXV note for thread tid of process pid. + """ mm = self.mms[pid] num_auxv = len(mm["mm_saved_auxv"]) / 2 @@ -506,8 +506,8 @@ class elf_auxv(ctypes.Structure): def _gen_files(self, pid): """ - Generate NT_FILE note for process pid. - """ + Generate NT_FILE note for process pid. + """ mm = self.mms[pid] class mmaped_file_info: @@ -597,8 +597,8 @@ def _gen_thread_notes(self, pid, tid): def _gen_notes(self, pid): """ - Generate notes for core dump of process pid. - """ + Generate notes for core dump of process pid. + """ notes = [] notes.append(self._gen_prpsinfo(pid)) @@ -622,8 +622,8 @@ def _gen_notes(self, pid): def _get_page(self, pid, page_no): """ - Try to find memory page page_no in pages.img image for process pid. - """ + Try to find memory page page_no in pages.img image for process pid. + """ pagemap = self.pagemaps[pid] # First entry is pagemap_head, we will need it later to open @@ -654,8 +654,8 @@ def _get_page(self, pid, page_no): def _gen_mem_chunk(self, pid, vma, size): """ - Obtain vma contents for process pid. - """ + Obtain vma contents for process pid. + """ f = None if size == 0: @@ -749,8 +749,8 @@ def _gen_mem_chunk(self, pid, vma, size): def _gen_cmdline(self, pid): """ - Generate full command with arguments. - """ + Generate full command with arguments. + """ mm = self.mms[pid] vma = {} @@ -768,8 +768,8 @@ def _gen_cmdline(self, pid): def _get_vma_dump_size(self, vma): """ - Calculate amount of vma to put into core dump. - """ + Calculate amount of vma to put into core dump. + """ if vma["status"] & status["VMA_AREA_VVAR"] or \ vma["status"] & status["VMA_AREA_VSYSCALL"] or \ vma["status"] & status["VMA_AREA_VDSO"]: @@ -791,8 +791,8 @@ def _get_vma_dump_size(self, vma): def _get_vma_flags(self, vma): """ - Convert vma flags int elf flags. - """ + Convert vma flags int elf flags. + """ flags = 0 if vma['prot'] & prot["PROT_READ"]: @@ -808,8 +808,8 @@ def _get_vma_flags(self, vma): def _gen_vmas(self, pid): """ - Generate vma contents for core dump for process pid. - """ + Generate vma contents for core dump for process pid. + """ mm = self.mms[pid] class vma_class: diff --git a/coredump/criu_coredump/elf.py b/coredump/criu_coredump/elf.py index 65da583c3..e65919e6b 100644 --- a/coredump/criu_coredump/elf.py +++ b/coredump/criu_coredump/elf.py @@ -120,9 +120,9 @@ class Elf64_auxv_t(ctypes.Structure): # typedef struct NT_PRPSINFO = 3 # #define NT_PRPSINFO 3 /* Contains copy of prpsinfo struct */ NT_AUXV = 6 # #define NT_AUXV 6 /* Contains copy of auxv array */ NT_SIGINFO = 0x53494749 # #define NT_SIGINFO 0x53494749 /* Contains copy of siginfo_t, -# size might increase */ +# size might increase */ NT_FILE = 0x46494c45 # #define NT_FILE 0x46494c45 /* Contains information about mapped -# files */ +# files */ NT_X86_XSTATE = 0x202 # #define NT_X86_XSTATE 0x202 /* x86 extended state using xsave */ @@ -259,7 +259,7 @@ class user_regs_struct(ctypes.Structure): # struct user_regs_struct ] # }; -#elf_greg_t = ctypes.c_ulonglong +#elf_greg_t = ctypes.c_ulonglong #ELF_NGREG = ctypes.sizeof(user_regs_struct)/ctypes.sizeof(elf_greg_t) #elf_gregset_t = elf_greg_t*ELF_NGREG elf_gregset_t = user_regs_struct @@ -450,7 +450,7 @@ class _siginfo_t_U_sigpoll(ctypes.Structure): # struct ] # } _sigpoll; - # /* SIGSYS. */ + # /* SIGSYS. */ class _siginfo_t_U_sigsys(ctypes.Structure): # struct _fields_ = [ # { ("_call_addr", ctypes.c_void_p @@ -515,7 +515,7 @@ class _siginfo_t_U(ctypes.Union): # union # int si_fd; # } _sigpoll; # - # /* SIGSYS. */ + # /* SIGSYS. */ ("_sigsys", _siginfo_t_U_sigpoll) # struct # { # void *_call_addr; /* Calling user insn. */ @@ -587,7 +587,7 @@ class siginfo_t(ctypes.Structure): # typedef struct # int si_fd; # } _sigpoll; # - # /* SIGSYS. */ + # /* SIGSYS. */ # struct # { # void *_call_addr; /* Calling user insn. */ diff --git a/lib/py/criu.py b/lib/py/criu.py index d94fea9e1..f3e018095 100644 --- a/lib/py/criu.py +++ b/lib/py/criu.py @@ -11,8 +11,8 @@ class _criu_comm: """ - Base class for communication classes. - """ + Base class for communication classes. + """ COMM_SK = 0 COMM_FD = 1 COMM_BIN = 2 @@ -22,22 +22,22 @@ class _criu_comm: 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). - """ + 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. - """ + Disconnect from criu. + """ pass class _criu_comm_sk(_criu_comm): """ - Communication class for unix socket. - """ + Communication class for unix socket. + """ def __init__(self, sk_path): self.comm_type = self.COMM_SK @@ -55,8 +55,8 @@ def disconnect(self): class _criu_comm_fd(_criu_comm): """ - Communication class for file descriptor. - """ + Communication class for file descriptor. + """ def __init__(self, fd): self.comm_type = self.COMM_FD @@ -74,8 +74,8 @@ def disconnect(self): class _criu_comm_bin(_criu_comm): """ - Communication class for binary. - """ + Communication class for binary. + """ def __init__(self, bin_path): self.comm_type = self.COMM_BIN @@ -139,8 +139,8 @@ def disconnect(self): class CRIUException(Exception): """ - Exception class for handling and storing criu errors. - """ + Exception class for handling and storing criu errors. + """ typ = None _str = None @@ -150,8 +150,8 @@ def __str__(self): class CRIUExceptionInternal(CRIUException): """ - Exception class for handling and storing internal errors. - """ + Exception class for handling and storing internal errors. + """ def __init__(self, typ, s): self.typ = typ @@ -161,8 +161,8 @@ def __init__(self, typ, s): class CRIUExceptionExternal(CRIUException): """ - Exception class for handling and storing criu RPC errors. - """ + Exception class for handling and storing criu RPC errors. + """ def __init__(self, req_typ, resp_typ, errno): self.typ = req_typ @@ -196,8 +196,8 @@ def _gen_error_str(self): class criu: """ - Call criu through RPC. - """ + Call criu through RPC. + """ opts = None #CRIU options in pb format _comm = None #Communication method @@ -209,26 +209,26 @@ def __init__(self): def use_sk(self, sk_name): """ - Access criu using unix socket which that belongs to criu service daemon. - """ + 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. - """ + 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. - """ + 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. - """ + 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 @@ -262,8 +262,8 @@ def _send_req_and_recv_resp(self, req): def check(self): """ - Checks whether the kernel support is up-to-date. - """ + Checks whether the kernel support is up-to-date. + """ req = rpc.criu_req() req.type = rpc.CHECK @@ -274,8 +274,8 @@ def check(self): def dump(self): """ - Checkpoint a process/tree identified by opts.pid. - """ + Checkpoint a process/tree identified by opts.pid. + """ req = rpc.criu_req() req.type = rpc.DUMP req.opts.MergeFrom(self.opts) @@ -289,8 +289,8 @@ def dump(self): def pre_dump(self): """ - Checkpoint a process/tree identified by opts.pid. - """ + Checkpoint a process/tree identified by opts.pid. + """ req = rpc.criu_req() req.type = rpc.PRE_DUMP req.opts.MergeFrom(self.opts) @@ -304,8 +304,8 @@ def pre_dump(self): def restore(self): """ - Restore a process/tree. - """ + Restore a process/tree. + """ req = rpc.criu_req() req.type = rpc.RESTORE req.opts.MergeFrom(self.opts) diff --git a/lib/py/images/images.py b/lib/py/images/images.py index 28c6d9e1f..f4517d845 100644 --- a/lib/py/images/images.py +++ b/lib/py/images/images.py @@ -12,8 +12,8 @@ # 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. +# 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 @@ -23,18 +23,18 @@ # Using dict(json) format, criu images can be described like: # # { -# 'magic' : 'FOO', -# 'entries' : [ -# entry, -# ... -# ] +# 'magic' : 'FOO', +# 'entries' : [ +# entry, +# ... +# ] # } # # Entry, in its turn, could be described as: # # { -# pb_msg, -# 'extra' : extra_msg +# pb_msg, +# 'extra' : extra_msg # } # import io @@ -72,23 +72,23 @@ def __init__(self, magic): # 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). - """ + 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. - """ + Sets payload class and extra handler class. + """ self.payload = payload self.extra_handler = extra_handler def load(self, f, pretty=False, no_payload=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. - """ + 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: @@ -128,17 +128,17 @@ def human_readable(num): def loads(self, s, pretty=False): """ - Same as load(), but takes a string as an argument. - """ + 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. - """ + 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) @@ -156,17 +156,17 @@ def dump(self, entries, f): def dumps(self, entries): """ - Same as dump(), but doesn't take file-like object and just - returns a string. - """ + 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 - """ + Counts the number of top-level object in the image file + """ entries = 0 while True: @@ -183,10 +183,10 @@ def count(self, f): # 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. - """ + 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, no_payload=False): entries = [] @@ -547,10 +547,10 @@ def __rhandler(f): def load(f, pretty=False, no_payload=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. - """ + 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) @@ -574,18 +574,18 @@ def info(f): def loads(s, pretty=False): """ - Same as load(), but takes a string. - """ + 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. - """ + 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']] @@ -609,9 +609,9 @@ def dump(img, f): def dumps(img): """ - Same as dump(), but takes only an image and returns - a string. - """ + Same as dump(), but takes only an image and returns + a string. + """ f = io.BytesIO(b'') dump(img, f) return f.getvalue() diff --git a/lib/py/images/pb2dict.py b/lib/py/images/pb2dict.py index 8825f9897..30be93fa8 100644 --- a/lib/py/images/pb2dict.py +++ b/lib/py/images/pb2dict.py @@ -311,9 +311,9 @@ def _pb2dict_cast(field, value, pretty=False, is_hex=False): def pb2dict(pb, pretty=False, is_hex=False): """ - Convert protobuf msg to dictionary. - Takes a protobuf message and returns a dict. - """ + 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: @@ -382,9 +382,9 @@ def _dict2pb_cast(field, value): def dict2pb(d, pb): """ - Convert dictionary to protobuf msg. - Takes dict and protobuf message to be merged into. - """ + 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 diff --git a/scripts/magic-gen.py b/scripts/magic-gen.py index 3d9777735..3b1f29fb5 100755 --- a/scripts/magic-gen.py +++ b/scripts/magic-gen.py @@ -15,8 +15,8 @@ def main(argv): out = open(magic_py, 'w+') # all_magic is used to parse constructions like: - # #define PAGEMAP_MAGIC 0x56084025 - # #define SHMEM_PAGEMAP_MAGIC PAGEMAP_MAGIC + # #define PAGEMAP_MAGIC 0x56084025 + # #define SHMEM_PAGEMAP_MAGIC PAGEMAP_MAGIC all_magic = {} # and magic is used to store only unique magic. magic = {} From 258bf940782126a30646949d7fcf2a3161936f92 Mon Sep 17 00:00:00 2001 From: Radostin Stoyanov Date: Mon, 1 Jul 2019 17:40:44 +0300 Subject: [PATCH 183/249] py: Manual fixlets of code formatting Signed-off-by: Pavel Emelyanov --- coredump/criu_coredump/coredump.py | 3 +-- lib/py/images/pb2dict.py | 4 ++-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/coredump/criu_coredump/coredump.py b/coredump/criu_coredump/coredump.py index bc53a7705..68dc16bf2 100644 --- a/coredump/criu_coredump/coredump.py +++ b/coredump/criu_coredump/coredump.py @@ -645,8 +645,7 @@ def _get_page(self, pid, page_no): ppid = self.pstree[pid]["ppid"] return self._get_page(ppid, page_no) else: - with open(self._imgs_dir + "/" + "pages-" + str(pages_id) + - ".img") as f: + with open(self._imgs_dir + "/pages-%s.img" % pages_id) as f: f.seek(off * PAGESIZE) return f.read(PAGESIZE) diff --git a/lib/py/images/pb2dict.py b/lib/py/images/pb2dict.py index 30be93fa8..8c039bcf2 100644 --- a/lib/py/images/pb2dict.py +++ b/lib/py/images/pb2dict.py @@ -212,8 +212,8 @@ 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)) + return "%d:%d" % (value >> kern_minorbits, + value & ((1 << kern_minorbits) - 1)) def encode_dev(field, value): From 08abfeefd9c4cf96173c056c36ffffa04a251c5e Mon Sep 17 00:00:00 2001 From: Pavel Emelyanov Date: Mon, 1 Jul 2019 17:43:56 +0300 Subject: [PATCH 184/249] pyimages: Add pb2dict.py to checked and fix warnings/errors Signed-off-by: Pavel Emelyanov --- Makefile | 1 + lib/py/images/pb2dict.py | 24 ++++++++++-------------- 2 files changed, 11 insertions(+), 14 deletions(-) diff --git a/Makefile b/Makefile index 0b49364fb..0140330e1 100644 --- a/Makefile +++ b/Makefile @@ -384,6 +384,7 @@ lint: flake8 --config=scripts/flake8.cfg test/zdtm.py flake8 --config=scripts/flake8.cfg test/inhfd/*.py flake8 --config=scripts/flake8.cfg test/others/rpc/config_file.py + flake8 --config=scripts/flake8.cfg lib/py/images/pb2dict.py include Makefile.install diff --git a/lib/py/images/pb2dict.py b/lib/py/images/pb2dict.py index 8c039bcf2..bc11b2c68 100644 --- a/lib/py/images/pb2dict.py +++ b/lib/py/images/pb2dict.py @@ -294,7 +294,7 @@ def _pb2dict_cast(field, value, pretty=False, is_hex=False): if flags: try: flags_map = flags_maps[flags] - except: + except Exception: return "0x%x" % value # flags are better seen as hex anyway else: return map_flags(value, flags_map) @@ -324,9 +324,9 @@ def pb2dict(pb, pretty=False, is_hex=False): addr = 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])) + (socket.ntohl(value[1]) << (32 * 2)) + \ + (socket.ntohl(value[2]) << (32 * 1)) + \ + (socket.ntohl(value[3])) addr = IPv6Address(v) d_val.append(addr.compressed) @@ -358,7 +358,7 @@ def _dict2pb_cast(field, value): if flags: try: flags_map = flags_maps[flags] - except: + except Exception: pass # Try to use plain string cast else: return unmap_flags(value, flags_map) @@ -366,7 +366,7 @@ def _dict2pb_cast(field, value): dct = _marked_as_dict(field) if dct: ret = dict_maps[dct][1][field.name].get(value, None) - if ret == None: + if ret is None: ret = cast(value, 0) return ret @@ -397,14 +397,10 @@ def dict2pb(d, pb): 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)) + 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) From 8ff73c85e3297fd5fa7414dcad6876c3b8160f56 Mon Sep 17 00:00:00 2001 From: Harshavardhan Unnibhavi Date: Fri, 8 Mar 2019 15:43:46 +0530 Subject: [PATCH 185/249] Documentation: Create man page for libcompel Resolves #349 Signed-off-by: Harshavardhan Unnibhavi --- Documentation/Makefile | 1 + Documentation/compel.txt | 119 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 120 insertions(+) create mode 100644 Documentation/compel.txt diff --git a/Documentation/Makefile b/Documentation/Makefile index aa5d3ebbf..cbc7ff2c8 100644 --- a/Documentation/Makefile +++ b/Documentation/Makefile @@ -12,6 +12,7 @@ endif FOOTER := footer.txt SRC1 += crit.txt +SRC1 += compel.txt SRC8 += criu.txt SRC := $(SRC1) $(SRC8) XMLS := $(patsubst %.txt,%.xml,$(SRC)) diff --git a/Documentation/compel.txt b/Documentation/compel.txt new file mode 100644 index 000000000..744a3b35d --- /dev/null +++ b/Documentation/compel.txt @@ -0,0 +1,119 @@ +COMPEL(1) +========== +include::footer.txt[] + +NAME +---- +compel - Execute parasitic code within another process. + +SYNOPSIS +-------- +*compel* 'hgen' ['option' ...] + +*compel* 'plugins' ['PLUGIN_NAME' ...] + +*compel* ['--compat'] 'includes' | 'cflags' | 'ldflags' + +*compel* ['--compat'] ['--static'] 'libs' + +DESCRIPTION +------------ +*compel* is a utility to execute arbitrary code, also called parasite code, +in the context of a foreign process. The parasitic code, once compiled with +compel flags and packed, can be executed in the context of other tasks. Currently +there is only one way to load the parasitic blob into victim task using libcompel.a, +called c-header. + +ARGUMENTS +---------- + +Positional Arguments +~~~~~~~~~~~~~~~~~~~~ + +*hgen*:: + create a header from the .po file, which is the parasite binary. + +*plugins*:: + prints the plugins available. + +*ldflags*:: + prints the ldflags available to compel during linking of parasite code. + +*cflags*:: + prints the compel cflags to be used during compilation of parasitic code. + +*includes*:: + prints list of standard include directories. + +*libs*:: + prints list of static or dynamic libraries that compel can link with. + +OPTIONS +-------- +*-f*, *--file* 'FILE':: + Path to the binary file, 'FILE', which *compel* must turn into a header + +*-o*, *--output* 'FILE':: + Path to the header file, 'FILE', where compel must write the resulting header. + +*-p*, *--prefix* 'NAME':: + Specify prefix for var names + +*-l*, *--log-level* 'NUM':: + Default log level of compel. + +*-h*, *--help*:: + Prints usage and exits. + +*-V*, *--version*:: + Prints version number of compel. + +SOURCE EXAMPLES +---------------- + +Parasitic Code +~~~~~~~~~~~~~~ + +*#include * + +*int parasite_trap_cmd(int cmd, void *args);* //gets called by compel_run_in_thread() + +*int parasite_daemon_cmd(int cmd, void *arg);* // gets called by compel_rpc_call() and compel_rpc_call_sync() + +*void parasite_cleanup(void);* //gets called on parasite unload by compel_cure() + +Infecting code +~~~~~~~~~~~~~~ +The parasitic code is compiled and converted to a header using *compel*, and included here. + +*#include * + +*#include "parasite.h"* + +Following steps are perfomed to infect the victim process: + + - stop the task: *int compel_stop_task(int pid);* + - prepare infection handler: *struct parasite_ctl *compel_prepare(int pid);* + - execute system call: *int compel_syscall(ctl, int syscall_nr, long *ret, int arg ...);* + - infect victim: *int compel_infect(ctl, nr_thread, size_of_args_area);* + - cure the victim: *int compel_cure(ctl);* //ctl pointer is freed by this call + - Resume victim: *int compel_resume_task(pid, orig_state, state);* + +*ctl* must be configured with blob information by calling *PREFIX_setup_c_header()*, with ctl as its argument. +*PREFIX* is the argument given to *-p* when calling hgen, else it is deduced from file name. + + +EXAMPLES +--------- +To generate a header file(.h) from a parasite binary file(.po) use: + +---------- + compel hgen -f parasite.po -o parasite.h +---------- + +'parasite.po' file is obtained by compiling the parasite source with compel flags and +linking it with the compel plugins. + +AUTHOR +------ +The CRIU team. From c57cc7e563124e37949c24f5ccbdd4158846961f Mon Sep 17 00:00:00 2001 From: Dengguangxing Date: Wed, 19 Jun 2019 09:13:39 +0000 Subject: [PATCH 186/249] fix segmentation fault caused by uninitialized mutex Segmentation fault was raised while trying to restore a process with tty. Coredump file says this is caused by uninitialized tty_mutex: (gdb) where #0 0x00000000004d7270 in atomic_add_return (i=1, v=0x0) at include/common/asm/atomic.h:34 #1 0x00000000004d7398 in mutex_lock (m=0x0) at include/common/lock.h:151 #2 0x00000000004d840c in __pty_open_ptmx_index (index=3, flags=2, cb=0x4dce50 , arg=0x11, path=0x5562e0 "ptmx") at criu/tty.c:603 #3 0x00000000004dced8 in pty_create_ptmx_index (dfd=17, index=3, flags=2) at criu/tty.c:2384 since init_tty_mutex() is reentrantable, just calling it before mutex_lock() Signed-off-by: Deng Guangxing Reviewed-by: Cyrill Gorcunov Signed-off-by: Andrei Vagin --- criu/tty.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/criu/tty.c b/criu/tty.c index 6fe11530a..e9a28897c 100644 --- a/criu/tty.c +++ b/criu/tty.c @@ -600,6 +600,9 @@ static int __pty_open_ptmx_index(int index, int flags, memset(fds, 0xff, sizeof(fds)); + if (init_tty_mutex()) + return -1; + mutex_lock(tty_mutex); for (i = 0; i < ARRAY_SIZE(fds); i++) { From 32c730f04f873cc06272775f8f74802b288cfea6 Mon Sep 17 00:00:00 2001 From: Cyrill Gorcunov Date: Tue, 25 Jun 2019 15:16:26 +0300 Subject: [PATCH 187/249] tty: Move tty layer shared init into tty_init_restore Instead of using tty_mutex value in atomic context (which is wrong, since it is not atomic) better move tty_mutex allocation into cr_restore_tasks where our all initializers live. Otherwise weird race effect might be observed. Reported-by: Deng Guangxing Signed-off-by: Cyrill Gorcunov --- criu/cr-restore.c | 3 +++ criu/include/tty.h | 1 + criu/tty.c | 15 +-------------- 3 files changed, 5 insertions(+), 14 deletions(-) diff --git a/criu/cr-restore.c b/criu/cr-restore.c index ecfee1296..b184a58a0 100644 --- a/criu/cr-restore.c +++ b/criu/cr-restore.c @@ -2354,6 +2354,9 @@ int cr_restore_tasks(void) if (vdso_init_restore()) goto err; + if (tty_init_restore()) + goto err; + if (opts.cpu_cap & CPU_CAP_IMAGE) { if (cpu_validate_cpuinfo()) goto err; diff --git a/criu/include/tty.h b/criu/include/tty.h index 95ced8396..8419593e5 100644 --- a/criu/include/tty.h +++ b/criu/include/tty.h @@ -32,6 +32,7 @@ struct mount_info; extern int devpts_restore(struct mount_info *pm); extern int tty_prep_fds(void); +extern int tty_init_restore(void); extern int devpts_check_bindmount(struct mount_info *m); diff --git a/criu/tty.c b/criu/tty.c index e9a28897c..dee8d46bf 100644 --- a/criu/tty.c +++ b/criu/tty.c @@ -349,11 +349,8 @@ static mutex_t *tty_mutex; static bool tty_is_master(struct tty_info *info); -static int init_tty_mutex(void) +int tty_init_restore(void) { - if (tty_mutex) - return 0; - tty_mutex = shmalloc(sizeof(*tty_mutex)); if (!tty_mutex) { pr_err("Can't create ptmx index mutex\n"); @@ -600,9 +597,6 @@ static int __pty_open_ptmx_index(int index, int flags, memset(fds, 0xff, sizeof(fds)); - if (init_tty_mutex()) - return -1; - mutex_lock(tty_mutex); for (i = 0; i < ARRAY_SIZE(fds); i++) { @@ -1792,13 +1786,6 @@ static int tty_info_setup(struct tty_info *info) add_post_prepare_cb_once(&prep_tty_restore); - /* - * Call it explicitly. Post-callbacks will be called after - * namespaces preparation, while the latter needs this mutex. - */ - if (init_tty_mutex()) - return -1; - info->fdstore_id = -1; return file_desc_add(&info->d, info->tfe->id, &tty_desc_ops); } From ad4e398ced9d5dc854202a363d49c2bb36752324 Mon Sep 17 00:00:00 2001 From: Adrian Reber Date: Tue, 14 May 2019 18:12:53 +0000 Subject: [PATCH 188/249] sk-inet: fix coverity IDENTICAL_BRANCHES criu-3.12/criu/sk-inet.c:575: identical_branches: The same code is executed when the condition "pb_write_one(img_from_set(glob_imgset, CR_FD_FILES), &fe, PB_FILE)" is true or false, because the code in the if-then branch and after the if statement is identical. Should the if statement be removed? Signed-off-by: Adrian Reber --- criu/sk-inet.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/criu/sk-inet.c b/criu/sk-inet.c index 90ab492ed..f9c64c7af 100644 --- a/criu/sk-inet.c +++ b/criu/sk-inet.c @@ -573,7 +573,7 @@ static int do_dump_one_inet_fd(int lfd, u32 id, const struct fd_parms *p, int fa ie.ip_opts->raw = NULL; if (pb_write_one(img_from_set(glob_imgset, CR_FD_FILES), &fe, PB_FILE)) - goto err; + err = -1; err: ip_raw_opts_free(&ipopts_raw); release_skopts(&skopts); From 0e1362c12a46eba26a2a240850cdad2cf8459c11 Mon Sep 17 00:00:00 2001 From: Adrian Reber Date: Wed, 15 May 2019 12:23:56 +0000 Subject: [PATCH 189/249] seize: fix coverity RESOURCE_LEAK criu-3.12/criu/seize.c:648: leaked_storage: Variable "threads" going out of scope leaks the storage it points to. Signed-off-by: Adrian Reber --- criu/seize.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/criu/seize.c b/criu/seize.c index b958d4bf9..cce8911b9 100644 --- a/criu/seize.c +++ b/criu/seize.c @@ -627,6 +627,7 @@ static int collect_threads(struct pstree_item *item) { struct seccomp_entry *task_seccomp_entry; struct pid *threads = NULL; + struct pid *tmp = NULL; int nr_threads = 0, i = 0, ret, nr_inprogress, nr_stopped = 0; task_seccomp_entry = seccomp_find_entry(item->pid->real); @@ -643,9 +644,11 @@ static int collect_threads(struct pstree_item *item) } /* The number of threads can't be less than already frozen */ - item->threads = xrealloc(item->threads, nr_threads * sizeof(struct pid)); - if (item->threads == NULL) - return -1; + tmp = xrealloc(item->threads, nr_threads * sizeof(struct pid)); + if (tmp == NULL) + goto err; + + item->threads = tmp; if (item->nr_threads == 0) { item->threads[0].real = item->pid->real; From ecda8c06d281e3cdbc12625896126b8a7ad66411 Mon Sep 17 00:00:00 2001 From: Adrian Reber Date: Wed, 15 May 2019 14:03:19 +0000 Subject: [PATCH 190/249] util: fix clang 'null pointer passed' criu-3.12/criu/util.c:879:9: warning: Null pointer passed as an argument to a 'nonnull' parameter criu-3.12/criu/util.c:1171:3: warning: Value stored to 'ret' is never read Signed-off-by: Adrian Reber --- criu/util.c | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/criu/util.c b/criu/util.c index 31cdee1ff..f02486a15 100644 --- a/criu/util.c +++ b/criu/util.c @@ -878,6 +878,12 @@ void split(char *str, char token, char ***out, int *n) cur++; } + if (*n == 0) { + /* This can only happen if str == NULL */ + *out = NULL; + *n = -1; + return; + } *out = xmalloc((*n) * sizeof(char *)); if (!*out) { @@ -1088,7 +1094,7 @@ int run_tcp_server(bool daemon_mode, int *ask, int cfd, int sk) ret = cr_daemon(1, 0, cfd); if (ret == -1) { pr_err("Can't run in the background\n"); - goto out; + goto err; } if (ret > 0) { /* parent task, daemon started */ close_safe(&sk); @@ -1109,10 +1115,11 @@ int run_tcp_server(bool daemon_mode, int *ask, int cfd, int sk) return -1; if (sk >= 0) { - ret = *ask = accept(sk, (struct sockaddr *)&caddr, &clen); - if (*ask < 0) + *ask = accept(sk, (struct sockaddr *)&caddr, &clen); + if (*ask < 0) { pr_perror("Can't accept connection to server"); - else + goto err; + } else pr_info("Accepted connection from %s:%u\n", inet_ntoa(caddr.sin_addr), (int)ntohs(caddr.sin_port)); @@ -1120,7 +1127,7 @@ int run_tcp_server(bool daemon_mode, int *ask, int cfd, int sk) } return 0; -out: +err: close(sk); return -1; } From bfdd3db1af4020d02ec18844c81081aa1df8de89 Mon Sep 17 00:00:00 2001 From: Adrian Reber Date: Wed, 15 May 2019 07:37:58 +0000 Subject: [PATCH 191/249] files-reg: fix coverity RESOURCE_LEAK criu-3.12/criu/files-reg.c:774: leaked_storage: Variable "img" going out of scope leaks the storage it points to. criu-3.12/criu/files-reg.c:788: leaked_storage: Variable "img" going out of scope leaks the storage it points to. criu-3.12/criu/files-reg.c:797: leaked_storage: Variable "img" going out of scope leaks the storage it points to. Signed-off-by: Adrian Reber --- criu/files-reg.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/criu/files-reg.c b/criu/files-reg.c index d982126c0..1b51d1088 100644 --- a/criu/files-reg.c +++ b/criu/files-reg.c @@ -776,6 +776,7 @@ static struct collect_image_info remap_cinfo = { static int dump_ghost_file(int _fd, u32 id, const struct stat *st, dev_t phys_dev) { struct cr_img *img; + int exit_code = -1; GhostFileEntry gfe = GHOST_FILE_ENTRY__INIT; Timeval atim = TIMEVAL__INIT, mtim = TIMEVAL__INIT; @@ -812,7 +813,7 @@ static int dump_ghost_file(int _fd, u32 id, const struct stat *st, dev_t phys_de } if (pb_write_one(img, &gfe, PB_GHOST_FILE)) - return -1; + goto err_out; if (S_ISREG(st->st_mode)) { int fd, ret; @@ -826,7 +827,7 @@ static int dump_ghost_file(int _fd, u32 id, const struct stat *st, dev_t phys_de fd = open(lpath, O_RDONLY); if (fd < 0) { pr_perror("Can't open ghost original file"); - return -1; + goto err_out; } if (gfe.chunks) @@ -835,11 +836,13 @@ static int dump_ghost_file(int _fd, u32 id, const struct stat *st, dev_t phys_de ret = copy_file(fd, img_raw_fd(img), st->st_size); close(fd); if (ret) - return -1; + goto err_out; } + exit_code = 0; +err_out: close_image(img); - return 0; + return exit_code; } struct file_remap *lookup_ghost_remap(u32 dev, u32 ino) From 0318abcd43e68d4f0ab8199eb81d991f4489133b Mon Sep 17 00:00:00 2001 From: Pavel Tikhomirov Date: Tue, 2 Jul 2019 12:51:50 +0300 Subject: [PATCH 192/249] dedup: convert noisy warning to debug and improve messages We want to grep warnings from zdtm tests to travis final logs. And I see a lot of these: (00.250989) Warn (criu/pagemap.c:90): Missing 7f84103e3000 in parent pagemap (00.250999) p 0x7f84103f5000 [1] We do a lookup of an intersecting pagemap entry with a memory region we want to dedup, it is expected that sometimes we don't have some subrange in pagemap entries. So these should not be a warning, make it debug message. While on it change the message to save us from been confused with other "Missing..." error messages, and change abstract "parent image" message to the IDs of pages image in all messages in dedup_one_iovec(). v2: print image ids --- criu/pagemap.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/criu/pagemap.c b/criu/pagemap.c index a19969b2f..3c3930b7b 100644 --- a/criu/pagemap.c +++ b/criu/pagemap.c @@ -87,11 +87,15 @@ int dedup_one_iovec(struct page_read *pr, unsigned long off, unsigned long len) ret = pr->seek_pagemap(pr, off); if (ret == 0) { - pr_warn("Missing %lx in parent pagemap\n", off); - if (off < pr->cvaddr && pr->cvaddr < iov_end) + if (off < pr->cvaddr && pr->cvaddr < iov_end) { + pr_debug("pr%lu-%u:No range %lx-%lx in pagemap\n", + pr->img_id, pr->id, off, pr->cvaddr); off = pr->cvaddr; - else + } else { + pr_debug("pr%lu-%u:No range %lx-%lx in pagemap\n", + pr->img_id, pr->id, off, iov_end); return 0; + } } if (!pr->pe) @@ -106,7 +110,8 @@ int dedup_one_iovec(struct page_read *pr, unsigned long off, unsigned long len) prp = pr->parent; if (prp) { /* recursively */ - pr_debug("Go to next parent level\n"); + pr_debug("pr%lu-%u:Go to next parent level\n", + pr->img_id, pr->id); len = min(piov_end, iov_end) - off; ret = dedup_one_iovec(prp, off, len); if (ret != 0) From 63cc957fc6dd03f335944321556a2f7d61a41984 Mon Sep 17 00:00:00 2001 From: Pavel Tikhomirov Date: Tue, 2 Jul 2019 13:33:30 +0300 Subject: [PATCH 193/249] inventory: skip warning in case of no parent directory We want to grep warnings from zdtm tests to travis final logs. And I see a lot of these: Warn (criu/image.c:137): Failed to open parent directory If there is no parent images directory then there is no previous dump and no pid-reuse problem with pagemaps possible, so it is fine to have no parent inventory image at the same time which is used here to fix the problem. These always hapens on the first iteration of iterative dump. So don't warn here. While on it also fix error message in detect_pid_reuse. v2: add detect_pid_reuse part v3: improve comments --- criu/image.c | 22 +++++++++++++++++++++- criu/mem.c | 2 +- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/criu/image.c b/criu/image.c index 78947ab5f..c21ac1774 100644 --- a/criu/image.c +++ b/criu/image.c @@ -126,6 +126,18 @@ int inventory_save_uptime(InventoryEntry *he) return 0; } +/* + * This function is intended to get an inventory image from previous (parent) + * dump iteration. We use dump_uptime from the image in detect_pid_reuse(). + * + * You see that these function never fails by itself, it only prints warnings + * to better understand reasons why we don't found a proper image, failing here + * is too early. We get to detect_pid_reuse() only if we have a parent pagemap + * and that's the proper place to fail: we know that there is a parent pagemap + * but we don't have (can't access, etc) parent inventory => can't detect + * pid-reuse => fail. + */ + InventoryEntry *get_parent_inventory(void) { struct cr_img *img; @@ -134,7 +146,15 @@ InventoryEntry *get_parent_inventory(void) dir = openat(get_service_fd(IMG_FD_OFF), CR_PARENT_LINK, O_RDONLY); if (dir == -1) { - pr_warn("Failed to open parent directory\n"); + /* + * We print the warning below to be notified that we had some + * unexpected problem on open. For instance we have a parent + * directory but have no access. Having no parent inventory + * when also having no parent directory is an expected case of + * first dump iteration. + */ + if (errno != ENOENT) + pr_warn("Failed to open parent directory\n"); return NULL; } diff --git a/criu/mem.c b/criu/mem.c index 6a1a87a1e..d2a39a9db 100644 --- a/criu/mem.c +++ b/criu/mem.c @@ -318,7 +318,7 @@ static int detect_pid_reuse(struct pstree_item *item, if (!parent_ie) { pr_err("Pid-reuse detection failed: no parent inventory, " \ - "check warnings in get_parent_stats\n"); + "check warnings in get_parent_inventory\n"); return -1; } From 74731d9b44103d62b4588eb58974bf544ac98c59 Mon Sep 17 00:00:00 2001 From: Pavel Tikhomirov Date: Mon, 1 Jul 2019 11:28:43 +0300 Subject: [PATCH 194/249] zdtm: make grep_errors also grep warnings It is inspired by the discussion about inotify fix: https://github.com/checkpoint-restore/criu/pull/728#issuecomment-506929427 From one point of view, warnings might be important to understand why we detect some visible change in the environment after c/r-ing the process, and if this change is expected or not. So we should add "Warn" messages to the output. From over point, these warnings if they are expected, can spoil our final logs with a lot of unnecessary details, so add changes in previous patches to silence the most noisy of these warnings. Signed-off-by: Pavel Tikhomirov --- test/zdtm.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/zdtm.py b/test/zdtm.py index 69be1df5a..510b7ec17 100755 --- a/test/zdtm.py +++ b/test/zdtm.py @@ -2062,7 +2062,7 @@ def grep_errors(fname): before.append(l) if len(before) > 5: before.pop(0) - if "Error" in l: + if "Error" in l or "Warn" in l: if first: print_fname(fname, 'log') print_sep("grep Error", "-", 60) From a53cef2725350eee71940743406b5224f0fa23d9 Mon Sep 17 00:00:00 2001 From: Andrei Vagin Date: Mon, 15 Jul 2019 01:04:42 -0700 Subject: [PATCH 195/249] test/packet_sock_mmap: parse inode as unsigned long long 7f95a16df000-7f95a16e1000 rw-p 00000000 00:09 2183152397 socket:[2183152397] Reported-by: Mr Jenkins Signed-off-by: Andrei Vagin --- test/zdtm/static/packet_sock_mmap.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/test/zdtm/static/packet_sock_mmap.c b/test/zdtm/static/packet_sock_mmap.c index 2a82950bc..93d6ebbf2 100644 --- a/test/zdtm/static/packet_sock_mmap.c +++ b/test/zdtm/static/packet_sock_mmap.c @@ -35,16 +35,17 @@ struct tpacket_req3 { static void check_map_is_there(unsigned long addr, int sk) { FILE *f; - char line[64]; + char line[4096]; struct stat ss; fstat(sk, &ss); f = fopen("/proc/self/maps", "r"); while (fgets(line, sizeof(line), f) != NULL) { + unsigned long long ino; unsigned long start; - int maj, min, ino; + int maj, min; - sscanf(line, "%lx-%*x %*s %*s %x:%x %d %*s", &start, &maj, &min, &ino); + sscanf(line, "%lx-%*x %*s %*s %x:%x %llu %*s", &start, &maj, &min, &ino); if ((start == addr) && ss.st_dev == makedev(maj, min) && ss.st_ino == ino) { pass(); fclose(f); From d364f6c11f6875a7c02afffe9ec65eb67ed5af8c Mon Sep 17 00:00:00 2001 From: Andrei Vagin Date: Wed, 17 Jul 2019 00:42:24 -0700 Subject: [PATCH 196/249] zdtm: use a proper page size for the host In zdtm.py, the page size is hardcoded as 4096, but on ppc64le, is is equal to 64K and all test fail with errors like this: ERROR: bad page counts, stats = 13 real = 208(0) Signed-off-by: Andrei Vagin --- test/zdtm.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/test/zdtm.py b/test/zdtm.py index 510b7ec17..9b93a51e7 100755 --- a/test/zdtm.py +++ b/test/zdtm.py @@ -23,6 +23,7 @@ import datetime import yaml import struct +import mmap import pycriu as crpc os.chdir(os.path.dirname(os.path.abspath(__file__))) @@ -1199,8 +1200,8 @@ def check_pages_counts(self): if f.startswith('pages-'): real_written += os.path.getsize(os.path.join(self.__ddir(), f)) - r_pages = real_written / 4096 - r_off = real_written % 4096 + r_pages = real_written / mmap.PAGESIZE + r_off = real_written % mmap.PAGESIZE if (stats_written != r_pages) or (r_off != 0): print("ERROR: bad page counts, stats = %d real = %d(%d)" % (stats_written, r_pages, r_off)) From 01b2ec706d2545ecc1400581c67ec9737daf2193 Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Wed, 17 Jul 2019 14:30:01 +0200 Subject: [PATCH 197/249] Replace libprotobuf-c0-dev with libprotobuf-c-dev The `libprotobuf-c0-dev` virtual package is no longer available in Debian Buster, but is provided by `libprotobuf-c-dev`, which is available. Signed-off-by: Sebastiaan van Stijn --- contrib/debian/dev-packages.lst | 2 +- criu/Makefile.packages | 2 +- scripts/build/Dockerfile.tmpl | 2 +- scripts/travis/travis-tests | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/contrib/debian/dev-packages.lst b/contrib/debian/dev-packages.lst index b0b664f72..049bbd82d 100644 --- a/contrib/debian/dev-packages.lst +++ b/contrib/debian/dev-packages.lst @@ -1,7 +1,7 @@ # Required packages for development in Debian build-essential libprotobuf-dev -libprotobuf-c0-dev +libprotobuf-c-dev protobuf-c-compiler protobuf-compiler python-protobuf diff --git a/criu/Makefile.packages b/criu/Makefile.packages index b01b4b044..f380fa2f0 100644 --- a/criu/Makefile.packages +++ b/criu/Makefile.packages @@ -11,7 +11,7 @@ REQ-RPM-PKG-NAMES += $(PYTHON)-future REQ-RPM-PKG-TEST-NAMES += libaio-devel REQ-DEB-PKG-NAMES += libprotobuf-dev -REQ-DEB-PKG-NAMES += libprotobuf-c0-dev +REQ-DEB-PKG-NAMES += libprotobuf-c-dev REQ-DEB-PKG-NAMES += protobuf-c-compiler REQ-DEB-PKG-NAMES += protobuf-compiler REQ-DEB-PKG-NAMES += python-protobuf diff --git a/scripts/build/Dockerfile.tmpl b/scripts/build/Dockerfile.tmpl index 4378ba149..d90a1d229 100644 --- a/scripts/build/Dockerfile.tmpl +++ b/scripts/build/Dockerfile.tmpl @@ -15,7 +15,7 @@ RUN apt-get update && apt-get install -y \ libgnutls28-dev \ libgnutls30 \ libnl-3-dev \ - libprotobuf-c0-dev \ + libprotobuf-c-dev \ libprotobuf-dev \ libselinux-dev \ pkg-config \ diff --git a/scripts/travis/travis-tests b/scripts/travis/travis-tests index 664f723e9..348daca1f 100755 --- a/scripts/travis/travis-tests +++ b/scripts/travis/travis-tests @@ -1,7 +1,7 @@ #!/bin/sh set -x -e -TRAVIS_PKGS="protobuf-c-compiler libprotobuf-c0-dev libaio-dev +TRAVIS_PKGS="protobuf-c-compiler libprotobuf-c-dev libaio-dev libgnutls28-dev libgnutls30 libprotobuf-dev protobuf-compiler libcap-dev libnl-3-dev gcc-multilib gdb bash python-protobuf libnet-dev util-linux asciidoctor libnl-route-3-dev" From 5cbd1316ede729a13764babab6f29fb569978c9c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20C=C5=82api=C5=84ski?= Date: Fri, 12 Jul 2019 18:12:42 +0200 Subject: [PATCH 198/249] Add support for migrating CHILD_SUBREAPER prctl MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. Checkpoint it via parasite. 2. Restore it after forking. Signed-off-by: Michał Cłapiński Reviewed-by: Pavel Tikhomirov --- criu/cr-dump.c | 8 ++++++-- criu/cr-restore.c | 7 +++++-- criu/include/parasite.h | 1 + criu/include/restorer.h | 1 + criu/pie/parasite.c | 12 +++++++++++- criu/pie/restorer.c | 13 +++++++++++++ images/core.proto | 2 ++ 7 files changed, 39 insertions(+), 5 deletions(-) diff --git a/criu/cr-dump.c b/criu/cr-dump.c index 7f2e5edfc..f250a7da2 100644 --- a/criu/cr-dump.c +++ b/criu/cr-dump.c @@ -727,7 +727,8 @@ int dump_thread_core(int pid, CoreEntry *core, const struct parasite_dump_thread static int dump_task_core_all(struct parasite_ctl *ctl, struct pstree_item *item, const struct proc_pid_stat *stat, - const struct cr_imgset *cr_imgset) + const struct cr_imgset *cr_imgset, + const struct parasite_dump_misc *misc) { struct cr_img *img; CoreEntry *core = item->core[0]; @@ -741,6 +742,9 @@ static int dump_task_core_all(struct parasite_ctl *ctl, pr_info("Dumping core (pid: %d)\n", pid); pr_info("----------------------------------------\n"); + core->tc->child_subreaper = misc->child_subreaper; + core->tc->has_child_subreaper = true; + ret = get_task_personality(pid, &core->tc->personality); if (ret < 0) goto err; @@ -1379,7 +1383,7 @@ static int dump_one_task(struct pstree_item *item, InventoryEntry *parent_ie) goto err_cure; } - ret = dump_task_core_all(parasite_ctl, item, &pps_buf, cr_imgset); + ret = dump_task_core_all(parasite_ctl, item, &pps_buf, cr_imgset, &misc); if (ret) { pr_err("Dump core (pid: %d) failed with %d\n", pid, ret); goto err_cure; diff --git a/criu/cr-restore.c b/criu/cr-restore.c index b184a58a0..d56068396 100644 --- a/criu/cr-restore.c +++ b/criu/cr-restore.c @@ -807,10 +807,13 @@ static int prepare_oom_score_adj(int value) return ret; } -static int prepare_proc_misc(pid_t pid, TaskCoreEntry *tc) +static int prepare_proc_misc(pid_t pid, TaskCoreEntry *tc, struct task_restore_args *args) { int ret; + if (tc->has_child_subreaper) + args->child_subreaper = tc->child_subreaper; + /* loginuid value is critical to restore */ if (kdat.luid == LUID_FULL && tc->has_loginuid && tc->loginuid != INVALID_UID) { @@ -878,7 +881,7 @@ static int restore_one_alive_task(int pid, CoreEntry *core) if (collect_zombie_pids(ta) < 0) return -1; - if (prepare_proc_misc(pid, core->tc)) + if (prepare_proc_misc(pid, core->tc, ta)) return -1; /* diff --git a/criu/include/parasite.h b/criu/include/parasite.h index 0a62f2439..d9570948a 100644 --- a/criu/include/parasite.h +++ b/criu/include/parasite.h @@ -126,6 +126,7 @@ struct parasite_dump_misc { int dumpable; int thp_disabled; + int child_subreaper; }; /* diff --git a/criu/include/restorer.h b/criu/include/restorer.h index effbc3655..f980bfad3 100644 --- a/criu/include/restorer.h +++ b/criu/include/restorer.h @@ -217,6 +217,7 @@ struct task_restore_args { unsigned page_size; #endif int lsm_type; + int child_subreaper; } __aligned(64); /* diff --git a/criu/pie/parasite.c b/criu/pie/parasite.c index 01bacd311..9a179ef8b 100644 --- a/criu/pie/parasite.c +++ b/criu/pie/parasite.c @@ -39,6 +39,10 @@ static struct parasite_dump_pages_args *mprotect_args = NULL; #define PR_GET_PDEATHSIG 2 #endif +#ifndef PR_GET_CHILD_SUBREAPER +#define PR_GET_CHILD_SUBREAPER 37 +#endif + static int mprotect_vmas(struct parasite_dump_pages_args *args) { struct parasite_vma_entry *vmas, *vma; @@ -202,6 +206,8 @@ static int dump_thread_common(struct parasite_dump_thread *ti) static int dump_misc(struct parasite_dump_misc *args) { + int ret; + args->brk = sys_brk(0); args->pid = sys_getpid(); @@ -212,7 +218,11 @@ static int dump_misc(struct parasite_dump_misc *args) args->dumpable = sys_prctl(PR_GET_DUMPABLE, 0, 0, 0, 0); args->thp_disabled = sys_prctl(PR_GET_THP_DISABLE, 0, 0, 0, 0); - return 0; + ret = sys_prctl(PR_GET_CHILD_SUBREAPER, (unsigned long)&args->child_subreaper, 0, 0, 0); + if (ret) + pr_err("PR_GET_CHILD_SUBREAPER failed (%d)\n", ret); + + return ret; } static int dump_creds(struct parasite_dump_creds *args) diff --git a/criu/pie/restorer.c b/criu/pie/restorer.c index 513be74e0..324a11e0c 100644 --- a/criu/pie/restorer.c +++ b/criu/pie/restorer.c @@ -52,6 +52,10 @@ #define PR_SET_PDEATHSIG 1 #endif +#ifndef PR_SET_CHILD_SUBREAPER +#define PR_SET_CHILD_SUBREAPER 36 +#endif + #ifndef FALLOC_FL_KEEP_SIZE #define FALLOC_FL_KEEP_SIZE 0x01 #endif @@ -1231,6 +1235,14 @@ static bool vdso_needs_parking(struct task_restore_args *args) return !vdso_unmapped(args); } +static inline int restore_child_subreaper(int child_subreaper) +{ + if (child_subreaper) + return sys_prctl(PR_SET_CHILD_SUBREAPER, child_subreaper, 0, 0, 0); + else + return 0; +} + /* * The main routine to restore task via sigreturn. * This one is very special, we never return there @@ -1731,6 +1743,7 @@ long __export_restore_task(struct task_restore_args *args) args->lsm_type); ret = ret || restore_dumpable_flag(&args->mm); ret = ret || restore_pdeath_sig(args->t); + ret = ret || restore_child_subreaper(args->child_subreaper); futex_set_and_wake(&thread_inprogress, args->nr_threads); diff --git a/images/core.proto b/images/core.proto index 312a983f0..9f3f870c9 100644 --- a/images/core.proto +++ b/images/core.proto @@ -51,6 +51,8 @@ message task_core_entry { // Reserved for tty inheritance //optional int32 tty_nr = 16; //optional int32 tty_pgrp = 17; + + optional int32 child_subreaper = 18; } message task_kobj_ids_entry { From 0740d18da588307323e98e7d596925fe912526ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20C=C5=82api=C5=84ski?= Date: Fri, 12 Jul 2019 18:14:41 +0200 Subject: [PATCH 199/249] Add ZDTM tests for child subreaper property MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. Basic check if property is migrated 2. Check that property is restored for existing children 3. Check that child subreaper does not affect reparenting Signed-off-by: Pavel Tikhomirov Signed-off-by: Michał Cłapiński Reviewed-by: Pavel Tikhomirov --- test/zdtm/static/Makefile | 3 + test/zdtm/static/child_subreaper.c | 36 +++++ .../static/child_subreaper_and_reparent.c | 142 ++++++++++++++++++ .../static/child_subreaper_and_reparent.desc | 1 + .../static/child_subreaper_existing_child.c | 138 +++++++++++++++++ 5 files changed, 320 insertions(+) create mode 100644 test/zdtm/static/child_subreaper.c create mode 100644 test/zdtm/static/child_subreaper_and_reparent.c create mode 100644 test/zdtm/static/child_subreaper_and_reparent.desc create mode 100644 test/zdtm/static/child_subreaper_existing_child.c diff --git a/test/zdtm/static/Makefile b/test/zdtm/static/Makefile index 7799c0b0a..52bd00602 100644 --- a/test/zdtm/static/Makefile +++ b/test/zdtm/static/Makefile @@ -214,6 +214,9 @@ TST_NOFILE := \ selinux00 \ selinux01 \ selinux02 \ + child_subreaper \ + child_subreaper_existing_child \ + child_subreaper_and_reparent \ # jobctl00 \ ifneq ($(SRCARCH),arm) diff --git a/test/zdtm/static/child_subreaper.c b/test/zdtm/static/child_subreaper.c new file mode 100644 index 000000000..267795249 --- /dev/null +++ b/test/zdtm/static/child_subreaper.c @@ -0,0 +1,36 @@ +#include +#include + +#include "zdtmtst.h" + +const char *test_doc = "Check that child subreaper attribute is restored"; +const char *test_author = "Michał Cłapiński "; + +int main(int argc, char **argv) +{ + test_init(argc, argv); + + int cs_before = 1; + int ret = prctl(PR_SET_CHILD_SUBREAPER, cs_before, 0, 0, 0); + if (ret) { + pr_perror("Can't set child subreaper attribute, err = %d", ret); + exit(1); + } + + test_daemon(); + test_waitsig(); + + int cs_after; + ret = prctl(PR_GET_CHILD_SUBREAPER, (unsigned long)&cs_after, 0, 0, 0); + if (ret) { + pr_perror("Can't get child subreaper attribute, err = %d", ret); + exit(1); + } + + if (cs_before != cs_after) + fail("%d != %d\n", cs_before, cs_after); + else + pass(); + + return 0; +} diff --git a/test/zdtm/static/child_subreaper_and_reparent.c b/test/zdtm/static/child_subreaper_and_reparent.c new file mode 100644 index 000000000..57943a67b --- /dev/null +++ b/test/zdtm/static/child_subreaper_and_reparent.c @@ -0,0 +1,142 @@ +#include +#include +#include +#include +#include +#include + +#include "zdtmtst.h" +#include "lock.h" + +const char *test_doc = "Check that child subreaper does not affect reparenting"; +const char *test_author = "Pavel Tikhomirov "; + +enum { + TEST_FORK, + TEST_SAVE, + TEST_CRIU, + TEST_CHECK, + TEST_EXIT, +}; + +struct shared { + futex_t fstate; + int parent_before_cr; + int parent_after_cr; +} *sh; + +int orphan() +{ + /* + * Wait until reparented to the pidns init. (By waiting + * for the SUBREAPER to reap our parent.) + */ + futex_wait_until(&sh->fstate, TEST_SAVE); + + sh->parent_before_cr = getppid(); + + /* Return the control back to MAIN worker to do C/R */ + futex_set_and_wake(&sh->fstate, TEST_CRIU); + futex_wait_until(&sh->fstate, TEST_CHECK); + + sh->parent_after_cr = getppid(); + + futex_set_and_wake(&sh->fstate, TEST_EXIT); + return 0; +} + +int helper() +{ + int pid; + + pid = fork(); + if (pid < 0) { + pr_perror("Failed to fork"); + return 1; + } else if (pid == 0) { + exit(orphan()); + } + return 0; +} + +int subreaper() +{ + int pid, ret, status; + + setsid(); + + pid = fork(); + if (pid < 0) { + pr_perror("Failed to fork"); + return 1; + } else if (pid == 0) { + exit(helper()); + } + + /* Reap the HELPER */ + waitpid(pid, &status, 0); + if (!WIFEXITED(status) || WEXITSTATUS(status)) { + pr_perror("Wrong exit status for helper: %d", status); + return 1; + } + + ret = prctl(PR_SET_CHILD_SUBREAPER, 1, 0, 0, 0); + if (ret) { + pr_perror("Can't set child subreaper attribute, err = %d", ret); + return 1; + } + + /* Give control to ORPHAN to save it's parent */ + futex_set_and_wake(&sh->fstate, TEST_SAVE); + futex_wait_until(&sh->fstate, TEST_EXIT); + return 0; +} + +int main(int argc, char **argv) +{ + int pid, status; + + sh = mmap(NULL, sizeof(struct shared), PROT_WRITE | PROT_READ, MAP_SHARED | MAP_ANONYMOUS, -1, 0); + if (sh == MAP_FAILED) { + pr_perror("Failed to alloc shared region"); + exit(1); + } + + futex_set(&sh->fstate, TEST_FORK); + + test_init(argc, argv); + + setsid(); + + pid = fork(); + if (pid < 0) { + pr_perror("Failed to fork"); + exit(1); + } else if (pid == 0) { + exit(subreaper()); + } + + /* Wait until ORPHAN is ready to C/R */ + futex_wait_until(&sh->fstate, TEST_CRIU); + + test_daemon(); + test_waitsig(); + + /* Give control to ORPHAN to check it's parent */ + futex_set_and_wake(&sh->fstate, TEST_CHECK); + futex_wait_until(&sh->fstate, TEST_EXIT); + + /* Cleanup */ + while (wait(&status) > 0) { + if (!WIFEXITED(status) || WEXITSTATUS(status)) { + fail("Wrong exit status: %d", status); + return 1; + } + } + + if (sh->parent_before_cr != sh->parent_after_cr) + fail("Parent mismatch before %d after %d", sh->parent_before_cr, sh->parent_after_cr); + else + pass(); + return 0; +} diff --git a/test/zdtm/static/child_subreaper_and_reparent.desc b/test/zdtm/static/child_subreaper_and_reparent.desc new file mode 100644 index 000000000..6c4afe5f0 --- /dev/null +++ b/test/zdtm/static/child_subreaper_and_reparent.desc @@ -0,0 +1 @@ +{'flavor': 'ns uns'} diff --git a/test/zdtm/static/child_subreaper_existing_child.c b/test/zdtm/static/child_subreaper_existing_child.c new file mode 100644 index 000000000..28e9dbb8a --- /dev/null +++ b/test/zdtm/static/child_subreaper_existing_child.c @@ -0,0 +1,138 @@ +#include +#include +#include +#include +#include + +#include "zdtmtst.h" +#include "lock.h" + +const char *test_doc = "Check that property is restored for existing children"; +const char *test_author = "Michał Cłapiński "; + +enum { + TEST_FORK, + TEST_CRIU, + TEST_DIE, + TEST_CHECK, + TEST_EXIT, +}; + +struct shared { + futex_t fstate; + int ppid_after_reparent; +} *sh; + + +int orphan() +{ + /* Return the control back to MAIN worker to do C/R */ + futex_set_and_wake(&sh->fstate, TEST_CRIU); + futex_wait_until(&sh->fstate, TEST_CHECK); + + sh->ppid_after_reparent = getppid(); + + futex_set_and_wake(&sh->fstate, TEST_EXIT); + return 0; +} + +int helper() +{ + int pid; + + pid = fork(); + if (pid < 0) { + pr_perror("Failed to fork"); + return 1; + } else if (pid == 0) { + exit(orphan()); + } + + futex_wait_until(&sh->fstate, TEST_DIE); + return 0; +} + +int subreaper() +{ + int pid, ret, status; + + pid = fork(); + if (pid < 0) { + pr_perror("Failed to fork"); + return 1; + } else if (pid == 0) { + exit(helper()); + } + + ret = prctl(PR_SET_CHILD_SUBREAPER, 1, 0, 0, 0); + if (ret) { + pr_perror("Can't set child subreaper attribute, err = %d", ret); + return 1; + } + + /* Reap the HELPER */ + waitpid(pid, &status, 0); + if (!WIFEXITED(status) || WEXITSTATUS(status)) { + pr_perror("Wrong exit status for HELPER: %d", status); + return 1; + } + + /* Give control to ORPHAN so it can check its parent */ + futex_set_and_wake(&sh->fstate, TEST_CHECK); + futex_wait_until(&sh->fstate, TEST_EXIT); + + /* Cleanup: reap the ORPHAN */ + wait(&status); + if (!WIFEXITED(status) || WEXITSTATUS(status)) { + pr_perror("Wrong exit status for ORPHAN: %d", status); + return 1; + } + + return 0; +} + +int main(int argc, char **argv) +{ + int pid, status; + + sh = mmap(NULL, sizeof(struct shared), PROT_WRITE | PROT_READ, MAP_SHARED | MAP_ANONYMOUS, -1, 0); + if (sh == MAP_FAILED) { + pr_perror("Failed to alloc shared region"); + exit(1); + } + + futex_set(&sh->fstate, TEST_FORK); + + test_init(argc, argv); + + pid = fork(); + if (pid < 0) { + pr_perror("Failed to fork"); + exit(1); + } else if (pid == 0) { + exit(subreaper()); + } + + /* Wait until ORPHAN is ready to C/R */ + futex_wait_until(&sh->fstate, TEST_CRIU); + + test_daemon(); + test_waitsig(); + + /* Give control to HELPER so it can die */ + futex_set_and_wake(&sh->fstate, TEST_DIE); + futex_wait_until(&sh->fstate, TEST_EXIT); + + /* Cleanup: reap the SUBREAPER */ + waitpid(pid, &status, 0); + if (!WIFEXITED(status) || WEXITSTATUS(status)) { + fail("Wrong exit status: %d", status); + return 1; + } + + if (sh->ppid_after_reparent != pid) + fail("Orphan was reparented to %d instead of %d", sh->ppid_after_reparent, pid); + else + pass(); + return 0; +} From 6eaf08e4322ccec12cf08e542240f0c2f169259e Mon Sep 17 00:00:00 2001 From: Cyrill Gorcunov Date: Fri, 5 Jul 2019 18:37:56 +0300 Subject: [PATCH 200/249] mem/page-pipe: Eliminate redundant pipe_off setup In case if we may use previous pipe the pipe_off get set directly so no need for redundat unconditional assignment. Signed-off-by: Cyrill Gorcunov Acked-by: Mike Rapoport Signed-off-by: Andrei Vagin --- criu/page-pipe.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/criu/page-pipe.c b/criu/page-pipe.c index c32b89332..33741db42 100644 --- a/criu/page-pipe.c +++ b/criu/page-pipe.c @@ -104,8 +104,6 @@ static struct page_pipe_buf *ppb_alloc(struct page_pipe *pp, return NULL; cnt_add(CNT_PAGE_PIPE_BUFS, 1); - ppb->pipe_off = 0; - if (prev && ppb_resize_pipe(prev) == 0) { /* The previous pipe isn't full and we can continue to use it. */ ppb->p[0] = prev->p[0]; @@ -120,6 +118,7 @@ static struct page_pipe_buf *ppb_alloc(struct page_pipe *pp, } cnt_add(CNT_PAGE_PIPES, 1); + ppb->pipe_off = 0; ppb->pipe_size = fcntl(ppb->p[0], F_GETPIPE_SZ, 0) / PAGE_SIZE; pp->nr_pipes++; } From 23e095e7b780f2630d635a17d23bbcf2762b23ed Mon Sep 17 00:00:00 2001 From: Cyrill Gorcunov Date: Fri, 5 Jul 2019 18:37:57 +0300 Subject: [PATCH 201/249] mem/page-pipe: create_page_pipe -- Drop redundant zero assignment We allocate with xzalloc, no need for additional zero assignemtns. Signed-off-by: Cyrill Gorcunov Signed-off-by: Andrei Vagin --- criu/page-pipe.c | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/criu/page-pipe.c b/criu/page-pipe.c index 33741db42..32be2f981 100644 --- a/criu/page-pipe.c +++ b/criu/page-pipe.c @@ -187,26 +187,18 @@ struct page_pipe *create_page_pipe(unsigned int nr_segs, struct iovec *iovs, uns if (!pp) return NULL; + INIT_LIST_HEAD(&pp->free_bufs); + INIT_LIST_HEAD(&pp->bufs); + pp->nr_iovs = nr_segs; pp->flags = flags; if (!iovs) { iovs = xmalloc(sizeof(*iovs) * nr_segs); if (!iovs) goto err_free_pp; - pp->flags |= PP_OWN_IOVS; } - - pp->nr_pipes = 0; - INIT_LIST_HEAD(&pp->bufs); - INIT_LIST_HEAD(&pp->free_bufs); - pp->nr_iovs = nr_segs; pp->iovs = iovs; - pp->free_iov = 0; - - pp->nr_holes = 0; - pp->free_hole = 0; - pp->holes = NULL; if (page_pipe_grow(pp, 0)) goto err_free_iovs; From 2510112da8b71c42423ec33a87ecc7525116f1b7 Mon Sep 17 00:00:00 2001 From: Cyrill Gorcunov Date: Fri, 5 Jul 2019 18:37:58 +0300 Subject: [PATCH 202/249] mem/page-pipe: Align members for readability sake Signed-off-by: Cyrill Gorcunov Reviewed-by: Mike Rapoport Signed-off-by: Andrei Vagin --- criu/include/page-pipe.h | 46 ++++++++++++++++++++-------------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/criu/include/page-pipe.h b/criu/include/page-pipe.h index 80e595871..decd14321 100644 --- a/criu/include/page-pipe.h +++ b/criu/include/page-pipe.h @@ -91,15 +91,15 @@ struct kernel_pipe_buffer { */ struct page_pipe_buf { - int p[2]; /* pipe with pages */ - unsigned int pipe_size; /* how many pages can be fit into pipe */ - unsigned int pipe_off; /* where this buf is started in a pipe */ - unsigned int pages_in; /* how many pages are there */ - unsigned int nr_segs; /* how many iov-s are busy */ + int p[2]; /* pipe with pages */ + unsigned int pipe_size; /* how many pages can be fit into pipe */ + unsigned int pipe_off; /* where this buf is started in a pipe */ + unsigned int pages_in; /* how many pages are there */ + unsigned int nr_segs; /* how many iov-s are busy */ #define PPB_LAZY (1 << 0) - unsigned int flags; - struct iovec *iov; /* vaddr:len map */ - struct list_head l; /* links into page_pipe->bufs */ + unsigned int flags; + struct iovec *iov; /* vaddr:len map */ + struct list_head l; /* links into page_pipe->bufs */ }; /* @@ -114,21 +114,21 @@ struct page_pipe_buf { #define PP_HOLE_PARENT (1 << 0) struct page_pipe { - unsigned int nr_pipes; /* how many page_pipe_bufs in there */ - struct list_head bufs; /* list of bufs */ - struct list_head free_bufs; /* list of bufs */ - struct page_pipe_buf *prev[PP_PIPE_TYPES]; /* last ppb of each type - for pipe sharing */ - unsigned int nr_iovs; /* number of iovs */ - unsigned int free_iov; /* first free iov */ - struct iovec *iovs; /* iovs. They are provided into create_page_pipe - and all bufs have their iov-s in there */ - - unsigned int nr_holes; /* number of holes allocated */ - unsigned int free_hole; /* number of holes in use */ - struct iovec *holes; /* holes */ - unsigned int *hole_flags; - unsigned flags; /* PP_FOO flags below */ + unsigned int nr_pipes; /* how many page_pipe_bufs in there */ + struct list_head bufs; /* list of bufs */ + struct list_head free_bufs; /* list of bufs */ + struct page_pipe_buf *prev[PP_PIPE_TYPES]; /* last ppb of each type for pipe sharing */ + unsigned int nr_iovs; /* number of iovs */ + unsigned int free_iov; /* first free iov */ + + struct iovec *iovs; /* iovs. They are provided into create_page_pipe + and all bufs have their iov-s in there */ + + unsigned int nr_holes; /* number of holes allocated */ + unsigned int free_hole; /* number of holes in use */ + struct iovec *holes; /* holes */ + unsigned int *hole_flags; + unsigned int flags; /* PP_FOO flags below */ }; #define PP_CHUNK_MODE 0x1 /* Restrict the maximum buffer size of pipes From c5957101f4c6a8d7b2224b43552c796c928496bc Mon Sep 17 00:00:00 2001 From: Cyrill Gorcunov Date: Fri, 5 Jul 2019 18:37:59 +0300 Subject: [PATCH 203/249] mem/page-pipe: Use ssize_t for splice/tee results Integer value is too short. Signed-off-by: Cyrill Gorcunov Reviewed-by: Mike Rapoport Signed-off-by: Andrei Vagin --- criu/page-pipe.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/criu/page-pipe.c b/criu/page-pipe.c index 32be2f981..534380b0f 100644 --- a/criu/page-pipe.c +++ b/criu/page-pipe.c @@ -389,7 +389,7 @@ int page_pipe_read(struct page_pipe *pp, struct pipe_read_dest *prd, struct page_pipe_buf *ppb; struct iovec *iov = NULL; unsigned long skip = 0, len; - int ret; + ssize_t ret; /* * Get ppb that contains addr and count length of data between @@ -418,13 +418,13 @@ int page_pipe_read(struct page_pipe *pp, struct pipe_read_dest *prd, ret = tee(ppb->p[0], prd->p[1], len, 0); if (ret != len) { - pr_perror("tee: %d", ret); + pr_perror("tee: %zd", ret); return -1; } ret = splice(prd->p[0], NULL, prd->sink_fd, NULL, skip, 0); if (ret != skip) { - pr_perror("splice: %d", ret); + pr_perror("splice: %zd", ret); return -1; } From 4d1edc463ae6c53415b235051d8e46787f829e2a Mon Sep 17 00:00:00 2001 From: Cyrill Gorcunov Date: Fri, 5 Jul 2019 18:38:00 +0300 Subject: [PATCH 204/249] mem/vma: Use memset for vm_area_list_init To eliminate side effects, in particular setting nr_aios is already missing here. Signed-off-by: Cyrill Gorcunov Reviewed-by: Mike Rapoport Signed-off-by: Andrei Vagin --- criu/include/vma.h | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/criu/include/vma.h b/criu/include/vma.h index c297c0d14..6f5ee19d3 100644 --- a/criu/include/vma.h +++ b/criu/include/vma.h @@ -7,6 +7,7 @@ #include "images/vma.pb-c.h" #include +#include struct vm_area_list { struct list_head h; @@ -21,11 +22,8 @@ struct vm_area_list { static inline void vm_area_list_init(struct vm_area_list *vml) { + memset(vml, 0, sizeof(*vml)); INIT_LIST_HEAD(&vml->h); - vml->nr = 0; - vml->priv_size = 0; - vml->priv_longest = 0; - vml->shared_longest = 0; } struct file_desc; From a289959332fd002af11e1254b39b40d2a673ea43 Mon Sep 17 00:00:00 2001 From: Cyrill Gorcunov Date: Fri, 5 Jul 2019 18:38:01 +0300 Subject: [PATCH 205/249] mem/vma: Use vm_area_list_init where appropriate Signed-off-by: Cyrill Gorcunov Reviewed-by: Mike Rapoport Signed-off-by: Andrei Vagin --- criu/cr-dump.c | 9 +++------ criu/proc_parse.c | 7 +------ 2 files changed, 4 insertions(+), 12 deletions(-) diff --git a/criu/cr-dump.c b/criu/cr-dump.c index f250a7da2..34029460a 100644 --- a/criu/cr-dump.c +++ b/criu/cr-dump.c @@ -109,8 +109,7 @@ void free_mappings(struct vm_area_list *vma_area_list) free(vma_area); } - INIT_LIST_HEAD(&vma_area_list->h); - vma_area_list->nr = 0; + vm_area_list_init(vma_area_list); } int collect_mappings(pid_t pid, struct vm_area_list *vma_area_list, @@ -1143,8 +1142,7 @@ static int pre_dump_one_task(struct pstree_item *item, InventoryEntry *parent_ie struct parasite_dump_misc misc; struct mem_dump_ctl mdc; - INIT_LIST_HEAD(&vmas.h); - vmas.nr = 0; + vm_area_list_init(&vmas); pr_info("========================================\n"); pr_info("Pre-dumping task (pid: %d)\n", pid); @@ -1225,8 +1223,7 @@ static int dump_one_task(struct pstree_item *item, InventoryEntry *parent_ie) struct proc_posix_timers_stat proc_args; struct mem_dump_ctl mdc; - INIT_LIST_HEAD(&vmas.h); - vmas.nr = 0; + vm_area_list_init(&vmas); pr_info("========================================\n"); pr_info("Dumping task (pid: %d)\n", pid); diff --git a/criu/proc_parse.c b/criu/proc_parse.c index f6ebb1fd6..4c127f264 100644 --- a/criu/proc_parse.c +++ b/criu/proc_parse.c @@ -705,12 +705,7 @@ int parse_smaps(pid_t pid, struct vm_area_list *vma_area_list, DIR *map_files_dir = NULL; struct bfd f; - vma_area_list->nr = 0; - vma_area_list->nr_aios = 0; - vma_area_list->priv_longest = 0; - vma_area_list->priv_size = 0; - vma_area_list->shared_longest = 0; - INIT_LIST_HEAD(&vma_area_list->h); + vm_area_list_init(vma_area_list); f.fd = open_proc(pid, "smaps"); if (f.fd < 0) From b6ffd68377e5e1e18eda64a68f371574710ad065 Mon Sep 17 00:00:00 2001 From: Cyrill Gorcunov Date: Fri, 5 Jul 2019 18:38:02 +0300 Subject: [PATCH 206/249] mem/vma: Drop never used VM_AREA_LIST macro Signed-off-by: Cyrill Gorcunov Reviewed-by: Mike Rapoport Signed-off-by: Andrei Vagin --- criu/include/vma.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/criu/include/vma.h b/criu/include/vma.h index 6f5ee19d3..3cdd1b319 100644 --- a/criu/include/vma.h +++ b/criu/include/vma.h @@ -18,8 +18,6 @@ struct vm_area_list { unsigned long shared_longest; /* nr of pages in longest shared VMA */ }; -#define VM_AREA_LIST(name) struct vm_area_list name = { .h = LIST_HEAD_INIT(name.h), .nr = 0, } - static inline void vm_area_list_init(struct vm_area_list *vml) { memset(vml, 0, sizeof(*vml)); From f1593515cc4d1e52983286e24311250440ca9a0a Mon Sep 17 00:00:00 2001 From: Cyrill Gorcunov Date: Fri, 5 Jul 2019 18:38:03 +0300 Subject: [PATCH 207/249] mem/vma: Sanitize struct vm_area_list - make names more descriptive - add comments - use union for nr_priv_pages and rst_priv_size since former priv_size has been used with different meaning: number of pages during checkpoint time and size in bytes on restore moment Signed-off-by: Cyrill Gorcunov Reviewed-by: Mike Rapoport Signed-off-by: Andrei Vagin --- criu/cr-dump.c | 2 +- criu/include/vma.h | 15 +++++++++------ criu/mem.c | 20 ++++++++++---------- criu/proc_parse.c | 9 +++++---- 4 files changed, 25 insertions(+), 21 deletions(-) diff --git a/criu/cr-dump.c b/criu/cr-dump.c index 34029460a..e070b8b25 100644 --- a/criu/cr-dump.c +++ b/criu/cr-dump.c @@ -126,7 +126,7 @@ int collect_mappings(pid_t pid, struct vm_area_list *vma_area_list, goto err; pr_info("Collected, longest area occupies %lu pages\n", - vma_area_list->priv_longest); + vma_area_list->nr_priv_pages_longest); pr_info_vma_list(&vma_area_list->h); pr_info("----------------------------------------\n"); diff --git a/criu/include/vma.h b/criu/include/vma.h index 3cdd1b319..5e3f3527b 100644 --- a/criu/include/vma.h +++ b/criu/include/vma.h @@ -10,12 +10,15 @@ #include struct vm_area_list { - struct list_head h; - unsigned nr; - unsigned int nr_aios; - unsigned long priv_size; /* nr of pages in private VMAs */ - unsigned long priv_longest; /* nr of pages in longest private VMA */ - unsigned long shared_longest; /* nr of pages in longest shared VMA */ + struct list_head h; /* list of VMAs */ + unsigned nr; /* nr of all VMAs in the list */ + unsigned int nr_aios; /* nr of AIOs VMAs in the list */ + union { + unsigned long nr_priv_pages; /* dmp: nr of pages in private VMAs */ + unsigned long rst_priv_size; /* rst: size of private VMAs */ + }; + unsigned long nr_priv_pages_longest; /* nr of pages in longest private VMA */ + unsigned long nr_shared_pages_longest;/* nr of pages in longest shared VMA */ }; static inline void vm_area_list_init(struct vm_area_list *vml) diff --git a/criu/mem.c b/criu/mem.c index d2a39a9db..de66a6210 100644 --- a/criu/mem.c +++ b/criu/mem.c @@ -81,7 +81,7 @@ unsigned long dump_pages_args_size(struct vm_area_list *vmas) /* In the worst case I need one iovec for each page */ return sizeof(struct parasite_dump_pages_args) + vmas->nr * sizeof(struct parasite_vma_entry) + - (vmas->priv_size + 1) * sizeof(struct iovec); + (vmas->nr_priv_pages + 1) * sizeof(struct iovec); } static inline bool __page_is_zero(u64 pme) @@ -414,14 +414,14 @@ static int __parasite_dump_pages_seized(struct pstree_item *item, timing_start(TIME_MEMDUMP); pr_debug(" Private vmas %lu/%lu pages\n", - vma_area_list->priv_longest, vma_area_list->priv_size); + vma_area_list->nr_priv_pages_longest, vma_area_list->nr_priv_pages); /* * Step 0 -- prepare */ - pmc_size = max(vma_area_list->priv_longest, - vma_area_list->shared_longest); + pmc_size = max(vma_area_list->nr_priv_pages_longest, + vma_area_list->nr_shared_pages_longest); if (pmc_init(&pmc, item->pid->real, &vma_area_list->h, pmc_size * PAGE_SIZE)) return -1; @@ -433,7 +433,7 @@ static int __parasite_dump_pages_seized(struct pstree_item *item, * use, i.e. on non-lazy non-predump. */ cpp_flags |= PP_CHUNK_MODE; - pp = create_page_pipe(vma_area_list->priv_size, + pp = create_page_pipe(vma_area_list->nr_priv_pages, mdc->lazy ? NULL : pargs_iovs(args), cpp_flags); if (!pp) @@ -612,9 +612,9 @@ int prepare_mm_pid(struct pstree_item *i) list_add_tail(&vma->list, &ri->vmas.h); if (vma_area_is_private(vma, kdat.task_size)) { - ri->vmas.priv_size += vma_area_len(vma); + ri->vmas.rst_priv_size += vma_area_len(vma); if (vma_has_guard_gap_hidden(vma)) - ri->vmas.priv_size += PAGE_SIZE; + ri->vmas.rst_priv_size += PAGE_SIZE; } pr_info("vma 0x%"PRIx64" 0x%"PRIx64"\n", vma->e->start, vma->e->end); @@ -1171,17 +1171,17 @@ int prepare_mappings(struct pstree_item *t) goto out; /* Reserve a place for mapping private vma-s one by one */ - addr = mmap(NULL, vmas->priv_size, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, 0, 0); + addr = mmap(NULL, vmas->rst_priv_size, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, 0, 0); if (addr == MAP_FAILED) { ret = -1; - pr_perror("Unable to reserve memory (%lu bytes)", vmas->priv_size); + pr_perror("Unable to reserve memory (%lu bytes)", vmas->rst_priv_size); goto out; } old_premmapped_addr = rsti(t)->premmapped_addr; old_premmapped_len = rsti(t)->premmapped_len; rsti(t)->premmapped_addr = addr; - rsti(t)->premmapped_len = vmas->priv_size; + rsti(t)->premmapped_len = vmas->rst_priv_size; ret = open_page_read(vpid(t), &pr, PR_TASK); if (ret <= 0) diff --git a/criu/proc_parse.c b/criu/proc_parse.c index 4c127f264..0e8b6f209 100644 --- a/criu/proc_parse.c +++ b/criu/proc_parse.c @@ -660,14 +660,15 @@ static int vma_list_add(struct vma_area *vma_area, unsigned long pages; pages = vma_area_len(vma_area) / PAGE_SIZE; - vma_area_list->priv_size += pages; - vma_area_list->priv_longest = max(vma_area_list->priv_longest, pages); + vma_area_list->nr_priv_pages += pages; + vma_area_list->nr_priv_pages_longest = + max(vma_area_list->nr_priv_pages_longest, pages); } else if (vma_area_is(vma_area, VMA_ANON_SHARED)) { unsigned long pages; pages = vma_area_len(vma_area) / PAGE_SIZE; - vma_area_list->shared_longest = - max(vma_area_list->shared_longest, pages); + vma_area_list->nr_shared_pages_longest = + max(vma_area_list->nr_shared_pages_longest, pages); } *prev_vfi = *vfi; From 5af347e45c088a78d24ea69fa8d3db635922457d Mon Sep 17 00:00:00 2001 From: Cyrill Gorcunov Date: Fri, 5 Jul 2019 18:38:04 +0300 Subject: [PATCH 208/249] mem/page-xfer: Add log prefix Signed-off-by: Cyrill Gorcunov Reviewed-by: Mike Rapoport Signed-off-by: Andrei Vagin --- criu/page-xfer.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/criu/page-xfer.c b/criu/page-xfer.c index 9cdffd8d0..fe457d201 100644 --- a/criu/page-xfer.c +++ b/criu/page-xfer.c @@ -7,6 +7,9 @@ #include #include +#undef LOG_PREFIX +#define LOG_PREFIX "page-xfer: " + #include "types.h" #include "cr_options.h" #include "servicefd.h" From 9723c18bcd84cbafe896b7c52219b59994b672ab Mon Sep 17 00:00:00 2001 From: Cyrill Gorcunov Date: Fri, 5 Jul 2019 18:38:05 +0300 Subject: [PATCH 209/249] mem/pmc: Use pr_warn_once if cache is disabled No need to spam on every pmc_init call. Signed-off-by: Cyrill Gorcunov Reviewed-by: Mike Rapoport Signed-off-by: Andrei Vagin --- criu/pagemap-cache.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/criu/pagemap-cache.c b/criu/pagemap-cache.c index a1c2d42f4..c80776123 100644 --- a/criu/pagemap-cache.c +++ b/criu/pagemap-cache.c @@ -56,7 +56,7 @@ int pmc_init(pmc_t *pmc, pid_t pid, const struct list_head *vma_head, size_t siz goto err; if (pagemap_cache_disabled) - pr_debug("The pagemap cache is disabled\n"); + pr_warn_once("The pagemap cache is disabled\n"); if (kdat.pmap == PM_DISABLED) { /* From c93090c4ef1612e65c2c9a501899e6533568ffad Mon Sep 17 00:00:00 2001 From: Cyrill Gorcunov Date: Fri, 5 Jul 2019 18:38:06 +0300 Subject: [PATCH 210/249] mem/pmc: Print pid for debug sake When logs are massive it is convenient for grepping. Signed-off-by: Cyrill Gorcunov Reviewed-by: Mike Rapoport Signed-off-by: Andrei Vagin --- criu/pagemap-cache.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/criu/pagemap-cache.c b/criu/pagemap-cache.c index c80776123..61ab09387 100644 --- a/criu/pagemap-cache.c +++ b/criu/pagemap-cache.c @@ -105,8 +105,8 @@ static int pmc_fill_cache(pmc_t *pmc, const struct vma_area *vma) pmc->start = vma->e->start; pmc->end = vma->e->end; - pr_debug("filling VMA %lx-%lx (%zuK) [l:%lx h:%lx]\n", - (long)vma->e->start, (long)vma->e->end, len >> 10, low, high); + pr_debug("%d: filling VMA %lx-%lx (%zuK) [l:%lx h:%lx]\n", + pmc->pid, (long)vma->e->start, (long)vma->e->end, len >> 10, low, high); /* * If we meet a small VMA, lets try to fit 2M cache @@ -123,8 +123,8 @@ static int pmc_fill_cache(pmc_t *pmc, const struct vma_area *vma) size_t size_cov = len; size_t nr_vmas = 1; - pr_debug("\t%16lx-%-16lx nr:%-5zu cov:%zu\n", - (long)vma->e->start, (long)vma->e->end, nr_vmas, size_cov); + pr_debug("\t%d: %16lx-%-16lx nr:%-5zu cov:%zu\n", + pmc->pid, (long)vma->e->start, (long)vma->e->end, nr_vmas, size_cov); list_for_each_entry_continue(vma, pmc->vma_head, list) { if (vma->e->start > high || vma->e->end > high) @@ -134,8 +134,8 @@ static int pmc_fill_cache(pmc_t *pmc, const struct vma_area *vma) size_cov += vma_area_len(vma); nr_vmas++; - pr_debug("\t%16lx-%-16lx nr:%-5zu cov:%zu\n", - (long)vma->e->start, (long)vma->e->end, nr_vmas, size_cov); + pr_debug("\t%d: %16lx-%-16lx nr:%-5zu cov:%zu\n", + pmc->pid, (long)vma->e->start, (long)vma->e->end, nr_vmas, size_cov); } if (nr_vmas > 1) { @@ -145,9 +145,9 @@ static int pmc_fill_cache(pmc_t *pmc, const struct vma_area *vma) * allows us to save a couple of code bytes. */ pmc->end = high; - pr_debug("\tcache mode [l:%lx h:%lx]\n", pmc->start, pmc->end); + pr_debug("\t%d: cache mode [l:%lx h:%lx]\n", pmc->pid, pmc->start, pmc->end); } else - pr_debug("\tsimple mode [l:%lx h:%lx]\n", pmc->start, pmc->end); + pr_debug("\t%d: simple mode [l:%lx h:%lx]\n", pmc->pid, pmc->start, pmc->end); } size_map = PAGEMAP_LEN(pmc->end - pmc->start); From 2fd40e067ade06b6864685134f152355b8a63732 Mon Sep 17 00:00:00 2001 From: Cyrill Gorcunov Date: Fri, 5 Jul 2019 18:38:07 +0300 Subject: [PATCH 211/249] mem/page-pipe: Use xrealloc_safe in page_pipe_add_hole To shrink code a bit. Signed-off-by: Cyrill Gorcunov Reviewed-by: Mike Rapoport Signed-off-by: Andrei Vagin --- criu/page-pipe.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/criu/page-pipe.c b/criu/page-pipe.c index 534380b0f..a8216962d 100644 --- a/criu/page-pipe.c +++ b/criu/page-pipe.c @@ -298,14 +298,12 @@ int page_pipe_add_hole(struct page_pipe *pp, unsigned long addr, unsigned int flags) { if (pp->free_hole >= pp->nr_holes) { - pp->holes = xrealloc(pp->holes, - (pp->nr_holes + PP_HOLES_BATCH) * sizeof(struct iovec)); - if (!pp->holes) + size_t new_size = (pp->nr_holes + PP_HOLES_BATCH) * sizeof(struct iovec); + if (xrealloc_safe(&pp->holes, new_size)) return -1; - pp->hole_flags = xrealloc(pp->hole_flags, - (pp->nr_holes + PP_HOLES_BATCH) * sizeof(unsigned int)); - if(!pp->hole_flags) + new_size = (pp->nr_holes + PP_HOLES_BATCH) * sizeof(unsigned int); + if (xrealloc_safe(&pp->hole_flags, new_size)) return -1; pp->nr_holes += PP_HOLES_BATCH; From 82b8b89bc1e248b30408258143b04241a9b0d044 Mon Sep 17 00:00:00 2001 From: Cyrill Gorcunov Date: Fri, 5 Jul 2019 18:38:08 +0300 Subject: [PATCH 212/249] mem/shmem: Use xrealloc_safe in expand_shmem Signed-off-by: Cyrill Gorcunov Reviewed-by: Mike Rapoport Signed-off-by: Andrei Vagin --- criu/shmem.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/criu/shmem.c b/criu/shmem.c index 03b088f26..da9242674 100644 --- a/criu/shmem.c +++ b/criu/shmem.c @@ -197,8 +197,7 @@ static int expand_shmem(struct shmem_info *si, unsigned long new_size) BUG_ON(new_map_size < map_size); - si->pstate_map = xrealloc(si->pstate_map, new_map_size); - if (!si->pstate_map) + if (xrealloc_safe(&si->pstate_map, new_map_size)) return -1; memzero(si->pstate_map + nr_map_items, new_map_size - map_size); return 0; From 9d3eb6ca9aa191db01158be80efcbdb1e3249deb Mon Sep 17 00:00:00 2001 From: Cyrill Gorcunov Date: Fri, 5 Jul 2019 20:47:10 +0300 Subject: [PATCH 213/249] mem/shmem: More elegant entries declaration Signed-off-by: Cyrill Gorcunov Signed-off-by: Andrei Vagin --- criu/shmem.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/criu/shmem.c b/criu/shmem.c index da9242674..1e7013a06 100644 --- a/criu/shmem.c +++ b/criu/shmem.c @@ -179,11 +179,12 @@ static void set_pstate(unsigned long *pstate_map, unsigned long pfn, static int expand_shmem(struct shmem_info *si, unsigned long new_size) { - unsigned long nr_pages, nr_map_items, map_size, - nr_new_map_items, new_map_size, old_size; + unsigned long nr_pages, nr_map_items, map_size; + unsigned long nr_new_map_items, new_map_size, old_size; old_size = si->size; si->size = new_size; + if (!is_shmem_tracking_en()) return 0; From 552626190ce58ee054ccfc8b3e6209ea87caac65 Mon Sep 17 00:00:00 2001 From: Cyrill Gorcunov Date: Fri, 5 Jul 2019 18:38:10 +0300 Subject: [PATCH 214/249] mem/shmem: Use xmalloc in collect_sysv_shmem To get error message in log if no memory available. Signed-off-by: Cyrill Gorcunov Reviewed-by: Mike Rapoport Signed-off-by: Andrei Vagin --- criu/shmem.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/criu/shmem.c b/criu/shmem.c index 1e7013a06..cb41ef5b3 100644 --- a/criu/shmem.c +++ b/criu/shmem.c @@ -234,7 +234,7 @@ int collect_sysv_shmem(unsigned long shmid, unsigned long size) * Tasks will not modify this object, so don't * shmalloc() as we do it for anon shared mem */ - si = malloc(sizeof(*si)); + si = xmalloc(sizeof(*si)); if (!si) return -1; From 728c9afae2f1b314283c61f3444676ce475a641c Mon Sep 17 00:00:00 2001 From: Cyrill Gorcunov Date: Fri, 5 Jul 2019 18:38:11 +0300 Subject: [PATCH 215/249] mem/shmem: Fix typos for_each_shmem macro Since we use _i as a counter in macro declaration we should use it as a reference. This macro simply happen to work now because of being called with variable i declarated in the caller code. Signed-off-by: Cyrill Gorcunov Reviewed-by: Mike Rapoport Signed-off-by: Andrei Vagin --- criu/shmem.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/criu/shmem.c b/criu/shmem.c index cb41ef5b3..6978621fe 100644 --- a/criu/shmem.c +++ b/criu/shmem.c @@ -43,8 +43,8 @@ #define SHMEM_HASH_SIZE 32 static struct hlist_head shmems_hash[SHMEM_HASH_SIZE]; -#define for_each_shmem(_i, _si) \ - for (i = 0; i < SHMEM_HASH_SIZE; i++) \ +#define for_each_shmem(_i, _si) \ + for (_i = 0; _i < SHMEM_HASH_SIZE; _i++) \ hlist_for_each_entry(_si, &shmems_hash[_i], h) struct shmem_info { From 8748fcf165aa50884cfe1ac255070341f0b55346 Mon Sep 17 00:00:00 2001 From: Radostin Stoyanov Date: Wed, 10 Jul 2019 18:47:56 +0100 Subject: [PATCH 216/249] make: Insert version macros in criu.h Including the version information of CRIU in criu.h is required by projects that use libcriu to preserve backward compatibility. Closes #738 Signed-off-by: Radostin Stoyanov --- lib/Makefile | 2 +- lib/c/criu.h | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/Makefile b/lib/Makefile index 67c50b95a..f9b66701e 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -1,6 +1,6 @@ CRIU_SO := libcriu.so CRIU_A := libcriu.a -UAPI_HEADERS := lib/c/criu.h images/rpc.proto +UAPI_HEADERS := lib/c/criu.h images/rpc.proto criu/include/version.h # # File to keep track of files installed by setup.py diff --git a/lib/c/criu.h b/lib/c/criu.h index 4462ce082..76f3547fc 100644 --- a/lib/c/criu.h +++ b/lib/c/criu.h @@ -21,8 +21,10 @@ #include +#include "version.h" + #ifdef __GNUG__ - extern "C" { + extern "C" { #endif enum criu_service_comm { From b6ed8da60bc3999fe1ff330e084c49045976f6ff Mon Sep 17 00:00:00 2001 From: Andrei Vagin Date: Wed, 17 Jul 2019 13:07:34 -0700 Subject: [PATCH 217/249] restorer: print errors if prctl syscalls failed Signed-off-by: Andrei Vagin --- criu/pie/restorer.c | 34 ++++++++++++++++++++++++++-------- 1 file changed, 26 insertions(+), 8 deletions(-) diff --git a/criu/pie/restorer.c b/criu/pie/restorer.c index 324a11e0c..2a7180d6a 100644 --- a/criu/pie/restorer.c +++ b/criu/pie/restorer.c @@ -328,10 +328,18 @@ static int restore_creds(struct thread_creds_args *args, int procfd, static inline int restore_pdeath_sig(struct thread_restore_args *ta) { - if (ta->pdeath_sig) - return sys_prctl(PR_SET_PDEATHSIG, ta->pdeath_sig, 0, 0, 0); - else + int ret; + + if (!ta->pdeath_sig) return 0; + + ret = sys_prctl(PR_SET_PDEATHSIG, ta->pdeath_sig, 0, 0, 0); + if (ret) { + pr_err("Unable to set PR_SET_PDEATHSIG(%d): %d\n", ta->pdeath_sig, ret); + return -1; + } + + return 0; } static int restore_dumpable_flag(MmEntry *mme) @@ -1237,10 +1245,18 @@ static bool vdso_needs_parking(struct task_restore_args *args) static inline int restore_child_subreaper(int child_subreaper) { - if (child_subreaper) - return sys_prctl(PR_SET_CHILD_SUBREAPER, child_subreaper, 0, 0, 0); - else + int ret; + + if (!child_subreaper) return 0; + + ret = sys_prctl(PR_SET_CHILD_SUBREAPER, child_subreaper, 0, 0, 0); + if (ret) { + pr_err("Unable to set PR_SET_CHILD_SUBREAPER(%d): %d\n", child_subreaper, ret); + return -1; + } + + return 0; } /* @@ -1364,8 +1380,10 @@ long __export_restore_task(struct task_restore_args *args) if (args->uffd > -1) { /* re-enable THP if we disabled it previously */ if (args->has_thp_enabled) { - if (sys_prctl(PR_SET_THP_DISABLE, 0, 0, 0, 0)) { - pr_err("Cannot re-enable THP\n"); + int ret; + ret = sys_prctl(PR_SET_THP_DISABLE, 0, 0, 0, 0); + if (ret) { + pr_err("Cannot re-enable THP: %d\n", ret); goto core_restore_end; } } From fa76ec4d55c5912c0143f320d20129ee424162e4 Mon Sep 17 00:00:00 2001 From: Andrei Vagin Date: Wed, 17 Jul 2019 13:08:56 -0700 Subject: [PATCH 218/249] images: convert type of child_subreaper from int32 to bool Signed-off-by: Andrei Vagin --- images/core.proto | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/core.proto b/images/core.proto index 9f3f870c9..c3dba6f6d 100644 --- a/images/core.proto +++ b/images/core.proto @@ -52,7 +52,7 @@ message task_core_entry { //optional int32 tty_nr = 16; //optional int32 tty_pgrp = 17; - optional int32 child_subreaper = 18; + optional bool child_subreaper = 18; } message task_kobj_ids_entry { From e94e596a2ec9d4c31d015ec8100771aabfd3c2f8 Mon Sep 17 00:00:00 2001 From: Adrian Reber Date: Mon, 29 Jul 2019 13:25:20 +0000 Subject: [PATCH 219/249] scripts: add possibility to override docker with podman To be able to run the test containers in scripts/build with podman this puts the name of the container runtime into $CONTAINER_RUNTIME. Now it can be overridden with make fedora-rawhide CONTAINER_RUNTIME=podman Signed-off-by: Adrian Reber --- scripts/build/Makefile | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/scripts/build/Makefile b/scripts/build/Makefile index f333b214a..bb2e9ca9d 100644 --- a/scripts/build/Makefile +++ b/scripts/build/Makefile @@ -2,6 +2,7 @@ QEMU_ARCHES := armv7hf aarch64 ppc64le s390x fedora-rawhide-aarch64 # require qe ARCHES := $(QEMU_ARCHES) x86_64 fedora-asan fedora-rawhide centos TARGETS := $(ARCHES) alpine TARGETS_CLANG := $(addsuffix $(TARGETS),-clang) +CONTAINER_RUNTIME := docker all: $(TARGETS) $(TARGETS_CLANG) .PHONY: all @@ -27,8 +28,8 @@ $(QEMU_ARCHES): qemu-user-static binfmt_misc $(TARGETS): mkdir -p $(HOME)/.ccache mv $(HOME)/.ccache ../../ - docker build -t criu-$@ -f Dockerfile.$@ $(DB_CC) $(DB_ENV) ../.. - docker run criu-$@ tar c -C /tmp .ccache | tar x -C $(HOME) + $(CONTAINER_RUNTIME) build -t criu-$@ -f Dockerfile.$@ $(DB_CC) $(DB_ENV) ../.. + $(CONTAINER_RUNTIME) run criu-$@ tar c -C /tmp .ccache | tar x -C $(HOME) .PHONY: $(TARGETS) # Clang builds add some Docker build env From 6f1ee001bfe2f0141e7f0ecd6e299d22e1e1bad9 Mon Sep 17 00:00:00 2001 From: Adrian Reber Date: Mon, 29 Jul 2019 13:51:21 +0000 Subject: [PATCH 220/249] scripts: remove python2 from Fedora Dockerfiles More and more python2 packages are being removed from future Fedora releases. This removes python2 packages explicitly listed in CRIU's Dockerfiles, which all are not required for the current level of testing. Signed-off-by: Adrian Reber --- scripts/build/Dockerfile.fedora.tmpl | 5 ----- 1 file changed, 5 deletions(-) diff --git a/scripts/build/Dockerfile.fedora.tmpl b/scripts/build/Dockerfile.fedora.tmpl index 965309623..94f112671 100644 --- a/scripts/build/Dockerfile.fedora.tmpl +++ b/scripts/build/Dockerfile.fedora.tmpl @@ -18,14 +18,9 @@ RUN dnf install -y \ procps-ng \ protobuf-c-devel \ protobuf-devel \ - python2-protobuf \ - python2 \ - # Starting with Fedora 28 this is python2-ipaddress python-ipaddress \ - # Starting with Fedora 28 this is python2-pyyaml python-yaml \ python3-pip \ - python2-future \ python3-PyYAML \ python3-future \ python3-protobuf \ From 2076b7ff7dba3690b2cfc31d572fce498409cbdc Mon Sep 17 00:00:00 2001 From: Pavel Tikhomirov Date: Mon, 29 Jul 2019 15:28:08 +0300 Subject: [PATCH 221/249] mount: fix inconsistent return and goto err alternation Signed-off-by: Pavel Tikhomirov --- criu/mount.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/criu/mount.c b/criu/mount.c index c03a435c5..486d01719 100644 --- a/criu/mount.c +++ b/criu/mount.c @@ -2327,7 +2327,7 @@ static int do_bind_mount(struct mount_info *mi) if (restore_shared_options(mi, private, mi->shared_id && !shared, mi->master_id && !master)) - return -1; + goto err; mi->mounted = true; exit_code = 0; From f7d9a0890edeb23065c1ce8c0507cee01e400b37 Mon Sep 17 00:00:00 2001 From: Radostin Stoyanov Date: Mon, 29 Jul 2019 21:34:51 +0100 Subject: [PATCH 222/249] scripts: Remove yaml/ipaddress Py2 fedora modules Signed-off-by: Radostin Stoyanov --- scripts/build/Dockerfile.fedora.tmpl | 2 -- 1 file changed, 2 deletions(-) diff --git a/scripts/build/Dockerfile.fedora.tmpl b/scripts/build/Dockerfile.fedora.tmpl index 94f112671..15460665a 100644 --- a/scripts/build/Dockerfile.fedora.tmpl +++ b/scripts/build/Dockerfile.fedora.tmpl @@ -18,8 +18,6 @@ RUN dnf install -y \ procps-ng \ protobuf-c-devel \ protobuf-devel \ - python-ipaddress \ - python-yaml \ python3-pip \ python3-PyYAML \ python3-future \ From b5cdf11d27f56fa97b136a9aa0c533d137e24cb8 Mon Sep 17 00:00:00 2001 From: Radostin Stoyanov Date: Mon, 29 Jul 2019 21:42:19 +0100 Subject: [PATCH 223/249] scripts: Set PYTHON=python3 in Fedora Dockerfiles Signed-off-by: Radostin Stoyanov --- scripts/build/Dockerfile.fedora.tmpl | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/build/Dockerfile.fedora.tmpl b/scripts/build/Dockerfile.fedora.tmpl index 15460665a..74348f3e6 100644 --- a/scripts/build/Dockerfile.fedora.tmpl +++ b/scripts/build/Dockerfile.fedora.tmpl @@ -37,6 +37,7 @@ RUN dnf install -y \ RUN dnf install -y --allowerasing coreutils RUN ln -sf python3 /usr/bin/python +ENV PYTHON=python3 COPY . /criu WORKDIR /criu From ced1df5ded701b2c3f01388d5f71bb326ac4d2ca Mon Sep 17 00:00:00 2001 From: Radostin Stoyanov Date: Wed, 31 Jul 2019 07:02:10 +0100 Subject: [PATCH 224/249] scripts: Install flake8 with dnf in Fedora In the Fedora tests we install python3-pip only to install flake8. This is not necessary as there is a Fedora package for flake8. Signed-off-by: Radostin Stoyanov --- scripts/build/Dockerfile.fedora.tmpl | 2 +- scripts/travis/travis-tests | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/scripts/build/Dockerfile.fedora.tmpl b/scripts/build/Dockerfile.fedora.tmpl index 74348f3e6..280ce1cdd 100644 --- a/scripts/build/Dockerfile.fedora.tmpl +++ b/scripts/build/Dockerfile.fedora.tmpl @@ -18,7 +18,7 @@ RUN dnf install -y \ procps-ng \ protobuf-c-devel \ protobuf-devel \ - python3-pip \ + python3-flake8 \ python3-PyYAML \ python3-future \ python3-protobuf \ diff --git a/scripts/travis/travis-tests b/scripts/travis/travis-tests index 348daca1f..c055860fd 100755 --- a/scripts/travis/travis-tests +++ b/scripts/travis/travis-tests @@ -163,7 +163,9 @@ ip net add test make -C test/others/shell-job -pip install flake8 +if ! [ -x "$(command -v flake8)" ]; then + pip install flake8 +fi make lint # Check that help output fits into 80 columns From 990a798e875d7bcca450b5ca1c0a410398d06a4b Mon Sep 17 00:00:00 2001 From: Radostin Stoyanov Date: Wed, 31 Jul 2019 07:08:20 +0100 Subject: [PATCH 225/249] pb2dict: Disable undefined name 'basestring' The following error is falsely reported by flake8: lib/py/images/pb2dict.py:266:24: F821 undefined name 'basestring' This error occurs because `basestring` is not available in Python 3, however the if condition on the line above ensures that this error will not occur at run time. Signed-off-by: Radostin Stoyanov --- lib/py/images/pb2dict.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/py/images/pb2dict.py b/lib/py/images/pb2dict.py index bc11b2c68..daaa7297e 100644 --- a/lib/py/images/pb2dict.py +++ b/lib/py/images/pb2dict.py @@ -263,7 +263,7 @@ def get_bytes_dec(field): def is_string(value): # Python 3 compatibility if "basestring" in __builtins__: - string_types = basestring + string_types = basestring # noqa: F821 else: string_types = (str, bytes) return isinstance(value, string_types) From 26171933b6eeeaae8f2f6ab40c93bd4b47b5f012 Mon Sep 17 00:00:00 2001 From: Radostin Stoyanov Date: Mon, 29 Jul 2019 22:07:10 +0100 Subject: [PATCH 226/249] test/other: Resolve Py3 compatibility issues When Python 2 is not installed we assume that /usr/bin/python refers to version 3 of Python and the executable /usr/bin/python2 does not exist. This commit also resolves a compatibility issue with Popen where in Py2 file descriptors will be inherited by the child process and in Py3 they will be closed by default. Signed-off-by: Radostin Stoyanov --- soccr/test/run.py | 2 +- test/check_actions.py | 2 +- test/others/ext-tty/run.py | 2 +- test/others/mounts/mounts.sh | 2 +- test/others/rpc/config_file.py | 15 ++++++++++----- test/others/rpc/errno.py | 2 +- test/others/rpc/ps_test.py | 2 +- test/others/rpc/restore-loop.py | 2 +- test/others/rpc/test.py | 2 +- test/others/rpc/version.py | 17 ++++++++++------- test/others/shell-job/run.py | 6 +++--- 11 files changed, 31 insertions(+), 23 deletions(-) diff --git a/soccr/test/run.py b/soccr/test/run.py index 446584a71..1ffe58a58 100644 --- a/soccr/test/run.py +++ b/soccr/test/run.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python2 +#!/usr/bin/env python import sys, os import hashlib diff --git a/test/check_actions.py b/test/check_actions.py index ae909e668..4973e3938 100755 --- a/test/check_actions.py +++ b/test/check_actions.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python2 +#!/usr/bin/env python import sys import os diff --git a/test/others/ext-tty/run.py b/test/others/ext-tty/run.py index b1dcb4a5a..2c0bacc84 100755 --- a/test/others/ext-tty/run.py +++ b/test/others/ext-tty/run.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python2 +#!/usr/bin/env python import subprocess import os, sys, time, signal, pty diff --git a/test/others/mounts/mounts.sh b/test/others/mounts/mounts.sh index a9a1cc80c..19116d0cf 100755 --- a/test/others/mounts/mounts.sh +++ b/test/others/mounts/mounts.sh @@ -20,7 +20,7 @@ for i in `cat /proc/self/mounts | awk '{ print $2 }'`; do umount -l $i done -python2 mounts.py +python mounts.py kill $INMNTNS_PID while :; do sleep 10 diff --git a/test/others/rpc/config_file.py b/test/others/rpc/config_file.py index 3579ac76f..e4b395e31 100755 --- a/test/others/rpc/config_file.py +++ b/test/others/rpc/config_file.py @@ -1,4 +1,4 @@ -#!/usr/bin/python2 +#!/usr/bin/python import os import socket @@ -15,10 +15,15 @@ def setup_swrk(): print('Connecting to CRIU in swrk mode.') - css = socket.socketpair(socket.AF_UNIX, socket.SOCK_SEQPACKET) - swrk = subprocess.Popen(['./criu', "swrk", "%d" % css[0].fileno()]) - css[0].close() - return swrk, css[1] + s1, s2 = socket.socketpair(socket.AF_UNIX, socket.SOCK_SEQPACKET) + + kwargs = {} + if sys.version_info.major == 3: + kwargs["pass_fds"] = [s1.fileno()] + + swrk = subprocess.Popen(['./criu', "swrk", "%d" % s1.fileno()], **kwargs) + s1.close() + return swrk, s2 def setup_config_file(content): diff --git a/test/others/rpc/errno.py b/test/others/rpc/errno.py index 49cb622de..01a6eee7b 100755 --- a/test/others/rpc/errno.py +++ b/test/others/rpc/errno.py @@ -1,4 +1,4 @@ -#!/usr/bin/python2 +#!/usr/bin/python # Test criu errno import socket, os, errno diff --git a/test/others/rpc/ps_test.py b/test/others/rpc/ps_test.py index d16efd3f6..b51357d42 100755 --- a/test/others/rpc/ps_test.py +++ b/test/others/rpc/ps_test.py @@ -1,4 +1,4 @@ -#!/usr/bin/python2 +#!/usr/bin/python import socket, os, sys, errno import rpc_pb2 as rpc diff --git a/test/others/rpc/restore-loop.py b/test/others/rpc/restore-loop.py index c81567426..84a2ce56d 100755 --- a/test/others/rpc/restore-loop.py +++ b/test/others/rpc/restore-loop.py @@ -1,4 +1,4 @@ -#!/usr/bin/python2 +#!/usr/bin/python import socket, os, sys import rpc_pb2 as rpc diff --git a/test/others/rpc/test.py b/test/others/rpc/test.py index 9a35e0e97..80f6338f4 100755 --- a/test/others/rpc/test.py +++ b/test/others/rpc/test.py @@ -1,4 +1,4 @@ -#!/usr/bin/python2 +#!/usr/bin/python import socket, os, sys import rpc_pb2 as rpc diff --git a/test/others/rpc/version.py b/test/others/rpc/version.py index f978c6c37..3b8f1b961 100755 --- a/test/others/rpc/version.py +++ b/test/others/rpc/version.py @@ -1,4 +1,4 @@ -#!/usr/bin/python2 +#!/usr/bin/python import socket import sys @@ -7,11 +7,14 @@ print('Connecting to CRIU in swrk mode to check the version:') -css = socket.socketpair(socket.AF_UNIX, socket.SOCK_SEQPACKET) -swrk = subprocess.Popen(['./criu', "swrk", "%d" % css[0].fileno()]) -css[0].close() +s1, s2 = socket.socketpair(socket.AF_UNIX, socket.SOCK_SEQPACKET) -s = css[1] +kwargs = {} +if sys.version_info.major == 3: + kwargs["pass_fds"] = [s2.fileno()] + +swrk = subprocess.Popen(['./criu', "swrk", "%d" % s2.fileno()], **kwargs) +s2.close() # Create criu msg, set it's type to dump request # and set dump options. Checkout more options in protobuf/rpc.proto @@ -19,12 +22,12 @@ req.type = rpc.VERSION # Send request -s.send(req.SerializeToString()) +s1.send(req.SerializeToString()) # Recv response resp = rpc.criu_resp() MAX_MSG_SIZE = 1024 -resp.ParseFromString(s.recv(MAX_MSG_SIZE)) +resp.ParseFromString(s1.recv(MAX_MSG_SIZE)) if resp.type != rpc.VERSION: print('RPC: Unexpected msg type') diff --git a/test/others/shell-job/run.py b/test/others/shell-job/run.py index bd5c42509..a59945d6a 100755 --- a/test/others/shell-job/run.py +++ b/test/others/shell-job/run.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python2 +#!/usr/bin/env python import os, pty, sys, subprocess import termios, fcntl, time @@ -9,11 +9,11 @@ def create_pty(): (fd1, fd2) = pty.openpty() - return (os.fdopen(fd1, "w+"), os.fdopen(fd2, "w+")) + return (os.fdopen(fd1, "wb"), os.fdopen(fd2, "wb")) if not os.access("work", os.X_OK): - os.mkdir("work", 0755) + os.mkdir("work", 0o755) open("running", "w").close() m, s = create_pty() From b65d9a4359b2c67e56ab635b39fc4565a2321891 Mon Sep 17 00:00:00 2001 From: Radostin Stoyanov Date: Wed, 31 Jul 2019 09:46:18 +0100 Subject: [PATCH 227/249] test/others: Reuse setup_swrk() Reduce code duplication by taking setup_swrk() function into a separate module that can be reused in multiple places. Signed-off-by: Radostin Stoyanov --- test/others/rpc/config_file.py | 17 ++--------------- test/others/rpc/setup_swrk.py | 16 ++++++++++++++++ test/others/rpc/version.py | 13 +++---------- 3 files changed, 21 insertions(+), 25 deletions(-) create mode 100644 test/others/rpc/setup_swrk.py diff --git a/test/others/rpc/config_file.py b/test/others/rpc/config_file.py index e4b395e31..7b07bc145 100755 --- a/test/others/rpc/config_file.py +++ b/test/others/rpc/config_file.py @@ -1,31 +1,18 @@ #!/usr/bin/python import os -import socket import sys import rpc_pb2 as rpc import argparse -import subprocess from tempfile import mkstemp import time +from setup_swrk import setup_swrk + log_file = 'config_file_test.log' does_not_exist = 'does-not.exist' -def setup_swrk(): - print('Connecting to CRIU in swrk mode.') - s1, s2 = socket.socketpair(socket.AF_UNIX, socket.SOCK_SEQPACKET) - - kwargs = {} - if sys.version_info.major == 3: - kwargs["pass_fds"] = [s1.fileno()] - - swrk = subprocess.Popen(['./criu', "swrk", "%d" % s1.fileno()], **kwargs) - s1.close() - return swrk, s2 - - def setup_config_file(content): # Creating a temporary file which will be used as configuration file. fd, path = mkstemp() diff --git a/test/others/rpc/setup_swrk.py b/test/others/rpc/setup_swrk.py new file mode 100644 index 000000000..c7f84f952 --- /dev/null +++ b/test/others/rpc/setup_swrk.py @@ -0,0 +1,16 @@ +import sys +import socket +import subprocess + +def setup_swrk(): + print('Connecting to CRIU in swrk mode.') + s1, s2 = socket.socketpair(socket.AF_UNIX, socket.SOCK_SEQPACKET) + + kwargs = {} + if sys.version_info.major == 3: + kwargs["pass_fds"] = [s1.fileno()] + + swrk = subprocess.Popen(['./criu', "swrk", "%d" % s1.fileno()], **kwargs) + s1.close() + return swrk, s2 + diff --git a/test/others/rpc/version.py b/test/others/rpc/version.py index 3b8f1b961..9d7fa745b 100755 --- a/test/others/rpc/version.py +++ b/test/others/rpc/version.py @@ -1,20 +1,13 @@ #!/usr/bin/python -import socket import sys import rpc_pb2 as rpc -import subprocess -print('Connecting to CRIU in swrk mode to check the version:') - -s1, s2 = socket.socketpair(socket.AF_UNIX, socket.SOCK_SEQPACKET) +from setup_swrk import setup_swrk -kwargs = {} -if sys.version_info.major == 3: - kwargs["pass_fds"] = [s2.fileno()] +print('Connecting to CRIU in swrk mode to check the version:') -swrk = subprocess.Popen(['./criu', "swrk", "%d" % s2.fileno()], **kwargs) -s2.close() +swrk, s1 = setup_swrk() # Create criu msg, set it's type to dump request # and set dump options. Checkout more options in protobuf/rpc.proto From 6d858563812c5118fa063a6d44677a6aff2e57f2 Mon Sep 17 00:00:00 2001 From: Dmitry Safonov Date: Thu, 25 Jul 2019 23:01:07 +0100 Subject: [PATCH 228/249] compel/log: Use enum as parameter for std_log_set_loglevel() Doesn't change uapi, but makes it a bit more friendly and documented which loglevel means what for foreign user. Signed-off-by: Dmitry Safonov Signed-off-by: Andrei Vagin --- compel/plugins/include/uapi/std/log.h | 4 +++- compel/plugins/std/log.c | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/compel/plugins/include/uapi/std/log.h b/compel/plugins/include/uapi/std/log.h index fbd1803bb..7b27b1250 100644 --- a/compel/plugins/include/uapi/std/log.h +++ b/compel/plugins/include/uapi/std/log.h @@ -1,10 +1,12 @@ #ifndef COMPEL_PLUGIN_STD_LOG_H__ #define COMPEL_PLUGIN_STD_LOG_H__ +#include "compel/loglevels.h" + #define STD_LOG_SIMPLE_CHUNK 256 extern void std_log_set_fd(int fd); -extern void std_log_set_loglevel(unsigned int level); +extern void std_log_set_loglevel(enum __compel_log_levels level); extern void std_log_set_start(struct timeval *tv); extern int std_vprint_num(char *buf, int blen, int num, char **ps); extern void std_sprintf(char output[STD_LOG_SIMPLE_CHUNK], const char *format, ...) diff --git a/compel/plugins/std/log.c b/compel/plugins/std/log.c index 403ea46f7..06b9894ae 100644 --- a/compel/plugins/std/log.c +++ b/compel/plugins/std/log.c @@ -120,7 +120,7 @@ void std_log_set_fd(int fd) logfd = fd; } -void std_log_set_loglevel(unsigned int level) +void std_log_set_loglevel(enum __compel_log_levels level) { cur_loglevel = level; } From 20da17d74ac9a0114f567e02001987b97c854cbf Mon Sep 17 00:00:00 2001 From: Dmitry Safonov Date: Thu, 25 Jul 2019 23:01:08 +0100 Subject: [PATCH 229/249] compel/std/uapi: Provide setter for gettimeofday() Provide a way to set gettimeofday() function for an infected task. CRIU's parasite & restorer are very voluble as more logs are better than lesser in terms of bug investigations. In all modern kernels there is a way to get time without entering kernel: vdso. So, add a way to reduce the cost of logging without making it less valuable. [I'm not particularly fond of std_log_set_gettimeofday() name, so if someone can come with a better naming - I'm up for a change] Signed-off-by: Dmitry Safonov Signed-off-by: Andrei Vagin --- compel/plugins/include/uapi/std/log.h | 12 ++++++++++++ compel/plugins/std/log.c | 16 +++++++++++++++- 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/compel/plugins/include/uapi/std/log.h b/compel/plugins/include/uapi/std/log.h index 7b27b1250..f21b6df0d 100644 --- a/compel/plugins/include/uapi/std/log.h +++ b/compel/plugins/include/uapi/std/log.h @@ -8,6 +8,18 @@ extern void std_log_set_fd(int fd); extern void std_log_set_loglevel(enum __compel_log_levels level); extern void std_log_set_start(struct timeval *tv); + +/* + * Provides a function to get time *in the infected task* for log timings. + * Expected use-case: address on the vdso page to get time. + * If not set or called with NULL - compel will use raw syscall, + * which requires enter in the kernel and as a result affects performance. + */ +typedef int (*gettimeofday_t)(struct timeval *tv, struct timezone *tz); +extern void std_log_set_gettimeofday(gettimeofday_t gtod); +/* std plugin helper to get time (hopefully, efficiently) */ +extern int std_gettimeofday(struct timeval *tv, struct timezone *tz); + extern int std_vprint_num(char *buf, int blen, int num, char **ps); extern void std_sprintf(char output[STD_LOG_SIMPLE_CHUNK], const char *format, ...) __attribute__ ((__format__ (__printf__, 2, 3))); diff --git a/compel/plugins/std/log.c b/compel/plugins/std/log.c index 06b9894ae..f9be432ea 100644 --- a/compel/plugins/std/log.c +++ b/compel/plugins/std/log.c @@ -16,6 +16,7 @@ struct simple_buf { static int logfd = -1; static int cur_loglevel = COMPEL_DEFAULT_LOGLEVEL; static struct timeval start; +static gettimeofday_t __std_gettimeofday; static void sbuf_log_flush(struct simple_buf *b); @@ -54,7 +55,7 @@ static void sbuf_log_init(struct simple_buf *b) if (start.tv_sec != 0) { struct timeval now; - sys_gettimeofday(&now, NULL); + std_gettimeofday(&now, NULL); timediff(&start, &now); /* Seconds */ @@ -130,6 +131,19 @@ void std_log_set_start(struct timeval *s) start = *s; } +void std_log_set_gettimeofday(gettimeofday_t gtod) +{ + __std_gettimeofday = gtod; +} + +int std_gettimeofday(struct timeval *tv, struct timezone *tz) +{ + if (__std_gettimeofday != NULL) + return __std_gettimeofday(tv, tz); + + return sys_gettimeofday(tv, tz); +} + static void print_string(const char *msg, struct simple_buf *b) { while (*msg) { From f451fa996a78024b5c05c07585a56a0e1d6463ce Mon Sep 17 00:00:00 2001 From: Dmitry Safonov Date: Thu, 25 Jul 2019 23:01:09 +0100 Subject: [PATCH 230/249] vdso/restorer: Try best to preserve vdso during restore vdso will be used in restorer for timings in logs - try to keep it during restore process. Signed-off-by: Dmitry Safonov Signed-off-by: Andrei Vagin --- criu/include/parasite-vdso.h | 2 +- criu/pie/parasite-vdso.c | 5 ++- criu/pie/restorer.c | 68 +++++++++++++++++++++++++----------- 3 files changed, 53 insertions(+), 22 deletions(-) diff --git a/criu/include/parasite-vdso.h b/criu/include/parasite-vdso.h index 3cf67bbb3..cf15d135f 100644 --- a/criu/include/parasite-vdso.h +++ b/criu/include/parasite-vdso.h @@ -84,7 +84,7 @@ static inline bool is_vdso_mark(void *addr) extern int vdso_do_park(struct vdso_maps *rt, unsigned long park_at, unsigned long park_size); extern int vdso_map_compat(unsigned long map_at); -extern int vdso_proxify(struct vdso_symtable *sym_rt, +extern int vdso_proxify(struct vdso_symtable *sym_rt, bool *added_proxy, unsigned long vdso_rt_parked_at, VmaEntry *vmas, size_t nr_vmas, bool compat_vdso, bool force_trampolines); diff --git a/criu/pie/parasite-vdso.c b/criu/pie/parasite-vdso.c index 00bc2bffa..c4197d0cf 100644 --- a/criu/pie/parasite-vdso.c +++ b/criu/pie/parasite-vdso.c @@ -242,7 +242,8 @@ static int add_vdso_proxy(VmaEntry *vma_vdso, VmaEntry *vma_vvar, return 0; } -int vdso_proxify(struct vdso_symtable *sym_rt, unsigned long vdso_rt_parked_at, +int vdso_proxify(struct vdso_symtable *sym_rt, bool *added_proxy, + unsigned long vdso_rt_parked_at, VmaEntry *vmas, size_t nr_vmas, bool compat_vdso, bool force_trampolines) { @@ -289,11 +290,13 @@ int vdso_proxify(struct vdso_symtable *sym_rt, unsigned long vdso_rt_parked_at, vma_vvar ? (unsigned long)vma_vvar->start : VVAR_BAD_ADDR, vma_vvar ? (unsigned long)vma_vvar->end : VVAR_BAD_ADDR); + *added_proxy = false; if (blobs_matches(vma_vdso, vma_vvar, &s, sym_rt) && !force_trampolines) { return remap_rt_vdso(vma_vdso, vma_vvar, sym_rt, vdso_rt_parked_at); } + *added_proxy = true; return add_vdso_proxy(vma_vdso, vma_vvar, &s, sym_rt, vdso_rt_parked_at, compat_vdso); } diff --git a/criu/pie/restorer.c b/criu/pie/restorer.c index 2a7180d6a..565ea0167 100644 --- a/criu/pie/restorer.c +++ b/criu/pie/restorer.c @@ -1215,32 +1215,37 @@ static int wait_zombies(struct task_restore_args *task_args) return 0; } -static bool vdso_unmapped(struct task_restore_args *args) +static bool can_restore_vdso(struct task_restore_args *args) { + struct vdso_maps *rt = &args->vdso_maps_rt; + bool had_vdso = false, had_vvar = false; unsigned int i; - /* Don't park rt-vdso or rt-vvar if dumpee doesn't have them */ for (i = 0; i < args->vmas_n; i++) { VmaEntry *vma = &args->vmas[i]; - if (vma_entry_is(vma, VMA_AREA_VDSO) || - vma_entry_is(vma, VMA_AREA_VVAR)) - return false; + if (vma_entry_is(vma, VMA_AREA_VDSO)) + had_vdso = true; + if (vma_entry_is(vma, VMA_AREA_VVAR)) + had_vvar = true; } - return true; -} - -static bool vdso_needs_parking(struct task_restore_args *args) -{ - /* Compatible vDSO will be mapped, not moved */ - if (args->compatible_mode) + if (had_vdso && (rt->vdso_start == VDSO_BAD_ADDR)) { + pr_err("Task had vdso, restorer doesn't\n"); return false; + } - if (args->can_map_vdso) - return false; + /* + * There is a use-case for restoring vvar alone: valgrind (see #488). + * On the other side, we expect that vvar is touched by application + * only from vdso. So, we can put a stale page and proceed restore + * if kernel doesn't provide vvar [but provides vdso, if needede. + * Just warn aloud that we don't like it. + */ + if (had_vvar && (rt->vvar_start == VVAR_BAD_ADDR)) + pr_warn("Can't restore vvar - continuing regardless\n"); - return !vdso_unmapped(args); + return true; } static inline int restore_child_subreaper(int child_subreaper) @@ -1279,6 +1284,7 @@ long __export_restore_task(struct task_restore_args *args) k_rtsigset_t to_block; pid_t my_pid = sys_getpid(); rt_sigaction_t act; + bool has_vdso_proxy; bootstrap_start = args->bootstrap_start; bootstrap_len = args->bootstrap_len; @@ -1325,7 +1331,21 @@ long __export_restore_task(struct task_restore_args *args) pr_debug("lazy-pages: uffd %d\n", args->uffd); } - if (vdso_needs_parking(args)) { + /* + * Park vdso/vvar in a safe place if architecture doesn't support + * mapping them with arch_prctl(). + * Always preserve/map rt-vdso pair if it's possible, regardless + * it's presence in original task: vdso will be used for fast + * getttimeofday() in restorer's log timings. + */ + if (!args->can_map_vdso) { + /* It's already checked in kdat, but let's check again */ + if (args->compatible_mode) { + pr_err("Compatible mode without vdso map support\n"); + goto core_restore_end; + } + if (!can_restore_vdso(args)) + goto core_restore_end; if (vdso_do_park(&args->vdso_maps_rt, args->vdso_rt_parked_at, vdso_rt_size)) goto core_restore_end; @@ -1336,9 +1356,12 @@ long __export_restore_task(struct task_restore_args *args) goto core_restore_end; /* Map vdso that wasn't parked */ - if (!vdso_unmapped(args) && args->can_map_vdso) { - if (arch_map_vdso(args->vdso_rt_parked_at, - args->compatible_mode) < 0) { + if (args->can_map_vdso) { + int err = arch_map_vdso(args->vdso_rt_parked_at, + args->compatible_mode); + + if (err < 0) { + pr_err("Failed to map vdso %d\n", err); goto core_restore_end; } } @@ -1473,11 +1496,16 @@ long __export_restore_task(struct task_restore_args *args) /* * Proxify vDSO. */ - if (vdso_proxify(&args->vdso_maps_rt.sym, args->vdso_rt_parked_at, + if (vdso_proxify(&args->vdso_maps_rt.sym, &has_vdso_proxy, + args->vdso_rt_parked_at, args->vmas, args->vmas_n, args->compatible_mode, fault_injected(FI_VDSO_TRAMPOLINES))) goto core_restore_end; + /* unmap rt-vdso with restorer blob after restore's finished */ + if (!has_vdso_proxy) + vdso_rt_size = 0; + /* * Walk though all VMAs again to drop PROT_WRITE * if it was not there. From 0918c7667647ac16d6082764f78d1c0b407f3536 Mon Sep 17 00:00:00 2001 From: Dmitry Safonov Date: Thu, 25 Jul 2019 23:01:10 +0100 Subject: [PATCH 231/249] vdso/restorer: Always track vdso/vvar positions in vdso_maps_rt For simplicity, make them always valid in restorer. rt->vdso_start will be used to calculate gettimeofday() address. Signed-off-by: Dmitry Safonov Signed-off-by: Andrei Vagin --- criu/include/parasite-vdso.h | 3 +- criu/pie/parasite-vdso.c | 81 +++++++++++++----------------------- criu/pie/restorer.c | 44 ++++++++++++++------ 3 files changed, 60 insertions(+), 68 deletions(-) diff --git a/criu/include/parasite-vdso.h b/criu/include/parasite-vdso.h index cf15d135f..872105133 100644 --- a/criu/include/parasite-vdso.h +++ b/criu/include/parasite-vdso.h @@ -84,8 +84,7 @@ static inline bool is_vdso_mark(void *addr) extern int vdso_do_park(struct vdso_maps *rt, unsigned long park_at, unsigned long park_size); extern int vdso_map_compat(unsigned long map_at); -extern int vdso_proxify(struct vdso_symtable *sym_rt, bool *added_proxy, - unsigned long vdso_rt_parked_at, +extern int vdso_proxify(struct vdso_maps *rt, bool *added_proxy, VmaEntry *vmas, size_t nr_vmas, bool compat_vdso, bool force_trampolines); extern int vdso_redirect_calls(unsigned long base_to, unsigned long base_from, diff --git a/criu/pie/parasite-vdso.c b/criu/pie/parasite-vdso.c index c4197d0cf..848883b42 100644 --- a/criu/pie/parasite-vdso.c +++ b/criu/pie/parasite-vdso.c @@ -24,19 +24,19 @@ #endif #define LOG_PREFIX "vdso: " - -static int vdso_remap(char *who, unsigned long from, unsigned long to, size_t size) +/* Updates @from on success */ +static int vdso_remap(char *who, unsigned long *from, unsigned long to, size_t size) { unsigned long addr; - pr_debug("Remap %s %lx -> %lx\n", who, from, to); + pr_debug("Remap %s %lx -> %lx\n", who, *from, to); - addr = sys_mremap(from, size, size, MREMAP_MAYMOVE | MREMAP_FIXED, to); + addr = sys_mremap(*from, size, size, MREMAP_MAYMOVE | MREMAP_FIXED, to); if (addr != to) { - pr_err("Unable to remap %lx -> %lx %lx\n", - from, to, addr); + pr_err("Unable to remap %lx -> %lx %lx\n", *from, to, addr); return -1; } + *from = addr; return 0; } @@ -57,7 +57,7 @@ int vdso_do_park(struct vdso_maps *rt, unsigned long park_at, if (rt->vvar_start == VVAR_BAD_ADDR) { BUG_ON(vdso_size < park_size); - return vdso_remap("rt-vdso", rt->vdso_start, + return vdso_remap("rt-vdso", &rt->vdso_start, rt_vdso_park, vdso_size); } @@ -68,8 +68,8 @@ int vdso_do_park(struct vdso_maps *rt, unsigned long park_at, else rt_vdso_park = park_at + vvar_size; - ret = vdso_remap("rt-vdso", rt->vdso_start, rt_vdso_park, vdso_size); - ret |= vdso_remap("rt-vvar", rt->vvar_start, rt_vvar_park, vvar_size); + ret = vdso_remap("rt-vdso", &rt->vdso_start, rt_vdso_park, vdso_size); + ret |= vdso_remap("rt-vvar", &rt->vvar_start, rt_vvar_park, vvar_size); return ret; } @@ -144,10 +144,8 @@ static bool blobs_matches(VmaEntry *vdso_img, VmaEntry *vvar_img, * to dumpee position without generating any proxy. */ static int remap_rt_vdso(VmaEntry *vma_vdso, VmaEntry *vma_vvar, - struct vdso_symtable *sym_rt, unsigned long vdso_rt_parked_at) + struct vdso_maps *rt) { - unsigned long rt_vvar_addr = vdso_rt_parked_at; - unsigned long rt_vdso_addr = vdso_rt_parked_at; void *remap_addr; int ret; @@ -164,8 +162,8 @@ static int remap_rt_vdso(VmaEntry *vma_vdso, VmaEntry *vma_vvar, } if (!vma_vvar) { - return vdso_remap("rt-vdso", rt_vdso_addr, - vma_vdso->start, sym_rt->vdso_size); + return vdso_remap("rt-vdso", &rt->vdso_start, + vma_vdso->start, rt->sym.vdso_size); } remap_addr = (void *)(uintptr_t)vma_vvar->start; @@ -174,15 +172,10 @@ static int remap_rt_vdso(VmaEntry *vma_vdso, VmaEntry *vma_vvar, return -1; } - if (vma_vdso->start < vma_vvar->start) - rt_vvar_addr = vdso_rt_parked_at + sym_rt->vdso_size; - else - rt_vdso_addr = vdso_rt_parked_at + sym_rt->vvar_size; - - ret = vdso_remap("rt-vdso", rt_vdso_addr, - vma_vdso->start, sym_rt->vdso_size); - ret |= vdso_remap("rt-vvar", rt_vvar_addr, - vma_vvar->start, sym_rt->vvar_size); + ret = vdso_remap("rt-vdso", &rt->vdso_start, + vma_vdso->start, rt->sym.vdso_size); + ret |= vdso_remap("rt-vvar", &rt->vvar_start, + vma_vvar->start, rt->sym.vvar_size); return ret; } @@ -193,28 +186,14 @@ static int remap_rt_vdso(VmaEntry *vma_vdso, VmaEntry *vma_vvar, * to operate as proxy vdso. */ static int add_vdso_proxy(VmaEntry *vma_vdso, VmaEntry *vma_vvar, - struct vdso_symtable *sym_img, struct vdso_symtable *sym_rt, - unsigned long vdso_rt_parked_at, bool compat_vdso) + struct vdso_symtable *sym_img, struct vdso_maps *rt, + bool compat_vdso) { - unsigned long rt_vvar_addr = vdso_rt_parked_at; - unsigned long rt_vdso_addr = vdso_rt_parked_at; unsigned long orig_vvar_addr = vma_vvar ? vma_vvar->start : VVAR_BAD_ADDR; pr_info("Runtime vdso mismatches dumpee, generate proxy\n"); - /* - * Don't forget to shift if vvar is before vdso. - */ - if (sym_rt->vvar_size == VVAR_BAD_SIZE) { - rt_vvar_addr = VVAR_BAD_ADDR; - } else { - if (sym_rt->vdso_before_vvar) - rt_vvar_addr += sym_rt->vdso_size; - else - rt_vdso_addr += sym_rt->vvar_size; - } - /* * Note: we assume that after first migration with inserted * rt-vdso and trampoilines on the following migrations @@ -223,8 +202,8 @@ static int add_vdso_proxy(VmaEntry *vma_vdso, VmaEntry *vma_vvar, * jumps, so we can't remove them if on the following migration * found that number of symbols in vdso has decreased. */ - if (vdso_redirect_calls(rt_vdso_addr, vma_vdso->start, - sym_rt, sym_img, compat_vdso)) { + if (vdso_redirect_calls(rt->vdso_start, vma_vdso->start, + &rt->sym, sym_img, compat_vdso)) { pr_err("Failed to proxify dumpee contents\n"); return -1; } @@ -234,16 +213,15 @@ static int add_vdso_proxy(VmaEntry *vma_vdso, VmaEntry *vma_vvar, * routine we could detect this vdso and do not dump it, since * it's auto-generated every new session if proxy required. */ - sys_mprotect((void *)rt_vdso_addr, sym_rt->vdso_size, PROT_WRITE); - vdso_put_mark((void *)rt_vdso_addr, rt_vvar_addr, - vma_vdso->start, orig_vvar_addr); - sys_mprotect((void *)rt_vdso_addr, sym_rt->vdso_size, VDSO_PROT); + sys_mprotect((void *)rt->vdso_start, rt->sym.vdso_size, PROT_WRITE); + vdso_put_mark((void *)rt->vdso_start, rt->vvar_start, + vma_vdso->start, orig_vvar_addr); + sys_mprotect((void *)rt->vdso_start, rt->sym.vdso_size, VDSO_PROT); return 0; } -int vdso_proxify(struct vdso_symtable *sym_rt, bool *added_proxy, - unsigned long vdso_rt_parked_at, +int vdso_proxify(struct vdso_maps *rt, bool *added_proxy, VmaEntry *vmas, size_t nr_vmas, bool compat_vdso, bool force_trampolines) { @@ -291,12 +269,9 @@ int vdso_proxify(struct vdso_symtable *sym_rt, bool *added_proxy, vma_vvar ? (unsigned long)vma_vvar->end : VVAR_BAD_ADDR); *added_proxy = false; - if (blobs_matches(vma_vdso, vma_vvar, &s, sym_rt) && !force_trampolines) { - return remap_rt_vdso(vma_vdso, vma_vvar, - sym_rt, vdso_rt_parked_at); - } + if (blobs_matches(vma_vdso, vma_vvar, &s, &rt->sym) && !force_trampolines) + return remap_rt_vdso(vma_vdso, vma_vvar, rt); *added_proxy = true; - return add_vdso_proxy(vma_vdso, vma_vvar, &s, sym_rt, - vdso_rt_parked_at, compat_vdso); + return add_vdso_proxy(vma_vdso, vma_vvar, &s, rt, compat_vdso); } diff --git a/criu/pie/restorer.c b/criu/pie/restorer.c index 565ea0167..d60fdbebf 100644 --- a/criu/pie/restorer.c +++ b/criu/pie/restorer.c @@ -1264,6 +1264,32 @@ static inline int restore_child_subreaper(int child_subreaper) return 0; } +static int map_vdso(struct task_restore_args *args, bool compatible) +{ + struct vdso_maps *rt = &args->vdso_maps_rt; + int err; + + err = arch_map_vdso(args->vdso_rt_parked_at, compatible); + if (err < 0) { + pr_err("Failed to map vdso %d\n", err); + return err; + } + + if (rt->sym.vdso_before_vvar) { + rt->vdso_start = args->vdso_rt_parked_at; + /* kernel may provide only vdso */ + if (rt->sym.vvar_size != VVAR_BAD_SIZE) + rt->vvar_start = rt->vdso_start + rt->sym.vdso_size; + else + rt->vvar_start = VVAR_BAD_ADDR; + } else { + rt->vvar_start = args->vdso_rt_parked_at; + rt->vdso_start = rt->vvar_start + rt->sym.vvar_size; + } + + return 0; +} + /* * The main routine to restore task via sigreturn. * This one is very special, we never return there @@ -1356,15 +1382,8 @@ long __export_restore_task(struct task_restore_args *args) goto core_restore_end; /* Map vdso that wasn't parked */ - if (args->can_map_vdso) { - int err = arch_map_vdso(args->vdso_rt_parked_at, - args->compatible_mode); - - if (err < 0) { - pr_err("Failed to map vdso %d\n", err); - goto core_restore_end; - } - } + if (args->can_map_vdso && (map_vdso(args, args->compatible_mode) < 0)) + goto core_restore_end; /* Shift private vma-s to the left */ for (i = 0; i < args->vmas_n; i++) { @@ -1496,10 +1515,9 @@ long __export_restore_task(struct task_restore_args *args) /* * Proxify vDSO. */ - if (vdso_proxify(&args->vdso_maps_rt.sym, &has_vdso_proxy, - args->vdso_rt_parked_at, - args->vmas, args->vmas_n, args->compatible_mode, - fault_injected(FI_VDSO_TRAMPOLINES))) + if (vdso_proxify(&args->vdso_maps_rt, &has_vdso_proxy, + args->vmas, args->vmas_n, args->compatible_mode, + fault_injected(FI_VDSO_TRAMPOLINES))) goto core_restore_end; /* unmap rt-vdso with restorer blob after restore's finished */ From 946699d0a877c82320d10c3657a75a63d1fc1fdb Mon Sep 17 00:00:00 2001 From: Dmitry Safonov Date: Thu, 25 Jul 2019 23:01:11 +0100 Subject: [PATCH 232/249] restorer/parasite-vdso: Don't move vvar if failed to move vdso Also slight refactor. Signed-off-by: Dmitry Safonov Signed-off-by: Andrei Vagin --- criu/pie/parasite-vdso.c | 51 +++++++++++++++++----------------------- 1 file changed, 22 insertions(+), 29 deletions(-) diff --git a/criu/pie/parasite-vdso.c b/criu/pie/parasite-vdso.c index 848883b42..be90090de 100644 --- a/criu/pie/parasite-vdso.c +++ b/criu/pie/parasite-vdso.c @@ -25,7 +25,7 @@ #define LOG_PREFIX "vdso: " /* Updates @from on success */ -static int vdso_remap(char *who, unsigned long *from, unsigned long to, size_t size) +static int remap_one(char *who, unsigned long *from, unsigned long to, size_t size) { unsigned long addr; @@ -41,37 +41,38 @@ static int vdso_remap(char *who, unsigned long *from, unsigned long to, size_t s return 0; } +static int park_at(struct vdso_maps *rt, unsigned long vdso, unsigned long vvar) +{ + int ret; + + ret = remap_one("rt-vdso", &rt->vdso_start, vdso, rt->sym.vdso_size); + + if (ret || !vvar) + return ret; + + return remap_one("rt-vvar", &rt->vvar_start, vvar, rt->sym.vvar_size); +} + /* * Park runtime vDSO in some safe place where it can be accessible * from the restorer */ -int vdso_do_park(struct vdso_maps *rt, unsigned long park_at, - unsigned long park_size) +int vdso_do_park(struct vdso_maps *rt, unsigned long addr, unsigned long space) { unsigned long vvar_size = rt->sym.vvar_size; unsigned long vdso_size = rt->sym.vdso_size; - unsigned long rt_vvar_park = park_at; - unsigned long rt_vdso_park = park_at; - int ret; - if (rt->vvar_start == VVAR_BAD_ADDR) { - BUG_ON(vdso_size < park_size); - return vdso_remap("rt-vdso", &rt->vdso_start, - rt_vdso_park, vdso_size); + BUG_ON(vdso_size < space); + return park_at(rt, addr, 0); } - BUG_ON((vdso_size + vvar_size) < park_size); + BUG_ON((vdso_size + vvar_size) < space); if (rt->sym.vdso_before_vvar) - rt_vvar_park = park_at + vdso_size; + return park_at(rt, addr, addr + vvar_size); else - rt_vdso_park = park_at + vvar_size; - - ret = vdso_remap("rt-vdso", &rt->vdso_start, rt_vdso_park, vdso_size); - ret |= vdso_remap("rt-vvar", &rt->vvar_start, rt_vvar_park, vvar_size); - - return ret; + return park_at(rt, addr + vdso_size, addr); } #ifndef CONFIG_COMPAT @@ -147,7 +148,6 @@ static int remap_rt_vdso(VmaEntry *vma_vdso, VmaEntry *vma_vvar, struct vdso_maps *rt) { void *remap_addr; - int ret; pr_info("Runtime vdso/vvar matches dumpee, remap inplace\n"); @@ -161,10 +161,8 @@ static int remap_rt_vdso(VmaEntry *vma_vdso, VmaEntry *vma_vvar, return -1; } - if (!vma_vvar) { - return vdso_remap("rt-vdso", &rt->vdso_start, - vma_vdso->start, rt->sym.vdso_size); - } + if (!vma_vvar) + return park_at(rt, vma_vdso->start, 0); remap_addr = (void *)(uintptr_t)vma_vvar->start; if (sys_munmap(remap_addr, vma_entry_len(vma_vvar))) { @@ -172,12 +170,7 @@ static int remap_rt_vdso(VmaEntry *vma_vdso, VmaEntry *vma_vvar, return -1; } - ret = vdso_remap("rt-vdso", &rt->vdso_start, - vma_vdso->start, rt->sym.vdso_size); - ret |= vdso_remap("rt-vvar", &rt->vvar_start, - vma_vvar->start, rt->sym.vvar_size); - - return ret; + return park_at(rt, vma_vdso->start, vma_vvar->start); } /* From d38e93f13b8614ac414c35f7465c4b4076d45581 Mon Sep 17 00:00:00 2001 From: Dmitry Safonov Date: Thu, 25 Jul 2019 23:01:12 +0100 Subject: [PATCH 233/249] seccomp/restorer: Disable gtod from vdso in strict mode Signed-off-by: Dmitry Safonov Signed-off-by: Andrei Vagin --- criu/pie/restorer.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/criu/pie/restorer.c b/criu/pie/restorer.c index d60fdbebf..9d49a8313 100644 --- a/criu/pie/restorer.c +++ b/criu/pie/restorer.c @@ -476,6 +476,23 @@ static int restore_seccomp(struct thread_restore_args *args) return 0; break; case SECCOMP_MODE_STRICT: + /* + * Disable gettimeofday() from vdso: it may use TSC + * which is restricted by kernel: + * + * static long seccomp_set_mode_strict(void) + * { + * [..] + * #ifdef TIF_NOTSC + * disable_TSC(); + * #endif + * [..] + * + * XXX: It may need to be fixed in kernel under + * PTRACE_O_SUSPEND_SECCOMP, but for now just get timings + * with a raw syscall instead of vdso. + */ + std_log_set_gettimeofday(NULL); ret = sys_prctl(PR_SET_SECCOMP, SECCOMP_MODE_STRICT, 0, 0, 0); if (ret < 0) { pr_err("seccomp: SECCOMP_MODE_STRICT returned %d on tid %d\n", From c16258a247596e0c1839ef1fcb946e21efd70ef6 Mon Sep 17 00:00:00 2001 From: Dmitry Safonov Date: Thu, 25 Jul 2019 23:01:13 +0100 Subject: [PATCH 234/249] vdso: Add compatible property to vdso_maps We need to differ compatible (ia32) vdso maps from x86_64. That dictates ABI on vdso code. According to that, the decision to (not) use gettimeofday() from vdso in 64-bit restorer. Signed-off-by: Dmitry Safonov Signed-off-by: Andrei Vagin --- criu/include/util-vdso.h | 1 + criu/vdso.c | 6 ++++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/criu/include/util-vdso.h b/criu/include/util-vdso.h index c74360c87..33b7411de 100644 --- a/criu/include/util-vdso.h +++ b/criu/include/util-vdso.h @@ -38,6 +38,7 @@ struct vdso_maps { unsigned long vdso_start; unsigned long vvar_start; struct vdso_symtable sym; + bool compatible; }; #define VDSO_SYMBOL_INIT { .offset = VDSO_BAD_ADDR, } diff --git a/criu/vdso.c b/criu/vdso.c index 257cbcd92..50b8b8dba 100644 --- a/criu/vdso.c +++ b/criu/vdso.c @@ -597,7 +597,8 @@ int vdso_init_restore(void) vdso_maps.sym = kdat.vdso_sym; #ifdef CONFIG_COMPAT - vdso_maps_compat.sym = kdat.vdso_sym_compat; + vdso_maps_compat.sym = kdat.vdso_sym_compat; + vdso_maps_compat.compatible = true; #endif return 0; @@ -621,7 +622,8 @@ int kerndat_vdso_fill_symtable(void) pr_err("Failed to fill compat vdso symtable\n"); return -1; } - kdat.vdso_sym_compat = vdso_maps_compat.sym; + vdso_maps_compat.compatible = true; + kdat.vdso_sym_compat = vdso_maps_compat.sym; #endif return 0; From f12a0f0a8ff3b880771997058a72ccff4bcb3834 Mon Sep 17 00:00:00 2001 From: Dmitry Safonov Date: Thu, 25 Jul 2019 23:01:14 +0100 Subject: [PATCH 235/249] restorer: Use gettimeofday() from rt-vdso for log timings Omit calling raw syscalls and use vdso for the purpose of logging. That will eliminate as much as one-syscall-per-PIE-message. Getting time without switching to kernel will speed up C/R, keeping logs as informative as they were. Fixes: #346 I haven't enabled vdso timings for ia32 applications as it needs more changes and complexity.. Maybe later. Signed-off-by: Dmitry Safonov Signed-off-by: Andrei Vagin --- criu/arch/aarch64/include/asm/vdso.h | 1 + criu/arch/arm/include/asm/vdso.h | 1 + criu/arch/ppc64/include/asm/vdso.h | 1 + criu/arch/s390/include/asm/vdso.h | 3 +- criu/arch/x86/include/asm/vdso.h | 3 +- criu/include/parasite-vdso.h | 1 + criu/pie/parasite-vdso.c | 59 +++++++++++++++++++++++++--- criu/pie/restorer.c | 2 + 8 files changed, 64 insertions(+), 7 deletions(-) diff --git a/criu/arch/aarch64/include/asm/vdso.h b/criu/arch/aarch64/include/asm/vdso.h index a7802a279..8a65e0947 100644 --- a/criu/arch/aarch64/include/asm/vdso.h +++ b/criu/arch/aarch64/include/asm/vdso.h @@ -10,6 +10,7 @@ * we should support at the moment. */ #define VDSO_SYMBOL_MAX 4 +#define VDSO_SYMBOL_GTOD 2 /* * Workaround for VDSO array symbol table's relocation. diff --git a/criu/arch/arm/include/asm/vdso.h b/criu/arch/arm/include/asm/vdso.h index cf9d500be..f57790ac2 100644 --- a/criu/arch/arm/include/asm/vdso.h +++ b/criu/arch/arm/include/asm/vdso.h @@ -10,6 +10,7 @@ * Poke from kernel file arch/arm/vdso/vdso.lds.S */ #define VDSO_SYMBOL_MAX 2 +#define VDSO_SYMBOL_GTOD 1 #define ARCH_VDSO_SYMBOLS \ "__vdso_clock_gettime", \ "__vdso_gettimeofday" diff --git a/criu/arch/ppc64/include/asm/vdso.h b/criu/arch/ppc64/include/asm/vdso.h index 9546e2460..6c92348d6 100644 --- a/criu/arch/ppc64/include/asm/vdso.h +++ b/criu/arch/ppc64/include/asm/vdso.h @@ -13,6 +13,7 @@ * inside the text page which should not be used as is from user space. */ #define VDSO_SYMBOL_MAX 10 +#define VDSO_SYMBOL_GTOD 5 #define ARCH_VDSO_SYMBOLS \ "__kernel_clock_getres", \ "__kernel_clock_gettime", \ diff --git a/criu/arch/s390/include/asm/vdso.h b/criu/arch/s390/include/asm/vdso.h index 63e7e0464..c54d848ad 100644 --- a/criu/arch/s390/include/asm/vdso.h +++ b/criu/arch/s390/include/asm/vdso.h @@ -8,7 +8,8 @@ * This is a minimal amount of symbols * we should support at the moment. */ -#define VDSO_SYMBOL_MAX 4 +#define VDSO_SYMBOL_MAX 4 +#define VDSO_SYMBOL_GTOD 0 /* * This definition is used in pie/util-vdso.c to initialize the vdso symbol diff --git a/criu/arch/x86/include/asm/vdso.h b/criu/arch/x86/include/asm/vdso.h index 046db2336..28ae2d15a 100644 --- a/criu/arch/x86/include/asm/vdso.h +++ b/criu/arch/x86/include/asm/vdso.h @@ -12,7 +12,8 @@ * This is a minimal amount of symbols * we should support at the moment. */ -#define VDSO_SYMBOL_MAX 6 +#define VDSO_SYMBOL_MAX 6 +#define VDSO_SYMBOL_GTOD 2 /* * XXX: we don't patch __kernel_vsyscall as it's too small: diff --git a/criu/include/parasite-vdso.h b/criu/include/parasite-vdso.h index 872105133..9ee32f2a7 100644 --- a/criu/include/parasite-vdso.h +++ b/criu/include/parasite-vdso.h @@ -81,6 +81,7 @@ static inline bool is_vdso_mark(void *addr) return false; } +extern void vdso_update_gtod_addr(struct vdso_maps *rt); extern int vdso_do_park(struct vdso_maps *rt, unsigned long park_at, unsigned long park_size); extern int vdso_map_compat(unsigned long map_at); diff --git a/criu/pie/parasite-vdso.c b/criu/pie/parasite-vdso.c index be90090de..38da76680 100644 --- a/criu/pie/parasite-vdso.c +++ b/criu/pie/parasite-vdso.c @@ -12,7 +12,8 @@ #include "int.h" #include "types.h" #include "page.h" -#include +#include "compel/plugins/std/syscall.h" +#include "compel/plugins/std/log.h" #include "image.h" #include "parasite-vdso.h" #include "vma.h" @@ -43,14 +44,62 @@ static int remap_one(char *who, unsigned long *from, unsigned long to, size_t si static int park_at(struct vdso_maps *rt, unsigned long vdso, unsigned long vvar) { + unsigned long vvar_size = rt->sym.vvar_size; + unsigned long vdso_size = rt->sym.vdso_size; int ret; - ret = remap_one("rt-vdso", &rt->vdso_start, vdso, rt->sym.vdso_size); - - if (ret || !vvar) + ret = remap_one("rt-vdso", &rt->vdso_start, vdso, vdso_size); + if (ret) return ret; - return remap_one("rt-vvar", &rt->vvar_start, vvar, rt->sym.vvar_size); + std_log_set_gettimeofday(NULL); /* stop using vdso for timings */ + + if (vvar) + ret = remap_one("rt-vvar", &rt->vvar_start, vvar, vvar_size); + + if (!ret) + vdso_update_gtod_addr(rt); + + return ret; +} + +void vdso_update_gtod_addr(struct vdso_maps *rt) +{ + struct vdso_symbol *gtod_sym; + void *gtod; + + if (rt->vdso_start == VDSO_BAD_ADDR) { + pr_debug("No rt-vdso - no fast gettimeofday()\n"); + return; + } + + if (VDSO_SYMBOL_GTOD < 0) { + pr_debug("Arch doesn't support gettimeofday() from vdso\n"); + return; + } + + /* + * XXX: Don't enable vdso timings for compatible applications. + * We would need to temporary map 64-bit vdso for timings in restorer + * and remap it with compatible at the end of restore. + * And vdso proxification should be done much later. + * Also, restorer should have two sets of vdso_maps in arguments. + */ + if (rt->compatible) { + pr_debug("compat mode: using syscall for gettimeofday()\n"); + return; + } + + gtod_sym = &rt->sym.symbols[VDSO_SYMBOL_GTOD]; + if (gtod_sym->offset == VDSO_BAD_ADDR) { + pr_debug("No gettimeofday() on rt-vdso\n"); + return; + } + + gtod = (void*)(rt->vdso_start + gtod_sym->offset); + pr_info("Using gettimeofday() on vdso at %p\n", gtod); + + std_log_set_gettimeofday(gtod); } /* diff --git a/criu/pie/restorer.c b/criu/pie/restorer.c index 9d49a8313..4fff2c85d 100644 --- a/criu/pie/restorer.c +++ b/criu/pie/restorer.c @@ -1402,6 +1402,8 @@ long __export_restore_task(struct task_restore_args *args) if (args->can_map_vdso && (map_vdso(args, args->compatible_mode) < 0)) goto core_restore_end; + vdso_update_gtod_addr(&args->vdso_maps_rt); + /* Shift private vma-s to the left */ for (i = 0; i < args->vmas_n; i++) { vma_entry = args->vmas + i; From a3cdf948699c629fbce45bb6864df38353b28907 Mon Sep 17 00:00:00 2001 From: Pavel Tikhomirov Date: Wed, 26 Jun 2019 11:55:19 +0300 Subject: [PATCH 236/249] inotify: cleanup auxiliary events from queue I've mentioned the problem that after c/r each inotify receives one or more unexpected events. This happens because our algorithm mixes setting up an inotify watch on the file with opening and closing it. We mix inotify creation and watched file open/close because we need to create the inotify watch on the file from another mntns (generally). And we do a trick opening the file so that it can be referenced in current mntns by /proc//fd/ path. Moreover if we have several inotifies on the same file, than queue gets even more events than just one which happens in a simple case. note: For now we don't have a way to c/r events in queue but we need to at least leave the queue clean from events generated by our own. These, still, looks harder to rewrite wd creation without this proc-fd trick than to remove unexpected events from queues. So just cleanup these events for each fdt-restorer process, for each of its inotify fds _after_ restore stage (at CR_STATE_RESTORE_SIGCHLD). These is a closest place where for an _alive_ process we know that all prepare_fds() are done by all processes. These means we need to do the cleanup in PIE code, so need to add sys_ppoll definitions for PIE and divide process in two phases: first collect and transfer fds, second do real cleanup. note: We still do prepare_fds() for zombies. But zombies have no fds in /proc/pid/fd so we will collect no in collect_fds() and therefore we have no in prepare_fds(), thus there is no need to cleanup inotifies for zombies. v2: adopt to multiple unexpected events v3: do not cleanup from fdt-receivers, done from fdt-restorer v4: do without additional fds restore stage v5: replace sys_poll with sys_ppoll and fix minor nits Signed-off-by: Pavel Tikhomirov use ppoll always and remove poll --- .../arch/arm/plugins/std/syscalls/syscall.def | 1 + .../plugins/std/syscalls/syscall-ppc64.tbl | 1 + .../plugins/std/syscalls/syscall-s390.tbl | 1 + .../x86/plugins/std/syscalls/syscall_32.tbl | 1 + .../x86/plugins/std/syscalls/syscall_64.tbl | 1 + .../plugins/include/uapi/std/syscall-types.h | 1 + criu/cr-restore.c | 38 ++++++++++ criu/include/restorer.h | 3 + criu/pie/restorer.c | 70 +++++++++++++++++++ 9 files changed, 117 insertions(+) diff --git a/compel/arch/arm/plugins/std/syscalls/syscall.def b/compel/arch/arm/plugins/std/syscalls/syscall.def index 653a7539b..721ff16dc 100644 --- a/compel/arch/arm/plugins/std/syscalls/syscall.def +++ b/compel/arch/arm/plugins/std/syscalls/syscall.def @@ -111,3 +111,4 @@ preadv_raw 69 361 (int fd, struct iovec *iov, unsigned long nr, unsigned long userfaultfd 282 388 (int flags) fallocate 47 352 (int fd, int mode, loff_t offset, loff_t len) cacheflush ! 983042 (void *start, void *end, int flags) +ppoll 73 336 (struct pollfd *fds, unsigned int nfds, const struct timespec *tmo, const sigset_t *sigmask, size_t sigsetsize) diff --git a/compel/arch/ppc64/plugins/std/syscalls/syscall-ppc64.tbl b/compel/arch/ppc64/plugins/std/syscalls/syscall-ppc64.tbl index 62e0bc1a0..3b3079040 100644 --- a/compel/arch/ppc64/plugins/std/syscalls/syscall-ppc64.tbl +++ b/compel/arch/ppc64/plugins/std/syscalls/syscall-ppc64.tbl @@ -107,3 +107,4 @@ __NR_ipc 117 sys_ipc (unsigned int call, int first, unsigned long second, un __NR_gettimeofday 78 sys_gettimeofday (struct timeval *tv, struct timezone *tz) __NR_preadv 320 sys_preadv_raw (int fd, struct iovec *iov, unsigned long nr, unsigned long pos_l, unsigned long pos_h) __NR_userfaultfd 364 sys_userfaultfd (int flags) +__NR_ppoll 281 sys_ppoll (struct pollfd *fds, unsigned int nfds, const struct timespec *tmo, const sigset_t *sigmask, size_t sigsetsize) diff --git a/compel/arch/s390/plugins/std/syscalls/syscall-s390.tbl b/compel/arch/s390/plugins/std/syscalls/syscall-s390.tbl index 3521e9150..cc13a63dd 100644 --- a/compel/arch/s390/plugins/std/syscalls/syscall-s390.tbl +++ b/compel/arch/s390/plugins/std/syscalls/syscall-s390.tbl @@ -107,3 +107,4 @@ __NR_ipc 117 sys_ipc (unsigned int call, int first, unsigned long second, un __NR_userfaultfd 355 sys_userfaultfd (int flags) __NR_preadv 328 sys_preadv_raw (int fd, struct iovec *iov, unsigned long nr, unsigned long pos_l, unsigned long pos_h) __NR_gettimeofday 78 sys_gettimeofday (struct timeval *tv, struct timezone *tz) +__NR_ppoll 302 sys_ppoll (struct pollfd *fds, unsigned int nfds, const struct timespec *tmo, const sigset_t *sigmask, size_t sigsetsize) diff --git a/compel/arch/x86/plugins/std/syscalls/syscall_32.tbl b/compel/arch/x86/plugins/std/syscalls/syscall_32.tbl index a6c55b83c..7903ab150 100644 --- a/compel/arch/x86/plugins/std/syscalls/syscall_32.tbl +++ b/compel/arch/x86/plugins/std/syscalls/syscall_32.tbl @@ -95,3 +95,4 @@ __NR_kcmp 349 sys_kcmp (pid_t pid1, pid_t pid2, int type, unsigned long idx1, __NR_seccomp 354 sys_seccomp (unsigned int op, unsigned int flags, const char *uargs) __NR_memfd_create 356 sys_memfd_create (const char *name, unsigned int flags) __NR_userfaultfd 374 sys_userfaultfd (int flags) +__NR_ppoll 309 sys_ppoll (struct pollfd *fds, unsigned int nfds, const struct timespec *tmo, const sigset_t *sigmask, size_t sigsetsize) diff --git a/compel/arch/x86/plugins/std/syscalls/syscall_64.tbl b/compel/arch/x86/plugins/std/syscalls/syscall_64.tbl index 642715147..4ac9164ea 100644 --- a/compel/arch/x86/plugins/std/syscalls/syscall_64.tbl +++ b/compel/arch/x86/plugins/std/syscalls/syscall_64.tbl @@ -106,3 +106,4 @@ __NR_setns 308 sys_setns (int fd, int nstype) __NR_kcmp 312 sys_kcmp (pid_t pid1, pid_t pid2, int type, unsigned long idx1, unsigned long idx2) __NR_memfd_create 319 sys_memfd_create (const char *name, unsigned int flags) __NR_userfaultfd 323 sys_userfaultfd (int flags) +__NR_ppoll 271 sys_ppoll (struct pollfd *fds, unsigned int nfds, const struct timespec *tmo, const sigset_t *sigmask, size_t sigsetsize) diff --git a/compel/plugins/include/uapi/std/syscall-types.h b/compel/plugins/include/uapi/std/syscall-types.h index ddb740c82..57865e741 100644 --- a/compel/plugins/include/uapi/std/syscall-types.h +++ b/compel/plugins/include/uapi/std/syscall-types.h @@ -38,6 +38,7 @@ struct siginfo; struct msghdr; struct rusage; struct iocb; +struct pollfd; typedef unsigned long aio_context_t; diff --git a/criu/cr-restore.c b/criu/cr-restore.c index d56068396..de0b2cb40 100644 --- a/criu/cr-restore.c +++ b/criu/cr-restore.c @@ -727,6 +727,40 @@ static int collect_zombie_pids(struct task_restore_args *ta) return collect_child_pids(TASK_DEAD, &ta->zombies_n); } +static int collect_inotify_fds(struct task_restore_args *ta) +{ + struct list_head *list = &rsti(current)->fds; + struct fdt *fdt = rsti(current)->fdt; + struct fdinfo_list_entry *fle; + + /* Check we are an fdt-restorer */ + if (fdt && fdt->pid != vpid(current)) + return 0; + + ta->inotify_fds = (int *)rst_mem_align_cpos(RM_PRIVATE); + + list_for_each_entry(fle, list, ps_list) { + struct file_desc *d = fle->desc; + int *inotify_fd; + + if (d->ops->type != FD_TYPES__INOTIFY) + continue; + + if (fle != file_master(d)) + continue; + + inotify_fd = rst_mem_alloc(sizeof(*inotify_fd), RM_PRIVATE); + if (!inotify_fd) + return -1; + + ta->inotify_fds_n++; + *inotify_fd = fle->fe->fd; + + pr_debug("Collect inotify fd %d to cleanup later\n", *inotify_fd); + } + return 0; +} + static int open_core(int pid, CoreEntry **pcore) { int ret; @@ -881,6 +915,9 @@ static int restore_one_alive_task(int pid, CoreEntry *core) if (collect_zombie_pids(ta) < 0) return -1; + if (collect_inotify_fds(ta) < 0) + return -1; + if (prepare_proc_misc(pid, core->tc, ta)) return -1; @@ -3417,6 +3454,7 @@ static int sigreturn_restore(pid_t pid, struct task_restore_args *task_args, uns RST_MEM_FIXUP_PPTR(task_args->helpers); RST_MEM_FIXUP_PPTR(task_args->zombies); RST_MEM_FIXUP_PPTR(task_args->vma_ios); + RST_MEM_FIXUP_PPTR(task_args->inotify_fds); task_args->compatible_mode = core_is_compat(core); /* diff --git a/criu/include/restorer.h b/criu/include/restorer.h index f980bfad3..b93807f5f 100644 --- a/criu/include/restorer.h +++ b/criu/include/restorer.h @@ -177,6 +177,9 @@ struct task_restore_args { pid_t *zombies; unsigned int zombies_n; + int *inotify_fds; /* fds to cleanup inotify events at CR_STATE_RESTORE_SIGCHLD stage */ + unsigned int inotify_fds_n; + /* * * * * * * * * * * * * * * * * * * * */ unsigned long task_size; diff --git a/criu/pie/restorer.c b/criu/pie/restorer.c index 4fff2c85d..6f8f1ae54 100644 --- a/criu/pie/restorer.c +++ b/criu/pie/restorer.c @@ -16,6 +16,7 @@ #include #include #include +#include #include "linux/userfaultfd.h" @@ -1307,6 +1308,72 @@ static int map_vdso(struct task_restore_args *args, bool compatible) return 0; } +static int fd_poll(int inotify_fd) +{ + struct pollfd pfd = {inotify_fd, POLLIN, 0}; + struct timespec tmo = {0, 0}; + + return sys_ppoll(&pfd, 1, &tmo, NULL, sizeof(sigset_t)); +} + +/* + * note: Actually kernel may want even more space for one event (see + * round_event_name_len), so using buffer of EVENT_BUFF_SIZE size may fail. + * To be on the safe side - take a bigger buffer, and these also allows to + * read more events in one syscall. + */ +#define EVENT_BUFF_SIZE ((sizeof(struct inotify_event) + PATH_MAX)) + +/* + * Read all available events from inotify queue + */ +static int cleanup_inotify_events(int inotify_fd) +{ + char buf[EVENT_BUFF_SIZE * 8]; + int ret; + + while (1) { + ret = fd_poll(inotify_fd); + if (ret < 0) { + pr_err("Failed to poll from inotify fd: %d\n", ret); + return -1; + } else if (ret == 0) { + break; + } + + ret = sys_read(inotify_fd, buf, sizeof(buf)); + if (ret < 0) { + pr_err("Failed to read inotify events\n"); + return -1; + } + } + + return 0; +} + +/* + * When we restore inotifies we can open and close files we create a watch + * for. So wee need to cleanup these auxiliary events which we've generated. + * + * note: For now we don't have a way to c/r events in queue but we need to + * at least leave the queue clean from events generated by our own. + */ +int cleanup_current_inotify_events(struct task_restore_args *task_args) +{ + int i; + + for (i = 0; i < task_args->inotify_fds_n; i++) { + int inotify_fd = task_args->inotify_fds[i]; + + pr_debug("Cleaning inotify events from %d\n", inotify_fd); + + if (cleanup_inotify_events(inotify_fd)) + return -1; + } + + return 0; +} + /* * The main routine to restore task via sigreturn. * This one is very special, we never return there @@ -1767,6 +1834,9 @@ long __export_restore_task(struct task_restore_args *args) restore_finish_stage(task_entries_local, CR_STATE_RESTORE); + if (cleanup_current_inotify_events(args)) + goto core_restore_end; + if (wait_helpers(args) < 0) goto core_restore_end; if (wait_zombies(args) < 0) From 3dbd894bbcc57872beecb3f7de435a0615629b50 Mon Sep 17 00:00:00 2001 From: Pavel Tikhomirov Date: Wed, 26 Jun 2019 15:47:39 +0300 Subject: [PATCH 237/249] zdtm/inotify: add a test that no unexpected events appear after c/r Just create two inotify watches on a testfile, and do nothing except c/r, it is expected that there is no events in queue after these. before "inotify: cleanup auxiliary events from queue": [root@snorch criu]# ./test/zdtm.py run -t zdtm/static/inotify04 === Run 1/1 ================ zdtm/static/inotify04 ======================== Run zdtm/static/inotify04 in h ======================== DEP inotify04.d CC inotify04.o LINK inotify04 Start test ./inotify04 --pidfile=inotify04.pid --outfile=inotify04.out --dirname=inotify04.test Run criu dump Run criu restore Send the 15 signal to 60 Wait for zdtm/static/inotify04(60) to die for 0.100000 =============== Test zdtm/static/inotify04 FAIL at result check ================ Test output: ================================ 18:37:14.279: 60: Event 0x10 18:37:14.280: 60: Event 0x20 18:37:14.280: 60: Event 0x10 18:37:14.280: 60: Read 3 events 18:37:14.280: 60: FAIL: inotify04.c:105: Found 3 unexpected inotify events (errno = 11 (Resource temporarily unavailable)) <<< ================================ v2: make two inotifies on the same file Signed-off-by: Pavel Tikhomirov zdtm: inotify04 add another inotify on the same file --- test/zdtm/static/Makefile | 1 + test/zdtm/static/inotify04.c | 124 +++++++++++++++++++++++++++++++++++ 2 files changed, 125 insertions(+) create mode 100644 test/zdtm/static/inotify04.c diff --git a/test/zdtm/static/Makefile b/test/zdtm/static/Makefile index 52bd00602..d8279d6f8 100644 --- a/test/zdtm/static/Makefile +++ b/test/zdtm/static/Makefile @@ -311,6 +311,7 @@ TST_DIR = \ inotify00 \ inotify01 \ inotify02 \ + inotify04 \ cgroup00 \ rmdir_open \ cgroup01 \ diff --git a/test/zdtm/static/inotify04.c b/test/zdtm/static/inotify04.c new file mode 100644 index 000000000..fb9293024 --- /dev/null +++ b/test/zdtm/static/inotify04.c @@ -0,0 +1,124 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "zdtmtst.h" + +const char *test_doc = "Check inotify does not have trash in queue after c/r"; +const char *test_author = "Pavel Tikhomirov "; + +char *dirname; +TEST_OPTION(dirname, string, "directory name", 1); + +#define TEST_FILE "inotify-testfile" + +#define BUFF_SIZE ((sizeof(struct inotify_event) + PATH_MAX)) + +static int inotify_read_events(int inotify_fd, unsigned int *n) +{ + struct inotify_event *event; + char buf[BUFF_SIZE * 8]; + int ret, off; + + *n = 0; + + while (1) { + ret = read(inotify_fd, buf, sizeof(buf)); + if (ret < 0) { + if (errno != EAGAIN) { + pr_perror("Can't read inotify queue"); + return -1; + } else { + ret = 0; + break; + } + } else if (ret == 0) + break; + + for (off = 0; off < ret; (*n)++, off += sizeof(*event) + event->len) { + event = (void *)(buf + off); + test_msg("Event %#10x\n", event->mask); + } + } + + test_msg("Read %u events\n", *n); + return ret; +} + +int main (int argc, char *argv[]) +{ + unsigned int mask = IN_ALL_EVENTS; + char test_file_path[PATH_MAX]; + int fd, ifd, ifd2, ret; + unsigned int n; + + test_init(argc, argv); + + if (mkdir(dirname, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH)) { + pr_perror("Can't create directory %s", dirname); + return 1; + } + + snprintf(test_file_path, sizeof(test_file_path), "%s/%s", dirname, TEST_FILE); + + fd = open(test_file_path, O_CREAT, 0644); + if (fd < 0) { + pr_perror("Failed to create %s", test_file_path); + return 1; + } + close(fd); + + ifd = inotify_init1(IN_NONBLOCK); + if (ifd < 0) { + pr_perror("Failed inotify_init"); + return 1; + } + + ifd2 = inotify_init1(IN_NONBLOCK); + if (ifd2 < 0) { + pr_perror("Failed inotify_init"); + return 1; + } + + if (inotify_add_watch(ifd, test_file_path, mask) < 0) { + pr_perror("Failed inotify_add_watch"); + return 1; + } + + if (inotify_add_watch(ifd2, test_file_path, mask) < 0) { + pr_perror("Failed inotify_add_watch"); + return 1; + } + + test_daemon(); + test_waitsig(); + + ret = inotify_read_events(ifd, &n); + if (ret < 0) { + fail("Failed to read inotify events"); + return 1; + } else if (n != 0) { + fail("Found %d unexpected inotify events", n); + return 1; + } + + ret = inotify_read_events(ifd, &n); + if (ret < 0) { + fail("Failed to read inotify events"); + return 1; + } else if (n != 0) { + fail("Found %d unexpected inotify events", n); + return 1; + } + + close(ifd); + close(ifd2); + unlink(test_file_path); + pass(); + + return 0; +} From 664d680668822b0acc5ed3bb7873a61c718e9558 Mon Sep 17 00:00:00 2001 From: Cyrill Gorcunov Date: Thu, 25 Jul 2019 15:51:04 +0300 Subject: [PATCH 238/249] flog: Initial merge Signed-off-by: Cyrill Gorcunov --- Makefile | 11 +++ flog/Makefile | 8 +++ flog/include/flog.h | 9 +++ flog/include/uapi/flog.h | 151 +++++++++++++++++++++++++++++++++++++++ flog/src/flog.c | 104 +++++++++++++++++++++++++++ 5 files changed, 283 insertions(+) create mode 100644 flog/Makefile create mode 100644 flog/include/flog.h create mode 100644 flog/include/uapi/flog.h create mode 100644 flog/src/flog.c diff --git a/Makefile b/Makefile index 0140330e1..169cf06e7 100644 --- a/Makefile +++ b/Makefile @@ -218,6 +218,17 @@ soccr/built-in.o: $(CONFIG_HEADER) .FORCE $(SOCCR_A): |soccr/built-in.o criu-deps += $(SOCCR_A) +# +# Fast logging library +FLOG_A := flog/libflog.a +flog/Makefile: ; +flog/%: $(CONFIG_HEADER) .FORCE + $(Q) $(MAKE) $(build)=flog $@ +flog/built-in.o: $(CONFIG_HEADER) .FORCE + $(Q) $(MAKE) $(build)=flog all +$(FLOG_A): | flog/built-in.o +criu-deps += $(FLOG_A) + # # CRIU building done in own directory # with slightly different rules so we diff --git a/flog/Makefile b/flog/Makefile new file mode 100644 index 000000000..71e398ed0 --- /dev/null +++ b/flog/Makefile @@ -0,0 +1,8 @@ +ccflags-y += -iquote flog/include +ccflags-y += -iquote include +ccflags-y += -fno-strict-aliasing +ldflags-y += -r + +lib-name := libflog.a + +lib-y += src/flog.o diff --git a/flog/include/flog.h b/flog/include/flog.h new file mode 100644 index 000000000..f00c20541 --- /dev/null +++ b/flog/include/flog.h @@ -0,0 +1,9 @@ +#ifndef __FLOG_H__ +#define __FLOG_H__ + +#include +#include + +#include "uapi/flog.h" + +#endif /* __FLOG_H__ */ diff --git a/flog/include/uapi/flog.h b/flog/include/uapi/flog.h new file mode 100644 index 000000000..9f6e11818 --- /dev/null +++ b/flog/include/uapi/flog.h @@ -0,0 +1,151 @@ +#ifndef __UAPI_FLOG_H__ +#define __UAPI_FLOG_H__ + +#include +#include +#include + +/* + * We work with up to 32 arguments in macros here. + * If more provided -- behaviour is undefined. + */ + +/* + * By Laurent Deniau at https://groups.google.com/forum/#!topic/comp.std.c/d-6Mj5Lko_s + */ +#define FLOG_PP_NARG_(...) FLOG_PP_ARG_N(__VA_ARGS__) +#define FLOG_PP_NARG(...) FLOG_PP_NARG_(1, ##__VA_ARGS__, FLOG_PP_RSEQ_N()) + +#define FLOG_PP_ARG_N( _0, _1, _2, _3, _4, \ + _5, _6, _7, _8, _9, \ + _10,_11,_12,_13,_14, \ + _15,_16,_17,_18,_19, \ + _20,_21,_22,_23,_24, \ + _25,_26,_27,_28,_29, \ + _30,_31, N, ...) N + +#define FLOG_PP_RSEQ_N() \ + 31, 30, 29, 28, 27, \ + 26, 25, 24, 23, 22, \ + 21, 20, 19, 18, 17, \ + 16, 15, 14, 13, 12, \ + 11, 10, 9, 8, 7, \ + 6, 5, 4, 3, 2, \ + 1, 0 + +#define FLOG_GENMASK_0(N, x) 0 +#define FLOG_GENMASK_1(N, op, x, ...) (op(N, 0, x)) +#define FLOG_GENMASK_2(N, op, x, ...) ((op(N, 1, x)) | FLOG_GENMASK_1(N, op, __VA_ARGS__)) +#define FLOG_GENMASK_3(N, op, x, ...) ((op(N, 2, x)) | FLOG_GENMASK_2(N, op, __VA_ARGS__)) +#define FLOG_GENMASK_4(N, op, x, ...) ((op(N, 3, x)) | FLOG_GENMASK_3(N, op, __VA_ARGS__)) +#define FLOG_GENMASK_5(N, op, x, ...) ((op(N, 4, x)) | FLOG_GENMASK_4(N, op, __VA_ARGS__)) +#define FLOG_GENMASK_6(N, op, x, ...) ((op(N, 5, x)) | FLOG_GENMASK_5(N, op, __VA_ARGS__)) +#define FLOG_GENMASK_7(N, op, x, ...) ((op(N, 6, x)) | FLOG_GENMASK_6(N, op, __VA_ARGS__)) +#define FLOG_GENMASK_8(N, op, x, ...) ((op(N, 7, x)) | FLOG_GENMASK_7(N, op, __VA_ARGS__)) +#define FLOG_GENMASK_9(N, op, x, ...) ((op(N, 8, x)) | FLOG_GENMASK_8(N, op, __VA_ARGS__)) +#define FLOG_GENMASK_10(N, op, x, ...) ((op(N, 9, x)) | FLOG_GENMASK_9(N, op, __VA_ARGS__)) +#define FLOG_GENMASK_11(N, op, x, ...) ((op(N, 10, x)) | FLOG_GENMASK_10(N, op, __VA_ARGS__)) +#define FLOG_GENMASK_12(N, op, x, ...) ((op(N, 11, x)) | FLOG_GENMASK_11(N, op, __VA_ARGS__)) +#define FLOG_GENMASK_13(N, op, x, ...) ((op(N, 12, x)) | FLOG_GENMASK_12(N, op, __VA_ARGS__)) +#define FLOG_GENMASK_14(N, op, x, ...) ((op(N, 13, x)) | FLOG_GENMASK_13(N, op, __VA_ARGS__)) +#define FLOG_GENMASK_15(N, op, x, ...) ((op(N, 14, x)) | FLOG_GENMASK_14(N, op, __VA_ARGS__)) +#define FLOG_GENMASK_16(N, op, x, ...) ((op(N, 15, x)) | FLOG_GENMASK_15(N, op, __VA_ARGS__)) +#define FLOG_GENMASK_17(N, op, x, ...) ((op(N, 16, x)) | FLOG_GENMASK_16(N, op, __VA_ARGS__)) +#define FLOG_GENMASK_18(N, op, x, ...) ((op(N, 17, x)) | FLOG_GENMASK_17(N, op, __VA_ARGS__)) +#define FLOG_GENMASK_19(N, op, x, ...) ((op(N, 18, x)) | FLOG_GENMASK_18(N, op, __VA_ARGS__)) +#define FLOG_GENMASK_20(N, op, x, ...) ((op(N, 19, x)) | FLOG_GENMASK_19(N, op, __VA_ARGS__)) +#define FLOG_GENMASK_21(N, op, x, ...) ((op(N, 20, x)) | FLOG_GENMASK_20(N, op, __VA_ARGS__)) +#define FLOG_GENMASK_22(N, op, x, ...) ((op(N, 21, x)) | FLOG_GENMASK_21(N, op, __VA_ARGS__)) +#define FLOG_GENMASK_23(N, op, x, ...) ((op(N, 22, x)) | FLOG_GENMASK_22(N, op, __VA_ARGS__)) +#define FLOG_GENMASK_24(N, op, x, ...) ((op(N, 23, x)) | FLOG_GENMASK_23(N, op, __VA_ARGS__)) +#define FLOG_GENMASK_25(N, op, x, ...) ((op(N, 24, x)) | FLOG_GENMASK_24(N, op, __VA_ARGS__)) +#define FLOG_GENMASK_26(N, op, x, ...) ((op(N, 25, x)) | FLOG_GENMASK_25(N, op, __VA_ARGS__)) +#define FLOG_GENMASK_27(N, op, x, ...) ((op(N, 26, x)) | FLOG_GENMASK_26(N, op, __VA_ARGS__)) +#define FLOG_GENMASK_28(N, op, x, ...) ((op(N, 27, x)) | FLOG_GENMASK_27(N, op, __VA_ARGS__)) +#define FLOG_GENMASK_29(N, op, x, ...) ((op(N, 28, x)) | FLOG_GENMASK_28(N, op, __VA_ARGS__)) +#define FLOG_GENMASK_30(N, op, x, ...) ((op(N, 29, x)) | FLOG_GENMASK_29(N, op, __VA_ARGS__)) +#define FLOG_GENMASK_31(N, op, x, ...) ((op(N, 30, x)) | FLOG_GENMASK_30(N, op, __VA_ARGS__)) +#define FLOG_GENMASK_32(N, op, x, ...) ((op(N, 31, x)) | FLOG_GENMASK_31(N, op, __VA_ARGS__)) + +#define FLOG_CONCAT(arg1, arg2) FLOG_CONCAT1(arg1, arg2) +#define FLOG_CONCAT1(arg1, arg2) FLOG_CONCAT2(arg1, arg2) +#define FLOG_CONCAT2(arg1, arg2) arg1##arg2 + +#define FLOG_GENMASK_(N, op, ...) FLOG_CONCAT(FLOG_GENMASK_, N)(N, op, ##__VA_ARGS__) +#define FLOG_GENMASK(op, ...) FLOG_GENMASK_(FLOG_PP_NARG(__VA_ARGS__), op, ##__VA_ARGS__) + +#define flog_genbit(ord, n, v, ...) \ + _Generic((v), \ + \ + /* Basic types */ \ + char: 0, \ + signed char: 0, \ + unsigned char: 0, \ + signed short int: 0, \ + unsigned short int: 0, \ + signed int: 0, \ + unsigned int: 0, \ + signed long: 0, \ + unsigned long: 0, \ + signed long long: 0, \ + unsigned long long: 0, \ + \ + /* Not used for a while */ \ + /* float: 12, */ \ + /* double: 13, */ \ + /* long double: 14, */ \ + \ + /* Basic poniters */ \ + char *: (1u << (ord - n - 1)), \ + signed char *: (1u << (ord - n - 1)), \ + unsigned char *: (1u << (ord - n - 1)), \ + signed short int *: 0, \ + unsigned short int *: 0, \ + signed int *: 0, \ + unsigned int *: 0, \ + signed long *: 0, \ + unsigned long *: 0, \ + signed long long *: 0, \ + unsigned long long *: 0, \ + void *: 0, \ + \ + /* Const basic pointers */ \ + const char *: (1u << (ord - n - 1)), \ + const signed char *: (1u << (ord - n - 1)), \ + const unsigned char *: (1u << (ord - n - 1)), \ + const signed short int *: 0, \ + const unsigned short int *: 0, \ + const signed int *: 0, \ + const unsigned int *: 0, \ + const signed long *: 0, \ + const unsigned long *: 0, \ + const signed long long *: 0, \ + const unsigned long long *: 0, \ + const void *: 0, \ + \ + /* Systypes and pointers */ \ + default: -1) + +#define FLOG_MAGIC 0x676f6c66 +#define FLOG_VERSION 1 + +typedef struct { + unsigned int magic; + unsigned int version; + unsigned int size; + unsigned int nargs; + unsigned int mask; + long fmt; + long args[0]; +} flog_msg_t; + +extern int flog_encode_msg(char *mbuf, size_t mbuf_size, + unsigned int nargs, unsigned int mask, + const char *format, ...); +extern int flog_decode_msg(flog_msg_t *m, int fdout); + +#define flog_encode(where, fmt, ...) \ + flog_encode_msg(where, FLOG_PP_NARG(__VA_ARGS__), \ + FLOG_GENMASK(flog_genbit, ##__VA_ARGS__), fmt, ##__VA_ARGS__) + +#endif /* __UAPI_FLOG_H__ */ diff --git a/flog/src/flog.c b/flog/src/flog.c new file mode 100644 index 000000000..4c427aa6c --- /dev/null +++ b/flog/src/flog.c @@ -0,0 +1,104 @@ +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include "common/compiler.h" + +#include "flog.h" + +int flog_decode_msg(flog_msg_t *m, int fdout) +{ + ffi_type *args[34] = { + [0] = &ffi_type_sint, + [1] = &ffi_type_pointer, + [2 ... 33] = &ffi_type_slong + }; + void *values[34]; + ffi_cif cif; + ffi_arg rc; + + size_t i, ret = 0; + char *fmt; + + values[0] = (void *)&fdout; + + if (m->magic != FLOG_MAGIC) + return -EINVAL; + if (m->version != FLOG_VERSION) + return -EINVAL; + + fmt = (void *)m + m->fmt; + values[1] = &fmt; + + for (i = 0; i < m->nargs; i++) { + values[i + 2] = (void *)&m->args[i]; + if (m->mask & (1u << i)) + m->args[i] = (long)((void *)m + m->args[i]); + } + + if (ffi_prep_cif(&cif, FFI_DEFAULT_ABI, m->nargs + 2, + &ffi_type_sint, args) == FFI_OK) { + ffi_call(&cif, FFI_FN(dprintf), &rc, values); + } else + ret = -1; + + return ret; +} + +int flog_encode_msg(char *mbuf, size_t mbuf_size, unsigned int nargs, unsigned int mask, const char *format, ...) +{ + flog_msg_t *m = (void *)mbuf; + char *str_start, *p; + va_list argptr; + size_t i; + + m->nargs = nargs; + m->mask = mask; + + str_start = (void *)m->args + sizeof(m->args[0]) * nargs; + p = memccpy(str_start, format, 0, mbuf_size - (str_start - mbuf)); + if (!p) + return -ENOMEM; + + m->fmt = str_start - mbuf; + str_start = p; + + va_start(argptr, format); + for (i = 0; i < nargs; i++) { + m->args[i] = (long)va_arg(argptr, long); + /* + * If we got a string, we should either + * reference it when in rodata, or make + * a copy (FIXME implement rodata refs). + */ + if (mask & (1u << i)) { + p = memccpy(str_start, (void *)m->args[i], 0, mbuf_size - (str_start - mbuf)); + if (!p) + return -ENOMEM; + m->args[i] = str_start - mbuf; + str_start = p; + } + } + va_end(argptr); + m->size = str_start - mbuf; + + /* + * A magic is required to know where we stop writing into a log file, + * if it was not properly closed. The file is mapped into memory, so a + * space in the file is allocated in advance and at the end it can have + * some unused tail. + */ + m->magic = FLOG_MAGIC; + m->version = FLOG_VERSION; + + m->size = round_up(m->size, 8); + + return 0; +} From 72034d43cdb00567d2d2c00b6d8c85e5eb4eb091 Mon Sep 17 00:00:00 2001 From: Cyrill Gorcunov Date: Thu, 25 Jul 2019 15:54:11 +0300 Subject: [PATCH 239/249] flog: Export paths to use inside criu Signed-off-by: Cyrill Gorcunov --- Makefile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Makefile b/Makefile index 169cf06e7..c2091a88a 100644 --- a/Makefile +++ b/Makefile @@ -127,6 +127,8 @@ ifeq ($(GMON),1) export GMON GMONLDOPT endif +CFLAGS += -iquote flog/include/ -iquote flog/include/uapi + AFLAGS += -D__ASSEMBLY__ CFLAGS += $(USERCFLAGS) $(WARNINGS) $(DEFINES) -iquote include/ HOSTCFLAGS += $(WARNINGS) $(DEFINES) -iquote include/ From 0566209f6e342826dae3c60cb63a49f767d09292 Mon Sep 17 00:00:00 2001 From: Cyrill Gorcunov Date: Fri, 9 Aug 2019 23:17:49 +0300 Subject: [PATCH 240/249] flog: Keep flog params in flog_ctx_t structure For now we will use static buffer to keep all messages. Signed-off-by: Cyrill Gorcunov --- flog/include/uapi/flog.h | 20 ++++++++++++++++---- flog/src/flog.c | 37 ++++++++++++++++++++++++++++++------- 2 files changed, 46 insertions(+), 11 deletions(-) diff --git a/flog/include/uapi/flog.h b/flog/include/uapi/flog.h index 9f6e11818..834fedd4b 100644 --- a/flog/include/uapi/flog.h +++ b/flog/include/uapi/flog.h @@ -139,13 +139,25 @@ typedef struct { long args[0]; } flog_msg_t; -extern int flog_encode_msg(char *mbuf, size_t mbuf_size, +typedef struct { + char *buf; + char *pos; + size_t size; + size_t left; +} flog_ctx_t; + +extern int flog_init(flog_ctx_t *ctx); +extern void flog_fini(flog_ctx_t *ctx); + +extern int flog_encode_msg(flog_ctx_t *ctx, unsigned int nargs, unsigned int mask, const char *format, ...); extern int flog_decode_msg(flog_msg_t *m, int fdout); -#define flog_encode(where, fmt, ...) \ - flog_encode_msg(where, FLOG_PP_NARG(__VA_ARGS__), \ - FLOG_GENMASK(flog_genbit, ##__VA_ARGS__), fmt, ##__VA_ARGS__) +#define flog_encode(ctx, fmt, ...) \ + flog_encode_msg(ctx, \ + FLOG_PP_NARG(__VA_ARGS__), \ + FLOG_GENMASK(flog_genbit, ##__VA_ARGS__), \ + fmt, ##__VA_ARGS__) #endif /* __UAPI_FLOG_H__ */ diff --git a/flog/src/flog.c b/flog/src/flog.c index 4c427aa6c..b1f2a5dd0 100644 --- a/flog/src/flog.c +++ b/flog/src/flog.c @@ -13,6 +13,25 @@ #include "flog.h" +int flog_init(flog_ctx_t *ctx) +{ + memset(ctx, 0, sizeof(*ctx)); + + /* 20M should be enough for now */ + ctx->size = ctx->left = 20 << 20; + ctx->pos = ctx->buf = malloc(ctx->size); + if (!ctx->buf) + return -ENOMEM; + + return 0; +} + +void flog_fini(flog_ctx_t *ctx) +{ + free(ctx->buf); + memset(ctx, 0, sizeof(*ctx)); +} + int flog_decode_msg(flog_msg_t *m, int fdout) { ffi_type *args[34] = { @@ -52,9 +71,9 @@ int flog_decode_msg(flog_msg_t *m, int fdout) return ret; } -int flog_encode_msg(char *mbuf, size_t mbuf_size, unsigned int nargs, unsigned int mask, const char *format, ...) +int flog_encode_msg(flog_ctx_t *ctx, unsigned int nargs, unsigned int mask, const char *format, ...) { - flog_msg_t *m = (void *)mbuf; + flog_msg_t *m = (void *)ctx->pos; char *str_start, *p; va_list argptr; size_t i; @@ -63,11 +82,11 @@ int flog_encode_msg(char *mbuf, size_t mbuf_size, unsigned int nargs, unsigned i m->mask = mask; str_start = (void *)m->args + sizeof(m->args[0]) * nargs; - p = memccpy(str_start, format, 0, mbuf_size - (str_start - mbuf)); + p = memccpy(str_start, format, 0, ctx->left - (str_start - ctx->pos)); if (!p) return -ENOMEM; - m->fmt = str_start - mbuf; + m->fmt = str_start - ctx->pos; str_start = p; va_start(argptr, format); @@ -79,15 +98,15 @@ int flog_encode_msg(char *mbuf, size_t mbuf_size, unsigned int nargs, unsigned i * a copy (FIXME implement rodata refs). */ if (mask & (1u << i)) { - p = memccpy(str_start, (void *)m->args[i], 0, mbuf_size - (str_start - mbuf)); + p = memccpy(str_start, (void *)m->args[i], 0, ctx->left - (str_start - ctx->pos)); if (!p) return -ENOMEM; - m->args[i] = str_start - mbuf; + m->args[i] = str_start - ctx->pos; str_start = p; } } va_end(argptr); - m->size = str_start - mbuf; + m->size = str_start - ctx->pos; /* * A magic is required to know where we stop writing into a log file, @@ -100,5 +119,9 @@ int flog_encode_msg(char *mbuf, size_t mbuf_size, unsigned int nargs, unsigned i m->size = round_up(m->size, 8); + /* Advance position and left bytes in context memory */ + ctx->left -= m->size; + ctx->pos += m->size; + return 0; } From 2f1941745a6051fb0897c093ca1df89a1cc82f37 Mon Sep 17 00:00:00 2001 From: Cyrill Gorcunov Date: Fri, 9 Aug 2019 23:35:41 +0300 Subject: [PATCH 241/249] flog: Initialize context on criu start Signed-off-by: Cyrill Gorcunov --- criu/Makefile | 1 + criu/Makefile.packages | 2 +- criu/crtools.c | 6 +++++- criu/include/log.h | 5 +++++ 4 files changed, 12 insertions(+), 2 deletions(-) diff --git a/criu/Makefile b/criu/Makefile index 4134e5052..ee0aa2913 100644 --- a/criu/Makefile +++ b/criu/Makefile @@ -70,6 +70,7 @@ PROGRAM-BUILTINS += images/built-in.o PROGRAM-BUILTINS += $(obj)/built-in.o PROGRAM-BUILTINS += $(ARCH-LIB) PROGRAM-BUILTINS += soccr/libsoccr.a +PROGRAM-BUILTINS += flog/libflog.a PROGRAM-BUILTINS += $(COMPEL_LIBS) $(obj)/built-in.o: pie diff --git a/criu/Makefile.packages b/criu/Makefile.packages index f380fa2f0..65111f9c0 100644 --- a/criu/Makefile.packages +++ b/criu/Makefile.packages @@ -32,7 +32,7 @@ REQ-DEB-PKG-NAMES += python-future REQ-RPM-PKG-TEST-NAMES += $(PYTHON)-pyyaml endif -export LIBS += -lprotobuf-c -ldl -lnl-3 -lsoccr -Lsoccr/ -lnet +export LIBS += -lprotobuf-c -ldl -lnl-3 -lsoccr -Lsoccr/ -lnet -lffi check-packages-failed: $(warning Can not find some of the required libraries) diff --git a/criu/crtools.c b/criu/crtools.c index 97a6d6d6c..00753bc16 100644 --- a/criu/crtools.c +++ b/criu/crtools.c @@ -48,6 +48,8 @@ #include "sysctl.h" #include "img-remote.h" +flog_ctx_t flog_ctx; + int main(int argc, char *argv[], char *envp[]) { int ret = -1; @@ -56,6 +58,9 @@ int main(int argc, char *argv[], char *envp[]) bool has_sub_command; int state = PARSING_GLOBAL_CONF; + /* FIXME: where to put flog_fini? */ + flog_init(&flog_ctx); + BUILD_BUG_ON(CTL_32 != SYSCTL_TYPE__CTL_32); BUILD_BUG_ON(__CTL_STR != SYSCTL_TYPE__CTL_STR); /* We use it for fd overlap handling in clone_service_fd() */ @@ -73,7 +78,6 @@ int main(int argc, char *argv[], char *envp[]) init_opts(); - ret = parse_options(argc, argv, &usage_error, &has_exec_cmd, state); if (ret == 1) diff --git a/criu/include/log.h b/criu/include/log.h index 15787b09f..869bc0322 100644 --- a/criu/include/log.h +++ b/criu/include/log.h @@ -9,6 +9,11 @@ #include #include +#include "flog.h" + +/* FIXME: Should be in options? */ +extern flog_ctx_t flog_ctx; + extern void vprint_on_level(unsigned int loglevel, const char *format, va_list params); From 0615f38537b8c4d79ae10c449bcf86f39c37fe96 Mon Sep 17 00:00:00 2001 From: Cyrill Gorcunov Date: Fri, 9 Aug 2019 23:51:33 +0300 Subject: [PATCH 242/249] flog: Inject macro calls into pr_x helpers Signed-off-by: Cyrill Gorcunov --- criu/include/log.h | 31 ++++++++++++++++++++++++++----- 1 file changed, 26 insertions(+), 5 deletions(-) diff --git a/criu/include/log.h b/criu/include/log.h index 869bc0322..08c573816 100644 --- a/criu/include/log.h +++ b/criu/include/log.h @@ -37,35 +37,53 @@ extern void print_on_level(unsigned int loglevel, const char *format, ...) void flush_early_log_buffer(int fd); +#ifdef CR_NOGLIBC +#undef flog_encode +#define flog_encode(ctx, fmt, ...) +#endif + #define print_once(loglevel, fmt, ...) \ do { \ static bool __printed; \ if (!__printed) { \ + flog_encode(&flog_ctx, fmt, ##__VA_ARGS__); \ print_on_level(loglevel, fmt, ##__VA_ARGS__); \ __printed = 1; \ } \ } while (0) #define pr_msg(fmt, ...) \ + do { \ + flog_encode(&flog_ctx, fmt, ##__VA_ARGS__); \ print_on_level(LOG_MSG, \ - fmt, ##__VA_ARGS__) + fmt, ##__VA_ARGS__); \ + } while (0) #define pr_info(fmt, ...) \ + do { \ + flog_encode(&flog_ctx, fmt, ##__VA_ARGS__); \ print_on_level(LOG_INFO, \ - LOG_PREFIX fmt, ##__VA_ARGS__) + LOG_PREFIX fmt, ##__VA_ARGS__); \ + } while (0) #define pr_err(fmt, ...) \ + do { \ + flog_encode(&flog_ctx, fmt, ##__VA_ARGS__); \ print_on_level(LOG_ERROR, \ "Error (%s:%d): " LOG_PREFIX fmt, \ - __FILE__, __LINE__, ##__VA_ARGS__) + __FILE__, __LINE__, ##__VA_ARGS__); \ + } while (0) #define pr_err_once(fmt, ...) \ print_once(LOG_ERROR, fmt, ##__VA_ARGS__) #define pr_warn(fmt, ...) \ + do { \ + flog_encode(&flog_ctx, fmt, ##__VA_ARGS__); \ print_on_level(LOG_WARN, \ "Warn (%s:%d): " LOG_PREFIX fmt, \ - __FILE__, __LINE__, ##__VA_ARGS__) + __FILE__, __LINE__, ##__VA_ARGS__); \ + } while (0) #define pr_warn_once(fmt, ...) \ print_once(LOG_WARN, \ @@ -73,8 +91,11 @@ void flush_early_log_buffer(int fd); __FILE__, __LINE__, ##__VA_ARGS__) #define pr_debug(fmt, ...) \ + do { \ + flog_encode(&flog_ctx, fmt, ##__VA_ARGS__); \ print_on_level(LOG_DEBUG, \ - LOG_PREFIX fmt, ##__VA_ARGS__) + LOG_PREFIX fmt, ##__VA_ARGS__); \ + } while (0) #ifndef CR_NOGLIBC From 3828463bbc33a1e9740e4bcb95dd7893ac82585e Mon Sep 17 00:00:00 2001 From: Cyrill Gorcunov Date: Sat, 10 Aug 2019 00:03:23 +0300 Subject: [PATCH 243/249] flog: Example of decoding all messages to with criu check Just an example of decoding messages with "criu check" command. The flogs messages are printed to stdout after --- FLOG --- message. Signed-off-by: Cyrill Gorcunov --- criu/crtools.c | 8 ++++++-- flog/include/uapi/flog.h | 2 ++ flog/src/flog.c | 12 ++++++++++++ 3 files changed, 20 insertions(+), 2 deletions(-) diff --git a/criu/crtools.c b/criu/crtools.c index 00753bc16..5d6381318 100644 --- a/criu/crtools.c +++ b/criu/crtools.c @@ -227,8 +227,12 @@ int main(int argc, char *argv[], char *envp[]) if (!strcmp(argv[optind], "lazy-pages")) return cr_lazy_pages(opts.daemon_mode) != 0; - if (!strcmp(argv[optind], "check")) - return cr_check() != 0; + if (!strcmp(argv[optind], "check")) { + ret = cr_check(); + printf("--- FLOG ---\n"); + flog_decode_all(&flog_ctx, STDOUT_FILENO); + return ret != 0; + } if (!strcmp(argv[optind], "page-server")) return cr_page_server(opts.daemon_mode, false, -1) != 0; diff --git a/flog/include/uapi/flog.h b/flog/include/uapi/flog.h index 834fedd4b..b0dee0229 100644 --- a/flog/include/uapi/flog.h +++ b/flog/include/uapi/flog.h @@ -149,6 +149,8 @@ typedef struct { extern int flog_init(flog_ctx_t *ctx); extern void flog_fini(flog_ctx_t *ctx); +extern void flog_decode_all(flog_ctx_t *ctx, int fdout); + extern int flog_encode_msg(flog_ctx_t *ctx, unsigned int nargs, unsigned int mask, const char *format, ...); diff --git a/flog/src/flog.c b/flog/src/flog.c index b1f2a5dd0..0cdd75b1b 100644 --- a/flog/src/flog.c +++ b/flog/src/flog.c @@ -71,6 +71,18 @@ int flog_decode_msg(flog_msg_t *m, int fdout) return ret; } +void flog_decode_all(flog_ctx_t *ctx, int fdout) +{ + flog_msg_t *m; + char *pos; + + for (pos = ctx->buf; pos < ctx->pos; ) { + m = (void *)pos; + flog_decode_msg(m ,fdout); + pos += m->size; + } +} + int flog_encode_msg(flog_ctx_t *ctx, unsigned int nargs, unsigned int mask, const char *format, ...) { flog_msg_t *m = (void *)ctx->pos; From 5b0f911e961b7cde5c463dac235a1b65c5d86edb Mon Sep 17 00:00:00 2001 From: Anastasia Markina Date: Sun, 18 Aug 2019 23:34:44 +0300 Subject: [PATCH 244/249] 1st working version with binlog New --binlog option can be used in addition to the older --log-file filename option to use binary log format --- criu/config.c | 1 + criu/cr-restore.c | 2 +- criu/cr-service.c | 2 +- criu/crtools.c | 19 ++++++-- criu/include/cr_options.h | 1 + criu/include/criu-log.h | 5 ++- criu/log.c | 23 +++++++--- flog/include/uapi/flog.h | 2 + flog/src/flog.c | 92 +++++++++++++++++++++++++++++++++++---- 9 files changed, 125 insertions(+), 22 deletions(-) diff --git a/criu/config.c b/criu/config.c index 3a54afd4b..bcb016d0d 100644 --- a/criu/config.c +++ b/criu/config.c @@ -458,6 +458,7 @@ int parse_options(int argc, char **argv, bool *usage_error, BOOL_OPT(SK_EST_PARAM, &opts.tcp_established_ok), { "close", required_argument, 0, 1043 }, BOOL_OPT("log-pid", &opts.log_file_per_pid), + BOOL_OPT("binlog", &opts.binlog), { "version", no_argument, 0, 'V' }, BOOL_OPT("evasive-devices", &opts.evasive_devices), { "pidfile", required_argument, 0, 1046 }, diff --git a/criu/cr-restore.c b/criu/cr-restore.c index de0b2cb40..bc2db42e2 100644 --- a/criu/cr-restore.c +++ b/criu/cr-restore.c @@ -1692,7 +1692,7 @@ static int restore_task_with_children(void *_arg) goto err; } - if (log_init_by_pid(vpid(current))) + if (log_init_by_pid(vpid(current),opts.binlog)) return -1; if (current->parent == NULL) { diff --git a/criu/cr-service.c b/criu/cr-service.c index 0938db02b..cb94106e4 100644 --- a/criu/cr-service.c +++ b/criu/cr-service.c @@ -384,7 +384,7 @@ static int setup_opts_from_req(int sk, CriuOpts *req) /* This is needed later to correctly set the log_level */ opts.log_level = req->log_level; log_set_loglevel(req->log_level); - if (log_init(opts.output) == -1) { + if (log_init(opts.output, opts.binlog) == -1) { pr_perror("Can't initiate log"); goto err; } diff --git a/criu/crtools.c b/criu/crtools.c index 5d6381318..7b85d213a 100644 --- a/criu/crtools.c +++ b/criu/crtools.c @@ -59,7 +59,7 @@ int main(int argc, char *argv[], char *envp[]) int state = PARSING_GLOBAL_CONF; /* FIXME: where to put flog_fini? */ - flog_init(&flog_ctx); + //flog_init(&flog_ctx); BUILD_BUG_ON(CTL_32 != SYSCTL_TYPE__CTL_32); BUILD_BUG_ON(__CTL_STR != SYSCTL_TYPE__CTL_STR); @@ -80,6 +80,13 @@ int main(int argc, char *argv[], char *envp[]) ret = parse_options(argc, argv, &usage_error, &has_exec_cmd, state); + //if (flog_init(&flog_ctx)) + // return 1; + + + + + if (ret == 1) return 1; if (ret == 2) @@ -170,9 +177,9 @@ int main(int argc, char *argv[], char *envp[]) return 1; } - if (log_init(opts.output)) + if (log_init(opts.output, opts.binlog)) return 1; - + if (kerndat_init()) return 1; @@ -281,6 +288,9 @@ int main(int argc, char *argv[], char *envp[]) return -1; } + printf("--- FLOG ---\n"); + flog_decode_all(&flog_ctx, STDOUT_FILENO); + printf("--- FLOG ---\n"); pr_msg("Error: unknown command: %s\n", argv[optind]); usage: pr_msg("\n" @@ -311,6 +321,8 @@ int main(int argc, char *argv[], char *envp[]) if (usage_error) { pr_msg("\nTry -h|--help for more info\n"); + + flog_fini(&flog_ctx); return 1; } @@ -433,6 +445,7 @@ int main(int argc, char *argv[], char *envp[]) "\n" "* Logging:\n" " -o|--log-file FILE log file name\n" +" --binlog use faster binary log instead of slower text log\n" " --log-pid enable per-process logging to separate FILE.pid files\n" " -v[v...]|--verbosity increase verbosity (can use multiple v)\n" " -vNUM|--verbosity=NUM set verbosity to NUM (higher level means more output):\n" diff --git a/criu/include/cr_options.h b/criu/include/cr_options.h index c519c740d..43ce3fbfa 100644 --- a/criu/include/cr_options.h +++ b/criu/include/cr_options.h @@ -81,6 +81,7 @@ struct cr_options { int evasive_devices; int link_remap_ok; int log_file_per_pid; + int binlog; bool swrk_restore; char *output; char *root; diff --git a/criu/include/criu-log.h b/criu/include/criu-log.h index 21ef54307..85cd7235c 100644 --- a/criu/include/criu-log.h +++ b/criu/include/criu-log.h @@ -24,9 +24,10 @@ struct timeval; -extern int log_init(const char *output); +extern int log_init(const char *output, bool binlog); + extern void log_fini(void); -extern int log_init_by_pid(pid_t pid); +extern int log_init_by_pid(pid_t pid, bool binlog); extern void log_closedir(void); extern int log_keep_err(void); extern char *log_first_err(void); diff --git a/criu/log.c b/criu/log.c index 8bdf83534..38085ddf5 100644 --- a/criu/log.c +++ b/criu/log.c @@ -204,9 +204,10 @@ void flush_early_log_buffer(int fd) early_log_buf_off = 0; } -int log_init(const char *output) +int log_init(const char *output, bool binlog) { int new_logfd, fd; + int log_file_open_mode, log_file_perm; gettimeofday(&start, NULL); reset_buf_off(); @@ -218,7 +219,9 @@ int log_init(const char *output) return -1; } } else if (output) { - new_logfd = open(output, O_CREAT|O_TRUNC|O_WRONLY|O_APPEND, 0600); + log_file_open_mode = opts.binlog?(O_RDWR | O_CREAT | O_TRUNC):(O_CREAT|O_TRUNC|O_WRONLY|O_APPEND); + log_file_perm = opts.binlog?0644:0600; + new_logfd = open(output, log_file_open_mode, log_file_perm); if (new_logfd < 0) { pr_perror("Can't create log file %s", output); return -1; @@ -234,14 +237,20 @@ int log_init(const char *output) fd = install_service_fd(LOG_FD_OFF, new_logfd); if (fd < 0) goto err; - + if (opts.binlog) { + //flog_init(&flog_ctx); + //strncpy(opts.output, "-", 2); + flog_map_buf(fd, &flog_ctx); + } + init_done = 1; /* * Once logging is setup this write out all early log messages. * Only those messages which have to correct log level are printed. */ - flush_early_log_buffer(fd); + + //flush_early_log_buffer(fd); print_versions(); @@ -252,7 +261,7 @@ int log_init(const char *output) return -1; } -int log_init_by_pid(pid_t pid) +int log_init_by_pid(pid_t pid, bool binlog) { char path[PATH_MAX]; @@ -272,7 +281,7 @@ int log_init_by_pid(pid_t pid) snprintf(path, PATH_MAX, "%s.%d", opts.output, pid); - return log_init(path); + return log_init(path, binlog); } void log_fini(void) @@ -394,7 +403,7 @@ void vprint_on_level(unsigned int loglevel, const char *format, va_list params) void print_on_level(unsigned int loglevel, const char *format, ...) { va_list params; - + if (opts.binlog) return; va_start(params, format); vprint_on_level(loglevel, format, params); va_end(params); diff --git a/flog/include/uapi/flog.h b/flog/include/uapi/flog.h index b0dee0229..b9d7d1114 100644 --- a/flog/include/uapi/flog.h +++ b/flog/include/uapi/flog.h @@ -156,6 +156,8 @@ extern int flog_encode_msg(flog_ctx_t *ctx, const char *format, ...); extern int flog_decode_msg(flog_msg_t *m, int fdout); +extern int flog_map_buf(int fdout, flog_ctx_t *flog_ctx); + #define flog_encode(ctx, fmt, ...) \ flog_encode_msg(ctx, \ FLOG_PP_NARG(__VA_ARGS__), \ diff --git a/flog/src/flog.c b/flog/src/flog.c index 0cdd75b1b..6853ccc73 100644 --- a/flog/src/flog.c +++ b/flog/src/flog.c @@ -13,22 +13,92 @@ #include "flog.h" +#define BUF_SIZE (1<<20) + +static char _mbuf[BUF_SIZE]; +static char *mbuf = _mbuf; +static char *fbuf; +static uint64_t fsize; +static uint64_t mbuf_size = sizeof(_mbuf); +static int binlog_fd; + +int flog_map_buf(int fdout, flog_ctx_t *flog_ctx) +{ + uint64_t off = 0; + void *addr; + + /* + * Two buffers are mmaped into memory. A new one is mapped when a first + * one is completly filled. + */ + if (fbuf && (mbuf - fbuf < BUF_SIZE)) + return 0; + + if (fbuf) { + if (munmap(fbuf, BUF_SIZE * 2)) { + fprintf(stderr, "Unable to unmap a buffer: %m"); + return 1; + } + off = mbuf - fbuf - BUF_SIZE; + fbuf = NULL; + } + + if (fsize == 0) + fsize += BUF_SIZE; + fsize += BUF_SIZE; + + if (ftruncate(fdout, fsize)) { + fprintf(stderr, "Unable to truncate a file: %m"); + return -1; + } + if (!fbuf) + addr = mmap(NULL, BUF_SIZE * 2, PROT_WRITE | PROT_READ, + MAP_FILE | MAP_SHARED, fdout, fsize - 2 * BUF_SIZE); + else + addr = mremap(fbuf + BUF_SIZE, BUF_SIZE, + BUF_SIZE * 2, MREMAP_FIXED, fbuf); + if (addr == MAP_FAILED) { + fprintf(stderr, "Unable to map a buffer: %m"); + return -1; + } + + fbuf = addr; + mbuf = fbuf + off; + mbuf_size = 2 * BUF_SIZE; + //flog_ctx->buf=flog_ctx->pos=mbuf; + printf("flog_map_buf: mapping buffer to file, address: %ld\n", (long)mbuf); + binlog_fd=fdout; + flog_init(flog_ctx); + return 0; +} + int flog_init(flog_ctx_t *ctx) { memset(ctx, 0, sizeof(*ctx)); /* 20M should be enough for now */ - ctx->size = ctx->left = 20 << 20; - ctx->pos = ctx->buf = malloc(ctx->size); + ctx->size = ctx->left = mbuf_size; //20 << 20; + ctx->pos = ctx->buf = mbuf;//malloc(ctx->size); if (!ctx->buf) return -ENOMEM; - + printf("flog_init: ctx pos address: %ld\n", (long) ctx->pos); return 0; } void flog_fini(flog_ctx_t *ctx) { - free(ctx->buf); + if (mbuf == _mbuf) + return; + printf("closing binary log...\n"); + munmap(ctx->buf, BUF_SIZE * 2); + //printf("closed\n"); + ctx->size=(size_t) (ctx->pos - ctx->buf); + //printf("flog_fini: size should be %ld\n",ctx->size); + if (ftruncate(binlog_fd, ctx->size)) { + fprintf(stderr, "Unable to truncate a file: %m"); + } + + //free(ctx->buf); memset(ctx, 0, sizeof(*ctx)); } @@ -48,10 +118,15 @@ int flog_decode_msg(flog_msg_t *m, int fdout) values[0] = (void *)&fdout; - if (m->magic != FLOG_MAGIC) + if (m->magic != FLOG_MAGIC) { + //printf("bad magic %d, not %d\n", m->magic, FLOG_MAGIC); + //printf("fmt is %ld, format string is %s\n", m->fmt, "(char *)m + m->fmt"); return -EINVAL; - if (m->version != FLOG_VERSION) + } + if (m->version != FLOG_VERSION) { + //printf("bad version\n"); return -EINVAL; + } fmt = (void *)m + m->fmt; values[1] = &fmt; @@ -78,6 +153,7 @@ void flog_decode_all(flog_ctx_t *ctx, int fdout) for (pos = ctx->buf; pos < ctx->pos; ) { m = (void *)pos; + //printf("flog_decode_all: ctx pos address: %ld, pos address: %ld\n", (long) ctx->pos,(long) pos); flog_decode_msg(m ,fdout); pos += m->size; } @@ -100,7 +176,7 @@ int flog_encode_msg(flog_ctx_t *ctx, unsigned int nargs, unsigned int mask, cons m->fmt = str_start - ctx->pos; str_start = p; - + printf("arguments: %d, mask: %d, fmt: %s\n", nargs, mask, format); va_start(argptr, format); for (i = 0; i < nargs; i++) { m->args[i] = (long)va_arg(argptr, long); @@ -134,6 +210,6 @@ int flog_encode_msg(flog_ctx_t *ctx, unsigned int nargs, unsigned int mask, cons /* Advance position and left bytes in context memory */ ctx->left -= m->size; ctx->pos += m->size; - + //printf("flog_encode_msg: ctx pos address: %ld\n", (long) ctx->pos); return 0; } From d00e6461945ccdc7c6b50d96fa2e5e522d9b79be Mon Sep 17 00:00:00 2001 From: Anastasia Markina Date: Tue, 20 Aug 2019 06:52:24 +0300 Subject: [PATCH 245/249] print-log option to print binary log and exit new --print-log option allows to use criu as a reader tool for binlog files. Filename of the log file is to be passed as usual via --log-file option. --- criu/config.c | 1 + criu/cr-restore.c | 2 +- criu/cr-service.c | 2 +- criu/crtools.c | 118 ++++++++++++++++++++++---------------- criu/include/cr_options.h | 1 + criu/include/criu-log.h | 4 +- criu/log.c | 39 ++++++++++--- flog/include/uapi/flog.h | 1 + flog/src/flog.c | 100 +++++++++++++++++++++----------- 9 files changed, 173 insertions(+), 95 deletions(-) diff --git a/criu/config.c b/criu/config.c index bcb016d0d..7d79c6389 100644 --- a/criu/config.c +++ b/criu/config.c @@ -459,6 +459,7 @@ int parse_options(int argc, char **argv, bool *usage_error, { "close", required_argument, 0, 1043 }, BOOL_OPT("log-pid", &opts.log_file_per_pid), BOOL_OPT("binlog", &opts.binlog), + BOOL_OPT("print-log", &opts.printlog), { "version", no_argument, 0, 'V' }, BOOL_OPT("evasive-devices", &opts.evasive_devices), { "pidfile", required_argument, 0, 1046 }, diff --git a/criu/cr-restore.c b/criu/cr-restore.c index bc2db42e2..de0b2cb40 100644 --- a/criu/cr-restore.c +++ b/criu/cr-restore.c @@ -1692,7 +1692,7 @@ static int restore_task_with_children(void *_arg) goto err; } - if (log_init_by_pid(vpid(current),opts.binlog)) + if (log_init_by_pid(vpid(current))) return -1; if (current->parent == NULL) { diff --git a/criu/cr-service.c b/criu/cr-service.c index cb94106e4..0938db02b 100644 --- a/criu/cr-service.c +++ b/criu/cr-service.c @@ -384,7 +384,7 @@ static int setup_opts_from_req(int sk, CriuOpts *req) /* This is needed later to correctly set the log_level */ opts.log_level = req->log_level; log_set_loglevel(req->log_level); - if (log_init(opts.output, opts.binlog) == -1) { + if (log_init(opts.output) == -1) { pr_perror("Can't initiate log"); goto err; } diff --git a/criu/crtools.c b/criu/crtools.c index 7b85d213a..6bda6945a 100644 --- a/criu/crtools.c +++ b/criu/crtools.c @@ -50,6 +50,12 @@ flog_ctx_t flog_ctx; + +int criu_quit(int ret_val) { + log_fini(); + exit(ret_val); +} + int main(int argc, char *argv[], char *envp[]) { int ret = -1; @@ -57,6 +63,12 @@ int main(int argc, char *argv[], char *envp[]) bool has_exec_cmd = false; bool has_sub_command; int state = PARSING_GLOBAL_CONF; + + flog_ctx.readonly=1; + /* It is not known yet if we will have binlog, but let's not try to + * write into it for safety reasons + */ + /* FIXME: where to put flog_fini? */ //flog_init(&flog_ctx); @@ -72,26 +84,34 @@ int main(int argc, char *argv[], char *envp[]) cr_pb_init(); setproctitle_init(argc, argv, envp); - - if (argc < 2) + + if (argc < 2) { goto usage; - + } + init_opts(); ret = parse_options(argc, argv, &usage_error, &has_exec_cmd, state); - //if (flog_init(&flog_ctx)) - // return 1; - - - + if (opts.printlog) { + + if (log_init(opts.output)) + return 1; + /* print and exit */ + printf("--- printing log from %s file ---\n", opts.output); + struct stat st; + stat(opts.output, &st); + if(st.st_size) + flog_decode_all(&flog_ctx, STDOUT_FILENO); + printf("--- end of the log ---\n"); + criu_quit(0); + } - if (ret == 1) - return 1; + return 1; if (ret == 2) - goto usage; - + goto usage; + log_set_loglevel(opts.log_level); if (!strcmp(argv[1], "swrk")) { @@ -109,6 +129,7 @@ int main(int argc, char *argv[], char *envp[]) if (check_options()) { flush_early_log_buffer(STDERR_FILENO); + flog_fini(&flog_ctx); return 1; } @@ -117,7 +138,7 @@ int main(int argc, char *argv[], char *envp[]) if (opts.work_dir == NULL) SET_CHAR_OPTS(work_dir, opts.imgs_dir); - + if (optind >= argc) { pr_msg("Error: command is required\n"); goto usage; @@ -142,8 +163,8 @@ int main(int argc, char *argv[], char *envp[]) } opts.exec_cmd = xmalloc((argc - optind) * sizeof(char *)); - if (!opts.exec_cmd) - return 1; + if (!opts.exec_cmd) + criu_quit(1); memcpy(opts.exec_cmd, &argv[optind + 1], (argc - optind - 1) * sizeof(char *)); opts.exec_cmd[argc - optind - 1] = NULL; } else { @@ -159,7 +180,7 @@ int main(int argc, char *argv[], char *envp[]) if (strcmp(argv[optind], "service")) { ret = open_image_dir(opts.imgs_dir); if (ret < 0) - return 1; + criu_quit(1); } /* @@ -174,14 +195,14 @@ int main(int argc, char *argv[], char *envp[]) if (chdir(opts.work_dir)) { pr_perror("Can't change directory to %s", opts.work_dir); - return 1; + criu_quit(1); } - if (log_init(opts.output, opts.binlog)) - return 1; - + if (log_init(opts.output)) + return 1; + if (kerndat_init()) - return 1; + criu_quit(1); if (opts.deprecated_ok) pr_debug("DEPRECATED ON\n"); @@ -189,7 +210,7 @@ int main(int argc, char *argv[], char *envp[]) if (!list_empty(&opts.inherit_fds)) { if (strcmp(argv[optind], "restore")) { pr_err("--inherit-fd is restore-only option\n"); - return 1; + criu_quit(1); } /* now that log file is set up, print inherit fd list */ inherit_fd_log(); @@ -201,7 +222,7 @@ int main(int argc, char *argv[], char *envp[]) if (!strcmp(argv[optind], "dump")) { if (!opts.tree_id) goto opt_pid_missing; - return cr_dump_tasks(opts.tree_id); + criu_quit(cr_dump_tasks(opts.tree_id)); } if (!strcmp(argv[optind], "pre-dump")) { @@ -210,10 +231,10 @@ int main(int argc, char *argv[], char *envp[]) if (opts.lazy_pages) { pr_err("Cannot pre-dump with --lazy-pages\n"); - return 1; + criu_quit(1); } - return cr_pre_dump_tasks(opts.tree_id) != 0; + criu_quit(cr_pre_dump_tasks(opts.tree_id) != 0); } if (!strcmp(argv[optind], "restore")) { @@ -228,43 +249,41 @@ int main(int argc, char *argv[], char *envp[]) ret = 1; } - return ret != 0; + criu_quit(ret != 0); } if (!strcmp(argv[optind], "lazy-pages")) - return cr_lazy_pages(opts.daemon_mode) != 0; + criu_quit(cr_lazy_pages(opts.daemon_mode) != 0); if (!strcmp(argv[optind], "check")) { - ret = cr_check(); - printf("--- FLOG ---\n"); - flog_decode_all(&flog_ctx, STDOUT_FILENO); - return ret != 0; + ret = cr_check(); + criu_quit(ret != 0); } if (!strcmp(argv[optind], "page-server")) - return cr_page_server(opts.daemon_mode, false, -1) != 0; + criu_quit(cr_page_server(opts.daemon_mode, false, -1) != 0); if (!strcmp(argv[optind], "image-cache")) { if (!opts.port) goto opt_port_missing; - return image_cache(opts.daemon_mode, DEFAULT_CACHE_SOCKET); + criu_quit(image_cache(opts.daemon_mode, DEFAULT_CACHE_SOCKET)); } if (!strcmp(argv[optind], "image-proxy")) { if (!opts.addr) { pr_msg("Error: address not specified\n"); - return 1; + criu_quit(1); } if (!opts.port) goto opt_port_missing; - return image_proxy(opts.daemon_mode, DEFAULT_PROXY_SOCKET); + criu_quit(image_proxy(opts.daemon_mode, DEFAULT_PROXY_SOCKET)); } if (!strcmp(argv[optind], "service")) - return cr_service(opts.daemon_mode); + criu_quit(cr_service(opts.daemon_mode)); if (!strcmp(argv[optind], "dedup")) - return cr_dedup() != 0; + criu_quit(cr_dedup() != 0); if (!strcmp(argv[optind], "cpuinfo")) { if (!argv[optind + 1]) { @@ -274,25 +293,23 @@ int main(int argc, char *argv[], char *envp[]) if (!strcmp(argv[optind + 1], "dump")) return cpuinfo_dump(); else if (!strcmp(argv[optind + 1], "check")) - return cpuinfo_check(); + criu_quit(cpuinfo_check()); } if (!strcmp(argv[optind], "exec")) { pr_msg("The \"exec\" action is deprecated by the Compel library.\n"); - return -1; + criu_quit(-1); } if (!strcmp(argv[optind], "show")) { pr_msg("The \"show\" action is deprecated by the CRIT utility.\n"); pr_msg("To view an image use the \"crit decode -i $name --pretty\" command.\n"); - return -1; + criu_quit(-1); } - - printf("--- FLOG ---\n"); - flog_decode_all(&flog_ctx, STDOUT_FILENO); - printf("--- FLOG ---\n"); + pr_msg("Error: unknown command: %s\n", argv[optind]); usage: + printf("usage\n"); pr_msg("\n" "Usage:\n" " criu dump|pre-dump -t PID []\n" @@ -320,10 +337,8 @@ int main(int argc, char *argv[], char *envp[]) ); if (usage_error) { - pr_msg("\nTry -h|--help for more info\n"); - - flog_fini(&flog_ctx); - return 1; + pr_msg("\nTry -h|--help for more info\n"); + criu_quit(1); } pr_msg("\n" @@ -447,6 +462,7 @@ int main(int argc, char *argv[], char *envp[]) " -o|--log-file FILE log file name\n" " --binlog use faster binary log instead of slower text log\n" " --log-pid enable per-process logging to separate FILE.pid files\n" +" --print-log print log from the binlog file and exit\n" " -v[v...]|--verbosity increase verbosity (can use multiple v)\n" " -vNUM|--verbosity=NUM set verbosity to NUM (higher level means more output):\n" " -v1 - only errors and messages\n" @@ -487,13 +503,13 @@ int main(int argc, char *argv[], char *envp[]) " -V|--version show version\n" ); - return 0; + criu_quit(0); opt_port_missing: pr_msg("Error: port not specified\n"); - return 1; + criu_quit(1); opt_pid_missing: pr_msg("Error: pid not specified\n"); - return 1; + criu_quit(1); } diff --git a/criu/include/cr_options.h b/criu/include/cr_options.h index 43ce3fbfa..a79bdebc4 100644 --- a/criu/include/cr_options.h +++ b/criu/include/cr_options.h @@ -82,6 +82,7 @@ struct cr_options { int link_remap_ok; int log_file_per_pid; int binlog; + int printlog; bool swrk_restore; char *output; char *root; diff --git a/criu/include/criu-log.h b/criu/include/criu-log.h index 85cd7235c..57792a444 100644 --- a/criu/include/criu-log.h +++ b/criu/include/criu-log.h @@ -24,10 +24,10 @@ struct timeval; -extern int log_init(const char *output, bool binlog); +extern int log_init(const char *output); extern void log_fini(void); -extern int log_init_by_pid(pid_t pid, bool binlog); +extern int log_init_by_pid(pid_t pid); extern void log_closedir(void); extern int log_keep_err(void); extern char *log_first_err(void); diff --git a/criu/log.c b/criu/log.c index 38085ddf5..096c683aa 100644 --- a/criu/log.c +++ b/criu/log.c @@ -10,6 +10,7 @@ #include #include #include +#include #include @@ -204,14 +205,14 @@ void flush_early_log_buffer(int fd) early_log_buf_off = 0; } -int log_init(const char *output, bool binlog) +int log_init(const char *output) { int new_logfd, fd; int log_file_open_mode, log_file_perm; gettimeofday(&start, NULL); reset_buf_off(); - + memset(&flog_ctx, 0, sizeof(flog_ctx)); if (output && !strncmp(output, "-", 2)) { new_logfd = dup(STDOUT_FILENO); if (new_logfd < 0) { @@ -219,7 +220,23 @@ int log_init(const char *output, bool binlog) return -1; } } else if (output) { - log_file_open_mode = opts.binlog?(O_RDWR | O_CREAT | O_TRUNC):(O_CREAT|O_TRUNC|O_WRONLY|O_APPEND); + if (opts.printlog) { + //printf("printlog!\n"); + /* read old binlog */ + log_file_open_mode = O_RDONLY; + flog_ctx.readonly=1; /* don't try to write into it */ + struct stat st; + stat(opts.output, &st); + flog_ctx.size=st.st_size; + //printf("file %s is %ld bytes\n",opts.output, flog_ctx.size); + } + else if (opts.binlog) { + log_file_open_mode = O_RDWR | O_CREAT | O_TRUNC; + } + else { + flog_ctx.readonly=1; /* no binlog, don't try to write into it */ + log_file_open_mode = O_CREAT|O_TRUNC|O_WRONLY|O_APPEND; + } log_file_perm = opts.binlog?0644:0600; new_logfd = open(output, log_file_open_mode, log_file_perm); if (new_logfd < 0) { @@ -237,12 +254,15 @@ int log_init(const char *output, bool binlog) fd = install_service_fd(LOG_FD_OFF, new_logfd); if (fd < 0) goto err; - if (opts.binlog) { + + if (opts.binlog||opts.printlog) { + //flog_init(&flog_ctx); //strncpy(opts.output, "-", 2); flog_map_buf(fd, &flog_ctx); } + init_done = 1; /* @@ -261,7 +281,7 @@ int log_init(const char *output, bool binlog) return -1; } -int log_init_by_pid(pid_t pid, bool binlog) +int log_init_by_pid(pid_t pid) { char path[PATH_MAX]; @@ -281,12 +301,15 @@ int log_init_by_pid(pid_t pid, bool binlog) snprintf(path, PATH_MAX, "%s.%d", opts.output, pid); - return log_init(path, binlog); + return log_init(path); } void log_fini(void) { - close_service_fd(LOG_FD_OFF); + flog_fini(&flog_ctx); + + //close_service_fd(LOG_FD_OFF); + } static void soccr_print_on_level(unsigned int loglevel, const char *format, ...) @@ -401,7 +424,7 @@ void vprint_on_level(unsigned int loglevel, const char *format, va_list params) } void print_on_level(unsigned int loglevel, const char *format, ...) -{ +{ va_list params; if (opts.binlog) return; va_start(params, format); diff --git a/flog/include/uapi/flog.h b/flog/include/uapi/flog.h index b9d7d1114..8ab3566ca 100644 --- a/flog/include/uapi/flog.h +++ b/flog/include/uapi/flog.h @@ -144,6 +144,7 @@ typedef struct { char *pos; size_t size; size_t left; + int readonly; } flog_ctx_t; extern int flog_init(flog_ctx_t *ctx); diff --git a/flog/src/flog.c b/flog/src/flog.c index 6853ccc73..0205054e4 100644 --- a/flog/src/flog.c +++ b/flog/src/flog.c @@ -26,6 +26,10 @@ int flog_map_buf(int fdout, flog_ctx_t *flog_ctx) { uint64_t off = 0; void *addr; + + mbuf_size = 2 * BUF_SIZE; + + if (flog_ctx->size==0) flog_ctx->size=BUF_SIZE; /* * Two buffers are mmaped into memory. A new one is mapped when a first @@ -47,16 +51,27 @@ int flog_map_buf(int fdout, flog_ctx_t *flog_ctx) fsize += BUF_SIZE; fsize += BUF_SIZE; - if (ftruncate(fdout, fsize)) { - fprintf(stderr, "Unable to truncate a file: %m"); - return -1; + if (!flog_ctx->readonly) { + if (ftruncate(fdout, fsize)) { + fprintf(stderr, "Unable to truncate a file: %m"); + return -1; + } } - if (!fbuf) - addr = mmap(NULL, BUF_SIZE * 2, PROT_WRITE | PROT_READ, + if (!fbuf) { + if (!flog_ctx->readonly) { + addr = mmap(NULL, BUF_SIZE * 2, PROT_WRITE | PROT_READ, MAP_FILE | MAP_SHARED, fdout, fsize - 2 * BUF_SIZE); - else + } + else { + addr = mmap(NULL, flog_ctx->size, PROT_READ, + MAP_FILE | MAP_SHARED, fdout, 0); + mbuf_size = flog_ctx->size; + } + } + else { addr = mremap(fbuf + BUF_SIZE, BUF_SIZE, BUF_SIZE * 2, MREMAP_FIXED, fbuf); + } if (addr == MAP_FAILED) { fprintf(stderr, "Unable to map a buffer: %m"); return -1; @@ -64,24 +79,25 @@ int flog_map_buf(int fdout, flog_ctx_t *flog_ctx) fbuf = addr; mbuf = fbuf + off; - mbuf_size = 2 * BUF_SIZE; + //flog_ctx->buf=flog_ctx->pos=mbuf; - printf("flog_map_buf: mapping buffer to file, address: %ld\n", (long)mbuf); + //printf("flog_map_buf: mapping buffer to file, address: %ld\n", (long)mbuf); binlog_fd=fdout; flog_init(flog_ctx); + //printf("exited from flog_init\n"); return 0; } int flog_init(flog_ctx_t *ctx) { - memset(ctx, 0, sizeof(*ctx)); + //memset(ctx, 0, sizeof(*ctx)); - /* 20M should be enough for now */ - ctx->size = ctx->left = mbuf_size; //20 << 20; - ctx->pos = ctx->buf = mbuf;//malloc(ctx->size); + + ctx->size = ctx->left = mbuf_size; + ctx->pos = ctx->buf = mbuf; if (!ctx->buf) return -ENOMEM; - printf("flog_init: ctx pos address: %ld\n", (long) ctx->pos); + return 0; } @@ -89,20 +105,22 @@ void flog_fini(flog_ctx_t *ctx) { if (mbuf == _mbuf) return; - printf("closing binary log...\n"); + //printf("closing binary log...\n"); munmap(ctx->buf, BUF_SIZE * 2); //printf("closed\n"); ctx->size=(size_t) (ctx->pos - ctx->buf); //printf("flog_fini: size should be %ld\n",ctx->size); - if (ftruncate(binlog_fd, ctx->size)) { - fprintf(stderr, "Unable to truncate a file: %m"); + if (!ctx->readonly) { + if (ftruncate(binlog_fd, ctx->size)) { + fprintf(stderr, "Unable to truncate a file: %m"); + } } //free(ctx->buf); - memset(ctx, 0, sizeof(*ctx)); + //memset(ctx, 0, sizeof(*ctx)); } -int flog_decode_msg(flog_msg_t *m, int fdout) +int flog_decode_msg(flog_msg_t *ro_m, int fdout) { ffi_type *args[34] = { [0] = &ffi_type_sint, @@ -112,15 +130,16 @@ int flog_decode_msg(flog_msg_t *m, int fdout) void *values[34]; ffi_cif cif; ffi_arg rc; - + flog_msg_t *m; size_t i, ret = 0; char *fmt; + m=malloc(ro_m->size); + memcpy(m, ro_m, ro_m->size); values[0] = (void *)&fdout; - + //printf("nargs is %d, fmt is %ld, format string is %s\n", m->nargs, m->fmt, (char *)m + m->fmt); if (m->magic != FLOG_MAGIC) { - //printf("bad magic %d, not %d\n", m->magic, FLOG_MAGIC); - //printf("fmt is %ld, format string is %s\n", m->fmt, "(char *)m + m->fmt"); + //printf("bad magic %d, not %d\n", m->magic, FLOG_MAGIC); return -EINVAL; } if (m->version != FLOG_VERSION) { @@ -130,19 +149,28 @@ int flog_decode_msg(flog_msg_t *m, int fdout) fmt = (void *)m + m->fmt; values[1] = &fmt; - - for (i = 0; i < m->nargs; i++) { + + for (i = 0; i < m->nargs; i++) { + //printf("args %ld: %ld\n", i, m->args[i]); values[i + 2] = (void *)&m->args[i]; - if (m->mask & (1u << i)) + if (m->mask & (1u << i)) { m->args[i] = (long)((void *)m + m->args[i]); + //printf("args %ld: %s\n", i, (char *)m->args[i]); + } + } - - if (ffi_prep_cif(&cif, FFI_DEFAULT_ABI, m->nargs + 2, - &ffi_type_sint, args) == FFI_OK) { + //printf("one line soon will be done\n"); + int sdf=ffi_prep_cif(&cif, FFI_DEFAULT_ABI, m->nargs + 2, + &ffi_type_sint, args); + //printf("one line prep = %d\n", sdf==FFI_OK); + if ( sdf == FFI_OK) { ffi_call(&cif, FFI_FN(dprintf), &rc, values); } else ret = -1; - + + //printf("one line done\n"); + + free(m); return ret; } @@ -150,7 +178,11 @@ void flog_decode_all(flog_ctx_t *ctx, int fdout) { flog_msg_t *m; char *pos; - + printf("log size is %ld\n", ctx->size); + if (ctx->size == 0) + return; + if (ctx->readonly) ctx->pos=ctx->buf + ctx->size; + //printf("flog_decode_all: ctx pos address: %ld, ctx buff address: %ld\n", (long) ctx->pos,(long) ctx->buf); for (pos = ctx->buf; pos < ctx->pos; ) { m = (void *)pos; //printf("flog_decode_all: ctx pos address: %ld, pos address: %ld\n", (long) ctx->pos,(long) pos); @@ -160,7 +192,11 @@ void flog_decode_all(flog_ctx_t *ctx, int fdout) } int flog_encode_msg(flog_ctx_t *ctx, unsigned int nargs, unsigned int mask, const char *format, ...) -{ +{ + if (ctx->readonly) { + //printf("readonly\n"); + return 0; + } flog_msg_t *m = (void *)ctx->pos; char *str_start, *p; va_list argptr; @@ -176,7 +212,7 @@ int flog_encode_msg(flog_ctx_t *ctx, unsigned int nargs, unsigned int mask, cons m->fmt = str_start - ctx->pos; str_start = p; - printf("arguments: %d, mask: %d, fmt: %s\n", nargs, mask, format); + //printf("arguments: %d, mask: %d, fmt: %s\n", nargs, mask, format); va_start(argptr, format); for (i = 0; i < nargs; i++) { m->args[i] = (long)va_arg(argptr, long); From 220455fc9a82087f98bb57cf9229cfa8a45eb207 Mon Sep 17 00:00:00 2001 From: Anastasia Markina Date: Mon, 26 Aug 2019 10:52:24 +0300 Subject: [PATCH 246/249] added documentation about binlog Documentation/binlog.txt file describes binlog file format and used options --- Documentation/binlog.txt | 52 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 Documentation/binlog.txt diff --git a/Documentation/binlog.txt b/Documentation/binlog.txt new file mode 100644 index 000000000..1a5c80c1d --- /dev/null +++ b/Documentation/binlog.txt @@ -0,0 +1,52 @@ +CRIU binary logging +=================== + +Usage: turning on binary logging +-------------------------------- + +Classical text logging is done by default. +Binary logging is turned on by the `--binlog` command line parameter. +It should be combined with `--log-file filename` parameter to specify +the log file name (this second parameter is used with both text and binary logging). + +The example: + +`criu --log-file /tmp/sdf.bin --binlog check` + +Usage: reading binary log file +------------------------------ + +There is a `--print-log` parameter which turnes criy into a log reader. +It should be combined with `--log-file filename` parameter, which specifies +the log file name. When these parameters are used, CRIU prints all messages +from the binary log file on the screen and exits. This looks as follows: + +`criu --log-file /tmp/sdf.bin --binlog --print-log` + +Binary log file format +---------------------- + +Text logging in CRIU is based on the printf functions family, with format string +followed by arguments. To make logging in binary form faster, CRIU just stores +format string and arguments in binary log without parsing. When binary log file +is read with `--print-log` parameter, these data are taken from the binary log +records and passed to a printf function. + +Binary log file format is highly inspired by the [flog experimental project](https://github.com/cyrillos/flog). +Each log message is stored as a format string aned a variable number of +arguments. The variable size record has following structure: + + +| field | datatype | explanation | +|---------|--------------|---------------------------------| +| magic | unsigned int | magic code, equal to `0x676f6c66` | +| version | unsigned int | binlog file version, equal to `1` | +| size | unsigned int | actual size of the record (is needed because of variable arguments| +| nargs | unsigned int | number of arguments | +| mask | unsigned int | mask, which encodes the argument type, e.g. long integer or string| +| fmt | long | offset to the format string, starting from the beginning of the record| +| args[0] | long | 1st argument: the value, if it is a number, or the offset to the string, starting from the beginning of the record | +| ..... | | | +| args[nargs-1] | long | last argument: the value, if it is a number, or the offset to the string, starting from the beginning of the record | +| ..... | | format string and all other strings passed as arguments, if any | + From 2391ba9d05d06466ba2676192266e64fb165cec6 Mon Sep 17 00:00:00 2001 From: Anastasia Markina Date: Mon, 26 Aug 2019 15:20:04 +0300 Subject: [PATCH 247/249] code clean-up --- criu/crtools.c | 2 -- criu/log.c | 8 -------- flog/src/flog.c | 26 +------------------------- 3 files changed, 1 insertion(+), 35 deletions(-) diff --git a/criu/crtools.c b/criu/crtools.c index 6bda6945a..010d8d11b 100644 --- a/criu/crtools.c +++ b/criu/crtools.c @@ -71,8 +71,6 @@ int main(int argc, char *argv[], char *envp[]) /* FIXME: where to put flog_fini? */ - //flog_init(&flog_ctx); - BUILD_BUG_ON(CTL_32 != SYSCTL_TYPE__CTL_32); BUILD_BUG_ON(__CTL_STR != SYSCTL_TYPE__CTL_STR); /* We use it for fd overlap handling in clone_service_fd() */ diff --git a/criu/log.c b/criu/log.c index 096c683aa..723fe72d0 100644 --- a/criu/log.c +++ b/criu/log.c @@ -221,14 +221,12 @@ int log_init(const char *output) } } else if (output) { if (opts.printlog) { - //printf("printlog!\n"); /* read old binlog */ log_file_open_mode = O_RDONLY; flog_ctx.readonly=1; /* don't try to write into it */ struct stat st; stat(opts.output, &st); flog_ctx.size=st.st_size; - //printf("file %s is %ld bytes\n",opts.output, flog_ctx.size); } else if (opts.binlog) { log_file_open_mode = O_RDWR | O_CREAT | O_TRUNC; @@ -257,8 +255,6 @@ int log_init(const char *output) if (opts.binlog||opts.printlog) { - //flog_init(&flog_ctx); - //strncpy(opts.output, "-", 2); flog_map_buf(fd, &flog_ctx); } @@ -269,9 +265,6 @@ int log_init(const char *output) * Once logging is setup this write out all early log messages. * Only those messages which have to correct log level are printed. */ - - //flush_early_log_buffer(fd); - print_versions(); return 0; @@ -308,7 +301,6 @@ void log_fini(void) { flog_fini(&flog_ctx); - //close_service_fd(LOG_FD_OFF); } diff --git a/flog/src/flog.c b/flog/src/flog.c index 0205054e4..9a538795c 100644 --- a/flog/src/flog.c +++ b/flog/src/flog.c @@ -80,19 +80,13 @@ int flog_map_buf(int fdout, flog_ctx_t *flog_ctx) fbuf = addr; mbuf = fbuf + off; - //flog_ctx->buf=flog_ctx->pos=mbuf; - //printf("flog_map_buf: mapping buffer to file, address: %ld\n", (long)mbuf); binlog_fd=fdout; flog_init(flog_ctx); - //printf("exited from flog_init\n"); return 0; } int flog_init(flog_ctx_t *ctx) { - //memset(ctx, 0, sizeof(*ctx)); - - ctx->size = ctx->left = mbuf_size; ctx->pos = ctx->buf = mbuf; if (!ctx->buf) @@ -105,19 +99,14 @@ void flog_fini(flog_ctx_t *ctx) { if (mbuf == _mbuf) return; - //printf("closing binary log...\n"); munmap(ctx->buf, BUF_SIZE * 2); - //printf("closed\n"); ctx->size=(size_t) (ctx->pos - ctx->buf); - //printf("flog_fini: size should be %ld\n",ctx->size); if (!ctx->readonly) { if (ftruncate(binlog_fd, ctx->size)) { fprintf(stderr, "Unable to truncate a file: %m"); } } - //free(ctx->buf); - //memset(ctx, 0, sizeof(*ctx)); } int flog_decode_msg(flog_msg_t *ro_m, int fdout) @@ -137,13 +126,10 @@ int flog_decode_msg(flog_msg_t *ro_m, int fdout) m=malloc(ro_m->size); memcpy(m, ro_m, ro_m->size); values[0] = (void *)&fdout; - //printf("nargs is %d, fmt is %ld, format string is %s\n", m->nargs, m->fmt, (char *)m + m->fmt); if (m->magic != FLOG_MAGIC) { - //printf("bad magic %d, not %d\n", m->magic, FLOG_MAGIC); return -EINVAL; } if (m->version != FLOG_VERSION) { - //printf("bad version\n"); return -EINVAL; } @@ -151,25 +137,20 @@ int flog_decode_msg(flog_msg_t *ro_m, int fdout) values[1] = &fmt; for (i = 0; i < m->nargs; i++) { - //printf("args %ld: %ld\n", i, m->args[i]); values[i + 2] = (void *)&m->args[i]; if (m->mask & (1u << i)) { m->args[i] = (long)((void *)m + m->args[i]); - //printf("args %ld: %s\n", i, (char *)m->args[i]); } } - //printf("one line soon will be done\n"); + int sdf=ffi_prep_cif(&cif, FFI_DEFAULT_ABI, m->nargs + 2, &ffi_type_sint, args); - //printf("one line prep = %d\n", sdf==FFI_OK); if ( sdf == FFI_OK) { ffi_call(&cif, FFI_FN(dprintf), &rc, values); } else ret = -1; - //printf("one line done\n"); - free(m); return ret; } @@ -182,10 +163,8 @@ void flog_decode_all(flog_ctx_t *ctx, int fdout) if (ctx->size == 0) return; if (ctx->readonly) ctx->pos=ctx->buf + ctx->size; - //printf("flog_decode_all: ctx pos address: %ld, ctx buff address: %ld\n", (long) ctx->pos,(long) ctx->buf); for (pos = ctx->buf; pos < ctx->pos; ) { m = (void *)pos; - //printf("flog_decode_all: ctx pos address: %ld, pos address: %ld\n", (long) ctx->pos,(long) pos); flog_decode_msg(m ,fdout); pos += m->size; } @@ -194,7 +173,6 @@ void flog_decode_all(flog_ctx_t *ctx, int fdout) int flog_encode_msg(flog_ctx_t *ctx, unsigned int nargs, unsigned int mask, const char *format, ...) { if (ctx->readonly) { - //printf("readonly\n"); return 0; } flog_msg_t *m = (void *)ctx->pos; @@ -212,7 +190,6 @@ int flog_encode_msg(flog_ctx_t *ctx, unsigned int nargs, unsigned int mask, cons m->fmt = str_start - ctx->pos; str_start = p; - //printf("arguments: %d, mask: %d, fmt: %s\n", nargs, mask, format); va_start(argptr, format); for (i = 0; i < nargs; i++) { m->args[i] = (long)va_arg(argptr, long); @@ -246,6 +223,5 @@ int flog_encode_msg(flog_ctx_t *ctx, unsigned int nargs, unsigned int mask, cons /* Advance position and left bytes in context memory */ ctx->left -= m->size; ctx->pos += m->size; - //printf("flog_encode_msg: ctx pos address: %ld\n", (long) ctx->pos); return 0; } From 1355ac11112d9de039bb3d9dabf1540931937d13 Mon Sep 17 00:00:00 2001 From: Anastasia Markina Date: Sat, 31 Aug 2019 00:16:35 +0300 Subject: [PATCH 248/249] Fix crash when no log file name was specified in commandline --- criu/log.c | 1 + 1 file changed, 1 insertion(+) diff --git a/criu/log.c b/criu/log.c index 723fe72d0..c4b7bd79e 100644 --- a/criu/log.c +++ b/criu/log.c @@ -242,6 +242,7 @@ int log_init(const char *output) return -1; } } else { + flog_ctx.readonly=1; /* don't try to write into it */ new_logfd = dup(DEFAULT_LOGFD); if (new_logfd < 0) { pr_perror("Can't dup log file"); From e38eedd52cdab766defdd54ff34ed50013bffcf0 Mon Sep 17 00:00:00 2001 From: Anastasia Markina Date: Sun, 1 Sep 2019 22:11:44 +0300 Subject: [PATCH 249/249] fix crash when null argument is passed for the %s in format string --- flog/src/flog.c | 1 + 1 file changed, 1 insertion(+) diff --git a/flog/src/flog.c b/flog/src/flog.c index 9a538795c..9a0e18965 100644 --- a/flog/src/flog.c +++ b/flog/src/flog.c @@ -199,6 +199,7 @@ int flog_encode_msg(flog_ctx_t *ctx, unsigned int nargs, unsigned int mask, cons * a copy (FIXME implement rodata refs). */ if (mask & (1u << i)) { + if (!m->args[i]) m->args[i] = (long)"NULL"; p = memccpy(str_start, (void *)m->args[i], 0, ctx->left - (str_start - ctx->pos)); if (!p) return -ENOMEM;