Code

git-rebase.sh: Fix --merge --abort failures when path contains whitespace
[git.git] / run-command.c
index 7e779d33ee9ea5f7d2e6aedc8c3a0a0476e87135..44100a749b09439121073e32e7a3143b54185d31 100644 (file)
@@ -17,15 +17,22 @@ static inline void dup_devnull(int to)
 
 int start_command(struct child_process *cmd)
 {
-       int need_in, need_out;
-       int fdin[2], fdout[2];
+       int need_in, need_out, need_err;
+       int fdin[2], fdout[2], fderr[2];
+
+       /*
+        * In case of errors we must keep the promise to close FDs
+        * that have been passed in via ->in and ->out.
+        */
 
        need_in = !cmd->no_stdin && cmd->in < 0;
        if (need_in) {
-               if (pipe(fdin) < 0)
+               if (pipe(fdin) < 0) {
+                       if (cmd->out > 0)
+                               close(cmd->out);
                        return -ERR_RUN_COMMAND_PIPE;
+               }
                cmd->in = fdin[1];
-               cmd->close_in = 1;
        }
 
        need_out = !cmd->no_stdout
@@ -35,18 +42,41 @@ int start_command(struct child_process *cmd)
                if (pipe(fdout) < 0) {
                        if (need_in)
                                close_pair(fdin);
+                       else if (cmd->in)
+                               close(cmd->in);
                        return -ERR_RUN_COMMAND_PIPE;
                }
                cmd->out = fdout[0];
-               cmd->close_out = 1;
+       }
+
+       need_err = !cmd->no_stderr && cmd->err < 0;
+       if (need_err) {
+               if (pipe(fderr) < 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);
+                       return -ERR_RUN_COMMAND_PIPE;
+               }
+               cmd->err = fderr[0];
        }
 
        cmd->pid = fork();
        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;
        }
 
@@ -61,6 +91,13 @@ int start_command(struct child_process *cmd)
                        close(cmd->in);
                }
 
+               if (cmd->no_stderr)
+                       dup_devnull(2);
+               else if (need_err) {
+                       dup2(fderr[1], 2);
+                       close_pair(fderr);
+               }
+
                if (cmd->no_stdout)
                        dup_devnull(1);
                else if (cmd->stdout_to_stderr)
@@ -99,22 +136,20 @@ int start_command(struct child_process *cmd)
 
        if (need_out)
                close(fdout[1]);
-       else if (cmd->out > 1)
+       else if (cmd->out)
                close(cmd->out);
 
+       if (need_err)
+               close(fderr[1]);
+
        return 0;
 }
 
-int finish_command(struct child_process *cmd)
+static int wait_or_whine(pid_t pid)
 {
-       if (cmd->close_in)
-               close(cmd->in);
-       if (cmd->close_out)
-               close(cmd->out);
-
        for (;;) {
                int status, code;
-               pid_t waiting = waitpid(cmd->pid, &status, 0);
+               pid_t waiting = waitpid(pid, &status, 0);
 
                if (waiting < 0) {
                        if (errno == EINTR)
@@ -122,7 +157,7 @@ int finish_command(struct child_process *cmd)
                        error("waitpid failed (%s)", strerror(errno));
                        return -ERR_RUN_COMMAND_WAITPID;
                }
-               if (waiting != cmd->pid)
+               if (waiting != pid)
                        return -ERR_RUN_COMMAND_WAITPID_WRONG_PID;
                if (WIFSIGNALED(status))
                        return -ERR_RUN_COMMAND_WAITPID_SIGNAL;
@@ -136,6 +171,11 @@ int finish_command(struct child_process *cmd)
        }
 }
 
+int finish_command(struct child_process *cmd)
+{
+       return wait_or_whine(cmd->pid);
+}
+
 int run_command(struct child_process *cmd)
 {
        int code = start_command(cmd);
@@ -178,3 +218,34 @@ int run_command_v_opt_cd_env(const char **argv, int opt, const char *dir, const
        cmd.env = env;
        return run_command(&cmd);
 }
+
+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;
+}