Exec git programs without using PATH.
authorMichal Ostrowski <mostrows@watson.ibm.com>
Wed, 11 Jan 2006 02:12:17 +0000 (21:12 -0500)
committerJunio C Hamano <junkio@cox.net>
Sat, 14 Jan 2006 00:49:01 +0000 (16:49 -0800)
The git suite may not be in PATH (and thus programs such as
git-send-pack could not exec git-rev-list).  Thus there is a need for
logic that will locate these programs.  Modifying PATH is not
desirable as it result in behavior differing from the user's
intentions, as we may end up prepending "/usr/bin" to PATH.

- git C programs will use exec*_git_cmd() APIs to exec sub-commands.
- exec*_git_cmd() will execute a git program by searching for it in
  the following directories:
1. --exec-path (as used by "git")
2. The GIT_EXEC_PATH environment variable.
3. $(gitexecdir) as set in Makefile (default value $(bindir)).
- git wrapper will modify PATH as before to enable shell scripts to
  invoke "git-foo" commands.

Ideally, shell scripts should use the git wrapper to become independent
of PATH, and then modifying PATH will not be necessary.

[jc: with minor updates after a brief review.]

Signed-off-by: Michal Ostrowski <mostrows@watson.ibm.com>
Signed-off-by: Junio C Hamano <junkio@cox.net>
12 files changed:
Makefile
daemon.c
exec_cmd.c [new file with mode: 0644]
exec_cmd.h [new file with mode: 0644]
fetch-clone.c
git.c
receive-pack.c
run-command.c
run-command.h
send-pack.c
shell.c
upload-pack.c

index 37388119528f1d687116075760c31fe9715aeb13..c48ae3bc656980eab9bf1e5f649778e447ffd11c 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -69,6 +69,7 @@ STRIP ?= strip
 
 prefix = $(HOME)
 bindir = $(prefix)/bin
+gitexecdir = $(prefix)/bin
 template_dir = $(prefix)/share/git-core/templates/
 GIT_PYTHON_DIR = $(prefix)/share/git-core/python
 # DESTDIR=
@@ -142,7 +143,7 @@ PROGRAMS = \
        git-describe$X
 
 # what 'all' will build and 'install' will install.
-ALL_PROGRAMS = $(PROGRAMS) $(SIMPLE_PROGRAMS) $(SCRIPTS) git$X
+ALL_PROGRAMS = $(PROGRAMS) $(SIMPLE_PROGRAMS) $(SCRIPTS)
 
 # Backward compatibility -- to be removed after 1.0
 PROGRAMS += git-ssh-pull$X git-ssh-push$X
@@ -174,7 +175,7 @@ DIFF_OBJS = \
 
 LIB_OBJS = \
        blob.o commit.o connect.o count-delta.o csum-file.o \
-       date.o diff-delta.o entry.o ident.o index.o \
+       date.o diff-delta.o entry.o exec_cmd.o ident.o index.o \
        object.o pack-check.o patch-delta.o path.o pkt-line.o \
        quote.o read-cache.o refs.o run-command.o \
        server-info.o setup.o sha1_file.o sha1_name.o strbuf.o \
@@ -367,7 +368,7 @@ LIB_OBJS += $(COMPAT_OBJS)
 export prefix TAR INSTALL DESTDIR SHELL_PATH template_dir
 ### Build rules
 
-all: $(ALL_PROGRAMS)
+all: $(ALL_PROGRAMS) git$X
 
 all:
        $(MAKE) -C templates
@@ -376,7 +377,7 @@ strip: $(PROGRAMS) git$X
        $(STRIP) $(STRIP_OPTS) $(PROGRAMS) git$X
 
 git$X: git.c $(LIB_FILE)
-       $(CC) -DGIT_EXEC_PATH='"$(bindir)"' -DGIT_VERSION='"$(GIT_VERSION)"' \
+       $(CC) -DGIT_VERSION='"$(GIT_VERSION)"' \
                $(CFLAGS) $(COMPAT_CFLAGS) -o $@ $(filter %.c,$^) $(LIB_FILE)
 
 $(patsubst %.sh,%,$(SCRIPT_SH)) : % : %.sh
