xref: /linux-6.15/tools/lib/subcmd/run-command.c (revision 5ce42b5d)
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