Exec git programs without using PATH.
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>maint
							parent
							
								
									c884dd9a54
								
							
						
					
					
						commit
						77cb17e940
					
				
							
								
								
									
										13
									
								
								Makefile
								
								
								
								
							
							
						
						
									
										13
									
								
								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)) | ||||
|  |  | |||
							
								
								
									
										3
									
								
								daemon.c
								
								
								
								
							
							
						
						
									
										3
									
								
								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; | ||||
| } | ||||
|  | ||||
|  |  | |||
|  | @ -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); | ||||
| } | ||||
|  | @ -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_ */ | ||||
|  | @ -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]); | ||||
|  |  | |||
							
								
								
									
										50
									
								
								git.c
								
								
								
								
							
							
						
						
									
										50
									
								
								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); | ||||
| 	exec_path = git_exec_path(); | ||||
| 	prepend_to_path(exec_path, strlen(exec_path)); | ||||
|  | ||||
| 		/* 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); | ||||
| 	} | ||||
|  | ||||
| 	/* 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]); | ||||
|  |  | |||
|  | @ -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) { | ||||
|  |  | |||
|  | @ -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 (;;) { | ||||
|  |  | |||
|  | @ -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, ...); | ||||
|  |  | |||
|  | @ -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)); | ||||
| } | ||||
|  | ||||
|  |  | |||
							
								
								
									
										7
									
								
								shell.c
								
								
								
								
							
							
						
						
									
										7
									
								
								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 { | ||||
|  |  | |||
|  | @ -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"); | ||||
| } | ||||
|  | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue
	
	 Michal Ostrowski
						Michal Ostrowski