]> git.proxmox.com Git - libgit2.git/blame - src/sysdir.c
Bump debhelper compatibility level to 13
[libgit2.git] / src / sysdir.c
CommitLineData
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
21static 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
31static 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
41static 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
72out:
73 git__free(buf);
74 return error;
75}
76#endif
77
83634d38
ET
78static 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
117static 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
154static 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
163struct git_sysdir__dir {
164 git_buf buf;
165 int (*guess)(git_buf *out);
166};
83634d38 167
031d34b7
ET
168static 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
176static 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
184int 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
195static 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
205int 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
219int 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
256done:
257 if (git_buf_oom(&git_sysdir__dirs[which].buf))
258 return -1;
259
260 return 0;
83634d38
ET
261}
262
263static 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 298done:
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
307int 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
313int 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
319int 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
325int 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
331int 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
337int 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}