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>
33 #include "got_error.h"
34 #include "got_object.h"
36 #include "got_repository.h"
38 #include "got_lib_delta.h"
39 #include "got_lib_object.h"
40 #include "got_lib_privsep.h"
42 #include "gotconfig.h"
45 static volatile sig_atomic_t sigint_received
;
48 catch_sigint(int signo
)
53 static const struct got_error
*
54 make_fetch_url(char **url
, struct gotconfig_remote_repo
*repo
)
56 const struct got_error
*err
= NULL
;
57 char *s
= NULL
, *p
= NULL
;
58 const char *protocol
, *server
, *repo_path
;
63 if (repo
->fetch_config
&& repo
->fetch_config
->protocol
)
64 protocol
= repo
->fetch_config
->protocol
;
66 protocol
= repo
->protocol
;
68 return got_error_fmt(GOT_ERR_PARSE_CONFIG
,
69 "fetch protocol required for remote repository \"%s\"",
71 if (asprintf(&s
, "%s://", protocol
) == -1)
72 return got_error_from_errno("asprintf");
74 if (repo
->fetch_config
&& repo
->fetch_config
->server
)
75 server
= repo
->fetch_config
->server
;
77 server
= repo
->server
;
79 return got_error_fmt(GOT_ERR_PARSE_CONFIG
,
80 "fetch server required for remote repository \"%s\"",
84 if (asprintf(&s
, "%s%s", p
, server
) == -1) {
85 err
= got_error_from_errno("asprintf");
91 if (repo
->fetch_config
&& repo
->fetch_config
->server
)
92 port
= repo
->fetch_config
->port
;
98 if (asprintf(&s
, "%s:%d", p
, repo
->port
) == -1) {
99 err
= got_error_from_errno("asprintf");
106 if (repo
->fetch_config
&& repo
->fetch_config
->repository
)
107 repo_path
= repo
->fetch_config
->repository
;
109 repo_path
= repo
->repository
;
110 if (repo_path
== NULL
)
111 return got_error_fmt(GOT_ERR_PARSE_CONFIG
,
112 "fetch repository path required for remote "
113 "repository \"%s\"", repo
->name
);
115 while (repo_path
[0] == '/')
119 if (asprintf(&s
, "%s/%s", p
, repo_path
) == -1) {
120 err
= got_error_from_errno("asprintf");
126 got_path_strip_trailing_slashes(s
);
136 static const struct got_error
*
137 make_send_url(char **url
, struct gotconfig_remote_repo
*repo
)
139 const struct got_error
*err
= NULL
;
140 char *s
= NULL
, *p
= NULL
;
141 const char *protocol
, *server
, *repo_path
;
146 if (repo
->send_config
&& repo
->send_config
->protocol
)
147 protocol
= repo
->send_config
->protocol
;
149 protocol
= repo
->protocol
;
150 if (protocol
== NULL
)
151 return got_error_fmt(GOT_ERR_PARSE_CONFIG
,
152 "send protocol required for remote repository \"%s\"",
154 if (asprintf(&s
, "%s://", protocol
) == -1)
155 return got_error_from_errno("asprintf");
157 if (repo
->send_config
&& repo
->send_config
->server
)
158 server
= repo
->send_config
->server
;
160 server
= repo
->server
;
162 return got_error_fmt(GOT_ERR_PARSE_CONFIG
,
163 "send server required for remote repository \"%s\"",
167 if (asprintf(&s
, "%s%s", p
, server
) == -1) {
168 err
= got_error_from_errno("asprintf");
174 if (repo
->send_config
&& repo
->send_config
->server
)
175 port
= repo
->send_config
->port
;
181 if (asprintf(&s
, "%s:%d", p
, repo
->port
) == -1) {
182 err
= got_error_from_errno("asprintf");
189 if (repo
->send_config
&& repo
->send_config
->repository
)
190 repo_path
= repo
->send_config
->repository
;
192 repo_path
= repo
->repository
;
193 if (repo_path
== NULL
)
194 return got_error_fmt(GOT_ERR_PARSE_CONFIG
,
195 "send repository path required for remote "
196 "repository \"%s\"", repo
->name
);
198 while (repo_path
[0] == '/')
202 if (asprintf(&s
, "%s/%s", p
, repo_path
) == -1) {
203 err
= got_error_from_errno("asprintf");
209 got_path_strip_trailing_slashes(s
);
219 static const struct got_error
*
220 send_gotconfig_str(struct imsgbuf
*ibuf
, const char *value
)
222 size_t len
= value
? strlen(value
) : 0;
224 if (imsg_compose(ibuf
, GOT_IMSG_GOTCONFIG_STR_VAL
, 0, 0, -1,
226 return got_error_from_errno("imsg_compose GOTCONFIG_STR_VAL");
228 return got_privsep_flush_imsg(ibuf
);
231 static const struct got_error
*
232 send_gotconfig_remotes(struct imsgbuf
*ibuf
,
233 struct gotconfig_remote_repo_list
*remotes
, int nremotes
)
235 const struct got_error
*err
= NULL
;
236 struct got_imsg_remotes iremotes
;
237 struct gotconfig_remote_repo
*repo
;
238 char *fetch_url
= NULL
, *send_url
= NULL
;
240 iremotes
.nremotes
= nremotes
;
241 if (imsg_compose(ibuf
, GOT_IMSG_GOTCONFIG_REMOTES
, 0, 0, -1,
242 &iremotes
, sizeof(iremotes
)) == -1)
243 return got_error_from_errno("imsg_compose GOTCONFIG_REMOTES");
245 err
= got_privsep_flush_imsg(ibuf
);
250 TAILQ_FOREACH(repo
, remotes
, entry
) {
251 struct got_imsg_remote iremote
;
252 size_t len
= sizeof(iremote
);
254 struct node_branch
*branch
;
255 struct node_ref
*ref
;
256 int nfetch_branches
= 0, nsend_branches
= 0, nfetch_refs
= 0;
258 if (repo
->fetch_config
&& repo
->fetch_config
->branch
)
259 branch
= repo
->fetch_config
->branch
;
261 branch
= repo
->branch
;
263 branch
= branch
->next
;
267 if (repo
->send_config
&& repo
->send_config
->branch
)
268 branch
= repo
->send_config
->branch
;
270 branch
= repo
->branch
;
272 branch
= branch
->next
;
276 ref
= repo
->fetch_ref
;
282 iremote
.nfetch_branches
= nfetch_branches
;
283 iremote
.nsend_branches
= nsend_branches
;
284 iremote
.nfetch_refs
= nfetch_refs
;
285 iremote
.mirror_references
= repo
->mirror_references
;
286 iremote
.fetch_all_branches
= repo
->fetch_all_branches
;
288 iremote
.name_len
= strlen(repo
->name
);
289 len
+= iremote
.name_len
;
291 err
= make_fetch_url(&fetch_url
, repo
);
294 iremote
.fetch_url_len
= strlen(fetch_url
);
295 len
+= iremote
.fetch_url_len
;
297 err
= make_send_url(&send_url
, repo
);
300 iremote
.send_url_len
= strlen(send_url
);
301 len
+= iremote
.send_url_len
;
303 wbuf
= imsg_create(ibuf
, GOT_IMSG_GOTCONFIG_REMOTE
, 0, 0, len
);
305 err
= got_error_from_errno(
306 "imsg_create GOTCONFIG_REMOTE");
310 if (imsg_add(wbuf
, &iremote
, sizeof(iremote
)) == -1) {
311 err
= got_error_from_errno(
312 "imsg_add GOTCONFIG_REMOTE");
316 if (imsg_add(wbuf
, repo
->name
, iremote
.name_len
) == -1) {
317 err
= got_error_from_errno(
318 "imsg_add GOTCONFIG_REMOTE");
321 if (imsg_add(wbuf
, fetch_url
, iremote
.fetch_url_len
) == -1) {
322 err
= got_error_from_errno(
323 "imsg_add GOTCONFIG_REMOTE");
326 if (imsg_add(wbuf
, send_url
, iremote
.send_url_len
) == -1) {
327 err
= got_error_from_errno(
328 "imsg_add GOTCONFIG_REMOTE");
332 imsg_close(ibuf
, wbuf
);
333 err
= got_privsep_flush_imsg(ibuf
);
342 if (repo
->fetch_config
&& repo
->fetch_config
->branch
)
343 branch
= repo
->fetch_config
->branch
;
345 branch
= repo
->branch
;
347 err
= send_gotconfig_str(ibuf
, branch
->branch_name
);
350 branch
= branch
->next
;
353 if (repo
->send_config
&& repo
->send_config
->branch
)
354 branch
= repo
->send_config
->branch
;
356 branch
= repo
->branch
;
358 err
= send_gotconfig_str(ibuf
, branch
->branch_name
);
361 branch
= branch
->next
;
364 ref
= repo
->fetch_ref
;
366 err
= send_gotconfig_str(ibuf
, ref
->ref_name
);
378 static const struct got_error
*
379 validate_protocol(const char *protocol
, const char *repo_name
)
381 static char msg
[512];
383 if (strcmp(protocol
, "ssh") != 0 &&
384 strcmp(protocol
, "git+ssh") != 0 &&
385 strcmp(protocol
, "git") != 0 &&
386 strcmp(protocol
, "git+http") != 0 &&
387 strcmp(protocol
, "http") != 0 &&
388 strcmp(protocol
, "https") != 0 &&
389 strcmp(protocol
, "git+https") != 0) {
390 snprintf(msg
, sizeof(msg
),"unknown protocol \"%s\" "
391 "for remote repository \"%s\"", protocol
, repo_name
);
392 return got_error_msg(GOT_ERR_PARSE_CONFIG
, msg
);
398 static const struct got_error
*
399 validate_config(struct gotconfig
*gotconfig
)
401 const struct got_error
*err
;
402 struct gotconfig_remote_repo
*repo
, *repo2
;
403 static char msg
[512];
405 TAILQ_FOREACH(repo
, &gotconfig
->remotes
, entry
) {
406 if (repo
->name
== NULL
) {
407 return got_error_msg(GOT_ERR_PARSE_CONFIG
,
408 "name required for remote repository");
411 TAILQ_FOREACH(repo2
, &gotconfig
->remotes
, entry
) {
413 strcmp(repo
->name
, repo2
->name
) != 0)
415 snprintf(msg
, sizeof(msg
),
416 "duplicate remote repository name '%s'",
418 return got_error_msg(GOT_ERR_PARSE_CONFIG
, msg
);
421 if (repo
->server
== NULL
&&
422 (repo
->fetch_config
== NULL
||
423 repo
->fetch_config
->server
== NULL
) &&
424 (repo
->send_config
== NULL
||
425 repo
->send_config
->server
== NULL
)) {
426 snprintf(msg
, sizeof(msg
),
427 "server required for remote repository \"%s\"",
429 return got_error_msg(GOT_ERR_PARSE_CONFIG
, msg
);
432 if (repo
->protocol
== NULL
&&
433 (repo
->fetch_config
== NULL
||
434 repo
->fetch_config
->protocol
== NULL
) &&
435 (repo
->send_config
== NULL
||
436 repo
->send_config
->protocol
== NULL
)) {
437 snprintf(msg
, sizeof(msg
),
438 "protocol required for remote repository \"%s\"",
440 return got_error_msg(GOT_ERR_PARSE_CONFIG
, msg
);
443 if (repo
->protocol
) {
444 err
= validate_protocol(repo
->protocol
, repo
->name
);
448 if (repo
->fetch_config
&& repo
->fetch_config
->protocol
) {
449 err
= validate_protocol(repo
->fetch_config
->protocol
,
454 if (repo
->send_config
&& repo
->send_config
->protocol
) {
455 err
= validate_protocol(repo
->send_config
->protocol
,
461 if (repo
->repository
== NULL
&&
462 (repo
->fetch_config
== NULL
||
463 repo
->fetch_config
->repository
== NULL
) &&
464 (repo
->send_config
== NULL
||
465 repo
->send_config
->repository
== NULL
)) {
466 snprintf(msg
, sizeof(msg
),
467 "repository path required for remote "
468 "repository \"%s\"", repo
->name
);
469 return got_error_msg(GOT_ERR_PARSE_CONFIG
, msg
);
477 main(int argc
, char *argv
[])
479 const struct got_error
*err
= NULL
;
481 struct gotconfig
*gotconfig
= NULL
;
483 const char *filename
= "got.conf";
490 signal(SIGINT
, catch_sigint
);
492 imsg_init(&ibuf
, GOT_IMSG_FD_CHILD
);
495 /* revoke access to most system calls */
496 if (pledge("stdio recvfd", NULL
) == -1) {
497 err
= got_error_from_errno("pledge");
498 got_privsep_send_error(&ibuf
, err
);
502 /* revoke fs access */
503 if (landlock_no_fs() == -1) {
504 err
= got_error_from_errno("landlock_no_fs");
505 got_privsep_send_error(&ibuf
, err
);
508 if (cap_enter() == -1) {
509 err
= got_error_from_errno("cap_enter");
510 got_privsep_send_error(&ibuf
, err
);
522 memset(&imsg
, 0, sizeof(imsg
));
524 if (sigint_received
) {
525 err
= got_error(GOT_ERR_CANCELLED
);
529 err
= got_privsep_recv_imsg(&imsg
, &ibuf
, 0);
531 if (err
->code
== GOT_ERR_PRIVSEP_PIPE
)
536 if (imsg
.hdr
.type
== GOT_IMSG_STOP
)
539 switch (imsg
.hdr
.type
) {
540 case GOT_IMSG_GOTCONFIG_PARSE_REQUEST
:
541 datalen
= imsg
.hdr
.len
- IMSG_HEADER_SIZE
;
543 err
= got_error(GOT_ERR_PRIVSEP_LEN
);
546 fd
= imsg_get_fd(&imsg
);
548 err
= got_error(GOT_ERR_PRIVSEP_NO_FD
);
553 gotconfig_free(gotconfig
);
554 err
= gotconfig_parse(&gotconfig
, filename
, &fd
);
557 err
= validate_config(gotconfig
);
559 case GOT_IMSG_GOTCONFIG_AUTHOR_REQUEST
:
560 if (gotconfig
== NULL
) {
561 err
= got_error(GOT_ERR_PRIVSEP_MSG
);
564 err
= send_gotconfig_str(&ibuf
,
565 gotconfig
->author
? gotconfig
->author
: "");
567 case GOT_IMSG_GOTCONFIG_ALLOWEDSIGNERS_REQUEST
:
568 if (gotconfig
== NULL
) {
569 err
= got_error(GOT_ERR_PRIVSEP_MSG
);
572 err
= send_gotconfig_str(&ibuf
,
573 gotconfig
->allowed_signers_file
?
574 gotconfig
->allowed_signers_file
: "");
576 case GOT_IMSG_GOTCONFIG_REVOKEDSIGNERS_REQUEST
:
577 if (gotconfig
== NULL
) {
578 err
= got_error(GOT_ERR_PRIVSEP_MSG
);
581 err
= send_gotconfig_str(&ibuf
,
582 gotconfig
->revoked_signers_file
?
583 gotconfig
->revoked_signers_file
: "");
585 case GOT_IMSG_GOTCONFIG_SIGNERID_REQUEST
:
586 if (gotconfig
== NULL
) {
587 err
= got_error(GOT_ERR_PRIVSEP_MSG
);
590 err
= send_gotconfig_str(&ibuf
,
591 gotconfig
->signer_id
? gotconfig
->signer_id
: "");
593 case GOT_IMSG_GOTCONFIG_REMOTES_REQUEST
:
594 if (gotconfig
== NULL
) {
595 err
= got_error(GOT_ERR_PRIVSEP_MSG
);
598 err
= send_gotconfig_remotes(&ibuf
,
599 &gotconfig
->remotes
, gotconfig
->nremotes
);
602 err
= got_error(GOT_ERR_PRIVSEP_MSG
);
607 if (close(fd
) == -1 && err
== NULL
)
608 err
= got_error_from_errno("close");
618 if (!sigint_received
&& err
->code
!= GOT_ERR_PRIVSEP_PIPE
) {
619 fprintf(stderr
, "%s: %s\n", getprogname(), err
->msg
);
620 got_privsep_send_error(&ibuf
, err
);
623 if (close(GOT_IMSG_FD_CHILD
) == -1 && err
== NULL
)
624 err
= got_error_from_errno("close");