1  // SPDX-License-Identifier: GPL-2.0
2  #include <unistd.h>
3  #include <sys/types.h>
4  #include <sys/stat.h>
5  #include <fcntl.h>
6  #include <string.h>
7  #include <linux/string.h>
8  #include <errno.h>
9  #include <sys/wait.h>
10  #include "subcmd-util.h"
11  #include "run-command.h"
12  #include "exec-cmd.h"
13  
14  #define STRERR_BUFSIZE 128
15  
close_pair(int fd[2])16  static inline void close_pair(int fd[2])
17  {
18  	close(fd[0]);
19  	close(fd[1]);
20  }
21  
dup_devnull(int to)22  static inline void dup_devnull(int to)
23  {
24  	int fd = open("/dev/null", O_RDWR);
25  	dup2(fd, to);
26  	close(fd);
27  }
28  
start_command(struct child_process * cmd)29  int start_command(struct child_process *cmd)
30  {
31  	int need_in, need_out, need_err;
32  	int fdin[2], fdout[2], fderr[2];
33  	char sbuf[STRERR_BUFSIZE];
34  
35  	/*
36  	 * In case of errors we must keep the promise to close FDs
37  	 * that have been passed in via ->in and ->out.
38  	 */
39  
40  	need_in = !cmd->no_stdin && cmd->in < 0;
41  	if (need_in) {
42  		if (pipe(fdin) < 0) {
43  			if (cmd->out > 0)
44  				close(cmd->out);
45  			return -ERR_RUN_COMMAND_PIPE;
46  		}
47  		cmd->in = fdin[1];
48  	}
49  
50  	need_out = !cmd->no_stdout
51  		&& !cmd->stdout_to_stderr
52  		&& cmd->out < 0;
53  	if (need_out) {
54  		if (pipe(fdout) < 0) {
55  			if (need_in)
56  				close_pair(fdin);
57  			else if (cmd->in)
58  				close(cmd->in);
59  			return -ERR_RUN_COMMAND_PIPE;
60  		}
61  		cmd->out = fdout[0];
62  	}
63  
64  	need_err = !cmd->no_stderr && cmd->err < 0;
65  	if (need_err) {
66  		if (pipe(fderr) < 0) {
67  			if (need_in)
68  				close_pair(fdin);
69  			else if (cmd->in)
70  				close(cmd->in);
71  			if (need_out)
72  				close_pair(fdout);
73  			else if (cmd->out)
74  				close(cmd->out);
75  			return -ERR_RUN_COMMAND_PIPE;
76  		}
77  		cmd->err = fderr[0];
78  	}
79  
80  	fflush(NULL);
81  	cmd->pid = fork();
82  	if (!cmd->pid) {
83  		if (cmd->no_stdin)
84  			dup_devnull(0);
85  		else if (need_in) {
86  			dup2(fdin[0], 0);
87  			close_pair(fdin);
88  		} else if (cmd->in) {
89  			dup2(cmd->in, 0);
90  			close(cmd->in);
91  		}
92  
93  		if (cmd->no_stderr)
94  			dup_devnull(2);
95  		else if (need_err) {
96  			dup2(fderr[1], 2);
97  			close_pair(fderr);
98  		}
99  
100  		if (cmd->no_stdout)
101  			dup_devnull(1);
102  		else if (cmd->stdout_to_stderr)
103  			dup2(2, 1);
104  		else if (need_out) {
105  			dup2(fdout[1], 1);
106  			close_pair(fdout);
107  		} else if (cmd->out > 1) {
108  			dup2(cmd->out, 1);
109  			close(cmd->out);
110  		}
111  
112  		if (cmd->dir && chdir(cmd->dir))
113  			die("exec %s: cd to %s failed (%s)", cmd->argv[0],
114  			    cmd->dir, str_error_r(errno, sbuf, sizeof(sbuf)));
115  		if (cmd->env) {
116  			for (; *cmd->env; cmd->env++) {
117  				if (strchr(*cmd->env, '='))
118  					putenv((char*)*cmd->env);
119  				else
120  					unsetenv(*cmd->env);
121  			}
122  		}
123  		if (cmd->preexec_cb)
124  			cmd->preexec_cb();
125  		if (cmd->no_exec_cmd)
126  			exit(cmd->no_exec_cmd(cmd));
127  		if (cmd->exec_cmd) {
128  			execv_cmd(cmd->argv);
129  		} else {
130  			execvp(cmd->argv[0], (char *const*) cmd->argv);
131  		}
132  		exit(127);
133  	}
134  
135  	if (cmd->pid < 0) {
136  		int err = errno;
137  		if (need_in)
138  			close_pair(fdin);
139  		else if (cmd->in)
140  			close(cmd->in);
141  		if (need_out)
142  			close_pair(fdout);
143  		else if (cmd->out)
144  			close(cmd->out);
145  		if (need_err)
146  			close_pair(fderr);
147  		return err == ENOENT ?
148  			-ERR_RUN_COMMAND_EXEC :
149  			-ERR_RUN_COMMAND_FORK;
150  	}
151  
152  	if (need_in)
153  		close(fdin[0]);
154  	else if (cmd->in)
155  		close(cmd->in);
156  
157  	if (need_out)
158  		close(fdout[1]);
159  	else if (cmd->out)
160  		close(cmd->out);
161  
162  	if (need_err)
163  		close(fderr[1]);
164  
165  	return 0;
166  }
167  
wait_or_whine(struct child_process * cmd,bool block)168  static int wait_or_whine(struct child_process *cmd, bool block)
169  {
170  	bool finished = cmd->finished;
171  	int result = cmd->finish_result;
172  
173  	while (!finished) {
174  		int status, code;
175  		pid_t waiting = waitpid(cmd->pid, &status, block ? 0 : WNOHANG);
176  
177  		if (!block && waiting == 0)
178  			break;
179  
180  		if (waiting < 0 && errno == EINTR)
181  			continue;
182  
183  		finished = true;
184  		if (waiting < 0) {
185  			char sbuf[STRERR_BUFSIZE];
186  
187  			fprintf(stderr, " Error: waitpid failed (%s)",
188  				str_error_r(errno, sbuf, sizeof(sbuf)));
189  			result = -ERR_RUN_COMMAND_WAITPID;
190  		} else if (waiting != cmd->pid) {
191  			result = -ERR_RUN_COMMAND_WAITPID_WRONG_PID;
192  		} else if (WIFSIGNALED(status)) {
193  			result = -ERR_RUN_COMMAND_WAITPID_SIGNAL;
194  		} else if (!WIFEXITED(status)) {
195  			result = -ERR_RUN_COMMAND_WAITPID_NOEXIT;
196  		} else {
197  			code = WEXITSTATUS(status);
198  			switch (code) {
199  			case 127:
200  				result = -ERR_RUN_COMMAND_EXEC;
201  				break;
202  			case 0:
203  				result = 0;
204  				break;
205  			default:
206  				result = -code;
207  				break;
208  			}
209  		}
210  	}
211  	if (finished) {
212  		cmd->finished = 1;
213  		cmd->finish_result = result;
214  	}
215  	return result;
216  }
217  
check_if_command_finished(struct child_process * cmd)218  int check_if_command_finished(struct child_process *cmd)
219  {
220  	wait_or_whine(cmd, /*block=*/false);
221  	return cmd->finished;
222  }
223  
finish_command(struct child_process * cmd)224  int finish_command(struct child_process *cmd)
225  {
226  	return wait_or_whine(cmd, /*block=*/true);
227  }
228  
run_command(struct child_process * cmd)229  int run_command(struct child_process *cmd)
230  {
231  	int code = start_command(cmd);
232  	if (code)
233  		return code;
234  	return finish_command(cmd);
235  }
236  
prepare_run_command_v_opt(struct child_process * cmd,const char ** argv,int opt)237  static void prepare_run_command_v_opt(struct child_process *cmd,
238  				      const char **argv,
239  				      int opt)
240  {
241  	memset(cmd, 0, sizeof(*cmd));
242  	cmd->argv = argv;
243  	cmd->no_stdin = opt & RUN_COMMAND_NO_STDIN ? 1 : 0;
244  	cmd->exec_cmd = opt & RUN_EXEC_CMD ? 1 : 0;
245  	cmd->stdout_to_stderr = opt & RUN_COMMAND_STDOUT_TO_STDERR ? 1 : 0;
246  }
247  
run_command_v_opt(const char ** argv,int opt)248  int run_command_v_opt(const char **argv, int opt)
249  {
250  	struct child_process cmd;
251  	prepare_run_command_v_opt(&cmd, argv, opt);
252  	return run_command(&cmd);
253  }
254