2 * Copyright (c) 2019 Stefan Sperling <stsp@openbsd.org>
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 #include <sys/types.h>
18 #include <sys/queue.h>
32 #include "got_compat.h"
34 #include "got_error.h"
35 #include "got_object.h"
36 #include "got_repository.h"
38 #include "got_lib_delta.h"
39 #include "got_lib_hash.h"
40 #include "got_lib_object.h"
41 #include "got_lib_privsep.h"
42 #include "got_lib_gitconfig.h"
44 static volatile sig_atomic_t sigint_received
;
47 catch_sigint(int signo
)
52 static const struct got_error
*
53 send_gitconfig_int(struct imsgbuf
*ibuf
, int value
)
55 if (imsg_compose(ibuf
, GOT_IMSG_GITCONFIG_INT_VAL
, 0, 0, -1,
56 &value
, sizeof(value
)) == -1)
57 return got_error_from_errno("imsg_compose GITCONFIG_INT_VAL");
59 return got_privsep_flush_imsg(ibuf
);
62 static const struct got_error
*
63 gitconfig_num_request(struct imsgbuf
*ibuf
, struct got_gitconfig
*gitconfig
,
64 const char *section
, const char *tag
, int def
)
68 if (gitconfig
== NULL
)
69 return got_error(GOT_ERR_PRIVSEP_MSG
);
71 value
= got_gitconfig_get_num(gitconfig
, section
, tag
, def
);
72 return send_gitconfig_int(ibuf
, value
);
75 static const struct got_error
*
76 send_gitconfig_str(struct imsgbuf
*ibuf
, const char *value
)
78 size_t len
= value
? strlen(value
) : 0;
80 if (imsg_compose(ibuf
, GOT_IMSG_GITCONFIG_STR_VAL
, 0, 0, -1,
82 return got_error_from_errno("imsg_compose GITCONFIG_STR_VAL");
84 return got_privsep_flush_imsg(ibuf
);
87 static const struct got_error
*
88 send_gitconfig_pair(struct imsgbuf
*ibuf
, const char *key
, const char *val
)
91 size_t klen
= key
? strlen(key
) : 0;
92 size_t vlen
= val
? strlen(val
) : 0;
93 size_t tot
= sizeof(klen
) + sizeof(vlen
) + klen
+ vlen
;
95 if (tot
> MAX_IMSGSIZE
- IMSG_HEADER_SIZE
)
96 return got_error(GOT_ERR_NO_SPACE
);
98 wbuf
= imsg_create(ibuf
, GOT_IMSG_GITCONFIG_PAIR
, 0, 0, tot
);
100 return got_error_from_errno("imsg_create GITCONFIG_PAIR");
102 /* Keep in sync with got_imsg_gitconfig_pair */
103 if (imsg_add(wbuf
, &klen
, sizeof(klen
)) == -1)
104 return got_error_from_errno("imsg_add GITCONFIG_PAIR");
105 if (imsg_add(wbuf
, &vlen
, sizeof(vlen
)) == -1)
106 return got_error_from_errno("imsg_add GITCONFIG_PAIR");
107 if (imsg_add(wbuf
, key
, klen
) == -1)
108 return got_error_from_errno("imsg_add GITCONFIG_PAIR");
109 if (imsg_add(wbuf
, val
, vlen
) == -1)
110 return got_error_from_errno("imsg_add GITCONFIG_PAIR");
112 imsg_close(ibuf
, wbuf
);
113 return got_privsep_flush_imsg(ibuf
);
116 static const struct got_error
*
117 gitconfig_str_request(struct imsgbuf
*ibuf
, struct got_gitconfig
*gitconfig
,
118 const char *section
, const char *tag
)
122 if (gitconfig
== NULL
)
123 return got_error(GOT_ERR_PRIVSEP_MSG
);
125 value
= got_gitconfig_get_str(gitconfig
, section
, tag
);
126 return send_gitconfig_str(ibuf
, value
);
129 static const struct got_error
*
130 send_gitconfig_remotes(struct imsgbuf
*ibuf
, struct got_remote_repo
*remotes
,
133 const struct got_error
*err
= NULL
;
134 struct got_imsg_remotes iremotes
;
137 iremotes
.nremotes
= nremotes
;
138 if (imsg_compose(ibuf
, GOT_IMSG_GITCONFIG_REMOTES
, 0, 0, -1,
139 &iremotes
, sizeof(iremotes
)) == -1)
140 return got_error_from_errno("imsg_compose GITCONFIG_REMOTES");
142 err
= got_privsep_flush_imsg(ibuf
);
146 for (i
= 0; i
< nremotes
; i
++) {
147 struct got_imsg_remote iremote
;
148 size_t len
= sizeof(iremote
);
151 iremote
.mirror_references
= remotes
[i
].mirror_references
;
152 iremote
.name_len
= strlen(remotes
[i
].name
);
153 len
+= iremote
.name_len
;
154 iremote
.fetch_url_len
= strlen(remotes
[i
].fetch_url
);
155 len
+= iremote
.fetch_url_len
;
156 iremote
.send_url_len
= strlen(remotes
[i
].send_url
);
157 len
+= iremote
.send_url_len
;
159 wbuf
= imsg_create(ibuf
, GOT_IMSG_GITCONFIG_REMOTE
, 0, 0, len
);
161 return got_error_from_errno(
162 "imsg_create GITCONFIG_REMOTE");
164 if (imsg_add(wbuf
, &iremote
, sizeof(iremote
)) == -1)
165 return got_error_from_errno(
166 "imsg_add GITCONFIG_REMOTE");
168 if (imsg_add(wbuf
, remotes
[i
].name
, iremote
.name_len
) == -1)
169 return got_error_from_errno(
170 "imsg_add GITCONFIG_REMOTE");
171 if (imsg_add(wbuf
, remotes
[i
].fetch_url
, iremote
.fetch_url_len
) == -1)
172 return got_error_from_errno(
173 "imsg_add GITCONFIG_REMOTE");
174 if (imsg_add(wbuf
, remotes
[i
].send_url
, iremote
.send_url_len
) == -1)
175 return got_error_from_errno(
176 "imsg_add GITCONFIG_REMOTE");
178 imsg_close(ibuf
, wbuf
);
179 err
= got_privsep_flush_imsg(ibuf
);
188 get_boolean_val(char *val
)
190 return (strcasecmp(val
, "true") == 0 ||
191 strcasecmp(val
, "on") == 0 ||
192 strcasecmp(val
, "yes") == 0 ||
193 strcmp(val
, "1") == 0);
197 skip_node(struct got_gitconfig
*gitconfig
, struct got_gitconfig_list_node
*node
)
200 * Skip config nodes which do not describe remotes, and remotes
201 * which do not have a fetch URL defined (as used by git-annex).
203 return (strncasecmp("remote \"", node
->field
, 8) != 0 ||
204 got_gitconfig_get_str(gitconfig
, node
->field
, "url") == NULL
);
207 static const struct got_error
*
208 gitconfig_remotes_request(struct imsgbuf
*ibuf
, struct got_gitconfig
*gitconfig
)
210 const struct got_error
*err
= NULL
;
211 struct got_gitconfig_list
*sections
;
212 struct got_gitconfig_list_node
*node
;
213 struct got_remote_repo
*remotes
= NULL
;
216 if (gitconfig
== NULL
)
217 return got_error(GOT_ERR_PRIVSEP_MSG
);
219 err
= got_gitconfig_get_section_list(§ions
, gitconfig
);
223 TAILQ_FOREACH(node
, §ions
->fields
, link
) {
224 if (skip_node(gitconfig
, node
))
230 err
= send_gitconfig_remotes(ibuf
, NULL
, 0);
234 remotes
= recallocarray(NULL
, 0, nremotes
, sizeof(*remotes
));
235 if (remotes
== NULL
) {
236 err
= got_error_from_errno("recallocarray");
241 TAILQ_FOREACH(node
, §ions
->fields
, link
) {
242 char *name
, *end
, *mirror
;
244 if (skip_node(gitconfig
, node
))
247 name
= strdup(node
->field
+ 8);
249 err
= got_error_from_errno("strdup");
252 end
= strrchr(name
, '"');
255 remotes
[i
].name
= name
;
257 remotes
[i
].fetch_url
= got_gitconfig_get_str(gitconfig
,
260 remotes
[i
].send_url
= got_gitconfig_get_str(gitconfig
,
261 node
->field
, "pushurl");
262 if (remotes
[i
].send_url
== NULL
)
263 remotes
[i
].send_url
= remotes
[i
].fetch_url
;
265 remotes
[i
].mirror_references
= 0;
266 mirror
= got_gitconfig_get_str(gitconfig
, node
->field
,
268 if (mirror
!= NULL
&& get_boolean_val(mirror
))
269 remotes
[i
].mirror_references
= 1;
274 err
= send_gitconfig_remotes(ibuf
, remotes
, nremotes
);
276 for (i
= 0; i
< nremotes
; i
++)
277 free(remotes
[i
].name
);
279 got_gitconfig_free_list(sections
);
283 static const struct got_error
*
284 gitconfig_owner_request(struct imsgbuf
*ibuf
, struct got_gitconfig
*gitconfig
)
288 if (gitconfig
== NULL
)
289 return got_error(GOT_ERR_PRIVSEP_MSG
);
291 value
= got_gitconfig_get_str(gitconfig
, "gotweb", "owner");
293 return send_gitconfig_str(ibuf
, value
);
294 value
= got_gitconfig_get_str(gitconfig
, "gitweb", "owner");
295 return send_gitconfig_str(ibuf
, value
);
298 static const struct got_error
*
299 gitconfig_extensions_request(struct imsgbuf
*ibuf
,
300 struct got_gitconfig
*gitconfig
)
302 const struct got_error
*err
= NULL
;
303 struct got_gitconfig_list
*tags
;
304 struct got_gitconfig_list_node
*node
;
308 if (gitconfig
== NULL
)
309 return got_error(GOT_ERR_PRIVSEP_MSG
);
311 tags
= got_gitconfig_get_tag_list(gitconfig
, "extensions");
313 return send_gitconfig_int(ibuf
, 0);
315 TAILQ_FOREACH(node
, &tags
->fields
, link
)
318 err
= send_gitconfig_int(ibuf
, nextensions
);
322 TAILQ_FOREACH(node
, &tags
->fields
, link
) {
323 val
= got_gitconfig_get_str(gitconfig
, "extensions",
325 err
= send_gitconfig_pair(ibuf
, node
->field
, val
);
330 got_gitconfig_free_list(tags
);
335 main(int argc
, char *argv
[])
337 const struct got_error
*err
= NULL
;
340 struct got_gitconfig
*gitconfig
= NULL
;
347 signal(SIGINT
, catch_sigint
);
349 if (imsgbuf_init(&ibuf
, GOT_IMSG_FD_CHILD
) == -1) {
350 warn("imsgbuf_init");
353 imsgbuf_allow_fdpass(&ibuf
);
356 /* revoke access to most system calls */
357 if (pledge("stdio recvfd", NULL
) == -1) {
358 err
= got_error_from_errno("pledge");
359 got_privsep_send_error(&ibuf
, err
);
360 imsgbuf_clear(&ibuf
);
364 /* revoke fs access */
365 if (landlock_no_fs() == -1) {
366 err
= got_error_from_errno("landlock_no_fs");
367 got_privsep_send_error(&ibuf
, err
);
370 if (cap_enter() == -1) {
371 err
= got_error_from_errno("cap_enter");
372 got_privsep_send_error(&ibuf
, err
);
381 memset(&imsg
, 0, sizeof(imsg
));
383 if (sigint_received
) {
384 err
= got_error(GOT_ERR_CANCELLED
);
388 err
= got_privsep_recv_imsg(&imsg
, &ibuf
, 0);
390 if (err
->code
== GOT_ERR_PRIVSEP_PIPE
)
395 if (imsg
.hdr
.type
== GOT_IMSG_STOP
)
398 switch (imsg
.hdr
.type
) {
399 case GOT_IMSG_GITCONFIG_PARSE_REQUEST
:
400 datalen
= imsg
.hdr
.len
- IMSG_HEADER_SIZE
;
402 err
= got_error(GOT_ERR_PRIVSEP_LEN
);
405 fd
= imsg_get_fd(&imsg
);
407 err
= got_error(GOT_ERR_PRIVSEP_NO_FD
);
412 got_gitconfig_close(gitconfig
);
413 err
= got_gitconfig_open(&gitconfig
, fd
);
415 case GOT_IMSG_GITCONFIG_REPOSITORY_FORMAT_VERSION_REQUEST
:
416 err
= gitconfig_num_request(&ibuf
, gitconfig
, "core",
417 "repositoryformatversion", 0);
419 case GOT_IMSG_GITCONFIG_REPOSITORY_EXTENSIONS_REQUEST
:
420 err
= gitconfig_extensions_request(&ibuf
, gitconfig
);
422 case GOT_IMSG_GITCONFIG_AUTHOR_NAME_REQUEST
:
423 err
= gitconfig_str_request(&ibuf
, gitconfig
, "user",
426 case GOT_IMSG_GITCONFIG_AUTHOR_EMAIL_REQUEST
:
427 err
= gitconfig_str_request(&ibuf
, gitconfig
, "user",
430 case GOT_IMSG_GITCONFIG_REMOTES_REQUEST
:
431 err
= gitconfig_remotes_request(&ibuf
, gitconfig
);
433 case GOT_IMSG_GITCONFIG_OWNER_REQUEST
:
434 err
= gitconfig_owner_request(&ibuf
, gitconfig
);
437 err
= got_error(GOT_ERR_PRIVSEP_MSG
);
442 if (close(fd
) == -1 && err
== NULL
)
443 err
= got_error_from_errno("close");
452 if (!sigint_received
&& err
->code
!= GOT_ERR_PRIVSEP_PIPE
) {
453 fprintf(stderr
, "%s: %s\n", getprogname(), err
->msg
);
454 got_privsep_send_error(&ibuf
, err
);
458 got_gitconfig_close(gitconfig
);
459 imsgbuf_clear(&ibuf
);
460 if (close(GOT_IMSG_FD_CHILD
) == -1 && err
== NULL
)
461 err
= got_error_from_errno("close");