Coverage Report

Created: 2024-09-16 06:10

/src/git/exec-cmd.c
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
}