run-command.c 6.2 KB
Newer Older
1 2
#include "cache.h"
#include "run-command.h"
3
#include "exec_cmd.h"
4

5 6 7 8 9 10
static inline void close_pair(int fd[2])
{
	close(fd[0]);
	close(fd[1]);
}

11 12 13 14 15 16 17
static inline void dup_devnull(int to)
{
	int fd = open("/dev/null", O_RDWR);
	dup2(fd, to);
	close(fd);
}

18
int start_command(struct child_process *cmd)
19
{
20 21
	int need_in, need_out, need_err;
	int fdin[2], fdout[2], fderr[2];
22

23 24 25 26 27
	/*
	 * In case of errors we must keep the promise to close FDs
	 * that have been passed in via ->in and ->out.
	 */

28
	need_in = !cmd->no_stdin && cmd->in < 0;
29
	if (need_in) {
30 31 32
		if (pipe(fdin) < 0) {
			if (cmd->out > 0)
				close(cmd->out);
33
			return -ERR_RUN_COMMAND_PIPE;
34
		}
35 36 37
		cmd->in = fdin[1];
	}

38 39 40
	need_out = !cmd->no_stdout
		&& !cmd->stdout_to_stderr
		&& cmd->out < 0;
41 42 43 44
	if (need_out) {
		if (pipe(fdout) < 0) {
			if (need_in)
				close_pair(fdin);
45 46
			else if (cmd->in)
				close(cmd->in);
47 48 49 50 51
			return -ERR_RUN_COMMAND_PIPE;
		}
		cmd->out = fdout[0];
	}

52
	need_err = !cmd->no_stderr && cmd->err < 0;
53 54 55 56
	if (need_err) {
		if (pipe(fderr) < 0) {
			if (need_in)
				close_pair(fdin);
57 58
			else if (cmd->in)
				close(cmd->in);
59 60
			if (need_out)
				close_pair(fdout);
61 62
			else if (cmd->out)
				close(cmd->out);
63 64 65 66 67
			return -ERR_RUN_COMMAND_PIPE;
		}
		cmd->err = fderr[0];
	}

68
#ifndef __MINGW32__
69 70
	cmd->pid = fork();
	if (!cmd->pid) {
71 72 73
		if (cmd->no_stdin)
			dup_devnull(0);
		else if (need_in) {
74
			dup2(fdin[0], 0);
75
			close_pair(fdin);
76 77 78
		} else if (cmd->in) {
			dup2(cmd->in, 0);
			close(cmd->in);
79
		}
80

81 82 83 84 85 86 87
		if (cmd->no_stderr)
			dup_devnull(2);
		else if (need_err) {
			dup2(fderr[1], 2);
			close_pair(fderr);
		}

88 89 90
		if (cmd->no_stdout)
			dup_devnull(1);
		else if (cmd->stdout_to_stderr)
91
			dup2(2, 1);
92 93 94 95 96 97 98 99
		else if (need_out) {
			dup2(fdout[1], 1);
			close_pair(fdout);
		} else if (cmd->out > 1) {
			dup2(cmd->out, 1);
			close(cmd->out);
		}

100 101 102
		if (cmd->dir && chdir(cmd->dir))
			die("exec %s: cd to %s failed (%s)", cmd->argv[0],
			    cmd->dir, strerror(errno));
103
		if (cmd->env) {
104 105 106 107 108 109
			for (; *cmd->env; cmd->env++) {
				if (strchr(*cmd->env, '='))
					putenv((char*)*cmd->env);
				else
					unsetenv(*cmd->env);
			}
110
		}
111 112
		if (cmd->git_cmd) {
			execv_git_cmd(cmd->argv);
113
		} else {
114
			execvp(cmd->argv[0], (char *const*) cmd->argv);
115
		}
116
		die("exec %s failed.", cmd->argv[0]);
117
	}
118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199
#else
	int s0 = -1, s1 = -1, s2 = -1;	/* backups of stdin, stdout, stderr */
	const char *sargv0 = cmd->argv[0];
	char **env = environ;
	struct strbuf git_cmd;

	if (cmd->no_stdin) {
		s0 = dup(0);
		dup_devnull(0);
	} else if (need_in) {
		s0 = dup(0);
		dup2(fdin[0], 0);
	} else if (cmd->in) {
		s0 = dup(0);
		dup2(cmd->in, 0);
	}

	if (cmd->no_stderr) {
		s2 = dup(2);
		dup_devnull(2);
	} else if (need_err) {
		s2 = dup(2);
		dup2(fderr[1], 2);
	}

	if (cmd->no_stdout) {
		s1 = dup(1);
		dup_devnull(1);
	} else if (cmd->stdout_to_stderr) {
		s1 = dup(1);
		dup2(2, 1);
	} else if (need_out) {
		s1 = dup(1);
		dup2(fdout[1], 1);
	} else if (cmd->out > 1) {
		s1 = dup(1);
		dup2(cmd->out, 1);
	}

	if (cmd->dir)
		die("chdir in start_command() not implemented");
	if (cmd->env) {
		env = copy_environ();
		for (; *cmd->env; cmd->env++)
			env = env_setenv(env, *cmd->env);
	}

	if (cmd->git_cmd) {
		strbuf_init(&git_cmd, 0);
		strbuf_addf(&git_cmd, "git-%s", cmd->argv[0]);
		cmd->argv[0] = git_cmd.buf;
	}

	cmd->pid = spawnvpe(_P_NOWAIT, cmd->argv[0], cmd->argv, (const char **)env);

	if (cmd->env)
		free_environ(env);
	if (cmd->git_cmd)
		strbuf_release(&git_cmd);

	cmd->argv[0] = sargv0;
	if (s0 >= 0)
		dup2(s0, 0), close(s0);
	if (s1 >= 0)
		dup2(s1, 1), close(s1);
	if (s2 >= 0)
		dup2(s2, 2), close(s2);
#endif

	if (cmd->pid < 0) {
		if (need_in)
			close_pair(fdin);
		else if (cmd->in)
			close(cmd->in);
		if (need_out)
			close_pair(fdout);
		else if (cmd->out)
			close(cmd->out);
		if (need_err)
			close_pair(fderr);
		return -ERR_RUN_COMMAND_FORK;
	}
200 201 202 203 204 205

	if (need_in)
		close(fdin[0]);
	else if (cmd->in)
		close(cmd->in);

206 207
	if (need_out)
		close(fdout[1]);
208
	else if (cmd->out)
209 210
		close(cmd->out);

211 212 213
	if (need_err)
		close(fderr[1]);

214 215 216
	return 0;
}

