2 * Copyright (c) 2020 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 "got_compat.h"
19 #include <sys/types.h>
20 #include <sys/queue.h>
34 #include "got_error.h"
35 #include "got_object.h"
37 #include "got_repository.h"
39 #include "got_lib_delta.h"
40 #include "got_lib_hash.h"
41 #include "got_lib_object.h"
42 #include "got_lib_privsep.h"
44 #include "gotconfig.h"
47 static volatile sig_atomic_t sigint_received
;
50 catch_sigint(int signo
)
55 static const struct got_error
*
56 make_fetch_url(char **url
, struct gotconfig_remote_repo
*repo
)
58 const struct got_error
*err
= NULL
;
59 char *s
= NULL
, *p
= NULL
;
60 const char *protocol
, *server
, *repo_path
;
65 if (repo
->fetch_config
&& repo
->fetch_config
->protocol
)
66 protocol
= repo
->fetch_config
->protocol
;
68 protocol
= repo
->protocol
;
70 return got_error_fmt(GOT_ERR_PARSE_CONFIG
,
71 "fetch protocol required for remote repository \"%s\"",
73 if (asprintf(&s
, "%s://", protocol
) == -1)
74 return got_error_from_errno("asprintf");
76 if (repo
->fetch_config
&& repo
->fetch_config
->server
)
77 server
= repo
->fetch_config
->server
;
79 server
= repo
->server
;
81 return got_error_fmt(GOT_ERR_PARSE_CONFIG
,
82 "fetch server required for remote repository \"%s\"",
86 if (asprintf(&s
, "%s%s", p
, server
) == -1) {
87 err
= got_error_from_errno("asprintf");
93 if (repo
->fetch_config
&& repo
->fetch_config
->server
)
94 port
= repo
->fetch_config
->port
;
100 if (asprintf(&s
, "%s:%d", p
, repo
->port
) == -1) {
101 err
= got_error_from_errno("asprintf");
108 if (repo
->fetch_config
&& repo
->fetch_config
->repository
)
109 repo_path
= repo
->fetch_config
->repository
;
111 repo_path
= repo
->repository
;
112 if (repo_path
== NULL
)
113 return got_error_fmt(GOT_ERR_PARSE_CONFIG
,
114 "fetch repository path required for remote "
115 "repository \"%s\"", repo
->name
);
117 while (repo_path
[0] == '/')
121 if (asprintf(&s
, "%s/%s", p
, repo_path
) == -1) {
122 err
= got_error_from_errno("asprintf");
128 got_path_strip_trailing_slashes(s
);
138 static const struct got_error
*
139 make_send_url(char **url
, struct gotconfig_remote_repo
*repo
)
141 const struct got_error
*err
= NULL
;
142 char *s
= NULL
, *p
= NULL
;
143 const char *protocol
, *server
, *repo_path
;
148 if (repo
->send_config
&& repo
->send_config
->protocol
)
149 protocol
= repo
->send_config
->protocol
;
151 protocol
= repo
->protocol
;
152 if (protocol
== NULL
)
153 return got_error_fmt(GOT_ERR_PARSE_CONFIG
,
154 "send protocol required for remote repository \"%s\"",
156 if (asprintf(&s
, "%s://", protocol
) == -1)
157 return got_error_from_errno("asprintf");
159 if (repo
->send_config
&& repo
->send_config
->server
)
160 server
= repo
->send_config
->server
;
162 server
= repo
->server
;
164 return got_error_fmt(GOT_ERR_PARSE_CONFIG
,
165 "send server required for remote repository \"%s\"",
169 if (asprintf(&s
, "%s%s", p
, server
) == -1) {
170 err
= got_error_from_errno("asprintf");
176 if (repo
->send_config
&& repo
->send_config
->server
)
177 port
= repo
->send_config
->port
;
183 if (asprintf(&s
, "%s:%d", p
, repo
->port
) == -1) {
184 err
= got_error_from_errno("asprintf");
191 if (repo
->send_config
&& repo
->send_config
->repository
)
192 repo_path
= repo
->send_config
->repository
;
194 repo_path
= repo
->repository
;
195 if (repo_path
== NULL
)
196 return got_error_fmt(GOT_ERR_PARSE_CONFIG
,
197 "send repository path required for remote "
198 "repository \"%s\"", repo
->name
);
200 while (repo_path
[0] == '/')
204 if (asprintf(&s
, "%s/%s", p
, repo_path
) == -1) {
205 err
= got_error_from_errno("asprintf");
211 got_path_strip_trailing_slashes(s
);
221 static const struct got_error
*
222 send_gotconfig_str(struct imsgbuf
*ibuf
, const char *value
)
224 size_t len
= value
? strlen(value
) : 0;
226 if (imsg_compose(ibuf
, GOT_IMSG_GOTCONFIG_STR_VAL
, 0, 0, -1,
228 return got_error_from_errno("imsg_compose GOTCONFIG_STR_VAL");
230 return got_privsep_flush_imsg(ibuf
);
233 static const struct got_error
*
234 send_gotconfig_remotes(struct imsgbuf
*ibuf
,
235 struct gotconfig_remote_repo_list
*remotes
, int nremotes
)
237 const struct got_error
*err
= NULL
;
238 struct got_imsg_remotes iremotes
;
239 struct gotconfig_remote_repo
*repo
;
240 char *fetch_url
= NULL
, *send_url
= NULL
;
242 iremotes
.nremotes
= nremotes
;
243 if (imsg_compose(ibuf
, GOT_IMSG_GOTCONFIG_REMOTES
, 0, 0, -1,
244 &iremotes
, sizeof(iremotes
)) == -1)
245 return got_error_from_errno("imsg_compose GOTCONFIG_REMOTES");
247 err
= got_privsep_flush_imsg(ibuf
);
251 TAILQ_FOREACH(repo
, remotes
, entry
) {
252 struct got_imsg_remote iremote
;
253 size_t len
= sizeof(iremote
);
255 struct node_branch
*branch
;
256 struct node_ref
*ref
;
257 int nfetch_branches
= 0, nsend_branches
= 0, nfetch_refs
= 0;
259 if (repo
->fetch_config
&& repo
->fetch_config
->branch
)
260 branch
= repo
->fetch_config
->branch
;
262 branch
= repo
->branch
;
264 branch
= branch
->next
;
268 if (repo
->send_config
&& repo
->send_config
->branch
)
269 branch
= repo
->send_config
->branch
;
271 branch
= repo
->branch
;
273 branch
= branch
->next
;
277 ref
= repo
->fetch_ref
;
283 iremote
.nfetch_branches
= nfetch_branches
;
284 iremote
.nsend_branches
= nsend_branches
;
285 iremote
.nfetch_refs
= nfetch_refs
;
286 iremote
.mirror_references
= repo
->mirror_references
;
287 iremote
.fetch_all_branches
= repo
->fetch_all_branches
;
289 iremote
.name_len
= strlen(repo
->name
);
290 len
+= iremote
.name_len
;
292 err
= make_fetch_url(&fetch_url
, repo
);
295 iremote
.fetch_url_len
= strlen(fetch_url
);
296 len
+= iremote
.fetch_url_len
;
298 err
= make_send_url(&send_url
, repo
);
301 iremote
.send_url_len
= strlen(send_url
);
302 len
+= iremote
.send_url_len
;
304 wbuf
= imsg_create(ibuf
, GOT_IMSG_GOTCONFIG_REMOTE
, 0, 0, len
);
306 err
= got_error_from_errno(
307 "imsg_create GOTCONFIG_REMOTE");
311 if (imsg_add(wbuf
, &iremote
, sizeof(iremote
)) == -1) {
312 err
= got_error_from_errno(
313 "imsg_add GOTCONFIG_REMOTE");
317 if (imsg_add(wbuf
, repo
->name
, iremote
.name_len
) == -1) {
318 err
= got_error_from_errno(
319 "imsg_add GOTCONFIG_REMOTE");
322 if (imsg_add(wbuf
, fetch_url
, iremote
.fetch_url_len
) == -1) {
323 err
= got_error_from_errno(
324 "imsg_add GOTCONFIG_REMOTE");
327 if (imsg_add(wbuf
, send_url
, iremote
.send_url_len
) == -1) {
328 err
= got_error_from_errno(
329 "imsg_add GOTCONFIG_REMOTE");
333 imsg_close(ibuf
, wbuf
);
334 err
= got_privsep_flush_imsg(ibuf
);
343 if (repo
->fetch_config
&& repo
->fetch_config
->branch
)
344 branch
= repo
->fetch_config
->branch
;
346 branch
= repo
->branch
;
348 err
= send_gotconfig_str(ibuf
, branch
->branch_name
);
351 branch
= branch
->next
;
354 if (repo
->send_config
&& repo
->send_config
->branch
)
355 branch
= repo
->send_config
->branch
;
357 branch
= repo
->branch
;
359 err
= send_gotconfig_str(ibuf
, branch
->branch_name
);
362 branch
= branch
->next
;
365 ref
= repo
->fetch_ref
;
367 err
= send_gotconfig_str(ibuf
, ref
->ref_name
);
379 static const struct got_error
*
380 validate_protocol(const char *protocol
, const char *repo_name
)
382 static char msg
[512];
384 if (strcmp(protocol
, "ssh") != 0 &&
385 strcmp(protocol
, "git+ssh") != 0 &&
386 strcmp(protocol
, "git") != 0 &&
387 strcmp(protocol
, "git+http") != 0 &&
388 strcmp(protocol
, "http") != 0 &&
389 strcmp(protocol
, "https") != 0 &&
390 strcmp(protocol
, "git+https") != 0) {
391 snprintf(msg
, sizeof(msg
),"unknown protocol \"%s\" "
392 "for remote repository \"%s\"", protocol
, repo_name
);
393 return got_error_msg(GOT_ERR_PARSE_CONFIG
, msg
);
399 static const struct got_error
*
400 validate_config(struct gotconfig
*gotconfig
)
402 const struct got_error
*err
;
403 struct gotconfig_remote_repo
*repo
, *repo2
;
404 static char msg
[512];
406 TAILQ_FOREACH(repo
, &gotconfig
->remotes
, entry
) {
407 if (repo
->name
== NULL
) {
408 return got_error_msg(GOT_ERR_PARSE_CONFIG
,
409 "name required for remote repository");
412 TAILQ_FOREACH(repo2
, &gotconfig
->remotes
, entry
) {
414 strcmp(repo
->name
, repo2
->name
) != 0)
416 snprintf(msg
, sizeof(msg
),
417 "duplicate remote repository name '%s'",
419 return got_error_msg(GOT_ERR_PARSE_CONFIG
, msg
);
422 if (repo
->server
== NULL
&&
423 (repo
->fetch_config
== NULL
||
424 repo
->fetch_config
->server
== NULL
) &&
425 (repo
->send_config
== NULL
||
426 repo
->send_config
->server
== NULL
)) {
427 snprintf(msg
, sizeof(msg
),
428 "server required for remote repository \"%s\"",
430 return got_error_msg(GOT_ERR_PARSE_CONFIG
, msg
);
433 if (repo
->protocol
== NULL
&&
434 (repo
->fetch_config
== NULL
||
435 repo
->fetch_config
->protocol
== NULL
) &&
436 (repo
->send_config
== NULL
||
437 repo
->send_config
->protocol
== NULL
)) {
438 snprintf(msg
, sizeof(msg
),
439 "protocol required for remote repository \"%s\"",
441 return got_error_msg(GOT_ERR_PARSE_CONFIG
, msg
);
444 if (repo
->protocol
) {
445 err
= validate_protocol(repo
->protocol
, repo
->name
);
449 if (repo
->fetch_config
&& repo
->fetch_config
->protocol
) {
450 err
= validate_protocol(repo
->fetch_config
->protocol
,
455 if (repo
->send_config
&& repo
->send_config
->protocol
) {
456 err
= validate_protocol(repo
->send_config
->protocol
,
462 if (repo
->repository
== NULL
&&
463 (repo
->fetch_config
== NULL
||
464 repo
->fetch_config
->repository
== NULL
) &&
465 (repo
->send_config
== NULL
||
466 repo
->send_config
->repository
== NULL
)) {
467 snprintf(msg
, sizeof(msg
),
468 "repository path required for remote "
469 "repository \"%s\"", repo
->name
);
470 return got_error_msg(GOT_ERR_PARSE_CONFIG
, msg
);
478 main(int argc
, char *argv
[])
480 const struct got_error
*err
= NULL
;
482 struct gotconfig
*gotconfig
= NULL
;
484 const char *filename
= "got.conf";
491 signal(SIGINT
, catch_sigint
);
493 if (imsgbuf_init(&ibuf
, GOT_IMSG_FD_CHILD
) == -1) {
494 warn("imsgbuf_init");
497 imsgbuf_allow_fdpass(&ibuf
);
500 /* revoke access to most system calls */
501 if (pledge("stdio recvfd", NULL
) == -1) {
502 err
= got_error_from_errno("pledge");
503 got_privsep_send_error(&ibuf
, err
);
507 /* revoke fs access */
508 if (landlock_no_fs() == -1) {
509 err
= got_error_from_errno("landlock_no_fs");
510 got_privsep_send_error(&ibuf
, err
);
513 if (cap_enter() == -1) {
514 err
= got_error_from_errno("cap_enter");
515 got_privsep_send_error(&ibuf
, err
);
527 memset(&imsg
, 0, sizeof(imsg
));
529 if (sigint_received
) {
530 err
= got_error(GOT_ERR_CANCELLED
);
534 err
= got_privsep_recv_imsg(&imsg
, &ibuf
, 0);
536 if (err
->code
== GOT_ERR_PRIVSEP_PIPE
)
541 if (imsg
.hdr
.type
== GOT_IMSG_STOP
)
544 switch (imsg
.hdr
.type
) {
545 case GOT_IMSG_GOTCONFIG_PARSE_REQUEST
:
546 datalen
= imsg
.hdr
.len
- IMSG_HEADER_SIZE
;
548 err
= got_error(GOT_ERR_PRIVSEP_LEN
);
551 fd
= imsg_get_fd(&imsg
);
553 err
= got_error(GOT_ERR_PRIVSEP_NO_FD
);
558 gotconfig_free(gotconfig
);
559 err
= gotconfig_parse(&gotconfig
, filename
, &fd
);
562 err
= validate_config(gotconfig
);
564 case GOT_IMSG_GOTCONFIG_AUTHOR_REQUEST
:
565 if (gotconfig
== NULL
) {
566 err
= got_error(GOT_ERR_PRIVSEP_MSG
);
569 err
= send_gotconfig_str(&ibuf
,
570 gotconfig
->author
? gotconfig
->author
: "");
572 case GOT_IMSG_GOTCONFIG_ALLOWEDSIGNERS_REQUEST
:
573 if (gotconfig
== NULL
) {
574 err
= got_error(GOT_ERR_PRIVSEP_MSG
);
577 err
= send_gotconfig_str(&ibuf
,
578 gotconfig
->allowed_signers_file
?
579 gotconfig
->allowed_signers_file
: "");
581 case GOT_IMSG_GOTCONFIG_REVOKEDSIGNERS_REQUEST
:
582 if (gotconfig
== NULL
) {
583 err
= got_error(GOT_ERR_PRIVSEP_MSG
);
586 err
= send_gotconfig_str(&ibuf
,
587 gotconfig
->revoked_signers_file
?
588 gotconfig
->revoked_signers_file
: "");
590 case GOT_IMSG_GOTCONFIG_SIGNERID_REQUEST
:
591 if (gotconfig
== NULL
) {
592 err
= got_error(GOT_ERR_PRIVSEP_MSG
);
595 err
= send_gotconfig_str(&ibuf
,
596 gotconfig
->signer_id
? gotconfig
->signer_id
: "");
598 case GOT_IMSG_GOTCONFIG_REMOTES_REQUEST
:
599 if (gotconfig
== NULL
) {
600 err
= got_error(GOT_ERR_PRIVSEP_MSG
);
603 err
= send_gotconfig_remotes(&ibuf
,
604 &gotconfig
->remotes
, gotconfig
->nremotes
);
607 err
= got_error(GOT_ERR_PRIVSEP_MSG
);
612 if (close(fd
) == -1 && err
== NULL
)
613 err
= got_error_from_errno("close");
622 if (!sigint_received
&& err
->code
!= GOT_ERR_PRIVSEP_PIPE
) {
623 fprintf(stderr
, "%s: %s\n", getprogname(), err
->msg
);
624 got_privsep_send_error(&ibuf
, err
);
627 imsgbuf_clear(&ibuf
);
628 if (close(GOT_IMSG_FD_CHILD
) == -1 && err
== NULL
)
629 err
= got_error_from_errno("close");