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");
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 snprintf(msg
, sizeof(msg
),"unknown protocol \"%s\" "
388 "for remote repository \"%s\"", protocol
, repo_name
);
389 return got_error_msg(GOT_ERR_PARSE_CONFIG
, msg
);
395 static const struct got_error
*
396 validate_config(struct gotconfig
*gotconfig
)
398 const struct got_error
*err
;
399 struct gotconfig_remote_repo
*repo
, *repo2
;
400 static char msg
[512];
402 TAILQ_FOREACH(repo
, &gotconfig
->remotes
, entry
) {
403 if (repo
->name
== NULL
) {
404 return got_error_msg(GOT_ERR_PARSE_CONFIG
,
405 "name required for remote repository");
408 TAILQ_FOREACH(repo2
, &gotconfig
->remotes
, entry
) {
410 strcmp(repo
->name
, repo2
->name
) != 0)
412 snprintf(msg
, sizeof(msg
),
413 "duplicate remote repository name '%s'",
415 return got_error_msg(GOT_ERR_PARSE_CONFIG
, msg
);
418 if (repo
->server
== NULL
&&
419 (repo
->fetch_config
== NULL
||
420 repo
->fetch_config
->server
== NULL
) &&
421 (repo
->send_config
== NULL
||
422 repo
->send_config
->server
== NULL
)) {
423 snprintf(msg
, sizeof(msg
),
424 "server required for remote repository \"%s\"",
426 return got_error_msg(GOT_ERR_PARSE_CONFIG
, msg
);
429 if (repo
->protocol
== NULL
&&
430 (repo
->fetch_config
== NULL
||
431 repo
->fetch_config
->protocol
== NULL
) &&
432 (repo
->send_config
== NULL
||
433 repo
->send_config
->protocol
== NULL
)) {
434 snprintf(msg
, sizeof(msg
),
435 "protocol required for remote repository \"%s\"",
437 return got_error_msg(GOT_ERR_PARSE_CONFIG
, msg
);
440 if (repo
->protocol
) {
441 err
= validate_protocol(repo
->protocol
, repo
->name
);
445 if (repo
->fetch_config
&& repo
->fetch_config
->protocol
) {
446 err
= validate_protocol(repo
->fetch_config
->protocol
,
451 if (repo
->send_config
&& repo
->send_config
->protocol
) {
452 err
= validate_protocol(repo
->send_config
->protocol
,
458 if (repo
->repository
== NULL
&&
459 (repo
->fetch_config
== NULL
||
460 repo
->fetch_config
->repository
== NULL
) &&
461 (repo
->send_config
== NULL
||
462 repo
->send_config
->repository
== NULL
)) {
463 snprintf(msg
, sizeof(msg
),
464 "repository path required for remote "
465 "repository \"%s\"", repo
->name
);
466 return got_error_msg(GOT_ERR_PARSE_CONFIG
, msg
);
474 main(int argc
, char *argv
[])
476 const struct got_error
*err
= NULL
;
478 struct gotconfig
*gotconfig
= NULL
;
480 const char *filename
= "got.conf";
487 signal(SIGINT
, catch_sigint
);
489 imsg_init(&ibuf
, GOT_IMSG_FD_CHILD
);
492 /* revoke access to most system calls */
493 if (pledge("stdio recvfd", NULL
) == -1) {
494 err
= got_error_from_errno("pledge");
495 got_privsep_send_error(&ibuf
, err
);
499 /* revoke fs access */
500 if (landlock_no_fs() == -1) {
501 err
= got_error_from_errno("landlock_no_fs");
502 got_privsep_send_error(&ibuf
, err
);
505 if (cap_enter() == -1) {
506 err
= got_error_from_errno("cap_enter");
507 got_privsep_send_error(&ibuf
, err
);
518 memset(&imsg
, 0, sizeof(imsg
));
521 if (sigint_received
) {
522 err
= got_error(GOT_ERR_CANCELLED
);
526 err
= got_privsep_recv_imsg(&imsg
, &ibuf
, 0);
528 if (err
->code
== GOT_ERR_PRIVSEP_PIPE
)
533 if (imsg
.hdr
.type
== GOT_IMSG_STOP
)
536 switch (imsg
.hdr
.type
) {
537 case GOT_IMSG_GOTCONFIG_PARSE_REQUEST
:
538 datalen
= imsg
.hdr
.len
- IMSG_HEADER_SIZE
;
540 err
= got_error(GOT_ERR_PRIVSEP_LEN
);
544 err
= got_error(GOT_ERR_PRIVSEP_NO_FD
);
549 gotconfig_free(gotconfig
);
550 err
= gotconfig_parse(&gotconfig
, filename
, &imsg
.fd
);
553 err
= validate_config(gotconfig
);
555 case GOT_IMSG_GOTCONFIG_AUTHOR_REQUEST
:
556 if (gotconfig
== NULL
) {
557 err
= got_error(GOT_ERR_PRIVSEP_MSG
);
560 err
= send_gotconfig_str(&ibuf
,
561 gotconfig
->author
? gotconfig
->author
: "");
563 case GOT_IMSG_GOTCONFIG_ALLOWEDSIGNERS_REQUEST
:
564 if (gotconfig
== NULL
) {
565 err
= got_error(GOT_ERR_PRIVSEP_MSG
);
568 err
= send_gotconfig_str(&ibuf
,
569 gotconfig
->allowed_signers_file
?
570 gotconfig
->allowed_signers_file
: "");
572 case GOT_IMSG_GOTCONFIG_REVOKEDSIGNERS_REQUEST
:
573 if (gotconfig
== NULL
) {
574 err
= got_error(GOT_ERR_PRIVSEP_MSG
);
577 err
= send_gotconfig_str(&ibuf
,
578 gotconfig
->revoked_signers_file
?
579 gotconfig
->revoked_signers_file
: "");
581 case GOT_IMSG_GOTCONFIG_SIGNERID_REQUEST
:
582 if (gotconfig
== NULL
) {
583 err
= got_error(GOT_ERR_PRIVSEP_MSG
);
586 err
= send_gotconfig_str(&ibuf
,
587 gotconfig
->signer_id
? gotconfig
->signer_id
: "");
589 case GOT_IMSG_GOTCONFIG_REMOTES_REQUEST
:
590 if (gotconfig
== NULL
) {
591 err
= got_error(GOT_ERR_PRIVSEP_MSG
);
594 err
= send_gotconfig_remotes(&ibuf
,
595 &gotconfig
->remotes
, gotconfig
->nremotes
);
598 err
= got_error(GOT_ERR_PRIVSEP_MSG
);
603 if (close(imsg
.fd
) == -1 && err
== NULL
)
604 err
= got_error_from_errno("close");
614 if (!sigint_received
&& err
->code
!= GOT_ERR_PRIVSEP_PIPE
) {
615 fprintf(stderr
, "%s: %s\n", getprogname(), err
->msg
);
616 got_privsep_send_error(&ibuf
, err
);
619 if (close(GOT_IMSG_FD_CHILD
) == -1 && err
== NULL
)
620 err
= got_error_from_errno("close");