Merge branch 'ab/t3070-test-dedup'
[gitweb.git] / run-command.c
index 615b6e9c9c77f069254314b93583f56df1a1c0b7..9e36151bf97d36945ca3464e719687718ee6d1c7 100644 (file)
@@ -117,6 +117,65 @@ static inline void close_pair(int fd[2])
        close(fd[1]);
 }
 
+int is_executable(const char *name)
+{
+       struct stat st;
+
+       if (stat(name, &st) || /* stat, not lstat */
+           !S_ISREG(st.st_mode))
+               return 0;
+
+#if defined(GIT_WINDOWS_NATIVE)
+       /*
+        * On Windows there is no executable bit. The file extension
+        * indicates whether it can be run as an executable, and Git
+        * has special-handling to detect scripts and launch them
+        * through the indicated script interpreter. We test for the
+        * file extension first because virus scanners may make
+        * it quite expensive to open many files.
+        */
+       if (ends_with(name, ".exe"))
+               return S_IXUSR;
+
+{
+       /*
+        * Now that we know it does not have an executable extension,
+        * peek into the file instead.
+        */
+       char buf[3] = { 0 };
+       int n;
+       int fd = open(name, O_RDONLY);
+       st.st_mode &= ~S_IXUSR;
+       if (fd >= 0) {
+               n = read(fd, buf, 2);
+               if (n == 2)
+                       /* look for a she-bang */
+                       if (!strcmp(buf, "#!"))
+                               st.st_mode |= S_IXUSR;
+               close(fd);
+       }
+}
+#endif
+       return st.st_mode & S_IXUSR;
+}
+
+/*
+ * Search $PATH for a command.  This emulates the path search that
+ * execvp would perform, without actually executing the command so it
+ * can be used before fork() to prepare to run a command using
+ * execve() or after execvp() to diagnose why it failed.
+ *
+ * The caller should ensure that file contains no directory
+ * separators.
+ *
+ * Returns the path to the command, as found in $PATH or NULL if the
+ * command could not be found.  The caller inherits ownership of the memory
+ * used to store the resultant path.
+ *
+ * This should not be used on Windows, where the $PATH search rules
+ * are more complicated (e.g., a search for "foo" should find
+ * "foo.exe").
+ */
 static char *locate_in_PATH(const char *file)
 {
        const char *p = getenv("PATH");
@@ -137,7 +196,7 @@ static char *locate_in_PATH(const char *file)
                }
                strbuf_addstr(&buf, file);
 
-               if (!access(buf.buf, F_OK))
+               if (is_executable(buf.buf))
                        return strbuf_detach(&buf, NULL);
 
                if (!*end)
@@ -215,6 +274,7 @@ enum child_errcode {
        CHILD_ERR_CHDIR,
        CHILD_ERR_DUP2,
        CHILD_ERR_CLOSE,
+       CHILD_ERR_SIGPROCMASK,
        CHILD_ERR_ENOENT,
        CHILD_ERR_SILENT,
        CHILD_ERR_ERRNO
@@ -303,6 +363,9 @@ static void child_err_spew(struct child_process *cmd, struct child_err *cerr)
        case CHILD_ERR_CLOSE:
                error_errno("close() in child failed");
                break;
+       case CHILD_ERR_SIGPROCMASK:
+               error_errno("sigprocmask failed restoring signals");
+               break;
        case CHILD_ERR_ENOENT:
                error_errno("cannot run %s", cmd->argv[0]);
                break;
@@ -398,7 +461,54 @@ static char **prep_childenv(const char *const *deltaenv)
        strbuf_release(&key);
        return childenv;
 }
+
+struct atfork_state {
+#ifndef NO_PTHREADS
+       int cs;
+#endif
+       sigset_t old;
+};
+
+#ifndef NO_PTHREADS
+static void bug_die(int err, const char *msg)
+{
+       if (err) {
+               errno = err;
+               die_errno("BUG: %s", msg);
+       }
+}
+#endif
+
+static void atfork_prepare(struct atfork_state *as)
+{
+       sigset_t all;
+
+       if (sigfillset(&all))
+               die_errno("sigfillset");
+#ifdef NO_PTHREADS
+       if (sigprocmask(SIG_SETMASK, &all, &as->old))
+               die_errno("sigprocmask");
+#else
+       bug_die(pthread_sigmask(SIG_SETMASK, &all, &as->old),
+               "blocking all signals");
+       bug_die(pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &as->cs),
+               "disabling cancellation");
+#endif
+}
+
+static void atfork_parent(struct atfork_state *as)
+{
+#ifdef NO_PTHREADS
+       if (sigprocmask(SIG_SETMASK, &as->old, NULL))
+               die_errno("sigprocmask");
+#else
+       bug_die(pthread_setcancelstate(as->cs, NULL),
+               "re-enabling cancellation");
+       bug_die(pthread_sigmask(SIG_SETMASK, &as->old, NULL),
+               "restoring signal mask");
 #endif
+}
+#endif /* GIT_WINDOWS_NATIVE */
 
 static inline void set_cloexec(int fd)
 {
@@ -523,6 +633,7 @@ int start_command(struct child_process *cmd)
        char **childenv;
        struct argv_array argv = ARGV_ARRAY_INIT;
        struct child_err cerr;
+       struct atfork_state as;
 
        if (pipe(notify_pipe))
                notify_pipe[0] = notify_pipe[1] = -1;
@@ -536,10 +647,21 @@ int start_command(struct child_process *cmd)
 
        prepare_cmd(&argv, cmd);
        childenv = prep_childenv(cmd->env);
+       atfork_prepare(&as);
 
+       /*
+        * NOTE: In order to prevent deadlocking when using threads special
+        * care should be taken with the function calls made in between the
+        * fork() and exec() calls.  No calls should be made to functions which
+        * require acquiring a lock (e.g. malloc) as the lock could have been
+        * held by another thread at the time of forking, causing the lock to
+        * never be released in the child process.  This means only
+        * Async-Signal-Safe functions are permitted in the child.
+        */
        cmd->pid = fork();
        failed_errno = errno;
        if (!cmd->pid) {
+               int sig;
                /*
                 * Ensure the default die/error/warn routines do not get
                 * called, they can take stdio locks and malloc.
@@ -587,6 +709,19 @@ int start_command(struct child_process *cmd)
                if (cmd->dir && chdir(cmd->dir))
                        child_die(CHILD_ERR_CHDIR);
 
+               /*
+                * restore default signal handlers here, in case
+                * we catch a signal right before execve below
+                */
+               for (sig = 1; sig < NSIG; sig++) {
+                       /* ignored signals get reset to SIG_DFL on execve */
+                       if (signal(sig, SIG_DFL) == SIG_IGN)
+                               signal(sig, SIG_IGN);
+               }
+
+               if (sigprocmask(SIG_SETMASK, &as.old, NULL) != 0)
+                       child_die(CHILD_ERR_SIGPROCMASK);
+
                /*
                 * Attempt to exec using the command and arguments starting at
                 * argv.argv[1].  argv.argv[0] contains SHELL_PATH which will
@@ -607,6 +742,7 @@ int start_command(struct child_process *cmd)
                        child_die(CHILD_ERR_ERRNO);
                }
        }
+       atfork_parent(&as);
        if (cmd->pid < 0)
                error_errno("cannot fork() for %s", cmd->argv[0]);
        else if (cmd->clean_on_exit)