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/socket.h>
35 #include "got_compat.h"
37 #include "got_error.h"
38 #include "got_repository.h"
39 #include "got_object.h"
41 #include "got_reference.h"
42 #include "got_opentemp.h"
44 #include "got_lib_hash.h"
45 #include "got_lib_delta.h"
46 #include "got_lib_object.h"
47 #include "got_lib_object_cache.h"
48 #include "got_lib_pack.h"
49 #include "got_lib_repository.h"
50 #include "got_lib_gitproto.h"
54 #include "session_write.h"
56 struct gotd_session_notif
{
57 STAILQ_ENTRY(gotd_session_notif
) entry
;
59 enum gotd_notification_action action
;
61 struct got_object_id old_id
;
62 struct got_object_id new_id
;
64 STAILQ_HEAD(gotd_session_notifications
, gotd_session_notif
) notifications
;
66 enum gotd_session_write_state
{
67 GOTD_STATE_EXPECT_LIST_REFS
,
68 GOTD_STATE_EXPECT_CAPABILITIES
,
69 GOTD_STATE_EXPECT_REF_UPDATE
,
70 GOTD_STATE_EXPECT_MORE_REF_UPDATES
,
71 GOTD_STATE_EXPECT_PACKFILE
,
75 static struct gotd_session_write
{
78 struct got_repository
*repo
;
79 struct gotd_repo
*repo_cfg
;
82 struct gotd_imsgev parent_iev
;
83 struct gotd_imsgev notifier_iev
;
84 struct timeval request_timeout
;
85 enum gotd_session_write_state state
;
86 struct gotd_imsgev repo_child_iev
;
89 static struct gotd_session_client
{
90 struct gotd_client_capability
*capabilities
;
96 struct gotd_imsgev iev
;
104 int accept_flush_pkt
;
105 int flush_disconnect
;
106 } gotd_session_client
;
108 static void session_write_shutdown(void);
111 disconnect(struct gotd_session_client
*client
)
113 log_debug("uid %d: disconnecting", client
->euid
);
115 if (gotd_imsg_compose_event(&gotd_session
.parent_iev
,
116 GOTD_IMSG_DISCONNECT
, PROC_SESSION_WRITE
, -1, NULL
, 0) == -1)
117 log_warn("imsg compose DISCONNECT");
119 imsgbuf_clear(&gotd_session
.repo_child_iev
.ibuf
);
120 event_del(&gotd_session
.repo_child_iev
.ev
);
121 evtimer_del(&client
->tmo
);
123 if (client
->delta_cache_fd
!= -1)
124 close(client
->delta_cache_fd
);
125 if (client
->packfile_path
) {
126 if (unlink(client
->packfile_path
) == -1 && errno
!= ENOENT
)
127 log_warn("unlink %s: ", client
->packfile_path
);
128 free(client
->packfile_path
);
130 if (client
->packidx_path
) {
131 if (unlink(client
->packidx_path
) == -1 && errno
!= ENOENT
)
132 log_warn("unlink %s: ", client
->packidx_path
);
133 free(client
->packidx_path
);
135 free(client
->capabilities
);
137 session_write_shutdown();
141 disconnect_on_error(struct gotd_session_client
*client
,
142 const struct got_error
*err
)
146 if (err
->code
!= GOT_ERR_EOF
) {
147 log_warnx("uid %d: %s", client
->euid
, err
->msg
);
148 if (imsgbuf_init(&ibuf
, client
->fd
) == -1) {
149 log_warn("imsgbuf_init");
151 gotd_imsg_send_error(&ibuf
, 0, PROC_SESSION_WRITE
,
153 imsgbuf_clear(&ibuf
);
161 gotd_request_timeout(int fd
, short events
, void *arg
)
163 struct gotd_session_client
*client
= arg
;
165 log_warnx("disconnecting uid %d due to timeout", client
->euid
);
170 session_write_sighdlr(int sig
, short event
, void *arg
)
173 * Normal signal handler rules don't apply because libevent
179 log_info("%s: ignoring SIGHUP", __func__
);
182 log_info("%s: ignoring SIGUSR1", __func__
);
186 session_write_shutdown();
190 fatalx("unexpected signal");
194 static const struct got_error
*
195 recv_packfile_install(struct imsg
*imsg
)
197 struct gotd_imsg_packfile_install inst
;
200 log_debug("packfile-install received");
202 datalen
= imsg
->hdr
.len
- IMSG_HEADER_SIZE
;
203 if (datalen
!= sizeof(inst
))
204 return got_error(GOT_ERR_PRIVSEP_LEN
);
205 memcpy(&inst
, imsg
->data
, sizeof(inst
));
210 static const struct got_error
*
211 recv_ref_updates_start(struct imsg
*imsg
)
213 struct gotd_imsg_ref_updates_start istart
;
216 log_debug("ref-updates-start received");
218 datalen
= imsg
->hdr
.len
- IMSG_HEADER_SIZE
;
219 if (datalen
!= sizeof(istart
))
220 return got_error(GOT_ERR_PRIVSEP_LEN
);
221 memcpy(&istart
, imsg
->data
, sizeof(istart
));
226 static const struct got_error
*
227 recv_ref_update(struct imsg
*imsg
)
229 struct gotd_imsg_ref_update iref
;
232 log_debug("ref-update received");
234 datalen
= imsg
->hdr
.len
- IMSG_HEADER_SIZE
;
235 if (datalen
< sizeof(iref
))
236 return got_error(GOT_ERR_PRIVSEP_LEN
);
237 memcpy(&iref
, imsg
->data
, sizeof(iref
));
242 static const struct got_error
*
243 send_ref_update_ok(struct gotd_session_client
*client
,
244 struct gotd_imsg_ref_update
*iref
, const char *refname
)
246 struct gotd_imsg_ref_update_ok iok
;
247 struct gotd_imsgev
*iev
= &client
->iev
;
251 memset(&iok
, 0, sizeof(iok
));
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 PROC_SESSION_WRITE
, 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");
267 imsg_close(&iev
->ibuf
, wbuf
);
268 gotd_imsg_event_add(iev
);
273 send_refs_updated(struct gotd_session_client
*client
)
275 if (gotd_imsg_compose_event(&client
->iev
, GOTD_IMSG_REFS_UPDATED
,
276 PROC_SESSION_WRITE
, -1, NULL
, 0) == -1)
277 log_warn("imsg compose REFS_UPDATED");
280 static const struct got_error
*
281 send_ref_update_ng(struct gotd_session_client
*client
,
282 struct gotd_imsg_ref_update
*iref
, const char *refname
,
285 const struct got_error
*ng_err
;
286 struct gotd_imsg_ref_update_ng ing
;
287 struct gotd_imsgev
*iev
= &client
->iev
;
291 memset(&ing
, 0, sizeof(ing
));
292 memcpy(ing
.old_id
, iref
->old_id
, SHA1_DIGEST_LENGTH
);
293 memcpy(ing
.new_id
, iref
->new_id
, SHA1_DIGEST_LENGTH
);
294 ing
.name_len
= strlen(refname
);
296 ng_err
= got_error_fmt(GOT_ERR_REF_BUSY
, "%s", reason
);
297 ing
.reason_len
= strlen(ng_err
->msg
);
299 len
= sizeof(ing
) + ing
.name_len
+ ing
.reason_len
;
300 wbuf
= imsg_create(&iev
->ibuf
, GOTD_IMSG_REF_UPDATE_NG
,
301 PROC_SESSION_WRITE
, gotd_session
.pid
, len
);
303 return got_error_from_errno("imsg_create REF_UPDATE_NG");
305 if (imsg_add(wbuf
, &ing
, sizeof(ing
)) == -1)
306 return got_error_from_errno("imsg_add REF_UPDATE_NG");
307 if (imsg_add(wbuf
, refname
, ing
.name_len
) == -1)
308 return got_error_from_errno("imsg_add REF_UPDATE_NG");
309 if (imsg_add(wbuf
, ng_err
->msg
, ing
.reason_len
) == -1)
310 return got_error_from_errno("imsg_add REF_UPDATE_NG");
312 imsg_close(&iev
->ibuf
, wbuf
);
313 gotd_imsg_event_add(iev
);
317 static const struct got_error
*
318 install_pack(struct gotd_session_client
*client
, const char *repo_path
,
321 const struct got_error
*err
= NULL
;
322 struct gotd_imsg_packfile_install inst
;
323 char hex
[SHA1_DIGEST_STRING_LENGTH
];
325 char *packfile_path
= NULL
, *packidx_path
= NULL
;
327 datalen
= imsg
->hdr
.len
- IMSG_HEADER_SIZE
;
328 if (datalen
!= sizeof(inst
))
329 return got_error(GOT_ERR_PRIVSEP_LEN
);
330 memcpy(&inst
, imsg
->data
, sizeof(inst
));
332 if (client
->packfile_path
== NULL
)
333 return got_error_msg(GOT_ERR_BAD_REQUEST
,
334 "client has no pack file");
335 if (client
->packidx_path
== NULL
)
336 return got_error_msg(GOT_ERR_BAD_REQUEST
,
337 "client has no pack file index");
339 if (got_sha1_digest_to_str(inst
.pack_sha1
, hex
, sizeof(hex
)) == NULL
)
340 return got_error_msg(GOT_ERR_NO_SPACE
,
341 "could not convert pack file SHA1 to hex");
343 if (asprintf(&packfile_path
, "/%s/%s/pack-%s.pack",
344 repo_path
, GOT_OBJECTS_PACK_DIR
, hex
) == -1) {
345 err
= got_error_from_errno("asprintf");
349 if (asprintf(&packidx_path
, "/%s/%s/pack-%s.idx",
350 repo_path
, GOT_OBJECTS_PACK_DIR
, hex
) == -1) {
351 err
= got_error_from_errno("asprintf");
355 if (rename(client
->packfile_path
, packfile_path
) == -1) {
356 err
= got_error_from_errno3("rename", client
->packfile_path
,
361 free(client
->packfile_path
);
362 client
->packfile_path
= NULL
;
364 if (rename(client
->packidx_path
, packidx_path
) == -1) {
365 err
= got_error_from_errno3("rename", client
->packidx_path
,
370 /* Ensure we re-read the pack index list upon next access. */
371 gotd_session
.repo
->pack_path_mtime
.tv_sec
= 0;
372 gotd_session
.repo
->pack_path_mtime
.tv_nsec
= 0;
374 free(client
->packidx_path
);
375 client
->packidx_path
= NULL
;
382 static const struct got_error
*
383 begin_ref_updates(struct gotd_session_client
*client
, struct imsg
*imsg
)
385 struct gotd_imsg_ref_updates_start istart
;
388 if (client
->nref_updates
!= -1)
389 return got_error(GOT_ERR_PRIVSEP_MSG
);
391 datalen
= imsg
->hdr
.len
- IMSG_HEADER_SIZE
;
392 if (datalen
!= sizeof(istart
))
393 return got_error(GOT_ERR_PRIVSEP_LEN
);
394 memcpy(&istart
, imsg
->data
, sizeof(istart
));
396 if (istart
.nref_updates
<= 0)
397 return got_error(GOT_ERR_PRIVSEP_MSG
);
399 client
->nref_updates
= istart
.nref_updates
;
403 static const struct got_error
*
404 validate_namespace(const char *namespace)
406 size_t len
= strlen(namespace);
408 if (len
< 5 || strncmp("refs/", namespace, 5) != 0 ||
409 namespace[len
- 1] != '/') {
410 return got_error_fmt(GOT_ERR_BAD_REF_NAME
,
411 "reference namespace '%s'", namespace);
417 static const struct got_error
*
418 queue_notification(struct got_object_id
*old_id
, struct got_object_id
*new_id
,
419 struct got_repository
*repo
, struct got_reference
*ref
)
421 const struct got_error
*err
= NULL
;
422 struct gotd_repo
*repo_cfg
= gotd_session
.repo_cfg
;
423 struct gotd_imsgev
*iev
= &gotd_session
.repo_child_iev
;
424 struct got_pathlist_entry
*pe
;
425 struct gotd_session_notif
*notif
;
427 if (iev
->ibuf
.fd
== -1 ||
428 STAILQ_EMPTY(&repo_cfg
->notification_targets
))
429 return NULL
; /* notifications unused */
431 TAILQ_FOREACH(pe
, &repo_cfg
->notification_refs
, entry
) {
432 const char *refname
= pe
->path
;
433 if (strcmp(got_ref_get_name(ref
), refname
) == 0)
437 TAILQ_FOREACH(pe
, &repo_cfg
->notification_ref_namespaces
,
439 const char *namespace = pe
->path
;
441 err
= validate_namespace(namespace);
444 if (strncmp(namespace, got_ref_get_name(ref
),
445 strlen(namespace)) == 0)
451 * If a branch or a reference namespace was specified in the
452 * configuration file then only send notifications if a match
455 if (pe
== NULL
&& (!TAILQ_EMPTY(&repo_cfg
->notification_refs
) ||
456 !TAILQ_EMPTY(&repo_cfg
->notification_ref_namespaces
)))
459 notif
= calloc(1, sizeof(*notif
));
461 return got_error_from_errno("calloc");
466 notif
->action
= GOTD_NOTIF_ACTION_CREATED
;
467 else if (new_id
== NULL
)
468 notif
->action
= GOTD_NOTIF_ACTION_REMOVED
;
470 notif
->action
= GOTD_NOTIF_ACTION_CHANGED
;
473 memcpy(¬if
->old_id
, old_id
, sizeof(notif
->old_id
));
475 memcpy(¬if
->new_id
, new_id
, sizeof(notif
->new_id
));
477 notif
->refname
= strdup(got_ref_get_name(ref
));
478 if (notif
->refname
== NULL
) {
479 err
= got_error_from_errno("strdup");
483 STAILQ_INSERT_TAIL(¬ifications
, notif
, entry
);
486 free(notif
->refname
);
492 /* Forward notification content to the NOTIFY process. */
493 static const struct got_error
*
494 forward_notification(struct gotd_session_client
*client
, struct imsg
*imsg
)
496 const struct got_error
*err
= NULL
;
497 struct gotd_imsgev
*iev
= &gotd_session
.notifier_iev
;
498 struct gotd_session_notif
*notif
;
499 struct gotd_imsg_notification_content icontent
;
500 char *refname
= NULL
, *id_str
= NULL
;
502 struct gotd_imsg_notify inotify
;
506 memset(&inotify
, 0, sizeof(inotify
));
508 datalen
= imsg
->hdr
.len
- IMSG_HEADER_SIZE
;
509 if (datalen
< sizeof(icontent
))
510 return got_error(GOT_ERR_PRIVSEP_LEN
);
511 memcpy(&icontent
, imsg
->data
, sizeof(icontent
));
512 if (datalen
!= sizeof(icontent
) + icontent
.refname_len
)
513 return got_error(GOT_ERR_PRIVSEP_LEN
);
514 refname
= strndup(imsg
->data
+ sizeof(icontent
), icontent
.refname_len
);
516 return got_error_from_errno("strndup");
518 notif
= STAILQ_FIRST(¬ifications
);
520 return got_error(GOT_ERR_PRIVSEP_MSG
);
522 STAILQ_REMOVE(¬ifications
, notif
, gotd_session_notif
, entry
);
524 if (notif
->action
!= icontent
.action
|| notif
->fd
== -1 ||
525 strcmp(notif
->refname
, refname
) != 0) {
526 err
= got_error(GOT_ERR_PRIVSEP_MSG
);
529 if (notif
->action
== GOTD_NOTIF_ACTION_CREATED
) {
530 if (memcmp(¬if
->new_id
, &icontent
.new_id
,
531 sizeof(notif
->new_id
)) != 0) {
532 err
= got_error_msg(GOT_ERR_PRIVSEP_MSG
,
533 "received notification content for unknown event");
536 } else if (notif
->action
== GOTD_NOTIF_ACTION_REMOVED
) {
537 if (memcmp(¬if
->old_id
, &icontent
.old_id
,
538 sizeof(notif
->old_id
)) != 0) {
539 err
= got_error_msg(GOT_ERR_PRIVSEP_MSG
,
540 "received notification content for unknown event");
543 } else if (memcmp(¬if
->old_id
, &icontent
.old_id
,
544 sizeof(notif
->old_id
)) != 0 ||
545 memcmp(¬if
->new_id
, &icontent
.new_id
,
546 sizeof(notif
->old_id
)) != 0) {
547 err
= got_error_msg(GOT_ERR_PRIVSEP_MSG
,
548 "received notification content for unknown event");
552 switch (notif
->action
) {
553 case GOTD_NOTIF_ACTION_CREATED
:
555 err
= got_object_id_str(&id_str
, ¬if
->new_id
);
559 case GOTD_NOTIF_ACTION_REMOVED
:
561 err
= got_object_id_str(&id_str
, ¬if
->old_id
);
565 case GOTD_NOTIF_ACTION_CHANGED
:
567 err
= got_object_id_str(&id_str
, ¬if
->new_id
);
572 err
= got_error(GOT_ERR_PRIVSEP_MSG
);
576 strlcpy(inotify
.repo_name
, gotd_session
.repo_cfg
->name
,
577 sizeof(inotify
.repo_name
));
579 snprintf(inotify
.subject_line
, sizeof(inotify
.subject_line
),
580 "%s: %s %s %s: %.12s", gotd_session
.repo_cfg
->name
,
581 client
->username
, action
, notif
->refname
, id_str
);
583 inotify
.username_len
= strlen(client
->username
);
584 wbuf
= imsg_create(&iev
->ibuf
, GOTD_IMSG_NOTIFY
,
585 PROC_SESSION_WRITE
, gotd_session
.pid
,
586 sizeof(inotify
) + inotify
.username_len
);
588 err
= got_error_from_errno("imsg_create NOTIFY");
591 if (imsg_add(wbuf
, &inotify
, sizeof(inotify
)) == -1) {
592 err
= got_error_from_errno("imsg_add NOTIFY");
595 if (imsg_add(wbuf
, client
->username
, inotify
.username_len
) == -1) {
596 err
= got_error_from_errno("imsg_add NOTIFY");
600 ibuf_fd_set(wbuf
, notif
->fd
);
603 imsg_close(&iev
->ibuf
, wbuf
);
604 gotd_imsg_event_add(iev
);
614 /* Request notification content from REPO_WRITE process. */
615 static const struct got_error
*
616 request_notification(struct gotd_session_notif
*notif
)
618 const struct got_error
*err
= NULL
;
619 struct gotd_imsgev
*iev
= &gotd_session
.repo_child_iev
;
620 struct gotd_imsg_notification_content icontent
;
625 fd
= got_opentempfd();
627 return got_error_from_errno("got_opentemp");
629 memset(&icontent
, 0, sizeof(icontent
));
631 icontent
.action
= notif
->action
;
632 memcpy(&icontent
.old_id
, ¬if
->old_id
, sizeof(notif
->old_id
));
633 memcpy(&icontent
.new_id
, ¬if
->new_id
, sizeof(notif
->new_id
));
634 icontent
.refname_len
= strlen(notif
->refname
);
636 len
= sizeof(icontent
) + icontent
.refname_len
;
637 wbuf
= imsg_create(&iev
->ibuf
, GOTD_IMSG_NOTIFY
,
638 PROC_SESSION_WRITE
, gotd_session
.pid
, len
);
640 err
= got_error_from_errno("imsg_create NOTIFY");
643 if (imsg_add(wbuf
, &icontent
, sizeof(icontent
)) == -1) {
644 err
= got_error_from_errno("imsg_add NOTIFY");
647 if (imsg_add(wbuf
, notif
->refname
, icontent
.refname_len
) == -1) {
648 err
= got_error_from_errno("imsg_add NOTIFY");
653 if (notif
->fd
== -1) {
654 err
= got_error_from_errno("dup");
658 ibuf_fd_set(wbuf
, fd
);
661 imsg_close(&iev
->ibuf
, wbuf
);
662 gotd_imsg_event_add(iev
);
669 static const struct got_error
*
670 update_ref(int *shut
, struct gotd_session_client
*client
,
671 const char *repo_path
, struct imsg
*imsg
)
673 const struct got_error
*err
= NULL
;
674 struct got_repository
*repo
= gotd_session
.repo
;
675 struct got_reference
*ref
= NULL
;
676 struct gotd_imsg_ref_update iref
;
677 struct got_object_id old_id
, new_id
;
678 struct gotd_session_notif
*notif
;
679 struct got_object_id
*id
= NULL
;
680 char *refname
= NULL
;
683 char hex1
[SHA1_DIGEST_STRING_LENGTH
];
684 char hex2
[SHA1_DIGEST_STRING_LENGTH
];
686 log_debug("update-ref from uid %d", client
->euid
);
688 if (client
->nref_updates
<= 0)
689 return got_error(GOT_ERR_PRIVSEP_MSG
);
691 datalen
= imsg
->hdr
.len
- IMSG_HEADER_SIZE
;
692 if (datalen
< sizeof(iref
))
693 return got_error(GOT_ERR_PRIVSEP_LEN
);
694 memcpy(&iref
, imsg
->data
, sizeof(iref
));
695 if (datalen
!= sizeof(iref
) + iref
.name_len
)
696 return got_error(GOT_ERR_PRIVSEP_LEN
);
697 refname
= strndup(imsg
->data
+ sizeof(iref
), iref
.name_len
);
699 return got_error_from_errno("strndup");
701 log_debug("updating ref %s for uid %d", refname
, client
->euid
);
703 memset(&old_id
, 0, sizeof(old_id
));
704 memcpy(old_id
.hash
, iref
.old_id
, SHA1_DIGEST_LENGTH
);
705 memset(&new_id
, 0, sizeof(new_id
));
706 memcpy(new_id
.hash
, iref
.new_id
, SHA1_DIGEST_LENGTH
);
707 err
= got_repo_find_object_id(iref
.delete_ref
? &old_id
: &new_id
,
712 if (iref
.ref_is_new
) {
713 err
= got_ref_open(&ref
, repo
, refname
, 0);
715 if (err
->code
!= GOT_ERR_NOT_REF
)
717 err
= got_ref_alloc(&ref
, refname
, &new_id
);
720 err
= got_ref_write(ref
, repo
); /* will lock/unlock */
723 err
= queue_notification(NULL
, &new_id
, repo
, ref
);
727 err
= got_ref_resolve(&id
, repo
, ref
);
730 got_object_id_hex(&new_id
, hex1
, sizeof(hex1
));
731 got_object_id_hex(id
, hex2
, sizeof(hex2
));
732 err
= got_error_fmt(GOT_ERR_REF_BUSY
,
733 "Addition %s: %s failed; %s: %s has been "
734 "created by someone else while transaction "
736 got_ref_get_name(ref
), hex1
,
737 got_ref_get_name(ref
), hex2
);
740 } else if (iref
.delete_ref
) {
741 err
= got_ref_open(&ref
, repo
, refname
, 1 /* lock */);
746 err
= got_ref_resolve(&id
, repo
, ref
);
750 if (got_object_id_cmp(id
, &old_id
) != 0) {
751 got_object_id_hex(&old_id
, hex1
, sizeof(hex1
));
752 got_object_id_hex(id
, hex2
, sizeof(hex2
));
753 err
= got_error_fmt(GOT_ERR_REF_BUSY
,
754 "Deletion %s: %s failed; %s: %s has been "
755 "created by someone else while transaction "
757 got_ref_get_name(ref
), hex1
,
758 got_ref_get_name(ref
), hex2
);
762 err
= got_ref_delete(ref
, repo
);
765 err
= queue_notification(&old_id
, NULL
, repo
, ref
);
771 err
= got_ref_open(&ref
, repo
, refname
, 1 /* lock */);
776 err
= got_ref_resolve(&id
, repo
, ref
);
780 if (got_object_id_cmp(id
, &old_id
) != 0) {
781 got_object_id_hex(&old_id
, hex1
, sizeof(hex1
));
782 got_object_id_hex(id
, hex2
, sizeof(hex2
));
783 err
= got_error_fmt(GOT_ERR_REF_BUSY
,
784 "Update %s: %s failed; %s: %s has been "
785 "created by someone else while transaction "
787 got_ref_get_name(ref
), hex1
,
788 got_ref_get_name(ref
), hex2
);
792 if (got_object_id_cmp(&new_id
, &old_id
) != 0) {
793 err
= got_ref_change_ref(ref
, &new_id
);
796 err
= got_ref_write(ref
, repo
);
799 err
= queue_notification(&old_id
, &new_id
, repo
, ref
);
809 if (err
->code
== GOT_ERR_LOCKFILE_TIMEOUT
) {
810 err
= got_error_fmt(GOT_ERR_LOCKFILE_TIMEOUT
,
811 "could not acquire exclusive file lock for %s",
814 send_ref_update_ng(client
, &iref
, refname
, err
->msg
);
816 send_ref_update_ok(client
, &iref
, refname
);
818 if (client
->nref_updates
> 0) {
819 client
->nref_updates
--;
820 if (client
->nref_updates
== 0) {
821 send_refs_updated(client
);
822 notif
= STAILQ_FIRST(¬ifications
);
824 gotd_session
.state
= GOTD_STATE_NOTIFY
;
825 err
= request_notification(notif
);
827 log_warn("could not send notification: "
829 client
->flush_disconnect
= 1;
832 client
->flush_disconnect
= 1;
837 const struct got_error
*unlock_err
;
838 unlock_err
= got_ref_unlock(ref
);
839 if (unlock_err
&& err
== NULL
)
849 static const struct got_error
*
850 recv_notification_content(struct imsg
*imsg
)
852 struct gotd_imsg_notification_content inotif
;
855 datalen
= imsg
->hdr
.len
- IMSG_HEADER_SIZE
;
856 if (datalen
< sizeof(inotif
))
857 return got_error(GOT_ERR_PRIVSEP_LEN
);
858 memcpy(&inotif
, imsg
->data
, sizeof(inotif
));
864 session_dispatch_repo_child(int fd
, short event
, void *arg
)
866 const struct got_error
*err
= NULL
;
867 struct gotd_imsgev
*iev
= arg
;
868 struct imsgbuf
*ibuf
= &iev
->ibuf
;
869 struct gotd_session_client
*client
= &gotd_session_client
;
874 if (event
& EV_READ
) {
875 if ((n
= imsgbuf_read(ibuf
)) == -1)
876 fatal("imsg_read error");
878 /* Connection closed. */
884 if (event
& EV_WRITE
) {
885 err
= gotd_imsg_flush(ibuf
);
887 fatalx("%s", err
->msg
);
891 const struct got_error
*err
= NULL
;
892 uint32_t client_id
= 0;
893 int do_disconnect
= 0;
894 int do_ref_updates
= 0, do_ref_update
= 0;
895 int do_packfile_install
= 0, do_notify
= 0;
897 if ((n
= imsg_get(ibuf
, &imsg
)) == -1)
898 fatal("%s: imsg_get error", __func__
);
899 if (n
== 0) /* No more messages. */
902 switch (imsg
.hdr
.type
) {
903 case GOTD_IMSG_ERROR
:
905 err
= gotd_imsg_recv_error(&client_id
, &imsg
);
907 case GOTD_IMSG_PACKFILE_INSTALL
:
908 err
= recv_packfile_install(&imsg
);
910 do_packfile_install
= 1;
912 case GOTD_IMSG_REF_UPDATES_START
:
913 err
= recv_ref_updates_start(&imsg
);
917 case GOTD_IMSG_REF_UPDATE
:
918 err
= recv_ref_update(&imsg
);
922 case GOTD_IMSG_NOTIFY
:
923 err
= recv_notification_content(&imsg
);
928 log_debug("unexpected imsg %d", imsg
.hdr
.type
);
932 if (do_disconnect
|| err
) {
934 disconnect_on_error(client
, err
);
938 struct gotd_session_notif
*notif
;
940 if (do_packfile_install
)
941 err
= install_pack(client
,
942 gotd_session
.repo
->path
, &imsg
);
943 else if (do_ref_updates
)
944 err
= begin_ref_updates(client
, &imsg
);
945 else if (do_ref_update
)
946 err
= update_ref(&shut
, client
,
947 gotd_session
.repo
->path
, &imsg
);
949 err
= forward_notification(client
, &imsg
);
951 log_warnx("uid %d: %s", client
->euid
, err
->msg
);
953 notif
= STAILQ_FIRST(¬ifications
);
954 if (notif
&& do_notify
) {
955 /* Request content for next notification. */
956 err
= request_notification(notif
);
958 log_warn("could not send notification: "
968 gotd_imsg_event_add(iev
);
970 /* This pipe is dead. Remove its event handler */
972 event_loopexit(NULL
);
976 static const struct got_error
*
977 recv_capabilities(struct gotd_session_client
*client
, struct imsg
*imsg
)
979 struct gotd_imsg_capabilities icapas
;
982 datalen
= imsg
->hdr
.len
- IMSG_HEADER_SIZE
;
983 if (datalen
!= sizeof(icapas
))
984 return got_error(GOT_ERR_PRIVSEP_LEN
);
985 memcpy(&icapas
, imsg
->data
, sizeof(icapas
));
987 client
->ncapa_alloc
= icapas
.ncapabilities
;
988 client
->capabilities
= calloc(client
->ncapa_alloc
,
989 sizeof(*client
->capabilities
));
990 if (client
->capabilities
== NULL
) {
991 client
->ncapa_alloc
= 0;
992 return got_error_from_errno("calloc");
995 log_debug("expecting %zu capabilities from uid %d",
996 client
->ncapa_alloc
, client
->euid
);
1000 static const struct got_error
*
1001 recv_capability(struct gotd_session_client
*client
, struct imsg
*imsg
)
1003 struct gotd_imsg_capability icapa
;
1004 struct gotd_client_capability
*capa
;
1006 char *key
, *value
= NULL
;
1008 if (client
->capabilities
== NULL
||
1009 client
->ncapabilities
>= client
->ncapa_alloc
) {
1010 return got_error_msg(GOT_ERR_BAD_REQUEST
,
1011 "unexpected capability received");
1014 memset(&icapa
, 0, sizeof(icapa
));
1016 datalen
= imsg
->hdr
.len
- IMSG_HEADER_SIZE
;
1017 if (datalen
< sizeof(icapa
))
1018 return got_error(GOT_ERR_PRIVSEP_LEN
);
1019 memcpy(&icapa
, imsg
->data
, sizeof(icapa
));
1021 if (datalen
!= sizeof(icapa
) + icapa
.key_len
+ icapa
.value_len
)
1022 return got_error(GOT_ERR_PRIVSEP_LEN
);
1024 key
= strndup(imsg
->data
+ sizeof(icapa
), icapa
.key_len
);
1026 return got_error_from_errno("strndup");
1027 if (icapa
.value_len
> 0) {
1028 value
= strndup(imsg
->data
+ sizeof(icapa
) + icapa
.key_len
,
1030 if (value
== NULL
) {
1032 return got_error_from_errno("strndup");
1036 capa
= &client
->capabilities
[client
->ncapabilities
++];
1038 capa
->value
= value
;
1041 log_debug("uid %d: capability %s=%s", client
->euid
, key
, value
);
1043 log_debug("uid %d: capability %s", client
->euid
, key
);
1048 static const struct got_error
*
1049 forward_ref_update(struct gotd_session_client
*client
, struct imsg
*imsg
)
1051 const struct got_error
*err
= NULL
;
1052 struct gotd_imsg_ref_update ireq
;
1053 struct gotd_imsg_ref_update
*iref
= NULL
;
1056 datalen
= imsg
->hdr
.len
- IMSG_HEADER_SIZE
;
1057 if (datalen
< sizeof(ireq
))
1058 return got_error(GOT_ERR_PRIVSEP_LEN
);
1059 memcpy(&ireq
, imsg
->data
, sizeof(ireq
));
1060 if (datalen
!= sizeof(ireq
) + ireq
.name_len
)
1061 return got_error(GOT_ERR_PRIVSEP_LEN
);
1063 iref
= malloc(datalen
);
1065 return got_error_from_errno("malloc");
1066 memcpy(iref
, imsg
->data
, datalen
);
1068 if (gotd_imsg_compose_event(&gotd_session
.repo_child_iev
,
1069 GOTD_IMSG_REF_UPDATE
, PROC_SESSION_WRITE
, -1,
1070 iref
, datalen
) == -1)
1071 err
= got_error_from_errno("imsg compose REF_UPDATE");
1077 client_has_capability(struct gotd_session_client
*client
, const char *capastr
)
1079 struct gotd_client_capability
*capa
;
1082 if (client
->ncapabilities
== 0)
1085 for (i
= 0; i
< client
->ncapabilities
; i
++) {
1086 capa
= &client
->capabilities
[i
];
1087 if (strcmp(capa
->key
, capastr
) == 0)
1094 static const struct got_error
*
1095 recv_packfile(struct gotd_session_client
*client
)
1097 const struct got_error
*err
= NULL
;
1098 struct gotd_imsg_recv_packfile ipack
;
1099 char *basepath
= NULL
, *pack_path
= NULL
, *idx_path
= NULL
;
1100 int packfd
= -1, idxfd
= -1;
1101 int pipe
[2] = { -1, -1 };
1103 if (client
->packfile_path
) {
1104 return got_error_fmt(GOT_ERR_PRIVSEP_MSG
,
1105 "uid %d already has a pack file", client
->euid
);
1108 if (socketpair(AF_UNIX
, SOCK_STREAM
, PF_UNSPEC
, pipe
) == -1)
1109 return got_error_from_errno("socketpair");
1111 /* Send pack pipe end 0 to repo child process. */
1112 if (gotd_imsg_compose_event(&gotd_session
.repo_child_iev
,
1113 GOTD_IMSG_PACKFILE_PIPE
, PROC_SESSION_WRITE
, pipe
[0],
1115 err
= got_error_from_errno("imsg compose PACKFILE_PIPE");
1121 /* Send pack pipe end 1 to gotsh(1) (expects just an fd, no data). */
1122 if (gotd_imsg_compose_event(&client
->iev
,
1123 GOTD_IMSG_PACKFILE_PIPE
, PROC_SESSION_WRITE
, pipe
[1],
1125 err
= got_error_from_errno("imsg compose PACKFILE_PIPE");
1128 if (asprintf(&basepath
, "%s/%s/receiving-from-uid-%d.pack",
1129 got_repo_get_path(gotd_session
.repo
), GOT_OBJECTS_PACK_DIR
,
1130 client
->euid
) == -1) {
1131 err
= got_error_from_errno("asprintf");
1135 err
= got_opentemp_named_fd(&pack_path
, &packfd
, basepath
, "");
1138 if (fchmod(packfd
, GOT_DEFAULT_PACK_MODE
) == -1) {
1139 err
= got_error_from_errno2("fchmod", pack_path
);
1144 if (asprintf(&basepath
, "%s/%s/receiving-from-uid-%d.idx",
1145 got_repo_get_path(gotd_session
.repo
), GOT_OBJECTS_PACK_DIR
,
1146 client
->euid
) == -1) {
1147 err
= got_error_from_errno("asprintf");
1151 err
= got_opentemp_named_fd(&idx_path
, &idxfd
, basepath
, "");
1154 if (fchmod(idxfd
, GOT_DEFAULT_PACK_MODE
) == -1) {
1155 err
= got_error_from_errno2("fchmod", idx_path
);
1159 if (gotd_imsg_compose_event(&gotd_session
.repo_child_iev
,
1160 GOTD_IMSG_PACKIDX_FILE
, PROC_SESSION_WRITE
,
1161 idxfd
, NULL
, 0) == -1) {
1162 err
= got_error_from_errno("imsg compose PACKIDX_FILE");
1168 memset(&ipack
, 0, sizeof(ipack
));
1169 if (client_has_capability(client
, GOT_CAPA_REPORT_STATUS
))
1170 ipack
.report_status
= 1;
1172 if (gotd_imsg_compose_event(&gotd_session
.repo_child_iev
,
1173 GOTD_IMSG_RECV_PACKFILE
, PROC_SESSION_WRITE
, packfd
,
1174 &ipack
, sizeof(ipack
)) == -1) {
1175 err
= got_error_from_errno("imsg compose RECV_PACKFILE");
1183 if (pipe
[0] != -1 && close(pipe
[0]) == -1 && err
== NULL
)
1184 err
= got_error_from_errno("close");
1185 if (pipe
[1] != -1 && close(pipe
[1]) == -1 && err
== NULL
)
1186 err
= got_error_from_errno("close");
1187 if (packfd
!= -1 && close(packfd
) == -1 && err
== NULL
)
1188 err
= got_error_from_errno("close");
1189 if (idxfd
!= -1 && close(idxfd
) == -1 && err
== NULL
)
1190 err
= got_error_from_errno("close");
1195 client
->packfile_path
= pack_path
;
1196 client
->packidx_path
= idx_path
;
1202 session_dispatch_client(int fd
, short events
, void *arg
)
1204 struct gotd_imsgev
*iev
= arg
;
1205 struct imsgbuf
*ibuf
= &iev
->ibuf
;
1206 struct gotd_session_client
*client
= &gotd_session_client
;
1207 const struct got_error
*err
= NULL
;
1211 if (events
& EV_WRITE
) {
1212 err
= gotd_imsg_flush(ibuf
);
1215 * The client has closed its socket. This can
1216 * happen when Git clients are done sending
1218 * Pending notifications should still be sent.
1220 if (STAILQ_FIRST(¬ifications
) != NULL
)
1222 if (err
->code
== GOT_ERR_ERRNO
&& errno
== EPIPE
) {
1226 disconnect_on_error(client
, err
);
1230 if (client
->flush_disconnect
) {
1236 if (events
& EV_READ
) {
1237 n
= imsgbuf_read(ibuf
);
1239 err
= got_error_from_errno("imsgbuf_read");
1240 disconnect_on_error(client
, err
);
1245 * The client has closed its socket. This can
1246 * happen when Git clients are done sending
1248 * Pending notifications should still be sent.
1250 if (STAILQ_FIRST(¬ifications
) != NULL
)
1252 err
= got_error(GOT_ERR_EOF
);
1253 disconnect_on_error(client
, err
);
1258 while (err
== NULL
) {
1259 n
= imsg_get(ibuf
, &imsg
);
1261 err
= got_error_from_errno("imsg_get");
1267 evtimer_del(&client
->tmo
);
1269 switch (imsg
.hdr
.type
) {
1270 case GOTD_IMSG_CAPABILITIES
:
1271 if (gotd_session
.state
!=
1272 GOTD_STATE_EXPECT_CAPABILITIES
) {
1273 err
= got_error_msg(GOT_ERR_BAD_REQUEST
,
1274 "unexpected capabilities received");
1277 log_debug("receiving capabilities from uid %d",
1279 err
= recv_capabilities(client
, &imsg
);
1281 case GOTD_IMSG_CAPABILITY
:
1282 if (gotd_session
.state
!= GOTD_STATE_EXPECT_CAPABILITIES
) {
1283 err
= got_error_msg(GOT_ERR_BAD_REQUEST
,
1284 "unexpected capability received");
1287 err
= recv_capability(client
, &imsg
);
1288 if (err
|| client
->ncapabilities
< client
->ncapa_alloc
)
1290 gotd_session
.state
= GOTD_STATE_EXPECT_REF_UPDATE
;
1291 client
->accept_flush_pkt
= 1;
1292 log_debug("uid %d: expecting ref-update-lines",
1295 case GOTD_IMSG_REF_UPDATE
:
1296 if (gotd_session
.state
!= GOTD_STATE_EXPECT_REF_UPDATE
&&
1297 gotd_session
.state
!=
1298 GOTD_STATE_EXPECT_MORE_REF_UPDATES
) {
1299 err
= got_error_msg(GOT_ERR_BAD_REQUEST
,
1300 "unexpected ref-update-line received");
1303 log_debug("received ref-update-line from uid %d",
1305 err
= forward_ref_update(client
, &imsg
);
1308 gotd_session
.state
= GOTD_STATE_EXPECT_MORE_REF_UPDATES
;
1309 client
->accept_flush_pkt
= 1;
1311 case GOTD_IMSG_FLUSH
:
1312 if (gotd_session
.state
!=
1313 GOTD_STATE_EXPECT_MORE_REF_UPDATES
) {
1314 err
= got_error_msg(GOT_ERR_BAD_REQUEST
,
1315 "unexpected flush-pkt received");
1318 if (!client
->accept_flush_pkt
) {
1319 err
= got_error_msg(GOT_ERR_BAD_REQUEST
,
1320 "unexpected flush-pkt received");
1325 * Accept just one flush packet at a time.
1326 * Future client state transitions will set this flag
1327 * again if another flush packet is expected.
1329 client
->accept_flush_pkt
= 0;
1331 log_debug("received flush-pkt from uid %d",
1333 if (gotd_session
.state
==
1334 GOTD_STATE_EXPECT_MORE_REF_UPDATES
) {
1335 gotd_session
.state
= GOTD_STATE_EXPECT_PACKFILE
;
1336 log_debug("uid %d: expecting packfile",
1338 err
= recv_packfile(client
);
1340 /* should not happen, see above */
1341 err
= got_error_msg(GOT_ERR_BAD_REQUEST
,
1342 "unexpected client state");
1347 log_debug("unexpected imsg %d", imsg
.hdr
.type
);
1348 err
= got_error(GOT_ERR_PRIVSEP_MSG
);
1356 if (err
->code
!= GOT_ERR_EOF
||
1357 (gotd_session
.state
!= GOTD_STATE_EXPECT_PACKFILE
&&
1358 gotd_session
.state
!= GOTD_STATE_NOTIFY
))
1359 disconnect_on_error(client
, err
);
1361 gotd_imsg_event_add(iev
);
1362 evtimer_add(&client
->tmo
, &gotd_session
.request_timeout
);
1366 static const struct got_error
*
1367 list_refs_request(void)
1369 static const struct got_error
*err
;
1370 struct gotd_session_client
*client
= &gotd_session_client
;
1371 struct gotd_imsgev
*iev
= &gotd_session
.repo_child_iev
;
1374 if (gotd_session
.state
!= GOTD_STATE_EXPECT_LIST_REFS
)
1375 return got_error(GOT_ERR_PRIVSEP_MSG
);
1377 fd
= dup(client
->fd
);
1379 return got_error_from_errno("dup");
1381 if (gotd_imsg_compose_event(iev
, GOTD_IMSG_LIST_REFS_INTERNAL
,
1382 PROC_SESSION_WRITE
, fd
, NULL
, 0) == -1) {
1383 err
= got_error_from_errno("imsg compose LIST_REFS_INTERNAL");
1388 gotd_session
.state
= GOTD_STATE_EXPECT_CAPABILITIES
;
1389 log_debug("uid %d: expecting capabilities", client
->euid
);
1393 static const struct got_error
*
1394 recv_connect(struct imsg
*imsg
)
1396 struct gotd_session_client
*client
= &gotd_session_client
;
1397 struct gotd_imsg_connect iconnect
;
1400 if (gotd_session
.state
!= GOTD_STATE_EXPECT_LIST_REFS
)
1401 return got_error(GOT_ERR_PRIVSEP_MSG
);
1403 datalen
= imsg
->hdr
.len
- IMSG_HEADER_SIZE
;
1404 if (datalen
< sizeof(iconnect
))
1405 return got_error(GOT_ERR_PRIVSEP_LEN
);
1406 memcpy(&iconnect
, imsg
->data
, sizeof(iconnect
));
1407 if (iconnect
.username_len
== 0 ||
1408 datalen
!= sizeof(iconnect
) + iconnect
.username_len
)
1409 return got_error(GOT_ERR_PRIVSEP_LEN
);
1411 client
->euid
= iconnect
.euid
;
1412 client
->egid
= iconnect
.egid
;
1413 client
->fd
= imsg_get_fd(imsg
);
1414 if (client
->fd
== -1)
1415 return got_error(GOT_ERR_PRIVSEP_NO_FD
);
1417 client
->username
= strndup(imsg
->data
+ sizeof(iconnect
),
1418 iconnect
.username_len
);
1419 if (client
->username
== NULL
)
1420 return got_error_from_errno("strndup");
1422 if (imsgbuf_init(&client
->iev
.ibuf
, client
->fd
) == -1)
1423 return got_error_from_errno("imsgbuf_init");
1424 imsgbuf_allow_fdpass(&client
->iev
.ibuf
);
1425 client
->iev
.handler
= session_dispatch_client
;
1426 client
->iev
.events
= EV_READ
;
1427 client
->iev
.handler_arg
= NULL
;
1428 event_set(&client
->iev
.ev
, client
->iev
.ibuf
.fd
, EV_READ
,
1429 session_dispatch_client
, &client
->iev
);
1430 gotd_imsg_event_add(&client
->iev
);
1431 evtimer_set(&client
->tmo
, gotd_request_timeout
, client
);
1432 evtimer_add(&client
->tmo
, &gotd_session
.request_timeout
);
1438 session_dispatch_notifier(int fd
, short event
, void *arg
)
1440 const struct got_error
*err
;
1441 struct gotd_session_client
*client
= &gotd_session_client
;
1442 struct gotd_imsgev
*iev
= arg
;
1443 struct imsgbuf
*ibuf
= &iev
->ibuf
;
1447 struct gotd_session_notif
*notif
;
1449 if (event
& EV_READ
) {
1450 if ((n
= imsgbuf_read(ibuf
)) == -1)
1451 fatal("imsg_read error");
1453 /* Connection closed. */
1459 if (event
& EV_WRITE
) {
1460 err
= gotd_imsg_flush(ibuf
);
1462 fatalx("%s", err
->msg
);
1466 if ((n
= imsg_get(ibuf
, &imsg
)) == -1)
1467 fatal("%s: imsg_get error", __func__
);
1468 if (n
== 0) /* No more messages. */
1471 switch (imsg
.hdr
.type
) {
1472 case GOTD_IMSG_NOTIFICATION_SENT
:
1473 if (gotd_session
.state
!= GOTD_STATE_NOTIFY
) {
1474 log_warn("unexpected imsg %d", imsg
.hdr
.type
);
1477 notif
= STAILQ_FIRST(¬ifications
);
1478 if (notif
== NULL
) {
1480 break; /* NOTREACHED */
1482 /* Request content for the next notification. */
1483 err
= request_notification(notif
);
1485 log_warn("could not send notification: %s",
1491 log_debug("unexpected imsg %d", imsg
.hdr
.type
);
1499 gotd_imsg_event_add(iev
);
1501 /* This pipe is dead. Remove its event handler */
1502 event_del(&iev
->ev
);
1503 imsgbuf_clear(&iev
->ibuf
);
1507 static const struct got_error
*
1508 recv_notifier(struct imsg
*imsg
)
1510 struct gotd_imsgev
*iev
= &gotd_session
.notifier_iev
;
1511 struct gotd_session_client
*client
= &gotd_session_client
;
1515 if (gotd_session
.state
!= GOTD_STATE_EXPECT_LIST_REFS
)
1516 return got_error(GOT_ERR_PRIVSEP_MSG
);
1518 /* We should already have received a pipe to the listener. */
1519 if (client
->fd
== -1)
1520 return got_error(GOT_ERR_PRIVSEP_MSG
);
1522 datalen
= imsg
->hdr
.len
- IMSG_HEADER_SIZE
;
1524 return got_error(GOT_ERR_PRIVSEP_LEN
);
1526 fd
= imsg_get_fd(imsg
);
1528 return NULL
; /* notifications unused */
1530 if (imsgbuf_init(&iev
->ibuf
, fd
) == -1) {
1532 return got_error_from_errno("imsgbuf_init");
1534 imsgbuf_allow_fdpass(&iev
->ibuf
);
1535 iev
->handler
= session_dispatch_notifier
;
1536 iev
->events
= EV_READ
;
1537 iev
->handler_arg
= NULL
;
1538 event_set(&iev
->ev
, iev
->ibuf
.fd
, EV_READ
,
1539 session_dispatch_notifier
, iev
);
1540 gotd_imsg_event_add(iev
);
1545 static const struct got_error
*
1546 recv_repo_child(struct imsg
*imsg
)
1548 struct gotd_imsg_connect_repo_child ichild
;
1549 struct gotd_session_client
*client
= &gotd_session_client
;
1553 if (gotd_session
.state
!= GOTD_STATE_EXPECT_LIST_REFS
)
1554 return got_error(GOT_ERR_PRIVSEP_MSG
);
1556 /* We should already have received a pipe to the listener. */
1557 if (client
->fd
== -1)
1558 return got_error(GOT_ERR_PRIVSEP_MSG
);
1560 datalen
= imsg
->hdr
.len
- IMSG_HEADER_SIZE
;
1561 if (datalen
!= sizeof(ichild
))
1562 return got_error(GOT_ERR_PRIVSEP_LEN
);
1564 memcpy(&ichild
, imsg
->data
, sizeof(ichild
));
1566 if (ichild
.proc_id
!= PROC_REPO_WRITE
)
1567 return got_error_msg(GOT_ERR_PRIVSEP_MSG
,
1568 "bad child process type");
1570 fd
= imsg_get_fd(imsg
);
1572 return got_error(GOT_ERR_PRIVSEP_NO_FD
);
1574 if (imsgbuf_init(&gotd_session
.repo_child_iev
.ibuf
, fd
) == -1) {
1576 return got_error_from_errno("imsgbuf_init");
1578 imsgbuf_allow_fdpass(&gotd_session
.repo_child_iev
.ibuf
);
1579 gotd_session
.repo_child_iev
.handler
= session_dispatch_repo_child
;
1580 gotd_session
.repo_child_iev
.events
= EV_READ
;
1581 gotd_session
.repo_child_iev
.handler_arg
= NULL
;
1582 event_set(&gotd_session
.repo_child_iev
.ev
,
1583 gotd_session
.repo_child_iev
.ibuf
.fd
, EV_READ
,
1584 session_dispatch_repo_child
, &gotd_session
.repo_child_iev
);
1585 gotd_imsg_event_add(&gotd_session
.repo_child_iev
);
1587 /* The "recvfd" pledge promise is no longer needed. */
1588 if (pledge("stdio rpath wpath cpath sendfd fattr flock", NULL
) == -1)
1595 session_dispatch(int fd
, short event
, void *arg
)
1597 const struct got_error
*err
= NULL
;
1598 struct gotd_imsgev
*iev
= arg
;
1599 struct imsgbuf
*ibuf
= &iev
->ibuf
;
1600 struct gotd_session_client
*client
= &gotd_session_client
;
1605 if (event
& EV_READ
) {
1606 if ((n
= imsgbuf_read(ibuf
)) == -1)
1607 fatal("imsg_read error");
1609 /* Connection closed. */
1615 if (event
& EV_WRITE
) {
1616 err
= gotd_imsg_flush(ibuf
);
1618 fatalx("%s", err
->msg
);
1622 const struct got_error
*err
= NULL
;
1623 uint32_t client_id
= 0;
1624 int do_disconnect
= 0, do_list_refs
= 0;
1626 if ((n
= imsg_get(ibuf
, &imsg
)) == -1)
1627 fatal("%s: imsg_get error", __func__
);
1628 if (n
== 0) /* No more messages. */
1631 switch (imsg
.hdr
.type
) {
1632 case GOTD_IMSG_ERROR
:
1634 err
= gotd_imsg_recv_error(&client_id
, &imsg
);
1636 case GOTD_IMSG_CONNECT
:
1637 err
= recv_connect(&imsg
);
1639 case GOTD_IMSG_DISCONNECT
:
1642 case GOTD_IMSG_CONNECT_NOTIFIER
:
1643 err
= recv_notifier(&imsg
);
1645 case GOTD_IMSG_CONNECT_REPO_CHILD
:
1646 err
= recv_repo_child(&imsg
);
1652 log_debug("unexpected imsg %d", imsg
.hdr
.type
);
1657 if (do_disconnect
) {
1659 disconnect_on_error(client
, err
);
1662 } else if (do_list_refs
)
1663 err
= list_refs_request();
1666 log_warnx("uid %d: %s", client
->euid
, err
->msg
);
1670 gotd_imsg_event_add(iev
);
1672 /* This pipe is dead. Remove its event handler */
1673 event_del(&iev
->ev
);
1674 event_loopexit(NULL
);
1679 session_write_main(const char *title
, const char *repo_path
,
1680 int *pack_fds
, int *temp_fds
, struct timeval
*request_timeout
,
1681 struct gotd_repo
*repo_cfg
)
1683 const struct got_error
*err
= NULL
;
1684 struct event evsigint
, evsigterm
, evsighup
, evsigusr1
;
1686 STAILQ_INIT(¬ifications
);
1688 gotd_session
.title
= title
;
1689 gotd_session
.pid
= getpid();
1690 gotd_session
.pack_fds
= pack_fds
;
1691 gotd_session
.temp_fds
= temp_fds
;
1692 memcpy(&gotd_session
.request_timeout
, request_timeout
,
1693 sizeof(gotd_session
.request_timeout
));
1694 gotd_session
.repo_cfg
= repo_cfg
;
1696 if (imsgbuf_init(&gotd_session
.notifier_iev
.ibuf
, -1) == -1) {
1697 err
= got_error_from_errno("imsgbuf_init");
1700 imsgbuf_allow_fdpass(&gotd_session
.notifier_iev
.ibuf
);
1702 err
= got_repo_open(&gotd_session
.repo
, repo_path
, NULL
, pack_fds
);
1705 if (!got_repo_is_bare(gotd_session
.repo
)) {
1706 err
= got_error_msg(GOT_ERR_NOT_GIT_REPO
,
1707 "bare git repository required");
1710 if (got_repo_get_object_format(gotd_session
.repo
) != GOT_HASH_SHA1
) {
1711 err
= got_error_msg(GOT_ERR_NOT_IMPL
,
1712 "sha256 object IDs unsupported in network protocol");
1716 got_repo_temp_fds_set(gotd_session
.repo
, temp_fds
);
1718 signal_set(&evsigint
, SIGINT
, session_write_sighdlr
, NULL
);
1719 signal_set(&evsigterm
, SIGTERM
, session_write_sighdlr
, NULL
);
1720 signal_set(&evsighup
, SIGHUP
, session_write_sighdlr
, NULL
);
1721 signal_set(&evsigusr1
, SIGUSR1
, session_write_sighdlr
, NULL
);
1722 signal(SIGPIPE
, SIG_IGN
);
1724 signal_add(&evsigint
, NULL
);
1725 signal_add(&evsigterm
, NULL
);
1726 signal_add(&evsighup
, NULL
);
1727 signal_add(&evsigusr1
, NULL
);
1729 gotd_session
.state
= GOTD_STATE_EXPECT_LIST_REFS
;
1731 gotd_session_client
.fd
= -1;
1732 gotd_session_client
.nref_updates
= -1;
1733 gotd_session_client
.delta_cache_fd
= -1;
1734 gotd_session_client
.accept_flush_pkt
= 1;
1736 if (imsgbuf_init(&gotd_session
.parent_iev
.ibuf
, GOTD_FILENO_MSG_PIPE
)
1738 err
= got_error_from_errno("imsgbuf_init");
1741 imsgbuf_allow_fdpass(&gotd_session
.parent_iev
.ibuf
);
1742 gotd_session
.parent_iev
.handler
= session_dispatch
;
1743 gotd_session
.parent_iev
.events
= EV_READ
;
1744 gotd_session
.parent_iev
.handler_arg
= NULL
;
1745 event_set(&gotd_session
.parent_iev
.ev
, gotd_session
.parent_iev
.ibuf
.fd
,
1746 EV_READ
, session_dispatch
, &gotd_session
.parent_iev
);
1747 if (gotd_imsg_compose_event(&gotd_session
.parent_iev
,
1748 GOTD_IMSG_CLIENT_SESSION_READY
, PROC_SESSION_WRITE
,
1749 -1, NULL
, 0) == -1) {
1750 err
= got_error_from_errno("imsg compose CLIENT_SESSION_READY");
1757 log_warnx("%s: %s", title
, err
->msg
);
1758 session_write_shutdown();
1762 session_write_shutdown(void)
1764 struct gotd_session_notif
*notif
;
1766 log_debug("%s: shutting down", gotd_session
.title
);
1768 while (!STAILQ_EMPTY(¬ifications
)) {
1769 notif
= STAILQ_FIRST(¬ifications
);
1770 STAILQ_REMOVE_HEAD(¬ifications
, entry
);
1771 if (notif
->fd
!= -1)
1773 free(notif
->refname
);
1777 if (gotd_session
.repo
)
1778 got_repo_close(gotd_session
.repo
);
1779 got_repo_pack_fds_close(gotd_session
.pack_fds
);
1780 got_repo_temp_fds_close(gotd_session
.temp_fds
);
1781 free(gotd_session_client
.username
);