Merge branch 'js/mingw-fallouts'
[git/mingw.git] / run-command.c
blobe5d029f7a2df30bb60601721656ca5abbf2e2c37
1 #include "cache.h"
2 #include "run-command.h"
3 #include "exec_cmd.h"
4 #include "spawn-pipe.h"
6 static inline void close_pair(int fd[2])
8 close(fd[0]);
9 close(fd[1]);
12 int start_command(struct child_process *cmd)
14 int need_in, need_out, need_err;
15 int fdin[2] = { -1, -1 };
16 int fdout[2] = { -1, -1 };
17 int fderr[2] = { -1, -1 };
18 char **env = environ;
20 need_in = !cmd->no_stdin && cmd->in < 0;
21 if (need_in) {
22 if (pipe(fdin) < 0)
23 return -ERR_RUN_COMMAND_PIPE;
24 cmd->in = fdin[1];
25 cmd->close_in = 1;
28 need_out = !cmd->no_stdout
29 && !cmd->stdout_to_stderr
30 && cmd->out < 0;
31 if (need_out) {
32 if (pipe(fdout) < 0) {
33 if (need_in)
34 close_pair(fdin);
35 return -ERR_RUN_COMMAND_PIPE;
37 cmd->out = fdout[0];
38 cmd->close_out = 1;
41 need_err = !cmd->no_stderr && cmd->err < 0;
42 if (need_err) {
43 if (pipe(fderr) < 0) {
44 if (need_in)
45 close_pair(fdin);
46 if (need_out)
47 close_pair(fdout);
48 return -ERR_RUN_COMMAND_PIPE;
50 cmd->err = fderr[0];
54 if (cmd->no_stdin)
55 fdin[0] = open("/dev/null", O_RDWR);
56 else if (need_in) {
57 /* nothing */
58 } else if (cmd->in) {
59 fdin[0] = cmd->in;
62 if (cmd->no_stdout)
63 fdout[1] = open("/dev/null", O_RDWR);
64 else if (cmd->stdout_to_stderr)
65 fdout[1] = dup(2);
66 else if (need_out) {
67 /* nothing */
68 } else if (cmd->out > 1) {
69 fdout[1] = cmd->out;
72 if (cmd->no_stderr)
73 fderr[1] = open("/dev/null", O_RDWR);
74 else if (need_err) {
75 /* nothing */
78 if (cmd->dir)
79 die("chdir in start_command() not implemented");
80 if (cmd->dir && chdir(cmd->dir))
81 die("exec %s: cd to %s failed (%s)", cmd->argv[0],
82 cmd->dir, strerror(errno));
83 if (cmd->env) {
84 if (cmd->git_cmd)
85 die("modifying environment for git_cmd in start_command() not implemented");
86 env = copy_environ();
87 for (; *cmd->env; cmd->env++) {
88 if (strchr(*cmd->env, '='))
89 die("setting environment in start_command() not implemented");
90 else
91 env_unsetenv(env, *cmd->env);
94 if (cmd->git_cmd) {
95 cmd->pid = spawnv_git_cmd(cmd->argv, fdin, fdout);
96 } else {
97 cmd->pid = spawnvpe_pipe(cmd->argv[0], cmd->argv, env, fdin, fdout);
100 if (cmd->pid < 0) {
101 if (need_in)
102 close_pair(fdin);
103 if (need_out)
104 close_pair(fdout);
105 if (need_err)
106 close_pair(fderr);
107 return -ERR_RUN_COMMAND_FORK;
110 return 0;
113 static int wait_or_whine(pid_t pid)
115 for (;;) {
116 int status, code;
117 pid_t waiting = waitpid(pid, &status, 0);
119 if (waiting < 0) {
120 if (errno == EINTR)
121 continue;
122 error("waitpid failed (%s)", strerror(errno));
123 return -ERR_RUN_COMMAND_WAITPID;
125 if (waiting != pid)
126 return -ERR_RUN_COMMAND_WAITPID_WRONG_PID;
127 if (WIFSIGNALED(status))
128 return -ERR_RUN_COMMAND_WAITPID_SIGNAL;
130 if (!WIFEXITED(status))
131 return -ERR_RUN_COMMAND_WAITPID_NOEXIT;
132 code = WEXITSTATUS(status);
133 if (code)
134 return -code;
135 return 0;
139 int finish_command(struct child_process *cmd)
141 if (cmd->close_in)
142 close(cmd->in);
143 if (cmd->close_out)
144 close(cmd->out);
145 return wait_or_whine(cmd->pid);
148 int run_command(struct child_process *cmd)
150 int code = start_command(cmd);
151 if (code)
152 return code;
153 return finish_command(cmd);
156 static void prepare_run_command_v_opt(struct child_process *cmd,
157 const char **argv,
158 int opt)
160 memset(cmd, 0, sizeof(*cmd));
161 cmd->argv = argv;
162 cmd->no_stdin = opt & RUN_COMMAND_NO_STDIN ? 1 : 0;
163 cmd->git_cmd = opt & RUN_GIT_CMD ? 1 : 0;
164 cmd->stdout_to_stderr = opt & RUN_COMMAND_STDOUT_TO_STDERR ? 1 : 0;
167 int run_command_v_opt(const char **argv, int opt)
169 struct child_process cmd;
170 prepare_run_command_v_opt(&cmd, argv, opt);
171 return run_command(&cmd);
174 int run_command_v_opt_cd(const char **argv, int opt, const char *dir)
176 struct child_process cmd;
177 prepare_run_command_v_opt(&cmd, argv, opt);
178 cmd.dir = dir;
179 return run_command(&cmd);
182 int run_command_v_opt_cd_env(const char **argv, int opt, const char *dir, const char *const *env)
184 struct child_process cmd;
185 prepare_run_command_v_opt(&cmd, argv, opt);
186 cmd.dir = dir;
187 cmd.env = env;
188 return run_command(&cmd);
191 #ifdef __MINGW32__
192 static __stdcall unsigned run_thread(void *data)
194 struct async *async = data;
195 return async->proc(async->fd_for_proc, async->data);
197 #endif
199 int start_async(struct async *async)
201 int pipe_out[2];
203 if (pipe(pipe_out) < 0)
204 return error("cannot create pipe: %s", strerror(errno));
205 async->out = pipe_out[0];
207 #ifndef __MINGW32__
208 async->pid = fork();
209 if (async->pid < 0) {
210 error("fork (async) failed: %s", strerror(errno));
211 close_pair(pipe_out);
212 return -1;
214 if (!async->pid) {
215 close(pipe_out[0]);
216 exit(!!async->proc(pipe_out[1], async->data));
218 close(pipe_out[1]);
219 #else
220 async->fd_for_proc = pipe_out[1];
221 async->tid = (HANDLE) _beginthreadex(NULL, 0, run_thread, async, 0, NULL);
222 if (!async->tid) {
223 error("cannot create thread: %s", strerror(errno));
224 close_pair(pipe_out);
225 return -1;
227 #endif
228 return 0;
231 int finish_async(struct async *async)
233 #ifndef __MINGW32__
234 int ret = 0;
236 if (wait_or_whine(async->pid))
237 ret = error("waitpid (async) failed");
238 #else
239 DWORD ret = 0;
240 if (WaitForSingleObject(async->tid, INFINITE) != WAIT_OBJECT_0)
241 ret = error("waiting for thread failed: %lu", GetLastError());
242 else if (!GetExitCodeThread(async->tid, &ret))
243 ret = error("cannot get thread exit code: %lu", GetLastError());
244 CloseHandle(async->tid);
245 #endif
246 return ret;