Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.15.
  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
 17static inline void close_pair(int fd[2])
 18{
 19	close(fd[0]);
 20	close(fd[1]);
 21}
 22
 23static inline void dup_devnull(int to)
 24{
 25	int fd = open("/dev/null", O_RDWR);
 26	dup2(fd, to);
 27	close(fd);
 28}
 29
 30int 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
169static 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
219int 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
257int finish_command(struct child_process *cmd)
258{
259	return wait_or_whine(cmd, /*block=*/true);
260}
261
262int 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
270static 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
281int 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}