@@ -416,6 +417,8 @@ git$X git.spec \
 %.o: %.S
        $(CC) -o $*.o -c $(ALL_CFLAGS) $<
 
+exec_cmd.o: ALL_CFLAGS += -DGIT_EXEC_PATH=\"$(gitexecdir)\"
+
 git-%$X: %.o $(LIB_FILE)
        $(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(LIBS)
 
@@ -472,7 +475,9 @@ check:
 
 install: all
        $(INSTALL) -d -m755 $(call shellquote,$(DESTDIR)$(bindir))
+       $(INSTALL) -d -m755 $(call shellquote,$(DESTDIR)$(gitexecdir))
        $(INSTALL) $(ALL_PROGRAMS) $(call shellquote,$(DESTDIR)$(bindir))
+       $(INSTALL) git$X $(call shellquote,$(DESTDIR)$(gitexecdir))
        $(MAKE) -C templates install
        $(INSTALL) -d -m755 $(call shellquote,$(DESTDIR)$(GIT_PYTHON_DIR))
        $(INSTALL) $(PYMODULES) $(call shellquote,$(DESTDIR)$(GIT_PYTHON_DIR))
index 3bd14269d7e779630c1b00a3cf8a8cdba96ffc27..bb014fa9c2cb8c02a3952d0fc521358222662da1 100644 (file)
--- a/daemon.c
+++ b/daemon.c
@@ -9,6 +9,7 @@
 #include <syslog.h>
 #include "pkt-line.h"
 #include "cache.h"
+#include "exec_cmd.h"
 
 static int log_syslog;
 static int verbose;
@@ -227,7 +228,7 @@ static int upload(char *dir)
        snprintf(timeout_buf, sizeof timeout_buf, "--timeout=%u", timeout);
 
        /* git-upload-pack only ever reads stuff, so this is safe */
-       execlp("git-upload-pack", "git-upload-pack", "--strict", timeout_buf, ".", NULL);
+       execl_git_cmd("upload-pack", "--strict", timeout_buf, ".", NULL);
        return -1;
 }
 
diff --git a/exec_cmd.c b/exec_cmd.c
new file mode 100644 (file)
index 0000000..55af33b
--- /dev/null
@@ -0,0 +1,117 @@
+#include "cache.h"
+#include "exec_cmd.h"
+#define MAX_ARGS       32
+
+extern char **environ;
+static const char *builtin_exec_path = GIT_EXEC_PATH;
+static const char *current_exec_path = NULL;
+
+void git_set_exec_path(const char *exec_path)
+{
+       current_exec_path = exec_path;
+}
+
+
+/* Returns the highest-priority, location to look for git programs. */
+const char *git_exec_path()
+{
+       const char *env;
+
+       if (current_exec_path)
+               return current_exec_path;
+
+       env = getenv("GIT_EXEC_PATH");
+       if (env) {
+               return env;
+       }
+
+       return builtin_exec_path;
+}
+
+
+int execv_git_cmd(char **argv)
+{
+       char git_command[PATH_MAX + 1];
+       char *tmp;
+       int len, err, i;
+       const char *paths[] = { current_exec_path,
+                               getenv("GIT_EXEC_PATH"),
+                               builtin_exec_path };
+
+       for (i = 0; i < sizeof(paths)/sizeof(paths[0]); ++i) {
+               const char *exec_dir = paths[i];
+               if (!exec_dir) continue;
+
+               if (*exec_dir != '/') {
+                       if (!getcwd(git_command, sizeof(git_command))) {
+                               fprintf(stderr, "git: cannot determine "
+                                       "current directory\n");
+                               exit(1);
+                       }
+                       len = strlen(git_command);
+
+                       /* Trivial cleanup */
+                       while (!strncmp(exec_dir, "./", 2)) {
+                               exec_dir += 2;
+                               while (*exec_dir == '/')
+                                       exec_dir++;
+                       }
+                       snprintf(git_command + len, sizeof(git_command) - len,
+                                "/%s", exec_dir);
+               } else {
+                       strcpy(git_command, exec_dir);
+               }
+
+               len = strlen(git_command);
+               len += snprintf(git_command + len, sizeof(git_command) - len,
+                               "/git-%s", argv[0]);
+
+               if (sizeof(git_command) <= len) {
+                       fprintf(stderr,
+                               "git: command name given is too long.\n");
+                       break;
+               }
+
+               /* argv[0] must be the git command, but the argv array
+                * belongs to the caller, and my be reused in
+                * subsequent loop iterations. Save argv[0] and
+                * restore it on error.
+                */
+
+               tmp = argv[0];
+               argv[0] = git_command;
+
+               /* execve() can only ever return if it fails */
+               execve(git_command, argv, environ);
+
+               err = errno;
+
+               argv[0] = tmp;
+       }
+       return -1;
+
+}
+
+
+int execl_git_cmd(char *cmd,...)
+{
+       int argc;
+       char *argv[MAX_ARGS + 1];
+       char *arg;
+       va_list param;
+
+       va_start(param, cmd);
+       argv[0] = cmd;
+       argc = 1;
+       while (argc < MAX_ARGS) {
+               arg = argv[argc++] = va_arg(param, char *);
+               if (!arg)
+                       break;
+       }
+       va_end(param);
+       if (MAX_ARGS <= argc)
+               return error("too many args to run %s", cmd);
+
+       argv[argc] = NULL;
+       return execv_git_cmd(argv);
+}
diff --git a/exec_cmd.h b/exec_cmd.h
new file mode 100644 (file)
index 0000000..5150ee2
--- /dev/null
@@ -0,0 +1,10 @@
+#ifndef __GIT_EXEC_CMD_H_
+#define __GIT_EXEC_CMD_H_
+
+extern void git_set_exec_path(const char *exec_path);
+extern const char* git_exec_path(void);
+extern int execv_git_cmd(char **argv); /* NULL terminated */
+extern int execl_git_cmd(char *cmd, ...);
+
+
+#endif /* __GIT_EXEC_CMD_H_ */
index f46fe6ecbb775b965fb6110dc81746cd5bb230c5..859f4009419d37c2a21ecf6941aaad4fe004e747 100644 (file)
@@ -1,4 +1,5 @@
 #include "cache.h"