217
static int wait_or_whine(pid_t pid)
218
{
219 220
	for (;;) {
		int status, code;
221
		pid_t waiting = waitpid(pid, &status, 0);
222

D
David Rientjes 已提交
223
		if (waiting < 0) {
224 225
			if (errno == EINTR)
				continue;
D
David Rientjes 已提交
226
			error("waitpid failed (%s)", strerror(errno));
227 228
			return -ERR_RUN_COMMAND_WAITPID;
		}
229
		if (waiting != pid)
230 231 232 233 234 235 236 237 238 239 240 241
			return -ERR_RUN_COMMAND_WAITPID_WRONG_PID;
		if (WIFSIGNALED(status))
			return -ERR_RUN_COMMAND_WAITPID_SIGNAL;

		if (!WIFEXITED(status))
			return -ERR_RUN_COMMAND_WAITPID_NOEXIT;
		code = WEXITSTATUS(status);
		if (code)
			return -code;
		return 0;
	}
}
242

243 244 245 246 247
int finish_command(struct child_process *cmd)
{
	return wait_or_whine(cmd->pid);
}

248 249 250 251 252 253 254 255
int run_command(struct child_process *cmd)
{
	int code = start_command(cmd);
	if (code)
		return code;
	return finish_command(cmd);
}

256
static void prepare_run_command_v_opt(struct child_process *cmd,
257 258
				      const char **argv,
				      int opt)
259 260 261 262 263 264 265 266
{
	memset(cmd, 0, sizeof(*cmd));
	cmd->argv = argv;
	cmd->no_stdin = opt & RUN_COMMAND_NO_STDIN ? 1 : 0;
	cmd->git_cmd = opt & RUN_GIT_CMD ? 1 : 0;
	cmd->stdout_to_stderr = opt & RUN_COMMAND_STDOUT_TO_STDERR ? 1 : 0;
}

267 268 269
int run_command_v_opt(const char **argv, int opt)
{
	struct child_process cmd;
270 271 272 273 274 275 276 277 278
	prepare_run_command_v_opt(&cmd, argv, opt);
	return run_command(&cmd);
}

int run_command_v_opt_cd(const char **argv, int opt, const char *dir)
{
	struct child_process cmd;
	prepare_run_command_v_opt(&cmd, argv, opt);
	cmd.dir = dir;
279 280
	return run_command(&cmd);
}
281 282 283 284 285 286 287 288 289

int run_command_v_opt_cd_env(const char **argv, int opt, const char *dir, const char *const *env)
{
	struct child_process cmd;
	prepare_run_command_v_opt(&cmd, argv, opt);
	cmd.dir = dir;
	cmd.env = env;
	return run_command(&cmd);
}
290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320

int start_async(struct async *async)
{
	int pipe_out[2];

	if (pipe(pipe_out) < 0)
		return error("cannot create pipe: %s", strerror(errno));

	async->pid = fork();
	if (async->pid < 0) {
		error("fork (async) failed: %s", strerror(errno));
		close_pair(pipe_out);
		return -1;
	}
	if (!async->pid) {
		close(pipe_out[0]);
		exit(!!async->proc(pipe_out[1], async->data));
	}
	async->out = pipe_out[0];
	close(pipe_out[1]);
	return 0;
}

int finish_async(struct async *async)
{
	int ret = 0;

	if (wait_or_whine(async->pid))
		ret = error("waitpid (async) failed");
	return ret;
}