brintos

brintos / linux-shallow public Read only

0
0
Text · 4.9 KiB · 4e3a557 Raw
254 lines · c
1// SPDX-License-Identifier: GPL-2.02#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 12815 16static inline void close_pair(int fd[2])17{18	close(fd[0]);19	close(fd[1]);20}21 22static inline void dup_devnull(int to)23{24	int fd = open("/dev/null", O_RDWR);25	dup2(fd, to);26	close(fd);27}28 29int 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 FDs37	 * 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_stdout51		&& !cmd->stdout_to_stderr52		&& 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				else120					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 168static 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 218int check_if_command_finished(struct child_process *cmd)219{220	wait_or_whine(cmd, /*block=*/false);221	return cmd->finished;222}223 224int finish_command(struct child_process *cmd)225{226	return wait_or_whine(cmd, /*block=*/true);227}228 229int 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 237static 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 248int 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