2 * Copyright (c) 2022, 2023 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>
21 #include <sys/socket.h>
36 #include "got_compat.h"
38 #include "got_error.h"
39 #include "got_repository.h"
40 #include "got_object.h"
42 #include "got_reference.h"
43 #include "got_opentemp.h"
45 #include "got_lib_hash.h"
46 #include "got_lib_delta.h"
47 #include "got_lib_object.h"
48 #include "got_lib_object_cache.h"
49 #include "got_lib_pack.h"
50 #include "got_lib_repository.h"
51 #include "got_lib_gitproto.h"
58 static struct gotd_session
{
61 struct got_repository
*repo
;
64 struct gotd_imsgev parent_iev
;
65 struct timeval request_timeout
;
66 enum gotd_procid proc_id
;
69 static struct gotd_session_client
{
70 enum gotd_session_state state
;
72 struct gotd_client_capability
*capabilities
;
78 struct gotd_imsgev iev
;
79 struct gotd_imsgev repo_child_iev
;
88 } gotd_session_client
;
90 void gotd_session_sighdlr(int sig
, short event
, void *arg
);
91 static void gotd_session_shutdown(void);
94 disconnect(struct gotd_session_client
*client
)
96 log_debug("uid %d: disconnecting", client
->euid
);
98 if (gotd_imsg_compose_event(&gotd_session
.parent_iev
,
99 GOTD_IMSG_DISCONNECT
, gotd_session
.proc_id
, -1, NULL
, 0) == -1)
100 log_warn("imsg compose DISCONNECT");
102 imsg_clear(&client
->repo_child_iev
.ibuf
);
103 event_del(&client
->repo_child_iev
.ev
);
104 evtimer_del(&client
->tmo
);
106 if (client
->delta_cache_fd
!= -1)
107 close(client
->delta_cache_fd
);
108 if (client
->packfile_path
) {
109 if (unlink(client
->packfile_path
) == -1 && errno
!= ENOENT
)
110 log_warn("unlink %s: ", client
->packfile_path
);
111 free(client
->packfile_path
);
113 if (client
->packidx_path
) {
114 if (unlink(client
->packidx_path
) == -1 && errno
!= ENOENT
)
115 log_warn("unlink %s: ", client
->packidx_path
);
116 free(client
->packidx_path
);
118 free(client
->capabilities
);
120 gotd_session_shutdown();
124 disconnect_on_error(struct gotd_session_client
*client
,
125 const struct got_error
*err
)
129 if (err
->code
!= GOT_ERR_EOF
) {
130 log_warnx("uid %d: %s", client
->euid
, err
->msg
);
131 imsg_init(&ibuf
, client
->fd
);
132 gotd_imsg_send_error(&ibuf
, 0, gotd_session
.proc_id
, err
);
140 gotd_request_timeout(int fd
, short events
, void *arg
)
142 struct gotd_session_client
*client
= arg
;
144 log_debug("disconnecting uid %d due to timeout", client
->euid
);
149 gotd_session_sighdlr(int sig
, short event
, void *arg
)
152 * Normal signal handler rules don't apply because libevent
158 log_info("%s: ignoring SIGHUP", __func__
);
161 log_info("%s: ignoring SIGUSR1", __func__
);
165 gotd_session_shutdown();
169 fatalx("unexpected signal");
173 static const struct got_error
*
174 recv_packfile_done(uint32_t *client_id
, struct imsg
*imsg
)
176 struct gotd_imsg_packfile_done idone
;
179 log_debug("packfile-done received");
181 datalen
= imsg
->hdr
.len
- IMSG_HEADER_SIZE
;
182 if (datalen
!= sizeof(idone
))
183 return got_error(GOT_ERR_PRIVSEP_LEN
);
184 memcpy(&idone
, imsg
->data
, sizeof(idone
));
186 *client_id
= idone
.client_id
;
190 static const struct got_error
*
191 recv_packfile_install(uint32_t *client_id
, struct imsg
*imsg
)
193 struct gotd_imsg_packfile_install inst
;
196 log_debug("packfile-install received");
198 datalen
= imsg
->hdr
.len
- IMSG_HEADER_SIZE
;
199 if (datalen
!= sizeof(inst
))
200 return got_error(GOT_ERR_PRIVSEP_LEN
);
201 memcpy(&inst
, imsg
->data
, sizeof(inst
));
203 *client_id
= inst
.client_id
;
207 static const struct got_error
*
208 recv_ref_updates_start(uint32_t *client_id
, struct imsg
*imsg
)
210 struct gotd_imsg_ref_updates_start istart
;
213 log_debug("ref-updates-start received");
215 datalen
= imsg
->hdr
.len
- IMSG_HEADER_SIZE
;
216 if (datalen
!= sizeof(istart
))
217 return got_error(GOT_ERR_PRIVSEP_LEN
);
218 memcpy(&istart
, imsg
->data
, sizeof(istart
));
220 *client_id
= istart
.client_id
;
224 static const struct got_error
*
225 recv_ref_update(uint32_t *client_id
, struct imsg
*imsg
)
227 struct gotd_imsg_ref_update iref
;
230 log_debug("ref-update received");
232 datalen
= imsg
->hdr
.len
- IMSG_HEADER_SIZE
;
233 if (datalen
< sizeof(iref
))
234 return got_error(GOT_ERR_PRIVSEP_LEN
);
235 memcpy(&iref
, imsg
->data
, sizeof(iref
));
237 *client_id
= iref
.client_id
;
241 static const struct got_error
*
242 send_ref_update_ok(struct gotd_session_client
*client
,
243 struct gotd_imsg_ref_update
*iref
, const char *refname
)
245 struct gotd_imsg_ref_update_ok iok
;
246 struct gotd_imsgev
*iev
= &client
->iev
;
250 memset(&iok
, 0, sizeof(iok
));
251 iok
.client_id
= client
->id
;
252 memcpy(iok
.old_id
, iref
->old_id
, SHA1_DIGEST_LENGTH
);
253 memcpy(iok
.new_id
, iref
->new_id
, SHA1_DIGEST_LENGTH
);
254 iok
.name_len
= strlen(refname
);
256 len
= sizeof(iok
) + iok
.name_len
;
257 wbuf
= imsg_create(&iev
->ibuf
, GOTD_IMSG_REF_UPDATE_OK
,
258 gotd_session
.proc_id
, gotd_session
.pid
, len
);
260 return got_error_from_errno("imsg_create REF_UPDATE_OK");
262 if (imsg_add(wbuf
, &iok
, sizeof(iok
)) == -1)
263 return got_error_from_errno("imsg_add REF_UPDATE_OK");
264 if (imsg_add(wbuf
, refname
, iok
.name_len
) == -1)
265 return got_error_from_errno("imsg_add REF_UPDATE_OK");
268 imsg_close(&iev
->ibuf
, wbuf
);
269 gotd_imsg_event_add(iev
);
274 send_refs_updated(struct gotd_session_client
*client
)
276 if (gotd_imsg_compose_event(&client
->iev
, GOTD_IMSG_REFS_UPDATED
,
277 gotd_session
.proc_id
, -1, NULL
, 0) == -1)
278 log_warn("imsg compose REFS_UPDATED");
281 static const struct got_error
*
282 send_ref_update_ng(struct gotd_session_client
*client
,
283 struct gotd_imsg_ref_update
*iref
, const char *refname
,
286 const struct got_error
*ng_err
;
287 struct gotd_imsg_ref_update_ng ing
;
288 struct gotd_imsgev
*iev
= &client
->iev
;
292 memset(&ing
, 0, sizeof(ing
));
293 ing
.client_id
= client
->id
;
294 memcpy(ing
.old_id
, iref
->old_id
, SHA1_DIGEST_LENGTH
);
295 memcpy(ing
.new_id
, iref
->new_id
, SHA1_DIGEST_LENGTH
);
296 ing
.name_len
= strlen(refname
);
298 ng_err
= got_error_fmt(GOT_ERR_REF_BUSY
, "%s", reason
);
299 ing
.reason_len
= strlen(ng_err
->msg
);
301 len
= sizeof(ing
) + ing
.name_len
+ ing
.reason_len
;
302 wbuf
= imsg_create(&iev
->ibuf
, GOTD_IMSG_REF_UPDATE_NG
,
303 gotd_session
.proc_id
, gotd_session
.pid
, len
);
305 return got_error_from_errno("imsg_create REF_UPDATE_NG");
307 if (imsg_add(wbuf
, &ing
, sizeof(ing
)) == -1)
308 return got_error_from_errno("imsg_add REF_UPDATE_NG");
309 if (imsg_add(wbuf
, refname
, ing
.name_len
) == -1)
310 return got_error_from_errno("imsg_add REF_UPDATE_NG");
311 if (imsg_add(wbuf
, ng_err
->msg
, ing
.reason_len
) == -1)
312 return got_error_from_errno("imsg_add REF_UPDATE_NG");
315 imsg_close(&iev
->ibuf
, wbuf
);
316 gotd_imsg_event_add(iev
);
320 static const struct got_error
*
321 install_pack(struct gotd_session_client
*client
, const char *repo_path
,
324 const struct got_error
*err
= NULL
;
325 struct gotd_imsg_packfile_install inst
;
326 char hex
[SHA1_DIGEST_STRING_LENGTH
];
328 char *packfile_path
= NULL
, *packidx_path
= NULL
;
330 datalen
= imsg
->hdr
.len
- IMSG_HEADER_SIZE
;
331 if (datalen
!= sizeof(inst
))
332 return got_error(GOT_ERR_PRIVSEP_LEN
);
333 memcpy(&inst
, imsg
->data
, sizeof(inst
));
335 if (client
->packfile_path
== NULL
)
336 return got_error_msg(GOT_ERR_BAD_REQUEST
,
337 "client has no pack file");
338 if (client
->packidx_path
== NULL
)
339 return got_error_msg(GOT_ERR_BAD_REQUEST
,
340 "client has no pack file index");
342 if (got_sha1_digest_to_str(inst
.pack_sha1
, hex
, sizeof(hex
)) == NULL
)
343 return got_error_msg(GOT_ERR_NO_SPACE
,
344 "could not convert pack file SHA1 to hex");
346 if (asprintf(&packfile_path
, "/%s/%s/pack-%s.pack",
347 repo_path
, GOT_OBJECTS_PACK_DIR
, hex
) == -1) {
348 err
= got_error_from_errno("asprintf");
352 if (asprintf(&packidx_path
, "/%s/%s/pack-%s.idx",
353 repo_path
, GOT_OBJECTS_PACK_DIR
, hex
) == -1) {
354 err
= got_error_from_errno("asprintf");
358 if (rename(client
->packfile_path
, packfile_path
) == -1) {
359 err
= got_error_from_errno3("rename", client
->packfile_path
,
364 free(client
->packfile_path
);
365 client
->packfile_path
= NULL
;
367 if (rename(client
->packidx_path
, packidx_path
) == -1) {
368 err
= got_error_from_errno3("rename", client
->packidx_path
,
373 free(client
->packidx_path
);
374 client
->packidx_path
= NULL
;
381 static const struct got_error
*
382 begin_ref_updates(struct gotd_session_client
*client
, struct imsg
*imsg
)
384 struct gotd_imsg_ref_updates_start istart
;
387 if (client
->nref_updates
!= -1)
388 return got_error(GOT_ERR_PRIVSEP_MSG
);
390 datalen
= imsg
->hdr
.len
- IMSG_HEADER_SIZE
;
391 if (datalen
!= sizeof(istart
))
392 return got_error(GOT_ERR_PRIVSEP_LEN
);
393 memcpy(&istart
, imsg
->data
, sizeof(istart
));
395 if (istart
.nref_updates
<= 0)
396 return got_error(GOT_ERR_PRIVSEP_MSG
);
398 client
->nref_updates
= istart
.nref_updates
;
402 static const struct got_error
*
403 update_ref(int *shut
, struct gotd_session_client
*client
,
404 const char *repo_path
, struct imsg
*imsg
)
406 const struct got_error
*err
= NULL
;
407 struct got_repository
*repo
= NULL
;
408 struct got_reference
*ref
= NULL
;
409 struct gotd_imsg_ref_update iref
;
410 struct got_object_id old_id
, new_id
;
411 struct got_object_id
*id
= NULL
;
412 struct got_object
*obj
= NULL
;
413 char *refname
= NULL
;
416 char hex1
[SHA1_DIGEST_STRING_LENGTH
];
417 char hex2
[SHA1_DIGEST_STRING_LENGTH
];
419 log_debug("update-ref from uid %d", client
->euid
);
421 if (client
->nref_updates
<= 0)
422 return got_error(GOT_ERR_PRIVSEP_MSG
);
424 datalen
= imsg
->hdr
.len
- IMSG_HEADER_SIZE
;
425 if (datalen
< sizeof(iref
))
426 return got_error(GOT_ERR_PRIVSEP_LEN
);
427 memcpy(&iref
, imsg
->data
, sizeof(iref
));
428 if (datalen
!= sizeof(iref
) + iref
.name_len
)
429 return got_error(GOT_ERR_PRIVSEP_LEN
);
430 refname
= strndup(imsg
->data
+ sizeof(iref
), iref
.name_len
);
432 return got_error_from_errno("strndup");
434 log_debug("updating ref %s for uid %d", refname
, client
->euid
);
436 err
= got_repo_open(&repo
, repo_path
, NULL
, NULL
);
440 memcpy(old_id
.sha1
, iref
.old_id
, SHA1_DIGEST_LENGTH
);
441 memcpy(new_id
.sha1
, iref
.new_id
, SHA1_DIGEST_LENGTH
);
442 err
= got_object_open(&obj
, repo
,
443 iref
.delete_ref
? &old_id
: &new_id
);
447 if (iref
.ref_is_new
) {
448 err
= got_ref_open(&ref
, repo
, refname
, 0);
450 if (err
->code
!= GOT_ERR_NOT_REF
)
452 err
= got_ref_alloc(&ref
, refname
, &new_id
);
455 err
= got_ref_write(ref
, repo
); /* will lock/unlock */
459 err
= got_ref_resolve(&id
, repo
, ref
);
462 got_object_id_hex(&new_id
, hex1
, sizeof(hex1
));
463 got_object_id_hex(id
, hex2
, sizeof(hex2
));
464 err
= got_error_fmt(GOT_ERR_REF_BUSY
,
465 "Addition %s: %s failed; %s: %s has been "
466 "created by someone else while transaction "
468 got_ref_get_name(ref
), hex1
,
469 got_ref_get_name(ref
), hex2
);
472 } else if (iref
.delete_ref
) {
473 err
= got_ref_open(&ref
, repo
, refname
, 1 /* lock */);
478 err
= got_ref_resolve(&id
, repo
, ref
);
482 if (got_object_id_cmp(id
, &old_id
) != 0) {
483 got_object_id_hex(&old_id
, hex1
, sizeof(hex1
));
484 got_object_id_hex(id
, hex2
, sizeof(hex2
));
485 err
= got_error_fmt(GOT_ERR_REF_BUSY
,
486 "Deletion %s: %s failed; %s: %s has been "
487 "created by someone else while transaction "
489 got_ref_get_name(ref
), hex1
,
490 got_ref_get_name(ref
), hex2
);
494 err
= got_ref_delete(ref
, repo
);
501 err
= got_ref_open(&ref
, repo
, refname
, 1 /* lock */);
506 err
= got_ref_resolve(&id
, repo
, ref
);
510 if (got_object_id_cmp(id
, &old_id
) != 0) {
511 got_object_id_hex(&old_id
, hex1
, sizeof(hex1
));
512 got_object_id_hex(id
, hex2
, sizeof(hex2
));
513 err
= got_error_fmt(GOT_ERR_REF_BUSY
,
514 "Update %s: %s failed; %s: %s has been "
515 "created by someone else while transaction "
517 got_ref_get_name(ref
), hex1
,
518 got_ref_get_name(ref
), hex2
);
522 if (got_object_id_cmp(&new_id
, &old_id
) != 0) {
523 err
= got_ref_change_ref(ref
, &new_id
);
527 err
= got_ref_write(ref
, repo
);
537 if (err
->code
== GOT_ERR_LOCKFILE_TIMEOUT
) {
538 err
= got_error_fmt(GOT_ERR_LOCKFILE_TIMEOUT
,
539 "could not acquire exclusive file lock for %s",
542 send_ref_update_ng(client
, &iref
, refname
, err
->msg
);
544 send_ref_update_ok(client
, &iref
, refname
);
546 if (client
->nref_updates
> 0) {
547 client
->nref_updates
--;
548 if (client
->nref_updates
== 0) {
549 send_refs_updated(client
);
550 client
->flush_disconnect
= 1;
555 const struct got_error
*unlock_err
;
556 unlock_err
= got_ref_unlock(ref
);
557 if (unlock_err
&& err
== NULL
)
563 got_object_close(obj
);
565 got_repo_close(repo
);
572 session_dispatch_repo_child(int fd
, short event
, void *arg
)
574 struct gotd_imsgev
*iev
= arg
;
575 struct imsgbuf
*ibuf
= &iev
->ibuf
;
576 struct gotd_session_client
*client
= &gotd_session_client
;
581 if (event
& EV_READ
) {
582 if ((n
= imsg_read(ibuf
)) == -1 && errno
!= EAGAIN
)
583 fatal("imsg_read error");
585 /* Connection closed. */
591 if (event
& EV_WRITE
) {
592 n
= msgbuf_write(&ibuf
->w
);
593 if (n
== -1 && errno
!= EAGAIN
)
594 fatal("msgbuf_write");
596 /* Connection closed. */
603 const struct got_error
*err
= NULL
;
604 uint32_t client_id
= 0;
605 int do_disconnect
= 0;
606 int do_ref_updates
= 0, do_ref_update
= 0;
607 int do_packfile_install
= 0;
609 if ((n
= imsg_get(ibuf
, &imsg
)) == -1)
610 fatal("%s: imsg_get error", __func__
);
611 if (n
== 0) /* No more messages. */
614 switch (imsg
.hdr
.type
) {
615 case GOTD_IMSG_ERROR
:
617 err
= gotd_imsg_recv_error(&client_id
, &imsg
);
619 case GOTD_IMSG_PACKFILE_DONE
:
621 err
= recv_packfile_done(&client_id
, &imsg
);
623 case GOTD_IMSG_PACKFILE_INSTALL
:
624 err
= recv_packfile_install(&client_id
, &imsg
);
626 do_packfile_install
= 1;
628 case GOTD_IMSG_REF_UPDATES_START
:
629 err
= recv_ref_updates_start(&client_id
, &imsg
);
633 case GOTD_IMSG_REF_UPDATE
:
634 err
= recv_ref_update(&client_id
, &imsg
);
639 log_debug("unexpected imsg %d", imsg
.hdr
.type
);
645 disconnect_on_error(client
, err
);
649 if (do_packfile_install
)
650 err
= install_pack(client
,
651 gotd_session
.repo
->path
, &imsg
);
652 else if (do_ref_updates
)
653 err
= begin_ref_updates(client
, &imsg
);
654 else if (do_ref_update
)
655 err
= update_ref(&shut
, client
,
656 gotd_session
.repo
->path
, &imsg
);
658 log_warnx("uid %d: %s", client
->euid
, err
->msg
);
664 gotd_imsg_event_add(iev
);
666 /* This pipe is dead. Remove its event handler */
668 event_loopexit(NULL
);
672 static const struct got_error
*
673 recv_capabilities(struct gotd_session_client
*client
, struct imsg
*imsg
)
675 struct gotd_imsg_capabilities icapas
;
678 datalen
= imsg
->hdr
.len
- IMSG_HEADER_SIZE
;
679 if (datalen
!= sizeof(icapas
))
680 return got_error(GOT_ERR_PRIVSEP_LEN
);
681 memcpy(&icapas
, imsg
->data
, sizeof(icapas
));
683 client
->ncapa_alloc
= icapas
.ncapabilities
;
684 client
->capabilities
= calloc(client
->ncapa_alloc
,
685 sizeof(*client
->capabilities
));
686 if (client
->capabilities
== NULL
) {
687 client
->ncapa_alloc
= 0;
688 return got_error_from_errno("calloc");
691 log_debug("expecting %zu capabilities from uid %d",
692 client
->ncapa_alloc
, client
->euid
);
696 static const struct got_error
*
697 recv_capability(struct gotd_session_client
*client
, struct imsg
*imsg
)
699 struct gotd_imsg_capability icapa
;
700 struct gotd_client_capability
*capa
;
702 char *key
, *value
= NULL
;
704 if (client
->capabilities
== NULL
||
705 client
->ncapabilities
>= client
->ncapa_alloc
) {
706 return got_error_msg(GOT_ERR_BAD_REQUEST
,
707 "unexpected capability received");
710 memset(&icapa
, 0, sizeof(icapa
));
712 datalen
= imsg
->hdr
.len
- IMSG_HEADER_SIZE
;
713 if (datalen
< sizeof(icapa
))
714 return got_error(GOT_ERR_PRIVSEP_LEN
);
715 memcpy(&icapa
, imsg
->data
, sizeof(icapa
));
717 if (datalen
!= sizeof(icapa
) + icapa
.key_len
+ icapa
.value_len
)
718 return got_error(GOT_ERR_PRIVSEP_LEN
);
720 key
= strndup(imsg
->data
+ sizeof(icapa
), icapa
.key_len
);
722 return got_error_from_errno("strndup");
723 if (icapa
.value_len
> 0) {
724 value
= strndup(imsg
->data
+ sizeof(icapa
) + icapa
.key_len
,
728 return got_error_from_errno("strndup");
732 capa
= &client
->capabilities
[client
->ncapabilities
++];
737 log_debug("uid %d: capability %s=%s", client
->euid
, key
, value
);
739 log_debug("uid %d: capability %s", client
->euid
, key
);
744 static const struct got_error
*
745 ensure_client_is_reading(struct gotd_session_client
*client
)
747 if (client
->is_writing
) {
748 return got_error_fmt(GOT_ERR_BAD_PACKET
,
749 "uid %d made a read-request but is not reading from "
750 "a repository", client
->euid
);
756 static const struct got_error
*
757 ensure_client_is_writing(struct gotd_session_client
*client
)
759 if (!client
->is_writing
) {
760 return got_error_fmt(GOT_ERR_BAD_PACKET
,
761 "uid %d made a write-request but is not writing to "
762 "a repository", client
->euid
);
768 static const struct got_error
*
769 forward_want(struct gotd_session_client
*client
, struct imsg
*imsg
)
771 struct gotd_imsg_want ireq
;
772 struct gotd_imsg_want iwant
;
775 datalen
= imsg
->hdr
.len
- IMSG_HEADER_SIZE
;
776 if (datalen
!= sizeof(ireq
))
777 return got_error(GOT_ERR_PRIVSEP_LEN
);
779 memcpy(&ireq
, imsg
->data
, datalen
);
781 memset(&iwant
, 0, sizeof(iwant
));
782 memcpy(iwant
.object_id
, ireq
.object_id
, SHA1_DIGEST_LENGTH
);
783 iwant
.client_id
= client
->id
;
785 if (gotd_imsg_compose_event(&client
->repo_child_iev
, GOTD_IMSG_WANT
,
786 gotd_session
.proc_id
, -1, &iwant
, sizeof(iwant
)) == -1)
787 return got_error_from_errno("imsg compose WANT");
792 static const struct got_error
*
793 forward_ref_update(struct gotd_session_client
*client
, struct imsg
*imsg
)
795 const struct got_error
*err
= NULL
;
796 struct gotd_imsg_ref_update ireq
;
797 struct gotd_imsg_ref_update
*iref
= NULL
;
800 datalen
= imsg
->hdr
.len
- IMSG_HEADER_SIZE
;
801 if (datalen
< sizeof(ireq
))
802 return got_error(GOT_ERR_PRIVSEP_LEN
);
803 memcpy(&ireq
, imsg
->data
, sizeof(ireq
));
804 if (datalen
!= sizeof(ireq
) + ireq
.name_len
)
805 return got_error(GOT_ERR_PRIVSEP_LEN
);
807 iref
= malloc(datalen
);
809 return got_error_from_errno("malloc");
810 memcpy(iref
, imsg
->data
, datalen
);
812 iref
->client_id
= client
->id
;
813 if (gotd_imsg_compose_event(&client
->repo_child_iev
,
814 GOTD_IMSG_REF_UPDATE
, gotd_session
.proc_id
, -1,
815 iref
, datalen
) == -1)
816 err
= got_error_from_errno("imsg compose REF_UPDATE");
821 static const struct got_error
*
822 forward_have(struct gotd_session_client
*client
, struct imsg
*imsg
)
824 struct gotd_imsg_have ireq
;
825 struct gotd_imsg_have ihave
;
828 datalen
= imsg
->hdr
.len
- IMSG_HEADER_SIZE
;
829 if (datalen
!= sizeof(ireq
))
830 return got_error(GOT_ERR_PRIVSEP_LEN
);
832 memcpy(&ireq
, imsg
->data
, datalen
);
834 memset(&ihave
, 0, sizeof(ihave
));
835 memcpy(ihave
.object_id
, ireq
.object_id
, SHA1_DIGEST_LENGTH
);
836 ihave
.client_id
= client
->id
;
838 if (gotd_imsg_compose_event(&client
->repo_child_iev
, GOTD_IMSG_HAVE
,
839 gotd_session
.proc_id
, -1, &ihave
, sizeof(ihave
)) == -1)
840 return got_error_from_errno("imsg compose HAVE");
846 client_has_capability(struct gotd_session_client
*client
, const char *capastr
)
848 struct gotd_client_capability
*capa
;
851 if (client
->ncapabilities
== 0)
854 for (i
= 0; i
< client
->ncapabilities
; i
++) {
855 capa
= &client
->capabilities
[i
];
856 if (strcmp(capa
->key
, capastr
) == 0)
863 static const struct got_error
*
864 recv_packfile(struct gotd_session_client
*client
)
866 const struct got_error
*err
= NULL
;
867 struct gotd_imsg_recv_packfile ipack
;
868 struct gotd_imsg_packfile_pipe ipipe
;
869 struct gotd_imsg_packidx_file ifile
;
870 char *basepath
= NULL
, *pack_path
= NULL
, *idx_path
= NULL
;
871 int packfd
= -1, idxfd
= -1;
872 int pipe
[2] = { -1, -1 };
874 if (client
->packfile_path
) {
875 return got_error_fmt(GOT_ERR_PRIVSEP_MSG
,
876 "uid %d already has a pack file", client
->euid
);
879 if (socketpair(AF_UNIX
, SOCK_STREAM
, PF_UNSPEC
, pipe
) == -1)
880 return got_error_from_errno("socketpair");
882 memset(&ipipe
, 0, sizeof(ipipe
));
883 ipipe
.client_id
= client
->id
;
885 /* Send pack pipe end 0 to repo child process. */
886 if (gotd_imsg_compose_event(&client
->repo_child_iev
,
887 GOTD_IMSG_PACKFILE_PIPE
, gotd_session
.proc_id
, pipe
[0],
888 &ipipe
, sizeof(ipipe
)) == -1) {
889 err
= got_error_from_errno("imsg compose PACKFILE_PIPE");
895 /* Send pack pipe end 1 to gotsh(1) (expects just an fd, no data). */
896 if (gotd_imsg_compose_event(&client
->iev
,
897 GOTD_IMSG_PACKFILE_PIPE
, gotd_session
.proc_id
, pipe
[1],
899 err
= got_error_from_errno("imsg compose PACKFILE_PIPE");
902 if (asprintf(&basepath
, "%s/%s/receiving-from-uid-%d.pack",
903 got_repo_get_path(gotd_session
.repo
), GOT_OBJECTS_PACK_DIR
,
904 client
->euid
) == -1) {
905 err
= got_error_from_errno("asprintf");
909 err
= got_opentemp_named_fd(&pack_path
, &packfd
, basepath
, "");
912 if (fchmod(packfd
, GOT_DEFAULT_PACK_MODE
) == -1) {
913 err
= got_error_from_errno2("fchmod", pack_path
);
918 if (asprintf(&basepath
, "%s/%s/receiving-from-uid-%d.idx",
919 got_repo_get_path(gotd_session
.repo
), GOT_OBJECTS_PACK_DIR
,
920 client
->euid
) == -1) {
921 err
= got_error_from_errno("asprintf");
925 err
= got_opentemp_named_fd(&idx_path
, &idxfd
, basepath
, "");
928 if (fchmod(idxfd
, GOT_DEFAULT_PACK_MODE
) == -1) {
929 err
= got_error_from_errno2("fchmod", idx_path
);
933 memset(&ifile
, 0, sizeof(ifile
));
934 ifile
.client_id
= client
->id
;
935 if (gotd_imsg_compose_event(&client
->repo_child_iev
,
936 GOTD_IMSG_PACKIDX_FILE
, gotd_session
.proc_id
,
937 idxfd
, &ifile
, sizeof(ifile
)) == -1) {
938 err
= got_error_from_errno("imsg compose PACKIDX_FILE");
944 memset(&ipack
, 0, sizeof(ipack
));
945 ipack
.client_id
= client
->id
;
946 if (client_has_capability(client
, GOT_CAPA_REPORT_STATUS
))
947 ipack
.report_status
= 1;
949 if (gotd_imsg_compose_event(&client
->repo_child_iev
,
950 GOTD_IMSG_RECV_PACKFILE
, gotd_session
.proc_id
, packfd
,
951 &ipack
, sizeof(ipack
)) == -1) {
952 err
= got_error_from_errno("imsg compose RECV_PACKFILE");
960 if (pipe
[0] != -1 && close(pipe
[0]) == -1 && err
== NULL
)
961 err
= got_error_from_errno("close");
962 if (pipe
[1] != -1 && close(pipe
[1]) == -1 && err
== NULL
)
963 err
= got_error_from_errno("close");
964 if (packfd
!= -1 && close(packfd
) == -1 && err
== NULL
)
965 err
= got_error_from_errno("close");
966 if (idxfd
!= -1 && close(idxfd
) == -1 && err
== NULL
)
967 err
= got_error_from_errno("close");
972 client
->packfile_path
= pack_path
;
973 client
->packidx_path
= idx_path
;
978 static const struct got_error
*
979 send_packfile(struct gotd_session_client
*client
)
981 const struct got_error
*err
= NULL
;
982 struct gotd_imsg_send_packfile ipack
;
983 struct gotd_imsg_packfile_pipe ipipe
;
986 if (socketpair(AF_UNIX
, SOCK_STREAM
, PF_UNSPEC
, pipe
) == -1)
987 return got_error_from_errno("socketpair");
989 memset(&ipack
, 0, sizeof(ipack
));
990 memset(&ipipe
, 0, sizeof(ipipe
));
992 ipack
.client_id
= client
->id
;
993 if (client_has_capability(client
, GOT_CAPA_SIDE_BAND_64K
))
994 ipack
.report_progress
= 1;
996 client
->delta_cache_fd
= got_opentempfd();
997 if (client
->delta_cache_fd
== -1)
998 return got_error_from_errno("got_opentempfd");
1000 if (gotd_imsg_compose_event(&client
->repo_child_iev
,
1001 GOTD_IMSG_SEND_PACKFILE
, PROC_GOTD
, client
->delta_cache_fd
,
1002 &ipack
, sizeof(ipack
)) == -1) {
1003 err
= got_error_from_errno("imsg compose SEND_PACKFILE");
1009 ipipe
.client_id
= client
->id
;
1011 /* Send pack pipe end 0 to repo child process. */
1012 if (gotd_imsg_compose_event(&client
->repo_child_iev
,
1013 GOTD_IMSG_PACKFILE_PIPE
, PROC_GOTD
,
1014 pipe
[0], &ipipe
, sizeof(ipipe
)) == -1) {
1015 err
= got_error_from_errno("imsg compose PACKFILE_PIPE");
1020 /* Send pack pipe end 1 to gotsh(1) (expects just an fd, no data). */
1021 if (gotd_imsg_compose_event(&client
->iev
,
1022 GOTD_IMSG_PACKFILE_PIPE
, PROC_GOTD
, pipe
[1], NULL
, 0) == -1)
1023 err
= got_error_from_errno("imsg compose PACKFILE_PIPE");
1029 session_dispatch_client(int fd
, short events
, void *arg
)
1031 struct gotd_imsgev
*iev
= arg
;
1032 struct imsgbuf
*ibuf
= &iev
->ibuf
;
1033 struct gotd_session_client
*client
= &gotd_session_client
;
1034 const struct got_error
*err
= NULL
;
1038 if (events
& EV_WRITE
) {
1039 while (ibuf
->w
.queued
) {
1040 n
= msgbuf_write(&ibuf
->w
);
1041 if (n
== -1 && errno
== EPIPE
) {
1043 * The client has closed its socket.
1044 * This can happen when Git clients are
1045 * done sending pack file data.
1047 msgbuf_clear(&ibuf
->w
);
1049 } else if (n
== -1 && errno
!= EAGAIN
) {
1050 err
= got_error_from_errno("imsg_flush");
1051 disconnect_on_error(client
, err
);
1055 /* Connection closed. */
1056 err
= got_error(GOT_ERR_EOF
);
1057 disconnect_on_error(client
, err
);
1062 if (client
->flush_disconnect
) {
1068 if ((events
& EV_READ
) == 0)
1071 memset(&imsg
, 0, sizeof(imsg
));
1073 while (err
== NULL
) {
1074 err
= gotd_imsg_recv(&imsg
, ibuf
, 0);
1076 if (err
->code
== GOT_ERR_PRIVSEP_READ
)
1078 else if (err
->code
== GOT_ERR_EOF
&&
1079 client
->state
== GOTD_STATE_EXPECT_CAPABILITIES
) {
1081 * The client has closed its socket before
1082 * sending its capability announcement.
1083 * This can happen when Git clients have
1084 * no ref-updates to send.
1086 disconnect_on_error(client
, err
);
1092 evtimer_del(&client
->tmo
);
1094 switch (imsg
.hdr
.type
) {
1095 case GOTD_IMSG_CAPABILITIES
:
1096 if (client
->state
!= GOTD_STATE_EXPECT_CAPABILITIES
) {
1097 err
= got_error_msg(GOT_ERR_BAD_REQUEST
,
1098 "unexpected capabilities received");
1101 log_debug("receiving capabilities from uid %d",
1103 err
= recv_capabilities(client
, &imsg
);
1105 case GOTD_IMSG_CAPABILITY
:
1106 if (client
->state
!= GOTD_STATE_EXPECT_CAPABILITIES
) {
1107 err
= got_error_msg(GOT_ERR_BAD_REQUEST
,
1108 "unexpected capability received");
1111 err
= recv_capability(client
, &imsg
);
1112 if (err
|| client
->ncapabilities
< client
->ncapa_alloc
)
1114 if (!client
->is_writing
) {
1115 client
->state
= GOTD_STATE_EXPECT_WANT
;
1116 client
->accept_flush_pkt
= 1;
1117 log_debug("uid %d: expecting want-lines",
1119 } else if (client
->is_writing
) {
1120 client
->state
= GOTD_STATE_EXPECT_REF_UPDATE
;
1121 client
->accept_flush_pkt
= 1;
1122 log_debug("uid %d: expecting ref-update-lines",
1125 fatalx("client %d is both reading and writing",
1128 case GOTD_IMSG_WANT
:
1129 if (client
->state
!= GOTD_STATE_EXPECT_WANT
) {
1130 err
= got_error_msg(GOT_ERR_BAD_REQUEST
,
1131 "unexpected want-line received");
1134 log_debug("received want-line from uid %d",
1136 err
= ensure_client_is_reading(client
);
1139 client
->accept_flush_pkt
= 1;
1140 err
= forward_want(client
, &imsg
);
1142 case GOTD_IMSG_REF_UPDATE
:
1143 if (client
->state
!= GOTD_STATE_EXPECT_REF_UPDATE
&&
1145 GOTD_STATE_EXPECT_MORE_REF_UPDATES
) {
1146 err
= got_error_msg(GOT_ERR_BAD_REQUEST
,
1147 "unexpected ref-update-line received");
1150 log_debug("received ref-update-line from uid %d",
1152 err
= ensure_client_is_writing(client
);
1155 err
= forward_ref_update(client
, &imsg
);
1158 client
->state
= GOTD_STATE_EXPECT_MORE_REF_UPDATES
;
1159 client
->accept_flush_pkt
= 1;
1161 case GOTD_IMSG_HAVE
:
1162 if (client
->state
!= GOTD_STATE_EXPECT_HAVE
) {
1163 err
= got_error_msg(GOT_ERR_BAD_REQUEST
,
1164 "unexpected have-line received");
1167 log_debug("received have-line from uid %d",
1169 err
= ensure_client_is_reading(client
);
1172 err
= forward_have(client
, &imsg
);
1175 client
->accept_flush_pkt
= 1;
1177 case GOTD_IMSG_FLUSH
:
1178 if (client
->state
== GOTD_STATE_EXPECT_WANT
||
1179 client
->state
== GOTD_STATE_EXPECT_HAVE
) {
1180 err
= ensure_client_is_reading(client
);
1183 } else if (client
->state
==
1184 GOTD_STATE_EXPECT_MORE_REF_UPDATES
) {
1185 err
= ensure_client_is_writing(client
);
1188 } else if (client
->state
!= GOTD_STATE_EXPECT_DONE
) {
1189 err
= got_error_msg(GOT_ERR_BAD_REQUEST
,
1190 "unexpected flush-pkt received");
1193 if (!client
->accept_flush_pkt
) {
1194 err
= got_error_msg(GOT_ERR_BAD_REQUEST
,
1195 "unexpected flush-pkt received");
1200 * Accept just one flush packet at a time.
1201 * Future client state transitions will set this flag
1202 * again if another flush packet is expected.
1204 client
->accept_flush_pkt
= 0;
1206 log_debug("received flush-pkt from uid %d",
1208 if (client
->state
== GOTD_STATE_EXPECT_WANT
) {
1209 client
->state
= GOTD_STATE_EXPECT_HAVE
;
1210 log_debug("uid %d: expecting have-lines",
1212 } else if (client
->state
== GOTD_STATE_EXPECT_HAVE
) {
1213 client
->state
= GOTD_STATE_EXPECT_DONE
;
1214 client
->accept_flush_pkt
= 1;
1215 log_debug("uid %d: expecting 'done'",
1217 } else if (client
->state
==
1218 GOTD_STATE_EXPECT_MORE_REF_UPDATES
) {
1219 client
->state
= GOTD_STATE_EXPECT_PACKFILE
;
1220 log_debug("uid %d: expecting packfile",
1222 err
= recv_packfile(client
);
1223 } else if (client
->state
!= GOTD_STATE_EXPECT_DONE
) {
1224 /* should not happen, see above */
1225 err
= got_error_msg(GOT_ERR_BAD_REQUEST
,
1226 "unexpected client state");
1230 case GOTD_IMSG_DONE
:
1231 if (client
->state
!= GOTD_STATE_EXPECT_HAVE
&&
1232 client
->state
!= GOTD_STATE_EXPECT_DONE
) {
1233 err
= got_error_msg(GOT_ERR_BAD_REQUEST
,
1234 "unexpected flush-pkt received");
1237 log_debug("received 'done' from uid %d", client
->euid
);
1238 err
= ensure_client_is_reading(client
);
1241 client
->state
= GOTD_STATE_DONE
;
1242 client
->accept_flush_pkt
= 1;
1243 err
= send_packfile(client
);
1246 log_debug("unexpected imsg %d", imsg
.hdr
.type
);
1247 err
= got_error(GOT_ERR_PRIVSEP_MSG
);
1255 if (err
->code
!= GOT_ERR_EOF
||
1256 client
->state
!= GOTD_STATE_EXPECT_PACKFILE
)
1257 disconnect_on_error(client
, err
);
1259 gotd_imsg_event_add(iev
);
1260 evtimer_add(&client
->tmo
, &gotd_session
.request_timeout
);
1264 static const struct got_error
*
1265 list_refs_request(void)
1267 static const struct got_error
*err
;
1268 struct gotd_session_client
*client
= &gotd_session_client
;
1269 struct gotd_imsgev
*iev
= &client
->repo_child_iev
;
1270 struct gotd_imsg_list_refs_internal ilref
;
1273 if (client
->state
!= GOTD_STATE_EXPECT_LIST_REFS
)
1274 return got_error(GOT_ERR_PRIVSEP_MSG
);
1276 memset(&ilref
, 0, sizeof(ilref
));
1277 ilref
.client_id
= client
->id
;
1279 fd
= dup(client
->fd
);
1281 return got_error_from_errno("dup");
1283 if (gotd_imsg_compose_event(iev
, GOTD_IMSG_LIST_REFS_INTERNAL
,
1284 gotd_session
.proc_id
, fd
, &ilref
, sizeof(ilref
)) == -1) {
1285 err
= got_error_from_errno("imsg compose LIST_REFS_INTERNAL");
1290 client
->state
= GOTD_STATE_EXPECT_CAPABILITIES
;
1291 log_debug("uid %d: expecting capabilities", client
->euid
);
1295 static const struct got_error
*
1296 recv_connect(struct imsg
*imsg
)
1298 struct gotd_session_client
*client
= &gotd_session_client
;
1299 struct gotd_imsg_connect iconnect
;
1302 if (client
->state
!= GOTD_STATE_EXPECT_LIST_REFS
)
1303 return got_error(GOT_ERR_PRIVSEP_MSG
);
1305 datalen
= imsg
->hdr
.len
- IMSG_HEADER_SIZE
;
1306 if (datalen
!= sizeof(iconnect
))
1307 return got_error(GOT_ERR_PRIVSEP_LEN
);
1308 memcpy(&iconnect
, imsg
->data
, sizeof(iconnect
));
1311 return got_error(GOT_ERR_PRIVSEP_NO_FD
);
1313 client
->fd
= imsg
->fd
;
1314 client
->euid
= iconnect
.euid
;
1315 client
->egid
= iconnect
.egid
;
1317 imsg_init(&client
->iev
.ibuf
, client
->fd
);
1318 client
->iev
.handler
= session_dispatch_client
;
1319 client
->iev
.events
= EV_READ
;
1320 client
->iev
.handler_arg
= NULL
;
1321 event_set(&client
->iev
.ev
, client
->iev
.ibuf
.fd
, EV_READ
,
1322 session_dispatch_client
, &client
->iev
);
1323 gotd_imsg_event_add(&client
->iev
);
1324 evtimer_set(&client
->tmo
, gotd_request_timeout
, client
);
1329 static const struct got_error
*
1330 recv_repo_child(struct imsg
*imsg
)
1332 struct gotd_imsg_connect_repo_child ichild
;
1333 struct gotd_session_client
*client
= &gotd_session_client
;
1336 if (client
->state
!= GOTD_STATE_EXPECT_LIST_REFS
)
1337 return got_error(GOT_ERR_PRIVSEP_MSG
);
1339 /* We should already have received a pipe to the listener. */
1340 if (client
->fd
== -1)
1341 return got_error(GOT_ERR_PRIVSEP_MSG
);
1343 datalen
= imsg
->hdr
.len
- IMSG_HEADER_SIZE
;
1344 if (datalen
!= sizeof(ichild
))
1345 return got_error(GOT_ERR_PRIVSEP_LEN
);
1347 memcpy(&ichild
, imsg
->data
, sizeof(ichild
));
1349 client
->id
= ichild
.client_id
;
1350 if (ichild
.proc_id
== PROC_REPO_WRITE
)
1351 client
->is_writing
= 1;
1352 else if (ichild
.proc_id
== PROC_REPO_READ
)
1353 client
->is_writing
= 0;
1355 return got_error_msg(GOT_ERR_PRIVSEP_MSG
,
1356 "bad child process type");
1359 return got_error(GOT_ERR_PRIVSEP_NO_FD
);
1361 imsg_init(&client
->repo_child_iev
.ibuf
, imsg
->fd
);
1362 client
->repo_child_iev
.handler
= session_dispatch_repo_child
;
1363 client
->repo_child_iev
.events
= EV_READ
;
1364 client
->repo_child_iev
.handler_arg
= NULL
;
1365 event_set(&client
->repo_child_iev
.ev
, client
->repo_child_iev
.ibuf
.fd
,
1366 EV_READ
, session_dispatch_repo_child
, &client
->repo_child_iev
);
1367 gotd_imsg_event_add(&client
->repo_child_iev
);
1369 /* The "recvfd" pledge promise is no longer needed. */
1370 if (pledge("stdio rpath wpath cpath sendfd fattr flock", NULL
) == -1)
1377 session_dispatch(int fd
, short event
, void *arg
)
1379 struct gotd_imsgev
*iev
= arg
;
1380 struct imsgbuf
*ibuf
= &iev
->ibuf
;
1381 struct gotd_session_client
*client
= &gotd_session_client
;
1386 if (event
& EV_READ
) {
1387 if ((n
= imsg_read(ibuf
)) == -1 && errno
!= EAGAIN
)
1388 fatal("imsg_read error");
1390 /* Connection closed. */
1396 if (event
& EV_WRITE
) {
1397 n
= msgbuf_write(&ibuf
->w
);
1398 if (n
== -1 && errno
!= EAGAIN
)
1399 fatal("msgbuf_write");
1401 /* Connection closed. */
1408 const struct got_error
*err
= NULL
;
1409 uint32_t client_id
= 0;
1410 int do_disconnect
= 0, do_list_refs
= 0;
1412 if ((n
= imsg_get(ibuf
, &imsg
)) == -1)
1413 fatal("%s: imsg_get error", __func__
);
1414 if (n
== 0) /* No more messages. */
1417 switch (imsg
.hdr
.type
) {
1418 case GOTD_IMSG_ERROR
:
1420 err
= gotd_imsg_recv_error(&client_id
, &imsg
);
1422 case GOTD_IMSG_CONNECT
:
1423 err
= recv_connect(&imsg
);
1425 case GOTD_IMSG_DISCONNECT
:
1428 case GOTD_IMSG_CONNECT_REPO_CHILD
:
1429 err
= recv_repo_child(&imsg
);
1435 log_debug("unexpected imsg %d", imsg
.hdr
.type
);
1440 if (do_disconnect
) {
1442 disconnect_on_error(client
, err
);
1445 } else if (do_list_refs
)
1446 err
= list_refs_request();
1449 log_warnx("uid %d: %s", client
->euid
, err
->msg
);
1453 gotd_imsg_event_add(iev
);
1455 /* This pipe is dead. Remove its event handler */
1456 event_del(&iev
->ev
);
1457 event_loopexit(NULL
);
1462 session_main(const char *title
, const char *repo_path
,
1463 int *pack_fds
, int *temp_fds
, struct timeval
*request_timeout
,
1464 enum gotd_procid proc_id
)
1466 const struct got_error
*err
= NULL
;
1467 struct event evsigint
, evsigterm
, evsighup
, evsigusr1
;
1469 gotd_session
.title
= title
;
1470 gotd_session
.pid
= getpid();
1471 gotd_session
.pack_fds
= pack_fds
;
1472 gotd_session
.temp_fds
= temp_fds
;
1473 memcpy(&gotd_session
.request_timeout
, request_timeout
,
1474 sizeof(gotd_session
.request_timeout
));
1475 gotd_session
.proc_id
= proc_id
;
1477 err
= got_repo_open(&gotd_session
.repo
, repo_path
, NULL
, pack_fds
);
1480 if (!got_repo_is_bare(gotd_session
.repo
)) {
1481 err
= got_error_msg(GOT_ERR_NOT_GIT_REPO
,
1482 "bare git repository required");
1486 got_repo_temp_fds_set(gotd_session
.repo
, temp_fds
);
1488 signal_set(&evsigint
, SIGINT
, gotd_session_sighdlr
, NULL
);
1489 signal_set(&evsigterm
, SIGTERM
, gotd_session_sighdlr
, NULL
);
1490 signal_set(&evsighup
, SIGHUP
, gotd_session_sighdlr
, NULL
);
1491 signal_set(&evsigusr1
, SIGUSR1
, gotd_session_sighdlr
, NULL
);
1492 signal(SIGPIPE
, SIG_IGN
);
1494 signal_add(&evsigint
, NULL
);
1495 signal_add(&evsigterm
, NULL
);
1496 signal_add(&evsighup
, NULL
);
1497 signal_add(&evsigusr1
, NULL
);
1499 gotd_session_client
.state
= GOTD_STATE_EXPECT_LIST_REFS
;
1500 gotd_session_client
.fd
= -1;
1501 gotd_session_client
.nref_updates
= -1;
1502 gotd_session_client
.delta_cache_fd
= -1;
1503 gotd_session_client
.accept_flush_pkt
= 1;
1505 imsg_init(&gotd_session
.parent_iev
.ibuf
, GOTD_FILENO_MSG_PIPE
);
1506 gotd_session
.parent_iev
.handler
= session_dispatch
;
1507 gotd_session
.parent_iev
.events
= EV_READ
;
1508 gotd_session
.parent_iev
.handler_arg
= NULL
;
1509 event_set(&gotd_session
.parent_iev
.ev
, gotd_session
.parent_iev
.ibuf
.fd
,
1510 EV_READ
, session_dispatch
, &gotd_session
.parent_iev
);
1511 if (gotd_imsg_compose_event(&gotd_session
.parent_iev
,
1512 GOTD_IMSG_CLIENT_SESSION_READY
, gotd_session
.proc_id
,
1513 -1, NULL
, 0) == -1) {
1514 err
= got_error_from_errno("imsg compose CLIENT_SESSION_READY");
1521 log_warnx("%s: %s", title
, err
->msg
);
1522 gotd_session_shutdown();
1526 gotd_session_shutdown(void)
1528 log_debug("shutting down");
1529 if (gotd_session
.repo
)
1530 got_repo_close(gotd_session
.repo
);
1531 got_repo_pack_fds_close(gotd_session
.pack_fds
);
1532 got_repo_temp_fds_close(gotd_session
.temp_fds
);