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