diff -Nru criu-3.12/compel/arch/aarch64/plugins/std/syscalls/syscall.def criu-3.13/compel/arch/aarch64/plugins/std/syscalls/syscall.def --- criu-3.12/compel/arch/aarch64/plugins/std/syscalls/syscall.def 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/compel/arch/aarch64/plugins/std/syscalls/syscall.def 2019-09-11 08:29:31.000000000 +0000 @@ -110,3 +110,5 @@ 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) +ppoll 73 336 (struct pollfd *fds, unsigned int nfds, const struct timespec *tmo, const sigset_t *sigmask, size_t sigsetsize) diff -Nru criu-3.12/compel/arch/arm/plugins/std/syscalls/syscall-aux.h criu-3.13/compel/arch/arm/plugins/std/syscalls/syscall-aux.h --- criu-3.12/compel/arch/arm/plugins/std/syscalls/syscall-aux.h 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/compel/arch/arm/plugins/std/syscalls/syscall-aux.h 2019-09-11 08:29:31.000000000 +0000 @@ -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 -Nru criu-3.12/compel/arch/arm/plugins/std/syscalls/syscall.def criu-3.13/compel/arch/arm/plugins/std/syscalls/syscall.def --- criu-3.12/compel/arch/arm/plugins/std/syscalls/syscall.def 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/compel/arch/arm/plugins/std/syscalls/syscall.def 2019-09-11 08:29:31.000000000 +0000 @@ -110,3 +110,5 @@ 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) +ppoll 73 336 (struct pollfd *fds, unsigned int nfds, const struct timespec *tmo, const sigset_t *sigmask, size_t sigsetsize) diff -Nru criu-3.12/compel/arch/arm/src/lib/include/uapi/asm/processor-flags.h criu-3.13/compel/arch/arm/src/lib/include/uapi/asm/processor-flags.h --- criu-3.12/compel/arch/arm/src/lib/include/uapi/asm/processor-flags.h 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/compel/arch/arm/src/lib/include/uapi/asm/processor-flags.h 2019-09-11 08:29:31.000000000 +0000 @@ -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 -Nru criu-3.12/compel/arch/ppc64/plugins/std/syscalls/syscall-ppc64.tbl criu-3.13/compel/arch/ppc64/plugins/std/syscalls/syscall-ppc64.tbl --- criu-3.12/compel/arch/ppc64/plugins/std/syscalls/syscall-ppc64.tbl 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/compel/arch/ppc64/plugins/std/syscalls/syscall-ppc64.tbl 2019-09-11 08:29:31.000000000 +0000 @@ -107,3 +107,4 @@ __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 -Nru criu-3.12/compel/arch/ppc64/src/lib/include/uapi/asm/breakpoints.h criu-3.13/compel/arch/ppc64/src/lib/include/uapi/asm/breakpoints.h --- criu-3.12/compel/arch/ppc64/src/lib/include/uapi/asm/breakpoints.h 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/compel/arch/ppc64/src/lib/include/uapi/asm/breakpoints.h 2019-09-11 08:29:31.000000000 +0000 @@ -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 -Nru criu-3.12/compel/arch/s390/plugins/std/syscalls/syscall-s390.tbl criu-3.13/compel/arch/s390/plugins/std/syscalls/syscall-s390.tbl --- criu-3.12/compel/arch/s390/plugins/std/syscalls/syscall-s390.tbl 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/compel/arch/s390/plugins/std/syscalls/syscall-s390.tbl 2019-09-11 08:29:31.000000000 +0000 @@ -107,3 +107,4 @@ __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 -Nru criu-3.12/compel/arch/s390/src/lib/infect.c criu-3.13/compel/arch/s390/src/lib/infect.c --- criu-3.12/compel/arch/s390/src/lib/infect.c 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/compel/arch/s390/src/lib/infect.c 2019-09-11 08:29:31.000000000 +0000 @@ -148,10 +148,8 @@ 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; } @@ -495,7 +493,7 @@ 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 */ diff -Nru criu-3.12/compel/arch/x86/plugins/std/syscalls/syscall_32.tbl criu-3.13/compel/arch/x86/plugins/std/syscalls/syscall_32.tbl --- criu-3.12/compel/arch/x86/plugins/std/syscalls/syscall_32.tbl 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/compel/arch/x86/plugins/std/syscalls/syscall_32.tbl 2019-09-11 08:29:31.000000000 +0000 @@ -95,3 +95,4 @@ __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 -Nru criu-3.12/compel/arch/x86/plugins/std/syscalls/syscall_64.tbl criu-3.13/compel/arch/x86/plugins/std/syscalls/syscall_64.tbl --- criu-3.12/compel/arch/x86/plugins/std/syscalls/syscall_64.tbl 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/compel/arch/x86/plugins/std/syscalls/syscall_64.tbl 2019-09-11 08:29:31.000000000 +0000 @@ -106,3 +106,4 @@ __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 -Nru criu-3.12/compel/arch/x86/src/lib/cpu.c criu-3.13/compel/arch/x86/src/lib/cpu.c --- criu-3.12/compel/arch/x86/src/lib/cpu.c 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/compel/arch/x86/src/lib/cpu.c 2019-09-11 08:29:31.000000000 +0000 @@ -269,7 +269,8 @@ 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", diff -Nru criu-3.12/compel/arch/x86/src/lib/include/uapi/asm/fpu.h criu-3.13/compel/arch/x86/src/lib/include/uapi/asm/fpu.h --- criu-3.12/compel/arch/x86/src/lib/include/uapi/asm/fpu.h 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/compel/arch/x86/src/lib/include/uapi/asm/fpu.h 2019-09-11 08:29:31.000000000 +0000 @@ -149,7 +149,7 @@ * 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 -Nru criu-3.12/compel/arch/x86/src/lib/infect.c criu-3.13/compel/arch/x86/src/lib/infect.c --- criu-3.12/compel/arch/x86/src/lib/infect.c 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/compel/arch/x86/src/lib/infect.c 2019-09-11 08:29:31.000000000 +0000 @@ -375,10 +375,13 @@ 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; } @@ -481,15 +484,15 @@ /* 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 -Nru criu-3.12/compel/include/uapi/common/arch/arm/asm/atomic.h criu-3.13/compel/include/uapi/common/arch/arm/asm/atomic.h --- criu-3.12/compel/include/uapi/common/arch/arm/asm/atomic.h 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/compel/include/uapi/common/arch/arm/asm/atomic.h 2019-09-11 08:29:31.000000000 +0000 @@ -29,9 +29,9 @@ "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 *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 @@ " 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 -Nru criu-3.12/compel/include/uapi/common/arch/arm/asm/linkage.h criu-3.13/compel/include/uapi/common/arch/arm/asm/linkage.h --- criu-3.12/compel/include/uapi/common/arch/arm/asm/linkage.h 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/compel/include/uapi/common/arch/arm/asm/linkage.h 2019-09-11 08:29:31.000000000 +0000 @@ -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__ */ diff -Nru criu-3.12/compel/include/uapi/common/arch/ppc64/asm/atomic.h criu-3.13/compel/include/uapi/common/arch/ppc64/asm/atomic.h --- criu-3.12/compel/include/uapi/common/arch/ppc64/asm/atomic.h 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/compel/include/uapi/common/arch/ppc64/asm/atomic.h 2019-09-11 08:29:31.000000000 +0000 @@ -8,7 +8,7 @@ */ typedef struct { - int counter; + int counter; } atomic_t; #include "common/arch/ppc64/asm/cmpxchg.h" diff -Nru criu-3.12/compel/include/uapi/common/arch/ppc64/asm/linkage.h criu-3.13/compel/include/uapi/common/arch/ppc64/asm/linkage.h --- criu-3.12/compel/include/uapi/common/arch/ppc64/asm/linkage.h 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/compel/include/uapi/common/arch/ppc64/asm/linkage.h 2019-09-11 08:29:31.000000000 +0000 @@ -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 -Nru criu-3.12/compel/include/uapi/common/arch/x86/asm/cmpxchg.h criu-3.13/compel/include/uapi/common/arch/x86/asm/cmpxchg.h --- criu-3.12/compel/include/uapi/common/arch/x86/asm/cmpxchg.h 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/compel/include/uapi/common/arch/x86/asm/cmpxchg.h 2019-09-11 08:29:31.000000000 +0000 @@ -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 -Nru criu-3.12/compel/include/uapi/plugins/std/log.h criu-3.13/compel/include/uapi/plugins/std/log.h --- criu-3.12/compel/include/uapi/plugins/std/log.h 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/compel/include/uapi/plugins/std/log.h 2019-09-11 08:29:31.000000000 +0000 @@ -1,11 +1,25 @@ #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); + +/* + * 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 -Nru criu-3.12/compel/include/uapi/plugins/std/syscall-types.h criu-3.13/compel/include/uapi/plugins/std/syscall-types.h --- criu-3.12/compel/include/uapi/plugins/std/syscall-types.h 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/compel/include/uapi/plugins/std/syscall-types.h 2019-09-11 08:29:31.000000000 +0000 @@ -38,6 +38,7 @@ struct msghdr; struct rusage; struct iocb; +struct pollfd; typedef unsigned long aio_context_t; diff -Nru criu-3.12/compel/include/uapi/sigframe-common.h criu-3.13/compel/include/uapi/sigframe-common.h --- criu-3.12/compel/include/uapi/sigframe-common.h 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/compel/include/uapi/sigframe-common.h 2019-09-11 08:29:31.000000000 +0000 @@ -52,8 +52,8 @@ 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 -Nru criu-3.12/compel/Makefile criu-3.13/compel/Makefile --- criu-3.12/compel/Makefile 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/compel/Makefile 2019-09-11 08:29:31.000000000 +0000 @@ -11,7 +11,6 @@ ccflags-y += -iquote compel/include ccflags-y += -fno-strict-aliasing ccflags-y += -fPIC -ccflags-y += $(CFLAGS_PIE) ldflags-y += -r # diff -Nru criu-3.12/compel/plugins/include/uapi/std/log.h criu-3.13/compel/plugins/include/uapi/std/log.h --- criu-3.12/compel/plugins/include/uapi/std/log.h 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/compel/plugins/include/uapi/std/log.h 2019-09-11 08:29:31.000000000 +0000 @@ -1,11 +1,25 @@ #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); + +/* + * 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 -Nru criu-3.12/compel/plugins/include/uapi/std/syscall-types.h criu-3.13/compel/plugins/include/uapi/std/syscall-types.h --- criu-3.12/compel/plugins/include/uapi/std/syscall-types.h 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/compel/plugins/include/uapi/std/syscall-types.h 2019-09-11 08:29:31.000000000 +0000 @@ -38,6 +38,7 @@ struct msghdr; struct rusage; struct iocb; +struct pollfd; typedef unsigned long aio_context_t; diff -Nru criu-3.12/compel/plugins/Makefile criu-3.13/compel/plugins/Makefile --- criu-3.12/compel/plugins/Makefile 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/compel/plugins/Makefile 2019-09-11 08:29:31.000000000 +0000 @@ -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 @@ -29,7 +29,7 @@ # 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 -Nru criu-3.12/compel/plugins/std/log.c criu-3.13/compel/plugins/std/log.c --- criu-3.12/compel/plugins/std/log.c 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/compel/plugins/std/log.c 2019-09-11 08:29:31.000000000 +0000 @@ -16,6 +16,7 @@ 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 @@ if (start.tv_sec != 0) { struct timeval now; - sys_gettimeofday(&now, NULL); + std_gettimeofday(&now, NULL); timediff(&start, &now); /* Seconds */ @@ -120,7 +121,7 @@ logfd = fd; } -void std_log_set_loglevel(unsigned int level) +void std_log_set_loglevel(enum __compel_log_levels level) { cur_loglevel = level; } @@ -130,6 +131,19 @@ 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) { diff -Nru criu-3.12/compel/src/lib/infect.c criu-3.13/compel/src/lib/infect.c --- criu-3.12/compel/src/lib/infect.c 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/compel/src/lib/infect.c 2019-09-11 08:29:31.000000000 +0000 @@ -273,7 +273,6 @@ goto err; } - ret = 0; if (free_status) free_status(pid, ss, data); goto try_again; diff -Nru criu-3.12/contrib/debian/dev-packages.lst criu-3.13/contrib/debian/dev-packages.lst --- criu-3.12/contrib/debian/dev-packages.lst 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/contrib/debian/dev-packages.lst 2019-09-11 08:29:31.000000000 +0000 @@ -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 -Nru criu-3.12/coredump/criu_coredump/coredump.py criu-3.13/coredump/criu_coredump/coredump.py --- criu-3.12/coredump/criu_coredump/coredump.py 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/coredump/criu_coredump/coredump.py 2019-09-11 08:29:31.000000000 +0000 @@ -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 @@ -36,794 +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; - - def write(self, f): - """ - Write core dump to file f. - """ - buf = io.BytesIO() - buf.write(self.ehdr) - - 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) - - 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 - - note_align = PAGESIZE - ((offset + filesz) % PAGESIZE) - - 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) + """ + 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; + + def write(self, f): + """ + Write core dump to file f. + """ + buf = io.BytesIO() + buf.write(self.ehdr) + + 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) + + 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 + + note_align = PAGESIZE - ((offset + filesz) % PAGESIZE) + + 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) - 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; - - 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): - """ - Load criu image and strip it from magic and redundant list. - """ - path = self._imgs_dir + "/" + name - if pid: - path += "-"+str(pid) - path += ".img" - - with open(path) as f: - img = images.load(f) - - if single: - return img["entries"][0] - else: - return img["entries"] - - - 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") - - 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.reg_files = self._img_open_and_strip("reg-files", False) - - for pid in self.pstree: - self.coredumps[pid] = self._gen_coredump(pid) - - return self.coredumps - - - 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) - - def _gen_coredump(self, pid): - """ - Generate core dump for pid. - """ - 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) - - return cd - - 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): - """ - Generate program headers for process pid. - """ - phdrs = [] - - 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 - - # 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) - - note_align = PAGESIZE - ((offset + filesz) % PAGESIZE) - - if note_align == PAGESIZE: - note_align = 0 - - offset += note_align - - # 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 - - phdrs.append(phdr) - - return phdrs - - 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): - """ - 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): - """ - 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): - """ - 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): - """ - 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)) - - 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 - - return note - - 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 - - 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] - - 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 - - return note - - 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): - """ - Generate notes for core dump of process pid. - """ - notes = [] - - notes.append(self._gen_prpsinfo(pid)) - - threads = self.pstree[pid]["threads"] - - # Main thread first - notes += self._gen_thread_notes(pid, pid) - - # Then other threads - for tid in threads: - if tid == pid: - continue - - notes += self._gen_thread_notes(pid, tid) - - notes.append(self._gen_auxv(pid)) - notes.append(self._gen_files(pid)) - - return notes - - 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): - """ - 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): - """ - Generate full command with arguments. - """ - 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"] - - chunk = self._gen_mem_chunk(pid, vma, size) - - # Replace all '\0's with spaces. - return chunk.replace('\0', ' ') - - 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): - """ - Convert vma flags int elf flags. - """ - flags = 0 - - 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_EXEC"]: - flags = flags | elf.PF_X - - return flags - - def _gen_vmas(self, pid): - """ - Generate vma contents for core dump for process pid. - """ - mm = self.mms[pid] - - 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) - - 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) + """ + Generate core dump from criu images. + """ + 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; + + 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" + + with open(path) as f: + img = images.load(f) + + if single: + return img["entries"][0] + else: + return img["entries"] + + 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") + + 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) + + 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) + + return self.coredumps + + 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) + + def _gen_coredump(self, pid): + """ + Generate core dump for pid. + """ + 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) + + return cd + + 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): + """ + Generate program headers for process pid. + """ + phdrs = [] + + 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 + + # 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) + + note_align = PAGESIZE - ((offset + filesz) % PAGESIZE) + + if note_align == PAGESIZE: + note_align = 0 + + offset += note_align + + # 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 + + phdrs.append(phdr) + + return phdrs + + 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): + """ + 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): + """ + 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): + """ + 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): + """ + 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)) + + 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 + + return note + + 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 + + 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] + + 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 + + return note + + 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): + """ + Generate notes for core dump of process pid. + """ + notes = [] + + notes.append(self._gen_prpsinfo(pid)) + + threads = self.pstree[pid]["threads"] + + # Main thread first + notes += self._gen_thread_notes(pid, pid) + + # Then other threads + for tid in threads: + if tid == pid: + continue + + notes += self._gen_thread_notes(pid, tid) + + notes.append(self._gen_auxv(pid)) + notes.append(self._gen_files(pid)) + + return notes + + 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): + """ + 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): + """ + Generate full command with arguments. + """ + 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"] + + chunk = self._gen_mem_chunk(pid, vma, size) + + # Replace all '\0's with spaces. + return chunk.replace('\0', ' ') + + 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): + """ + Convert vma flags int elf flags. + """ + flags = 0 + + 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_EXEC"]: + flags = flags | elf.PF_X + + return flags + + def _gen_vmas(self, pid): + """ + Generate vma contents for core dump for process pid. + """ + mm = self.mms[pid] + + 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) + + 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) - vmas.append(v) + vmas.append(v) - return vmas + return vmas diff -Nru criu-3.12/coredump/criu_coredump/elf.py criu-3.13/coredump/criu_coredump/elf.py --- criu-3.12/coredump/criu_coredump/elf.py 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/coredump/criu_coredump/elf.py 2019-09-11 08:29:31.000000000 +0000 @@ -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 */ +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; + +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, -# size might increase */ -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 */ - -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; +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 +# files */ +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; # 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_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. */ +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]; - ] # }; +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 +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 -Nru criu-3.12/coredump/pycriu/cli.py criu-3.13/coredump/pycriu/cli.py --- criu-3.12/coredump/pycriu/cli.py 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/coredump/pycriu/cli.py 2019-09-11 08:29:31.000000000 +0000 @@ -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) + + if opts['pretty']: + indent = 4 + + f = outf(opts) + json.dump(img, f, indent=indent) + if f == sys.stdout: + f.write("\n") - 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 - - 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 + 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) - pp = pss[p.ppid] - pp.kids.append(p) + print("%7s%7s%7s %s" % ('PID', 'PGID', 'SID', 'COMM')) + show_ps(psr, opts) - 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 len(files_img) == 0: - return None - - for f in files_img: - if f['id'] == fid: - return f + 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 + + 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']] - - 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 + 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 + 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 = {} -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 + 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 - 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)) - - 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']}))) + 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 get(self, iid): - ret = self.__ids.get(iid, None) - if not ret: - ret = self.__last - self.__last += 1 - self.__ids[iid] = ret + 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 + + 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 '-' + 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)) + 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)) - - + 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 +} -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) + 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) - opts["func"](opts) if __name__ == '__main__': - main() + main() diff -Nru criu-3.12/coredump/pycriu/criu.py criu-3.13/coredump/pycriu/criu.py --- criu-3.12/coredump/pycriu/criu.py 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/coredump/pycriu/criu.py 2019-09-11 08:29:31.000000000 +0000 @@ -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): - """ - Connect to criu and return socket object. - daemon -- is for whether or not criu should daemonize if executing criu from binary(comm_bin). - """ - pass - - def disconnect(self): - """ - Disconnect from criu. - """ - pass + """ + Base class for communication classes. + """ + COMM_SK = 0 + COMM_FD = 1 + COMM_BIN = 2 + comm_type = None + comm = None + sk = None + + def connect(self, daemon): + """ + Connect to criu and return socket object. + daemon -- is for whether or not criu should daemonize if executing criu from binary(comm_bin). + """ + pass + + def disconnect(self): + """ + Disconnect from criu. + """ + pass class _criu_comm_sk(_criu_comm): - """ - Communication class for unix socket. - """ - def __init__(self, sk_path): - self.comm_type = self.COMM_SK - self.comm = sk_path - - def connect(self, daemon): - self.sk = socket.socket(socket.AF_UNIX, socket.SOCK_SEQPACKET) - self.sk.connect(self.comm) + """ + Communication class for unix socket. + """ + + def __init__(self, sk_path): + self.comm_type = self.COMM_SK + self.comm = sk_path + + def connect(self, daemon): + self.sk = socket.socket(socket.AF_UNIX, socket.SOCK_SEQPACKET) + self.sk.connect(self.comm) - return self.sk + return self.sk - def disconnect(self): - self.sk.close() + 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 + """ + 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 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) + """ + 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) class CRIUException(Exception): - """ - Exception class for handling and storing criu errors. - """ - typ = None - _str = None + """ + Exception class for handling and storing criu errors. + """ + 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) + """ + Exception class for handling and storing internal errors. + """ + + def __init__(self, typ, s): + self.typ = typ + self._str = "%s failed with internal error: %s" % ( + rpc.criu_req_type.Name(self.typ), s) class CRIUExceptionExternal(CRIUException): - """ - Exception class for handling and storing criu RPC errors. - """ + """ + 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 - - _comm = None #Communication method - - def __init__(self): - self.use_binary('criu') - self.opts = rpc.criu_opts() - self.sk = None - - def use_sk(self, sk_name): - """ - Access criu using unix socket which that belongs to criu service daemon. - """ - self._comm = _criu_comm_sk(sk_name) - - def use_fd(self, fd): - """ - Access criu using provided fd. - """ - self._comm = _criu_comm_fd(fd) - - def use_binary(self, bin_name): - """ - Access criu by execing it using provided path to criu binary. - """ - self._comm = _criu_comm_bin(bin_name) - - def _send_req_and_recv_resp(self, req): - """ - As simple as send request and receive response. - """ - # In case of self-dump we need to spawn criu swrk detached - # from our current process, as criu has a hard time separating - # process resources from its own if criu is located in a same - # process tree it is trying to dump. - daemon = False - if req.type == rpc.DUMP and not req.opts.HasField('pid'): - daemon = True - - try: - if not self.sk: - s = self._comm.connect(daemon) - else: - s = self.sk - - if req.keep_open: - self.sk = s - - s.send(req.SerializeToString()) - - buf = s.recv(len(s.recv(1, socket.MSG_TRUNC | socket.MSG_PEEK))) - - 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)) - - return resp - - def check(self): - """ - Checks whether the kernel support is up-to-date. - """ - req = rpc.criu_req() - req.type = rpc.CHECK - - resp = self._send_req_and_recv_resp(req) - - if not resp.success: - raise CRIUExceptionExternal(req.type, resp.type, resp.cr_errno) - - def dump(self): - """ - Checkpoint a process/tree identified by opts.pid. - """ - req = rpc.criu_req() - req.type = rpc.DUMP - req.opts.MergeFrom(self.opts) - - resp = self._send_req_and_recv_resp(req) - - if not resp.success: - raise CRIUExceptionExternal(req.type, resp.type, resp.cr_errno) - - return resp.dump - - def 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) - - resp = self._send_req_and_recv_resp(req) - - if not resp.success: - raise CRIUExceptionExternal(req.type, resp.type, resp.cr_errno) - - return resp.dump - - def restore(self): - """ - Restore a process/tree. - """ - req = rpc.criu_req() - req.type = rpc.RESTORE - req.opts.MergeFrom(self.opts) - - resp = self._send_req_and_recv_resp(req) - - if not resp.success: - raise CRIUExceptionExternal(req.type, resp.type, resp.cr_errno) - - 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 - - resp = self._send_req_and_recv_resp(req) - - if not resp.success: - raise CRIUExceptionExternal(req.type, resp.type, resp.cr_errno) - - return resp.ps - - def wait_pid(self, pid): - req = rpc.criu_req() - req.type = rpc.WAIT_PID - req.pid = pid + """ + Call criu through RPC. + """ + opts = None #CRIU options in pb format + + _comm = None #Communication method + + def __init__(self): + self.use_binary('criu') + self.opts = rpc.criu_opts() + self.sk = None + + def use_sk(self, sk_name): + """ + Access criu using unix socket which that belongs to criu service daemon. + """ + self._comm = _criu_comm_sk(sk_name) + + def use_fd(self, fd): + """ + Access criu using provided fd. + """ + self._comm = _criu_comm_fd(fd) + + def use_binary(self, bin_name): + """ + Access criu by execing it using provided path to criu binary. + """ + self._comm = _criu_comm_bin(bin_name) + + def _send_req_and_recv_resp(self, req): + """ + As simple as send request and receive response. + """ + # In case of self-dump we need to spawn criu swrk detached + # from our current process, as criu has a hard time separating + # process resources from its own if criu is located in a same + # process tree it is trying to dump. + daemon = False + if req.type == rpc.DUMP and not req.opts.HasField('pid'): + daemon = True + + try: + if not self.sk: + s = self._comm.connect(daemon) + else: + s = self.sk + + if req.keep_open: + self.sk = s + + s.send(req.SerializeToString()) + + buf = s.recv(len(s.recv(1, socket.MSG_TRUNC | socket.MSG_PEEK))) + + 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)) + + return resp + + def check(self): + """ + Checks whether the kernel support is up-to-date. + """ + req = rpc.criu_req() + req.type = rpc.CHECK + + resp = self._send_req_and_recv_resp(req) + + if not resp.success: + raise CRIUExceptionExternal(req.type, resp.type, resp.cr_errno) + + def dump(self): + """ + Checkpoint a process/tree identified by opts.pid. + """ + req = rpc.criu_req() + req.type = rpc.DUMP + req.opts.MergeFrom(self.opts) + + resp = self._send_req_and_recv_resp(req) + + if not resp.success: + raise CRIUExceptionExternal(req.type, resp.type, resp.cr_errno) + + return resp.dump + + def 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) + + resp = self._send_req_and_recv_resp(req) + + if not resp.success: + raise CRIUExceptionExternal(req.type, resp.type, resp.cr_errno) + + return resp.dump + + def restore(self): + """ + Restore a process/tree. + """ + req = rpc.criu_req() + req.type = rpc.RESTORE + req.opts.MergeFrom(self.opts) + + resp = self._send_req_and_recv_resp(req) + + if not resp.success: + raise CRIUExceptionExternal(req.type, resp.type, resp.cr_errno) + + 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 + + resp = self._send_req_and_recv_resp(req) + + if not resp.success: + raise CRIUExceptionExternal(req.type, resp.type, resp.cr_errno) + + return resp.ps + + 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 -Nru criu-3.12/coredump/pycriu/images/images.py criu-3.13/coredump/pycriu/images/images.py --- criu-3.12/coredump/pycriu/images/images.py 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/coredump/pycriu/images/images.py 2019-09-11 08:29:31.000000000 +0000 @@ -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 @@ -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): - """ - 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. - """ - 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) - - 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): - """ - Same as dump(), but doesn't take file-like object and just - returns a string. - """ - f = io.BytesIO('') - self.dump(entries, f) - return f.read() - - def count(self, f): - """ - Counts the number of top-level object in the image file - """ - entries = 0 - - while True: - buf = f.read(4) - if buf == '': - break - size, = struct.unpack('i', buf) - f.seek(size, 1) - entries += 1 + """ + Generic class to handle loading/dumping criu images + entries from/to bin format to/from dict(json). + """ + + def __init__(self, payload, extra_handler=None): + """ + Sets payload class and extra handler class. + """ + self.payload = payload + self.extra_handler = extra_handler + + def load(self, f, pretty=False, 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): + """ + Same as load(), but takes a string as an argument. + """ + f = io.BytesIO(s) + return self.load(f, pretty) + + def dump(self, entries, f): + """ + Convert criu image entries from dict(json) format to binary. + Takes a list of entries and a file-like object to write entries + in binary format to. + """ + for entry in entries: + extra = entry.pop('extra', None) + + # Write payload + 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() + + def count(self, f): + """ + Counts the number of top-level object in the image file + """ + entries = 0 + + while True: + buf = f.read(4) + if buf == '': + break + size, = struct.unpack('i', buf) + f.seek(size, 1) + entries += 1 + + return entries - 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)) - - pbuff = pb.pagemap_entry() - - return entries - - def loads(self, s, pretty = False): - f = io.BytesIO(s) - return self.load(f, pretty) - - def dump(self, entries, f): - 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() - - def dumps(self, entries): - f = io.BytesIO('') - self.dump(entries, f) - return f.read() + """ + 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)) + + pbuff = pb.pagemap_entry() + + return entries + + def loads(self, s, pretty=False): + f = io.BytesIO(s) + return self.load(f, pretty) + + def dump(self, entries, f): + 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() + + 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 = [] + 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() + 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 @@ # 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 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 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 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 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 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 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 + 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 - 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)) - - try: - m = magic.by_val[img_magic] - except: - raise MagicException(img_magic) - - try: - handler = handlers[m] - except: - raise Exception("No handler found for image with magic " + m) - - return m, handler - -def load(f, pretty = False, 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 = {} + # Images v1.1 NOTE: First read "first" magic. + img_magic, = struct.unpack('i', f.read(4)) + if img_magic in (magic.by_name['IMG_COMMON'], + magic.by_name['IMG_SERVICE']): + img_magic, = struct.unpack('i', f.read(4)) + + try: + m = magic.by_val[img_magic] + except: + raise MagicException(img_magic) + + try: + handler = handlers[m] + except: + raise Exception("No handler found for image with magic " + m) + + return m, handler + + +def load(f, pretty=False, 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 = {} + + 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): + """ + Same as load(), but takes a string. + """ + f = io.BytesIO(s) + return load(f, pretty) -def loads(s, pretty = False): - """ - Same as load(), but takes a string. - """ - f = io.BytesIO(s) - return load(f, pretty) def dump(img, f): - """ - Convert criu image from dict(json) format to binary. - Takes an image in dict(json) format and file-like - object to write to. - """ - m = img['magic'] - magic_val = magic.by_name[img['magic']] - - # Images v1.1 NOTE: use "second" magic to identify what "first" - # should be written. - if m != 'INVENTORY': - if m in ('STATS', 'IRMAP_CACHE'): - f.write(struct.pack('i', magic.by_name['IMG_SERVICE'])) - else: - f.write(struct.pack('i', magic.by_name['IMG_COMMON'])) - - f.write(struct.pack('i', magic_val)) - - try: - handler = handlers[m] - except: - raise Exception("No handler found for image with such magic") + """ + Convert criu image from dict(json) format to binary. + Takes an image in dict(json) format and file-like + object to write to. + """ + m = img['magic'] + magic_val = magic.by_name[img['magic']] + + # Images v1.1 NOTE: use "second" magic to identify what "first" + # should be written. + if m != 'INVENTORY': + if m in ('STATS', 'IRMAP_CACHE'): + f.write(struct.pack('i', magic.by_name['IMG_SERVICE'])) + else: + f.write(struct.pack('i', magic.by_name['IMG_COMMON'])) + + f.write(struct.pack('i', magic_val)) + + try: + handler = handlers[m] + except: + raise Exception("No handler found for image with such magic") + + handler.dump(img['entries'], f) - 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() + """ + Same as dump(), but takes only an image and returns + a string. + """ + f = io.BytesIO(b'') + dump(img, f) + return f.getvalue() diff -Nru criu-3.12/coredump/pycriu/images/pb2dict.py criu-3.13/coredump/pycriu/images/pb2dict.py --- criu-3.12/coredump/pycriu/images/pb2dict.py 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/coredump/pycriu/images/pb2dict.py 2019-09-11 08:29:31.000000000 +0000 @@ -4,7 +4,13 @@ from ipaddress import IPv6Address import socket import collections -import os, six +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: @@ -23,345 +29,396 @@ # 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 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} -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): - return isinstance(value, six.string_types) + # Python 3 compatibility + if "basestring" in __builtins__: + string_types = basestring # noqa: F821 + 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 Exception: + 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) -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[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 Exception: + 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 is 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 + """ + 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 diff -Nru criu-3.12/crit/pycriu/cli.py criu-3.13/crit/pycriu/cli.py --- criu-3.12/crit/pycriu/cli.py 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/crit/pycriu/cli.py 2019-09-11 08:29:31.000000000 +0000 @@ -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) + + if opts['pretty']: + indent = 4 + + f = outf(opts) + json.dump(img, f, indent=indent) + if f == sys.stdout: + f.write("\n") - 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 - - 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 + 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) - pp = pss[p.ppid] - pp.kids.append(p) + print("%7s%7s%7s %s" % ('PID', 'PGID', 'SID', 'COMM')) + show_ps(psr, opts) - 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 len(files_img) == 0: - return None - - for f in files_img: - if f['id'] == fid: - return f + 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 + + 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']] - - 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 + 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 + 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 = {} -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 + 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 - 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)) - - 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']}))) + 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 get(self, iid): - ret = self.__ids.get(iid, None) - if not ret: - ret = self.__last - self.__last += 1 - self.__ids[iid] = ret + 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 + + 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 '-' + 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)) + 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)) - - + 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 +} -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) + 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) - opts["func"](opts) if __name__ == '__main__': - main() + main() diff -Nru criu-3.12/crit/pycriu/criu.py criu-3.13/crit/pycriu/criu.py --- criu-3.12/crit/pycriu/criu.py 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/crit/pycriu/criu.py 2019-09-11 08:29:31.000000000 +0000 @@ -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): - """ - Connect to criu and return socket object. - daemon -- is for whether or not criu should daemonize if executing criu from binary(comm_bin). - """ - pass - - def disconnect(self): - """ - Disconnect from criu. - """ - pass + """ + Base class for communication classes. + """ + COMM_SK = 0 + COMM_FD = 1 + COMM_BIN = 2 + comm_type = None + comm = None + sk = None + + def connect(self, daemon): + """ + Connect to criu and return socket object. + daemon -- is for whether or not criu should daemonize if executing criu from binary(comm_bin). + """ + pass + + def disconnect(self): + """ + Disconnect from criu. + """ + pass class _criu_comm_sk(_criu_comm): - """ - Communication class for unix socket. - """ - def __init__(self, sk_path): - self.comm_type = self.COMM_SK - self.comm = sk_path - - def connect(self, daemon): - self.sk = socket.socket(socket.AF_UNIX, socket.SOCK_SEQPACKET) - self.sk.connect(self.comm) + """ + Communication class for unix socket. + """ + + def __init__(self, sk_path): + self.comm_type = self.COMM_SK + self.comm = sk_path + + def connect(self, daemon): + self.sk = socket.socket(socket.AF_UNIX, socket.SOCK_SEQPACKET) + self.sk.connect(self.comm) - return self.sk + return self.sk - def disconnect(self): - self.sk.close() + 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 + """ + 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 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) + """ + 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) class CRIUException(Exception): - """ - Exception class for handling and storing criu errors. - """ - typ = None - _str = None + """ + Exception class for handling and storing criu errors. + """ + 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) + """ + Exception class for handling and storing internal errors. + """ + + def __init__(self, typ, s): + self.typ = typ + self._str = "%s failed with internal error: %s" % ( + rpc.criu_req_type.Name(self.typ), s) class CRIUExceptionExternal(CRIUException): - """ - Exception class for handling and storing criu RPC errors. - """ + """ + 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 - - _comm = None #Communication method - - def __init__(self): - self.use_binary('criu') - self.opts = rpc.criu_opts() - self.sk = None - - def use_sk(self, sk_name): - """ - Access criu using unix socket which that belongs to criu service daemon. - """ - self._comm = _criu_comm_sk(sk_name) - - def use_fd(self, fd): - """ - Access criu using provided fd. - """ - self._comm = _criu_comm_fd(fd) - - def use_binary(self, bin_name): - """ - Access criu by execing it using provided path to criu binary. - """ - self._comm = _criu_comm_bin(bin_name) - - def _send_req_and_recv_resp(self, req): - """ - As simple as send request and receive response. - """ - # In case of self-dump we need to spawn criu swrk detached - # from our current process, as criu has a hard time separating - # process resources from its own if criu is located in a same - # process tree it is trying to dump. - daemon = False - if req.type == rpc.DUMP and not req.opts.HasField('pid'): - daemon = True - - try: - if not self.sk: - s = self._comm.connect(daemon) - else: - s = self.sk - - if req.keep_open: - self.sk = s - - s.send(req.SerializeToString()) - - buf = s.recv(len(s.recv(1, socket.MSG_TRUNC | socket.MSG_PEEK))) - - 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)) - - return resp - - def check(self): - """ - Checks whether the kernel support is up-to-date. - """ - req = rpc.criu_req() - req.type = rpc.CHECK - - resp = self._send_req_and_recv_resp(req) - - if not resp.success: - raise CRIUExceptionExternal(req.type, resp.type, resp.cr_errno) - - def dump(self): - """ - Checkpoint a process/tree identified by opts.pid. - """ - req = rpc.criu_req() - req.type = rpc.DUMP - req.opts.MergeFrom(self.opts) - - resp = self._send_req_and_recv_resp(req) - - if not resp.success: - raise CRIUExceptionExternal(req.type, resp.type, resp.cr_errno) - - return resp.dump - - def 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) - - resp = self._send_req_and_recv_resp(req) - - if not resp.success: - raise CRIUExceptionExternal(req.type, resp.type, resp.cr_errno) - - return resp.dump - - def restore(self): - """ - Restore a process/tree. - """ - req = rpc.criu_req() - req.type = rpc.RESTORE - req.opts.MergeFrom(self.opts) - - resp = self._send_req_and_recv_resp(req) - - if not resp.success: - raise CRIUExceptionExternal(req.type, resp.type, resp.cr_errno) - - 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 - - resp = self._send_req_and_recv_resp(req) - - if not resp.success: - raise CRIUExceptionExternal(req.type, resp.type, resp.cr_errno) - - return resp.ps - - def wait_pid(self, pid): - req = rpc.criu_req() - req.type = rpc.WAIT_PID - req.pid = pid + """ + Call criu through RPC. + """ + opts = None #CRIU options in pb format + + _comm = None #Communication method + + def __init__(self): + self.use_binary('criu') + self.opts = rpc.criu_opts() + self.sk = None + + def use_sk(self, sk_name): + """ + Access criu using unix socket which that belongs to criu service daemon. + """ + self._comm = _criu_comm_sk(sk_name) + + def use_fd(self, fd): + """ + Access criu using provided fd. + """ + self._comm = _criu_comm_fd(fd) + + def use_binary(self, bin_name): + """ + Access criu by execing it using provided path to criu binary. + """ + self._comm = _criu_comm_bin(bin_name) + + def _send_req_and_recv_resp(self, req): + """ + As simple as send request and receive response. + """ + # In case of self-dump we need to spawn criu swrk detached + # from our current process, as criu has a hard time separating + # process resources from its own if criu is located in a same + # process tree it is trying to dump. + daemon = False + if req.type == rpc.DUMP and not req.opts.HasField('pid'): + daemon = True + + try: + if not self.sk: + s = self._comm.connect(daemon) + else: + s = self.sk + + if req.keep_open: + self.sk = s + + s.send(req.SerializeToString()) + + buf = s.recv(len(s.recv(1, socket.MSG_TRUNC | socket.MSG_PEEK))) + + 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)) + + return resp + + def check(self): + """ + Checks whether the kernel support is up-to-date. + """ + req = rpc.criu_req() + req.type = rpc.CHECK + + resp = self._send_req_and_recv_resp(req) + + if not resp.success: + raise CRIUExceptionExternal(req.type, resp.type, resp.cr_errno) + + def dump(self): + """ + Checkpoint a process/tree identified by opts.pid. + """ + req = rpc.criu_req() + req.type = rpc.DUMP + req.opts.MergeFrom(self.opts) + + resp = self._send_req_and_recv_resp(req) + + if not resp.success: + raise CRIUExceptionExternal(req.type, resp.type, resp.cr_errno) + + return resp.dump + + def 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) + + resp = self._send_req_and_recv_resp(req) + + if not resp.success: + raise CRIUExceptionExternal(req.type, resp.type, resp.cr_errno) + + return resp.dump + + def restore(self): + """ + Restore a process/tree. + """ + req = rpc.criu_req() + req.type = rpc.RESTORE + req.opts.MergeFrom(self.opts) + + resp = self._send_req_and_recv_resp(req) + + if not resp.success: + raise CRIUExceptionExternal(req.type, resp.type, resp.cr_errno) + + 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 + + resp = self._send_req_and_recv_resp(req) + + if not resp.success: + raise CRIUExceptionExternal(req.type, resp.type, resp.cr_errno) + + return resp.ps + + 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 -Nru criu-3.12/crit/pycriu/images/images.py criu-3.13/crit/pycriu/images/images.py --- criu-3.12/crit/pycriu/images/images.py 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/crit/pycriu/images/images.py 2019-09-11 08:29:31.000000000 +0000 @@ -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 @@ -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): - """ - 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. - """ - 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) - - 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): - """ - Same as dump(), but doesn't take file-like object and just - returns a string. - """ - f = io.BytesIO('') - self.dump(entries, f) - return f.read() - - def count(self, f): - """ - Counts the number of top-level object in the image file - """ - entries = 0 - - while True: - buf = f.read(4) - if buf == '': - break - size, = struct.unpack('i', buf) - f.seek(size, 1) - entries += 1 + """ + Generic class to handle loading/dumping criu images + entries from/to bin format to/from dict(json). + """ + + def __init__(self, payload, extra_handler=None): + """ + Sets payload class and extra handler class. + """ + self.payload = payload + self.extra_handler = extra_handler + + def load(self, f, pretty=False, 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): + """ + Same as load(), but takes a string as an argument. + """ + f = io.BytesIO(s) + return self.load(f, pretty) + + def dump(self, entries, f): + """ + Convert criu image entries from dict(json) format to binary. + Takes a list of entries and a file-like object to write entries + in binary format to. + """ + for entry in entries: + extra = entry.pop('extra', None) + + # Write payload + 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() + + def count(self, f): + """ + Counts the number of top-level object in the image file + """ + entries = 0 + + while True: + buf = f.read(4) + if buf == '': + break + size, = struct.unpack('i', buf) + f.seek(size, 1) + entries += 1 + + return entries - 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)) - - pbuff = pb.pagemap_entry() - - return entries - - def loads(self, s, pretty = False): - f = io.BytesIO(s) - return self.load(f, pretty) - - def dump(self, entries, f): - 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() - - def dumps(self, entries): - f = io.BytesIO('') - self.dump(entries, f) - return f.read() + """ + 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)) + + pbuff = pb.pagemap_entry() + + return entries + + def loads(self, s, pretty=False): + f = io.BytesIO(s) + return self.load(f, pretty) + + def dump(self, entries, f): + 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() + + 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 = [] + 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() + 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 @@ # 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 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 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 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 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 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 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 + 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 - 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)) - - try: - m = magic.by_val[img_magic] - except: - raise MagicException(img_magic) - - try: - handler = handlers[m] - except: - raise Exception("No handler found for image with magic " + m) - - return m, handler - -def load(f, pretty = False, 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 = {} + # Images v1.1 NOTE: First read "first" magic. + img_magic, = struct.unpack('i', f.read(4)) + if img_magic in (magic.by_name['IMG_COMMON'], + magic.by_name['IMG_SERVICE']): + img_magic, = struct.unpack('i', f.read(4)) + + try: + m = magic.by_val[img_magic] + except: + raise MagicException(img_magic) + + try: + handler = handlers[m] + except: + raise Exception("No handler found for image with magic " + m) + + return m, handler + + +def load(f, pretty=False, 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 = {} + + 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): + """ + Same as load(), but takes a string. + """ + f = io.BytesIO(s) + return load(f, pretty) -def loads(s, pretty = False): - """ - Same as load(), but takes a string. - """ - f = io.BytesIO(s) - return load(f, pretty) def dump(img, f): - """ - Convert criu image from dict(json) format to binary. - Takes an image in dict(json) format and file-like - object to write to. - """ - m = img['magic'] - magic_val = magic.by_name[img['magic']] - - # Images v1.1 NOTE: use "second" magic to identify what "first" - # should be written. - if m != 'INVENTORY': - if m in ('STATS', 'IRMAP_CACHE'): - f.write(struct.pack('i', magic.by_name['IMG_SERVICE'])) - else: - f.write(struct.pack('i', magic.by_name['IMG_COMMON'])) - - f.write(struct.pack('i', magic_val)) - - try: - handler = handlers[m] - except: - raise Exception("No handler found for image with such magic") + """ + Convert criu image from dict(json) format to binary. + Takes an image in dict(json) format and file-like + object to write to. + """ + m = img['magic'] + magic_val = magic.by_name[img['magic']] + + # Images v1.1 NOTE: use "second" magic to identify what "first" + # should be written. + if m != 'INVENTORY': + if m in ('STATS', 'IRMAP_CACHE'): + f.write(struct.pack('i', magic.by_name['IMG_SERVICE'])) + else: + f.write(struct.pack('i', magic.by_name['IMG_COMMON'])) + + f.write(struct.pack('i', magic_val)) + + try: + handler = handlers[m] + except: + raise Exception("No handler found for image with such magic") + + handler.dump(img['entries'], f) - 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() + """ + Same as dump(), but takes only an image and returns + a string. + """ + f = io.BytesIO(b'') + dump(img, f) + return f.getvalue() diff -Nru criu-3.12/crit/pycriu/images/pb2dict.py criu-3.13/crit/pycriu/images/pb2dict.py --- criu-3.12/crit/pycriu/images/pb2dict.py 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/crit/pycriu/images/pb2dict.py 2019-09-11 08:29:31.000000000 +0000 @@ -4,7 +4,13 @@ from ipaddress import IPv6Address import socket import collections -import os, six +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: @@ -23,345 +29,396 @@ # 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 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} -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): - return isinstance(value, six.string_types) + # Python 3 compatibility + if "basestring" in __builtins__: + string_types = basestring # noqa: F821 + 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 Exception: + 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) -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[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 Exception: + 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 is 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 + """ + 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 diff -Nru criu-3.12/criu/arch/aarch64/include/asm/restore.h criu-3.13/criu/arch/aarch64/include/asm/restore.h --- criu-3.12/criu/arch/aarch64/include/asm/restore.h 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/criu/arch/aarch64/include/asm/restore.h 2019-09-11 08:29:31.000000000 +0000 @@ -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) { diff -Nru criu-3.12/criu/arch/aarch64/include/asm/vdso.h criu-3.13/criu/arch/aarch64/include/asm/vdso.h --- criu-3.12/criu/arch/aarch64/include/asm/vdso.h 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/criu/arch/aarch64/include/asm/vdso.h 2019-09-11 08:29:31.000000000 +0000 @@ -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 -Nru criu-3.12/criu/arch/aarch64/Makefile criu-3.13/criu/arch/aarch64/Makefile --- criu-3.12/criu/arch/aarch64/Makefile 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/criu/arch/aarch64/Makefile 2019-09-11 08:29:31.000000000 +0000 @@ -1,9 +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) -asflags-y += -D__ASSEMBLY__ ldflags-y += -r obj-y += cpu.o diff -Nru criu-3.12/criu/arch/arm/aeabi-helpers.S criu-3.13/criu/arch/arm/aeabi-helpers.S --- criu-3.12/criu/arch/arm/aeabi-helpers.S 1970-01-01 00:00:00.000000000 +0000 +++ criu-3.13/criu/arch/arm/aeabi-helpers.S 2019-09-11 08:29:31.000000000 +0000 @@ -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 -Nru criu-3.12/criu/arch/arm/include/asm/vdso.h criu-3.13/criu/arch/arm/include/asm/vdso.h --- criu-3.12/criu/arch/arm/include/asm/vdso.h 1970-01-01 00:00:00.000000000 +0000 +++ criu-3.13/criu/arch/arm/include/asm/vdso.h 2019-09-11 08:29:31.000000000 +0000 @@ -0,0 +1,18 @@ +#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 VDSO_SYMBOL_GTOD 1 +#define ARCH_VDSO_SYMBOLS \ + "__vdso_clock_gettime", \ + "__vdso_gettimeofday" + +#endif /* __CR_ASM_VDSO_H__ */ diff -Nru criu-3.12/criu/arch/arm/Makefile criu-3.13/criu/arch/arm/Makefile --- criu-3.12/criu/arch/arm/Makefile 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/criu/arch/arm/Makefile 2019-09-11 08:29:31.000000000 +0000 @@ -1,10 +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 += -D__ASSEMBLY__ ldflags-y += -r -z noexecstack obj-y += cpu.o diff -Nru criu-3.12/criu/arch/arm/pie-cacheflush.c criu-3.13/criu/arch/arm/pie-cacheflush.c --- criu-3.12/criu/arch/arm/pie-cacheflush.c 1970-01-01 00:00:00.000000000 +0000 +++ criu-3.13/criu/arch/arm/pie-cacheflush.c 2019-09-11 08:29:31.000000000 +0000 @@ -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 -Nru criu-3.12/criu/arch/arm/uidiv.S criu-3.13/criu/arch/arm/uidiv.S --- criu-3.12/criu/arch/arm/uidiv.S 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/criu/arch/arm/uidiv.S 1970-01-01 00:00:00.000000000 +0000 @@ -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 -Nru criu-3.12/criu/arch/arm/vdso-pie.c criu-3.13/criu/arch/arm/vdso-pie.c --- criu-3.12/criu/arch/arm/vdso-pie.c 1970-01-01 00:00:00.000000000 +0000 +++ criu-3.13/criu/arch/arm/vdso-pie.c 2019-09-11 08:29:31.000000000 +0000 @@ -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; +} diff -Nru criu-3.12/criu/arch/ppc64/include/asm/vdso.h criu-3.13/criu/arch/ppc64/include/asm/vdso.h --- criu-3.12/criu/arch/ppc64/include/asm/vdso.h 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/criu/arch/ppc64/include/asm/vdso.h 2019-09-11 08:29:31.000000000 +0000 @@ -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 -Nru criu-3.12/criu/arch/ppc64/Makefile criu-3.13/criu/arch/ppc64/Makefile --- criu-3.12/criu/arch/ppc64/Makefile 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/criu/arch/ppc64/Makefile 2019-09-11 08:29:31.000000000 +0000 @@ -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 -Nru criu-3.12/criu/arch/s390/include/asm/vdso.h criu-3.13/criu/arch/s390/include/asm/vdso.h --- criu-3.12/criu/arch/s390/include/asm/vdso.h 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/criu/arch/s390/include/asm/vdso.h 2019-09-11 08:29:31.000000000 +0000 @@ -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 -Nru criu-3.12/criu/arch/s390/Makefile criu-3.13/criu/arch/s390/Makefile --- criu-3.12/criu/arch/s390/Makefile 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/criu/arch/s390/Makefile 2019-09-11 08:29:31.000000000 +0000 @@ -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 -Nru criu-3.12/criu/arch/x86/crtools.c criu-3.13/criu/arch/x86/crtools.c --- criu-3.12/criu/arch/x86/crtools.c 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/criu/arch/x86/crtools.c 2019-09-11 08:29:31.000000000 +0000 @@ -354,7 +354,7 @@ }; 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) { diff -Nru criu-3.12/criu/arch/x86/include/asm/parasite.h criu-3.13/criu/arch/x86/include/asm/parasite.h --- criu-3.12/criu/arch/x86/include/asm/parasite.h 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/criu/arch/x86/include/asm/parasite.h 2019-09-11 08:29:31.000000000 +0000 @@ -28,8 +28,8 @@ * }; */ 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 -Nru criu-3.12/criu/arch/x86/include/asm/restorer.h criu-3.13/criu/arch/x86/include/asm/restorer.h --- criu-3.12/criu/arch/x86/include/asm/restorer.h 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/criu/arch/x86/include/asm/restorer.h 2019-09-11 08:29:31.000000000 +0000 @@ -25,12 +25,12 @@ } #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 @@ "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" \ @@ -72,14 +72,6 @@ : "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 -Nru criu-3.12/criu/arch/x86/include/asm/vdso.h criu-3.13/criu/arch/x86/include/asm/vdso.h --- criu-3.12/criu/arch/x86/include/asm/vdso.h 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/criu/arch/x86/include/asm/vdso.h 2019-09-11 08:29:31.000000000 +0000 @@ -12,16 +12,62 @@ * This is a minimal amount of symbols * we should support at the moment. */ -#define VDSO_SYMBOL_MAX 7 +#define VDSO_SYMBOL_MAX 6 +#define VDSO_SYMBOL_GTOD 2 +/* + * 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 + +#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 -Nru criu-3.12/criu/arch/x86/Makefile criu-3.13/criu/arch/x86/Makefile --- criu-3.12/criu/arch/x86/Makefile 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/criu/arch/x86/Makefile 2019-09-11 08:29:31.000000000 +0000 @@ -1,11 +1,7 @@ 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 += -D__ASSEMBLY__ -nostdlib -fomit-frame-pointer +asflags-y += -nostdlib -fomit-frame-pointer asflags-y += -iquote $(obj)/include ldflags-y += -r -z noexecstack diff -Nru criu-3.12/criu/autofs.c criu-3.13/criu/autofs.c --- criu-3.12/criu/autofs.c 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/criu/autofs.c 2019-09-11 08:29:31.000000000 +0000 @@ -110,8 +110,10 @@ 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 @@ { char **opts; int nr_opts, i; + int parse_error = 0; entry->fd = AUTOFS_OPT_UNKNOWN; entry->timeout = AUTOFS_OPT_UNKNOWN; @@ -250,14 +253,19 @@ 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 @@ 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_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 @@ 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; } diff -Nru criu-3.12/criu/cgroup.c criu-3.13/criu/cgroup.c --- criu-3.12/criu/cgroup.c 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/criu/cgroup.c 2019-09-11 08:29:31.000000000 +0000 @@ -818,6 +818,7 @@ 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++; diff -Nru criu-3.12/criu/clone-noasan.c criu-3.13/criu/clone-noasan.c --- criu-3.12/criu/clone-noasan.c 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/criu/clone-noasan.c 2019-09-11 08:29:31.000000000 +0000 @@ -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 diff -Nru criu-3.12/criu/config.c criu-3.13/criu/config.c --- criu-3.12/criu/config.c 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/criu/config.c 2019-09-11 08:29:31.000000000 +0000 @@ -368,6 +368,8 @@ 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; @@ -508,6 +510,12 @@ { "ps-socket", required_argument, 0, 1091}, { "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), + {"tls-no-cn-verify", no_argument, &opts.tls_no_cn_verify, true}, { }, }; @@ -794,6 +802,18 @@ 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")) @@ -844,9 +864,23 @@ 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; + } + } + +#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"); diff -Nru criu-3.12/criu/cr-check.c criu-3.13/criu/cr-check.c --- criu-3.12/criu/cr-check.c 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/criu/cr-check.c 2019-09-11 08:29:31.000000000 +0000 @@ -468,7 +468,7 @@ } #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) @@ -582,7 +582,7 @@ 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 @@ 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 @@ 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. diff -Nru criu-3.12/criu/cr-dump.c criu-3.13/criu/cr-dump.c --- criu-3.12/criu/cr-dump.c 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/criu/cr-dump.c 2019-09-11 08:29:31.000000000 +0000 @@ -108,8 +108,7 @@ 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, @@ -126,7 +125,7 @@ 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"); @@ -726,7 +725,8 @@ 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]; @@ -740,6 +740,9 @@ 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; @@ -1138,8 +1141,7 @@ 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); @@ -1220,8 +1222,7 @@ 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); @@ -1378,7 +1379,7 @@ 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 -Nru criu-3.12/criu/cr-restore.c criu-3.13/criu/cr-restore.c --- criu-3.12/criu/cr-restore.c 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/criu/cr-restore.c 2019-09-11 08:29:31.000000000 +0000 @@ -390,10 +390,10 @@ 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) @@ -726,6 +726,40 @@ 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; @@ -806,10 +840,13 @@ 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) { @@ -877,7 +914,10 @@ if (collect_zombie_pids(ta) < 0) return -1; - if (prepare_proc_misc(pid, core->tc)) + if (collect_inotify_fds(ta) < 0) + return -1; + + if (prepare_proc_misc(pid, core->tc, ta)) return -1; /* @@ -2353,6 +2393,9 @@ 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; @@ -2994,6 +3037,8 @@ 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); @@ -3059,6 +3104,40 @@ 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. */ @@ -3189,10 +3268,8 @@ 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; @@ -3243,7 +3320,6 @@ 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 @@ -3255,7 +3331,6 @@ 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 @@ -3373,6 +3448,7 @@ 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); /* @@ -3470,7 +3546,6 @@ } -#ifdef CONFIG_VDSO /* * Restorer needs own copy of vdso parameters. Runtime * vdso must be kept non intersecting with anything else, @@ -3482,7 +3557,6 @@ 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 -Nru criu-3.12/criu/cr-service.c criu-3.13/criu/cr-service.c --- criu-3.12/criu/cr-service.c 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/criu/cr-service.c 2019-09-11 08:29:31.000000000 +0000 @@ -608,6 +608,19 @@ 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; @@ -1156,7 +1169,7 @@ 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; } @@ -1302,7 +1315,8 @@ 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); diff -Nru criu-3.12/criu/crtools.c criu-3.13/criu/crtools.c --- criu-3.12/criu/crtools.c 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/criu/crtools.c 2019-09-11 08:29:31.000000000 +0000 @@ -95,8 +95,10 @@ 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, "."); @@ -345,7 +347,8 @@ " --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" @@ -424,6 +427,12 @@ " -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" +" --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 -Nru criu-3.12/criu/files.c criu-3.13/criu/files.c --- criu-3.12/criu/files.c 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/criu/files.c 2019-09-11 08:29:31.000000000 +0000 @@ -1247,6 +1247,8 @@ 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; } diff -Nru criu-3.12/criu/files-reg.c criu-3.13/criu/files-reg.c --- criu-3.12/criu/files-reg.c 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/criu/files-reg.c 2019-09-11 08:29:31.000000000 +0000 @@ -735,6 +735,7 @@ 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; @@ -771,7 +772,7 @@ } if (pb_write_one(img, &gfe, PB_GHOST_FILE)) - return -1; + goto err_out; if (S_ISREG(st->st_mode)) { int fd, ret; @@ -785,7 +786,7 @@ fd = open(lpath, O_RDONLY); if (fd < 0) { pr_perror("Can't open ghost original file"); - return -1; + goto err_out; } if (gfe.chunks) @@ -794,11 +795,13 @@ 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) @@ -1553,6 +1556,9 @@ } 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; @@ -1562,6 +1568,8 @@ } 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 @@ -1704,12 +1712,10 @@ } 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; } /* diff -Nru criu-3.12/criu/filesystems.c criu-3.13/criu/filesystems.c --- criu-3.12/criu/filesystems.c 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/criu/filesystems.c 2019-09-11 08:29:31.000000000 +0000 @@ -58,7 +58,7 @@ char *str; str = breadline(f); - if (IS_ERR(str)) + if (IS_ERR(str)) return -1; if (!str) break; diff -Nru criu-3.12/criu/fsnotify.c criu-3.13/criu/fsnotify.c --- criu-3.12/criu/fsnotify.c 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/criu/fsnotify.c 2019-09-11 08:29:31.000000000 +0000 @@ -175,7 +175,7 @@ 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 @@ 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 @@ 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 @@ } 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; diff -Nru criu-3.12/criu/image.c criu-3.13/criu/image.c --- criu-3.12/criu/image.c 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/criu/image.c 2019-09-11 08:29:31.000000000 +0000 @@ -92,6 +92,7 @@ int write_img_inventory(InventoryEntry *he) { struct cr_img *img; + int ret; pr_info("Writing image inventory (version %u)\n", CRTOOLS_IMAGES_V1); @@ -99,11 +100,12 @@ 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; } @@ -123,6 +125,18 @@ 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; @@ -131,7 +145,15 @@ 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 -Nru criu-3.12/criu/include/aio.h criu-3.13/criu/include/aio.h --- criu-3.12/criu/include/aio.h 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/criu/include/aio.h 2019-09-11 08:29:31.000000000 +0000 @@ -13,18 +13,18 @@ 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 -Nru criu-3.12/criu/include/asm-generic/vdso.h criu-3.13/criu/include/asm-generic/vdso.h --- criu-3.12/criu/include/asm-generic/vdso.h 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/criu/include/asm-generic/vdso.h 2019-09-11 08:29:31.000000000 +0000 @@ -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 -Nru criu-3.12/criu/include/autofs.h criu-3.13/criu/include/autofs.h --- criu-3.12/criu/include/autofs.h 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/criu/include/autofs.h 2019-09-11 08:29:31.000000000 +0000 @@ -20,70 +20,70 @@ #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 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 -Nru criu-3.12/criu/include/cr_options.h criu-3.13/criu/include/cr_options.h --- criu-3.12/criu/include/cr_options.h 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/criu/include/cr_options.h 2019-09-11 08:29:31.000000000 +0000 @@ -138,6 +138,12 @@ pid_t tree_id; int log_level; char *imgs_dir; + char *tls_cacert; + char *tls_cacrl; + char *tls_cert; + char *tls_key; + int tls; + int tls_no_cn_verify; }; extern struct cr_options opts; diff -Nru criu-3.12/criu/include/image.h criu-3.13/criu/include/image.h --- criu-3.12/criu/include/image.h 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/criu/include/image.h 2019-09-11 08:29:31.000000000 +0000 @@ -133,6 +133,8 @@ static inline int img_raw_fd(struct cr_img *img) { + if (!img) + return -1; if (lazy_image(img) && open_image_lazy(img)) return -1; diff -Nru criu-3.12/criu/include/kerndat.h criu-3.13/criu/include/kerndat.h --- criu-3.12/criu/include/kerndat.h 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/criu/include/kerndat.h 2019-09-11 08:29:31.000000000 +0000 @@ -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; @@ -17,10 +15,6 @@ */ extern int kerndat_init(void); -extern int kerndat_get_dirty_track(void); -extern int kerndat_fdinfo_has_lock(void); -extern int kerndat_loginuid(void); -extern int kerndat_files_stat(bool early); enum pagemap_func { PM_UNKNOWN, @@ -61,16 +55,13 @@ 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; unsigned int sysctl_nr_open; - unsigned long files_stat_max_files; bool x86_has_ptrace_fpu_xsave_bug; bool has_inotify_setnextwd; bool has_kcmp_epoll_tfd; @@ -94,7 +85,4 @@ */ extern int kerndat_fs_virtualized(unsigned int which, u32 kdev); -extern int kerndat_tcp_repair(); -extern int kerndat_uffd(void); - #endif /* __CR_KERNDAT_H__ */ diff -Nru criu-3.12/criu/include/log.h criu-3.13/criu/include/log.h --- criu-3.12/criu/include/log.h 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/criu/include/log.h 2019-09-11 08:29:31.000000000 +0000 @@ -30,6 +30,8 @@ # define LOG_PREFIX #endif +void flush_early_log_buffer(int fd); + #define print_once(loglevel, fmt, ...) \ do { \ static bool __printed; \ diff -Nru criu-3.12/criu/include/lsm.h criu-3.13/criu/include/lsm.h --- criu-3.12/criu/include/lsm.h 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/criu/include/lsm.h 2019-09-11 08:29:31.000000000 +0000 @@ -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 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 -Nru criu-3.12/criu/include/packet_diag.h criu-3.13/criu/include/packet_diag.h --- criu-3.12/criu/include/packet_diag.h 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/criu/include/packet_diag.h 2019-09-11 08:29:31.000000000 +0000 @@ -12,18 +12,18 @@ __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 @@ }; struct packet_diag_info { - __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 + __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 }; #ifndef MAX_ADDR_LEN @@ -56,21 +56,21 @@ #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 -Nru criu-3.12/criu/include/page-pipe.h criu-3.13/criu/include/page-pipe.h --- criu-3.12/criu/include/page-pipe.h 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/criu/include/page-pipe.h 2019-09-11 08:29:31.000000000 +0000 @@ -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; }; /* @@ -91,15 +91,15 @@ */ 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 @@ #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 diff -Nru criu-3.12/criu/include/parasite.h criu-3.13/criu/include/parasite.h --- criu-3.12/criu/include/parasite.h 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/criu/include/parasite.h 2019-09-11 08:29:31.000000000 +0000 @@ -126,6 +126,7 @@ int dumpable; int thp_disabled; + int child_subreaper; }; /* diff -Nru criu-3.12/criu/include/parasite-vdso.h criu-3.13/criu/include/parasite-vdso.h --- criu-3.12/criu/include/parasite-vdso.h 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/criu/include/parasite-vdso.h 2019-09-11 08:29:31.000000000 +0000 @@ -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" @@ -84,21 +81,15 @@ 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); -extern int vdso_proxify(struct vdso_symtable *sym_rt, - 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, 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 -Nru criu-3.12/criu/include/ptrace-compat.h criu-3.13/criu/include/ptrace-compat.h --- criu-3.12/criu/include/ptrace-compat.h 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/criu/include/ptrace-compat.h 2019-09-11 08:29:31.000000000 +0000 @@ -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 -Nru criu-3.12/criu/include/restorer.h criu-3.13/criu/include/restorer.h --- criu-3.12/criu/include/restorer.h 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/criu/include/restorer.h 2019-09-11 08:29:31.000000000 +0000 @@ -69,8 +69,10 @@ 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; @@ -175,6 +177,9 @@ 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; @@ -205,11 +210,9 @@ 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; @@ -217,6 +220,7 @@ unsigned page_size; #endif int lsm_type; + int child_subreaper; } __aligned(64); /* diff -Nru criu-3.12/criu/include/servicefd.h criu-3.13/criu/include/servicefd.h --- criu-3.12/criu/include/servicefd.h 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/criu/include/servicefd.h 2019-09-11 08:29:31.000000000 +0000 @@ -35,17 +35,6 @@ 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 -Nru criu-3.12/criu/include/sockets.h criu-3.13/criu/include/sockets.h --- criu-3.12/criu/include/sockets.h 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/criu/include/sockets.h 2019-09-11 08:29:31.000000000 +0000 @@ -92,7 +92,7 @@ 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 -Nru criu-3.12/criu/include/stats.h criu-3.13/criu/include/stats.h --- criu-3.12/criu/include/stats.h 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/criu/include/stats.h 2019-09-11 08:29:31.000000000 +0000 @@ -29,6 +29,10 @@ CNT_PAGE_PIPES, CNT_PAGE_PIPE_BUFS, + CNT_SHPAGES_SCANNED, + CNT_SHPAGES_SKIPPED_PARENT, + CNT_SHPAGES_WRITTEN, + DUMP_CNT_NR_STATS, }; diff -Nru criu-3.12/criu/include/tls.h criu-3.13/criu/include/tls.h --- criu-3.12/criu/include/tls.h 1970-01-01 00:00:00.000000000 +0000 +++ criu-3.13/criu/include/tls.h 2019-09-11 08:29:31.000000000 +0000 @@ -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 -Nru criu-3.12/criu/include/tty.h criu-3.13/criu/include/tty.h --- criu-3.12/criu/include/tty.h 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/criu/include/tty.h 2019-09-11 08:29:31.000000000 +0000 @@ -32,6 +32,7 @@ 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 -Nru criu-3.12/criu/include/util.h criu-3.13/criu/include/util.h --- criu-3.12/criu/include/util.h 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/criu/include/util.h 2019-09-11 08:29:31.000000000 +0000 @@ -176,7 +176,7 @@ 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 -Nru criu-3.12/criu/include/util-pie.h criu-3.13/criu/include/util-pie.h --- criu-3.12/criu/include/util-pie.h 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/criu/include/util-pie.h 2019-09-11 08:29:31.000000000 +0000 @@ -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 -Nru criu-3.12/criu/include/util-vdso.h criu-3.13/criu/include/util-vdso.h --- criu-3.12/criu/include/util-vdso.h 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/criu/include/util-vdso.h 2019-09-11 08:29:31.000000000 +0000 @@ -38,6 +38,7 @@ unsigned long vdso_start; unsigned long vvar_start; struct vdso_symtable sym; + bool compatible; }; #define VDSO_SYMBOL_INIT { .offset = VDSO_BAD_ADDR, } @@ -75,6 +76,8 @@ #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 +95,6 @@ #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 -Nru criu-3.12/criu/include/vdso.h criu-3.13/criu/include/vdso.h --- criu-3.12/criu/include/vdso.h 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/criu/include/vdso.h 2019-09-11 08:29:31.000000000 +0000 @@ -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 @@ 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 -Nru criu-3.12/criu/include/vma.h criu-3.13/criu/include/vma.h --- criu-3.12/criu/include/vma.h 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/criu/include/vma.h 2019-09-11 08:29:31.000000000 +0000 @@ -7,25 +7,24 @@ #include "images/vma.pb-c.h" #include +#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 */ }; -#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)); INIT_LIST_HEAD(&vml->h); - vml->nr = 0; - vml->priv_size = 0; - vml->priv_longest = 0; - vml->shared_longest = 0; } struct file_desc; diff -Nru criu-3.12/criu/kerndat.c criu-3.13/criu/kerndat.c --- criu-3.12/criu/kerndat.c 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/criu/kerndat.c 2019-09-11 08:29:31.000000000 +0000 @@ -157,70 +157,28 @@ (unsigned long)kdat.mmap_min_addr); } -int kerndat_files_stat(bool early) +static int kerndat_files_stat(void) { static const uint32_t NR_OPEN_DEFAULT = 1024 * 1024; - static const uint64_t MAX_FILES_DEFAULT = 8192; - uint64_t max_files; uint32_t nr_open; struct sysctl_req req[] = { { - .name = "fs/file-max", - .arg = &max_files, - .type = CTL_U64, - }, - { .name = "fs/nr_open", .arg = &nr_open, .type = CTL_U32, }, }; - if (!early) { - if (sysctl_op(req, ARRAY_SIZE(req), CTL_READ, 0)) { - pr_warn("Can't fetch file_stat, using kernel defaults\n"); - nr_open = NR_OPEN_DEFAULT; - max_files = MAX_FILES_DEFAULT; - } - } else { - char buf[64]; - int fd1, fd2; - ssize_t ret; - - fd1 = open("/proc/sys/fs/file-max", O_RDONLY); - fd2 = open("/proc/sys/fs/nr_open", O_RDONLY); - + if (sysctl_op(req, ARRAY_SIZE(req), CTL_READ, 0)) { + pr_warn("Can't fetch file_stat, using kernel defaults\n"); nr_open = NR_OPEN_DEFAULT; - max_files = MAX_FILES_DEFAULT; - - if (fd1 < 0 || fd2 < 0) { - pr_warn("Can't fetch file_stat, using kernel defaults\n"); - } else { - ret = read(fd1, buf, sizeof(buf) - 1); - if (ret > 0) { - buf[ret] = '\0'; - max_files = atol(buf); - } - ret = read(fd2, buf, sizeof(buf) - 1); - if (ret > 0) { - buf[ret] = '\0'; - nr_open = atol(buf); - } - } - - if (fd1 >= 0) - close(fd1); - if (fd2 >= 0) - close(fd2); } kdat.sysctl_nr_open = nr_open; - kdat.files_stat_max_files = max_files; - pr_debug("files stat: %s %lu, %s %u\n", - req[0].name, kdat.files_stat_max_files, - req[1].name, kdat.sysctl_nr_open); + pr_debug("files stat: %s %u\n", + req[0].name, kdat.sysctl_nr_open); return 0; } @@ -348,7 +306,7 @@ * this functionality under CONFIG_MEM_SOFT_DIRTY option. */ -int kerndat_get_dirty_track(void) +static int kerndat_get_dirty_track(void) { char *map; int pm2; @@ -471,7 +429,7 @@ return 0; } -int kerndat_fdinfo_has_lock() +static int kerndat_fdinfo_has_lock() { int fd, pfd = -1, exit_code = -1, len; char buf[PAGE_SIZE]; @@ -521,7 +479,7 @@ return 0; } -int kerndat_loginuid(void) +static int kerndat_loginuid(void) { unsigned int saved_loginuid; int ret; @@ -742,7 +700,7 @@ return ret; } -int kerndat_has_inotify_setnextwd(void) +static int kerndat_has_inotify_setnextwd(void) { int ret = 0; int fd; @@ -765,7 +723,7 @@ return ret; } -int has_kcmp_epoll_tfd(void) +static int has_kcmp_epoll_tfd(void) { kcmp_epoll_slot_t slot = { }; int ret = -1, efd, tfd; @@ -907,7 +865,7 @@ } } -int kerndat_uffd(void) +static int kerndat_uffd(void) { int uffd; @@ -1088,7 +1046,7 @@ kerndat_lsm(); kerndat_mmap_min_addr(); - kerndat_files_stat(false); + kerndat_files_stat(); if (!ret) kerndat_save_cache(); diff -Nru criu-3.12/criu/libnetlink.c criu-3.13/criu/libnetlink.c --- criu-3.12/criu/libnetlink.c 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/criu/libnetlink.c 2019-09-11 08:29:31.000000000 +0000 @@ -222,5 +222,5 @@ int32_t nla_get_s32(const struct nlattr *nla) { - return *(const int32_t *) nla_data(nla); + return *(const int32_t *) nla_data(nla); } diff -Nru criu-3.12/criu/log.c criu-3.13/criu/log.c --- criu-3.12/criu/log.c 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/criu/log.c 2019-09-11 08:29:31.000000000 +0000 @@ -170,7 +170,7 @@ uint16_t len; }; -static void flush_early_log_buffer(int fd) +void flush_early_log_buffer(int fd) { unsigned int pos = 0; int ret; @@ -320,7 +320,7 @@ 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 */ diff -Nru criu-3.12/criu/lsm.c criu-3.13/criu/lsm.c --- criu-3.12/criu/lsm.c 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/criu/lsm.c 2019-09-11 08:29:31.000000000 +0000 @@ -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 @@ -30,8 +33,8 @@ return -1; if (fscanf(f, "%ms", profile_name) != 1) { - fclose(f); pr_perror("err scanfing"); + fclose(f); return -1; } @@ -86,6 +89,7 @@ if (!pos) { pr_err("Invalid selinux context %s\n", (char *)ctx); xfree(*output); + *output = NULL; goto err; } @@ -98,6 +102,101 @@ 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; + int ret; + + f = fopen_proc(pid, "attr/sockcreate"); + if (!f) + return -1; + + 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; +} + +int reset_setsockcreatecon() +{ + /* 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) +{ + 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. */ + 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) +{ + 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 security.selinux from FD %d failed\n", 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) @@ -132,6 +231,7 @@ int ret; ce->lsm_profile = NULL; + ce->lsm_sockcreate = NULL; switch (kdat.lsm) { case LSMTYPE__NO_LSM: @@ -143,6 +243,9 @@ #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 +256,8 @@ 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 -Nru criu-3.12/criu/Makefile criu-3.13/criu/Makefile --- criu-3.12/criu/Makefile 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/criu/Makefile 2019-09-11 08:29:31.000000000 +0000 @@ -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) @@ -7,30 +7,28 @@ 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) + CFLAGS_PIE += $(shell $(COMPEL_BIN) cflags) 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/"' # # 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 -Nru criu-3.12/criu/Makefile.crtools criu-3.13/criu/Makefile.crtools --- criu-3.12/criu/Makefile.crtools 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/criu/Makefile.crtools 2019-09-11 08:29:31.000000000 +0000 @@ -1,5 +1,3 @@ -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 @@ -75,6 +73,7 @@ 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 @@ -85,15 +84,12 @@ 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-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) -endif PROTOBUF_GEN := scripts/protobuf-gen.sh diff -Nru criu-3.12/criu/Makefile.packages criu-3.13/criu/Makefile.packages --- criu-3.12/criu/Makefile.packages 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/criu/Makefile.packages 2019-09-11 08:29:31.000000000 +0000 @@ -11,7 +11,7 @@ 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 -Nru criu-3.12/criu/mem.c criu-3.13/criu/mem.c --- criu-3.12/criu/mem.c 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/criu/mem.c 2019-09-11 08:29:31.000000000 +0000 @@ -81,7 +81,7 @@ /* 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) @@ -101,7 +101,6 @@ 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 @@ */ if (vma_entry_is(vmae, VMA_AREA_VVAR)) return false; -#endif + /* * Optimisation for private mapping pages, that haven't * yet being COW-ed @@ -149,7 +148,7 @@ 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; } @@ -170,13 +169,14 @@ 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 +195,22 @@ 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 +222,7 @@ 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, @@ -315,7 +318,7 @@ 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; } @@ -411,14 +414,14 @@ 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; @@ -430,7 +433,7 @@ * 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) @@ -594,7 +597,6 @@ if (!vma) break; - ret = 0; ri->vmas.nr++; if (!img) vma->e = ri->mm->vmas[vn++]; @@ -603,15 +605,16 @@ if (ret <= 0) { xfree(vma); close_image(img); + img = NULL; break; } } 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); @@ -629,6 +632,8 @@ break; } + if (img) + close_image(img); return ret; } @@ -1166,17 +1171,17 @@ 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 -Nru criu-3.12/criu/mount.c criu-3.13/criu/mount.c --- criu-3.12/criu/mount.c 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/criu/mount.c 2019-09-11 08:29:31.000000000 +0000 @@ -2325,9 +2325,9 @@ * mi->shared_id && !shared - create a new shared group */ if (restore_shared_options(mi, private, - mi->shared_id && !shared, - mi->master_id && !master)) - return -1; + mi->shared_id && !shared, + mi->master_id && !master)) + goto err; mi->mounted = true; exit_code = 0; diff -Nru criu-3.12/criu/net.c criu-3.13/criu/net.c --- criu-3.12/criu/net.c 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/criu/net.c 2019-09-11 08:29:31.000000000 +0000 @@ -344,7 +344,7 @@ * 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) { @@ -2765,7 +2765,7 @@ 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; } } @@ -3019,22 +3019,22 @@ #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 -Nru criu-3.12/criu/pagemap.c criu-3.13/criu/pagemap.c --- criu-3.12/criu/pagemap.c 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/criu/pagemap.c 2019-09-11 08:29:31.000000000 +0000 @@ -86,11 +86,15 @@ 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) @@ -105,7 +109,8 @@ 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) @@ -458,6 +463,7 @@ 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) @@ -677,11 +683,13 @@ 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; } } diff -Nru criu-3.12/criu/pagemap-cache.c criu-3.13/criu/pagemap-cache.c --- criu-3.12/criu/pagemap-cache.c 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/criu/pagemap-cache.c 2019-09-11 08:29:31.000000000 +0000 @@ -25,7 +25,7 @@ /* * It's a workaround for a kernel bug. In the 3.19 kernel when pagemap are read * for a few vma-s for one read call, it returns incorrect data. - * https://github.com/xemul/criu/issues/207 + * https://github.com/checkpoint-restore/criu/issues/207 */ static bool pagemap_cache_disabled; @@ -56,7 +56,7 @@ 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) { /* @@ -105,8 +105,8 @@ 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 @@ -119,12 +119,12 @@ * 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; - 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 @@ 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 @@ * 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); diff -Nru criu-3.12/criu/page-pipe.c criu-3.13/criu/page-pipe.c --- criu-3.12/criu/page-pipe.c 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/criu/page-pipe.c 2019-09-11 08:29:31.000000000 +0000 @@ -104,8 +104,6 @@ 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 @@ } 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++; } @@ -188,26 +187,18 @@ 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; @@ -307,14 +298,12 @@ 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; @@ -398,7 +387,7 @@ 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 @@ -427,13 +416,13 @@ 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; } diff -Nru criu-3.12/criu/page-xfer.c criu-3.13/criu/page-xfer.c --- criu-3.12/criu/page-xfer.c 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/criu/page-xfer.c 2019-09-11 08:29:31.000000000 +0000 @@ -7,6 +7,9 @@ #include #include +#undef LOG_PREFIX +#define LOG_PREFIX "page-xfer: " + #include "types.h" #include "cr_options.h" #include "servicefd.h" @@ -21,6 +24,7 @@ #include "parasite-syscall.h" #include "rst_info.h" #include "stats.h" +#include "tls.h" static int page_server_sk = -1; @@ -128,13 +132,22 @@ 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; } @@ -149,17 +162,28 @@ { 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; @@ -205,7 +229,7 @@ /* 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; } @@ -539,7 +563,7 @@ 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; } @@ -560,7 +584,7 @@ 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; } @@ -624,8 +648,7 @@ 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; @@ -684,14 +707,23 @@ 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)) @@ -733,9 +765,16 @@ 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); @@ -773,7 +812,7 @@ 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; @@ -823,7 +862,7 @@ * 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; } @@ -856,14 +895,15 @@ * 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); @@ -985,7 +1025,6 @@ 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; @@ -1012,6 +1051,11 @@ 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); @@ -1035,6 +1079,11 @@ page_server_sk = setup_tcp_client(opts.addr); 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 @@ -1077,14 +1126,16 @@ 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; } @@ -1161,10 +1212,14 @@ 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 -Nru criu-3.12/criu/pie/Makefile criu-3.13/criu/pie/Makefile --- criu-3.12/criu/pie/Makefile 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/criu/pie/Makefile 2019-09-11 08:29:31.000000000 +0000 @@ -1,26 +1,22 @@ +# 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)) -ccflags-y += $(COMPEL_UAPI_INCLUDES) -ccflags-y += $(CFLAGS_PIE) -ccflags-y += -DCR_NOGLIBC +CFLAGS += $(CFLAGS_PIE) 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 - -ifeq ($(SRCARCH),arm) - ccflags-y += -marm + LDFLAGS += $(shell $(COMPEL_BIN) ldflags) + compel_plugins := $(shell $(COMPEL_BIN) plugins) endif -asflags-y += -D__ASSEMBLY__ - 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) @@ -30,6 +26,14 @@ endif endif +ifeq ($(SRCARCH),aarch64) + restorer-obj-y += ./$(ARCH_DIR)/intraprocedure.o +endif + +ifeq ($(SRCARCH),ppc64) + restorer-obj-y += ./$(ARCH_DIR)/vdso-trampoline.o +endif + define gen-pie-rules $(1)-obj-y += $(1).o $(1)-obj-e += pie.lib.a diff -Nru criu-3.12/criu/pie/Makefile.library criu-3.13/criu/pie/Makefile.library --- criu-3.12/criu/pie/Makefile.library 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/criu/pie/Makefile.library 2019-09-11 08:29:31.000000000 +0000 @@ -1,24 +1,13 @@ -lib-name := pie.lib.a +# 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 -CFLAGS += -fno-stack-protector -DCR_NOGLIBC -fpie +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 -endif - -ifeq ($(SRCARCH),ppc64) - lib-y += ./$(ARCH_DIR)/misc.o -endif +lib-y += util-vdso.o ifeq ($(SRCARCH),x86) ifeq ($(CONFIG_COMPAT),y) @@ -27,19 +16,10 @@ 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__ -ccflags-y += $(COMPEL_UAPI_INCLUDES) -ccflags-y += $(CFLAGS_PIE) - ifeq ($(SRCARCH),arm) - ccflags-y += -marm + lib-y += ./$(ARCH_DIR)/aeabi-helpers.o + lib-y += ./$(ARCH_DIR)/pie-cacheflush.o endif +CFLAGS := $(filter-out -pg $(CFLAGS-GCOV) $(CFLAGS-ASAN),$(CFLAGS)) +CFLAGS += $(CFLAGS_PIE) diff -Nru criu-3.12/criu/pie/parasite.c criu-3.13/criu/pie/parasite.c --- criu-3.12/criu/pie/parasite.c 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/criu/pie/parasite.c 2019-09-11 08:29:31.000000000 +0000 @@ -39,6 +39,10 @@ #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_misc(struct parasite_dump_misc *args) { + int ret; + args->brk = sys_brk(0); args->pid = sys_getpid(); @@ -212,7 +218,11 @@ 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) @@ -283,7 +293,7 @@ args->uids[3] = sys_setfsuid(-1L); /* - * FIXME In https://github.com/xemul/criu/issues/95 it is + * FIXME In https://github.com/checkpoint-restore/criu/issues/95 it is * been reported that only low 16 bits are set upon syscall * on ARMv7. * @@ -573,7 +583,6 @@ #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 +618,6 @@ 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 -Nru criu-3.12/criu/pie/parasite-vdso.c criu-3.13/criu/pie/parasite-vdso.c --- criu-3.12/criu/pie/parasite-vdso.c 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/criu/pie/parasite-vdso.c 2019-09-11 08:29:31.000000000 +0000 @@ -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" @@ -24,68 +25,107 @@ #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 remap_one(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; } -/* - * 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) +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; - unsigned long rt_vvar_park = park_at; - unsigned long rt_vdso_park = park_at; int ret; + ret = remap_one("rt-vdso", &rt->vdso_start, vdso, vdso_size); + if (ret) + return 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); + 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; } - BUG_ON((vdso_size + vvar_size) < park_size); + if (VDSO_SYMBOL_GTOD < 0) { + pr_debug("Arch doesn't support gettimeofday() from vdso\n"); + return; + } - if (rt->sym.vdso_before_vvar) - rt_vvar_park = park_at + vdso_size; - else - rt_vdso_park = park_at + vvar_size; + /* + * 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; + } - 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); + gtod_sym = &rt->sym.symbols[VDSO_SYMBOL_GTOD]; + if (gtod_sym->offset == VDSO_BAD_ADDR) { + pr_debug("No gettimeofday() on rt-vdso\n"); + return; + } - return ret; + gtod = (void*)(rt->vdso_start + gtod_sym->offset); + pr_info("Using gettimeofday() on vdso at %p\n", gtod); + + std_log_set_gettimeofday(gtod); } -/* 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) +/* + * 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 addr, unsigned long space) { - if (compat_vdso) - return vdso_fill_symtable_compat(mem, size, t); + unsigned long vvar_size = rt->sym.vvar_size; + unsigned long vdso_size = rt->sym.vdso_size; + + if (rt->vvar_start == VVAR_BAD_ADDR) { + BUG_ON(vdso_size < space); + return park_at(rt, addr, 0); + } + + BUG_ON((vdso_size + vvar_size) < space); + + if (rt->sym.vdso_before_vvar) + return park_at(rt, addr, addr + vvar_size); else - return vdso_fill_symtable(mem, size, t); + return park_at(rt, addr + vdso_size, addr); } -#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); @@ -105,23 +145,44 @@ 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; @@ -133,40 +194,32 @@ * 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; - int ret; + void *remap_addr; 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; } - if (!vma_vvar) { - return vdso_remap("rt-vdso", rt_vdso_addr, - vma_vdso->start, sym_rt->vdso_size); - } + if (!vma_vvar) + return park_at(rt, vma_vdso->start, 0); - 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; } - 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); - - return ret; + return park_at(rt, vma_vdso->start, vma_vvar->start); } /* @@ -175,29 +228,15 @@ * 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 * number of vdso symbols will not decrease. @@ -205,8 +244,8 @@ * 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; } @@ -216,15 +255,15 @@ * 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, 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) { @@ -271,11 +310,10 @@ vma_vvar ? (unsigned long)vma_vvar->start : VVAR_BAD_ADDR, vma_vvar ? (unsigned long)vma_vvar->end : VVAR_BAD_ADDR); - 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 = false; + if (blobs_matches(vma_vdso, vma_vvar, &s, &rt->sym) && !force_trampolines) + return remap_rt_vdso(vma_vdso, vma_vvar, rt); - return add_vdso_proxy(vma_vdso, vma_vvar, &s, sym_rt, - vdso_rt_parked_at, compat_vdso); + *added_proxy = true; + return add_vdso_proxy(vma_vdso, vma_vvar, &s, rt, compat_vdso); } diff -Nru criu-3.12/criu/pie/restorer.c criu-3.13/criu/pie/restorer.c --- criu-3.12/criu/pie/restorer.c 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/criu/pie/restorer.c 2019-09-11 08:29:31.000000000 +0000 @@ -16,6 +16,7 @@ #include #include #include +#include #include "linux/userfaultfd.h" @@ -52,6 +53,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 @@ -149,7 +154,7 @@ 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 +162,9 @@ 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 +310,14 @@ * 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; } @@ -319,10 +329,18 @@ 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) @@ -459,6 +477,23 @@ 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", @@ -1076,11 +1111,7 @@ * 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; @@ -1202,32 +1233,148 @@ 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; } + if (had_vdso && (rt->vdso_start == VDSO_BAD_ADDR)) { + pr_err("Task had vdso, restorer doesn't\n"); + 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 true; } -static bool vdso_needs_parking(struct task_restore_args *args) +static inline int restore_child_subreaper(int child_subreaper) { - /* Compatible vDSO will be mapped, not moved */ - if (args->compatible_mode) - return false; + int ret; - if (args->can_map_vdso) - return false; + 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; +} + +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; + } + + /* kernel may provide only vdso */ + if (rt->sym.vvar_size == VVAR_BAD_SIZE) { + rt->vdso_start = args->vdso_rt_parked_at; + rt->vvar_start = VVAR_BAD_ADDR; + return 0; + } + + if (rt->sym.vdso_before_vvar) { + rt->vdso_start = args->vdso_rt_parked_at; + rt->vvar_start = rt->vdso_start + rt->sym.vdso_size; + } else { + rt->vvar_start = args->vdso_rt_parked_at; + rt->vdso_start = rt->vvar_start + rt->sym.vvar_size; + } + + return 0; +} + +static int fd_poll(int inotify_fd) +{ + struct pollfd pfd = {inotify_fd, POLLIN, 0}; + struct timespec tmo = {0, 0}; - return !vdso_unmapped(args); + 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; } /* @@ -1250,13 +1397,12 @@ 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; -#ifdef CONFIG_VDSO vdso_rt_size = args->vdso_rt_size; -#endif fi_strategy = args->fault_strategy; @@ -1298,7 +1444,21 @@ 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; @@ -1309,12 +1469,10 @@ 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) { - goto core_restore_end; - } - } + 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++) { @@ -1353,8 +1511,10 @@ 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; } } @@ -1441,15 +1601,17 @@ sys_close(args->vma_ios_fd); -#ifdef CONFIG_VDSO /* * Proxify vDSO. */ - if (vdso_proxify(&args->vdso_maps_rt.sym, 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; -#endif + + /* 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 @@ -1506,8 +1668,6 @@ } } - ret = 0; - /* * Tune up the task fields. */ @@ -1571,7 +1731,7 @@ 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; } @@ -1677,6 +1837,9 @@ 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) @@ -1736,6 +1899,7 @@ 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 -Nru criu-3.12/criu/pie/util-vdso.c criu-3.13/criu/pie/util-vdso.c --- criu-3.12/criu/pie/util-vdso.c 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/criu/pie/util-vdso.c 2019-09-11 08:29:31.000000000 +0000 @@ -242,7 +242,7 @@ 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 -Nru criu-3.12/criu/pie/util-vdso-elf32.c criu-3.13/criu/pie/util-vdso-elf32.c --- criu-3.12/criu/pie/util-vdso-elf32.c 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/criu/pie/util-vdso-elf32.c 2019-09-11 08:29:31.000000000 +0000 @@ -242,7 +242,7 @@ 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 -Nru criu-3.12/criu/pie-util-vdso.c criu-3.13/criu/pie-util-vdso.c --- criu-3.12/criu/pie-util-vdso.c 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/criu/pie-util-vdso.c 2019-09-11 08:29:31.000000000 +0000 @@ -242,7 +242,7 @@ 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 -Nru criu-3.12/criu/pie-util-vdso-elf32.c criu-3.13/criu/pie-util-vdso-elf32.c --- criu-3.12/criu/pie-util-vdso-elf32.c 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/criu/pie-util-vdso-elf32.c 2019-09-11 08:29:31.000000000 +0000 @@ -242,7 +242,7 @@ 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 -Nru criu-3.12/criu/proc_parse.c criu-3.13/criu/proc_parse.c --- criu-3.12/criu/proc_parse.c 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/criu/proc_parse.c 2019-09-11 08:29:31.000000000 +0000 @@ -502,7 +502,6 @@ 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 @@ 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, @@ -674,14 +660,15 @@ 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; @@ -719,12 +706,7 @@ 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) @@ -2277,6 +2259,7 @@ tmp = xrealloc(t, nr * sizeof(struct pid)); if (!tmp) { xfree(t); + closedir(dir); return -1; } t = tmp; diff -Nru criu-3.12/criu/seize.c criu-3.13/criu/seize.c --- criu-3.12/criu/seize.c 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/criu/seize.c 2019-09-11 08:29:31.000000000 +0000 @@ -627,6 +627,7 @@ { 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 @@ } /* 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; diff -Nru criu-3.12/criu/servicefd.c criu-3.13/criu/servicefd.c --- criu-3.12/criu/servicefd.c 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/criu/servicefd.c 2019-09-11 08:29:31.000000000 +0000 @@ -153,6 +153,7 @@ 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 @@ 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 @@ 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 -Nru criu-3.12/criu/shmem.c criu-3.13/criu/shmem.c --- criu-3.12/criu/shmem.c 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/criu/shmem.c 2019-09-11 08:29:31.000000000 +0000 @@ -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" @@ -42,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 { @@ -178,11 +179,12 @@ 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; @@ -196,8 +198,7 @@ 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; @@ -233,7 +234,7 @@ * 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; @@ -676,6 +677,7 @@ 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 @@ 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 @@ 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 @@ 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 -Nru criu-3.12/criu/sk-inet.c criu-3.13/criu/sk-inet.c --- criu-3.12/criu/sk-inet.c 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/criu/sk-inet.c 2019-09-11 08:29:31.000000000 +0000 @@ -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: " @@ -568,12 +573,13 @@ 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); xfree(ie.src_addr); xfree(ie.dst_addr); + xfree(ie.ifname); return err; } @@ -737,6 +743,10 @@ 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; } @@ -804,12 +814,18 @@ 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()) + goto err; + if (ie->v6only) { if (restore_opt(sk, SOL_IPV6, IPV6_V6ONLY, &yes) == -1) goto err; @@ -883,13 +899,19 @@ (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); + } } } *new_fd = sk; + return 1; err: close(sk); diff -Nru criu-3.12/criu/sk-queue.c criu-3.13/criu/sk-queue.c --- criu-3.12/criu/sk-queue.c 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/criu/sk-queue.c 2019-09-11 08:29:31.000000000 +0000 @@ -29,7 +29,7 @@ struct sk_packet { struct list_head list; SkPacketEntry *entry; - char *data; + char *data; unsigned scm_len; int *scm; }; @@ -273,6 +273,8 @@ pr_perror("setsockopt failed on restore"); ret = -1; } + if (pe.scm) + release_cmsg(&pe); err_brk: xfree(data); return ret; diff -Nru criu-3.12/criu/sk-unix.c criu-3.13/criu/sk-unix.c --- criu-3.12/criu/sk-unix.c 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/criu/sk-unix.c 2019-09-11 08:29:31.000000000 +0000 @@ -1208,14 +1208,14 @@ 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"); @@ -1542,7 +1542,6 @@ 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; @@ -1889,13 +1888,16 @@ } } - 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; diff -Nru criu-3.12/criu/sockets.c criu-3.13/criu/sockets.c --- criu-3.12/criu/sockets.c 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/criu/sockets.c 2019-09-11 08:29:31.000000000 +0000 @@ -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" @@ -565,6 +566,11 @@ 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; @@ -646,6 +652,10 @@ 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); @@ -663,6 +673,9 @@ 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 -Nru criu-3.12/criu/stats.c criu-3.13/criu/stats.c --- criu-3.12/criu/stats.c 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/criu/stats.c 2019-09-11 08:29:31.000000000 +0000 @@ -165,6 +165,13 @@ 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; @@ -194,7 +201,15 @@ 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; } diff -Nru criu-3.12/criu/tls.c criu-3.13/criu/tls.c --- criu-3.12/criu/tls.c 1970-01-01 00:00:00.000000000 +0000 +++ criu-3.13/criu/tls.c 2019-09-11 08:29:31.000000000 +0000 @@ -0,0 +1,370 @@ +#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; + const char *hostname = NULL; + + 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; + } + + 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 -Nru criu-3.12/criu/tty.c criu-3.13/criu/tty.c --- criu-3.12/criu/tty.c 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/criu/tty.c 2019-09-11 08:29:31.000000000 +0000 @@ -349,11 +349,8 @@ 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"); @@ -1789,13 +1786,6 @@ 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); } diff -Nru criu-3.12/criu/uffd.c criu-3.13/criu/uffd.c --- criu-3.12/criu/uffd.c 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/criu/uffd.c 2019-09-11 08:29:31.000000000 +0000 @@ -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" @@ -1416,7 +1417,7 @@ int lazy_sk; int ret; - if (kerndat_uffd() || !kdat.has_uffd) + if (!kdat.has_uffd) return -1; if (prepare_dummy_pstree()) @@ -1427,7 +1428,7 @@ 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; @@ -1469,5 +1470,7 @@ ret = handle_requests(epollfd, events, nr_fds); + tls_terminate_session(); + return ret; } diff -Nru criu-3.12/criu/util.c criu-3.13/criu/util.c --- criu-3.12/criu/util.c 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/criu/util.c 2019-09-11 08:29:31.000000000 +0000 @@ -230,19 +230,19 @@ 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 */ @@ -500,8 +500,10 @@ 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))) { @@ -643,7 +645,7 @@ 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; @@ -666,16 +668,6 @@ 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"); @@ -864,6 +856,12 @@ cur++; } + if (*n == 0) { + /* This can only happen if str == NULL */ + *out = NULL; + *n = -1; + return; + } *out = xmalloc((*n) * sizeof(char *)); if (!*out) { @@ -1144,10 +1142,10 @@ 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; + goto err; } if (ret > 0) { /* parent task, daemon started */ close_safe(&sk); @@ -1168,10 +1166,11 @@ 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)); @@ -1179,7 +1178,7 @@ } return 0; -out: +err: close(sk); return -1; } diff -Nru criu-3.12/criu/vdso.c criu-3.13/criu/vdso.c --- criu-3.12/criu/vdso.c 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/criu/vdso.c 2019-09-11 08:29:31.000000000 +0000 @@ -597,7 +597,8 @@ 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 @@ 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; diff -Nru criu-3.12/debian/changelog criu-3.13/debian/changelog --- criu-3.12/debian/changelog 2019-04-27 15:43:48.000000000 +0000 +++ criu-3.13/debian/changelog 2019-09-16 08:25:32.000000000 +0000 @@ -1,3 +1,9 @@ +criu (3.13-2ppa1.19.04) disco; urgency=medium + + * New upstream version 3.13 + + -- Adrian Reber Mon, 16 Sep 2019 16:43:48 +0100 + criu (3.12-2ppa1.19.04) disco; urgency=medium * New upstream version 3.12 diff -Nru criu-3.12/Documentation/compel.txt criu-3.13/Documentation/compel.txt --- criu-3.12/Documentation/compel.txt 1970-01-01 00:00:00.000000000 +0000 +++ criu-3.13/Documentation/compel.txt 2019-09-11 08:29:31.000000000 +0000 @@ -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. diff -Nru criu-3.12/Documentation/criu.txt criu-3.13/Documentation/criu.txt --- criu-3.12/Documentation/criu.txt 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/Documentation/criu.txt 2019-09-11 08:29:31.000000000 +0000 @@ -440,6 +440,8 @@ *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. @@ -592,6 +594,33 @@ 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 -Nru criu-3.12/Documentation/Makefile criu-3.13/Documentation/Makefile --- criu-3.12/Documentation/Makefile 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/Documentation/Makefile 2019-09-11 08:29:31.000000000 +0000 @@ -12,6 +12,7 @@ FOOTER := footer.txt SRC1 += crit.txt +SRC1 += compel.txt SRC8 += criu.txt SRC := $(SRC1) $(SRC8) XMLS := $(patsubst %.txt,%.xml,$(SRC)) diff -Nru criu-3.12/images/core.proto criu-3.13/images/core.proto --- criu-3.12/images/core.proto 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/images/core.proto 2019-09-11 08:29:31.000000000 +0000 @@ -51,6 +51,8 @@ // Reserved for tty inheritance //optional int32 tty_nr = 16; //optional int32 tty_pgrp = 17; + + optional bool child_subreaper = 18; } message task_kobj_ids_entry { diff -Nru criu-3.12/images/creds.proto criu-3.13/images/creds.proto --- criu-3.12/images/creds.proto 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/images/creds.proto 2019-09-11 08:29:31.000000000 +0000 @@ -20,4 +20,5 @@ repeated uint32 groups = 14; optional string lsm_profile = 15; + optional string lsm_sockcreate = 16; } diff -Nru criu-3.12/images/fdinfo.proto criu-3.13/images/fdinfo.proto --- criu-3.12/images/fdinfo.proto 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/images/fdinfo.proto 2019-09-11 08:29:31.000000000 +0000 @@ -47,6 +47,7 @@ required uint32 flags = 2; required fd_types type = 3; required uint32 fd = 4; + optional string xattr_security_selinux = 5; } message file_entry { diff -Nru criu-3.12/images/rpc.proto criu-3.13/images/rpc.proto --- criu-3.12/images/rpc.proto 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/images/rpc.proto 2019-09-11 08:29:31.000000000 +0000 @@ -114,6 +114,12 @@ 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; */ } diff -Nru criu-3.12/images/sk-opts.proto criu-3.13/images/sk-opts.proto --- criu-3.12/images/sk-opts.proto 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/images/sk-opts.proto 2019-09-11 08:29:31.000000000 +0000 @@ -22,6 +22,7 @@ repeated fixed64 so_filter = 16; optional bool so_reuseport = 17; + optional bool so_broadcast = 18; } enum sk_shutdown { diff -Nru criu-3.12/images/stats.proto criu-3.13/images/stats.proto --- criu-3.12/images/stats.proto 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/images/stats.proto 2019-09-11 08:29:31.000000000 +0000 @@ -16,6 +16,10 @@ 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 { diff -Nru criu-3.12/include/common/arch/arm/asm/atomic.h criu-3.13/include/common/arch/arm/asm/atomic.h --- criu-3.12/include/common/arch/arm/asm/atomic.h 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/include/common/arch/arm/asm/atomic.h 2019-09-11 08:29:31.000000000 +0000 @@ -29,9 +29,9 @@ "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 *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 @@ " 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 -Nru criu-3.12/include/common/arch/arm/asm/linkage.h criu-3.13/include/common/arch/arm/asm/linkage.h --- criu-3.12/include/common/arch/arm/asm/linkage.h 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/include/common/arch/arm/asm/linkage.h 2019-09-11 08:29:31.000000000 +0000 @@ -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__ */ diff -Nru criu-3.12/include/common/arch/ppc64/asm/atomic.h criu-3.13/include/common/arch/ppc64/asm/atomic.h --- criu-3.12/include/common/arch/ppc64/asm/atomic.h 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/include/common/arch/ppc64/asm/atomic.h 2019-09-11 08:29:31.000000000 +0000 @@ -8,7 +8,7 @@ */ typedef struct { - int counter; + int counter; } atomic_t; #include "common/arch/ppc64/asm/cmpxchg.h" diff -Nru criu-3.12/include/common/arch/ppc64/asm/linkage.h criu-3.13/include/common/arch/ppc64/asm/linkage.h --- criu-3.12/include/common/arch/ppc64/asm/linkage.h 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/include/common/arch/ppc64/asm/linkage.h 2019-09-11 08:29:31.000000000 +0000 @@ -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 -Nru criu-3.12/include/common/arch/x86/asm/cmpxchg.h criu-3.13/include/common/arch/x86/asm/cmpxchg.h --- criu-3.12/include/common/arch/x86/asm/cmpxchg.h 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/include/common/arch/x86/asm/cmpxchg.h 2019-09-11 08:29:31.000000000 +0000 @@ -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 -Nru criu-3.12/lib/c/criu.c criu-3.13/lib/c/criu.c --- criu-3.12/lib/c/criu.c 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/lib/c/criu.c 2019-09-11 08:29:31.000000000 +0000 @@ -41,10 +41,10 @@ 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; } } @@ -252,6 +252,7 @@ if (opts == NULL) { perror("Can't allocate memory for criu opts"); criu_local_free_opts(opts); + free(rpc); return -1; } @@ -865,8 +866,6 @@ err: if (str) free(str); - if (ptr) - free(ptr); return -ENOMEM; } @@ -902,8 +901,6 @@ err: if (str) free(str); - if (ptr) - free(ptr); return -ENOMEM; } diff -Nru criu-3.12/lib/c/criu.h criu-3.13/lib/c/criu.h --- criu-3.12/lib/c/criu.h 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/lib/c/criu.h 2019-09-11 08:29:31.000000000 +0000 @@ -21,8 +21,10 @@ #include +#include "version.h" + #ifdef __GNUG__ - extern "C" { + extern "C" { #endif enum criu_service_comm { diff -Nru criu-3.12/lib/Makefile criu-3.13/lib/Makefile --- criu-3.12/lib/Makefile 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/lib/Makefile 2019-09-11 08:29:31.000000000 +0000 @@ -1,11 +1,12 @@ CRIU_SO := libcriu.so -UAPI_HEADERS := lib/c/criu.h images/rpc.proto +CRIU_A := libcriu.a +UAPI_HEADERS := lib/c/criu.h images/rpc.proto criu/include/version.h # # 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. @@ -19,8 +20,12 @@ 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. @@ -37,15 +42,16 @@ $(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 +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 @@ -60,6 +66,7 @@ $(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 diff -Nru criu-3.12/lib/py/cli.py criu-3.13/lib/py/cli.py --- criu-3.12/lib/py/cli.py 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/lib/py/cli.py 2019-09-11 08:29:31.000000000 +0000 @@ -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) + + if opts['pretty']: + indent = 4 + + f = outf(opts) + json.dump(img, f, indent=indent) + if f == sys.stdout: + f.write("\n") - 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 - - 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 + 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) - pp = pss[p.ppid] - pp.kids.append(p) + print("%7s%7s%7s %s" % ('PID', 'PGID', 'SID', 'COMM')) + show_ps(psr, opts) - 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 len(files_img) == 0: - return None - - for f in files_img: - if f['id'] == fid: - return f + 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 + + 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']] - - 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 + 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 + 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 = {} -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 + 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 - 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)) - - 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']}))) + 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 get(self, iid): - ret = self.__ids.get(iid, None) - if not ret: - ret = self.__last - self.__last += 1 - self.__ids[iid] = ret + 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 + + 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 '-' + 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)) + 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)) - - + 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 +} -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) + 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) - opts["func"](opts) if __name__ == '__main__': - main() + main() diff -Nru criu-3.12/lib/py/criu.py criu-3.13/lib/py/criu.py --- criu-3.12/lib/py/criu.py 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/lib/py/criu.py 2019-09-11 08:29:31.000000000 +0000 @@ -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): - """ - Connect to criu and return socket object. - daemon -- is for whether or not criu should daemonize if executing criu from binary(comm_bin). - """ - pass - - def disconnect(self): - """ - Disconnect from criu. - """ - pass + """ + Base class for communication classes. + """ + COMM_SK = 0 + COMM_FD = 1 + COMM_BIN = 2 + comm_type = None + comm = None + sk = None + + def connect(self, daemon): + """ + Connect to criu and return socket object. + daemon -- is for whether or not criu should daemonize if executing criu from binary(comm_bin). + """ + pass + + def disconnect(self): + """ + Disconnect from criu. + """ + pass class _criu_comm_sk(_criu_comm): - """ - Communication class for unix socket. - """ - def __init__(self, sk_path): - self.comm_type = self.COMM_SK - self.comm = sk_path - - def connect(self, daemon): - self.sk = socket.socket(socket.AF_UNIX, socket.SOCK_SEQPACKET) - self.sk.connect(self.comm) + """ + Communication class for unix socket. + """ + + def __init__(self, sk_path): + self.comm_type = self.COMM_SK + self.comm = sk_path + + def connect(self, daemon): + self.sk = socket.socket(socket.AF_UNIX, socket.SOCK_SEQPACKET) + self.sk.connect(self.comm) - return self.sk + return self.sk - def disconnect(self): - self.sk.close() + 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 + """ + 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 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) + """ + 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) class CRIUException(Exception): - """ - Exception class for handling and storing criu errors. - """ - typ = None - _str = None + """ + Exception class for handling and storing criu errors. + """ + 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) + """ + Exception class for handling and storing internal errors. + """ + + def __init__(self, typ, s): + self.typ = typ + self._str = "%s failed with internal error: %s" % ( + rpc.criu_req_type.Name(self.typ), s) class CRIUExceptionExternal(CRIUException): - """ - Exception class for handling and storing criu RPC errors. - """ + """ + 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 - - _comm = None #Communication method - - def __init__(self): - self.use_binary('criu') - self.opts = rpc.criu_opts() - self.sk = None - - def use_sk(self, sk_name): - """ - Access criu using unix socket which that belongs to criu service daemon. - """ - self._comm = _criu_comm_sk(sk_name) - - def use_fd(self, fd): - """ - Access criu using provided fd. - """ - self._comm = _criu_comm_fd(fd) - - def use_binary(self, bin_name): - """ - Access criu by execing it using provided path to criu binary. - """ - self._comm = _criu_comm_bin(bin_name) - - def _send_req_and_recv_resp(self, req): - """ - As simple as send request and receive response. - """ - # In case of self-dump we need to spawn criu swrk detached - # from our current process, as criu has a hard time separating - # process resources from its own if criu is located in a same - # process tree it is trying to dump. - daemon = False - if req.type == rpc.DUMP and not req.opts.HasField('pid'): - daemon = True - - try: - if not self.sk: - s = self._comm.connect(daemon) - else: - s = self.sk - - if req.keep_open: - self.sk = s - - s.send(req.SerializeToString()) - - buf = s.recv(len(s.recv(1, socket.MSG_TRUNC | socket.MSG_PEEK))) - - 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)) - - return resp - - def check(self): - """ - Checks whether the kernel support is up-to-date. - """ - req = rpc.criu_req() - req.type = rpc.CHECK - - resp = self._send_req_and_recv_resp(req) - - if not resp.success: - raise CRIUExceptionExternal(req.type, resp.type, resp.cr_errno) - - def dump(self): - """ - Checkpoint a process/tree identified by opts.pid. - """ - req = rpc.criu_req() - req.type = rpc.DUMP - req.opts.MergeFrom(self.opts) - - resp = self._send_req_and_recv_resp(req) - - if not resp.success: - raise CRIUExceptionExternal(req.type, resp.type, resp.cr_errno) - - return resp.dump - - def 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) - - resp = self._send_req_and_recv_resp(req) - - if not resp.success: - raise CRIUExceptionExternal(req.type, resp.type, resp.cr_errno) - - return resp.dump - - def restore(self): - """ - Restore a process/tree. - """ - req = rpc.criu_req() - req.type = rpc.RESTORE - req.opts.MergeFrom(self.opts) - - resp = self._send_req_and_recv_resp(req) - - if not resp.success: - raise CRIUExceptionExternal(req.type, resp.type, resp.cr_errno) - - 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 - - resp = self._send_req_and_recv_resp(req) - - if not resp.success: - raise CRIUExceptionExternal(req.type, resp.type, resp.cr_errno) - - return resp.ps - - def wait_pid(self, pid): - req = rpc.criu_req() - req.type = rpc.WAIT_PID - req.pid = pid + """ + Call criu through RPC. + """ + opts = None #CRIU options in pb format + + _comm = None #Communication method + + def __init__(self): + self.use_binary('criu') + self.opts = rpc.criu_opts() + self.sk = None + + def use_sk(self, sk_name): + """ + Access criu using unix socket which that belongs to criu service daemon. + """ + self._comm = _criu_comm_sk(sk_name) + + def use_fd(self, fd): + """ + Access criu using provided fd. + """ + self._comm = _criu_comm_fd(fd) + + def use_binary(self, bin_name): + """ + Access criu by execing it using provided path to criu binary. + """ + self._comm = _criu_comm_bin(bin_name) + + def _send_req_and_recv_resp(self, req): + """ + As simple as send request and receive response. + """ + # In case of self-dump we need to spawn criu swrk detached + # from our current process, as criu has a hard time separating + # process resources from its own if criu is located in a same + # process tree it is trying to dump. + daemon = False + if req.type == rpc.DUMP and not req.opts.HasField('pid'): + daemon = True + + try: + if not self.sk: + s = self._comm.connect(daemon) + else: + s = self.sk + + if req.keep_open: + self.sk = s + + s.send(req.SerializeToString()) + + buf = s.recv(len(s.recv(1, socket.MSG_TRUNC | socket.MSG_PEEK))) + + 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)) + + return resp + + def check(self): + """ + Checks whether the kernel support is up-to-date. + """ + req = rpc.criu_req() + req.type = rpc.CHECK + + resp = self._send_req_and_recv_resp(req) + + if not resp.success: + raise CRIUExceptionExternal(req.type, resp.type, resp.cr_errno) + + def dump(self): + """ + Checkpoint a process/tree identified by opts.pid. + """ + req = rpc.criu_req() + req.type = rpc.DUMP + req.opts.MergeFrom(self.opts) + + resp = self._send_req_and_recv_resp(req) + + if not resp.success: + raise CRIUExceptionExternal(req.type, resp.type, resp.cr_errno) + + return resp.dump + + def 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) + + resp = self._send_req_and_recv_resp(req) + + if not resp.success: + raise CRIUExceptionExternal(req.type, resp.type, resp.cr_errno) + + return resp.dump + + def restore(self): + """ + Restore a process/tree. + """ + req = rpc.criu_req() + req.type = rpc.RESTORE + req.opts.MergeFrom(self.opts) + + resp = self._send_req_and_recv_resp(req) + + if not resp.success: + raise CRIUExceptionExternal(req.type, resp.type, resp.cr_errno) + + 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 + + resp = self._send_req_and_recv_resp(req) + + if not resp.success: + raise CRIUExceptionExternal(req.type, resp.type, resp.cr_errno) + + return resp.ps + + 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 -Nru criu-3.12/lib/py/images/images.py criu-3.13/lib/py/images/images.py --- criu-3.12/lib/py/images/images.py 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/lib/py/images/images.py 2019-09-11 08:29:31.000000000 +0000 @@ -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 @@ -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): - """ - 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. - """ - 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) - - 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): - """ - Same as dump(), but doesn't take file-like object and just - returns a string. - """ - f = io.BytesIO('') - self.dump(entries, f) - return f.read() - - def count(self, f): - """ - Counts the number of top-level object in the image file - """ - entries = 0 - - while True: - buf = f.read(4) - if buf == '': - break - size, = struct.unpack('i', buf) - f.seek(size, 1) - entries += 1 + """ + Generic class to handle loading/dumping criu images + entries from/to bin format to/from dict(json). + """ + + def __init__(self, payload, extra_handler=None): + """ + Sets payload class and extra handler class. + """ + self.payload = payload + self.extra_handler = extra_handler + + def load(self, f, pretty=False, 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): + """ + Same as load(), but takes a string as an argument. + """ + f = io.BytesIO(s) + return self.load(f, pretty) + + def dump(self, entries, f): + """ + Convert criu image entries from dict(json) format to binary. + Takes a list of entries and a file-like object to write entries + in binary format to. + """ + for entry in entries: + extra = entry.pop('extra', None) + + # Write payload + 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() + + def count(self, f): + """ + Counts the number of top-level object in the image file + """ + entries = 0 + + while True: + buf = f.read(4) + if buf == '': + break + size, = struct.unpack('i', buf) + f.seek(size, 1) + entries += 1 + + return entries - 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)) - - pbuff = pb.pagemap_entry() - - return entries - - def loads(self, s, pretty = False): - f = io.BytesIO(s) - return self.load(f, pretty) - - def dump(self, entries, f): - 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() - - def dumps(self, entries): - f = io.BytesIO('') - self.dump(entries, f) - return f.read() + """ + 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)) + + pbuff = pb.pagemap_entry() + + return entries + + def loads(self, s, pretty=False): + f = io.BytesIO(s) + return self.load(f, pretty) + + def dump(self, entries, f): + 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() + + 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 = [] + 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() + 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 @@ # 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 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 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 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 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 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 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 + 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 - 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)) - - try: - m = magic.by_val[img_magic] - except: - raise MagicException(img_magic) - - try: - handler = handlers[m] - except: - raise Exception("No handler found for image with magic " + m) - - return m, handler - -def load(f, pretty = False, 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 = {} + # Images v1.1 NOTE: First read "first" magic. + img_magic, = struct.unpack('i', f.read(4)) + if img_magic in (magic.by_name['IMG_COMMON'], + magic.by_name['IMG_SERVICE']): + img_magic, = struct.unpack('i', f.read(4)) + + try: + m = magic.by_val[img_magic] + except: + raise MagicException(img_magic) + + try: + handler = handlers[m] + except: + raise Exception("No handler found for image with magic " + m) + + return m, handler + + +def load(f, pretty=False, 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 = {} + + 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): + """ + Same as load(), but takes a string. + """ + f = io.BytesIO(s) + return load(f, pretty) -def loads(s, pretty = False): - """ - Same as load(), but takes a string. - """ - f = io.BytesIO(s) - return load(f, pretty) def dump(img, f): - """ - Convert criu image from dict(json) format to binary. - Takes an image in dict(json) format and file-like - object to write to. - """ - m = img['magic'] - magic_val = magic.by_name[img['magic']] - - # Images v1.1 NOTE: use "second" magic to identify what "first" - # should be written. - if m != 'INVENTORY': - if m in ('STATS', 'IRMAP_CACHE'): - f.write(struct.pack('i', magic.by_name['IMG_SERVICE'])) - else: - f.write(struct.pack('i', magic.by_name['IMG_COMMON'])) - - f.write(struct.pack('i', magic_val)) - - try: - handler = handlers[m] - except: - raise Exception("No handler found for image with such magic") + """ + Convert criu image from dict(json) format to binary. + Takes an image in dict(json) format and file-like + object to write to. + """ + m = img['magic'] + magic_val = magic.by_name[img['magic']] + + # Images v1.1 NOTE: use "second" magic to identify what "first" + # should be written. + if m != 'INVENTORY': + if m in ('STATS', 'IRMAP_CACHE'): + f.write(struct.pack('i', magic.by_name['IMG_SERVICE'])) + else: + f.write(struct.pack('i', magic.by_name['IMG_COMMON'])) + + f.write(struct.pack('i', magic_val)) + + try: + handler = handlers[m] + except: + raise Exception("No handler found for image with such magic") + + handler.dump(img['entries'], f) - 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() + """ + Same as dump(), but takes only an image and returns + a string. + """ + f = io.BytesIO(b'') + dump(img, f) + return f.getvalue() diff -Nru criu-3.12/lib/py/images/pb2dict.py criu-3.13/lib/py/images/pb2dict.py --- criu-3.12/lib/py/images/pb2dict.py 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/lib/py/images/pb2dict.py 2019-09-11 08:29:31.000000000 +0000 @@ -4,7 +4,13 @@ from ipaddress import IPv6Address import socket import collections -import os, six +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: @@ -23,345 +29,396 @@ # 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 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} -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): - return isinstance(value, six.string_types) + # Python 3 compatibility + if "basestring" in __builtins__: + string_types = basestring # noqa: F821 + 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 Exception: + 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) -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[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 Exception: + 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 is 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 + """ + 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 diff -Nru criu-3.12/Makefile criu-3.13/Makefile --- criu-3.12/Makefile 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/Makefile 2019-09-11 08:29:31.000000000 +0000 @@ -35,7 +35,7 @@ # 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 @@ -46,22 +46,26 @@ 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) - 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 @@ -74,14 +78,15 @@ ifeq ($(ARCH),s390) ARCH := s390 SRCARCH := s390 - VDSO := y DEFINES := -DCONFIG_S390 CFLAGS_PIE := -fno-optimize-sibling-calls endif + +CFLAGS_PIE += -DCR_NOGLIBC export CFLAGS_PIE LDARCH ?= $(SRCARCH) -export LDARCH VDSO +export LDARCH export PROTOUFIX DEFINES # @@ -122,9 +127,10 @@ 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 @@ -187,6 +193,7 @@ else # To clean all files, enable make/build options here export CONFIG_COMPAT := y +export CONFIG_GNUTLS := y endif # @@ -373,9 +380,11 @@ .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 + flake8 --config=scripts/flake8.cfg lib/py/images/pb2dict.py include Makefile.install diff -Nru criu-3.12/Makefile.config criu-3.13/Makefile.config --- criu-3.12/Makefile.config 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/Makefile.config 2019-09-11 08:29:31.000000000 +0000 @@ -15,6 +15,14 @@ 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 @@ -31,7 +39,7 @@ $(info Note: Building without ia32 C/R, missed ia32 support in gcc) $(info $(info) That may be related to missing gcc-multilib in your) $(info $(info) distribution or you may have Debian with buggy toolchain) - $(info $(info) (issue https://github.com/xemul/criu/issues/315)) + $(info $(info) (issue https://github.com/checkpoint-restore/criu/issues/315)) endif endif @@ -57,10 +65,6 @@ $(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 -Nru criu-3.12/Makefile.versions criu-3.13/Makefile.versions --- criu-3.12/Makefile.versions 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/Makefile.versions 2019-09-11 08:29:31.000000000 +0000 @@ -1,10 +1,10 @@ # # CRIU version. CRIU_VERSION_MAJOR := 3 -CRIU_VERSION_MINOR := 12 +CRIU_VERSION_MINOR := 13 CRIU_VERSION_SUBLEVEL := CRIU_VERSION_EXTRA := -CRIU_VERSION_NAME := Ice Penguin +CRIU_VERSION_NAME := Silicon Willet CRIU_VERSION := $(CRIU_VERSION_MAJOR)$(if $(CRIU_VERSION_MINOR),.$(CRIU_VERSION_MINOR))$(if $(CRIU_VERSION_SUBLEVEL),.$(CRIU_VERSION_SUBLEVEL))$(if $(CRIU_VERSION_EXTRA),.$(CRIU_VERSION_EXTRA)) export CRIU_VERSION_MAJOR CRIU_VERSION_MINOR CRIU_VERSION_SUBLEVEL diff -Nru criu-3.12/README.md criu-3.13/README.md --- criu-3.12/README.md 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/README.md 2019-09-11 08:29:31.000000000 +0000 @@ -63,8 +63,8 @@ looking for contributors of all kinds -- feedback, bug reports, testing, coding, writing, etc. Here are some useful hints to get involved. -* We have both -- [very simple](https://github.com/xemul/criu/issues?q=is%3Aissue+is%3Aopen+label%3Aenhancement) and [more sophisticated](https://github.com/xemul/criu/issues?q=is%3Aissue+is%3Aopen+label%3A%22new+feature%22) coding tasks; -* CRIU does need [extensive testing](https://github.com/xemul/criu/issues?q=is%3Aissue+is%3Aopen+label%3Atesting); +* We have both -- [very simple](https://checkpoint-restore/criu/issues?q=is%3Aissue+is%3Aopen+label%3Aenhancement) and [more sophisticated](https://checkpoint-restore/criu/issues?q=is%3Aissue+is%3Aopen+label%3A%22new+feature%22) coding tasks; +* CRIU does need [extensive testing](https://checkpoint-restore/criu/issues?q=is%3Aissue+is%3Aopen+label%3Atesting); * Documentation is always hard, we have [some information](https://criu.org/Category:Empty_articles) that is to be extracted from people's heads into wiki pages as well as [some texts](https://criu.org/Category:Editor_help_needed) that all need to be converted into useful articles; * Feedback is expected on the github issues page and on the [mailing list](https://lists.openvz.org/mailman/listinfo/criu); * For historical reasons we do not accept PRs, instead [patches are welcome](http://criu.org/How_to_submit_patches); diff -Nru criu-3.12/scripts/build/Dockerfile.aarch64.tmpl criu-3.13/scripts/build/Dockerfile.aarch64.tmpl --- criu-3.12/scripts/build/Dockerfile.aarch64.tmpl 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/scripts/build/Dockerfile.aarch64.tmpl 2019-09-11 08:29:31.000000000 +0000 @@ -12,8 +12,10 @@ iptables \ libaio-dev \ libcap-dev \ + libgnutls28-dev \ + libgnutls30 \ libnl-3-dev \ - libprotobuf-c0-dev \ + libprotobuf-c-dev \ libprotobuf-dev \ libselinux-dev \ pkg-config \ diff -Nru criu-3.12/scripts/build/Dockerfile.alpine criu-3.13/scripts/build/Dockerfile.alpine --- criu-3.12/scripts/build/Dockerfile.alpine 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/scripts/build/Dockerfile.alpine 2019-09-11 08:29:31.000000000 +0000 @@ -9,6 +9,7 @@ ccache \ coreutils \ git \ + gnutls-dev \ libaio-dev \ libcap-dev \ libnet-dev \ @@ -36,7 +37,7 @@ 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 -Nru criu-3.12/scripts/build/Dockerfile.armv7hf.tmpl criu-3.13/scripts/build/Dockerfile.armv7hf.tmpl --- criu-3.12/scripts/build/Dockerfile.armv7hf.tmpl 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/scripts/build/Dockerfile.armv7hf.tmpl 2019-09-11 08:29:31.000000000 +0000 @@ -12,8 +12,10 @@ iptables \ libaio-dev \ libcap-dev \ + libgnutls28-dev \ + libgnutls30 \ libnl-3-dev \ - libprotobuf-c0-dev \ + libprotobuf-c-dev \ libprotobuf-dev \ libselinux-dev \ pkg-config \ diff -Nru criu-3.12/scripts/build/Dockerfile.centos criu-3.13/scripts/build/Dockerfile.centos --- criu-3.12/scripts/build/Dockerfile.centos 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/scripts/build/Dockerfile.centos 2019-09-11 08:29:31.000000000 +0000 @@ -9,6 +9,7 @@ findutils \ gcc \ git \ + gnutls-devel \ iproute \ iptables \ libaio-devel \ @@ -32,7 +33,7 @@ which \ e2fsprogs \ python2-pip \ - asciidoc xmlto + rubygem-asciidoctor COPY . /criu WORKDIR /criu diff -Nru criu-3.12/scripts/build/Dockerfile.fedora-asan.tmpl criu-3.13/scripts/build/Dockerfile.fedora-asan.tmpl --- criu-3.12/scripts/build/Dockerfile.fedora-asan.tmpl 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/scripts/build/Dockerfile.fedora-asan.tmpl 2019-09-11 08:29:31.000000000 +0000 @@ -6,6 +6,7 @@ findutils \ gcc \ git \ + gnutls-devel \ iproute \ iptables \ libaio-devel \ @@ -17,14 +18,7 @@ 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-flake8 \ python3-PyYAML \ python3-future \ python3-protobuf \ @@ -33,7 +27,7 @@ tar \ which \ e2fsprogs \ - asciidoc xmlto \ + rubygem-asciidoctor \ kmod # Replace coreutils-single with "traditional" coreutils @@ -43,6 +37,7 @@ RUN dnf install -y --allowerasing coreutils RUN ln -sf python3 /usr/bin/python +ENV PYTHON=python3 COPY . /criu WORKDIR /criu diff -Nru criu-3.12/scripts/build/Dockerfile.fedora-rawhide-aarch64.tmpl criu-3.13/scripts/build/Dockerfile.fedora-rawhide-aarch64.tmpl --- criu-3.12/scripts/build/Dockerfile.fedora-rawhide-aarch64.tmpl 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/scripts/build/Dockerfile.fedora-rawhide-aarch64.tmpl 2019-09-11 08:29:31.000000000 +0000 @@ -6,6 +6,7 @@ findutils \ gcc \ git \ + gnutls-devel \ iproute \ iptables \ libaio-devel \ @@ -17,14 +18,7 @@ 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-flake8 \ python3-PyYAML \ python3-future \ python3-protobuf \ @@ -33,7 +27,7 @@ tar \ which \ e2fsprogs \ - asciidoc xmlto \ + rubygem-asciidoctor \ kmod # Replace coreutils-single with "traditional" coreutils @@ -43,6 +37,7 @@ RUN dnf install -y --allowerasing coreutils RUN ln -sf python3 /usr/bin/python +ENV PYTHON=python3 COPY . /criu WORKDIR /criu diff -Nru criu-3.12/scripts/build/Dockerfile.fedora-rawhide.tmpl criu-3.13/scripts/build/Dockerfile.fedora-rawhide.tmpl --- criu-3.12/scripts/build/Dockerfile.fedora-rawhide.tmpl 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/scripts/build/Dockerfile.fedora-rawhide.tmpl 2019-09-11 08:29:31.000000000 +0000 @@ -6,6 +6,7 @@ findutils \ gcc \ git \ + gnutls-devel \ iproute \ iptables \ libaio-devel \ @@ -17,14 +18,7 @@ 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-flake8 \ python3-PyYAML \ python3-future \ python3-protobuf \ @@ -33,7 +27,7 @@ tar \ which \ e2fsprogs \ - asciidoc xmlto \ + rubygem-asciidoctor \ kmod # Replace coreutils-single with "traditional" coreutils @@ -43,6 +37,7 @@ RUN dnf install -y --allowerasing coreutils RUN ln -sf python3 /usr/bin/python +ENV PYTHON=python3 COPY . /criu WORKDIR /criu diff -Nru criu-3.12/scripts/build/Dockerfile.fedora.tmpl criu-3.13/scripts/build/Dockerfile.fedora.tmpl --- criu-3.12/scripts/build/Dockerfile.fedora.tmpl 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/scripts/build/Dockerfile.fedora.tmpl 2019-09-11 08:29:31.000000000 +0000 @@ -6,6 +6,7 @@ findutils \ gcc \ git \ + gnutls-devel \ iproute \ iptables \ libaio-devel \ @@ -17,14 +18,7 @@ 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-flake8 \ python3-PyYAML \ python3-future \ python3-protobuf \ @@ -33,7 +27,7 @@ tar \ which \ e2fsprogs \ - asciidoc xmlto \ + rubygem-asciidoctor \ kmod # Replace coreutils-single with "traditional" coreutils @@ -43,6 +37,7 @@ RUN dnf install -y --allowerasing coreutils RUN ln -sf python3 /usr/bin/python +ENV PYTHON=python3 COPY . /criu WORKDIR /criu diff -Nru criu-3.12/scripts/build/Dockerfile.ppc64le.tmpl criu-3.13/scripts/build/Dockerfile.ppc64le.tmpl --- criu-3.12/scripts/build/Dockerfile.ppc64le.tmpl 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/scripts/build/Dockerfile.ppc64le.tmpl 2019-09-11 08:29:31.000000000 +0000 @@ -12,8 +12,10 @@ iptables \ libaio-dev \ libcap-dev \ + libgnutls28-dev \ + libgnutls30 \ libnl-3-dev \ - libprotobuf-c0-dev \ + libprotobuf-c-dev \ libprotobuf-dev \ libselinux-dev \ pkg-config \ diff -Nru criu-3.12/scripts/build/Dockerfile.s390x.tmpl criu-3.13/scripts/build/Dockerfile.s390x.tmpl --- criu-3.12/scripts/build/Dockerfile.s390x.tmpl 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/scripts/build/Dockerfile.s390x.tmpl 2019-09-11 08:29:31.000000000 +0000 @@ -12,8 +12,10 @@ iptables \ libaio-dev \ libcap-dev \ + libgnutls28-dev \ + libgnutls30 \ libnl-3-dev \ - libprotobuf-c0-dev \ + libprotobuf-c-dev \ libprotobuf-dev \ libselinux-dev \ pkg-config \ diff -Nru criu-3.12/scripts/build/Dockerfile.tmpl criu-3.13/scripts/build/Dockerfile.tmpl --- criu-3.12/scripts/build/Dockerfile.tmpl 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/scripts/build/Dockerfile.tmpl 2019-09-11 08:29:31.000000000 +0000 @@ -12,8 +12,10 @@ iptables \ libaio-dev \ libcap-dev \ + libgnutls28-dev \ + libgnutls30 \ libnl-3-dev \ - libprotobuf-c0-dev \ + libprotobuf-c-dev \ libprotobuf-dev \ libselinux-dev \ pkg-config \ diff -Nru criu-3.12/scripts/build/Dockerfile.x86_64.tmpl criu-3.13/scripts/build/Dockerfile.x86_64.tmpl --- criu-3.12/scripts/build/Dockerfile.x86_64.tmpl 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/scripts/build/Dockerfile.x86_64.tmpl 2019-09-11 08:29:31.000000000 +0000 @@ -12,8 +12,10 @@ iptables \ libaio-dev \ libcap-dev \ + libgnutls28-dev \ + libgnutls30 \ libnl-3-dev \ - libprotobuf-c0-dev \ + libprotobuf-c-dev \ libprotobuf-dev \ libselinux-dev \ pkg-config \ diff -Nru criu-3.12/scripts/build/Makefile criu-3.13/scripts/build/Makefile --- criu-3.12/scripts/build/Makefile 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/scripts/build/Makefile 2019-09-11 08:29:31.000000000 +0000 @@ -2,6 +2,7 @@ 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 @@ $(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 diff -Nru criu-3.12/scripts/crit-setup.py criu-3.13/scripts/crit-setup.py --- criu-3.12/scripts/crit-setup.py 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/scripts/crit-setup.py 2019-09-11 08:29:31.000000000 +0000 @@ -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/checkpoint-restore/criu", + package_dir={'pycriu': 'lib/py'}, + packages=["pycriu", "pycriu.images"], + scripts=["crit/crit"]) diff -Nru criu-3.12/scripts/flake8.cfg criu-3.13/scripts/flake8.cfg --- criu-3.12/scripts/flake8.cfg 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/scripts/flake8.cfg 2019-09-11 08:29:31.000000000 +0000 @@ -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 diff -Nru criu-3.12/scripts/magic-gen.py criu-3.13/scripts/magic-gen.py --- criu-3.12/scripts/magic-gen.py 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/scripts/magic-gen.py 2019-09-11 08:29:31.000000000 +0000 @@ -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 -Nru criu-3.12/scripts/nmk/scripts/tools.mk criu-3.13/scripts/nmk/scripts/tools.mk --- criu-3.12/scripts/nmk/scripts/tools.mk 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/scripts/nmk/scripts/tools.mk 2019-09-11 08:29:31.000000000 +0000 @@ -35,6 +35,8 @@ 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 -Nru criu-3.12/scripts/travis/Makefile criu-3.13/scripts/travis/Makefile --- criu-3.12/scripts/travis/Makefile 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/scripts/travis/Makefile 2019-09-11 08:29:31.000000000 +0000 @@ -38,5 +38,8 @@ docker-test: ./docker-test.sh +podman-test: + ./podman-test.sh + %: $(MAKE) -C ../build $@$(target-suffix) diff -Nru criu-3.12/scripts/travis/podman-test.sh criu-3.13/scripts/travis/podman-test.sh --- criu-3.12/scripts/travis/podman-test.sh 1970-01-01 00:00:00.000000000 +0000 +++ criu-3.13/scripts/travis/podman-test.sh 2019-09-11 08:29:31.000000000 +0000 @@ -0,0 +1,69 @@ +#!/bin/bash +set -x -e -o pipefail + +add-apt-repository -y ppa:projectatomic/ppa + +apt-get install -qq \ + apt-transport-https \ + ca-certificates \ + curl \ + software-properties-common + +apt-get update -qq + +apt-get install -qqy podman + +export SKIP_TRAVIS_TEST=1 + +./travis-tests + +cd ../../ + +make install + +podman info + +criu --version + +podman run --name cr -d docker.io/library/alpine /bin/sh -c 'i=0; while true; do echo $i; i=$(expr $i + 1); sleep 1; done' + +sleep 1 +for i in `seq 50`; do + echo "Test $i for podman container checkpoint" + podman exec cr ps axf + podman logs cr + [ `podman ps -f name=cr -q | wc -l` -eq "1" ] + podman container checkpoint cr + [ `podman ps -f name=cr -q | wc -l` -eq "0" ] + podman ps -a + podman container restore cr + [ `podman ps -f name=cr -q | wc -l` -eq "1" ] + podman logs cr +done + +for i in `seq 50`; do + echo "Test $i for podman container checkpoint --export" + podman ps -a + podman exec cr ps axf + podman logs cr + [ `podman ps -f name=cr -q | wc -l` -eq "1" ] + podman container checkpoint -l --export /tmp/chkpt.tar.gz + [ `podman ps -f name=cr -q | wc -l` -eq "0" ] + podman ps -a + podman rm -fa + podman ps -a + podman container restore --import /tmp/chkpt.tar.gz + [ `podman ps -f name=cr -q | wc -l` -eq "1" ] + podman container restore --name cr2 --import /tmp/chkpt.tar.gz + [ `podman ps -f name=cr2 -q | wc -l` -eq "1" ] + podman ps -a + podman logs cr + podman logs cr2 + podman ps -a + podman rm -fa + podman ps -a + podman container restore --import /tmp/chkpt.tar.gz + [ `podman ps -f name=cr -q | wc -l` -eq "1" ] + podman ps -a + rm -f /tmp/chkpt.tar.gz +done diff -Nru criu-3.12/scripts/travis/travis-tests criu-3.13/scripts/travis/travis-tests --- criu-3.12/scripts/travis/travis-tests 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/scripts/travis/travis-tests 2019-09-11 08:29:31.000000000 +0000 @@ -1,10 +1,10 @@ #!/bin/sh 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" +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" travis_prep () { [ -n "$SKIP_TRAVIS_PREP" ] && return @@ -100,7 +100,7 @@ export SKIP_PREP=1 # The 3.19 kernel (from Ubuntu 14.04) has a bug. When /proc/PID/pagemap # is read for a few VMAs in one read call, incorrect data is returned. -# See https://github.com/xemul/criu/issues/207 +# See https://github.com/checkpoint-restore/criu/issues/207 # Kernel 4.4 (from Ubuntu 14.04.5 update) fixes this. uname -r | grep -q ^3\.19 && export CRIU_PMC_OFF=1 @@ -125,6 +125,7 @@ ./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 @@ -162,7 +163,9 @@ 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 diff -Nru criu-3.12/soccr/soccr.c criu-3.13/soccr/soccr.c --- criu-3.12/soccr/soccr.c 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/soccr/soccr.c 2019-09-11 08:29:31.000000000 +0000 @@ -158,13 +158,13 @@ } 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, diff -Nru criu-3.12/soccr/test/run.py criu-3.13/soccr/test/run.py --- criu-3.12/soccr/test/run.py 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/soccr/test/run.py 2019-09-11 08:29:31.000000000 +0000 @@ -1,4 +1,4 @@ -#!/usr/bin/env python2 +#!/usr/bin/env python import sys, os import hashlib @@ -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 -Nru criu-3.12/test/check_actions.py criu-3.13/test/check_actions.py --- criu-3.12/test/check_actions.py 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/test/check_actions.py 2019-09-11 08:29:31.000000000 +0000 @@ -1,40 +1,41 @@ -#!/usr/bin/env python2 +#!/usr/bin/env python import sys 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') - - 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[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])) + act = act.strip().split() + act.append('EMPTY') + act.append('EMPTY') + + 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[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 -Nru criu-3.12/test/crit-recode.py criu-3.13/test/crit-recode.py --- criu-3.12/test/crit-recode.py 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/test/crit-recode.py 2019-09-11 08:29:31.000000000 +0000 @@ -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: - 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 + 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 + + 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 - - 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 + 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 find.wait() if not test_pass: - print("FAIL") - sys.exit(1) + print("FAIL") + sys.exit(1) print("PASS") diff -Nru criu-3.12/test/exhaustive/pipe.py criu-3.13/test/exhaustive/pipe.py --- criu-3.12/test/exhaustive/pipe.py 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/test/exhaustive/pipe.py 2019-09-11 08:29:31.000000000 +0000 @@ -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. - - # 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 ] + # 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)] + + # 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 - - 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) + 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]) + + 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 @@ # 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) + 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 + 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 + 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 + 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('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('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 @@ 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 -Nru criu-3.12/test/exhaustive/unix.py criu-3.13/test/exhaustive/unix.py --- criu-3.12/test/exhaustive/unix.py 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/test/exhaustive/unix.py 2019-09-11 08:29:31.000000000 +0000 @@ -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 do(self, st): - sk = socket.socket(socket.AF_UNIX, self.typ, 0) - st.real_sockets[self.sk_id] = sk + 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 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 do(self, st): - sk = st.real_sockets.pop(self.sk_id) - sk.close() + 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 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 do(self, st): - sk = st.real_sockets[self.sk_id] - sk.listen(10) + 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 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 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 __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 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 __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 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 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 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 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 = map(lambda x: x.describe(self), 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 + 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 +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 + # 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)) + print(' `- checked %d -> %d with %s' % (sk.sk_id, sk.peer, msgv)) - return CHK_PASS + return CHK_PASS def chk_state(st, opts): - print("Will check state") + 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 + 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 + 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 + 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 + 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 @@ 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 -Nru criu-3.12/test/inhfd/fifo.py criu-3.13/test/inhfd/fifo.py --- criu-3.12/test/inhfd/fifo.py 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/test/inhfd/fifo.py 2019-09-11 08:29:31.000000000 +0000 @@ -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") + 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) + global id_str + id_str = "file[%x:%x]" % (mnt_id, os.fstat(fd1.fileno()).st_ino) - return [(fd2, fd1)] + 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 -Nru criu-3.12/test/inhfd/pipe.py criu-3.13/test/inhfd/pipe.py --- criu-3.12/test/inhfd/pipe.py 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/test/inhfd/pipe.py 2019-09-11 08:29:31.000000000 +0000 @@ -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 -Nru criu-3.12/test/inhfd/socket.py criu-3.13/test/inhfd/socket.py --- criu-3.12/test/inhfd/socket.py 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/test/inhfd/socket.py 2019-09-11 08:29:31.000000000 +0000 @@ -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 -Nru criu-3.12/test/inhfd/tty.py criu-3.13/test/inhfd/tty.py --- criu-3.12/test/inhfd/tty.py 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/test/inhfd/tty.py 2019-09-11 08:29:31.000000000 +0000 @@ -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 -Nru criu-3.12/test/others/bers/bers.c criu-3.13/test/others/bers/bers.c --- criu-3.12/test/others/bers/bers.c 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/test/others/bers/bers.c 2019-09-11 08:29:31.000000000 +0000 @@ -93,7 +93,6 @@ 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_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 @@ -139,7 +138,7 @@ 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); @@ -265,7 +264,6 @@ char workdir[PATH_MAX]; int opt, idx, pidfd; char pidbuf[32]; - int status; pid_t pid; size_t i; diff -Nru criu-3.12/test/others/ext-tty/run.py criu-3.13/test/others/ext-tty/run.py --- criu-3.12/test/others/ext-tty/run.py 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/test/others/ext-tty/run.py 2019-09-11 08:29:31.000000000 +0000 @@ -1,36 +1,45 @@ -#!/usr/bin/env python2 +#!/usr/bin/env python import subprocess import os, sys, time, signal, pty 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 -Nru criu-3.12/test/others/libcriu/test_errno.c criu-3.13/test/others/libcriu/test_errno.c --- criu-3.12/test/others/libcriu/test_errno.c 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/test/others/libcriu/test_errno.c 2019-09-11 08:29:31.000000000 +0000 @@ -56,7 +56,7 @@ size_t len; ssize_t count; char *buf = NULL; - int pid, fd, ret; + int pid, ret; printf("--- Try to dump unexisting process\n"); diff -Nru criu-3.12/test/others/mounts/mounts.py criu-3.13/test/others/mounts/mounts.py --- criu-3.12/test/others/mounts/mounts.py 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/test/others/mounts/mounts.py 2019-09-11 08:29:31.000000000 +0000 @@ -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) - - print(cmd) - ret = os.system(cmd) - if ret: - print("failed") + 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") + -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 -Nru criu-3.12/test/others/mounts/mounts.sh criu-3.13/test/others/mounts/mounts.sh --- criu-3.12/test/others/mounts/mounts.sh 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/test/others/mounts/mounts.sh 2019-09-11 08:29:31.000000000 +0000 @@ -20,7 +20,7 @@ umount -l $i done -python2 mounts.py +python mounts.py kill $INMNTNS_PID while :; do sleep 10 diff -Nru criu-3.12/test/others/rpc/config_file.py criu-3.13/test/others/rpc/config_file.py --- criu-3.12/test/others/rpc/config_file.py 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/test/others/rpc/config_file.py 2019-09-11 08:29:31.000000000 +0000 @@ -1,182 +1,179 @@ -#!/usr/bin/python2 +#!/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.') - 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 + # 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) + # 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) - + # Testing with configuration file + # Just doing a dump and checking for the logfile -parser = argparse.ArgumentParser(description="Test config files using CRIU RPC") -parser.add_argument('dir', type = str, help = "Directory where CRIU images should be placed") + # 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 -Nru criu-3.12/test/others/rpc/errno.py criu-3.13/test/others/rpc/errno.py --- criu-3.12/test/others/rpc/errno.py 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/test/others/rpc/errno.py 2019-09-11 08:29:31.000000000 +0000 @@ -1,4 +1,4 @@ -#!/usr/bin/python2 +#!/usr/bin/python # Test criu errno import socket, os, errno @@ -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 -Nru criu-3.12/test/others/rpc/ps_test.py criu-3.13/test/others/rpc/ps_test.py --- criu-3.12/test/others/rpc/ps_test.py 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/test/others/rpc/ps_test.py 2019-09-11 08:29:31.000000000 +0000 @@ -1,12 +1,14 @@ -#!/usr/bin/python2 +#!/usr/bin/python import socket, os, sys, errno import rpc_pb2 as rpc 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 -Nru criu-3.12/test/others/rpc/read.py criu-3.13/test/others/rpc/read.py --- criu-3.12/test/others/rpc/read.py 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/test/others/rpc/read.py 2019-09-11 08:29:31.000000000 +0000 @@ -12,6 +12,6 @@ f.close() if r == '\0': - sys.exit(0) + sys.exit(0) sys.exit(-1) diff -Nru criu-3.12/test/others/rpc/restore-loop.py criu-3.13/test/others/rpc/restore-loop.py --- criu-3.12/test/others/rpc/restore-loop.py 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/test/others/rpc/restore-loop.py 2019-09-11 08:29:31.000000000 +0000 @@ -1,12 +1,15 @@ -#!/usr/bin/python2 +#!/usr/bin/python import socket, os, sys 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 -Nru criu-3.12/test/others/rpc/rpc.proto criu-3.13/test/others/rpc/rpc.proto --- criu-3.12/test/others/rpc/rpc.proto 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/test/others/rpc/rpc.proto 2019-09-11 08:29:31.000000000 +0000 @@ -114,6 +114,12 @@ 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; */ } diff -Nru criu-3.12/test/others/rpc/setup_swrk.py criu-3.13/test/others/rpc/setup_swrk.py --- criu-3.12/test/others/rpc/setup_swrk.py 1970-01-01 00:00:00.000000000 +0000 +++ criu-3.13/test/others/rpc/setup_swrk.py 2019-09-11 08:29:31.000000000 +0000 @@ -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 -Nru criu-3.12/test/others/rpc/test.py criu-3.13/test/others/rpc/test.py --- criu-3.12/test/others/rpc/test.py 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/test/others/rpc/test.py 2019-09-11 08:29:31.000000000 +0000 @@ -1,12 +1,15 @@ -#!/usr/bin/python2 +#!/usr/bin/python import socket, os, sys 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 -Nru criu-3.12/test/others/rpc/version.py criu-3.13/test/others/rpc/version.py --- criu-3.12/test/others/rpc/version.py 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/test/others/rpc/version.py 2019-09-11 08:29:31.000000000 +0000 @@ -1,17 +1,13 @@ -#!/usr/bin/python2 +#!/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:') +from setup_swrk import setup_swrk -css = socket.socketpair(socket.AF_UNIX, socket.SOCK_SEQPACKET) -swrk = subprocess.Popen(['./criu', "swrk", "%d" % css[0].fileno()]) -css[0].close() +print('Connecting to CRIU in swrk mode to check the version:') -s = css[1] +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 @@ -19,29 +15,29 @@ 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') - 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 -Nru criu-3.12/test/others/shell-job/run.py criu-3.13/test/others/shell-job/run.py --- criu-3.12/test/others/shell-job/run.py 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/test/others/shell-job/run.py 2019-09-11 08:29:31.000000000 +0000 @@ -1,4 +1,4 @@ -#!/usr/bin/env python2 +#!/usr/bin/env python import os, pty, sys, subprocess import termios, fcntl, time @@ -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, "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() +m, s = create_pty() p = os.pipe() pr = os.fdopen(p[0], "r") pw = os.fdopen(p[1], "w") @@ -46,14 +48,15 @@ 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 -Nru criu-3.12/test/pki/cacert.pem criu-3.13/test/pki/cacert.pem --- criu-3.12/test/pki/cacert.pem 1970-01-01 00:00:00.000000000 +0000 +++ criu-3.13/test/pki/cacert.pem 2019-09-11 08:29:31.000000000 +0000 @@ -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 -Nru criu-3.12/test/pki/cert.pem criu-3.13/test/pki/cert.pem --- criu-3.12/test/pki/cert.pem 1970-01-01 00:00:00.000000000 +0000 +++ criu-3.13/test/pki/cert.pem 2019-09-11 08:29:31.000000000 +0000 @@ -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 -Nru criu-3.12/test/pki/key.pem criu-3.13/test/pki/key.pem --- criu-3.12/test/pki/key.pem 1970-01-01 00:00:00.000000000 +0000 +++ criu-3.13/test/pki/key.pem 2019-09-11 08:29:31.000000000 +0000 @@ -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 -Nru criu-3.12/test/pycriu/cli.py criu-3.13/test/pycriu/cli.py --- criu-3.12/test/pycriu/cli.py 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/test/pycriu/cli.py 2019-09-11 08:29:31.000000000 +0000 @@ -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) + + if opts['pretty']: + indent = 4 + + f = outf(opts) + json.dump(img, f, indent=indent) + if f == sys.stdout: + f.write("\n") - 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 - - 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 + 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) - pp = pss[p.ppid] - pp.kids.append(p) + print("%7s%7s%7s %s" % ('PID', 'PGID', 'SID', 'COMM')) + show_ps(psr, opts) - 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 len(files_img) == 0: - return None - - for f in files_img: - if f['id'] == fid: - return f + 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 + + 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']] - - 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 + 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 + 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 = {} -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 + 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 - 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)) - - 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']}))) + 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 get(self, iid): - ret = self.__ids.get(iid, None) - if not ret: - ret = self.__last - self.__last += 1 - self.__ids[iid] = ret + 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 + + 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 '-' + 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)) + 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)) - - + 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 +} -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) + 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) - opts["func"](opts) if __name__ == '__main__': - main() + main() diff -Nru criu-3.12/test/pycriu/criu.py criu-3.13/test/pycriu/criu.py --- criu-3.12/test/pycriu/criu.py 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/test/pycriu/criu.py 2019-09-11 08:29:31.000000000 +0000 @@ -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): - """ - Connect to criu and return socket object. - daemon -- is for whether or not criu should daemonize if executing criu from binary(comm_bin). - """ - pass - - def disconnect(self): - """ - Disconnect from criu. - """ - pass + """ + Base class for communication classes. + """ + COMM_SK = 0 + COMM_FD = 1 + COMM_BIN = 2 + comm_type = None + comm = None + sk = None + + def connect(self, daemon): + """ + Connect to criu and return socket object. + daemon -- is for whether or not criu should daemonize if executing criu from binary(comm_bin). + """ + pass + + def disconnect(self): + """ + Disconnect from criu. + """ + pass class _criu_comm_sk(_criu_comm): - """ - Communication class for unix socket. - """ - def __init__(self, sk_path): - self.comm_type = self.COMM_SK - self.comm = sk_path - - def connect(self, daemon): - self.sk = socket.socket(socket.AF_UNIX, socket.SOCK_SEQPACKET) - self.sk.connect(self.comm) + """ + Communication class for unix socket. + """ + + def __init__(self, sk_path): + self.comm_type = self.COMM_SK + self.comm = sk_path + + def connect(self, daemon): + self.sk = socket.socket(socket.AF_UNIX, socket.SOCK_SEQPACKET) + self.sk.connect(self.comm) - return self.sk + return self.sk - def disconnect(self): - self.sk.close() + 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 + """ + 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 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) + """ + 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) class CRIUException(Exception): - """ - Exception class for handling and storing criu errors. - """ - typ = None - _str = None + """ + Exception class for handling and storing criu errors. + """ + 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) + """ + Exception class for handling and storing internal errors. + """ + + def __init__(self, typ, s): + self.typ = typ + self._str = "%s failed with internal error: %s" % ( + rpc.criu_req_type.Name(self.typ), s) class CRIUExceptionExternal(CRIUException): - """ - Exception class for handling and storing criu RPC errors. - """ + """ + 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 - - _comm = None #Communication method - - def __init__(self): - self.use_binary('criu') - self.opts = rpc.criu_opts() - self.sk = None - - def use_sk(self, sk_name): - """ - Access criu using unix socket which that belongs to criu service daemon. - """ - self._comm = _criu_comm_sk(sk_name) - - def use_fd(self, fd): - """ - Access criu using provided fd. - """ - self._comm = _criu_comm_fd(fd) - - def use_binary(self, bin_name): - """ - Access criu by execing it using provided path to criu binary. - """ - self._comm = _criu_comm_bin(bin_name) - - def _send_req_and_recv_resp(self, req): - """ - As simple as send request and receive response. - """ - # In case of self-dump we need to spawn criu swrk detached - # from our current process, as criu has a hard time separating - # process resources from its own if criu is located in a same - # process tree it is trying to dump. - daemon = False - if req.type == rpc.DUMP and not req.opts.HasField('pid'): - daemon = True - - try: - if not self.sk: - s = self._comm.connect(daemon) - else: - s = self.sk - - if req.keep_open: - self.sk = s - - s.send(req.SerializeToString()) - - buf = s.recv(len(s.recv(1, socket.MSG_TRUNC | socket.MSG_PEEK))) - - 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)) - - return resp - - def check(self): - """ - Checks whether the kernel support is up-to-date. - """ - req = rpc.criu_req() - req.type = rpc.CHECK - - resp = self._send_req_and_recv_resp(req) - - if not resp.success: - raise CRIUExceptionExternal(req.type, resp.type, resp.cr_errno) - - def dump(self): - """ - Checkpoint a process/tree identified by opts.pid. - """ - req = rpc.criu_req() - req.type = rpc.DUMP - req.opts.MergeFrom(self.opts) - - resp = self._send_req_and_recv_resp(req) - - if not resp.success: - raise CRIUExceptionExternal(req.type, resp.type, resp.cr_errno) - - return resp.dump - - def 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) - - resp = self._send_req_and_recv_resp(req) - - if not resp.success: - raise CRIUExceptionExternal(req.type, resp.type, resp.cr_errno) - - return resp.dump - - def restore(self): - """ - Restore a process/tree. - """ - req = rpc.criu_req() - req.type = rpc.RESTORE - req.opts.MergeFrom(self.opts) - - resp = self._send_req_and_recv_resp(req) - - if not resp.success: - raise CRIUExceptionExternal(req.type, resp.type, resp.cr_errno) - - 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 - - resp = self._send_req_and_recv_resp(req) - - if not resp.success: - raise CRIUExceptionExternal(req.type, resp.type, resp.cr_errno) - - return resp.ps - - def wait_pid(self, pid): - req = rpc.criu_req() - req.type = rpc.WAIT_PID - req.pid = pid + """ + Call criu through RPC. + """ + opts = None #CRIU options in pb format + + _comm = None #Communication method + + def __init__(self): + self.use_binary('criu') + self.opts = rpc.criu_opts() + self.sk = None + + def use_sk(self, sk_name): + """ + Access criu using unix socket which that belongs to criu service daemon. + """ + self._comm = _criu_comm_sk(sk_name) + + def use_fd(self, fd): + """ + Access criu using provided fd. + """ + self._comm = _criu_comm_fd(fd) + + def use_binary(self, bin_name): + """ + Access criu by execing it using provided path to criu binary. + """ + self._comm = _criu_comm_bin(bin_name) + + def _send_req_and_recv_resp(self, req): + """ + As simple as send request and receive response. + """ + # In case of self-dump we need to spawn criu swrk detached + # from our current process, as criu has a hard time separating + # process resources from its own if criu is located in a same + # process tree it is trying to dump. + daemon = False + if req.type == rpc.DUMP and not req.opts.HasField('pid'): + daemon = True + + try: + if not self.sk: + s = self._comm.connect(daemon) + else: + s = self.sk + + if req.keep_open: + self.sk = s + + s.send(req.SerializeToString()) + + buf = s.recv(len(s.recv(1, socket.MSG_TRUNC | socket.MSG_PEEK))) + + 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)) + + return resp + + def check(self): + """ + Checks whether the kernel support is up-to-date. + """ + req = rpc.criu_req() + req.type = rpc.CHECK + + resp = self._send_req_and_recv_resp(req) + + if not resp.success: + raise CRIUExceptionExternal(req.type, resp.type, resp.cr_errno) + + def dump(self): + """ + Checkpoint a process/tree identified by opts.pid. + """ + req = rpc.criu_req() + req.type = rpc.DUMP + req.opts.MergeFrom(self.opts) + + resp = self._send_req_and_recv_resp(req) + + if not resp.success: + raise CRIUExceptionExternal(req.type, resp.type, resp.cr_errno) + + return resp.dump + + def 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) + + resp = self._send_req_and_recv_resp(req) + + if not resp.success: + raise CRIUExceptionExternal(req.type, resp.type, resp.cr_errno) + + return resp.dump + + def restore(self): + """ + Restore a process/tree. + """ + req = rpc.criu_req() + req.type = rpc.RESTORE + req.opts.MergeFrom(self.opts) + + resp = self._send_req_and_recv_resp(req) + + if not resp.success: + raise CRIUExceptionExternal(req.type, resp.type, resp.cr_errno) + + 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 + + resp = self._send_req_and_recv_resp(req) + + if not resp.success: + raise CRIUExceptionExternal(req.type, resp.type, resp.cr_errno) + + return resp.ps + + 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 -Nru criu-3.12/test/pycriu/images/images.py criu-3.13/test/pycriu/images/images.py --- criu-3.12/test/pycriu/images/images.py 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/test/pycriu/images/images.py 2019-09-11 08:29:31.000000000 +0000 @@ -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 @@ -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): - """ - 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. - """ - 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) - - 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): - """ - Same as dump(), but doesn't take file-like object and just - returns a string. - """ - f = io.BytesIO('') - self.dump(entries, f) - return f.read() - - def count(self, f): - """ - Counts the number of top-level object in the image file - """ - entries = 0 - - while True: - buf = f.read(4) - if buf == '': - break - size, = struct.unpack('i', buf) - f.seek(size, 1) - entries += 1 + """ + Generic class to handle loading/dumping criu images + entries from/to bin format to/from dict(json). + """ + + def __init__(self, payload, extra_handler=None): + """ + Sets payload class and extra handler class. + """ + self.payload = payload + self.extra_handler = extra_handler + + def load(self, f, pretty=False, 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): + """ + Same as load(), but takes a string as an argument. + """ + f = io.BytesIO(s) + return self.load(f, pretty) + + def dump(self, entries, f): + """ + Convert criu image entries from dict(json) format to binary. + Takes a list of entries and a file-like object to write entries + in binary format to. + """ + for entry in entries: + extra = entry.pop('extra', None) + + # Write payload + 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() + + def count(self, f): + """ + Counts the number of top-level object in the image file + """ + entries = 0 + + while True: + buf = f.read(4) + if buf == '': + break + size, = struct.unpack('i', buf) + f.seek(size, 1) + entries += 1 + + return entries - 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)) - - pbuff = pb.pagemap_entry() - - return entries - - def loads(self, s, pretty = False): - f = io.BytesIO(s) - return self.load(f, pretty) - - def dump(self, entries, f): - 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() - - def dumps(self, entries): - f = io.BytesIO('') - self.dump(entries, f) - return f.read() + """ + 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)) + + pbuff = pb.pagemap_entry() + + return entries + + def loads(self, s, pretty=False): + f = io.BytesIO(s) + return self.load(f, pretty) + + def dump(self, entries, f): + 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() + + 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 = [] + 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() + 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 @@ # 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 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 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 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 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 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 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 + 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 - 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)) - - try: - m = magic.by_val[img_magic] - except: - raise MagicException(img_magic) - - try: - handler = handlers[m] - except: - raise Exception("No handler found for image with magic " + m) - - return m, handler - -def load(f, pretty = False, 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 = {} + # Images v1.1 NOTE: First read "first" magic. + img_magic, = struct.unpack('i', f.read(4)) + if img_magic in (magic.by_name['IMG_COMMON'], + magic.by_name['IMG_SERVICE']): + img_magic, = struct.unpack('i', f.read(4)) + + try: + m = magic.by_val[img_magic] + except: + raise MagicException(img_magic) + + try: + handler = handlers[m] + except: + raise Exception("No handler found for image with magic " + m) + + return m, handler + + +def load(f, pretty=False, 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 = {} + + 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): + """ + Same as load(), but takes a string. + """ + f = io.BytesIO(s) + return load(f, pretty) -def loads(s, pretty = False): - """ - Same as load(), but takes a string. - """ - f = io.BytesIO(s) - return load(f, pretty) def dump(img, f): - """ - Convert criu image from dict(json) format to binary. - Takes an image in dict(json) format and file-like - object to write to. - """ - m = img['magic'] - magic_val = magic.by_name[img['magic']] - - # Images v1.1 NOTE: use "second" magic to identify what "first" - # should be written. - if m != 'INVENTORY': - if m in ('STATS', 'IRMAP_CACHE'): - f.write(struct.pack('i', magic.by_name['IMG_SERVICE'])) - else: - f.write(struct.pack('i', magic.by_name['IMG_COMMON'])) - - f.write(struct.pack('i', magic_val)) - - try: - handler = handlers[m] - except: - raise Exception("No handler found for image with such magic") + """ + Convert criu image from dict(json) format to binary. + Takes an image in dict(json) format and file-like + object to write to. + """ + m = img['magic'] + magic_val = magic.by_name[img['magic']] + + # Images v1.1 NOTE: use "second" magic to identify what "first" + # should be written. + if m != 'INVENTORY': + if m in ('STATS', 'IRMAP_CACHE'): + f.write(struct.pack('i', magic.by_name['IMG_SERVICE'])) + else: + f.write(struct.pack('i', magic.by_name['IMG_COMMON'])) + + f.write(struct.pack('i', magic_val)) + + try: + handler = handlers[m] + except: + raise Exception("No handler found for image with such magic") + + handler.dump(img['entries'], f) - 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() + """ + Same as dump(), but takes only an image and returns + a string. + """ + f = io.BytesIO(b'') + dump(img, f) + return f.getvalue() diff -Nru criu-3.12/test/pycriu/images/pb2dict.py criu-3.13/test/pycriu/images/pb2dict.py --- criu-3.12/test/pycriu/images/pb2dict.py 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/test/pycriu/images/pb2dict.py 2019-09-11 08:29:31.000000000 +0000 @@ -4,7 +4,13 @@ from ipaddress import IPv6Address import socket import collections -import os, six +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: @@ -23,345 +29,396 @@ # 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 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} -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): - return isinstance(value, six.string_types) + # Python 3 compatibility + if "basestring" in __builtins__: + string_types = basestring # noqa: F821 + 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 Exception: + 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) -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[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 Exception: + 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 is 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 + """ + 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 diff -Nru criu-3.12/test/zdtm/lib/ns.c criu-3.13/test/zdtm/lib/ns.c --- criu-3.12/test/zdtm/lib/ns.c 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/test/zdtm/lib/ns.c 2019-09-11 08:29:31.000000000 +0000 @@ -325,11 +325,11 @@ 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); diff -Nru criu-3.12/test/zdtm/static/autofs.c criu-3.13/test/zdtm/static/autofs.c --- criu-3.12/test/zdtm/static/autofs.c 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/test/zdtm/static/autofs.c 2019-09-11 08:29:31.000000000 +0000 @@ -49,6 +49,7 @@ 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. */ @@ -311,7 +312,7 @@ { struct autofs_dev_ioctl *param; size_t size; - int fd; + int ret; size = sizeof(struct autofs_dev_ioctl) + strlen(mountpoint) + 1; param = malloc(size); @@ -324,13 +325,14 @@ 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 -Nru criu-3.12/test/zdtm/static/caps00.c criu-3.13/test/zdtm/static/caps00.c --- criu-3.12/test/zdtm/static/caps00.c 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/test/zdtm/static/caps00.c 2019-09-11 08:29:31.000000000 +0000 @@ -47,6 +47,7 @@ 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 -Nru criu-3.12/test/zdtm/static/child_subreaper_and_reparent.c criu-3.13/test/zdtm/static/child_subreaper_and_reparent.c --- criu-3.12/test/zdtm/static/child_subreaper_and_reparent.c 1970-01-01 00:00:00.000000000 +0000 +++ criu-3.13/test/zdtm/static/child_subreaper_and_reparent.c 2019-09-11 08:29:31.000000000 +0000 @@ -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 -Nru criu-3.12/test/zdtm/static/child_subreaper_and_reparent.desc criu-3.13/test/zdtm/static/child_subreaper_and_reparent.desc --- criu-3.12/test/zdtm/static/child_subreaper_and_reparent.desc 1970-01-01 00:00:00.000000000 +0000 +++ criu-3.13/test/zdtm/static/child_subreaper_and_reparent.desc 2019-09-11 08:29:31.000000000 +0000 @@ -0,0 +1 @@ +{'flavor': 'ns uns'} diff -Nru criu-3.12/test/zdtm/static/child_subreaper.c criu-3.13/test/zdtm/static/child_subreaper.c --- criu-3.12/test/zdtm/static/child_subreaper.c 1970-01-01 00:00:00.000000000 +0000 +++ criu-3.13/test/zdtm/static/child_subreaper.c 2019-09-11 08:29:31.000000000 +0000 @@ -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 -Nru criu-3.12/test/zdtm/static/child_subreaper_existing_child.c criu-3.13/test/zdtm/static/child_subreaper_existing_child.c --- criu-3.12/test/zdtm/static/child_subreaper_existing_child.c 1970-01-01 00:00:00.000000000 +0000 +++ criu-3.13/test/zdtm/static/child_subreaper_existing_child.c 2019-09-11 08:29:31.000000000 +0000 @@ -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; +} diff -Nru criu-3.12/test/zdtm/static/file_locks06.c criu-3.13/test/zdtm/static/file_locks06.c --- criu-3.12/test/zdtm/static/file_locks06.c 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/test/zdtm/static/file_locks06.c 2019-09-11 08:29:31.000000000 +0000 @@ -26,7 +26,7 @@ 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 -Nru criu-3.12/test/zdtm/static/file_locks07.c criu-3.13/test/zdtm/static/file_locks07.c --- criu-3.12/test/zdtm/static/file_locks07.c 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/test/zdtm/static/file_locks07.c 2019-09-11 08:29:31.000000000 +0000 @@ -45,7 +45,7 @@ } 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 -Nru criu-3.12/test/zdtm/static/file_locks08.c criu-3.13/test/zdtm/static/file_locks08.c --- criu-3.12/test/zdtm/static/file_locks08.c 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/test/zdtm/static/file_locks08.c 2019-09-11 08:29:31.000000000 +0000 @@ -28,7 +28,7 @@ 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 -Nru criu-3.12/test/zdtm/static/inotify04.c criu-3.13/test/zdtm/static/inotify04.c --- criu-3.12/test/zdtm/static/inotify04.c 1970-01-01 00:00:00.000000000 +0000 +++ criu-3.13/test/zdtm/static/inotify04.c 2019-09-11 08:29:31.000000000 +0000 @@ -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; +} diff -Nru criu-3.12/test/zdtm/static/inotify04.desc criu-3.13/test/zdtm/static/inotify04.desc --- criu-3.12/test/zdtm/static/inotify04.desc 1970-01-01 00:00:00.000000000 +0000 +++ criu-3.13/test/zdtm/static/inotify04.desc 2019-09-11 08:29:31.000000000 +0000 @@ -0,0 +1 @@ +{'flags': 'reqrst'} diff -Nru criu-3.12/test/zdtm/static/Makefile criu-3.13/test/zdtm/static/Makefile --- criu-3.12/test/zdtm/static/Makefile 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/test/zdtm/static/Makefile 2019-09-11 08:29:31.000000000 +0000 @@ -30,6 +30,7 @@ socket_listen6 \ socket_listen4v6 \ socket_udp \ + socket_udp-broadcast \ socket_udp-corked \ socket6_udp \ socket_udp_shutdown \ @@ -211,6 +212,11 @@ thp_disable \ pid_file \ selinux00 \ + selinux01 \ + selinux02 \ + child_subreaper \ + child_subreaper_existing_child \ + child_subreaper_and_reparent \ # jobctl00 \ ifneq ($(SRCARCH),arm) @@ -305,6 +311,7 @@ inotify00 \ inotify01 \ inotify02 \ + inotify04 \ cgroup00 \ rmdir_open \ cgroup01 \ @@ -513,6 +520,7 @@ 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 -Nru criu-3.12/test/zdtm/static/netns_sub.c criu-3.13/test/zdtm/static/netns_sub.c --- criu-3.12/test/zdtm/static/netns_sub.c 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/test/zdtm/static/netns_sub.c 2019-09-11 08:29:31.000000000 +0000 @@ -104,6 +104,8 @@ pr_perror("unshare"); return 1; } + if (system("ip link set up dev lo")) + return 1; sk = create_socket(1); if (sk < 0) return 1; @@ -166,6 +168,8 @@ pr_perror("unshare"); return 1; } + if (system("ip link set up dev lo")) + return 1; sk = create_socket(2); if (sk < 0) return 1; diff -Nru criu-3.12/test/zdtm/static/netns_sub.desc criu-3.13/test/zdtm/static/netns_sub.desc --- criu-3.12/test/zdtm/static/netns_sub.desc 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/test/zdtm/static/netns_sub.desc 2019-09-11 08:29:31.000000000 +0000 @@ -1 +1,5 @@ -{'flavor': 'ns uns', 'flags': 'suid'} +{ + 'deps': ['/bin/sh', '/sbin/ip|/bin/ip'], + 'flavor': 'ns uns', + 'flags': 'suid' +} diff -Nru criu-3.12/test/zdtm/static/netns_sub_veth.c criu-3.13/test/zdtm/static/netns_sub_veth.c --- criu-3.12/test/zdtm/static/netns_sub_veth.c 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/test/zdtm/static/netns_sub_veth.c 2019-09-11 08:29:31.000000000 +0000 @@ -50,6 +50,8 @@ if (unshare(CLONE_NEWNET)) return 1; + if (system("ip link set up dev lo")) + return 1; task_waiter_complete(&lock, i); test_waitsig(); diff -Nru criu-3.12/test/zdtm/static/netns_sub_veth.desc criu-3.13/test/zdtm/static/netns_sub_veth.desc --- criu-3.12/test/zdtm/static/netns_sub_veth.desc 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/test/zdtm/static/netns_sub_veth.desc 2019-09-11 08:29:31.000000000 +0000 @@ -1,5 +1,5 @@ { - 'deps': ['/sbin/ip', '/bin/sh'], + 'deps': ['/sbin/ip|/bin/ip', '/bin/sh'], 'flags': 'suid', 'flavor': 'ns uns', 'feature': 'link_nsid', diff -Nru criu-3.12/test/zdtm/static/ofd_file_locks.c criu-3.13/test/zdtm/static/ofd_file_locks.c --- criu-3.12/test/zdtm/static/ofd_file_locks.c 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/test/zdtm/static/ofd_file_locks.c 2019-09-11 08:29:31.000000000 +0000 @@ -86,7 +86,7 @@ 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 @@ /* 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 @@ } 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 -Nru criu-3.12/test/zdtm/static/ofd_file_locks.h criu-3.13/test/zdtm/static/ofd_file_locks.h --- criu-3.12/test/zdtm/static/ofd_file_locks.h 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/test/zdtm/static/ofd_file_locks.h 2019-09-11 08:29:31.000000000 +0000 @@ -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_ */ diff -Nru criu-3.12/test/zdtm/static/packet_sock_mmap.c criu-3.13/test/zdtm/static/packet_sock_mmap.c --- criu-3.12/test/zdtm/static/packet_sock_mmap.c 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/test/zdtm/static/packet_sock_mmap.c 2019-09-11 08:29:31.000000000 +0000 @@ -35,18 +35,20 @@ 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); return; } } diff -Nru criu-3.12/test/zdtm/static/s390x_runtime_instr.c criu-3.13/test/zdtm/static/s390x_runtime_instr.c --- criu-3.12/test/zdtm/static/s390x_runtime_instr.c 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/test/zdtm/static/s390x_runtime_instr.c 2019-09-11 08:29:31.000000000 +0000 @@ -147,9 +147,11 @@ 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 -Nru criu-3.12/test/zdtm/static/seccomp_filter_threads.c criu-3.13/test/zdtm/static/seccomp_filter_threads.c --- criu-3.12/test/zdtm/static/seccomp_filter_threads.c 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/test/zdtm/static/seccomp_filter_threads.c 2019-09-11 08:29:31.000000000 +0000 @@ -39,6 +39,8 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ if (pid == 0) { pthread_t thread[ARRAY_SIZE(pthread_seccomp_params)]; - void *p = NULL; + void *ret; zdtm_seccomp = 1; @@ -180,10 +185,13 @@ 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 -Nru criu-3.12/test/zdtm/static/seccomp_filter_tsync.c criu-3.13/test/zdtm/static/seccomp_filter_tsync.c --- criu-3.12/test/zdtm/static/seccomp_filter_tsync.c 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/test/zdtm/static/seccomp_filter_tsync.c 2019-09-11 08:29:31.000000000 +0000 @@ -34,6 +34,8 @@ pthread_mutex_t getpid_wait; +static int magic = 1234; + int get_seccomp_mode(pid_t pid) { FILE *f; @@ -65,7 +67,7 @@ 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 @@ 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 @@ 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); } diff -Nru criu-3.12/test/zdtm/static/selinux00.c criu-3.13/test/zdtm/static/selinux00.c --- criu-3.12/test/zdtm/static/selinux00.c 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/test/zdtm/static/selinux00.c 2019-09-11 08:29:31.000000000 +0000 @@ -83,6 +83,31 @@ 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 @@ 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(); diff -Nru criu-3.12/test/zdtm/static/selinux01.c criu-3.13/test/zdtm/static/selinux01.c --- criu-3.12/test/zdtm/static/selinux01.c 1970-01-01 00:00:00.000000000 +0000 +++ criu-3.13/test/zdtm/static/selinux01.c 2019-09-11 08:29:31.000000000 +0000 @@ -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 -Nru criu-3.12/test/zdtm/static/selinux01.checkskip criu-3.13/test/zdtm/static/selinux01.checkskip --- criu-3.12/test/zdtm/static/selinux01.checkskip 1970-01-01 00:00:00.000000000 +0000 +++ criu-3.13/test/zdtm/static/selinux01.checkskip 2019-09-11 08:29:31.000000000 +0000 @@ -0,0 +1,25 @@ +#!/bin/bash + +test -d /sys/fs/selinux || exit 1 + +# See selinux00.hook for details + +getsebool unconfined_dyntrans_all > /dev/null 2>&1 +RESULT=$? +BOOLEAN=0 + +if [ "$RESULT" = "0" ]; then + BOOLEAN=1 +fi + +if [ "$BOOLEAN" = "1" ]; then + getsebool unconfined_dyntrans_all | grep off -q + RESULT=$? + echo $RESULT > /tmp/zdtm.selinux.state + if [ "$RESULT" = "0" ]; then + setsebool -P unconfined_dyntrans_all 1 + fi +else + cat /sys/fs/selinux/enforce > /tmp/zdtm.selinux.state + setenforce 0 +fi diff -Nru criu-3.12/test/zdtm/static/selinux01.desc criu-3.13/test/zdtm/static/selinux01.desc --- criu-3.12/test/zdtm/static/selinux01.desc 1970-01-01 00:00:00.000000000 +0000 +++ criu-3.13/test/zdtm/static/selinux01.desc 2019-09-11 08:29:31.000000000 +0000 @@ -0,0 +1 @@ +{'flavor': 'h'} diff -Nru criu-3.12/test/zdtm/static/selinux01.hook criu-3.13/test/zdtm/static/selinux01.hook --- criu-3.12/test/zdtm/static/selinux01.hook 1970-01-01 00:00:00.000000000 +0000 +++ criu-3.13/test/zdtm/static/selinux01.hook 2019-09-11 08:29:31.000000000 +0000 @@ -0,0 +1,32 @@ +#!/bin/sh + +# This script configures SELinux in such a way to enable the +# test 'selinux00' to be able to dyntransition from one +# SELinux context to another, as well as CRIU to change the +# context of a restored process. +# If a new enough selinux-policy is installed which includes +# https://github.com/fedora-selinux/selinux-policy/commit/2d537cabbb2df614ea598ac20873c653cbf271a8 +# then the boolean 'unconfined_dyntrans_all' will be changed +# to enable this test. If that boolean is not available, +# this just does 'setenforce 0'. + +# also see selinux00.checkskip + +getsebool unconfined_dyntrans_all > /dev/null 2>&1 +RESULT=$? +BOOLEAN=0 + +if [ "$RESULT" = "0" ]; then + BOOLEAN=1 +fi + +[ "$1" = "--post-restore" ] && { + if [ "$BOOLEAN" = "1" ]; then + setsebool -P unconfined_dyntrans_all `cat /tmp/zdtm.selinux.state` + else + setenforce `cat /tmp/zdtm.selinux.state` + rm -f /tmp/zdtm.selinux.state + fi +} + +exit 0 diff -Nru criu-3.12/test/zdtm/static/selinux02.c criu-3.13/test/zdtm/static/selinux02.c --- criu-3.12/test/zdtm/static/selinux02.c 1970-01-01 00:00:00.000000000 +0000 +++ criu-3.13/test/zdtm/static/selinux02.c 2019-09-11 08:29:31.000000000 +0000 @@ -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 -Nru criu-3.12/test/zdtm/static/selinux02.checkskip criu-3.13/test/zdtm/static/selinux02.checkskip --- criu-3.12/test/zdtm/static/selinux02.checkskip 1970-01-01 00:00:00.000000000 +0000 +++ criu-3.13/test/zdtm/static/selinux02.checkskip 2019-09-11 08:29:31.000000000 +0000 @@ -0,0 +1,25 @@ +#!/bin/bash + +test -d /sys/fs/selinux || exit 1 + +# See selinux00.hook for details + +getsebool unconfined_dyntrans_all > /dev/null 2>&1 +RESULT=$? +BOOLEAN=0 + +if [ "$RESULT" = "0" ]; then + BOOLEAN=1 +fi + +if [ "$BOOLEAN" = "1" ]; then + getsebool unconfined_dyntrans_all | grep off -q + RESULT=$? + echo $RESULT > /tmp/zdtm.selinux.state + if [ "$RESULT" = "0" ]; then + setsebool -P unconfined_dyntrans_all 1 + fi +else + cat /sys/fs/selinux/enforce > /tmp/zdtm.selinux.state + setenforce 0 +fi diff -Nru criu-3.12/test/zdtm/static/selinux02.desc criu-3.13/test/zdtm/static/selinux02.desc --- criu-3.12/test/zdtm/static/selinux02.desc 1970-01-01 00:00:00.000000000 +0000 +++ criu-3.13/test/zdtm/static/selinux02.desc 2019-09-11 08:29:31.000000000 +0000 @@ -0,0 +1 @@ +{'flavor': 'h'} diff -Nru criu-3.12/test/zdtm/static/selinux02.hook criu-3.13/test/zdtm/static/selinux02.hook --- criu-3.12/test/zdtm/static/selinux02.hook 1970-01-01 00:00:00.000000000 +0000 +++ criu-3.13/test/zdtm/static/selinux02.hook 2019-09-11 08:29:31.000000000 +0000 @@ -0,0 +1,32 @@ +#!/bin/sh + +# This script configures SELinux in such a way to enable the +# test 'selinux00' to be able to dyntransition from one +# SELinux context to another, as well as CRIU to change the +# context of a restored process. +# If a new enough selinux-policy is installed which includes +# https://github.com/fedora-selinux/selinux-policy/commit/2d537cabbb2df614ea598ac20873c653cbf271a8 +# then the boolean 'unconfined_dyntrans_all' will be changed +# to enable this test. If that boolean is not available, +# this just does 'setenforce 0'. + +# also see selinux00.checkskip + +getsebool unconfined_dyntrans_all > /dev/null 2>&1 +RESULT=$? +BOOLEAN=0 + +if [ "$RESULT" = "0" ]; then + BOOLEAN=1 +fi + +[ "$1" = "--post-restore" ] && { + if [ "$BOOLEAN" = "1" ]; then + setsebool -P unconfined_dyntrans_all `cat /tmp/zdtm.selinux.state` + else + setenforce `cat /tmp/zdtm.selinux.state` + rm -f /tmp/zdtm.selinux.state + fi +} + +exit 0 diff -Nru criu-3.12/test/zdtm/static/socket-tcp-reseted.desc criu-3.13/test/zdtm/static/socket-tcp-reseted.desc --- criu-3.12/test/zdtm/static/socket-tcp-reseted.desc 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/test/zdtm/static/socket-tcp-reseted.desc 2019-09-11 08:29:31.000000000 +0000 @@ -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 -Nru criu-3.12/test/zdtm/static/socket-tcp-syn-sent.desc criu-3.13/test/zdtm/static/socket-tcp-syn-sent.desc --- criu-3.12/test/zdtm/static/socket-tcp-syn-sent.desc 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/test/zdtm/static/socket-tcp-syn-sent.desc 2019-09-11 08:29:31.000000000 +0000 @@ -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', diff -Nru criu-3.12/test/zdtm/static/socket_udp-broadcast.c criu-3.13/test/zdtm/static/socket_udp-broadcast.c --- criu-3.12/test/zdtm/static/socket_udp-broadcast.c 1970-01-01 00:00:00.000000000 +0000 +++ criu-3.13/test/zdtm/static/socket_udp-broadcast.c 2019-09-11 08:29:31.000000000 +0000 @@ -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; +} diff -Nru criu-3.12/test/zdtm/static/socket_udplite.c criu-3.13/test/zdtm/static/socket_udplite.c --- criu-3.12/test/zdtm/static/socket_udplite.c 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/test/zdtm/static/socket_udplite.c 2019-09-11 08:29:31.000000000 +0000 @@ -28,9 +28,9 @@ 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 @@ 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(); diff -Nru criu-3.12/test/zdtm/static/sock_peercred.c criu-3.13/test/zdtm/static/sock_peercred.c --- criu-3.12/test/zdtm/static/sock_peercred.c 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/test/zdtm/static/sock_peercred.c 2019-09-11 08:29:31.000000000 +0000 @@ -67,6 +67,7 @@ socklen_t len; char *stack; pid_t pid; + int exit_code = 1; test_init(argc, argv); @@ -78,7 +79,7 @@ stack = malloc(2 * STACK_SIZE); if (!stack) { pr_err("malloc\n"); - return 1; + goto out; } /* Find unused fd */ @@ -89,18 +90,18 @@ 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 @@ 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 -Nru criu-3.12/test/zdtm/static/unlink_multiple_largefiles.c criu-3.13/test/zdtm/static/unlink_multiple_largefiles.c --- criu-3.12/test/zdtm/static/unlink_multiple_largefiles.c 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/test/zdtm/static/unlink_multiple_largefiles.c 2019-09-11 08:29:31.000000000 +0000 @@ -31,7 +31,7 @@ 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 @@ 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 @@ 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 -Nru criu-3.12/test/zdtm/static/vdso01.c criu-3.13/test/zdtm/static/vdso01.c --- criu-3.12/test/zdtm/static/vdso01.c 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/test/zdtm/static/vdso01.c 2019-09-11 08:29:31.000000000 +0000 @@ -246,7 +246,7 @@ 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; diff -Nru criu-3.12/test/zdtm/static/vdso-proxy.c criu-3.13/test/zdtm/static/vdso-proxy.c --- criu-3.12/test/zdtm/static/vdso-proxy.c 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/test/zdtm/static/vdso-proxy.c 2019-09-11 08:29:31.000000000 +0000 @@ -1,3 +1,4 @@ +#include #include #include @@ -7,7 +8,11 @@ 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() + */ #define MAX_VMAS 80 #define BUF_SIZE 1024 @@ -18,8 +23,13 @@ * 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,20 +53,33 @@ 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); + +#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: [%#lx, %#lx]\n", v->start, v->end); + test_msg("[NOTE]\tVMA: [%#" PRIx64 ", %#" PRIx64 "]\n", + 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; } @@ -88,7 +111,7 @@ 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 +121,7 @@ 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--; } diff -Nru criu-3.12/test/zdtm/transition/lazy-thp.c criu-3.13/test/zdtm/transition/lazy-thp.c --- criu-3.12/test/zdtm/transition/lazy-thp.c 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/test/zdtm/transition/lazy-thp.c 2019-09-11 08:29:31.000000000 +0000 @@ -57,5 +57,7 @@ } pass(); + free(org); + free(mem); return 0; } diff -Nru criu-3.12/test/zdtm/transition/thread-bomb.c criu-3.13/test/zdtm/transition/thread-bomb.c --- criu-3.12/test/zdtm/transition/thread-bomb.c 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/test/zdtm/transition/thread-bomb.c 2019-09-11 08:29:31.000000000 +0000 @@ -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 @@ 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 @@ 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(); diff -Nru criu-3.12/test/zdtm.py criu-3.13/test/zdtm.py --- criu-3.12/test/zdtm.py 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/test/zdtm.py 2019-09-11 08:29:31.000000000 +0000 @@ -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__))) @@ -31,26 +32,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 +60,17 @@ 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 +79,61 @@ 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 - - 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) + 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) 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 - - 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) + 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) + + 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 +148,161 @@ class host_flavor: - def __init__(self, opts): - self.name = "host" - self.ns = False - self.root = None - - def init(self, l_bins, x_bins): - pass - - def fini(self): - pass - - @staticmethod - def clean(): - pass + def __init__(self, opts): + self.name = "host" + self.ns = False + self.root = None + + def init(self, l_bins, x_bins): + pass + + def fini(self): + pass + + @staticmethod + def clean(): + pass class ns_flavor: - __root_dirs = ["/bin", "/sbin", "/etc", "/lib", "/lib64", "/dev", "/dev/pts", "/dev/net", "/tmp", "/usr", "/proc", "/run"] + __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) - 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') + 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, 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 + 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) + + @staticmethod + def clean(): + pass flavors = {'h': host_flavor, 'ns': ns_flavor, 'uns': userns_flavor} @@ -299,47 +314,47 @@ 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 - - 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 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) 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 +364,17 @@ 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 +382,440 @@ 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,462 +828,543 @@ 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) + @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 + if nowait and ret == 0: + return p - return ret + 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 = (opts['page_server'] and True or False) - self.__remote_lazy_pages = (opts['remote_lazy_pages'] and True or False) - 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']) - 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.__criu = (opts['rpc'] and criu_rpc or criu_cli) - self.__show_stats = (opts['show_stats'] and True or False) - self.__lazy_pages_p = None - self.__page_server_p = None - self.__dump_process = None - 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 __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 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() - - 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"] - 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 += self.__test.getdopts() - - 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"] - 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") - - 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.__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"): - 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"] - if self.__remote_lazy_pages: - ps_opts = ["--pidfile", "ps.pid", - "--port", "12345", "--lazy-pages"] - 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.__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 / 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)) + 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.__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.__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)) # @@ -1256,583 +1375,615 @@ 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 + if opts['nocr']: + return - cr_api.set_test(test) + 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') + 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]) + 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) + 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") + 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 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 freeze(self): - if self.__state.startswith('f'): - self.__set_state('FROZEN') - - def thaw(self): - if self.__state.startswith('f'): - self.__set_state('THAWED') + 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 __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 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 + 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 + if not is_thread_stopped(get_thread_status("/proc/%s" % pid)): + return False - return True + 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', - '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', + '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()) + 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 + 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 @@ -1840,355 +1991,363 @@ 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 or "Warn" 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 = {} + 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() + 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']) - - 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") - - for t in tlist: - if excl and excl.match(t): - continue - - 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) - - groups += pend_groups - - 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 - - fn = os.path.join("groups", "%s.%d" % (suf, nr)) - g.dump(fn) - nr += 1 + excl = None + groups = [] + pend_groups = [] + maxs = int(opts['max_size']) + + 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") + + for t in tlist: + if excl and excl.match(t): + continue + + 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) + + groups += pend_groups + + 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 + + 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() # @@ -2196,102 +2355,167 @@ # 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 + # 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) + 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("-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("--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() diff -Nru criu-3.12/.travis.yml criu-3.13/.travis.yml --- criu-3.12/.travis.yml 2019-04-25 17:26:57.000000000 +0000 +++ criu-3.13/.travis.yml 2019-09-11 08:29:31.000000000 +0000 @@ -25,6 +25,7 @@ - TR_ARCH=fedora-rawhide - TR_ARCH=fedora-rawhide-aarch64 - TR_ARCH=centos + - TR_ARCH=podman-test matrix: allow_failures: - env: TR_ARCH=docker-test