mirror of
https://github.com/checkpoint-restore/criu.git
synced 2026-01-23 02:14:37 +00:00
util: add run_command()
Make it easy to run commands and capture the output in a user provided buffer. Signed-off-by: Christian Brauner <christian.brauner@ubuntu.com>
This commit is contained in:
parent
9422383b6b
commit
5dc373385d
3 changed files with 200 additions and 5 deletions
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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__ */
|
||||
|
|
|
|||
164
criu/util.c
164
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;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue