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