run-command.con commit bash completion: Improve responsiveness of git-log completion (ab02dfe)
   1#include "cache.h"
   2#include "run-command.h"
   3#include "exec_cmd.h"
   4
   5static inline void close_pair(int fd[2])
   6{
   7        close(fd[0]);
   8        close(fd[1]);
   9}
  10
  11static inline void dup_devnull(int to)
  12{
  13        int fd = open("/dev/null", O_RDWR);
  14        dup2(fd, to);
  15        close(fd);
  16}
  17
  18int start_command(struct child_process *cmd)
  19{
  20        int need_in, need_out, need_err;
  21        int fdin[2], fdout[2], fderr[2];
  22
  23        /*
  24         * In case of errors we must keep the promise to close FDs
  25         * that have been passed in via ->in and ->out.
  26         */
  27
  28        need_in = !cmd->no_stdin && cmd->in < 0;
  29        if (need_in) {
  30                if (pipe(fdin) < 0) {
  31                        if (cmd->out > 0)
  32                                close(cmd->out);
  33                        return -ERR_RUN_COMMAND_PIPE;
  34                }
  35                cmd->in = fdin[1];
  36        }
  37
  38        need_out = !cmd->no_stdout
  39                && !cmd->stdout_to_stderr
  40                && cmd->out < 0;
  41        if (need_out) {
  42                if (pipe(fdout) < 0) {
  43                        if (need_in)
  44                                close_pair(fdin);
  45                        else if (cmd->in)
  46                                close(cmd->in);
  47                        return -ERR_RUN_COMMAND_PIPE;
  48                }
  49                cmd->out = fdout[0];
  50        }
  51
  52        need_err = !cmd->no_stderr && cmd->err < 0;
  53        if (need_err) {
  54                if (pipe(fderr) < 0) {
  55                        if (need_in)
  56                                close_pair(fdin);
  57                        else if (cmd->in)
  58                                close(cmd->in);
  59                        if (need_out)
  60                                close_pair(fdout);
  61                        else if (cmd->out)
  62                                close(cmd->out);
  63                        return -ERR_RUN_COMMAND_PIPE;
  64                }
  65                cmd->err = fderr[0];
  66        }
  67
  68        trace_argv_printf(cmd->argv, "trace: run_command:");
  69
  70        cmd->pid = fork();
  71        if (cmd->pid < 0) {
  72                if (need_in)
  73                        close_pair(fdin);
  74                else if (cmd->in)
  75                        close(cmd->in);
  76                if (need_out)
  77                        close_pair(fdout);
  78                else if (cmd->out)
  79                        close(cmd->out);
  80                if (need_err)
  81                        close_pair(fderr);
  82                return -ERR_RUN_COMMAND_FORK;
  83        }
  84
  85        if (!cmd->pid) {
  86                if (cmd->no_stdin)
  87                        dup_devnull(0);
  88                else if (need_in) {
  89                        dup2(fdin[0], 0);
  90                        close_pair(fdin);
  91                } else if (cmd->in) {
  92                        dup2(cmd->in, 0);
  93                        close(cmd->in);
  94                }
  95
  96                if (cmd->no_stderr)
  97                        dup_devnull(2);
  98                else if (need_err) {
  99                        dup2(fderr[1], 2);
 100                        close_pair(fderr);
 101                }
 102
 103                if (cmd->no_stdout)
 104                        dup_devnull(1);
 105                else if (cmd->stdout_to_stderr)
 106                        dup2(2, 1);
 107                else if (need_out) {
 108                        dup2(fdout[1], 1);
 109                        close_pair(fdout);
 110                } else if (cmd->out > 1) {
 111                        dup2(cmd->out, 1);
 112                        close(cmd->out);
 113                }
 114
 115                if (cmd->dir && chdir(cmd->dir))
 116                        die("exec %s: cd to %s failed (%s)", cmd->argv[0],
 117                            cmd->dir, strerror(errno));
 118                if (cmd->env) {
 119                        for (; *cmd->env; cmd->env++) {
 120                                if (strchr(*cmd->env, '='))
 121                                        putenv((char*)*cmd->env);
 122                                else
 123                                        unsetenv(*cmd->env);
 124                        }
 125                }
 126                if (cmd->git_cmd) {
 127                        execv_git_cmd(cmd->argv);
 128                } else {
 129                        execvp(cmd->argv[0], (char *const*) cmd->argv);
 130                }
 131                die("exec %s failed.", cmd->argv[0]);
 132        }
 133
 134        if (need_in)
 135                close(fdin[0]);
 136        else if (cmd->in)
 137                close(cmd->in);
 138
 139        if (need_out)
 140                close(fdout[1]);
 141        else if (cmd->out)
 142                close(cmd->out);
 143
 144        if (need_err)
 145                close(fderr[1]);
 146
 147        return 0;
 148}
 149
 150static int wait_or_whine(pid_t pid)
 151{
 152        for (;;) {
 153                int status, code;
 154                pid_t waiting = waitpid(pid, &status, 0);
 155
 156                if (waiting < 0) {
 157                        if (errno == EINTR)
 158                                continue;
 159                        error("waitpid failed (%s)", strerror(errno));
 160                        return -ERR_RUN_COMMAND_WAITPID;
 161                }
 162                if (waiting != pid)
 163                        return -ERR_RUN_COMMAND_WAITPID_WRONG_PID;
 164                if (WIFSIGNALED(status))
 165                        return -ERR_RUN_COMMAND_WAITPID_SIGNAL;
 166
 167                if (!WIFEXITED(status))
 168                        return -ERR_RUN_COMMAND_WAITPID_NOEXIT;
 169                code = WEXITSTATUS(status);
 170                if (code)
 171                        return -code;
 172                return 0;
 173        }
 174}
 175
 176int finish_command(struct child_process *cmd)
 177{
 178        return wait_or_whine(cmd->pid);
 179}
 180
 181int run_command(struct child_process *cmd)
 182{
 183        int code = start_command(cmd);
 184        if (code)
 185                return code;
 186        return finish_command(cmd);
 187}
 188
 189static void prepare_run_command_v_opt(struct child_process *cmd,
 190                                      const char **argv,
 191                                      int opt)
 192{
 193        memset(cmd, 0, sizeof(*cmd));
 194        cmd->argv = argv;
 195        cmd->no_stdin = opt & RUN_COMMAND_NO_STDIN ? 1 : 0;
 196        cmd->git_cmd = opt & RUN_GIT_CMD ? 1 : 0;
 197        cmd->stdout_to_stderr = opt & RUN_COMMAND_STDOUT_TO_STDERR ? 1 : 0;
 198}
 199
 200int run_command_v_opt(const char **argv, int opt)
 201{
 202        struct child_process cmd;
 203        prepare_run_command_v_opt(&cmd, argv, opt);
 204        return run_command(&cmd);
 205}
 206
 207int run_command_v_opt_cd(const char **argv, int opt, const char *dir)
 208{
 209        struct child_process cmd;
 210        prepare_run_command_v_opt(&cmd, argv, opt);
 211        cmd.dir = dir;
 212        return run_command(&cmd);
 213}
 214
 215int run_command_v_opt_cd_env(const char **argv, int opt, const char *dir, const char *const *env)
 216{
 217        struct child_process cmd;
 218        prepare_run_command_v_opt(&cmd, argv, opt);
 219        cmd.dir = dir;
 220        cmd.env = env;
 221        return run_command(&cmd);
 222}
 223
 224int start_async(struct async *async)
 225{
 226        int pipe_out[2];
 227
 228        if (pipe(pipe_out) < 0)
 229                return error("cannot create pipe: %s", strerror(errno));
 230
 231        async->pid = fork();
 232        if (async->pid < 0) {
 233                error("fork (async) failed: %s", strerror(errno));
 234                close_pair(pipe_out);
 235                return -1;
 236        }
 237        if (!async->pid) {
 238                close(pipe_out[0]);
 239                exit(!!async->proc(pipe_out[1], async->data));
 240        }
 241        async->out = pipe_out[0];
 242        close(pipe_out[1]);
 243        return 0;
 244}
 245
 246int finish_async(struct async *async)
 247{
 248        int ret = 0;
 249
 250        if (wait_or_whine(async->pid))
 251                ret = error("waitpid (async) failed");
 252        return ret;
 253}