File Index
Files and directories
Sign in
FILE

run-command.c

File preview with syntax highlighting.

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