]>
Commit | Line | Data |
---|---|---|
83634d38 ET |
1 | /* |
2 | * Copyright (C) the libgit2 contributors. All rights reserved. | |
3 | * | |
4 | * This file is part of libgit2, distributed under the GNU GPL v2 with | |
5 | * a Linking Exception. For full terms see the included COPYING file. | |
6 | */ | |
7 | ||
83634d38 | 8 | #include "sysdir.h" |
eae0bfdc | 9 | |
c25aa7cd | 10 | #include "runtime.h" |
83634d38 ET |
11 | #include "buffer.h" |
12 | #include "path.h" | |
13 | #include <ctype.h> | |
14 | #if GIT_WIN32 | |
15 | #include "win32/findfile.h" | |
eae0bfdc PP |
16 | #else |
17 | #include <unistd.h> | |
18 | #include <pwd.h> | |
83634d38 ET |
19 | #endif |
20 | ||
8c7c5fa5 CMN |
21 | static int git_sysdir_guess_programdata_dirs(git_buf *out) |
22 | { | |
23 | #ifdef GIT_WIN32 | |
24 | return git_win32__find_programdata_dirs(out); | |
25 | #else | |
26 | git_buf_clear(out); | |
27 | return 0; | |
28 | #endif | |
29 | } | |
30 | ||
83634d38 ET |
31 | static int git_sysdir_guess_system_dirs(git_buf *out) |
32 | { | |
33 | #ifdef GIT_WIN32 | |
34 | return git_win32__find_system_dirs(out, L"etc\\"); | |
35 | #else | |
36 | return git_buf_sets(out, "/etc"); | |
37 | #endif | |
38 | } | |
39 | ||
eae0bfdc PP |
40 | #ifndef GIT_WIN32 |
41 | static int get_passwd_home(git_buf *out, uid_t uid) | |
42 | { | |
43 | struct passwd pwd, *pwdptr; | |
44 | char *buf = NULL; | |
45 | long buflen; | |
46 | int error; | |
47 | ||
c25aa7cd | 48 | GIT_ASSERT_ARG(out); |
eae0bfdc PP |
49 | |
50 | if ((buflen = sysconf(_SC_GETPW_R_SIZE_MAX)) == -1) | |
51 | buflen = 1024; | |
52 | ||
53 | do { | |
54 | buf = git__realloc(buf, buflen); | |
55 | error = getpwuid_r(uid, &pwd, buf, buflen, &pwdptr); | |
56 | buflen *= 2; | |
57 | } while (error == ERANGE && buflen <= 8192); | |
58 | ||
59 | if (error) { | |
ac3d33df | 60 | git_error_set(GIT_ERROR_OS, "failed to get passwd entry"); |
eae0bfdc PP |
61 | goto out; |
62 | } | |
63 | ||
64 | if (!pwdptr) { | |
ac3d33df | 65 | git_error_set(GIT_ERROR_OS, "no passwd entry found for user"); |
eae0bfdc PP |
66 | goto out; |
67 | } | |
68 | ||
69 | if ((error = git_buf_puts(out, pwdptr->pw_dir)) < 0) | |
70 | goto out; | |
71 | ||
72 | out: | |
73 | git__free(buf); | |
74 | return error; | |
75 | } | |
76 | #endif | |
77 | ||
83634d38 ET |
78 | static int git_sysdir_guess_global_dirs(git_buf *out) |
79 | { | |
80 | #ifdef GIT_WIN32 | |
81 | return git_win32__find_global_dirs(out); | |
82 | #else | |
eae0bfdc PP |
83 | int error; |
84 | uid_t uid, euid; | |
22a2d3d5 | 85 | const char *sandbox_id; |
eae0bfdc PP |
86 | |
87 | uid = getuid(); | |
88 | euid = geteuid(); | |
89 | ||
22a2d3d5 UG |
90 | /** |
91 | * If APP_SANDBOX_CONTAINER_ID is set, we are running in a | |
92 | * sandboxed environment on macOS. | |
93 | */ | |
94 | sandbox_id = getenv("APP_SANDBOX_CONTAINER_ID"); | |
95 | ||
eae0bfdc PP |
96 | /* |
97 | * In case we are running setuid, use the configuration | |
98 | * of the effective user. | |
22a2d3d5 UG |
99 | * |
100 | * If we are running in a sandboxed environment on macOS, | |
101 | * we have to get the HOME dir from the password entry file. | |
eae0bfdc | 102 | */ |
22a2d3d5 | 103 | if (!sandbox_id && uid == euid) |
eae0bfdc PP |
104 | error = git__getenv(out, "HOME"); |
105 | else | |
106 | error = get_passwd_home(out, euid); | |
e069c621 ET |
107 | |
108 | if (error == GIT_ENOTFOUND) { | |
ac3d33df | 109 | git_error_clear(); |
e069c621 ET |
110 | error = 0; |
111 | } | |
112 | ||
113 | return error; | |
83634d38 ET |
114 | #endif |
115 | } | |
116 | ||
117 | static int git_sysdir_guess_xdg_dirs(git_buf *out) | |
118 | { | |
119 | #ifdef GIT_WIN32 | |
120 | return git_win32__find_xdg_dirs(out); | |
121 | #else | |
e069c621 ET |
122 | git_buf env = GIT_BUF_INIT; |
123 | int error; | |
eae0bfdc PP |
124 | uid_t uid, euid; |
125 | ||
126 | uid = getuid(); | |
127 | euid = geteuid(); | |
128 | ||
129 | /* | |
130 | * In case we are running setuid, only look up passwd | |
131 | * directory of the effective user. | |
132 | */ | |
133 | if (uid == euid) { | |
134 | if ((error = git__getenv(&env, "XDG_CONFIG_HOME")) == 0) | |
135 | error = git_buf_joinpath(out, env.ptr, "git"); | |
136 | ||
137 | if (error == GIT_ENOTFOUND && (error = git__getenv(&env, "HOME")) == 0) | |
138 | error = git_buf_joinpath(out, env.ptr, ".config/git"); | |
139 | } else { | |
140 | if ((error = get_passwd_home(&env, euid)) == 0) | |
141 | error = git_buf_joinpath(out, env.ptr, ".config/git"); | |
142 | } | |
83634d38 | 143 | |
e069c621 | 144 | if (error == GIT_ENOTFOUND) { |
ac3d33df | 145 | git_error_clear(); |
e069c621 ET |
146 | error = 0; |
147 | } | |
83634d38 | 148 | |
ac3d33df | 149 | git_buf_dispose(&env); |
e069c621 | 150 | return error; |
83634d38 ET |
151 | #endif |
152 | } | |
153 | ||
154 | static int git_sysdir_guess_template_dirs(git_buf *out) | |
155 | { | |
156 | #ifdef GIT_WIN32 | |
157 | return git_win32__find_system_dirs(out, L"share\\git-core\\templates"); | |
158 | #else | |
159 | return git_buf_sets(out, "/usr/share/git-core/templates"); | |
160 | #endif | |
161 | } | |
162 | ||
031d34b7 ET |
163 | struct git_sysdir__dir { |
164 | git_buf buf; | |
165 | int (*guess)(git_buf *out); | |
166 | }; | |
83634d38 | 167 | |
031d34b7 ET |
168 | static struct git_sysdir__dir git_sysdir__dirs[] = { |
169 | { GIT_BUF_INIT, git_sysdir_guess_system_dirs }, | |
170 | { GIT_BUF_INIT, git_sysdir_guess_global_dirs }, | |
171 | { GIT_BUF_INIT, git_sysdir_guess_xdg_dirs }, | |
172 | { GIT_BUF_INIT, git_sysdir_guess_programdata_dirs }, | |
173 | { GIT_BUF_INIT, git_sysdir_guess_template_dirs }, | |
83634d38 ET |
174 | }; |
175 | ||
031d34b7 ET |
176 | static void git_sysdir_global_shutdown(void) |
177 | { | |
178 | size_t i; | |
179 | ||
180 | for (i = 0; i < ARRAY_SIZE(git_sysdir__dirs); ++i) | |
ac3d33df | 181 | git_buf_dispose(&git_sysdir__dirs[i].buf); |
031d34b7 | 182 | } |
83634d38 ET |
183 | |
184 | int git_sysdir_global_init(void) | |
185 | { | |
031d34b7 | 186 | size_t i; |
83634d38 ET |
187 | int error = 0; |
188 | ||
031d34b7 ET |
189 | for (i = 0; !error && i < ARRAY_SIZE(git_sysdir__dirs); i++) |
190 | error = git_sysdir__dirs[i].guess(&git_sysdir__dirs[i].buf); | |
83634d38 | 191 | |
c25aa7cd | 192 | return git_runtime_shutdown_register(git_sysdir_global_shutdown); |
83634d38 ET |
193 | } |
194 | ||
195 | static int git_sysdir_check_selector(git_sysdir_t which) | |
196 | { | |
031d34b7 | 197 | if (which < ARRAY_SIZE(git_sysdir__dirs)) |
83634d38 ET |
198 | return 0; |
199 | ||
ac3d33df | 200 | git_error_set(GIT_ERROR_INVALID, "config directory selector out of range"); |
83634d38 ET |
201 | return -1; |
202 | } | |
203 | ||
204 | ||
205 | int git_sysdir_get(const git_buf **out, git_sysdir_t which) | |
206 | { | |
c25aa7cd | 207 | GIT_ASSERT_ARG(out); |
83634d38 ET |
208 | |
209 | *out = NULL; | |
210 | ||
ac3d33df | 211 | GIT_ERROR_CHECK_ERROR(git_sysdir_check_selector(which)); |
83634d38 | 212 | |
031d34b7 | 213 | *out = &git_sysdir__dirs[which].buf; |
83634d38 ET |
214 | return 0; |
215 | } | |
216 | ||
83634d38 ET |
217 | #define PATH_MAGIC "$PATH" |
218 | ||
219 | int git_sysdir_set(git_sysdir_t which, const char *search_path) | |
220 | { | |
221 | const char *expand_path = NULL; | |
222 | git_buf merge = GIT_BUF_INIT; | |
223 | ||
ac3d33df | 224 | GIT_ERROR_CHECK_ERROR(git_sysdir_check_selector(which)); |
83634d38 ET |
225 | |
226 | if (search_path != NULL) | |
227 | expand_path = strstr(search_path, PATH_MAGIC); | |
228 | ||
031d34b7 | 229 | /* reset the default if this path has been cleared */ |
9f09f290 | 230 | if (!search_path) |
031d34b7 | 231 | git_sysdir__dirs[which].guess(&git_sysdir__dirs[which].buf); |
83634d38 ET |
232 | |
233 | /* if $PATH is not referenced, then just set the path */ | |
031d34b7 ET |
234 | if (!expand_path) { |
235 | if (search_path) | |
236 | git_buf_sets(&git_sysdir__dirs[which].buf, search_path); | |
237 | ||
238 | goto done; | |
239 | } | |
83634d38 ET |
240 | |
241 | /* otherwise set to join(before $PATH, old value, after $PATH) */ | |
242 | if (expand_path > search_path) | |
243 | git_buf_set(&merge, search_path, expand_path - search_path); | |
244 | ||
031d34b7 | 245 | if (git_buf_len(&git_sysdir__dirs[which].buf)) |
83634d38 | 246 | git_buf_join(&merge, GIT_PATH_LIST_SEPARATOR, |
031d34b7 | 247 | merge.ptr, git_sysdir__dirs[which].buf.ptr); |
83634d38 ET |
248 | |
249 | expand_path += strlen(PATH_MAGIC); | |
250 | if (*expand_path) | |
251 | git_buf_join(&merge, GIT_PATH_LIST_SEPARATOR, merge.ptr, expand_path); | |
252 | ||
031d34b7 | 253 | git_buf_swap(&git_sysdir__dirs[which].buf, &merge); |
ac3d33df | 254 | git_buf_dispose(&merge); |
83634d38 | 255 | |
031d34b7 ET |
256 | done: |
257 | if (git_buf_oom(&git_sysdir__dirs[which].buf)) | |
258 | return -1; | |
259 | ||
260 | return 0; | |
83634d38 ET |
261 | } |
262 | ||
263 | static int git_sysdir_find_in_dirlist( | |
264 | git_buf *path, | |
265 | const char *name, | |
266 | git_sysdir_t which, | |
267 | const char *label) | |
268 | { | |
269 | size_t len; | |
270 | const char *scan, *next = NULL; | |
271 | const git_buf *syspath; | |
272 | ||
ac3d33df | 273 | GIT_ERROR_CHECK_ERROR(git_sysdir_get(&syspath, which)); |
0f603132 RB |
274 | if (!syspath || !git_buf_len(syspath)) |
275 | goto done; | |
83634d38 ET |
276 | |
277 | for (scan = git_buf_cstr(syspath); scan; scan = next) { | |
0f603132 RB |
278 | /* find unescaped separator or end of string */ |
279 | for (next = scan; *next; ++next) { | |
280 | if (*next == GIT_PATH_LIST_SEPARATOR && | |
281 | (next <= scan || next[-1] != '\\')) | |
282 | break; | |
283 | } | |
83634d38 | 284 | |
0f603132 RB |
285 | len = (size_t)(next - scan); |
286 | next = (*next ? next + 1 : NULL); | |
83634d38 ET |
287 | if (!len) |
288 | continue; | |
289 | ||
ac3d33df | 290 | GIT_ERROR_CHECK_ERROR(git_buf_set(path, scan, len)); |
83634d38 | 291 | if (name) |
ac3d33df | 292 | GIT_ERROR_CHECK_ERROR(git_buf_joinpath(path, path->ptr, name)); |
83634d38 ET |
293 | |
294 | if (git_path_exists(path->ptr)) | |
295 | return 0; | |
296 | } | |
297 | ||
0f603132 | 298 | done: |
22a2d3d5 UG |
299 | if (name) |
300 | git_error_set(GIT_ERROR_OS, "the %s file '%s' doesn't exist", label, name); | |
301 | else | |
302 | git_error_set(GIT_ERROR_OS, "the %s directory doesn't exist", label); | |
ac3d33df | 303 | git_buf_dispose(path); |
83634d38 ET |
304 | return GIT_ENOTFOUND; |
305 | } | |
306 | ||
307 | int git_sysdir_find_system_file(git_buf *path, const char *filename) | |
308 | { | |
309 | return git_sysdir_find_in_dirlist( | |
310 | path, filename, GIT_SYSDIR_SYSTEM, "system"); | |
311 | } | |
312 | ||
313 | int git_sysdir_find_global_file(git_buf *path, const char *filename) | |
314 | { | |
315 | return git_sysdir_find_in_dirlist( | |
316 | path, filename, GIT_SYSDIR_GLOBAL, "global"); | |
317 | } | |
318 | ||
319 | int git_sysdir_find_xdg_file(git_buf *path, const char *filename) | |
320 | { | |
321 | return git_sysdir_find_in_dirlist( | |
322 | path, filename, GIT_SYSDIR_XDG, "global/xdg"); | |
323 | } | |
324 | ||
8c7c5fa5 CMN |
325 | int git_sysdir_find_programdata_file(git_buf *path, const char *filename) |
326 | { | |
327 | return git_sysdir_find_in_dirlist( | |
328 | path, filename, GIT_SYSDIR_PROGRAMDATA, "ProgramData"); | |
329 | } | |
330 | ||
83634d38 ET |
331 | int git_sysdir_find_template_dir(git_buf *path) |
332 | { | |
333 | return git_sysdir_find_in_dirlist( | |
334 | path, NULL, GIT_SYSDIR_TEMPLATE, "template"); | |
335 | } | |
336 | ||
5135ddaa ET |
337 | int git_sysdir_expand_global_file(git_buf *path, const char *filename) |
338 | { | |
339 | int error; | |
340 | ||
341 | if ((error = git_sysdir_find_global_file(path, NULL)) == 0) { | |
342 | if (filename) | |
343 | error = git_buf_joinpath(path, path->ptr, filename); | |
344 | } | |
345 | ||
346 | return error; | |
347 | } |