xref: /openbmc/linux/tools/lib/subcmd/run-command.c (revision 07eebccb)
14b6ab94eSJosh Poimboeuf #include <unistd.h>
24b6ab94eSJosh Poimboeuf #include <sys/types.h>
34b6ab94eSJosh Poimboeuf #include <sys/stat.h>
44b6ab94eSJosh Poimboeuf #include <fcntl.h>
54b6ab94eSJosh Poimboeuf #include <string.h>
607eebccbSArnaldo Carvalho de Melo #include <linux/string.h>
74b6ab94eSJosh Poimboeuf #include <errno.h>
84b6ab94eSJosh Poimboeuf #include <sys/wait.h>
94b6ab94eSJosh Poimboeuf #include "subcmd-util.h"
104b6ab94eSJosh Poimboeuf #include "run-command.h"
114b6ab94eSJosh Poimboeuf #include "exec-cmd.h"
124b6ab94eSJosh Poimboeuf 
134b6ab94eSJosh Poimboeuf #define STRERR_BUFSIZE 128
144b6ab94eSJosh Poimboeuf 
154b6ab94eSJosh Poimboeuf static inline void close_pair(int fd[2])
164b6ab94eSJosh Poimboeuf {
174b6ab94eSJosh Poimboeuf 	close(fd[0]);
184b6ab94eSJosh Poimboeuf 	close(fd[1]);
194b6ab94eSJosh Poimboeuf }
204b6ab94eSJosh Poimboeuf 
214b6ab94eSJosh Poimboeuf static inline void dup_devnull(int to)
224b6ab94eSJosh Poimboeuf {
234b6ab94eSJosh Poimboeuf 	int fd = open("/dev/null", O_RDWR);
244b6ab94eSJosh Poimboeuf 	dup2(fd, to);
254b6ab94eSJosh Poimboeuf 	close(fd);
264b6ab94eSJosh Poimboeuf }
274b6ab94eSJosh Poimboeuf 
284b6ab94eSJosh Poimboeuf int start_command(struct child_process *cmd)
294b6ab94eSJosh Poimboeuf {
304b6ab94eSJosh Poimboeuf 	int need_in, need_out, need_err;
314b6ab94eSJosh Poimboeuf 	int fdin[2], fdout[2], fderr[2];
324b6ab94eSJosh Poimboeuf 	char sbuf[STRERR_BUFSIZE];
334b6ab94eSJosh Poimboeuf 
344b6ab94eSJosh Poimboeuf 	/*
354b6ab94eSJosh Poimboeuf 	 * In case of errors we must keep the promise to close FDs
364b6ab94eSJosh Poimboeuf 	 * that have been passed in via ->in and ->out.
374b6ab94eSJosh Poimboeuf 	 */
384b6ab94eSJosh Poimboeuf 
394b6ab94eSJosh Poimboeuf 	need_in = !cmd->no_stdin && cmd->in < 0;
404b6ab94eSJosh Poimboeuf 	if (need_in) {
414b6ab94eSJosh Poimboeuf 		if (pipe(fdin) < 0) {
424b6ab94eSJosh Poimboeuf 			if (cmd->out > 0)
434b6ab94eSJosh Poimboeuf 				close(cmd->out);
444b6ab94eSJosh Poimboeuf 			return -ERR_RUN_COMMAND_PIPE;
454b6ab94eSJosh Poimboeuf 		}
464b6ab94eSJosh Poimboeuf 		cmd->in = fdin[1];
474b6ab94eSJosh Poimboeuf 	}
484b6ab94eSJosh Poimboeuf 
494b6ab94eSJosh Poimboeuf 	need_out = !cmd->no_stdout
504b6ab94eSJosh Poimboeuf 		&& !cmd->stdout_to_stderr
514b6ab94eSJosh Poimboeuf 		&& cmd->out < 0;
524b6ab94eSJosh Poimboeuf 	if (need_out) {
534b6ab94eSJosh Poimboeuf 		if (pipe(fdout) < 0) {
544b6ab94eSJosh Poimboeuf 			if (need_in)
554b6ab94eSJosh Poimboeuf 				close_pair(fdin);
564b6ab94eSJosh Poimboeuf 			else if (cmd->in)
574b6ab94eSJosh Poimboeuf 				close(cmd->in);
584b6ab94eSJosh Poimboeuf 			return -ERR_RUN_COMMAND_PIPE;
594b6ab94eSJosh Poimboeuf 		}
604b6ab94eSJosh Poimboeuf 		cmd->out = fdout[0];
614b6ab94eSJosh Poimboeuf 	}
624b6ab94eSJosh Poimboeuf 
634b6ab94eSJosh Poimboeuf 	need_err = !cmd->no_stderr && cmd->err < 0;
644b6ab94eSJosh Poimboeuf 	if (need_err) {
654b6ab94eSJosh Poimboeuf 		if (pipe(fderr) < 0) {
664b6ab94eSJosh Poimboeuf 			if (need_in)
674b6ab94eSJosh Poimboeuf 				close_pair(fdin);
684b6ab94eSJosh Poimboeuf 			else if (cmd->in)
694b6ab94eSJosh Poimboeuf 				close(cmd->in);
704b6ab94eSJosh Poimboeuf 			if (need_out)
714b6ab94eSJosh Poimboeuf 				close_pair(fdout);
724b6ab94eSJosh Poimboeuf 			else if (cmd->out)
734b6ab94eSJosh Poimboeuf 				close(cmd->out);
744b6ab94eSJosh Poimboeuf 			return -ERR_RUN_COMMAND_PIPE;
754b6ab94eSJosh Poimboeuf 		}
764b6ab94eSJosh Poimboeuf 		cmd->err = fderr[0];
774b6ab94eSJosh Poimboeuf 	}
784b6ab94eSJosh Poimboeuf 
794b6ab94eSJosh Poimboeuf 	fflush(NULL);
804b6ab94eSJosh Poimboeuf 	cmd->pid = fork();
814b6ab94eSJosh Poimboeuf 	if (!cmd->pid) {
824b6ab94eSJosh Poimboeuf 		if (cmd->no_stdin)
834b6ab94eSJosh Poimboeuf 			dup_devnull(0);
844b6ab94eSJosh Poimboeuf 		else if (need_in) {
854b6ab94eSJosh Poimboeuf 			dup2(fdin[0], 0);
864b6ab94eSJosh Poimboeuf 			close_pair(fdin);
874b6ab94eSJosh Poimboeuf 		} else if (cmd->in) {
884b6ab94eSJosh Poimboeuf 			dup2(cmd->in, 0);
894b6ab94eSJosh Poimboeuf 			close(cmd->in);
904b6ab94eSJosh Poimboeuf 		}
914b6ab94eSJosh Poimboeuf 
924b6ab94eSJosh Poimboeuf 		if (cmd->no_stderr)
934b6ab94eSJosh Poimboeuf 			dup_devnull(2);
944b6ab94eSJosh Poimboeuf 		else if (need_err) {
954b6ab94eSJosh Poimboeuf 			dup2(fderr[1], 2);
964b6ab94eSJosh Poimboeuf 			close_pair(fderr);
974b6ab94eSJosh Poimboeuf 		}
984b6ab94eSJosh Poimboeuf 
994b6ab94eSJosh Poimboeuf 		if (cmd->no_stdout)
1004b6ab94eSJosh Poimboeuf 			dup_devnull(1);
1014b6ab94eSJosh Poimboeuf 		else if (cmd->stdout_to_stderr)
1024b6ab94eSJosh Poimboeuf 			dup2(2, 1);
1034b6ab94eSJosh Poimboeuf 		else if (need_out) {
1044b6ab94eSJosh Poimboeuf 			dup2(fdout[1], 1);
1054b6ab94eSJosh Poimboeuf 			close_pair(fdout);
1064b6ab94eSJosh Poimboeuf 		} else if (cmd->out > 1) {
1074b6ab94eSJosh Poimboeuf 			dup2(cmd->out, 1);
1084b6ab94eSJosh Poimboeuf 			close(cmd->out);
1094b6ab94eSJosh Poimboeuf 		}
1104b6ab94eSJosh Poimboeuf 
1114b6ab94eSJosh Poimboeuf 		if (cmd->dir && chdir(cmd->dir))
1124b6ab94eSJosh Poimboeuf 			die("exec %s: cd to %s failed (%s)", cmd->argv[0],
11307eebccbSArnaldo Carvalho de Melo 			    cmd->dir, str_error_r(errno, sbuf, sizeof(sbuf)));
1144b6ab94eSJosh Poimboeuf 		if (cmd->env) {
1154b6ab94eSJosh Poimboeuf 			for (; *cmd->env; cmd->env++) {
1164b6ab94eSJosh Poimboeuf 				if (strchr(*cmd->env, '='))
1174b6ab94eSJosh Poimboeuf 					putenv((char*)*cmd->env);
1184b6ab94eSJosh Poimboeuf 				else
1194b6ab94eSJosh Poimboeuf 					unsetenv(*cmd->env);
1204b6ab94eSJosh Poimboeuf 			}
1214b6ab94eSJosh Poimboeuf 		}
1224b6ab94eSJosh Poimboeuf 		if (cmd->preexec_cb)
1234b6ab94eSJosh Poimboeuf 			cmd->preexec_cb();
1244b6ab94eSJosh Poimboeuf 		if (cmd->exec_cmd) {
1254b6ab94eSJosh Poimboeuf 			execv_cmd(cmd->argv);
1264b6ab94eSJosh Poimboeuf 		} else {
1274b6ab94eSJosh Poimboeuf 			execvp(cmd->argv[0], (char *const*) cmd->argv);
1284b6ab94eSJosh Poimboeuf 		}
1294b6ab94eSJosh Poimboeuf 		exit(127);
1304b6ab94eSJosh Poimboeuf 	}
1314b6ab94eSJosh Poimboeuf 
1324b6ab94eSJosh Poimboeuf 	if (cmd->pid < 0) {
1334b6ab94eSJosh Poimboeuf 		int err = errno;
1344b6ab94eSJosh Poimboeuf 		if (need_in)
1354b6ab94eSJosh Poimboeuf 			close_pair(fdin);
1364b6ab94eSJosh Poimboeuf 		else if (cmd->in)
1374b6ab94eSJosh Poimboeuf 			close(cmd->in);
1384b6ab94eSJosh Poimboeuf 		if (need_out)
1394b6ab94eSJosh Poimboeuf 			close_pair(fdout);
1404b6ab94eSJosh Poimboeuf 		else if (cmd->out)
1414b6ab94eSJosh Poimboeuf 			close(cmd->out);
1424b6ab94eSJosh Poimboeuf 		if (need_err)
1434b6ab94eSJosh Poimboeuf 			close_pair(fderr);
1444b6ab94eSJosh Poimboeuf 		return err == ENOENT ?
1454b6ab94eSJosh Poimboeuf 			-ERR_RUN_COMMAND_EXEC :
1464b6ab94eSJosh Poimboeuf 			-ERR_RUN_COMMAND_FORK;
1474b6ab94eSJosh Poimboeuf 	}
1484b6ab94eSJosh Poimboeuf 
1494b6ab94eSJosh Poimboeuf 	if (need_in)
1504b6ab94eSJosh Poimboeuf 		close(fdin[0]);
1514b6ab94eSJosh Poimboeuf 	else if (cmd->in)
1524b6ab94eSJosh Poimboeuf 		close(cmd->in);
1534b6ab94eSJosh Poimboeuf 
1544b6ab94eSJosh Poimboeuf 	if (need_out)
1554b6ab94eSJosh Poimboeuf 		close(fdout[1]);
1564b6ab94eSJosh Poimboeuf 	else if (cmd->out)
1574b6ab94eSJosh Poimboeuf 		close(cmd->out);
1584b6ab94eSJosh Poimboeuf 
1594b6ab94eSJosh Poimboeuf 	if (need_err)
1604b6ab94eSJosh Poimboeuf 		close(fderr[1]);
1614b6ab94eSJosh Poimboeuf 
1624b6ab94eSJosh Poimboeuf 	return 0;
1634b6ab94eSJosh Poimboeuf }
1644b6ab94eSJosh Poimboeuf 
1654b6ab94eSJosh Poimboeuf static int wait_or_whine(pid_t pid)
1664b6ab94eSJosh Poimboeuf {
1674b6ab94eSJosh Poimboeuf 	char sbuf[STRERR_BUFSIZE];
1684b6ab94eSJosh Poimboeuf 
1694b6ab94eSJosh Poimboeuf 	for (;;) {
1704b6ab94eSJosh Poimboeuf 		int status, code;
1714b6ab94eSJosh Poimboeuf 		pid_t waiting = waitpid(pid, &status, 0);
1724b6ab94eSJosh Poimboeuf 
1734b6ab94eSJosh Poimboeuf 		if (waiting < 0) {
1744b6ab94eSJosh Poimboeuf 			if (errno == EINTR)
1754b6ab94eSJosh Poimboeuf 				continue;
1764b6ab94eSJosh Poimboeuf 			fprintf(stderr, " Error: waitpid failed (%s)",
17707eebccbSArnaldo Carvalho de Melo 				str_error_r(errno, sbuf, sizeof(sbuf)));
1784b6ab94eSJosh Poimboeuf 			return -ERR_RUN_COMMAND_WAITPID;
1794b6ab94eSJosh Poimboeuf 		}
1804b6ab94eSJosh Poimboeuf 		if (waiting != pid)
1814b6ab94eSJosh Poimboeuf 			return -ERR_RUN_COMMAND_WAITPID_WRONG_PID;
1824b6ab94eSJosh Poimboeuf 		if (WIFSIGNALED(status))
1834b6ab94eSJosh Poimboeuf 			return -ERR_RUN_COMMAND_WAITPID_SIGNAL;
1844b6ab94eSJosh Poimboeuf 
1854b6ab94eSJosh Poimboeuf 		if (!WIFEXITED(status))
1864b6ab94eSJosh Poimboeuf 			return -ERR_RUN_COMMAND_WAITPID_NOEXIT;
1874b6ab94eSJosh Poimboeuf 		code = WEXITSTATUS(status);
1884b6ab94eSJosh Poimboeuf 		switch (code) {
1894b6ab94eSJosh Poimboeuf 		case 127:
1904b6ab94eSJosh Poimboeuf 			return -ERR_RUN_COMMAND_EXEC;
1914b6ab94eSJosh Poimboeuf 		case 0:
1924b6ab94eSJosh Poimboeuf 			return 0;
1934b6ab94eSJosh Poimboeuf 		default:
1944b6ab94eSJosh Poimboeuf 			return -code;
1954b6ab94eSJosh Poimboeuf 		}
1964b6ab94eSJosh Poimboeuf 	}
1974b6ab94eSJosh Poimboeuf }
1984b6ab94eSJosh Poimboeuf 
1994b6ab94eSJosh Poimboeuf int finish_command(struct child_process *cmd)
2004b6ab94eSJosh Poimboeuf {
2014b6ab94eSJosh Poimboeuf 	return wait_or_whine(cmd->pid);
2024b6ab94eSJosh Poimboeuf }
2034b6ab94eSJosh Poimboeuf 
2044b6ab94eSJosh Poimboeuf int run_command(struct child_process *cmd)
2054b6ab94eSJosh Poimboeuf {
2064b6ab94eSJosh Poimboeuf 	int code = start_command(cmd);
2074b6ab94eSJosh Poimboeuf 	if (code)
2084b6ab94eSJosh Poimboeuf 		return code;
2094b6ab94eSJosh Poimboeuf 	return finish_command(cmd);
2104b6ab94eSJosh Poimboeuf }
2114b6ab94eSJosh Poimboeuf 
2124b6ab94eSJosh Poimboeuf static void prepare_run_command_v_opt(struct child_process *cmd,
2134b6ab94eSJosh Poimboeuf 				      const char **argv,
2144b6ab94eSJosh Poimboeuf 				      int opt)
2154b6ab94eSJosh Poimboeuf {
2164b6ab94eSJosh Poimboeuf 	memset(cmd, 0, sizeof(*cmd));
2174b6ab94eSJosh Poimboeuf 	cmd->argv = argv;
2184b6ab94eSJosh Poimboeuf 	cmd->no_stdin = opt & RUN_COMMAND_NO_STDIN ? 1 : 0;
2194b6ab94eSJosh Poimboeuf 	cmd->exec_cmd = opt & RUN_EXEC_CMD ? 1 : 0;
2204b6ab94eSJosh Poimboeuf 	cmd->stdout_to_stderr = opt & RUN_COMMAND_STDOUT_TO_STDERR ? 1 : 0;
2214b6ab94eSJosh Poimboeuf }
2224b6ab94eSJosh Poimboeuf 
2234b6ab94eSJosh Poimboeuf int run_command_v_opt(const char **argv, int opt)
2244b6ab94eSJosh Poimboeuf {
2254b6ab94eSJosh Poimboeuf 	struct child_process cmd;
2264b6ab94eSJosh Poimboeuf 	prepare_run_command_v_opt(&cmd, argv, opt);
2274b6ab94eSJosh Poimboeuf 	return run_command(&cmd);
2284b6ab94eSJosh Poimboeuf }
229