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>
5*5ce42b5dSIan Rogers #include <ctype.h>
64b6ab94eSJosh Poimboeuf #include <fcntl.h>
74b6ab94eSJosh Poimboeuf #include <string.h>
807eebccbSArnaldo Carvalho de Melo #include <linux/string.h>
94b6ab94eSJosh Poimboeuf #include <errno.h>
104b6ab94eSJosh Poimboeuf #include <sys/wait.h>
114b6ab94eSJosh Poimboeuf #include "subcmd-util.h"
124b6ab94eSJosh Poimboeuf #include "run-command.h"
134b6ab94eSJosh Poimboeuf #include "exec-cmd.h"
144b6ab94eSJosh Poimboeuf
154b6ab94eSJosh Poimboeuf #define STRERR_BUFSIZE 128
164b6ab94eSJosh Poimboeuf
close_pair(int fd[2])174b6ab94eSJosh Poimboeuf static inline void close_pair(int fd[2])
184b6ab94eSJosh Poimboeuf {
194b6ab94eSJosh Poimboeuf close(fd[0]);
204b6ab94eSJosh Poimboeuf close(fd[1]);
214b6ab94eSJosh Poimboeuf }
224b6ab94eSJosh Poimboeuf
dup_devnull(int to)234b6ab94eSJosh Poimboeuf static inline void dup_devnull(int to)
244b6ab94eSJosh Poimboeuf {
254b6ab94eSJosh Poimboeuf int fd = open("/dev/null", O_RDWR);
264b6ab94eSJosh Poimboeuf dup2(fd, to);
274b6ab94eSJosh Poimboeuf close(fd);
284b6ab94eSJosh Poimboeuf }
294b6ab94eSJosh Poimboeuf
start_command(struct child_process * cmd)304b6ab94eSJosh Poimboeuf int start_command(struct child_process *cmd)
314b6ab94eSJosh Poimboeuf {
324b6ab94eSJosh Poimboeuf int need_in, need_out, need_err;
334b6ab94eSJosh Poimboeuf int fdin[2], fdout[2], fderr[2];
344b6ab94eSJosh Poimboeuf char sbuf[STRERR_BUFSIZE];
354b6ab94eSJosh Poimboeuf
364b6ab94eSJosh Poimboeuf /*
374b6ab94eSJosh Poimboeuf * In case of errors we must keep the promise to close FDs
384b6ab94eSJosh Poimboeuf * that have been passed in via ->in and ->out.
394b6ab94eSJosh Poimboeuf */
404b6ab94eSJosh Poimboeuf
414b6ab94eSJosh Poimboeuf need_in = !cmd->no_stdin && cmd->in < 0;
424b6ab94eSJosh Poimboeuf if (need_in) {
434b6ab94eSJosh Poimboeuf if (pipe(fdin) < 0) {
444b6ab94eSJosh Poimboeuf if (cmd->out > 0)
454b6ab94eSJosh Poimboeuf close(cmd->out);
464b6ab94eSJosh Poimboeuf return -ERR_RUN_COMMAND_PIPE;
474b6ab94eSJosh Poimboeuf }
484b6ab94eSJosh Poimboeuf cmd->in = fdin[1];
494b6ab94eSJosh Poimboeuf }
504b6ab94eSJosh Poimboeuf
514b6ab94eSJosh Poimboeuf need_out = !cmd->no_stdout
524b6ab94eSJosh Poimboeuf && !cmd->stdout_to_stderr
534b6ab94eSJosh Poimboeuf && cmd->out < 0;
544b6ab94eSJosh Poimboeuf if (need_out) {
554b6ab94eSJosh Poimboeuf if (pipe(fdout) < 0) {
564b6ab94eSJosh Poimboeuf if (need_in)
574b6ab94eSJosh Poimboeuf close_pair(fdin);
584b6ab94eSJosh Poimboeuf else if (cmd->in)
594b6ab94eSJosh Poimboeuf close(cmd->in);
604b6ab94eSJosh Poimboeuf return -ERR_RUN_COMMAND_PIPE;
614b6ab94eSJosh Poimboeuf }
624b6ab94eSJosh Poimboeuf cmd->out = fdout[0];
634b6ab94eSJosh Poimboeuf }
644b6ab94eSJosh Poimboeuf
654b6ab94eSJosh Poimboeuf need_err = !cmd->no_stderr && cmd->err < 0;
664b6ab94eSJosh Poimboeuf if (need_err) {
674b6ab94eSJosh Poimboeuf if (pipe(fderr) < 0) {
684b6ab94eSJosh Poimboeuf if (need_in)
694b6ab94eSJosh Poimboeuf close_pair(fdin);
704b6ab94eSJosh Poimboeuf else if (cmd->in)
714b6ab94eSJosh Poimboeuf close(cmd->in);
724b6ab94eSJosh Poimboeuf if (need_out)
734b6ab94eSJosh Poimboeuf close_pair(fdout);
744b6ab94eSJosh Poimboeuf else if (cmd->out)
754b6ab94eSJosh Poimboeuf close(cmd->out);
764b6ab94eSJosh Poimboeuf return -ERR_RUN_COMMAND_PIPE;
774b6ab94eSJosh Poimboeuf }
784b6ab94eSJosh Poimboeuf cmd->err = fderr[0];
794b6ab94eSJosh Poimboeuf }
804b6ab94eSJosh Poimboeuf
814b6ab94eSJosh Poimboeuf fflush(NULL);
824b6ab94eSJosh Poimboeuf cmd->pid = fork();
834b6ab94eSJosh Poimboeuf if (!cmd->pid) {
844b6ab94eSJosh Poimboeuf if (cmd->no_stdin)
854b6ab94eSJosh Poimboeuf dup_devnull(0);
864b6ab94eSJosh Poimboeuf else if (need_in) {
874b6ab94eSJosh Poimboeuf dup2(fdin[0], 0);
884b6ab94eSJosh Poimboeuf close_pair(fdin);
894b6ab94eSJosh Poimboeuf } else if (cmd->in) {
904b6ab94eSJosh Poimboeuf dup2(cmd->in, 0);
914b6ab94eSJosh Poimboeuf close(cmd->in);
924b6ab94eSJosh Poimboeuf }
934b6ab94eSJosh Poimboeuf
944b6ab94eSJosh Poimboeuf if (cmd->no_stderr)
954b6ab94eSJosh Poimboeuf dup_devnull(2);
964b6ab94eSJosh Poimboeuf else if (need_err) {
974b6ab94eSJosh Poimboeuf dup2(fderr[1], 2);
984b6ab94eSJosh Poimboeuf close_pair(fderr);
994b6ab94eSJosh Poimboeuf }
1004b6ab94eSJosh Poimboeuf
1014b6ab94eSJosh Poimboeuf if (cmd->no_stdout)
1024b6ab94eSJosh Poimboeuf dup_devnull(1);
1034b6ab94eSJosh Poimboeuf else if (cmd->stdout_to_stderr)
1044b6ab94eSJosh Poimboeuf dup2(2, 1);
1054b6ab94eSJosh Poimboeuf else if (need_out) {
1064b6ab94eSJosh Poimboeuf dup2(fdout[1], 1);
1074b6ab94eSJosh Poimboeuf close_pair(fdout);
1084b6ab94eSJosh Poimboeuf } else if (cmd->out > 1) {
1094b6ab94eSJosh Poimboeuf dup2(cmd->out, 1);
1104b6ab94eSJosh Poimboeuf close(cmd->out);
1114b6ab94eSJosh Poimboeuf }
1124b6ab94eSJosh Poimboeuf
1134b6ab94eSJosh Poimboeuf if (cmd->dir && chdir(cmd->dir))
1144b6ab94eSJosh Poimboeuf die("exec %s: cd to %s failed (%s)", cmd->argv[0],
11507eebccbSArnaldo Carvalho de Melo cmd->dir, str_error_r(errno, sbuf, sizeof(sbuf)));
1164b6ab94eSJosh Poimboeuf if (cmd->env) {
1174b6ab94eSJosh Poimboeuf for (; *cmd->env; cmd->env++) {
1184b6ab94eSJosh Poimboeuf if (strchr(*cmd->env, '='))
1194b6ab94eSJosh Poimboeuf putenv((char*)*cmd->env);
1204b6ab94eSJosh Poimboeuf else
1214b6ab94eSJosh Poimboeuf unsetenv(*cmd->env);
1224b6ab94eSJosh Poimboeuf }
1234b6ab94eSJosh Poimboeuf }
1244b6ab94eSJosh Poimboeuf if (cmd->preexec_cb)
1254b6ab94eSJosh Poimboeuf cmd->preexec_cb();
1261a562c0dSIan Rogers if (cmd->no_exec_cmd)
1271a562c0dSIan Rogers exit(cmd->no_exec_cmd(cmd));
1284b6ab94eSJosh Poimboeuf if (cmd->exec_cmd) {
1294b6ab94eSJosh Poimboeuf execv_cmd(cmd->argv);
1304b6ab94eSJosh Poimboeuf } else {
1314b6ab94eSJosh Poimboeuf execvp(cmd->argv[0], (char *const*) cmd->argv);
1324b6ab94eSJosh Poimboeuf }
1334b6ab94eSJosh Poimboeuf exit(127);
1344b6ab94eSJosh Poimboeuf }
1354b6ab94eSJosh Poimboeuf
1364b6ab94eSJosh Poimboeuf if (cmd->pid < 0) {
1374b6ab94eSJosh Poimboeuf int err = errno;
1384b6ab94eSJosh Poimboeuf if (need_in)
1394b6ab94eSJosh Poimboeuf close_pair(fdin);
1404b6ab94eSJosh Poimboeuf else if (cmd->in)
1414b6ab94eSJosh Poimboeuf close(cmd->in);
1424b6ab94eSJosh Poimboeuf if (need_out)
1434b6ab94eSJosh Poimboeuf close_pair(fdout);
1444b6ab94eSJosh Poimboeuf else if (cmd->out)
1454b6ab94eSJosh Poimboeuf close(cmd->out);
1464b6ab94eSJosh Poimboeuf if (need_err)
1474b6ab94eSJosh Poimboeuf close_pair(fderr);
1484b6ab94eSJosh Poimboeuf return err == ENOENT ?
1494b6ab94eSJosh Poimboeuf -ERR_RUN_COMMAND_EXEC :
1504b6ab94eSJosh Poimboeuf -ERR_RUN_COMMAND_FORK;
1514b6ab94eSJosh Poimboeuf }
1524b6ab94eSJosh Poimboeuf
1534b6ab94eSJosh Poimboeuf if (need_in)
1544b6ab94eSJosh Poimboeuf close(fdin[0]);
1554b6ab94eSJosh Poimboeuf else if (cmd->in)
1564b6ab94eSJosh Poimboeuf close(cmd->in);
1574b6ab94eSJosh Poimboeuf
1584b6ab94eSJosh Poimboeuf if (need_out)
1594b6ab94eSJosh Poimboeuf close(fdout[1]);
1604b6ab94eSJosh Poimboeuf else if (cmd->out)
1614b6ab94eSJosh Poimboeuf close(cmd->out);
1624b6ab94eSJosh Poimboeuf
1634b6ab94eSJosh Poimboeuf if (need_err)
1644b6ab94eSJosh Poimboeuf close(fderr[1]);
1654b6ab94eSJosh Poimboeuf
1664b6ab94eSJosh Poimboeuf return 0;
1674b6ab94eSJosh Poimboeuf }
1684b6ab94eSJosh Poimboeuf
wait_or_whine(struct child_process * cmd,bool block)169705c09bbSIan Rogers static int wait_or_whine(struct child_process *cmd, bool block)
1704b6ab94eSJosh Poimboeuf {
171705c09bbSIan Rogers bool finished = cmd->finished;
172705c09bbSIan Rogers int result = cmd->finish_result;
173705c09bbSIan Rogers
174705c09bbSIan Rogers while (!finished) {
175705c09bbSIan Rogers int status, code;
176705c09bbSIan Rogers pid_t waiting = waitpid(cmd->pid, &status, block ? 0 : WNOHANG);
177705c09bbSIan Rogers
178705c09bbSIan Rogers if (!block && waiting == 0)
179705c09bbSIan Rogers break;
180705c09bbSIan Rogers
181705c09bbSIan Rogers if (waiting < 0 && errno == EINTR)
182705c09bbSIan Rogers continue;
183705c09bbSIan Rogers
184705c09bbSIan Rogers finished = true;
185705c09bbSIan Rogers if (waiting < 0) {
1864b6ab94eSJosh Poimboeuf char sbuf[STRERR_BUFSIZE];
1874b6ab94eSJosh Poimboeuf
1884b6ab94eSJosh Poimboeuf fprintf(stderr, " Error: waitpid failed (%s)",
18907eebccbSArnaldo Carvalho de Melo str_error_r(errno, sbuf, sizeof(sbuf)));
190705c09bbSIan Rogers result = -ERR_RUN_COMMAND_WAITPID;
191705c09bbSIan Rogers } else if (waiting != cmd->pid) {
192705c09bbSIan Rogers result = -ERR_RUN_COMMAND_WAITPID_WRONG_PID;
193705c09bbSIan Rogers } else if (WIFSIGNALED(status)) {
194705c09bbSIan Rogers result = -ERR_RUN_COMMAND_WAITPID_SIGNAL;
195705c09bbSIan Rogers } else if (!WIFEXITED(status)) {
196705c09bbSIan Rogers result = -ERR_RUN_COMMAND_WAITPID_NOEXIT;
197705c09bbSIan Rogers } else {
1984b6ab94eSJosh Poimboeuf code = WEXITSTATUS(status);
1994b6ab94eSJosh Poimboeuf switch (code) {
2004b6ab94eSJosh Poimboeuf case 127:
201705c09bbSIan Rogers result = -ERR_RUN_COMMAND_EXEC;
202705c09bbSIan Rogers break;
2034b6ab94eSJosh Poimboeuf case 0:
204705c09bbSIan Rogers result = 0;
205705c09bbSIan Rogers break;
2064b6ab94eSJosh Poimboeuf default:
207705c09bbSIan Rogers result = -code;
208705c09bbSIan Rogers break;
2094b6ab94eSJosh Poimboeuf }
2104b6ab94eSJosh Poimboeuf }
2114b6ab94eSJosh Poimboeuf }
212705c09bbSIan Rogers if (finished) {
213705c09bbSIan Rogers cmd->finished = 1;
214705c09bbSIan Rogers cmd->finish_result = result;
215705c09bbSIan Rogers }
216705c09bbSIan Rogers return result;
217705c09bbSIan Rogers }
218705c09bbSIan Rogers
check_if_command_finished(struct child_process * cmd)219705c09bbSIan Rogers int check_if_command_finished(struct child_process *cmd)
220705c09bbSIan Rogers {
221*5ce42b5dSIan Rogers #ifdef __linux__
222*5ce42b5dSIan Rogers char filename[FILENAME_MAX + 12];
223*5ce42b5dSIan Rogers char status_line[256];
224*5ce42b5dSIan Rogers FILE *status_file;
225*5ce42b5dSIan Rogers
226*5ce42b5dSIan Rogers /*
227*5ce42b5dSIan Rogers * Check by reading /proc/<pid>/status as calling waitpid causes
228*5ce42b5dSIan Rogers * stdout/stderr to be closed and data lost.
229*5ce42b5dSIan Rogers */
230*5ce42b5dSIan Rogers sprintf(filename, "/proc/%d/status", cmd->pid);
231*5ce42b5dSIan Rogers status_file = fopen(filename, "r");
232*5ce42b5dSIan Rogers if (status_file == NULL) {
233*5ce42b5dSIan Rogers /* Open failed assume finish_command was called. */
234*5ce42b5dSIan Rogers return true;
235*5ce42b5dSIan Rogers }
236*5ce42b5dSIan Rogers while (fgets(status_line, sizeof(status_line), status_file) != NULL) {
237*5ce42b5dSIan Rogers char *p;
238*5ce42b5dSIan Rogers
239*5ce42b5dSIan Rogers if (strncmp(status_line, "State:", 6))
240*5ce42b5dSIan Rogers continue;
241*5ce42b5dSIan Rogers
242*5ce42b5dSIan Rogers fclose(status_file);
243*5ce42b5dSIan Rogers p = status_line + 6;
244*5ce42b5dSIan Rogers while (isspace(*p))
245*5ce42b5dSIan Rogers p++;
246*5ce42b5dSIan Rogers return *p == 'Z' ? 1 : 0;
247*5ce42b5dSIan Rogers }
248*5ce42b5dSIan Rogers /* Read failed assume finish_command was called. */
249*5ce42b5dSIan Rogers fclose(status_file);
250*5ce42b5dSIan Rogers return 1;
251*5ce42b5dSIan Rogers #else
252705c09bbSIan Rogers wait_or_whine(cmd, /*block=*/false);
253705c09bbSIan Rogers return cmd->finished;
254*5ce42b5dSIan Rogers #endif
255705c09bbSIan Rogers }
2564b6ab94eSJosh Poimboeuf
finish_command(struct child_process * cmd)2574b6ab94eSJosh Poimboeuf int finish_command(struct child_process *cmd)
2584b6ab94eSJosh Poimboeuf {
259705c09bbSIan Rogers return wait_or_whine(cmd, /*block=*/true);
2604b6ab94eSJosh Poimboeuf }
2614b6ab94eSJosh Poimboeuf
run_command(struct child_process * cmd)2624b6ab94eSJosh Poimboeuf int run_command(struct child_process *cmd)
2634b6ab94eSJosh Poimboeuf {
2644b6ab94eSJosh Poimboeuf int code = start_command(cmd);
2654b6ab94eSJosh Poimboeuf if (code)
2664b6ab94eSJosh Poimboeuf return code;
2674b6ab94eSJosh Poimboeuf return finish_command(cmd);
2684b6ab94eSJosh Poimboeuf }
2694b6ab94eSJosh Poimboeuf
prepare_run_command_v_opt(struct child_process * cmd,const char ** argv,int opt)2704b6ab94eSJosh Poimboeuf static void prepare_run_command_v_opt(struct child_process *cmd,
2714b6ab94eSJosh Poimboeuf const char **argv,
2724b6ab94eSJosh Poimboeuf int opt)
2734b6ab94eSJosh Poimboeuf {
2744b6ab94eSJosh Poimboeuf memset(cmd, 0, sizeof(*cmd));
2754b6ab94eSJosh Poimboeuf cmd->argv = argv;
2764b6ab94eSJosh Poimboeuf cmd->no_stdin = opt & RUN_COMMAND_NO_STDIN ? 1 : 0;
2774b6ab94eSJosh Poimboeuf cmd->exec_cmd = opt & RUN_EXEC_CMD ? 1 : 0;
2784b6ab94eSJosh Poimboeuf cmd->stdout_to_stderr = opt & RUN_COMMAND_STDOUT_TO_STDERR ? 1 : 0;
2794b6ab94eSJosh Poimboeuf }
2804b6ab94eSJosh Poimboeuf
run_command_v_opt(const char ** argv,int opt)2814b6ab94eSJosh Poimboeuf int run_command_v_opt(const char **argv, int opt)
2824b6ab94eSJosh Poimboeuf {
2834b6ab94eSJosh Poimboeuf struct child_process cmd;
2844b6ab94eSJosh Poimboeuf prepare_run_command_v_opt(&cmd, argv, opt);
2854b6ab94eSJosh Poimboeuf return run_command(&cmd);
2864b6ab94eSJosh Poimboeuf }
287