diff --git a/criu/apparmor.c b/criu/apparmor.c index 8c999b3f5..328fc606b 100644 --- a/criu/apparmor.c +++ b/criu/apparmor.c @@ -361,6 +361,28 @@ err: "}\n") char policydir[PATH_MAX] = ".criu.temp-aa-policy.XXXXXX"; +char cachedir[PATH_MAX]; + +struct apparmor_parser_args { + char *cache; + char *file; +}; + +static int apparmor_parser_exec(void *data) +{ + struct apparmor_parser_args *args = data; + + execlp("apparmor_parser", "apparmor_parser", "-QWL", args->cache, args->file, NULL); + + return -1; +} + +static int apparmor_cache_exec(void *data) +{ + execlp("apparmor_parser", "apparmor_parser", "--cache-loc", "/", "--print-cache-dir", (char *)NULL); + + return -1; +} static void *get_suspend_policy(char *name, off_t *len) { @@ -368,8 +390,9 @@ static void *get_suspend_policy(char *name, off_t *len) void *ret = NULL; int n, fd, policy_len, i; struct stat sb; - char *cmd[] = { - "apparmor_parser", "-QWL", cache, file, NULL, + struct apparmor_parser_args args = { + .cache = cache, + .file = file, }; *len = 0; @@ -416,13 +439,19 @@ static void *get_suspend_policy(char *name, off_t *len) return NULL; } - n = cr_system(-1, -1, -1, cmd[0], cmd, 0); - if (n < 0 || !WIFEXITED(n) || WEXITSTATUS(n)) { + n = run_command(cachedir, sizeof(cachedir), apparmor_cache_exec, NULL); + if (n < 0) { pr_err("apparmor parsing failed %d\n", n); return NULL; } - n = snprintf(file, sizeof(file), "%s/cache/%s", policydir, clean_name); + n = run_command(NULL, 0, apparmor_parser_exec, &args); + if (n < 0) { + pr_err("apparmor parsing failed %d\n", n); + return NULL; + } + + n = snprintf(file, sizeof(file), "%s/cache/%s/%s", policydir, cachedir, clean_name); if (n < 0 || n >= sizeof(policy)) { pr_err("policy name %s too long\n", clean_name); return NULL; diff --git a/criu/include/util.h b/criu/include/util.h index 58c70b9fe..a2dac2233 100644 --- a/criu/include/util.h +++ b/criu/include/util.h @@ -391,4 +391,6 @@ static inline void cleanup_freep(void *p) free(*pp); } +extern int run_command(char *buf, size_t buf_size, int (*child_fn)(void *), void *args); + #endif /* __CR_UTIL_H__ */ diff --git a/criu/util.c b/criu/util.c index 1c3c3c67b..06124c220 100644 --- a/criu/util.c +++ b/criu/util.c @@ -43,6 +43,7 @@ #include "cr-service.h" #include "files.h" #include "pstree.h" +#include "sched.h" #include "cr-errno.h" #include "action-scripts.h" @@ -1651,3 +1652,166 @@ out: target[offset] = 0; return ret; } + +__attribute__((returns_twice)) static pid_t raw_legacy_clone(unsigned long flags, int *pidfd) +{ +#if defined(__s390x__) || defined(__s390__) || defined(__CRIS__) + /* On s390/s390x and cris the order of the first and second arguments + * of the system call is reversed. + */ + return syscall(__NR_clone, NULL, flags | SIGCHLD, pidfd); +#elif defined(__sparc__) && defined(__arch64__) + { + /* + * sparc64 always returns the other process id in %o0, and a + * boolean flag whether this is the child or the parent in %o1. + * Inline assembly is needed to get the flag returned in %o1. + */ + register long g1 asm("g1") = __NR_clone; + register long o0 asm("o0") = flags | SIGCHLD; + register long o1 asm("o1") = 0; /* is parent/child indicator */ + register long o2 asm("o2") = (unsigned long)pidfd; + long is_error, retval, in_child; + pid_t child_pid; + + asm volatile( +#if defined(__arch64__) + "t 0x6d\n\t" /* 64-bit trap */ +#else + "t 0x10\n\t" /* 32-bit trap */ +#endif + /* + * catch errors: On sparc, the carry bit (csr) in the + * processor status register (psr) is used instead of a + * full register. + */ + "addx %%g0, 0, %%g1" + : "=r"(g1), "=r"(o0), "=r"(o1), "=r"(o2) /* outputs */ + : "r"(g1), "r"(o0), "r"(o1), "r"(o2) /* inputs */ + : "%cc"); /* clobbers */ + + is_error = g1; + retval = o0; + in_child = o1; + + if (is_error) { + errno = retval; + return -1; + } + + if (in_child) + return 0; + + child_pid = retval; + return child_pid; + } +#elif defined(__ia64__) + /* On ia64 the stack and stack size are passed as separate arguments. */ + return syscall(__NR_clone, flags | SIGCHLD, NULL, 0, pidfd); +#else + return syscall(__NR_clone, flags | SIGCHLD, NULL, pidfd); +#endif +} + +__attribute__((returns_twice)) static pid_t raw_clone(unsigned long flags, int *pidfd) +{ + pid_t pid; + struct _clone_args args = { + .flags = flags, + .pidfd = ptr_to_u64(pidfd), + }; + + if (flags & (CLONE_VM | CLONE_PARENT_SETTID | CLONE_CHILD_SETTID | CLONE_CHILD_CLEARTID | CLONE_SETTLS)) + return -EINVAL; + + /* On CLONE_PARENT we inherit the parent's exit signal. */ + if (!(flags & CLONE_PARENT)) + args.exit_signal = SIGCHLD; + + pid = syscall(__NR_clone3, &args, sizeof(args)); + if (pid < 0 && errno == ENOSYS) + return raw_legacy_clone(flags, pidfd); + + return pid; +} + +static int wait_for_pid(pid_t pid) +{ + int status, ret; + +again: + ret = waitpid(pid, &status, 0); + if (ret == -1) { + if (errno == EINTR) + goto again; + + return -1; + } + + if (ret != pid) + goto again; + + if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) + return -1; + + return 0; +} + +int run_command(char *buf, size_t buf_size, int (*child_fn)(void *), void *args) +{ + pid_t child; + int ret, fret, pipefd[2]; + ssize_t bytes; + + /* Make sure our callers do not receive uninitialized memory. */ + if (buf_size > 0 && buf) + buf[0] = '\0'; + + if (pipe(pipefd) < 0) + return -1; + + child = raw_clone(0, NULL); + if (child < 0) { + close(pipefd[0]); + close(pipefd[1]); + return -1; + } + + if (child == 0) { + /* Close the read-end of the pipe. */ + close(pipefd[0]); + + /* Redirect std{err,out} to write-end of the + * pipe. + */ + ret = dup2(pipefd[1], STDOUT_FILENO); + if (ret >= 0) + ret = dup2(pipefd[1], STDERR_FILENO); + + /* Close the write-end of the pipe. */ + close(pipefd[1]); + + if (ret < 0) + _exit(EXIT_FAILURE); + + /* Does not return. */ + child_fn(args); + _exit(EXIT_FAILURE); + } + + /* close the write-end of the pipe */ + close(pipefd[1]); + + if (buf && buf_size > 0) { + bytes = read_all(pipefd[0], buf, buf_size - 1); + if (bytes > 0) + buf[bytes - 1] = '\0'; + } + + fret = wait_for_pid(child); + + /* close the read-end of the pipe */ + close(pipefd[0]); + + return fret; +}