[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