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"
57 struct gotd_session_notif {
58 STAILQ_ENTRY(gotd_session_notif) entry;
60 enum gotd_notification_action action;
62 struct got_object_id old_id;
63 struct got_object_id new_id;
65 STAILQ_HEAD(gotd_session_notifications, gotd_session_notif) notifications;
67 static struct gotd_session {
70 struct got_repository *repo;
71 struct gotd_repo *repo_cfg;
74 struct gotd_imsgev parent_iev;
75 struct gotd_imsgev notifier_iev;
76 struct timeval request_timeout;
77 enum gotd_procid proc_id;
80 static struct gotd_session_client {
81 enum gotd_session_state state;
83 struct gotd_client_capability *capabilities;
89 struct gotd_imsgev iev;
90 struct gotd_imsgev repo_child_iev;
100 } gotd_session_client;
102 void gotd_session_sighdlr(int sig, short event, void *arg);
103 static void gotd_session_shutdown(void);
106 disconnect(struct gotd_session_client *client)
108 log_debug("uid %d: disconnecting", client->euid);
110 if (gotd_imsg_compose_event(&gotd_session.parent_iev,
111 GOTD_IMSG_DISCONNECT, gotd_session.proc_id, -1, NULL, 0) == -1)
112 log_warn("imsg compose DISCONNECT");
114 imsg_clear(&client->repo_child_iev.ibuf);
115 event_del(&client->repo_child_iev.ev);
116 evtimer_del(&client->tmo);
118 if (client->delta_cache_fd != -1)
119 close(client->delta_cache_fd);
120 if (client->packfile_path) {
121 if (unlink(client->packfile_path) == -1 && errno != ENOENT)
122 log_warn("unlink %s: ", client->packfile_path);
123 free(client->packfile_path);
125 if (client->packidx_path) {
126 if (unlink(client->packidx_path) == -1 && errno != ENOENT)
127 log_warn("unlink %s: ", client->packidx_path);
128 free(client->packidx_path);
130 free(client->capabilities);
132 gotd_session_shutdown();
136 disconnect_on_error(struct gotd_session_client *client,
137 const struct got_error *err)
141 if (err->code != GOT_ERR_EOF) {
142 log_warnx("uid %d: %s", client->euid, err->msg);
143 imsg_init(&ibuf, client->fd);
144 gotd_imsg_send_error(&ibuf, 0, gotd_session.proc_id, err);
152 gotd_request_timeout(int fd, short events, void *arg)
154 struct gotd_session_client *client = arg;
156 log_debug("disconnecting uid %d due to timeout", client->euid);
161 gotd_session_sighdlr(int sig, short event, void *arg)
164 * Normal signal handler rules don't apply because libevent
170 log_info("%s: ignoring SIGHUP", __func__);
173 log_info("%s: ignoring SIGUSR1", __func__);
177 gotd_session_shutdown();
181 fatalx("unexpected signal");
185 static const struct got_error *
186 recv_packfile_done(uint32_t *client_id, struct imsg *imsg)
188 struct gotd_imsg_packfile_done idone;
191 log_debug("packfile-done received");
193 datalen = imsg->hdr.len - IMSG_HEADER_SIZE;
194 if (datalen != sizeof(idone))
195 return got_error(GOT_ERR_PRIVSEP_LEN);
196 memcpy(&idone, imsg->data, sizeof(idone));
198 *client_id = idone.client_id;
202 static const struct got_error *
203 recv_packfile_install(uint32_t *client_id, struct imsg *imsg)
205 struct gotd_imsg_packfile_install inst;
208 log_debug("packfile-install received");
210 datalen = imsg->hdr.len - IMSG_HEADER_SIZE;
211 if (datalen != sizeof(inst))
212 return got_error(GOT_ERR_PRIVSEP_LEN);
213 memcpy(&inst, imsg->data, sizeof(inst));
215 *client_id = inst.client_id;
219 static const struct got_error *
220 recv_ref_updates_start(uint32_t *client_id, struct imsg *imsg)
222 struct gotd_imsg_ref_updates_start istart;
225 log_debug("ref-updates-start received");
227 datalen = imsg->hdr.len - IMSG_HEADER_SIZE;
228 if (datalen != sizeof(istart))
229 return got_error(GOT_ERR_PRIVSEP_LEN);
230 memcpy(&istart, imsg->data, sizeof(istart));
232 *client_id = istart.client_id;
236 static const struct got_error *
237 recv_ref_update(uint32_t *client_id, struct imsg *imsg)
239 struct gotd_imsg_ref_update iref;
242 log_debug("ref-update received");
244 datalen = imsg->hdr.len - IMSG_HEADER_SIZE;
245 if (datalen < sizeof(iref))
246 return got_error(GOT_ERR_PRIVSEP_LEN);
247 memcpy(&iref, imsg->data, sizeof(iref));
249 *client_id = iref.client_id;
253 static const struct got_error *
254 send_ref_update_ok(struct gotd_session_client *client,
255 struct gotd_imsg_ref_update *iref, const char *refname)
257 struct gotd_imsg_ref_update_ok iok;
258 struct gotd_imsgev *iev = &client->iev;
262 memset(&iok, 0, sizeof(iok));
263 iok.client_id = client->id;
264 memcpy(iok.old_id, iref->old_id, SHA1_DIGEST_LENGTH);
265 memcpy(iok.new_id, iref->new_id, SHA1_DIGEST_LENGTH);
266 iok.name_len = strlen(refname);
268 len = sizeof(iok) + iok.name_len;
269 wbuf = imsg_create(&iev->ibuf, GOTD_IMSG_REF_UPDATE_OK,
270 gotd_session.proc_id, gotd_session.pid, len);
272 return got_error_from_errno("imsg_create REF_UPDATE_OK");
274 if (imsg_add(wbuf, &iok, sizeof(iok)) == -1)
275 return got_error_from_errno("imsg_add REF_UPDATE_OK");
276 if (imsg_add(wbuf, refname, iok.name_len) == -1)
277 return got_error_from_errno("imsg_add REF_UPDATE_OK");
279 imsg_close(&iev->ibuf, wbuf);
280 gotd_imsg_event_add(iev);
285 send_refs_updated(struct gotd_session_client *client)
287 if (gotd_imsg_compose_event(&client->iev, GOTD_IMSG_REFS_UPDATED,
288 gotd_session.proc_id, -1, NULL, 0) == -1)
289 log_warn("imsg compose REFS_UPDATED");
292 static const struct got_error *
293 send_ref_update_ng(struct gotd_session_client *client,
294 struct gotd_imsg_ref_update *iref, const char *refname,
297 const struct got_error *ng_err;
298 struct gotd_imsg_ref_update_ng ing;
299 struct gotd_imsgev *iev = &client->iev;
303 memset(&ing, 0, sizeof(ing));
304 ing.client_id = client->id;
305 memcpy(ing.old_id, iref->old_id, SHA1_DIGEST_LENGTH);
306 memcpy(ing.new_id, iref->new_id, SHA1_DIGEST_LENGTH);
307 ing.name_len = strlen(refname);
309 ng_err = got_error_fmt(GOT_ERR_REF_BUSY, "%s", reason);
310 ing.reason_len = strlen(ng_err->msg);
312 len = sizeof(ing) + ing.name_len + ing.reason_len;
313 wbuf = imsg_create(&iev->ibuf, GOTD_IMSG_REF_UPDATE_NG,
314 gotd_session.proc_id, gotd_session.pid, len);
316 return got_error_from_errno("imsg_create REF_UPDATE_NG");
318 if (imsg_add(wbuf, &ing, sizeof(ing)) == -1)
319 return got_error_from_errno("imsg_add REF_UPDATE_NG");
320 if (imsg_add(wbuf, refname, ing.name_len) == -1)
321 return got_error_from_errno("imsg_add REF_UPDATE_NG");
322 if (imsg_add(wbuf, ng_err->msg, ing.reason_len) == -1)
323 return got_error_from_errno("imsg_add REF_UPDATE_NG");
325 imsg_close(&iev->ibuf, wbuf);
326 gotd_imsg_event_add(iev);
330 static const struct got_error *
331 install_pack(struct gotd_session_client *client, const char *repo_path,
334 const struct got_error *err = NULL;
335 struct gotd_imsg_packfile_install inst;
336 char hex[SHA1_DIGEST_STRING_LENGTH];
338 char *packfile_path = NULL, *packidx_path = NULL;
340 datalen = imsg->hdr.len - IMSG_HEADER_SIZE;
341 if (datalen != sizeof(inst))
342 return got_error(GOT_ERR_PRIVSEP_LEN);
343 memcpy(&inst, imsg->data, sizeof(inst));
345 if (client->packfile_path == NULL)
346 return got_error_msg(GOT_ERR_BAD_REQUEST,
347 "client has no pack file");
348 if (client->packidx_path == NULL)
349 return got_error_msg(GOT_ERR_BAD_REQUEST,
350 "client has no pack file index");
352 if (got_sha1_digest_to_str(inst.pack_sha1, hex, sizeof(hex)) == NULL)
353 return got_error_msg(GOT_ERR_NO_SPACE,
354 "could not convert pack file SHA1 to hex");
356 if (asprintf(&packfile_path, "/%s/%s/pack-%s.pack",
357 repo_path, GOT_OBJECTS_PACK_DIR, hex) == -1) {
358 err = got_error_from_errno("asprintf");
362 if (asprintf(&packidx_path, "/%s/%s/pack-%s.idx",
363 repo_path, GOT_OBJECTS_PACK_DIR, hex) == -1) {
364 err = got_error_from_errno("asprintf");
368 if (rename(client->packfile_path, packfile_path) == -1) {
369 err = got_error_from_errno3("rename", client->packfile_path,
374 free(client->packfile_path);
375 client->packfile_path = NULL;
377 if (rename(client->packidx_path, packidx_path) == -1) {
378 err = got_error_from_errno3("rename", client->packidx_path,
383 /* Ensure we re-read the pack index list upon next access. */
384 gotd_session.repo->pack_path_mtime.tv_sec = 0;
385 gotd_session.repo->pack_path_mtime.tv_nsec = 0;
387 free(client->packidx_path);
388 client->packidx_path = NULL;
395 static const struct got_error *
396 begin_ref_updates(struct gotd_session_client *client, struct imsg *imsg)
398 struct gotd_imsg_ref_updates_start istart;
401 if (client->nref_updates != -1)
402 return got_error(GOT_ERR_PRIVSEP_MSG);
404 datalen = imsg->hdr.len - IMSG_HEADER_SIZE;
405 if (datalen != sizeof(istart))
406 return got_error(GOT_ERR_PRIVSEP_LEN);
407 memcpy(&istart, imsg->data, sizeof(istart));
409 if (istart.nref_updates <= 0)
410 return got_error(GOT_ERR_PRIVSEP_MSG);
412 client->nref_updates = istart.nref_updates;
416 static const struct got_error *
417 validate_namespace(const char *namespace)
419 size_t len = strlen(namespace);
421 if (len < 5 || strncmp("refs/", namespace, 5) != 0 ||
422 namespace[len - 1] != '/') {
423 return got_error_fmt(GOT_ERR_BAD_REF_NAME,
424 "reference namespace '%s'", namespace);
430 static const struct got_error *
431 queue_notification(struct got_object_id *old_id, struct got_object_id *new_id,
432 struct got_repository *repo, struct got_reference *ref)
434 const struct got_error *err = NULL;
435 struct gotd_session_client *client = &gotd_session_client;
436 struct gotd_repo *repo_cfg = gotd_session.repo_cfg;
437 struct gotd_imsgev *iev = &client->repo_child_iev;
438 struct got_pathlist_entry *pe;
439 struct gotd_session_notif *notif;
441 if (iev->ibuf.fd == -1 ||
442 STAILQ_EMPTY(&repo_cfg->notification_targets))
443 return NULL; /* notifications unused */
445 TAILQ_FOREACH(pe, &repo_cfg->notification_refs, entry) {
446 const char *refname = pe->path;
447 if (strcmp(got_ref_get_name(ref), refname) == 0)
451 TAILQ_FOREACH(pe, &repo_cfg->notification_ref_namespaces,
453 const char *namespace = pe->path;
455 err = validate_namespace(namespace);
458 if (strncmp(namespace, got_ref_get_name(ref),
459 strlen(namespace)) == 0)
465 * If a branch or a reference namespace was specified in the
466 * configuration file then only send notifications if a match
469 if (pe == NULL && (!TAILQ_EMPTY(&repo_cfg->notification_refs) ||
470 !TAILQ_EMPTY(&repo_cfg->notification_ref_namespaces)))
473 notif = calloc(1, sizeof(*notif));
475 return got_error_from_errno("calloc");
480 notif->action = GOTD_NOTIF_ACTION_CREATED;
481 else if (new_id == NULL)
482 notif->action = GOTD_NOTIF_ACTION_REMOVED;
484 notif->action = GOTD_NOTIF_ACTION_CHANGED;
487 memcpy(¬if->old_id, old_id, sizeof(notif->old_id));
489 memcpy(¬if->new_id, new_id, sizeof(notif->new_id));
491 notif->refname = strdup(got_ref_get_name(ref));
492 if (notif->refname == NULL) {
493 err = got_error_from_errno("strdup");
497 STAILQ_INSERT_TAIL(¬ifications, notif, entry);
500 free(notif->refname);
506 /* Forward notification content to the NOTIFY process. */
507 static const struct got_error *
508 forward_notification(struct gotd_session_client *client, struct imsg *imsg)
510 const struct got_error *err = NULL;
511 struct gotd_imsgev *iev = &gotd_session.notifier_iev;
512 struct gotd_session_notif *notif;
513 struct gotd_imsg_notification_content icontent;
514 char *refname = NULL;
516 struct gotd_imsg_notify inotify;
519 memset(&inotify, 0, sizeof(inotify));
521 datalen = imsg->hdr.len - IMSG_HEADER_SIZE;
522 if (datalen < sizeof(icontent))
523 return got_error(GOT_ERR_PRIVSEP_LEN);
524 memcpy(&icontent, imsg->data, sizeof(icontent));
525 if (datalen != sizeof(icontent) + icontent.refname_len)
526 return got_error(GOT_ERR_PRIVSEP_LEN);
527 refname = strndup(imsg->data + sizeof(icontent), icontent.refname_len);
529 return got_error_from_errno("strndup");
531 notif = STAILQ_FIRST(¬ifications);
533 return got_error(GOT_ERR_PRIVSEP_MSG);
535 STAILQ_REMOVE(¬ifications, notif, gotd_session_notif, entry);
537 if (notif->action != icontent.action || notif->fd == -1 ||
538 strcmp(notif->refname, refname) != 0) {
539 err = got_error(GOT_ERR_PRIVSEP_MSG);
542 if (notif->action == GOTD_NOTIF_ACTION_CREATED) {
543 if (memcmp(notif->new_id.sha1, icontent.new_id,
544 SHA1_DIGEST_LENGTH) != 0) {
545 err = got_error_msg(GOT_ERR_PRIVSEP_MSG,
546 "received notification content for unknown event");
549 } else if (notif->action == GOTD_NOTIF_ACTION_REMOVED) {
550 if (memcmp(notif->old_id.sha1, icontent.old_id,
551 SHA1_DIGEST_LENGTH) != 0) {
552 err = got_error_msg(GOT_ERR_PRIVSEP_MSG,
553 "received notification content for unknown event");
556 } else if (memcmp(notif->old_id.sha1, icontent.old_id,
557 SHA1_DIGEST_LENGTH) != 0 ||
558 memcmp(notif->new_id.sha1, icontent.new_id,
559 SHA1_DIGEST_LENGTH) != 0) {
560 err = got_error_msg(GOT_ERR_PRIVSEP_MSG,
561 "received notification content for unknown event");
565 switch (notif->action) {
566 case GOTD_NOTIF_ACTION_CREATED:
569 case GOTD_NOTIF_ACTION_REMOVED:
572 case GOTD_NOTIF_ACTION_CHANGED:
576 err = got_error(GOT_ERR_PRIVSEP_MSG);
580 strlcpy(inotify.repo_name, gotd_session.repo_cfg->name,
581 sizeof(inotify.repo_name));
583 snprintf(inotify.subject_line, sizeof(inotify.subject_line),
584 "%s: %s %s %s", gotd_session.repo_cfg->name,
585 client->username, action, notif->refname);
587 if (gotd_imsg_compose_event(iev, GOTD_IMSG_NOTIFY,
588 PROC_SESSION_WRITE, notif->fd, &inotify, sizeof(inotify))
590 err = got_error_from_errno("imsg compose NOTIFY");
602 /* Request notification content from REPO_WRITE process. */
603 static const struct got_error *
604 request_notification(struct gotd_session_notif *notif)
606 const struct got_error *err = NULL;
607 struct gotd_session_client *client = &gotd_session_client;
608 struct gotd_imsgev *iev = &client->repo_child_iev;
609 struct gotd_imsg_notification_content icontent;
614 fd = got_opentempfd();
616 return got_error_from_errno("got_opentemp");
618 memset(&icontent, 0, sizeof(icontent));
619 icontent.client_id = client->id;
621 icontent.action = notif->action;
622 memcpy(&icontent.old_id, ¬if->old_id, sizeof(notif->old_id));
623 memcpy(&icontent.new_id, ¬if->new_id, sizeof(notif->new_id));
624 icontent.refname_len = strlen(notif->refname);
626 len = sizeof(icontent) + icontent.refname_len;
627 wbuf = imsg_create(&iev->ibuf, GOTD_IMSG_NOTIFY,
628 gotd_session.proc_id, gotd_session.pid, len);
630 err = got_error_from_errno("imsg_create NOTIFY");
633 if (imsg_add(wbuf, &icontent, sizeof(icontent)) == -1) {
634 err = got_error_from_errno("imsg_add NOTIFY");
637 if (imsg_add(wbuf, notif->refname, icontent.refname_len) == -1) {
638 err = got_error_from_errno("imsg_add NOTIFY");
643 if (notif->fd == -1) {
644 err = got_error_from_errno("dup");
648 ibuf_fd_set(wbuf, fd);
651 imsg_close(&iev->ibuf, wbuf);
652 gotd_imsg_event_add(iev);
659 static const struct got_error *
660 update_ref(int *shut, struct gotd_session_client *client,
661 const char *repo_path, struct imsg *imsg)
663 const struct got_error *err = NULL;
664 struct got_repository *repo = gotd_session.repo;
665 struct got_reference *ref = NULL;
666 struct gotd_imsg_ref_update iref;
667 struct got_object_id old_id, new_id;
668 struct gotd_session_notif *notif;
669 struct got_object_id *id = NULL;
670 char *refname = NULL;
673 char hex1[SHA1_DIGEST_STRING_LENGTH];
674 char hex2[SHA1_DIGEST_STRING_LENGTH];
676 log_debug("update-ref from uid %d", client->euid);
678 if (client->nref_updates <= 0)
679 return got_error(GOT_ERR_PRIVSEP_MSG);
681 datalen = imsg->hdr.len - IMSG_HEADER_SIZE;
682 if (datalen < sizeof(iref))
683 return got_error(GOT_ERR_PRIVSEP_LEN);
684 memcpy(&iref, imsg->data, sizeof(iref));
685 if (datalen != sizeof(iref) + iref.name_len)
686 return got_error(GOT_ERR_PRIVSEP_LEN);
687 refname = strndup(imsg->data + sizeof(iref), iref.name_len);
689 return got_error_from_errno("strndup");
691 log_debug("updating ref %s for uid %d", refname, client->euid);
693 memcpy(old_id.sha1, iref.old_id, SHA1_DIGEST_LENGTH);
694 memcpy(new_id.sha1, iref.new_id, SHA1_DIGEST_LENGTH);
695 err = got_repo_find_object_id(iref.delete_ref ? &old_id : &new_id,
700 if (iref.ref_is_new) {
701 err = got_ref_open(&ref, repo, refname, 0);
703 if (err->code != GOT_ERR_NOT_REF)
705 err = got_ref_alloc(&ref, refname, &new_id);
708 err = got_ref_write(ref, repo); /* will lock/unlock */
711 err = queue_notification(NULL, &new_id, repo, ref);
715 err = got_ref_resolve(&id, repo, ref);
718 got_object_id_hex(&new_id, hex1, sizeof(hex1));
719 got_object_id_hex(id, hex2, sizeof(hex2));
720 err = got_error_fmt(GOT_ERR_REF_BUSY,
721 "Addition %s: %s failed; %s: %s has been "
722 "created by someone else while transaction "
724 got_ref_get_name(ref), hex1,
725 got_ref_get_name(ref), hex2);
728 } else if (iref.delete_ref) {
729 err = got_ref_open(&ref, repo, refname, 1 /* lock */);
734 err = got_ref_resolve(&id, repo, ref);
738 if (got_object_id_cmp(id, &old_id) != 0) {
739 got_object_id_hex(&old_id, hex1, sizeof(hex1));
740 got_object_id_hex(id, hex2, sizeof(hex2));
741 err = got_error_fmt(GOT_ERR_REF_BUSY,
742 "Deletion %s: %s failed; %s: %s has been "
743 "created by someone else while transaction "
745 got_ref_get_name(ref), hex1,
746 got_ref_get_name(ref), hex2);
750 err = got_ref_delete(ref, repo);
753 err = queue_notification(&old_id, NULL, repo, ref);
759 err = got_ref_open(&ref, repo, refname, 1 /* lock */);
764 err = got_ref_resolve(&id, repo, ref);
768 if (got_object_id_cmp(id, &old_id) != 0) {
769 got_object_id_hex(&old_id, hex1, sizeof(hex1));
770 got_object_id_hex(id, hex2, sizeof(hex2));
771 err = got_error_fmt(GOT_ERR_REF_BUSY,
772 "Update %s: %s failed; %s: %s has been "
773 "created by someone else while transaction "
775 got_ref_get_name(ref), hex1,
776 got_ref_get_name(ref), hex2);
780 if (got_object_id_cmp(&new_id, &old_id) != 0) {
781 err = got_ref_change_ref(ref, &new_id);
784 err = got_ref_write(ref, repo);
787 err = queue_notification(&old_id, &new_id, repo, ref);
797 if (err->code == GOT_ERR_LOCKFILE_TIMEOUT) {
798 err = got_error_fmt(GOT_ERR_LOCKFILE_TIMEOUT,
799 "could not acquire exclusive file lock for %s",
802 send_ref_update_ng(client, &iref, refname, err->msg);
804 send_ref_update_ok(client, &iref, refname);
806 if (client->nref_updates > 0) {
807 client->nref_updates--;
808 if (client->nref_updates == 0) {
809 send_refs_updated(client);
810 notif = STAILQ_FIRST(¬ifications);
812 client->state = GOTD_STATE_NOTIFY;
813 err = request_notification(notif);
815 log_warn("could not send notification: "
817 client->flush_disconnect = 1;
820 client->flush_disconnect = 1;
825 const struct got_error *unlock_err;
826 unlock_err = got_ref_unlock(ref);
827 if (unlock_err && err == NULL)
837 static const struct got_error *
838 recv_notification_content(uint32_t *client_id, struct imsg *imsg)
840 struct gotd_imsg_notification_content inotif;
843 datalen = imsg->hdr.len - IMSG_HEADER_SIZE;
844 if (datalen < sizeof(inotif))
845 return got_error(GOT_ERR_PRIVSEP_LEN);
846 memcpy(&inotif, imsg->data, sizeof(inotif));
848 *client_id = inotif.client_id;
853 session_dispatch_repo_child(int fd, short event, void *arg)
855 struct gotd_imsgev *iev = arg;
856 struct imsgbuf *ibuf = &iev->ibuf;
857 struct gotd_session_client *client = &gotd_session_client;
862 if (event & EV_READ) {
863 if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN)
864 fatal("imsg_read error");
866 /* Connection closed. */
872 if (event & EV_WRITE) {
873 n = msgbuf_write(&ibuf->w);
874 if (n == -1 && errno != EAGAIN)
875 fatal("msgbuf_write");
877 /* Connection closed. */
884 const struct got_error *err = NULL;
885 uint32_t client_id = 0;
886 int do_disconnect = 0;
887 int do_ref_updates = 0, do_ref_update = 0;
888 int do_packfile_install = 0, do_notify = 0;
890 if ((n = imsg_get(ibuf, &imsg)) == -1)
891 fatal("%s: imsg_get error", __func__);
892 if (n == 0) /* No more messages. */
895 switch (imsg.hdr.type) {
896 case GOTD_IMSG_ERROR:
898 err = gotd_imsg_recv_error(&client_id, &imsg);
900 case GOTD_IMSG_PACKFILE_DONE:
902 err = recv_packfile_done(&client_id, &imsg);
904 case GOTD_IMSG_PACKFILE_INSTALL:
905 err = recv_packfile_install(&client_id, &imsg);
907 do_packfile_install = 1;
909 case GOTD_IMSG_REF_UPDATES_START:
910 err = recv_ref_updates_start(&client_id, &imsg);
914 case GOTD_IMSG_REF_UPDATE:
915 err = recv_ref_update(&client_id, &imsg);
919 case GOTD_IMSG_NOTIFY:
920 err = recv_notification_content(&client_id, &imsg);
925 log_debug("unexpected imsg %d", imsg.hdr.type);
931 disconnect_on_error(client, err);
935 struct gotd_session_notif *notif;
937 if (do_packfile_install)
938 err = install_pack(client,
939 gotd_session.repo->path, &imsg);
940 else if (do_ref_updates)
941 err = begin_ref_updates(client, &imsg);
942 else if (do_ref_update)
943 err = update_ref(&shut, client,
944 gotd_session.repo->path, &imsg);
946 err = forward_notification(client, &imsg);
948 log_warnx("uid %d: %s", client->euid, err->msg);
950 notif = STAILQ_FIRST(¬ifications);
951 if (notif && do_notify) {
952 /* Request content for next notification. */
953 err = request_notification(notif);
955 log_warn("could not send notification: "
965 gotd_imsg_event_add(iev);
967 /* This pipe is dead. Remove its event handler */
969 event_loopexit(NULL);
973 static const struct got_error *
974 recv_capabilities(struct gotd_session_client *client, struct imsg *imsg)
976 struct gotd_imsg_capabilities icapas;
979 datalen = imsg->hdr.len - IMSG_HEADER_SIZE;
980 if (datalen != sizeof(icapas))
981 return got_error(GOT_ERR_PRIVSEP_LEN);
982 memcpy(&icapas, imsg->data, sizeof(icapas));
984 client->ncapa_alloc = icapas.ncapabilities;
985 client->capabilities = calloc(client->ncapa_alloc,
986 sizeof(*client->capabilities));
987 if (client->capabilities == NULL) {
988 client->ncapa_alloc = 0;
989 return got_error_from_errno("calloc");
992 log_debug("expecting %zu capabilities from uid %d",
993 client->ncapa_alloc, client->euid);
997 static const struct got_error *
998 recv_capability(struct gotd_session_client *client, struct imsg *imsg)
1000 struct gotd_imsg_capability icapa;
1001 struct gotd_client_capability *capa;
1003 char *key, *value = NULL;
1005 if (client->capabilities == NULL ||
1006 client->ncapabilities >= client->ncapa_alloc) {
1007 return got_error_msg(GOT_ERR_BAD_REQUEST,
1008 "unexpected capability received");
1011 memset(&icapa, 0, sizeof(icapa));
1013 datalen = imsg->hdr.len - IMSG_HEADER_SIZE;
1014 if (datalen < sizeof(icapa))
1015 return got_error(GOT_ERR_PRIVSEP_LEN);
1016 memcpy(&icapa, imsg->data, sizeof(icapa));
1018 if (datalen != sizeof(icapa) + icapa.key_len + icapa.value_len)
1019 return got_error(GOT_ERR_PRIVSEP_LEN);
1021 key = strndup(imsg->data + sizeof(icapa), icapa.key_len);
1023 return got_error_from_errno("strndup");
1024 if (icapa.value_len > 0) {
1025 value = strndup(imsg->data + sizeof(icapa) + icapa.key_len,
1027 if (value == NULL) {
1029 return got_error_from_errno("strndup");
1033 capa = &client->capabilities[client->ncapabilities++];
1035 capa->value = value;
1038 log_debug("uid %d: capability %s=%s", client->euid, key, value);
1040 log_debug("uid %d: capability %s", client->euid, key);
1045 static const struct got_error *
1046 ensure_client_is_reading(struct gotd_session_client *client)
1048 if (client->is_writing) {
1049 return got_error_fmt(GOT_ERR_BAD_PACKET,
1050 "uid %d made a read-request but is not reading from "
1051 "a repository", client->euid);
1057 static const struct got_error *
1058 ensure_client_is_writing(struct gotd_session_client *client)
1060 if (!client->is_writing) {
1061 return got_error_fmt(GOT_ERR_BAD_PACKET,
1062 "uid %d made a write-request but is not writing to "
1063 "a repository", client->euid);
1069 static const struct got_error *
1070 forward_want(struct gotd_session_client *client, struct imsg *imsg)
1072 struct gotd_imsg_want ireq;
1073 struct gotd_imsg_want iwant;
1076 datalen = imsg->hdr.len - IMSG_HEADER_SIZE;
1077 if (datalen != sizeof(ireq))
1078 return got_error(GOT_ERR_PRIVSEP_LEN);
1080 memcpy(&ireq, imsg->data, datalen);
1082 memset(&iwant, 0, sizeof(iwant));
1083 memcpy(iwant.object_id, ireq.object_id, SHA1_DIGEST_LENGTH);
1084 iwant.client_id = client->id;
1086 if (gotd_imsg_compose_event(&client->repo_child_iev, GOTD_IMSG_WANT,
1087 gotd_session.proc_id, -1, &iwant, sizeof(iwant)) == -1)
1088 return got_error_from_errno("imsg compose WANT");
1093 static const struct got_error *
1094 forward_ref_update(struct gotd_session_client *client, struct imsg *imsg)
1096 const struct got_error *err = NULL;
1097 struct gotd_imsg_ref_update ireq;
1098 struct gotd_imsg_ref_update *iref = NULL;
1101 datalen = imsg->hdr.len - IMSG_HEADER_SIZE;
1102 if (datalen < sizeof(ireq))
1103 return got_error(GOT_ERR_PRIVSEP_LEN);
1104 memcpy(&ireq, imsg->data, sizeof(ireq));
1105 if (datalen != sizeof(ireq) + ireq.name_len)
1106 return got_error(GOT_ERR_PRIVSEP_LEN);
1108 iref = malloc(datalen);
1110 return got_error_from_errno("malloc");
1111 memcpy(iref, imsg->data, datalen);
1113 iref->client_id = client->id;
1114 if (gotd_imsg_compose_event(&client->repo_child_iev,
1115 GOTD_IMSG_REF_UPDATE, gotd_session.proc_id, -1,
1116 iref, datalen) == -1)
1117 err = got_error_from_errno("imsg compose REF_UPDATE");
1122 static const struct got_error *
1123 forward_have(struct gotd_session_client *client, struct imsg *imsg)
1125 struct gotd_imsg_have ireq;
1126 struct gotd_imsg_have ihave;
1129 datalen = imsg->hdr.len - IMSG_HEADER_SIZE;
1130 if (datalen != sizeof(ireq))
1131 return got_error(GOT_ERR_PRIVSEP_LEN);
1133 memcpy(&ireq, imsg->data, datalen);
1135 memset(&ihave, 0, sizeof(ihave));
1136 memcpy(ihave.object_id, ireq.object_id, SHA1_DIGEST_LENGTH);
1137 ihave.client_id = client->id;
1139 if (gotd_imsg_compose_event(&client->repo_child_iev, GOTD_IMSG_HAVE,
1140 gotd_session.proc_id, -1, &ihave, sizeof(ihave)) == -1)
1141 return got_error_from_errno("imsg compose HAVE");
1147 client_has_capability(struct gotd_session_client *client, const char *capastr)
1149 struct gotd_client_capability *capa;
1152 if (client->ncapabilities == 0)
1155 for (i = 0; i < client->ncapabilities; i++) {
1156 capa = &client->capabilities[i];
1157 if (strcmp(capa->key, capastr) == 0)
1164 static const struct got_error *
1165 recv_packfile(struct gotd_session_client *client)
1167 const struct got_error *err = NULL;
1168 struct gotd_imsg_recv_packfile ipack;
1169 struct gotd_imsg_packfile_pipe ipipe;
1170 struct gotd_imsg_packidx_file ifile;
1171 char *basepath = NULL, *pack_path = NULL, *idx_path = NULL;
1172 int packfd = -1, idxfd = -1;
1173 int pipe[2] = { -1, -1 };
1175 if (client->packfile_path) {
1176 return got_error_fmt(GOT_ERR_PRIVSEP_MSG,
1177 "uid %d already has a pack file", client->euid);
1180 if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, pipe) == -1)
1181 return got_error_from_errno("socketpair");
1183 memset(&ipipe, 0, sizeof(ipipe));
1184 ipipe.client_id = client->id;
1186 /* Send pack pipe end 0 to repo child process. */
1187 if (gotd_imsg_compose_event(&client->repo_child_iev,
1188 GOTD_IMSG_PACKFILE_PIPE, gotd_session.proc_id, pipe[0],
1189 &ipipe, sizeof(ipipe)) == -1) {
1190 err = got_error_from_errno("imsg compose PACKFILE_PIPE");
1196 /* Send pack pipe end 1 to gotsh(1) (expects just an fd, no data). */
1197 if (gotd_imsg_compose_event(&client->iev,
1198 GOTD_IMSG_PACKFILE_PIPE, gotd_session.proc_id, pipe[1],
1200 err = got_error_from_errno("imsg compose PACKFILE_PIPE");
1203 if (asprintf(&basepath, "%s/%s/receiving-from-uid-%d.pack",
1204 got_repo_get_path(gotd_session.repo), GOT_OBJECTS_PACK_DIR,
1205 client->euid) == -1) {
1206 err = got_error_from_errno("asprintf");
1210 err = got_opentemp_named_fd(&pack_path, &packfd, basepath, "");
1213 if (fchmod(packfd, GOT_DEFAULT_PACK_MODE) == -1) {
1214 err = got_error_from_errno2("fchmod", pack_path);
1219 if (asprintf(&basepath, "%s/%s/receiving-from-uid-%d.idx",
1220 got_repo_get_path(gotd_session.repo), GOT_OBJECTS_PACK_DIR,
1221 client->euid) == -1) {
1222 err = got_error_from_errno("asprintf");
1226 err = got_opentemp_named_fd(&idx_path, &idxfd, basepath, "");
1229 if (fchmod(idxfd, GOT_DEFAULT_PACK_MODE) == -1) {
1230 err = got_error_from_errno2("fchmod", idx_path);
1234 memset(&ifile, 0, sizeof(ifile));
1235 ifile.client_id = client->id;
1236 if (gotd_imsg_compose_event(&client->repo_child_iev,
1237 GOTD_IMSG_PACKIDX_FILE, gotd_session.proc_id,
1238 idxfd, &ifile, sizeof(ifile)) == -1) {
1239 err = got_error_from_errno("imsg compose PACKIDX_FILE");
1245 memset(&ipack, 0, sizeof(ipack));
1246 ipack.client_id = client->id;
1247 if (client_has_capability(client, GOT_CAPA_REPORT_STATUS))
1248 ipack.report_status = 1;
1250 if (gotd_imsg_compose_event(&client->repo_child_iev,
1251 GOTD_IMSG_RECV_PACKFILE, gotd_session.proc_id, packfd,
1252 &ipack, sizeof(ipack)) == -1) {
1253 err = got_error_from_errno("imsg compose RECV_PACKFILE");
1261 if (pipe[0] != -1 && close(pipe[0]) == -1 && err == NULL)
1262 err = got_error_from_errno("close");
1263 if (pipe[1] != -1 && close(pipe[1]) == -1 && err == NULL)
1264 err = got_error_from_errno("close");
1265 if (packfd != -1 && close(packfd) == -1 && err == NULL)
1266 err = got_error_from_errno("close");
1267 if (idxfd != -1 && close(idxfd) == -1 && err == NULL)
1268 err = got_error_from_errno("close");
1273 client->packfile_path = pack_path;
1274 client->packidx_path = idx_path;
1279 static const struct got_error *
1280 send_packfile(struct gotd_session_client *client)
1282 const struct got_error *err = NULL;
1283 struct gotd_imsg_send_packfile ipack;
1284 struct gotd_imsg_packfile_pipe ipipe;
1287 if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, pipe) == -1)
1288 return got_error_from_errno("socketpair");
1290 memset(&ipack, 0, sizeof(ipack));
1291 memset(&ipipe, 0, sizeof(ipipe));
1293 ipack.client_id = client->id;
1294 if (client_has_capability(client, GOT_CAPA_SIDE_BAND_64K))
1295 ipack.report_progress = 1;
1297 client->delta_cache_fd = got_opentempfd();
1298 if (client->delta_cache_fd == -1)
1299 return got_error_from_errno("got_opentempfd");
1301 if (gotd_imsg_compose_event(&client->repo_child_iev,
1302 GOTD_IMSG_SEND_PACKFILE, PROC_GOTD, client->delta_cache_fd,
1303 &ipack, sizeof(ipack)) == -1) {
1304 err = got_error_from_errno("imsg compose SEND_PACKFILE");
1310 ipipe.client_id = client->id;
1312 /* Send pack pipe end 0 to repo child process. */
1313 if (gotd_imsg_compose_event(&client->repo_child_iev,
1314 GOTD_IMSG_PACKFILE_PIPE, PROC_GOTD,
1315 pipe[0], &ipipe, sizeof(ipipe)) == -1) {
1316 err = got_error_from_errno("imsg compose PACKFILE_PIPE");
1321 /* Send pack pipe end 1 to gotsh(1) (expects just an fd, no data). */
1322 if (gotd_imsg_compose_event(&client->iev,
1323 GOTD_IMSG_PACKFILE_PIPE, PROC_GOTD, pipe[1], NULL, 0) == -1)
1324 err = got_error_from_errno("imsg compose PACKFILE_PIPE");
1330 session_dispatch_client(int fd, short events, void *arg)
1332 struct gotd_imsgev *iev = arg;
1333 struct imsgbuf *ibuf = &iev->ibuf;
1334 struct gotd_session_client *client = &gotd_session_client;
1335 const struct got_error *err = NULL;
1339 if (events & EV_WRITE) {
1340 while (ibuf->w.queued) {
1341 n = msgbuf_write(&ibuf->w);
1342 if (n == -1 && errno == EPIPE) {
1344 * The client has closed its socket.
1345 * This can happen when Git clients are
1346 * done sending pack file data.
1348 msgbuf_clear(&ibuf->w);
1350 } else if (n == -1 && errno != EAGAIN) {
1351 err = got_error_from_errno("imsg_flush");
1352 disconnect_on_error(client, err);
1356 /* Connection closed. */
1357 err = got_error(GOT_ERR_EOF);
1358 disconnect_on_error(client, err);
1363 if (client->flush_disconnect) {
1369 if ((events & EV_READ) == 0)
1372 memset(&imsg, 0, sizeof(imsg));
1374 while (err == NULL) {
1375 err = gotd_imsg_recv(&imsg, ibuf, 0);
1377 if (err->code == GOT_ERR_PRIVSEP_READ)
1379 else if (err->code == GOT_ERR_EOF &&
1380 client->state == GOTD_STATE_EXPECT_CAPABILITIES) {
1382 * The client has closed its socket before
1383 * sending its capability announcement.
1384 * This can happen when Git clients have
1385 * no ref-updates to send.
1387 disconnect_on_error(client, err);
1393 evtimer_del(&client->tmo);
1395 switch (imsg.hdr.type) {
1396 case GOTD_IMSG_CAPABILITIES:
1397 if (client->state != GOTD_STATE_EXPECT_CAPABILITIES) {
1398 err = got_error_msg(GOT_ERR_BAD_REQUEST,
1399 "unexpected capabilities received");
1402 log_debug("receiving capabilities from uid %d",
1404 err = recv_capabilities(client, &imsg);
1406 case GOTD_IMSG_CAPABILITY:
1407 if (client->state != GOTD_STATE_EXPECT_CAPABILITIES) {
1408 err = got_error_msg(GOT_ERR_BAD_REQUEST,
1409 "unexpected capability received");
1412 err = recv_capability(client, &imsg);
1413 if (err || client->ncapabilities < client->ncapa_alloc)
1415 if (!client->is_writing) {
1416 client->state = GOTD_STATE_EXPECT_WANT;
1417 client->accept_flush_pkt = 1;
1418 log_debug("uid %d: expecting want-lines",
1420 } else if (client->is_writing) {
1421 client->state = GOTD_STATE_EXPECT_REF_UPDATE;
1422 client->accept_flush_pkt = 1;
1423 log_debug("uid %d: expecting ref-update-lines",
1426 fatalx("client %d is both reading and writing",
1429 case GOTD_IMSG_WANT:
1430 if (client->state != GOTD_STATE_EXPECT_WANT) {
1431 err = got_error_msg(GOT_ERR_BAD_REQUEST,
1432 "unexpected want-line received");
1435 log_debug("received want-line from uid %d",
1437 err = ensure_client_is_reading(client);
1440 client->accept_flush_pkt = 1;
1441 err = forward_want(client, &imsg);
1443 case GOTD_IMSG_REF_UPDATE:
1444 if (client->state != GOTD_STATE_EXPECT_REF_UPDATE &&
1446 GOTD_STATE_EXPECT_MORE_REF_UPDATES) {
1447 err = got_error_msg(GOT_ERR_BAD_REQUEST,
1448 "unexpected ref-update-line received");
1451 log_debug("received ref-update-line from uid %d",
1453 err = ensure_client_is_writing(client);
1456 err = forward_ref_update(client, &imsg);
1459 client->state = GOTD_STATE_EXPECT_MORE_REF_UPDATES;
1460 client->accept_flush_pkt = 1;
1462 case GOTD_IMSG_HAVE:
1463 if (client->state != GOTD_STATE_EXPECT_HAVE) {
1464 err = got_error_msg(GOT_ERR_BAD_REQUEST,
1465 "unexpected have-line received");
1468 log_debug("received have-line from uid %d",
1470 err = ensure_client_is_reading(client);
1473 err = forward_have(client, &imsg);
1476 client->accept_flush_pkt = 1;
1478 case GOTD_IMSG_FLUSH:
1479 if (client->state == GOTD_STATE_EXPECT_WANT ||
1480 client->state == GOTD_STATE_EXPECT_HAVE) {
1481 err = ensure_client_is_reading(client);
1484 } else if (client->state ==
1485 GOTD_STATE_EXPECT_MORE_REF_UPDATES) {
1486 err = ensure_client_is_writing(client);
1489 } else if (client->state != GOTD_STATE_EXPECT_DONE) {
1490 err = got_error_msg(GOT_ERR_BAD_REQUEST,
1491 "unexpected flush-pkt received");
1494 if (!client->accept_flush_pkt) {
1495 err = got_error_msg(GOT_ERR_BAD_REQUEST,
1496 "unexpected flush-pkt received");
1501 * Accept just one flush packet at a time.
1502 * Future client state transitions will set this flag
1503 * again if another flush packet is expected.
1505 client->accept_flush_pkt = 0;
1507 log_debug("received flush-pkt from uid %d",
1509 if (client->state == GOTD_STATE_EXPECT_WANT) {
1510 client->state = GOTD_STATE_EXPECT_HAVE;
1511 log_debug("uid %d: expecting have-lines",
1513 } else if (client->state == GOTD_STATE_EXPECT_HAVE) {
1514 client->state = GOTD_STATE_EXPECT_DONE;
1515 client->accept_flush_pkt = 1;
1516 log_debug("uid %d: expecting 'done'",
1518 } else if (client->state ==
1519 GOTD_STATE_EXPECT_MORE_REF_UPDATES) {
1520 client->state = GOTD_STATE_EXPECT_PACKFILE;
1521 log_debug("uid %d: expecting packfile",
1523 err = recv_packfile(client);
1524 } else if (client->state != GOTD_STATE_EXPECT_DONE) {
1525 /* should not happen, see above */
1526 err = got_error_msg(GOT_ERR_BAD_REQUEST,
1527 "unexpected client state");
1531 case GOTD_IMSG_DONE:
1532 if (client->state != GOTD_STATE_EXPECT_HAVE &&
1533 client->state != GOTD_STATE_EXPECT_DONE) {
1534 err = got_error_msg(GOT_ERR_BAD_REQUEST,
1535 "unexpected flush-pkt received");
1538 log_debug("received 'done' from uid %d", client->euid);
1539 err = ensure_client_is_reading(client);
1542 client->state = GOTD_STATE_DONE;
1543 client->accept_flush_pkt = 1;
1544 err = send_packfile(client);
1547 log_debug("unexpected imsg %d", imsg.hdr.type);
1548 err = got_error(GOT_ERR_PRIVSEP_MSG);
1556 if (err->code != GOT_ERR_EOF ||
1557 client->state != GOTD_STATE_EXPECT_PACKFILE)
1558 disconnect_on_error(client, err);
1560 gotd_imsg_event_add(iev);
1561 evtimer_add(&client->tmo, &gotd_session.request_timeout);
1565 static const struct got_error *
1566 list_refs_request(void)
1568 static const struct got_error *err;
1569 struct gotd_session_client *client = &gotd_session_client;
1570 struct gotd_imsgev *iev = &client->repo_child_iev;
1571 struct gotd_imsg_list_refs_internal ilref;
1574 if (client->state != GOTD_STATE_EXPECT_LIST_REFS)
1575 return got_error(GOT_ERR_PRIVSEP_MSG);
1577 memset(&ilref, 0, sizeof(ilref));
1578 ilref.client_id = client->id;
1580 fd = dup(client->fd);
1582 return got_error_from_errno("dup");
1584 if (gotd_imsg_compose_event(iev, GOTD_IMSG_LIST_REFS_INTERNAL,
1585 gotd_session.proc_id, fd, &ilref, sizeof(ilref)) == -1) {
1586 err = got_error_from_errno("imsg compose LIST_REFS_INTERNAL");
1591 client->state = GOTD_STATE_EXPECT_CAPABILITIES;
1592 log_debug("uid %d: expecting capabilities", client->euid);
1596 static const struct got_error *
1597 recv_connect(struct imsg *imsg)
1599 struct gotd_session_client *client = &gotd_session_client;
1600 struct gotd_imsg_connect iconnect;
1603 if (client->state != GOTD_STATE_EXPECT_LIST_REFS)
1604 return got_error(GOT_ERR_PRIVSEP_MSG);
1606 datalen = imsg->hdr.len - IMSG_HEADER_SIZE;
1607 if (datalen < sizeof(iconnect))
1608 return got_error(GOT_ERR_PRIVSEP_LEN);
1609 memcpy(&iconnect, imsg->data, sizeof(iconnect));
1610 if (iconnect.username_len == 0 ||
1611 datalen != sizeof(iconnect) + iconnect.username_len)
1612 return got_error(GOT_ERR_PRIVSEP_LEN);
1614 client->euid = iconnect.euid;
1615 client->egid = iconnect.egid;
1616 client->fd = imsg_get_fd(imsg);
1617 if (client->fd == -1)
1618 return got_error(GOT_ERR_PRIVSEP_NO_FD);
1620 client->username = strndup(imsg->data + sizeof(iconnect),
1621 iconnect.username_len);
1622 if (client->username == NULL)
1623 return got_error_from_errno("strndup");
1625 imsg_init(&client->iev.ibuf, client->fd);
1626 client->iev.handler = session_dispatch_client;
1627 client->iev.events = EV_READ;
1628 client->iev.handler_arg = NULL;
1629 event_set(&client->iev.ev, client->iev.ibuf.fd, EV_READ,
1630 session_dispatch_client, &client->iev);
1631 gotd_imsg_event_add(&client->iev);
1632 evtimer_set(&client->tmo, gotd_request_timeout, client);
1638 session_dispatch_notifier(int fd, short event, void *arg)
1640 const struct got_error *err;
1641 struct gotd_session_client *client = &gotd_session_client;
1642 struct gotd_imsgev *iev = arg;
1643 struct imsgbuf *ibuf = &iev->ibuf;
1647 struct gotd_session_notif *notif;
1649 if (event & EV_READ) {
1650 if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN)
1651 fatal("imsg_read error");
1653 /* Connection closed. */
1659 if (event & EV_WRITE) {
1660 n = msgbuf_write(&ibuf->w);
1661 if (n == -1 && errno != EAGAIN)
1662 fatal("msgbuf_write");
1664 /* Connection closed. */
1671 if ((n = imsg_get(ibuf, &imsg)) == -1)
1672 fatal("%s: imsg_get error", __func__);
1673 if (n == 0) /* No more messages. */
1676 switch (imsg.hdr.type) {
1677 case GOTD_IMSG_NOTIFICATION_SENT:
1678 if (client->state != GOTD_STATE_NOTIFY) {
1679 log_warn("unexpected imsg %d", imsg.hdr.type);
1682 notif = STAILQ_FIRST(¬ifications);
1683 if (notif == NULL) {
1685 break; /* NOTREACHED */
1687 /* Request content for the next notification. */
1688 err = request_notification(notif);
1690 log_warn("could not send notification: %s",
1696 log_debug("unexpected imsg %d", imsg.hdr.type);
1704 gotd_imsg_event_add(iev);
1706 /* This pipe is dead. Remove its event handler */
1707 event_del(&iev->ev);
1708 imsg_clear(&iev->ibuf);
1709 imsg_init(&iev->ibuf, -1);
1713 static const struct got_error *
1714 recv_notifier(struct imsg *imsg)
1716 struct gotd_imsgev *iev = &gotd_session.notifier_iev;
1717 struct gotd_session_client *client = &gotd_session_client;
1721 if (client->state != GOTD_STATE_EXPECT_LIST_REFS)
1722 return got_error(GOT_ERR_PRIVSEP_MSG);
1724 /* We should already have received a pipe to the listener. */
1725 if (client->fd == -1)
1726 return got_error(GOT_ERR_PRIVSEP_MSG);
1728 datalen = imsg->hdr.len - IMSG_HEADER_SIZE;
1730 return got_error(GOT_ERR_PRIVSEP_LEN);
1732 fd = imsg_get_fd(imsg);
1734 return NULL; /* notifications unused */
1736 imsg_init(&iev->ibuf, fd);
1737 iev->handler = session_dispatch_notifier;
1738 iev->events = EV_READ;
1739 iev->handler_arg = NULL;
1740 event_set(&iev->ev, iev->ibuf.fd, EV_READ,
1741 session_dispatch_notifier, iev);
1742 gotd_imsg_event_add(iev);
1747 static const struct got_error *
1748 recv_repo_child(struct imsg *imsg)
1750 struct gotd_imsg_connect_repo_child ichild;
1751 struct gotd_session_client *client = &gotd_session_client;
1755 if (client->state != GOTD_STATE_EXPECT_LIST_REFS)
1756 return got_error(GOT_ERR_PRIVSEP_MSG);
1758 /* We should already have received a pipe to the listener. */
1759 if (client->fd == -1)
1760 return got_error(GOT_ERR_PRIVSEP_MSG);
1762 datalen = imsg->hdr.len - IMSG_HEADER_SIZE;
1763 if (datalen != sizeof(ichild))
1764 return got_error(GOT_ERR_PRIVSEP_LEN);
1766 memcpy(&ichild, imsg->data, sizeof(ichild));
1768 client->id = ichild.client_id;
1769 if (ichild.proc_id == PROC_REPO_WRITE)
1770 client->is_writing = 1;
1771 else if (ichild.proc_id == PROC_REPO_READ)
1772 client->is_writing = 0;
1774 return got_error_msg(GOT_ERR_PRIVSEP_MSG,
1775 "bad child process type");
1777 fd = imsg_get_fd(imsg);
1779 return got_error(GOT_ERR_PRIVSEP_NO_FD);
1781 imsg_init(&client->repo_child_iev.ibuf, fd);
1782 client->repo_child_iev.handler = session_dispatch_repo_child;
1783 client->repo_child_iev.events = EV_READ;
1784 client->repo_child_iev.handler_arg = NULL;
1785 event_set(&client->repo_child_iev.ev, client->repo_child_iev.ibuf.fd,
1786 EV_READ, session_dispatch_repo_child, &client->repo_child_iev);
1787 gotd_imsg_event_add(&client->repo_child_iev);
1789 /* The "recvfd" pledge promise is no longer needed. */
1790 if (pledge("stdio rpath wpath cpath sendfd fattr flock", NULL) == -1)
1797 session_dispatch(int fd, short event, void *arg)
1799 struct gotd_imsgev *iev = arg;
1800 struct imsgbuf *ibuf = &iev->ibuf;
1801 struct gotd_session_client *client = &gotd_session_client;
1806 if (event & EV_READ) {
1807 if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN)
1808 fatal("imsg_read error");
1810 /* Connection closed. */
1816 if (event & EV_WRITE) {
1817 n = msgbuf_write(&ibuf->w);
1818 if (n == -1 && errno != EAGAIN)
1819 fatal("msgbuf_write");
1821 /* Connection closed. */
1828 const struct got_error *err = NULL;
1829 uint32_t client_id = 0;
1830 int do_disconnect = 0, do_list_refs = 0;
1832 if ((n = imsg_get(ibuf, &imsg)) == -1)
1833 fatal("%s: imsg_get error", __func__);
1834 if (n == 0) /* No more messages. */
1837 switch (imsg.hdr.type) {
1838 case GOTD_IMSG_ERROR:
1840 err = gotd_imsg_recv_error(&client_id, &imsg);
1842 case GOTD_IMSG_CONNECT:
1843 err = recv_connect(&imsg);
1845 case GOTD_IMSG_DISCONNECT:
1848 case GOTD_IMSG_CONNECT_NOTIFIER:
1849 err = recv_notifier(&imsg);
1851 case GOTD_IMSG_CONNECT_REPO_CHILD:
1852 err = recv_repo_child(&imsg);
1858 log_debug("unexpected imsg %d", imsg.hdr.type);
1863 if (do_disconnect) {
1865 disconnect_on_error(client, err);
1868 } else if (do_list_refs)
1869 err = list_refs_request();
1872 log_warnx("uid %d: %s", client->euid, err->msg);
1876 gotd_imsg_event_add(iev);
1878 /* This pipe is dead. Remove its event handler */
1879 event_del(&iev->ev);
1880 event_loopexit(NULL);
1885 session_main(const char *title, const char *repo_path,
1886 int *pack_fds, int *temp_fds, struct timeval *request_timeout,
1887 struct gotd_repo *repo_cfg, enum gotd_procid proc_id)
1889 const struct got_error *err = NULL;
1890 struct event evsigint, evsigterm, evsighup, evsigusr1;
1892 STAILQ_INIT(¬ifications);
1894 gotd_session.title = title;
1895 gotd_session.pid = getpid();
1896 gotd_session.pack_fds = pack_fds;
1897 gotd_session.temp_fds = temp_fds;
1898 memcpy(&gotd_session.request_timeout, request_timeout,
1899 sizeof(gotd_session.request_timeout));
1900 gotd_session.repo_cfg = repo_cfg;
1901 gotd_session.proc_id = proc_id;
1903 imsg_init(&gotd_session.notifier_iev.ibuf, -1);
1905 err = got_repo_open(&gotd_session.repo, repo_path, NULL, pack_fds);
1908 if (!got_repo_is_bare(gotd_session.repo)) {
1909 err = got_error_msg(GOT_ERR_NOT_GIT_REPO,
1910 "bare git repository required");
1914 got_repo_temp_fds_set(gotd_session.repo, temp_fds);
1916 signal_set(&evsigint, SIGINT, gotd_session_sighdlr, NULL);
1917 signal_set(&evsigterm, SIGTERM, gotd_session_sighdlr, NULL);
1918 signal_set(&evsighup, SIGHUP, gotd_session_sighdlr, NULL);
1919 signal_set(&evsigusr1, SIGUSR1, gotd_session_sighdlr, NULL);
1920 signal(SIGPIPE, SIG_IGN);
1922 signal_add(&evsigint, NULL);
1923 signal_add(&evsigterm, NULL);
1924 signal_add(&evsighup, NULL);
1925 signal_add(&evsigusr1, NULL);
1927 gotd_session_client.state = GOTD_STATE_EXPECT_LIST_REFS;
1928 gotd_session_client.fd = -1;
1929 gotd_session_client.nref_updates = -1;
1930 gotd_session_client.delta_cache_fd = -1;
1931 gotd_session_client.accept_flush_pkt = 1;
1933 imsg_init(&gotd_session.parent_iev.ibuf, GOTD_FILENO_MSG_PIPE);
1934 gotd_session.parent_iev.handler = session_dispatch;
1935 gotd_session.parent_iev.events = EV_READ;
1936 gotd_session.parent_iev.handler_arg = NULL;
1937 event_set(&gotd_session.parent_iev.ev, gotd_session.parent_iev.ibuf.fd,
1938 EV_READ, session_dispatch, &gotd_session.parent_iev);
1939 if (gotd_imsg_compose_event(&gotd_session.parent_iev,
1940 GOTD_IMSG_CLIENT_SESSION_READY, gotd_session.proc_id,
1941 -1, NULL, 0) == -1) {
1942 err = got_error_from_errno("imsg compose CLIENT_SESSION_READY");
1949 log_warnx("%s: %s", title, err->msg);
1950 gotd_session_shutdown();
1954 gotd_session_shutdown(void)
1956 struct gotd_session_notif *notif;
1958 log_debug("shutting down");
1960 while (!STAILQ_EMPTY(¬ifications)) {
1961 notif = STAILQ_FIRST(¬ifications);
1962 STAILQ_REMOVE_HEAD(¬ifications, entry);
1963 if (notif->fd != -1)
1965 free(notif->refname);
1969 if (gotd_session.repo)
1970 got_repo_close(gotd_session.repo);
1971 got_repo_pack_fds_close(gotd_session.pack_fds);
1972 got_repo_temp_fds_close(gotd_session.temp_fds);
1973 free(gotd_session_client.username);