1*b2441318SGreg 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