+#include "exec_cmd.h"
 #include <sys/wait.h>
 
 static int finish_pack(const char *pack_tmp_name, const char *me)
@@ -27,8 +28,7 @@ static int finish_pack(const char *pack_tmp_name, const char *me)
                dup2(pipe_fd[1], 1);
                close(pipe_fd[0]);
                close(pipe_fd[1]);
-               execlp("git-index-pack","git-index-pack",
-                      "-o", idx, pack_tmp_name, NULL);
+               execl_git_cmd("index-pack", "-o", idx, pack_tmp_name, NULL);
                error("cannot exec git-index-pack <%s> <%s>",
                      idx, pack_tmp_name);
                exit(1);
@@ -105,8 +105,7 @@ int receive_unpack_pack(int fd[2], const char *me, int quiet)
                dup2(fd[0], 0);
                close(fd[0]);
                close(fd[1]);
-               execlp("git-unpack-objects", "git-unpack-objects",
-                      quiet ? "-q" : NULL, NULL);
+               execl_git_cmd("unpack-objects", quiet ? "-q" : NULL, NULL);
                die("git-unpack-objects exec failed");
        }
        close(fd[0]);
diff --git a/git.c b/git.c
index 5e7da74b6800a4256f9bcb0d0add7560213271e1..4616df6e62de5fb2f4a22ebb04fb87664579bb84 100644 (file)
--- a/git.c
+++ b/git.c
@@ -10,6 +10,7 @@
 #include <stdarg.h>
 #include <sys/ioctl.h>
 #include "git-compat-util.h"
+#include "exec_cmd.h"
 
 #ifndef PATH_MAX
 # define PATH_MAX 4096
