You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
367 lines
8.0 KiB
367 lines
8.0 KiB
#include "cache.h" |
|
#include "exec-cmd.h" |
|
#include "quote.h" |
|
#include "strvec.h" |
|
|
|
#if defined(RUNTIME_PREFIX) |
|
|
|
#if defined(HAVE_NS_GET_EXECUTABLE_PATH) |
|
#include <mach-o/dyld.h> |
|
#endif |
|
|
|
#if defined(HAVE_BSD_KERN_PROC_SYSCTL) |
|
#include <sys/param.h> |
|
#include <sys/types.h> |
|
#include <sys/sysctl.h> |
|
#endif |
|
|
|
#endif /* RUNTIME_PREFIX */ |
|
|
|
#define MAX_ARGS 32 |
|
|
|
static const char *system_prefix(void); |
|
|
|
#ifdef RUNTIME_PREFIX |
|
|
|
/** |
|
* When using a runtime prefix, Git dynamically resolves paths relative to its |
|
* executable. |
|
* |
|
* The method for determining the path of the executable is highly |
|
* platform-specific. |
|
*/ |
|
|
|
/** |
|
* Path to the current Git executable. Resolved on startup by |
|
* 'git_resolve_executable_dir'. |
|
*/ |
|
static const char *executable_dirname; |
|
|
|
static const char *system_prefix(void) |
|
{ |
|
static const char *prefix; |
|
|
|
assert(executable_dirname); |
|
assert(is_absolute_path(executable_dirname)); |
|
|
|
if (!prefix && |
|
!(prefix = strip_path_suffix(executable_dirname, GIT_EXEC_PATH)) && |
|
!(prefix = strip_path_suffix(executable_dirname, BINDIR)) && |
|
!(prefix = strip_path_suffix(executable_dirname, "git"))) { |
|
prefix = FALLBACK_RUNTIME_PREFIX; |
|
trace_printf("RUNTIME_PREFIX requested, " |
|
"but prefix computation failed. " |
|
"Using static fallback '%s'.\n", prefix); |
|
} |
|
return prefix; |
|
} |
|
|
|
/* |
|
* Resolves the executable path from argv[0], only if it is absolute. |
|
* |
|
* Returns 0 on success, -1 on failure. |
|
*/ |
|
static int git_get_exec_path_from_argv0(struct strbuf *buf, const char *argv0) |
|
{ |
|
const char *slash; |
|
|
|
if (!argv0 || !*argv0) |
|
return -1; |
|
|
|
slash = find_last_dir_sep(argv0); |
|
if (slash) { |
|
trace_printf("trace: resolved executable path from argv0: %s\n", |
|
argv0); |
|
strbuf_add_absolute_path(buf, argv0); |
|
return 0; |
|
} |
|
return -1; |
|
} |
|
|
|
#ifdef PROCFS_EXECUTABLE_PATH |
|
/* |
|
* Resolves the executable path by examining a procfs symlink. |
|
* |
|
* Returns 0 on success, -1 on failure. |
|
*/ |
|
static int git_get_exec_path_procfs(struct strbuf *buf) |
|
{ |
|
if (strbuf_realpath(buf, PROCFS_EXECUTABLE_PATH, 0)) { |
|
trace_printf( |
|
"trace: resolved executable path from procfs: %s\n", |
|
buf->buf); |
|
return 0; |
|
} |
|
return -1; |
|
} |
|
#endif /* PROCFS_EXECUTABLE_PATH */ |
|
|
|
#ifdef HAVE_BSD_KERN_PROC_SYSCTL |
|
/* |
|
* Resolves the executable path using KERN_PROC_PATHNAME BSD sysctl. |
|
* |
|
* Returns 0 on success, -1 on failure. |
|
*/ |
|
static int git_get_exec_path_bsd_sysctl(struct strbuf *buf) |
|
{ |
|
int mib[4]; |
|
char path[MAXPATHLEN]; |
|
size_t cb = sizeof(path); |
|
|
|
mib[0] = CTL_KERN; |
|
mib[1] = KERN_PROC; |
|
mib[2] = KERN_PROC_PATHNAME; |
|
mib[3] = -1; |
|
if (!sysctl(mib, 4, path, &cb, NULL, 0)) { |
|
trace_printf( |
|
"trace: resolved executable path from sysctl: %s\n", |
|
path); |
|
strbuf_addstr(buf, path); |
|
return 0; |
|
} |
|
return -1; |
|
} |
|
#endif /* HAVE_BSD_KERN_PROC_SYSCTL */ |
|
|
|
#ifdef HAVE_NS_GET_EXECUTABLE_PATH |
|
/* |
|
* Resolves the executable path by querying Darwin application stack. |
|
* |
|
* Returns 0 on success, -1 on failure. |
|
*/ |
|
static int git_get_exec_path_darwin(struct strbuf *buf) |
|
{ |
|
char path[PATH_MAX]; |
|
uint32_t size = sizeof(path); |
|
if (!_NSGetExecutablePath(path, &size)) { |
|
trace_printf( |
|
"trace: resolved executable path from Darwin stack: %s\n", |
|
path); |
|
strbuf_addstr(buf, path); |
|
return 0; |
|
} |
|
return -1; |
|
} |
|
#endif /* HAVE_NS_GET_EXECUTABLE_PATH */ |
|
|
|
#ifdef HAVE_WPGMPTR |
|
/* |
|
* Resolves the executable path by using the global variable _wpgmptr. |
|
* |
|
* Returns 0 on success, -1 on failure. |
|
*/ |
|
static int git_get_exec_path_wpgmptr(struct strbuf *buf) |
|
{ |
|
int len = wcslen(_wpgmptr) * 3 + 1; |
|
strbuf_grow(buf, len); |
|
len = xwcstoutf(buf->buf, _wpgmptr, len); |
|
if (len < 0) |
|
return -1; |
|
buf->len += len; |
|
return 0; |
|
} |
|
#endif /* HAVE_WPGMPTR */ |
|
|
|
/* |
|
* Resolves the absolute path of the current executable. |
|
* |
|
* Returns 0 on success, -1 on failure. |
|
*/ |
|
static int git_get_exec_path(struct strbuf *buf, const char *argv0) |
|
{ |
|
/* |
|
* Identifying the executable path is operating system specific. |
|
* Selectively employ all available methods in order of preference, |
|
* preferring highly-available authoritative methods over |
|
* selectively-available or non-authoritative methods. |
|
* |
|
* All cases fall back on resolving against argv[0] if there isn't a |
|
* better functional method. However, note that argv[0] can be |
|
* used-supplied on many operating systems, and is not authoritative |
|
* in those cases. |
|
* |
|
* Each of these functions returns 0 on success, so evaluation will stop |
|
* after the first successful method. |
|
*/ |
|
if ( |
|
#ifdef HAVE_BSD_KERN_PROC_SYSCTL |
|
git_get_exec_path_bsd_sysctl(buf) && |
|
#endif /* HAVE_BSD_KERN_PROC_SYSCTL */ |
|
|
|
#ifdef HAVE_NS_GET_EXECUTABLE_PATH |
|
git_get_exec_path_darwin(buf) && |
|
#endif /* HAVE_NS_GET_EXECUTABLE_PATH */ |
|
|
|
#ifdef PROCFS_EXECUTABLE_PATH |
|
git_get_exec_path_procfs(buf) && |
|
#endif /* PROCFS_EXECUTABLE_PATH */ |
|
|
|
#ifdef HAVE_WPGMPTR |
|
git_get_exec_path_wpgmptr(buf) && |
|
#endif /* HAVE_WPGMPTR */ |
|
|
|
git_get_exec_path_from_argv0(buf, argv0)) { |
|
return -1; |
|
} |
|
|
|
if (strbuf_normalize_path(buf)) { |
|
trace_printf("trace: could not normalize path: %s\n", buf->buf); |
|
return -1; |
|
} |
|
|
|
trace2_cmd_path(buf->buf); |
|
|
|
return 0; |
|
} |
|
|
|
void git_resolve_executable_dir(const char *argv0) |
|
{ |
|
struct strbuf buf = STRBUF_INIT; |
|
char *resolved; |
|
const char *slash; |
|
|
|
if (git_get_exec_path(&buf, argv0)) { |
|
trace_printf( |
|
"trace: could not determine executable path from: %s\n", |
|
argv0); |
|
strbuf_release(&buf); |
|
return; |
|
} |
|
|
|
resolved = strbuf_detach(&buf, NULL); |
|
slash = find_last_dir_sep(resolved); |
|
if (slash) |
|
resolved[slash - resolved] = '\0'; |
|
|
|
executable_dirname = resolved; |
|
trace_printf("trace: resolved executable dir: %s\n", |
|
executable_dirname); |
|
} |
|
|
|
#else |
|
|
|
/* |
|
* When not using a runtime prefix, Git uses a hard-coded path. |
|
*/ |
|
static const char *system_prefix(void) |
|
{ |
|
return FALLBACK_RUNTIME_PREFIX; |
|
} |
|
|
|
/* |
|
* This is called during initialization, but No work needs to be done here when |
|
* runtime prefix is not being used. |
|
*/ |
|
void git_resolve_executable_dir(const char *argv0) |
|
{ |
|
} |
|
|
|
#endif /* RUNTIME_PREFIX */ |
|
|
|
char *system_path(const char *path) |
|
{ |
|
struct strbuf d = STRBUF_INIT; |
|
|
|
if (is_absolute_path(path)) |
|
return xstrdup(path); |
|
|
|
strbuf_addf(&d, "%s/%s", system_prefix(), path); |
|
return strbuf_detach(&d, NULL); |
|
} |
|
|
|
static const char *exec_path_value; |
|
|
|
void git_set_exec_path(const char *exec_path) |
|
{ |
|
exec_path_value = exec_path; |
|
/* |
|
* Propagate this setting to external programs. |
|
*/ |
|
setenv(EXEC_PATH_ENVIRONMENT, exec_path, 1); |
|
} |
|
|
|
/* Returns the highest-priority location to look for git programs. */ |
|
const char *git_exec_path(void) |
|
{ |
|
if (!exec_path_value) { |
|
const char *env = getenv(EXEC_PATH_ENVIRONMENT); |
|
if (env && *env) |
|
exec_path_value = xstrdup(env); |
|
else |
|
exec_path_value = system_path(GIT_EXEC_PATH); |
|
} |
|
return exec_path_value; |
|
} |
|
|
|
static void add_path(struct strbuf *out, const char *path) |
|
{ |
|
if (path && *path) { |
|
strbuf_add_absolute_path(out, path); |
|
strbuf_addch(out, PATH_SEP); |
|
} |
|
} |
|
|
|
void setup_path(void) |
|
{ |
|
const char *exec_path = git_exec_path(); |
|
const char *old_path = getenv("PATH"); |
|
struct strbuf new_path = STRBUF_INIT; |
|
|
|
git_set_exec_path(exec_path); |
|
add_path(&new_path, exec_path); |
|
|
|
if (old_path) |
|
strbuf_addstr(&new_path, old_path); |
|
else |
|
strbuf_addstr(&new_path, _PATH_DEFPATH); |
|
|
|
setenv("PATH", new_path.buf, 1); |
|
|
|
strbuf_release(&new_path); |
|
} |
|
|
|
const char **prepare_git_cmd(struct strvec *out, const char **argv) |
|
{ |
|
strvec_push(out, "git"); |
|
strvec_pushv(out, argv); |
|
return out->v; |
|
} |
|
|
|
int execv_git_cmd(const char **argv) |
|
{ |
|
struct strvec nargv = STRVEC_INIT; |
|
|
|
prepare_git_cmd(&nargv, argv); |
|
trace_argv_printf(nargv.v, "trace: exec:"); |
|
|
|
/* execvp() can only ever return if it fails */ |
|
sane_execvp("git", (char **)nargv.v); |
|
|
|
trace_printf("trace: exec failed: %s\n", strerror(errno)); |
|
|
|
strvec_clear(&nargv); |
|
return -1; |
|
} |
|
|
|
int execl_git_cmd(const char *cmd, ...) |
|
{ |
|
int argc; |
|
const char *argv[MAX_ARGS + 1]; |
|
const 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); |
|
}
|
|
|