18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci#include <unistd.h>
38c2ecf20Sopenharmony_ci#include <sys/types.h>
48c2ecf20Sopenharmony_ci#include <sys/stat.h>
58c2ecf20Sopenharmony_ci#include <fcntl.h>
68c2ecf20Sopenharmony_ci#include <string.h>
78c2ecf20Sopenharmony_ci#include <linux/string.h>
88c2ecf20Sopenharmony_ci#include <errno.h>
98c2ecf20Sopenharmony_ci#include <sys/wait.h>
108c2ecf20Sopenharmony_ci#include "subcmd-util.h"
118c2ecf20Sopenharmony_ci#include "run-command.h"
128c2ecf20Sopenharmony_ci#include "exec-cmd.h"
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_ci#define STRERR_BUFSIZE 128
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_cistatic inline void close_pair(int fd[2])
178c2ecf20Sopenharmony_ci{
188c2ecf20Sopenharmony_ci	close(fd[0]);
198c2ecf20Sopenharmony_ci	close(fd[1]);
208c2ecf20Sopenharmony_ci}
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_cistatic inline void dup_devnull(int to)
238c2ecf20Sopenharmony_ci{
248c2ecf20Sopenharmony_ci	int fd = open("/dev/null", O_RDWR);
258c2ecf20Sopenharmony_ci	dup2(fd, to);
268c2ecf20Sopenharmony_ci	close(fd);
278c2ecf20Sopenharmony_ci}
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_ciint start_command(struct child_process *cmd)
308c2ecf20Sopenharmony_ci{
318c2ecf20Sopenharmony_ci	int need_in, need_out, need_err;
328c2ecf20Sopenharmony_ci	int fdin[2], fdout[2], fderr[2];
338c2ecf20Sopenharmony_ci	char sbuf[STRERR_BUFSIZE];
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci	/*
368c2ecf20Sopenharmony_ci	 * In case of errors we must keep the promise to close FDs
378c2ecf20Sopenharmony_ci	 * that have been passed in via ->in and ->out.
388c2ecf20Sopenharmony_ci	 */
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_ci	need_in = !cmd->no_stdin && cmd->in < 0;
418c2ecf20Sopenharmony_ci	if (need_in) {
428c2ecf20Sopenharmony_ci		if (pipe(fdin) < 0) {
438c2ecf20Sopenharmony_ci			if (cmd->out > 0)
448c2ecf20Sopenharmony_ci				close(cmd->out);
458c2ecf20Sopenharmony_ci			return -ERR_RUN_COMMAND_PIPE;
468c2ecf20Sopenharmony_ci		}
478c2ecf20Sopenharmony_ci		cmd->in = fdin[1];
488c2ecf20Sopenharmony_ci	}
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_ci	need_out = !cmd->no_stdout
518c2ecf20Sopenharmony_ci		&& !cmd->stdout_to_stderr
528c2ecf20Sopenharmony_ci		&& cmd->out < 0;
538c2ecf20Sopenharmony_ci	if (need_out) {
548c2ecf20Sopenharmony_ci		if (pipe(fdout) < 0) {
558c2ecf20Sopenharmony_ci			if (need_in)
568c2ecf20Sopenharmony_ci				close_pair(fdin);
578c2ecf20Sopenharmony_ci			else if (cmd->in)
588c2ecf20Sopenharmony_ci				close(cmd->in);
598c2ecf20Sopenharmony_ci			return -ERR_RUN_COMMAND_PIPE;
608c2ecf20Sopenharmony_ci		}
618c2ecf20Sopenharmony_ci		cmd->out = fdout[0];
628c2ecf20Sopenharmony_ci	}
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci	need_err = !cmd->no_stderr && cmd->err < 0;
658c2ecf20Sopenharmony_ci	if (need_err) {
668c2ecf20Sopenharmony_ci		if (pipe(fderr) < 0) {
678c2ecf20Sopenharmony_ci			if (need_in)
688c2ecf20Sopenharmony_ci				close_pair(fdin);
698c2ecf20Sopenharmony_ci			else if (cmd->in)
708c2ecf20Sopenharmony_ci				close(cmd->in);
718c2ecf20Sopenharmony_ci			if (need_out)
728c2ecf20Sopenharmony_ci				close_pair(fdout);
738c2ecf20Sopenharmony_ci			else if (cmd->out)
748c2ecf20Sopenharmony_ci				close(cmd->out);
758c2ecf20Sopenharmony_ci			return -ERR_RUN_COMMAND_PIPE;
768c2ecf20Sopenharmony_ci		}
778c2ecf20Sopenharmony_ci		cmd->err = fderr[0];
788c2ecf20Sopenharmony_ci	}
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci	fflush(NULL);
818c2ecf20Sopenharmony_ci	cmd->pid = fork();
828c2ecf20Sopenharmony_ci	if (!cmd->pid) {
838c2ecf20Sopenharmony_ci		if (cmd->no_stdin)
848c2ecf20Sopenharmony_ci			dup_devnull(0);
858c2ecf20Sopenharmony_ci		else if (need_in) {
868c2ecf20Sopenharmony_ci			dup2(fdin[0], 0);
878c2ecf20Sopenharmony_ci			close_pair(fdin);
888c2ecf20Sopenharmony_ci		} else if (cmd->in) {
898c2ecf20Sopenharmony_ci			dup2(cmd->in, 0);
908c2ecf20Sopenharmony_ci			close(cmd->in);
918c2ecf20Sopenharmony_ci		}
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci		if (cmd->no_stderr)
948c2ecf20Sopenharmony_ci			dup_devnull(2);
958c2ecf20Sopenharmony_ci		else if (need_err) {
968c2ecf20Sopenharmony_ci			dup2(fderr[1], 2);
978c2ecf20Sopenharmony_ci			close_pair(fderr);
988c2ecf20Sopenharmony_ci		}
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ci		if (cmd->no_stdout)
1018c2ecf20Sopenharmony_ci			dup_devnull(1);
1028c2ecf20Sopenharmony_ci		else if (cmd->stdout_to_stderr)
1038c2ecf20Sopenharmony_ci			dup2(2, 1);
1048c2ecf20Sopenharmony_ci		else if (need_out) {
1058c2ecf20Sopenharmony_ci			dup2(fdout[1], 1);
1068c2ecf20Sopenharmony_ci			close_pair(fdout);
1078c2ecf20Sopenharmony_ci		} else if (cmd->out > 1) {
1088c2ecf20Sopenharmony_ci			dup2(cmd->out, 1);
1098c2ecf20Sopenharmony_ci			close(cmd->out);
1108c2ecf20Sopenharmony_ci		}
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci		if (cmd->dir && chdir(cmd->dir))
1138c2ecf20Sopenharmony_ci			die("exec %s: cd to %s failed (%s)", cmd->argv[0],
1148c2ecf20Sopenharmony_ci			    cmd->dir, str_error_r(errno, sbuf, sizeof(sbuf)));
1158c2ecf20Sopenharmony_ci		if (cmd->env) {
1168c2ecf20Sopenharmony_ci			for (; *cmd->env; cmd->env++) {
1178c2ecf20Sopenharmony_ci				if (strchr(*cmd->env, '='))
1188c2ecf20Sopenharmony_ci					putenv((char*)*cmd->env);
1198c2ecf20Sopenharmony_ci				else
1208c2ecf20Sopenharmony_ci					unsetenv(*cmd->env);
1218c2ecf20Sopenharmony_ci			}
1228c2ecf20Sopenharmony_ci		}
1238c2ecf20Sopenharmony_ci		if (cmd->preexec_cb)
1248c2ecf20Sopenharmony_ci			cmd->preexec_cb();
1258c2ecf20Sopenharmony_ci		if (cmd->exec_cmd) {
1268c2ecf20Sopenharmony_ci			execv_cmd(cmd->argv);
1278c2ecf20Sopenharmony_ci		} else {
1288c2ecf20Sopenharmony_ci			execvp(cmd->argv[0], (char *const*) cmd->argv);
1298c2ecf20Sopenharmony_ci		}
1308c2ecf20Sopenharmony_ci		exit(127);
1318c2ecf20Sopenharmony_ci	}
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ci	if (cmd->pid < 0) {
1348c2ecf20Sopenharmony_ci		int err = errno;
1358c2ecf20Sopenharmony_ci		if (need_in)
1368c2ecf20Sopenharmony_ci			close_pair(fdin);
1378c2ecf20Sopenharmony_ci		else if (cmd->in)
1388c2ecf20Sopenharmony_ci			close(cmd->in);
1398c2ecf20Sopenharmony_ci		if (need_out)
1408c2ecf20Sopenharmony_ci			close_pair(fdout);
1418c2ecf20Sopenharmony_ci		else if (cmd->out)
1428c2ecf20Sopenharmony_ci			close(cmd->out);
1438c2ecf20Sopenharmony_ci		if (need_err)
1448c2ecf20Sopenharmony_ci			close_pair(fderr);
1458c2ecf20Sopenharmony_ci		return err == ENOENT ?
1468c2ecf20Sopenharmony_ci			-ERR_RUN_COMMAND_EXEC :
1478c2ecf20Sopenharmony_ci			-ERR_RUN_COMMAND_FORK;
1488c2ecf20Sopenharmony_ci	}
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ci	if (need_in)
1518c2ecf20Sopenharmony_ci		close(fdin[0]);
1528c2ecf20Sopenharmony_ci	else if (cmd->in)
1538c2ecf20Sopenharmony_ci		close(cmd->in);
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_ci	if (need_out)
1568c2ecf20Sopenharmony_ci		close(fdout[1]);
1578c2ecf20Sopenharmony_ci	else if (cmd->out)
1588c2ecf20Sopenharmony_ci		close(cmd->out);
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_ci	if (need_err)
1618c2ecf20Sopenharmony_ci		close(fderr[1]);
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_ci	return 0;
1648c2ecf20Sopenharmony_ci}
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_cistatic int wait_or_whine(pid_t pid)
1678c2ecf20Sopenharmony_ci{
1688c2ecf20Sopenharmony_ci	char sbuf[STRERR_BUFSIZE];
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_ci	for (;;) {
1718c2ecf20Sopenharmony_ci		int status, code;
1728c2ecf20Sopenharmony_ci		pid_t waiting = waitpid(pid, &status, 0);
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_ci		if (waiting < 0) {
1758c2ecf20Sopenharmony_ci			if (errno == EINTR)
1768c2ecf20Sopenharmony_ci				continue;
1778c2ecf20Sopenharmony_ci			fprintf(stderr, " Error: waitpid failed (%s)",
1788c2ecf20Sopenharmony_ci				str_error_r(errno, sbuf, sizeof(sbuf)));
1798c2ecf20Sopenharmony_ci			return -ERR_RUN_COMMAND_WAITPID;
1808c2ecf20Sopenharmony_ci		}
1818c2ecf20Sopenharmony_ci		if (waiting != pid)
1828c2ecf20Sopenharmony_ci			return -ERR_RUN_COMMAND_WAITPID_WRONG_PID;
1838c2ecf20Sopenharmony_ci		if (WIFSIGNALED(status))
1848c2ecf20Sopenharmony_ci			return -ERR_RUN_COMMAND_WAITPID_SIGNAL;
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_ci		if (!WIFEXITED(status))
1878c2ecf20Sopenharmony_ci			return -ERR_RUN_COMMAND_WAITPID_NOEXIT;
1888c2ecf20Sopenharmony_ci		code = WEXITSTATUS(status);
1898c2ecf20Sopenharmony_ci		switch (code) {
1908c2ecf20Sopenharmony_ci		case 127:
1918c2ecf20Sopenharmony_ci			return -ERR_RUN_COMMAND_EXEC;
1928c2ecf20Sopenharmony_ci		case 0:
1938c2ecf20Sopenharmony_ci			return 0;
1948c2ecf20Sopenharmony_ci		default:
1958c2ecf20Sopenharmony_ci			return -code;
1968c2ecf20Sopenharmony_ci		}
1978c2ecf20Sopenharmony_ci	}
1988c2ecf20Sopenharmony_ci}
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_ciint finish_command(struct child_process *cmd)
2018c2ecf20Sopenharmony_ci{
2028c2ecf20Sopenharmony_ci	return wait_or_whine(cmd->pid);
2038c2ecf20Sopenharmony_ci}
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ciint run_command(struct child_process *cmd)
2068c2ecf20Sopenharmony_ci{
2078c2ecf20Sopenharmony_ci	int code = start_command(cmd);
2088c2ecf20Sopenharmony_ci	if (code)
2098c2ecf20Sopenharmony_ci		return code;
2108c2ecf20Sopenharmony_ci	return finish_command(cmd);
2118c2ecf20Sopenharmony_ci}
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_cistatic void prepare_run_command_v_opt(struct child_process *cmd,
2148c2ecf20Sopenharmony_ci				      const char **argv,
2158c2ecf20Sopenharmony_ci				      int opt)
2168c2ecf20Sopenharmony_ci{
2178c2ecf20Sopenharmony_ci	memset(cmd, 0, sizeof(*cmd));
2188c2ecf20Sopenharmony_ci	cmd->argv = argv;
2198c2ecf20Sopenharmony_ci	cmd->no_stdin = opt & RUN_COMMAND_NO_STDIN ? 1 : 0;
2208c2ecf20Sopenharmony_ci	cmd->exec_cmd = opt & RUN_EXEC_CMD ? 1 : 0;
2218c2ecf20Sopenharmony_ci	cmd->stdout_to_stderr = opt & RUN_COMMAND_STDOUT_TO_STDERR ? 1 : 0;
2228c2ecf20Sopenharmony_ci}
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_ciint run_command_v_opt(const char **argv, int opt)
2258c2ecf20Sopenharmony_ci{
2268c2ecf20Sopenharmony_ci	struct child_process cmd;
2278c2ecf20Sopenharmony_ci	prepare_run_command_v_opt(&cmd, argv, opt);
2288c2ecf20Sopenharmony_ci	return run_command(&cmd);
2298c2ecf20Sopenharmony_ci}
230