Line | Count | Source (jump to first uncovered line) |
1 | | #include "git-compat-util.h" |
2 | | #include "abspath.h" |
3 | | #include "environment.h" |
4 | | #include "exec-cmd.h" |
5 | | #include "gettext.h" |
6 | | #include "path.h" |
7 | | #include "run-command.h" |
8 | | #include "strvec.h" |
9 | | #include "trace.h" |
10 | | #include "trace2.h" |
11 | | |
12 | | #if defined(RUNTIME_PREFIX) |
13 | | |
14 | | #if defined(HAVE_NS_GET_EXECUTABLE_PATH) |
15 | | #include <mach-o/dyld.h> |
16 | | #endif |
17 | | |
18 | | #if defined(HAVE_BSD_KERN_PROC_SYSCTL) |
19 | | #include <sys/param.h> |
20 | | #include <sys/types.h> |
21 | | #include <sys/sysctl.h> |
22 | | #endif |
23 | | |
24 | | #endif /* RUNTIME_PREFIX */ |
25 | | |
26 | 0 | #define MAX_ARGS 32 |
27 | | |
28 | | static const char *system_prefix(void); |
29 | | |
30 | | #ifdef RUNTIME_PREFIX |
31 | | |
32 | | /** |
33 | | * When using a runtime prefix, Git dynamically resolves paths relative to its |
34 | | * executable. |
35 | | * |
36 | | * The method for determining the path of the executable is highly |
37 | | * platform-specific. |
38 | | */ |
39 | | |
40 | | /** |
41 | | * Path to the current Git executable. Resolved on startup by |
42 | | * 'git_resolve_executable_dir'. |
43 | | */ |
44 | | static const char *executable_dirname; |
45 | | |
46 | | static const char *system_prefix(void) |
47 | | { |
48 | | static const char *prefix; |
49 | | |
50 | | assert(executable_dirname); |
51 | | assert(is_absolute_path(executable_dirname)); |
52 | | |
53 | | if (!prefix && |
54 | | !(prefix = strip_path_suffix(executable_dirname, GIT_EXEC_PATH)) && |
55 | | !(prefix = strip_path_suffix(executable_dirname, BINDIR)) && |
56 | | !(prefix = strip_path_suffix(executable_dirname, "git"))) { |
57 | | prefix = FALLBACK_RUNTIME_PREFIX; |
58 | | trace_printf("RUNTIME_PREFIX requested, " |
59 | | "but prefix computation failed. " |
60 | | "Using static fallback '%s'.\n", prefix); |
61 | | } |
62 | | return prefix; |
63 | | } |
64 | | |
65 | | /* |
66 | | * Resolves the executable path from argv[0], only if it is absolute. |
67 | | * |
68 | | * Returns 0 on success, -1 on failure. |
69 | | */ |
70 | | static int git_get_exec_path_from_argv0(struct strbuf *buf, const char *argv0) |
71 | | { |
72 | | const char *slash; |
73 | | |
74 | | if (!argv0 || !*argv0) |
75 | | return -1; |
76 | | |
77 | | slash = find_last_dir_sep(argv0); |
78 | | if (slash) { |
79 | | trace_printf("trace: resolved executable path from argv0: %s\n", |
80 | | argv0); |
81 | | strbuf_add_absolute_path(buf, argv0); |
82 | | return 0; |
83 | | } |
84 | | return -1; |
85 | | } |
86 | | |
87 | | #ifdef PROCFS_EXECUTABLE_PATH |
88 | | /* |
89 | | * Resolves the executable path by examining a procfs symlink. |
90 | | * |
91 | | * Returns 0 on success, -1 on failure. |
92 | | */ |
93 | | static int git_get_exec_path_procfs(struct strbuf *buf) |
94 | | { |
95 | | if (strbuf_realpath(buf, PROCFS_EXECUTABLE_PATH, 0)) { |
96 | | trace_printf( |
97 | | "trace: resolved executable path from procfs: %s\n", |
98 | | buf->buf); |
99 | | return 0; |
100 | | } |
101 | | return -1; |
102 | | } |
103 | | #endif /* PROCFS_EXECUTABLE_PATH */ |
104 | | |
105 | | #ifdef HAVE_BSD_KERN_PROC_SYSCTL |
106 | | /* |
107 | | * Resolves the executable path using KERN_PROC_PATHNAME BSD sysctl. |
108 | | * |
109 | | * Returns 0 on success, -1 on failure. |
110 | | */ |
111 | | static int git_get_exec_path_bsd_sysctl(struct strbuf *buf) |
112 | | { |
113 | | int mib[4]; |
114 | | char path[MAXPATHLEN]; |
115 | | size_t cb = sizeof(path); |
116 | | |
117 | | mib[0] = CTL_KERN; |
118 | | mib[1] = KERN_PROC; |
119 | | mib[2] = KERN_PROC_PATHNAME; |
120 | | mib[3] = -1; |
121 | | if (!sysctl(mib, 4, path, &cb, NULL, 0)) { |
122 | | trace_printf( |
123 | | "trace: resolved executable path from sysctl: %s\n", |
124 | | path); |
125 | | strbuf_addstr(buf, path); |
126 | | return 0; |
127 | | } |
128 | | return -1; |
129 | | } |
130 | | #endif /* HAVE_BSD_KERN_PROC_SYSCTL */ |
131 | | |
132 | | #ifdef HAVE_NS_GET_EXECUTABLE_PATH |
133 | | /* |
134 | | * Resolves the executable path by querying Darwin application stack. |
135 | | * |
136 | | * Returns 0 on success, -1 on failure. |
137 | | */ |
138 | | static int git_get_exec_path_darwin(struct strbuf *buf) |
139 | | { |
140 | | char path[PATH_MAX]; |
141 | | uint32_t size = sizeof(path); |
142 | | if (!_NSGetExecutablePath(path, &size)) { |
143 | | trace_printf( |
144 | | "trace: resolved executable path from Darwin stack: %s\n", |
145 | | path); |
146 | | strbuf_addstr(buf, path); |
147 | | return 0; |
148 | | } |
149 | | return -1; |
150 | | } |
151 | | #endif /* HAVE_NS_GET_EXECUTABLE_PATH */ |
152 | | |
153 | | #ifdef HAVE_ZOS_GET_EXECUTABLE_PATH |
154 | | /* |
155 | | * Resolves the executable path from current program's directory and name. |
156 | | * |
157 | | * Returns 0 on success, -1 on failure. |
158 | | */ |
159 | | static int git_get_exec_path_zos(struct strbuf *buf) |
160 | | { |
161 | | char *dir = __getprogramdir(); |
162 | | char *exe = getprogname(); |
163 | | if (dir && exe) { |
164 | | strbuf_addf(buf, "%s/%s", dir, exe); |
165 | | return 0; |
166 | | } |
167 | | return -1; |
168 | | } |
169 | | |
170 | | #endif /* HAVE_ZOS_GET_EXECUTABLE_PATH */ |
171 | | |
172 | | #ifdef HAVE_WPGMPTR |
173 | | /* |
174 | | * Resolves the executable path by using the global variable _wpgmptr. |
175 | | * |
176 | | * Returns 0 on success, -1 on failure. |
177 | | */ |
178 | | static int git_get_exec_path_wpgmptr(struct strbuf *buf) |
179 | | { |
180 | | int len = wcslen(_wpgmptr) * 3 + 1; |
181 | | strbuf_grow(buf, len); |
182 | | len = xwcstoutf(buf->buf, _wpgmptr, len); |
183 | | if (len < 0) |
184 | | return -1; |
185 | | buf->len += len; |
186 | | return 0; |
187 | | } |
188 | | #endif /* HAVE_WPGMPTR */ |
189 | | |
190 | | /* |
191 | | * Resolves the absolute path of the current executable. |
192 | | * |
193 | | * Returns 0 on success, -1 on failure. |
194 | | */ |
195 | | static int git_get_exec_path(struct strbuf *buf, const char *argv0) |
196 | | { |
197 | | /* |
198 | | * Identifying the executable path is operating system specific. |
199 | | * Selectively employ all available methods in order of preference, |
200 | | * preferring highly-available authoritative methods over |
201 | | * selectively-available or non-authoritative methods. |
202 | | * |
203 | | * All cases fall back on resolving against argv[0] if there isn't a |
204 | | * better functional method. However, note that argv[0] can be |
205 | | * used-supplied on many operating systems, and is not authoritative |
206 | | * in those cases. |
207 | | * |
208 | | * Each of these functions returns 0 on success, so evaluation will stop |
209 | | * after the first successful method. |
210 | | */ |
211 | | if ( |
212 | | #ifdef HAVE_BSD_KERN_PROC_SYSCTL |
213 | | git_get_exec_path_bsd_sysctl(buf) && |
214 | | #endif /* HAVE_BSD_KERN_PROC_SYSCTL */ |
215 | | |
216 | | #ifdef HAVE_NS_GET_EXECUTABLE_PATH |
217 | | git_get_exec_path_darwin(buf) && |
218 | | #endif /* HAVE_NS_GET_EXECUTABLE_PATH */ |
219 | | |
220 | | #ifdef PROCFS_EXECUTABLE_PATH |
221 | | git_get_exec_path_procfs(buf) && |
222 | | #endif /* PROCFS_EXECUTABLE_PATH */ |
223 | | |
224 | | #ifdef HAVE_WPGMPTR |
225 | | git_get_exec_path_wpgmptr(buf) && |
226 | | #endif /* HAVE_WPGMPTR */ |
227 | | |
228 | | #ifdef HAVE_ZOS_GET_EXECUTABLE_PATH |
229 | | git_get_exec_path_zos(buf) && |
230 | | #endif /*HAVE_ZOS_GET_EXECUTABLE_PATH */ |
231 | | |
232 | | git_get_exec_path_from_argv0(buf, argv0)) { |
233 | | return -1; |
234 | | } |
235 | | |
236 | | if (strbuf_normalize_path(buf)) { |
237 | | trace_printf("trace: could not normalize path: %s\n", buf->buf); |
238 | | return -1; |
239 | | } |
240 | | |
241 | | trace2_cmd_path(buf->buf); |
242 | | |
243 | | return 0; |
244 | | } |
245 | | |
246 | | void git_resolve_executable_dir(const char *argv0) |
247 | | { |
248 | | struct strbuf buf = STRBUF_INIT; |
249 | | char *resolved; |
250 | | const char *slash; |
251 | | |
252 | | if (git_get_exec_path(&buf, argv0)) { |
253 | | trace_printf( |
254 | | "trace: could not determine executable path from: %s\n", |
255 | | argv0); |
256 | | strbuf_release(&buf); |
257 | | return; |
258 | | } |
259 | | |
260 | | resolved = strbuf_detach(&buf, NULL); |
261 | | slash = find_last_dir_sep(resolved); |
262 | | if (slash) |
263 | | resolved[slash - resolved] = '\0'; |
264 | | |
265 | | executable_dirname = resolved; |
266 | | trace_printf("trace: resolved executable dir: %s\n", |
267 | | executable_dirname); |
268 | | } |
269 | | |
270 | | #else |
271 | | |
272 | | /* |
273 | | * When not using a runtime prefix, Git uses a hard-coded path. |
274 | | */ |
275 | | static const char *system_prefix(void) |
276 | 0 | { |
277 | 0 | return FALLBACK_RUNTIME_PREFIX; |
278 | 0 | } |
279 | | |
280 | | /* |
281 | | * This is called during initialization, but No work needs to be done here when |
282 | | * runtime prefix is not being used. |
283 | | */ |
284 | | void git_resolve_executable_dir(const char *argv0 UNUSED) |
285 | 0 | { |
286 | 0 | } |
287 | | |
288 | | #endif /* RUNTIME_PREFIX */ |
289 | | |
290 | | char *system_path(const char *path) |
291 | 0 | { |
292 | 0 | struct strbuf d = STRBUF_INIT; |
293 | |
|
294 | 0 | if (is_absolute_path(path)) |
295 | 0 | return xstrdup(path); |
296 | | |
297 | 0 | strbuf_addf(&d, "%s/%s", system_prefix(), path); |
298 | 0 | return strbuf_detach(&d, NULL); |
299 | 0 | } |
300 | | |
301 | | static const char *exec_path_value; |
302 | | |
303 | | void git_set_exec_path(const char *exec_path) |
304 | 0 | { |
305 | 0 | exec_path_value = exec_path; |
306 | | /* |
307 | | * Propagate this setting to external programs. |
308 | | */ |
309 | 0 | setenv(EXEC_PATH_ENVIRONMENT, exec_path, 1); |
310 | 0 | } |
311 | | |
312 | | /* Returns the highest-priority location to look for git programs. */ |
313 | | const char *git_exec_path(void) |
314 | 0 | { |
315 | 0 | if (!exec_path_value) { |
316 | 0 | const char *env = getenv(EXEC_PATH_ENVIRONMENT); |
317 | 0 | if (env && *env) |
318 | 0 | exec_path_value = xstrdup(env); |
319 | 0 | else |
320 | 0 | exec_path_value = system_path(GIT_EXEC_PATH); |
321 | 0 | } |
322 | 0 | return exec_path_value; |
323 | 0 | } |
324 | | |
325 | | static void add_path(struct strbuf *out, const char *path) |
326 | 0 | { |
327 | 0 | if (path && *path) { |
328 | 0 | strbuf_add_absolute_path(out, path); |
329 | 0 | strbuf_addch(out, PATH_SEP); |
330 | 0 | } |
331 | 0 | } |
332 | | |
333 | | void setup_path(void) |
334 | 0 | { |
335 | 0 | const char *exec_path = git_exec_path(); |
336 | 0 | const char *old_path = getenv("PATH"); |
337 | 0 | struct strbuf new_path = STRBUF_INIT; |
338 | |
|
339 | 0 | git_set_exec_path(exec_path); |
340 | 0 | add_path(&new_path, exec_path); |
341 | |
|
342 | 0 | if (old_path) |
343 | 0 | strbuf_addstr(&new_path, old_path); |
344 | 0 | else |
345 | 0 | strbuf_addstr(&new_path, _PATH_DEFPATH); |
346 | |
|
347 | 0 | setenv("PATH", new_path.buf, 1); |
348 | |
|
349 | 0 | strbuf_release(&new_path); |
350 | 0 | } |
351 | | |
352 | | const char **prepare_git_cmd(struct strvec *out, const char **argv) |
353 | 0 | { |
354 | 0 | strvec_push(out, "git"); |
355 | 0 | strvec_pushv(out, argv); |
356 | 0 | return out->v; |
357 | 0 | } |
358 | | |
359 | | int execv_git_cmd(const char **argv) |
360 | 0 | { |
361 | 0 | struct strvec nargv = STRVEC_INIT; |
362 | |
|
363 | 0 | prepare_git_cmd(&nargv, argv); |
364 | 0 | trace_argv_printf(nargv.v, "trace: exec:"); |
365 | | |
366 | | /* execvp() can only ever return if it fails */ |
367 | 0 | sane_execvp("git", (char **)nargv.v); |
368 | |
|
369 | 0 | trace_printf("trace: exec failed: %s\n", strerror(errno)); |
370 | |
|
371 | 0 | strvec_clear(&nargv); |
372 | 0 | return -1; |
373 | 0 | } |
374 | | |
375 | | int execl_git_cmd(const char *cmd, ...) |
376 | 0 | { |
377 | 0 | int argc; |
378 | 0 | const char *argv[MAX_ARGS + 1]; |
379 | 0 | const char *arg; |
380 | 0 | va_list param; |
381 | |
|
382 | 0 | va_start(param, cmd); |
383 | 0 | argv[0] = cmd; |
384 | 0 | argc = 1; |
385 | 0 | while (argc < MAX_ARGS) { |
386 | 0 | arg = argv[argc++] = va_arg(param, char *); |
387 | 0 | if (!arg) |
388 | 0 | break; |
389 | 0 | } |
390 | 0 | va_end(param); |
391 | 0 | if (MAX_ARGS <= argc) |
392 | 0 | return error(_("too many args to run %s"), cmd); |
393 | | |
394 | 0 | argv[argc] = NULL; |
395 | 0 | return execv_git_cmd(argv); |
396 | 0 | } |