Blob


1 /*
2 * Copyright (c) 2022 Stefan Sperling <stsp@openbsd.org>
3 *
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.
7 *
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.
15 */
17 #include <sys/queue.h>
18 #include <sys/types.h>
20 #include <event.h>
21 #include <errno.h>
22 #include <imsg.h>
23 #include <signal.h>
24 #include <stdlib.h>
25 #include <limits.h>
26 #include <poll.h>
27 #include <sha1.h>
28 #include <siphash.h>
29 #include <stdio.h>
30 #include <string.h>
31 #include <unistd.h>
33 #include "got_error.h"
34 #include "got_cancel.h"
35 #include "got_object.h"
36 #include "got_repository.h"
37 #include "got_reference.h"
38 #include "got_repository_admin.h"
40 #include "got_lib_delta.h"
41 #include "got_lib_object.h"
42 #include "got_lib_object_idset.h"
43 #include "got_lib_sha1.h"
44 #include "got_lib_pack.h"
45 #include "got_lib_ratelimit.h"
46 #include "got_lib_pack_create.h"
47 #include "got_lib_poll.h"
49 #include "log.h"
50 #include "gotd.h"
51 #include "repo_read.h"
53 #ifndef nitems
54 #define nitems(_a) (sizeof((_a)) / sizeof((_a)[0]))
55 #endif
57 static struct repo_read {
58 pid_t pid;
59 const char *title;
60 struct got_repository *repo;
61 int *pack_fds;
62 int *temp_fds;
63 } repo_read;
65 struct repo_read_client {
66 STAILQ_ENTRY(repo_read_client) entry;
67 uint32_t id;
68 int fd;
69 int delta_cache_fd;
70 int report_progress;
71 int pack_pipe[2];
72 struct gotd_object_id_array want_ids;
73 struct gotd_object_id_array have_ids;
74 };
75 STAILQ_HEAD(repo_read_clients, repo_read_client);
77 static struct repo_read_clients repo_read_clients[GOTD_CLIENT_TABLE_SIZE];
78 static SIPHASH_KEY clients_hash_key;
80 static uint64_t
81 client_hash(uint32_t client_id)
82 {
83 return SipHash24(&clients_hash_key, &client_id, sizeof(client_id));
84 }
86 static void
87 add_client(struct repo_read_client *client, uint32_t client_id, int fd)
88 {
89 uint64_t slot;
91 client->id = client_id;
92 client->fd = fd;
93 client->delta_cache_fd = -1;
94 client->pack_pipe[0] = -1;
95 client->pack_pipe[1] = -1;
96 slot = client_hash(client->id) % nitems(repo_read_clients);
97 STAILQ_INSERT_HEAD(&repo_read_clients[slot], client, entry);
98 }
100 static struct repo_read_client *
101 find_client(uint32_t client_id)
103 uint64_t slot;
104 struct repo_read_client *c;
106 slot = client_hash(client_id) % nitems(repo_read_clients);
107 STAILQ_FOREACH(c, &repo_read_clients[slot], entry) {
108 if (c->id == client_id)
109 return c;
112 return NULL;
115 static volatile sig_atomic_t sigint_received;
116 static volatile sig_atomic_t sigterm_received;
118 static void
119 catch_sigint(int signo)
121 sigint_received = 1;
124 static void
125 catch_sigterm(int signo)
127 sigterm_received = 1;
130 static const struct got_error *
131 check_cancelled(void *arg)
133 if (sigint_received || sigterm_received)
134 return got_error(GOT_ERR_CANCELLED);
136 return NULL;
139 static const struct got_error *
140 send_symref(struct got_reference *symref, struct imsgbuf *ibuf)
142 const struct got_error *err = NULL;
143 struct gotd_imsg_symref isymref;
144 const char *refname = got_ref_get_name(symref);
145 const char *target = got_ref_get_symref_target(symref);
146 size_t len;
147 struct ibuf *wbuf;
148 struct got_object_id *target_id;
150 err = got_ref_resolve(&target_id, repo_read.repo, symref);
151 if (err)
152 return err;
154 memset(&isymref, 0, sizeof(isymref));
155 isymref.name_len = strlen(refname);
156 isymref.target_len = strlen(target);
157 memcpy(isymref.target_id, target_id->sha1, sizeof(isymref.target_id));
159 len = sizeof(isymref) + isymref.name_len + isymref.target_len;
160 if (len > MAX_IMSGSIZE - IMSG_HEADER_SIZE) {
161 err = got_error(GOT_ERR_NO_SPACE);
162 goto done;
165 wbuf = imsg_create(ibuf, GOTD_IMSG_SYMREF, 0, 0, len);
166 if (wbuf == NULL) {
167 err = got_error_from_errno("imsg_create SYMREF");
168 goto done;
171 if (imsg_add(wbuf, &isymref, sizeof(isymref)) == -1) {
172 err = got_error_from_errno("imsg_add SYMREF");
173 goto done;
175 if (imsg_add(wbuf, refname, isymref.name_len) == -1) {
176 err = got_error_from_errno("imsg_add SYMREF");
177 goto done;
179 if (imsg_add(wbuf, target, isymref.target_len) == -1) {
180 err = got_error_from_errno("imsg_add SYMREF");
181 goto done;
184 wbuf->fd = -1;
185 imsg_close(ibuf, wbuf);
186 done:
187 free(target_id);
188 return err;
191 static const struct got_error *
192 send_peeled_tag_ref(struct got_reference *ref, struct got_object *obj,
193 struct imsgbuf *ibuf)
195 const struct got_error *err = NULL;
196 struct got_tag_object *tag;
197 size_t namelen, len;
198 char *peeled_refname = NULL;
199 struct got_object_id *id;
200 struct ibuf *wbuf;
202 err = got_object_tag_open(&tag, repo_read.repo, obj);
203 if (err)
204 return err;
206 if (asprintf(&peeled_refname, "%s^{}", got_ref_get_name(ref)) == -1) {
207 err = got_error_from_errno("asprintf");
208 goto done;
211 id = got_object_tag_get_object_id(tag);
212 namelen = strlen(peeled_refname);
214 len = sizeof(struct gotd_imsg_ref) + namelen;
215 if (len > MAX_IMSGSIZE - IMSG_HEADER_SIZE) {
216 err = got_error(GOT_ERR_NO_SPACE);
217 goto done;
220 wbuf = imsg_create(ibuf, GOTD_IMSG_REF, PROC_REPO_READ,
221 repo_read.pid, len);
222 if (wbuf == NULL) {
223 err = got_error_from_errno("imsg_create MREF");
224 goto done;
227 /* Keep in sync with struct gotd_imsg_ref definition. */
228 if (imsg_add(wbuf, id->sha1, SHA1_DIGEST_LENGTH) == -1) {
229 err = got_error_from_errno("imsg_add REF");
230 goto done;
232 if (imsg_add(wbuf, &namelen, sizeof(namelen)) == -1) {
233 err = got_error_from_errno("imsg_add REF");
234 goto done;
236 if (imsg_add(wbuf, peeled_refname, namelen) == -1) {
237 err = got_error_from_errno("imsg_add REF");
238 goto done;
241 wbuf->fd = -1;
242 imsg_close(ibuf, wbuf);
243 done:
244 got_object_tag_close(tag);
245 return err;
248 static const struct got_error *
249 send_ref(struct got_reference *ref, struct imsgbuf *ibuf)
251 const struct got_error *err;
252 const char *refname = got_ref_get_name(ref);
253 size_t namelen;
254 struct got_object_id *id = NULL;
255 struct got_object *obj = NULL;
256 size_t len;
257 struct ibuf *wbuf;
259 namelen = strlen(refname);
261 len = sizeof(struct gotd_imsg_ref) + namelen;
262 if (len > MAX_IMSGSIZE - IMSG_HEADER_SIZE)
263 return got_error(GOT_ERR_NO_SPACE);
265 err = got_ref_resolve(&id, repo_read.repo, ref);
266 if (err)
267 return err;
269 wbuf = imsg_create(ibuf, GOTD_IMSG_REF, PROC_REPO_READ,
270 repo_read.pid, len);
271 if (wbuf == NULL) {
272 err = got_error_from_errno("imsg_create REF");
273 goto done;
276 /* Keep in sync with struct gotd_imsg_ref definition. */
277 if (imsg_add(wbuf, id->sha1, SHA1_DIGEST_LENGTH) == -1)
278 return got_error_from_errno("imsg_add REF");
279 if (imsg_add(wbuf, &namelen, sizeof(namelen)) == -1)
280 return got_error_from_errno("imsg_add REF");
281 if (imsg_add(wbuf, refname, namelen) == -1)
282 return got_error_from_errno("imsg_add REF");
284 wbuf->fd = -1;
285 imsg_close(ibuf, wbuf);
287 err = got_object_open(&obj, repo_read.repo, id);
288 if (err)
289 goto done;
290 if (obj->type == GOT_OBJ_TYPE_TAG)
291 err = send_peeled_tag_ref(ref, obj, ibuf);
292 done:
293 if (obj)
294 got_object_close(obj);
295 free(id);
296 return err;
299 static const struct got_error *
300 list_refs(struct repo_read_client **client, struct imsg *imsg)
302 const struct got_error *err;
303 struct got_reflist_head refs;
304 struct got_reflist_entry *re;
305 struct gotd_imsg_list_refs_internal ireq;
306 size_t datalen;
307 struct gotd_imsg_reflist irefs;
308 struct imsgbuf ibuf;
309 int client_fd = imsg->fd;
311 TAILQ_INIT(&refs);
313 if (client_fd == -1)
314 return got_error(GOT_ERR_PRIVSEP_NO_FD);
316 datalen = imsg->hdr.len - IMSG_HEADER_SIZE;
317 if (datalen != sizeof(ireq))
318 return got_error(GOT_ERR_PRIVSEP_LEN);
319 memcpy(&ireq, imsg->data, sizeof(ireq));
321 *client = find_client(ireq.client_id);
322 if (*client)
323 return got_error_msg(GOT_ERR_CLIENT_ID, "duplicate client ID");
325 *client = calloc(1, sizeof(**client));
326 if (*client == NULL)
327 return got_error_from_errno("calloc");
328 add_client(*client, ireq.client_id, client_fd);
330 imsg_init(&ibuf, client_fd);
332 err = got_ref_list(&refs, repo_read.repo, "",
333 got_ref_cmp_by_name, NULL);
334 if (err)
335 return err;
337 memset(&irefs, 0, sizeof(irefs));
338 TAILQ_FOREACH(re, &refs, entry) {
339 struct got_object_id *id;
340 int obj_type;
342 if (got_ref_is_symbolic(re->ref)) {
343 const char *refname = got_ref_get_name(re->ref);
344 if (strcmp(refname, GOT_REF_HEAD) == 0)
345 irefs.nrefs++;
346 continue;
349 irefs.nrefs++;
351 /* Account for a peeled tag refs. */
352 err = got_ref_resolve(&id, repo_read.repo, re->ref);
353 if (err)
354 goto done;
355 err = got_object_get_type(&obj_type, repo_read.repo, id);
356 free(id);
357 if (err)
358 goto done;
359 if (obj_type == GOT_OBJ_TYPE_TAG)
360 irefs.nrefs++;
363 if (imsg_compose(&ibuf, GOTD_IMSG_REFLIST, PROC_REPO_READ,
364 repo_read.pid, -1, &irefs, sizeof(irefs)) == -1) {
365 err = got_error_from_errno("imsg_compose REFLIST");
366 goto done;
369 /*
370 * Send the HEAD symref first. In Git-protocol versions < 2
371 * the HEAD symref must be announced on the initial line of
372 * the server's ref advertisement.
373 * For now, we do not advertise symrefs other than HEAD.
374 */
375 TAILQ_FOREACH(re, &refs, entry) {
376 if (!got_ref_is_symbolic(re->ref) ||
377 strcmp(got_ref_get_name(re->ref), GOT_REF_HEAD) != 0)
378 continue;
379 err = send_symref(re->ref, &ibuf);
380 if (err)
381 goto done;
382 break;
384 TAILQ_FOREACH(re, &refs, entry) {
385 if (got_ref_is_symbolic(re->ref))
386 continue;
387 err = send_ref(re->ref, &ibuf);
388 if (err)
389 goto done;
392 err = gotd_imsg_flush(&ibuf);
393 done:
394 got_ref_list_free(&refs);
395 imsg_clear(&ibuf);
396 return err;
399 static const struct got_error *
400 record_object_id(struct gotd_object_id_array *array, struct got_object_id *id)
402 const size_t alloc_chunksz = 256;
404 if (array->ids == NULL) {
405 array->ids = reallocarray(NULL, alloc_chunksz,
406 sizeof(*array->ids));
407 if (array->ids == NULL)
408 return got_error_from_errno("reallocarray");
409 array->nalloc = alloc_chunksz;
410 array->nids = 0;
411 } else if (array->nalloc <= array->nids) {
412 struct got_object_id **new;
413 new = recallocarray(array->ids, array->nalloc,
414 array->nalloc + alloc_chunksz, sizeof(*new));
415 if (new == NULL)
416 return got_error_from_errno("recallocarray");
417 array->ids = new;
418 array->nalloc += alloc_chunksz;
421 array->ids[array->nids] = got_object_id_dup(id);
422 if (array->ids[array->nids] == NULL)
423 return got_error_from_errno("got_object_id_dup");
424 array->nids++;
425 return NULL;
428 static void
429 free_object_ids(struct gotd_object_id_array *array)
431 size_t i;
433 for (i = 0; i < array->nids; i++)
434 free(array->ids[i]);
435 free(array->ids);
437 array->ids = NULL;
438 array->nalloc = 0;
439 array->nids = 0;
442 static const struct got_error *
443 recv_want(struct repo_read_client **client, struct imsg *imsg)
445 const struct got_error *err;
446 struct gotd_imsg_want iwant;
447 size_t datalen;
448 char hex[SHA1_DIGEST_STRING_LENGTH];
449 struct got_object_id id;
450 int obj_type;
451 struct imsgbuf ibuf;
453 datalen = imsg->hdr.len - IMSG_HEADER_SIZE;
454 if (datalen != sizeof(iwant))
455 return got_error(GOT_ERR_PRIVSEP_LEN);
456 memcpy(&iwant, imsg->data, sizeof(iwant));
458 memset(&id, 0, sizeof(id));
459 memcpy(id.sha1, iwant.object_id, SHA1_DIGEST_LENGTH);
461 if (log_getverbose() > 0 &&
462 got_sha1_digest_to_str(id.sha1, hex, sizeof(hex)))
463 log_debug("client wants %s", hex);
465 *client = find_client(iwant.client_id);
466 if (*client == NULL)
467 return got_error(GOT_ERR_CLIENT_ID);
469 imsg_init(&ibuf, (*client)->fd);
471 err = got_object_get_type(&obj_type, repo_read.repo, &id);
472 if (err)
473 return err;
475 if (obj_type != GOT_OBJ_TYPE_COMMIT &&
476 obj_type != GOT_OBJ_TYPE_TAG)
477 return got_error(GOT_ERR_OBJ_TYPE);
479 err = record_object_id(&(*client)->want_ids, &id);
480 if (err)
481 return err;
483 gotd_imsg_send_ack(&id, &ibuf, PROC_REPO_READ, repo_read.pid);
484 imsg_clear(&ibuf);
485 return err;
488 static const struct got_error *
489 recv_have(struct repo_read_client **client, struct imsg *imsg)
491 const struct got_error *err;
492 struct gotd_imsg_have ihave;
493 size_t datalen;
494 char hex[SHA1_DIGEST_STRING_LENGTH];
495 struct got_object_id id;
496 int obj_type;
497 struct imsgbuf ibuf;
499 datalen = imsg->hdr.len - IMSG_HEADER_SIZE;
500 if (datalen != sizeof(ihave))
501 return got_error(GOT_ERR_PRIVSEP_LEN);
502 memcpy(&ihave, imsg->data, sizeof(ihave));
504 memset(&id, 0, sizeof(id));
505 memcpy(id.sha1, ihave.object_id, SHA1_DIGEST_LENGTH);
507 if (log_getverbose() > 0 &&
508 got_sha1_digest_to_str(id.sha1, hex, sizeof(hex)))
509 log_debug("client has %s", hex);
511 *client = find_client(ihave.client_id);
512 if (*client == NULL)
513 return got_error(GOT_ERR_CLIENT_ID);
515 imsg_init(&ibuf, (*client)->fd);
517 err = got_object_get_type(&obj_type, repo_read.repo, &id);
518 if (err) {
519 if (err->code == GOT_ERR_NO_OBJ) {
520 gotd_imsg_send_nak(&id, &ibuf,
521 PROC_REPO_READ, repo_read.pid);
522 err = NULL;
524 goto done;
527 if (obj_type != GOT_OBJ_TYPE_COMMIT &&
528 obj_type != GOT_OBJ_TYPE_TAG) {
529 gotd_imsg_send_nak(&id, &ibuf, PROC_REPO_READ, repo_read.pid);
530 err = got_error(GOT_ERR_OBJ_TYPE);
531 goto done;
534 err = record_object_id(&(*client)->have_ids, &id);
535 if (err)
536 return err;
538 gotd_imsg_send_ack(&id, &ibuf, PROC_REPO_READ, repo_read.pid);
539 done:
540 imsg_clear(&ibuf);
541 return err;
544 struct repo_read_pack_progress_arg {
545 int report_progress;
546 struct imsgbuf *ibuf;
547 int sent_ready;
548 };
550 static const struct got_error *
551 pack_progress(void *arg, int ncolored, int nfound, int ntrees,
552 off_t packfile_size, int ncommits, int nobj_total, int nobj_deltify,
553 int nobj_written)
555 struct repo_read_pack_progress_arg *a = arg;
556 struct gotd_imsg_packfile_progress iprog;
557 int ret;
559 if (!a->report_progress)
560 return NULL;
561 if (packfile_size > 0 && a->sent_ready)
562 return NULL;
564 memset(&iprog, 0, sizeof(iprog));
565 iprog.ncolored = ncolored;
566 iprog.nfound = nfound;
567 iprog.ntrees = ntrees;
568 iprog.packfile_size = packfile_size;
569 iprog.ncommits = ncommits;
570 iprog.nobj_total = nobj_total;
571 iprog.nobj_deltify = nobj_deltify;
572 iprog.nobj_written = nobj_written;
574 /* Using synchronous writes since we are blocking the event loop. */
575 if (packfile_size == 0) {
576 ret = imsg_compose(a->ibuf, GOTD_IMSG_PACKFILE_PROGRESS,
577 PROC_REPO_READ, repo_read.pid, -1, &iprog, sizeof(iprog));
578 if (ret == -1) {
579 return got_error_from_errno("imsg compose "
580 "PACKFILE_PROGRESS");
582 } else {
583 a->sent_ready = 1;
584 ret = imsg_compose(a->ibuf, GOTD_IMSG_PACKFILE_READY,
585 PROC_REPO_READ, repo_read.pid, -1, &iprog, sizeof(iprog));
586 if (ret == -1) {
587 return got_error_from_errno("imsg compose "
588 "PACKFILE_READY");
592 return gotd_imsg_flush(a->ibuf);
595 static const struct got_error *
596 receive_delta_cache_fd(struct repo_read_client **client, struct imsg *imsg,
597 struct gotd_imsgev *iev)
599 struct gotd_imsg_send_packfile ireq;
600 size_t datalen;
602 log_debug("receving delta cache file");
604 if (imsg->fd == -1)
605 return got_error(GOT_ERR_PRIVSEP_NO_FD);
607 datalen = imsg->hdr.len - IMSG_HEADER_SIZE;
608 if (datalen != sizeof(ireq))
609 return got_error(GOT_ERR_PRIVSEP_LEN);
610 memcpy(&ireq, imsg->data, sizeof(ireq));
612 *client = find_client(ireq.client_id);
613 if (*client == NULL)
614 return got_error(GOT_ERR_CLIENT_ID);
616 if ((*client)->delta_cache_fd != -1)
617 return got_error(GOT_ERR_PRIVSEP_MSG);
619 (*client)->delta_cache_fd = imsg->fd;
620 (*client)->report_progress = ireq.report_progress;
621 return NULL;
624 static const struct got_error *
625 receive_pack_pipe(struct repo_read_client **client, struct imsg *imsg,
626 struct gotd_imsgev *iev)
628 struct gotd_imsg_packfile_pipe ireq;
629 size_t datalen;
631 log_debug("receving pack pipe descriptor");
633 if (imsg->fd == -1)
634 return got_error(GOT_ERR_PRIVSEP_NO_FD);
636 datalen = imsg->hdr.len - IMSG_HEADER_SIZE;
637 if (datalen != sizeof(ireq))
638 return got_error(GOT_ERR_PRIVSEP_LEN);
639 memcpy(&ireq, imsg->data, sizeof(ireq));
641 *client = find_client(ireq.client_id);
642 if (*client == NULL)
643 return got_error(GOT_ERR_CLIENT_ID);
644 if ((*client)->pack_pipe[1] != -1)
645 return got_error(GOT_ERR_PRIVSEP_MSG);
647 if ((*client)->pack_pipe[0] == -1)
648 (*client)->pack_pipe[0] = imsg->fd;
649 else
650 (*client)->pack_pipe[1] = imsg->fd;
652 return NULL;
655 static const struct got_error *
656 send_packfile(struct repo_read_client *client, struct imsg *imsg,
657 struct gotd_imsgev *iev)
659 const struct got_error *err = NULL;
660 struct gotd_imsg_packfile_done idone;
661 uint8_t packsha1[SHA1_DIGEST_LENGTH];
662 char hex[SHA1_DIGEST_STRING_LENGTH];
663 FILE *delta_cache = NULL;
664 struct imsgbuf ibuf;
665 struct repo_read_pack_progress_arg pa;
666 struct got_ratelimit rl;
668 log_debug("packfile request received");
670 got_ratelimit_init(&rl, 2, 0);
672 if (client->delta_cache_fd == -1 ||
673 client->pack_pipe[0] == -1 ||
674 client->pack_pipe[1] == -1)
675 return got_error(GOT_ERR_PRIVSEP_NO_FD);
677 imsg_init(&ibuf, client->fd);
679 /* Send pack file pipe to gotsh(1). */
680 if (imsg_compose(&ibuf, GOTD_IMSG_PACKFILE_PIPE, PROC_REPO_READ,
681 repo_read.pid, client->pack_pipe[1], NULL, 0) == -1) {
682 err = got_error_from_errno("imsg_compose ACK");
683 if (err)
684 goto done;
686 client->pack_pipe[1] = -1;
687 err = gotd_imsg_flush(&ibuf);
688 if (err)
689 goto done;
691 delta_cache = fdopen(client->delta_cache_fd, "w+");
692 if (delta_cache == NULL) {
693 err = got_error_from_errno("fdopen");
694 goto done;
696 client->delta_cache_fd = -1;
698 memset(&pa, 0, sizeof(pa));
699 pa.ibuf = &ibuf;
700 pa.report_progress = client->report_progress;
702 err = got_pack_create(packsha1, client->pack_pipe[0], delta_cache,
703 client->have_ids.ids, client->have_ids.nids,
704 client->want_ids.ids, client->want_ids.nids,
705 repo_read.repo, 0, 1, pack_progress, &pa, &rl,
706 check_cancelled, NULL);
707 if (err)
708 goto done;
710 if (log_getverbose() > 0 &&
711 got_sha1_digest_to_str(packsha1, hex, sizeof(hex)))
712 log_debug("sent pack-%s.pack", hex);
714 memset(&idone, 0, sizeof(idone));
715 idone.client_id = client->id;
716 if (gotd_imsg_compose_event(iev, GOTD_IMSG_PACKFILE_DONE,
717 PROC_REPO_READ, -1, &idone, sizeof(idone)) == -1)
718 err = got_error_from_errno("imsg compose PACKFILE_DONE");
719 done:
720 if (delta_cache != NULL && fclose(delta_cache) == EOF && err == NULL)
721 err = got_error_from_errno("fclose");
722 imsg_clear(&ibuf);
723 return err;
726 static const struct got_error *
727 recv_disconnect(struct imsg *imsg)
729 const struct got_error *err = NULL;
730 struct gotd_imsg_disconnect idisconnect;
731 size_t datalen;
732 int client_fd, delta_cache_fd, pipe[2];
733 struct repo_read_client *client = NULL;
734 uint64_t slot;
736 datalen = imsg->hdr.len - IMSG_HEADER_SIZE;
737 if (datalen != sizeof(idisconnect))
738 return got_error(GOT_ERR_PRIVSEP_LEN);
739 memcpy(&idisconnect, imsg->data, sizeof(idisconnect));
741 log_debug("client disconnecting");
743 client = find_client(idisconnect.client_id);
744 if (client == NULL)
745 return got_error(GOT_ERR_CLIENT_ID);
747 slot = client_hash(client->id) % nitems(repo_read_clients);
748 STAILQ_REMOVE(&repo_read_clients[slot], client, repo_read_client,
749 entry);
750 free_object_ids(&client->have_ids);
751 free_object_ids(&client->want_ids);
752 client_fd = client->fd;
753 delta_cache_fd = client->delta_cache_fd;
754 pipe[0] = client->pack_pipe[0];
755 pipe[1] = client->pack_pipe[1];
756 free(client);
757 if (close(client_fd) == -1)
758 err = got_error_from_errno("close");
759 if (delta_cache_fd != -1 && close(delta_cache_fd) == -1 && err == NULL)
760 return got_error_from_errno("close");
761 if (pipe[0] != -1 && close(pipe[0]) == -1 && err == NULL)
762 return got_error_from_errno("close");
763 if (pipe[1] != -1 && close(pipe[1]) == -1 && err == NULL)
764 return got_error_from_errno("close");
765 return err;
768 static void
769 repo_read_dispatch(int fd, short event, void *arg)
771 const struct got_error *err = NULL;
772 struct gotd_imsgev *iev = arg;
773 struct imsgbuf *ibuf = &iev->ibuf;
774 struct imsg imsg;
775 ssize_t n;
776 int shut = 0;
777 struct repo_read_client *client = NULL;
779 if (event & EV_READ) {
780 if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN)
781 fatal("imsg_read error");
782 if (n == 0) /* Connection closed. */
783 shut = 1;
786 if (event & EV_WRITE) {
787 n = msgbuf_write(&ibuf->w);
788 if (n == -1 && errno != EAGAIN)
789 fatal("msgbuf_write");
790 if (n == 0) /* Connection closed. */
791 shut = 1;
794 while (err == NULL && check_cancelled(NULL) == NULL) {
795 client = NULL;
796 if ((n = imsg_get(ibuf, &imsg)) == -1)
797 fatal("%s: imsg_get", __func__);
798 if (n == 0) /* No more messages. */
799 break;
801 switch (imsg.hdr.type) {
802 case GOTD_IMSG_LIST_REFS_INTERNAL:
803 err = list_refs(&client, &imsg);
804 if (err)
805 log_warnx("%s: ls-refs: %s", repo_read.title,
806 err->msg);
807 break;
808 case GOTD_IMSG_WANT:
809 err = recv_want(&client, &imsg);
810 if (err)
811 log_warnx("%s: want-line: %s", repo_read.title,
812 err->msg);
813 break;
814 case GOTD_IMSG_HAVE:
815 err = recv_have(&client, &imsg);
816 if (err)
817 log_warnx("%s: have-line: %s", repo_read.title,
818 err->msg);
819 break;
820 case GOTD_IMSG_SEND_PACKFILE:
821 err = receive_delta_cache_fd(&client, &imsg, iev);
822 if (err)
823 log_warnx("%s: receiving delta cache: %s",
824 repo_read.title, err->msg);
825 break;
826 case GOTD_IMSG_PACKFILE_PIPE:
827 err = receive_pack_pipe(&client, &imsg, iev);
828 if (err) {
829 log_warnx("%s: receiving pack pipe: %s",
830 repo_read.title, err->msg);
831 break;
833 if (client->pack_pipe[1] == -1)
834 break;
835 err = send_packfile(client, &imsg, iev);
836 if (err)
837 log_warnx("%s: sending packfile: %s",
838 repo_read.title, err->msg);
839 break;
840 case GOTD_IMSG_DISCONNECT:
841 err = recv_disconnect(&imsg);
842 if (err)
843 log_warnx("%s: disconnect: %s",
844 repo_read.title, err->msg);
845 break;
846 default:
847 log_debug("%s: unexpected imsg %d", repo_read.title,
848 imsg.hdr.type);
849 break;
852 imsg_free(&imsg);
855 if (!shut && check_cancelled(NULL) == NULL) {
856 if (err &&
857 gotd_imsg_send_error_event(iev, PROC_REPO_READ,
858 client ? client->id : 0, err) == -1) {
859 log_warnx("could not send error to parent: %s",
860 err->msg);
862 gotd_imsg_event_add(iev);
863 } else {
864 /* This pipe is dead. Remove its event handler */
865 event_del(&iev->ev);
866 event_loopexit(NULL);
870 void
871 repo_read_main(const char *title, int *pack_fds, int *temp_fds)
873 const struct got_error *err = NULL;
874 struct gotd_imsgev iev;
876 repo_read.title = title;
877 repo_read.pid = getpid();
878 repo_read.pack_fds = pack_fds;
879 repo_read.temp_fds = temp_fds;
881 arc4random_buf(&clients_hash_key, sizeof(clients_hash_key));
883 /*
884 * Open a repository in the root directory.
885 * We are already in chroot at this point.
886 */
887 err = got_repo_open(&repo_read.repo, "/", NULL, pack_fds);
888 if (err)
889 goto done;
890 if (!got_repo_is_bare(repo_read.repo)) {
891 err = got_error_msg(GOT_ERR_NOT_GIT_REPO,
892 "bare git repository required");
893 goto done;
896 got_repo_temp_fds_set(repo_read.repo, temp_fds);
898 signal(SIGINT, catch_sigint);
899 signal(SIGTERM, catch_sigterm);
900 signal(SIGPIPE, SIG_IGN);
901 signal(SIGHUP, SIG_IGN);
903 imsg_init(&iev.ibuf, GOTD_SOCK_FILENO);
904 iev.handler = repo_read_dispatch;
905 iev.events = EV_READ;
906 iev.handler_arg = NULL;
907 event_set(&iev.ev, iev.ibuf.fd, EV_READ, repo_read_dispatch, &iev);
908 if (event_add(&iev.ev, NULL) == -1) {
909 err = got_error_from_errno("event_add");
910 goto done;
913 event_dispatch();
914 done:
915 if (err)
916 log_warnx("%s: %s", title, err->msg);
917 repo_read_shutdown();
920 void
921 repo_read_shutdown(void)
923 log_debug("%s: shutting down", repo_read.title);
924 if (repo_read.repo)
925 got_repo_close(repo_read.repo);
926 got_repo_pack_fds_close(repo_read.pack_fds);
927 got_repo_temp_fds_close(repo_read.temp_fds);
928 exit(0);