2
0
mirror of https://github.com/checkpoint-restore/criu synced 2025-08-31 14:25:49 +00:00

util: add a function for executing an extrenal tools (v2)

For executing an external tools we need to block a SIGCHLD
and to juggle file descriptors.

SIGCHLD is blocked for getting an exit code.

A problem with file descriptors can be if we want to set 2 to STDIN,
1 to STDERR, 0 to STDOUT for example.

v2: use helpers reopen_fd_as and move_img_fd

Signed-off-by: Andrey Vagin <avagin@openvz.org>
Signed-off-by: Pavel Emelyanov <xemul@parallels.com>
This commit is contained in:
Andrey Vagin
2012-09-28 14:09:57 +04:00
committed by Pavel Emelyanov
parent af02c2698f
commit cd2be93ba4
2 changed files with 108 additions and 0 deletions

106
util.c
View File

@@ -404,3 +404,109 @@ int run_scripts(char *action)
unsetenv("CRTOOLS_SCRIPT_ACTION");
return ret;
}
#define DUP_SAFE(fd, out) \
({ \
int ret__; \
ret__ = dup(fd); \
if (ret__ == -1) { \
pr_perror("dup(%d) failed", fd); \
goto out; \
} \
ret__; \
})
/*
* If "in" is negative, stdin will be closed.
* If "out" or "err" are negative, a log file descriptor will be used.
*/
int cr_system(int in, int out, int err, char *cmd, char *const argv[])
{
sigset_t blockmask, oldmask;
int ret = -1, status;
pid_t pid;
sigemptyset(&blockmask);
sigaddset(&blockmask, SIGCHLD);
if (sigprocmask(SIG_BLOCK, &blockmask, &oldmask) == -1) {
pr_perror("Can not set mask of blocked signals");
return -1;
}
pid = fork();
if (pid == -1) {
pr_perror("fork() failed\n");
goto out;
} else if (pid == 0) {
if (out < 0)
out = log_get_fd();
if (err < 0)
err = log_get_fd();
/*
* out, err, in should be a separate fds,
* because reopen_fd_as() closes an old fd
*/
if (err == out || err == in)
err = DUP_SAFE(err, out_chld);
if (out == in)
out = DUP_SAFE(out, out_chld);
if (in < 0) {
close(STDIN_FILENO);
} else {
if (move_img_fd(&out, STDIN_FILENO) ||
move_img_fd(&err, STDIN_FILENO))
goto out_chld;
if (reopen_fd_as_nocheck(STDIN_FILENO, in))
goto out_chld;
}
if (move_img_fd(&err, STDOUT_FILENO))
goto out_chld;
if (reopen_fd_as_nocheck(STDOUT_FILENO, out))
goto out_chld;
if (reopen_fd_as_nocheck(STDERR_FILENO, err))
goto out_chld;
execvp(cmd, argv);
pr_perror("exec failed");
out_chld:
_exit(1);
}
while (1) {
ret = waitpid(pid, &status, 0);
if (ret == -1) {
pr_perror("waitpid() failed");
goto out;
}
if (WIFEXITED(status)) {
if (WEXITSTATUS(status))
pr_err("exited, status=%d\n", WEXITSTATUS(status));
break;
} else if (WIFSIGNALED(status)) {
pr_err("killed by signal %d\n", WTERMSIG(status));
break;
} else if (WIFSTOPPED(status)) {
pr_err("stopped by signal %d\n", WSTOPSIG(status));
} else if (WIFCONTINUED(status)) {
pr_err("continued\n");
}
}
ret = status ? -1 : 0;
out:
if (sigprocmask(SIG_SETMASK, &oldmask, NULL) == -1) {
pr_perror("Can not unset mask of blocked signals");
BUG();
}
return ret;
}