[CRIU] [PATCH cr 1/2] util: add a function for executing an
extrenal tools (v2)
Andrey Vagin
avagin at openvz.org
Tue Sep 25 18:05:41 EDT 2012
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 at openvz.org>
---
include/util.h | 2 +
util.c | 106 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 108 insertions(+), 0 deletions(-)
diff --git a/include/util.h b/include/util.h
index 93749b3..ae74fa0 100644
--- a/include/util.h
+++ b/include/util.h
@@ -325,4 +325,6 @@ extern void *shmalloc(size_t bytes);
extern void shfree_last(void *ptr);
extern int run_scripts(char *action);
+extern int cr_system(int in, int out, int err, char *cmd, char *const argv[]);
+
#endif /* UTIL_H_ */
diff --git a/util.c b/util.c
index e459527..db9deed 100644
--- a/util.c
+++ b/util.c
@@ -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 set mask of blocked signals");
+ BUG();
+ }
+
+ return ret;
+}
--
1.7.1
More information about the CRIU
mailing list