@@ -233,14 +234,11 @@ int main(int argc, char **argv, char **envp)
 {
        char git_command[PATH_MAX + 1];
        char wd[PATH_MAX + 1];
-       int i, len, show_help = 0;
-       char *exec_path = getenv("GIT_EXEC_PATH");
+       int i, show_help = 0;
+       const char *exec_path;
 
        getcwd(wd, PATH_MAX);
 
-       if (!exec_path)
-               exec_path = GIT_EXEC_PATH;
-
        for (i = 1; i < argc; i++) {
                char *arg = argv[i];
 
@@ -256,10 +254,11 @@ int main(int argc, char **argv, char **envp)
 
                if (!strncmp(arg, "exec-path", 9)) {
                        arg += 9;
-                       if (*arg == '=')
+                       if (*arg == '=') {
                                exec_path = arg + 1;
-                       else {
-                               puts(exec_path);
+                               git_set_exec_path(exec_path);
+                       } else {
+                               puts(git_exec_path());
                                exit(0);
                        }
                }
@@ -275,42 +274,15 @@ int main(int argc, char **argv, char **envp)
 
        if (i >= argc || show_help) {
                if (i >= argc)
-                       cmd_usage(exec_path, NULL);
+                       cmd_usage(git_exec_path(), NULL);
 
                show_man_page(argv[i]);
        }
 
-       if (*exec_path != '/') {
-               if (!getcwd(git_command, sizeof(git_command))) {
-                       fprintf(stderr,
-                               "git: cannot determine current directory\n");
-                       exit(1);
-               }
-               len = strlen(git_command);
-
-               /* Trivial cleanup */
-               while (!strncmp(exec_path, "./", 2)) {
-                       exec_path += 2;
-                       while (*exec_path == '/')
-                               exec_path++;
-               }
-               snprintf(git_command + len, sizeof(git_command) - len,
-                        "/%s", exec_path);
-       }
-       else
-               strcpy(git_command, exec_path);
-       len = strlen(git_command);
-       prepend_to_path(git_command, len);
-
-       len += snprintf(git_command + len, sizeof(git_command) - len,
-                       "/git-%s", argv[i]);
-       if (sizeof(git_command) <= len) {
-               fprintf(stderr, "git: command name given is too long.\n");
-               exit(1);
-       }
+       exec_path = git_exec_path();
+       prepend_to_path(exec_path, strlen(exec_path));
 
-       /* execve() can only ever return if it fails */
-       execve(git_command, &argv[i], envp);
+       execv_git_cmd(argv + i);
 
        if (errno == ENOENT)
                cmd_usage(exec_path, "'%s' is not a git-command", argv[i]);
index f847ec2b54b74e2b9bee7bde8fccbaa8ee569573..eae31e370d44988b9a8ee9ddb6c3191b4f18bf3e 100644 (file)
@@ -6,7 +6,7 @@
 
 static const char receive_pack_usage[] = "git-receive-pack <git-dir>";
 
-static const char unpacker[] = "git-unpack-objects";
+static char *unpacker[] = { "unpack-objects", NULL };
 
 static int report_status = 0;
 
@@ -257,7 +257,7 @@ static void read_head_info(void)
 
 static const char *unpack(int *error_code)
 {
-       int code = run_command(unpacker, NULL);
+       int code = run_command_v_opt(1, unpacker, RUN_GIT_CMD);
 
        *error_code = 0;
        switch (code) {
index 8bf5922fc3b30d06ca7336a8df18a12c389c8681..b3d287e97e7d13c1f8d4e8eab5c9af7359ccbc07 100644 (file)
@@ -1,6 +1,7 @@
 #include "cache.h"
 #include "run-command.h"
 #include <sys/wait.h>
+#include "exec_cmd.h"
 
 int run_command_v_opt(int argc, char **argv, int flags)
 {
@@ -13,9 +14,13 @@ int run_command_v_opt(int argc, char **argv, int flags)
                        int fd = open("/dev/null", O_RDWR);
                        dup2(fd, 0);
                        dup2(fd, 1);
-                       close(fd);                      
+                       close(fd);
+               }
+               if (flags & RUN_GIT_CMD) {
+                       execv_git_cmd(argv);
+               } else {
+                       execvp(argv[0], (char *const*) argv);
                }
-               execvp(argv[0], (char *const*) argv);
                die("exec %s failed.", argv[0]);
        }
        for (;;) {
index 2469eeaefbc19424d5ec61f173cc17b68a183bc5..ef3ee053dee4e60db1887020c8ab8840027ff649 100644 (file)
@@ -12,7 +12,7 @@ enum {
 };
 
 #define RUN_COMMAND_NO_STDIO 1
-
+#define RUN_GIT_CMD         2  /*If this is to be git sub-command */
 int run_command_v_opt(int argc, char **argv, int opt);
 int run_command_v(int argc, char **argv);
 int run_command(const char *cmd, ...);
index cd361934449e9bdbce229febaccb8b638037b2e7..990be3f1a338a34025afa4acbe25f4c67ea6ce30 100644 (file)
@@ -3,6 +3,7 @@
 #include "tag.h"
 #include "refs.h"
 #include "pkt-line.h"
+#include "exec_cmd.h"
 
 static const char send_pack_usage[] =
 "git-send-pack [--all] [--exec=git-receive-pack] <remote> [<head>...]\n"
@@ -26,11 +27,11 @@ static int is_zero_sha1(const unsigned char *sha1)
 static void exec_pack_objects(void)
 {
        static char *args[] = {
-               "git-pack-objects",
+               "pack-objects",
                "--stdout",
                NULL
        };
-       execvp("git-pack-objects", args);
+       execv_git_cmd(args);
        die("git-pack-objects exec failed (%s)", strerror(errno));
 }
 
@@ -39,7 +40,7 @@ static void exec_rev_list(struct ref *refs)
        static char *args[1000];
        int i = 0;
 
-       args[i++] = "git-rev-list";     /* 0 */
+       args[i++] = "rev-list"; /* 0 */
        args[i++] = "--objects";        /* 1 */
        while (refs) {
                char *buf = malloc(100);
@@ -58,7 +59,7 @@ static void exec_rev_list(struct ref *refs)
                refs = refs->next;
        }
        args[i] = NULL;
-       execvp("git-rev-list", args);
+       execv_git_cmd(args);
        die("git-rev-list exec failed (%s)", strerror(errno));
 }
 
diff --git a/shell.c b/shell.c
index cd316185e96c73c1f7d0e6309abe3241c2deb7c4..fc0c73cde7370801a0695befbf0e2610d2ea7ef1 100644 (file)
--- a/shell.c
+++ b/shell.c
@@ -1,5 +1,6 @@
 #include "cache.h"
 #include "quote.h"
+#include "exec_cmd.h"
 
 static int do_generic_cmd(const char *me, char *arg)
 {
@@ -7,12 +8,14 @@ static int do_generic_cmd(const char *me, char *arg)
 
        if (!arg || !(arg = sq_dequote(arg)))
                die("bad argument");
+       if (strncmp(me, "git-", 4))
+               die("bad command");
 
-       my_argv[0] = me;
+       my_argv[0] = me + 4;
        my_argv[1] = arg;
        my_argv[2] = NULL;
 
-       return execvp(me, (char**) my_argv);
+       return execv_git_cmd((char**) my_argv);
 }
 
 static struct commands {
index 1834b6ba8c52ea9d8e166d9bc14a4156c289ba20..d1980556ca5b9c62a76129907cc511cd5c0c6c83 100644 (file)
@@ -4,6 +4,7 @@
 #include "tag.h"
 #include "object.h"
 #include "commit.h"
+#include "exec_cmd.h"
 
 static const char upload_pack_usage[] = "git-upload-pack [--strict] [--timeout=nn] <dir>";
 
@@ -60,7 +61,7 @@ static void create_pack_file(void)
                close(0);
                close(fd[0]);
                close(fd[1]);
-               *p++ = "git-rev-list";
+               *p++ = "rev-list";
                *p++ = "--objects";
                if (create_full_pack || MAX_NEEDS <= nr_needs)
                        *p++ = "--all";
@@ -79,13 +80,13 @@ static void create_pack_file(void)
                                buf += 41;
                        }
                *p++ = NULL;
-               execvp("git-rev-list", argv);
+               execv_git_cmd(argv);
                die("git-upload-pack: unable to exec git-rev-list");
        }
        dup2(fd[0], 0);
        close(fd[0]);
        close(fd[1]);
-       execlp("git-pack-objects", "git-pack-objects", "--stdout", NULL);
+       execl_git_cmd("pack-objects", "--stdout", NULL);
        die("git-upload-pack: unable to exec git-pack-objects");
 }