Commit Diff


commit - 6f86da294d3b81aba4c403d76e423598958c2100
commit + fd9f46f196aef97157fc2532b99e727b72ea32b2
blob - 23a37dbbe11d71dc588924f24f2d9ff4b4a35b89
blob + 8eb6f4396784abfa7a956c90fb3cc41e5d2b433f
--- gotctl/gotctl.c
+++ gotctl/gotctl.c
@@ -137,10 +137,14 @@ cmd_info(int argc, char *argv[], int gotd_sock)
 	struct imsgbuf ibuf;
 	struct imsg imsg;
 
-	imsg_init(&ibuf, gotd_sock);
+	if (imsgbuf_init(&ibuf, gotd_sock) == -1)
+		return got_error_from_errno("imsgbuf_init");
+	imsgbuf_allow_fdpass(&ibuf);
 
-	if (imsg_compose(&ibuf, GOTD_IMSG_INFO, 0, 0, -1, NULL, 0) == -1)
+	if (imsg_compose(&ibuf, GOTD_IMSG_INFO, 0, 0, -1, NULL, 0) == -1) {
+		imsgbuf_clear(&ibuf);
 		return got_error_from_errno("imsg_compose INFO");
+	}
 
 	err = gotd_imsg_flush(&ibuf);
 	while (err == NULL) {
@@ -172,7 +176,7 @@ cmd_info(int argc, char *argv[], int gotd_sock)
 		imsg_free(&imsg);
 	}
 
-	imsg_clear(&ibuf);
+	imsgbuf_clear(&ibuf);
 	return err;
 }
 
@@ -190,10 +194,14 @@ cmd_stop(int argc, char *argv[], int gotd_sock)
 	struct imsgbuf ibuf;
 	struct imsg imsg;
 
-	imsg_init(&ibuf, gotd_sock);
+	if (imsgbuf_init(&ibuf, gotd_sock) == -1)
+		return got_error_from_errno("imsgbuf_init");
+	imsgbuf_allow_fdpass(&ibuf);
 
-	if (imsg_compose(&ibuf, GOTD_IMSG_STOP, 0, 0, -1, NULL, 0) == -1)
+	if (imsg_compose(&ibuf, GOTD_IMSG_STOP, 0, 0, -1, NULL, 0) == -1) {
+		imsgbuf_clear(&ibuf);
 		return got_error_from_errno("imsg_compose STOP");
+	}
 
 	err = gotd_imsg_flush(&ibuf);
 	while (err == NULL) {
@@ -216,7 +224,7 @@ cmd_stop(int argc, char *argv[], int gotd_sock)
 		imsg_free(&imsg);
 	}
 
-	imsg_clear(&ibuf);
+	imsgbuf_clear(&ibuf);
 	return err;
 }
 
blob - 80c9ed0ee1c0db65d55b64e028363e68817afbed
blob + 2ac6c8d38c0175476ee810cfc7dd4a08d4eefece
--- gotd/auth.c
+++ gotd/auth.c
@@ -252,18 +252,15 @@ auth_dispatch(int fd, short event, void *arg)
 	int shut = 0;
 
 	if (event & EV_READ) {
-		if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN)
+		if ((n = imsgbuf_read(ibuf)) == -1)
 			fatal("imsg_read error");
 		if (n == 0)	/* Connection closed. */
 			shut = 1;
 	}
 
 	if (event & EV_WRITE) {
-		n = msgbuf_write(&ibuf->w);
-		if (n == -1 && errno != EAGAIN)
+		if (imsgbuf_write(ibuf) == -1)
 			fatal("msgbuf_write");
-		if (n == 0)	/* Connection closed. */
-			shut = 1;
 	}
 
 	for (;;) {
@@ -325,7 +322,9 @@ auth_main(const char *title, struct gotd_repolist *rep
 	signal_add(&evsighup, NULL);
 	signal_add(&evsigusr1, NULL);
 
-	imsg_init(&iev.ibuf, GOTD_FILENO_MSG_PIPE);
+	if (imsgbuf_init(&iev.ibuf, GOTD_FILENO_MSG_PIPE) == -1)
+		fatal("imsgbuf_init");
+	imsgbuf_allow_fdpass(&iev.ibuf);
 	iev.handler = auth_dispatch;
 	iev.events = EV_READ;
 	iev.handler_arg = NULL;
blob - fa63d7158cd8f380a8721696c604c93400a8d432
blob + 0d701c77551d7e3b172e7c6aef490386f799d5a1
--- gotd/gotd.c
+++ gotd/gotd.c
@@ -312,7 +312,7 @@ proc_done(struct gotd_child_proc *proc)
 
 	if (proc->iev.ibuf.fd != -1) {
 		event_del(&proc->iev.ev);
-		msgbuf_clear(&proc->iev.ibuf.w);
+		imsgbuf_clear(&proc->iev.ibuf);
 		close(proc->iev.ibuf.fd);
 	}
 
@@ -370,7 +370,7 @@ disconnect(struct gotd_client *client)
 
 	slot = client_hash(client->id) % nitems(gotd_clients);
 	STAILQ_REMOVE(&gotd_clients[slot], client, gotd_client, entry);
-	imsg_clear(&client->iev.ibuf);
+	imsgbuf_clear(&client->iev.ibuf);
 	event_del(&client->iev.ev);
 	evtimer_del(&client->tmo);
 	if (client->fd != -1)
@@ -390,9 +390,12 @@ disconnect_on_error(struct gotd_client *client, const 
 	if (err->code != GOT_ERR_EOF) {
 		log_warnx("uid %d: %s", client->euid, err->msg);
 		if (client->fd != -1) {
-			imsg_init(&ibuf, client->fd);
-			gotd_imsg_send_error(&ibuf, 0, PROC_GOTD, err);
-			imsg_clear(&ibuf);
+			if (imsgbuf_init(&ibuf, client->fd) != -1) {
+				gotd_imsg_send_error(&ibuf, 0, PROC_GOTD,
+				    err);
+				imsgbuf_clear(&ibuf);
+			} else
+				log_warn("%s: imsgbuf_init failed", __func__);
 		}
 	}
 	disconnect(client);
@@ -506,7 +509,6 @@ send_info(struct gotd_client *client)
 static const struct got_error *
 stop_gotd(struct gotd_client *client)
 {
-
 	if (client->euid != 0)
 		return got_error_set_errno(EPERM, "stop");
 
@@ -579,48 +581,51 @@ gotd_request(int fd, short events, void *arg)
 	ssize_t n;
 
 	if (events & EV_WRITE) {
-		while (ibuf->w.queued) {
-			n = msgbuf_write(&ibuf->w);
-			if (n == -1 && errno == EPIPE) {
-				/*
-				 * The client has closed its socket.
-				 * This can happen when Git clients are
-				 * done sending pack file data.
-				 */
-				msgbuf_clear(&ibuf->w);
-				continue;
-			} else if (n == -1 && errno != EAGAIN) {
-				err = got_error_from_errno("msgbuf_write");
-				disconnect_on_error(client, err);
+		if (imsgbuf_write(ibuf) == -1) {
+			/*
+			 * The client has closed its socket.  This can
+			 * happen when Git clients are done sending
+			 * pack file data.
+			 */
+			if (errno == EPIPE) {
+				disconnect(client);
 				return;
 			}
-			if (n == 0) {
-				/* Connection closed. */
-				err = got_error(GOT_ERR_EOF);
-				disconnect_on_error(client, err);
-				return;
-			}
+			err = got_error_from_errno("imsgbuf_write");
+			disconnect_on_error(client, err);
+			return;
 		}
 
-		/* Disconnect gotctl(8) now that messages have been sent. */
-		if (!client_is_reading(client) && !client_is_writing(client)) {
+		/* Disconnect gotctl(8) if all messages have been sent. */
+		if (!client_is_reading(client) && !client_is_writing(client) &&
+		    imsgbuf_queuelen(ibuf) == 0) {
 			disconnect(client);
 			return;
 		}
 	}
 
-	if ((events & EV_READ) == 0)
-		return;
+	if (events & EV_READ) {
+		n = imsgbuf_read(ibuf);
+		if (n == -1) {
+			err = got_error_from_errno("imsgbuf_read");
+			disconnect_on_error(client, err);
+			return;
+		}
+		if (n == 0) {
+			 err = got_error(GOT_ERR_EOF);
+			 disconnect_on_error(client, err);
+			 return;
+		}
+	}
 
-	memset(&imsg, 0, sizeof(imsg));
-
 	while (err == NULL) {
-		err = gotd_imsg_recv(&imsg, ibuf, 0);
-		if (err) {
-			if (err->code == GOT_ERR_PRIVSEP_READ)
-				err = NULL;
+		n = imsg_get(ibuf, &imsg);
+		if (n == -1) {
+			err = got_error_from_errno("imsg_get");
 			break;
 		}
+		if (n == 0)
+			break;
 
 		evtimer_del(&client->tmo);
 
@@ -666,7 +671,6 @@ recv_connect(uint32_t *client_id, struct imsg *imsg)
 	const struct got_error *err = NULL;
 	struct gotd_imsg_connect iconnect;
 	size_t datalen;
-	int s = -1;
 	struct gotd_client *client = NULL;
 
 	*client_id = 0;
@@ -676,12 +680,6 @@ recv_connect(uint32_t *client_id, struct imsg *imsg)
 		return got_error(GOT_ERR_PRIVSEP_LEN);
 	memcpy(&iconnect, imsg->data, sizeof(iconnect));
 
-	s = imsg_get_fd(imsg);
-	if (s == -1) {
-		err = got_error(GOT_ERR_PRIVSEP_NO_FD);
-		goto done;
-	}
-
 	if (find_client(iconnect.client_id)) {
 		err = got_error_msg(GOT_ERR_CLIENT_ID, "duplicate client ID");
 		goto done;
@@ -697,13 +695,20 @@ recv_connect(uint32_t *client_id, struct imsg *imsg)
 
 	client->state = GOTD_CLIENT_STATE_NEW;
 	client->id = iconnect.client_id;
-	client->fd = s;
-	s = -1;
 	/* The auth process will verify UID/GID for us. */
 	client->euid = iconnect.euid;
 	client->egid = iconnect.egid;
 
-	imsg_init(&client->iev.ibuf, client->fd);
+	client->fd = imsg_get_fd(imsg);
+	if (client->fd == -1) {
+		err = got_error(GOT_ERR_PRIVSEP_NO_FD);
+		goto done;
+	}
+	if (imsgbuf_init(&client->iev.ibuf, client->fd) == -1) {
+		err = got_error_from_errno("imsgbuf_init");
+		goto done;
+	}
+	imsgbuf_allow_fdpass(&client->iev.ibuf);
 	client->iev.handler = gotd_request;
 	client->iev.events = EV_READ;
 	client->iev.handler_arg = client;
@@ -718,7 +723,7 @@ recv_connect(uint32_t *client_id, struct imsg *imsg)
 	log_debug("%s: new client uid %d connected on fd %d", __func__,
 	    client->euid, client->fd);
 done:
-	if (err) {
+	if (err && client) {
 		struct gotd_child_proc *listen_proc = gotd.listen_proc;
 		struct gotd_imsg_disconnect idisconnect;
 
@@ -728,8 +733,9 @@ done:
 		    &idisconnect, sizeof(idisconnect)) == -1)
 			log_warn("imsg compose DISCONNECT");
 
-		if (s != -1)
-			close(s);
+		if (client->fd != -1)
+			close(client->fd);
+		free(client);
 	}
 
 	return err;
@@ -756,7 +762,7 @@ kill_proc(struct gotd_child_proc *proc, int fatal)
 
 	if (proc->iev.ibuf.fd != -1) {
 		event_del(&proc->iev.ev);
-		msgbuf_clear(&proc->iev.ibuf.w);
+		imsgbuf_clear(&proc->iev.ibuf);
 		close(proc->iev.ibuf.fd);
 		proc->iev.ibuf.fd = -1;
 	}
@@ -1050,7 +1056,7 @@ gotd_dispatch_listener(int fd, short event, void *arg)
 		fatalx("%s: unexpected fd %d", __func__, fd);
 
 	if (event & EV_READ) {
-		if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN)
+		if ((n = imsgbuf_read(ibuf)) == -1)
 			fatal("imsg_read error");
 		if (n == 0) {
 			/* Connection closed. */
@@ -1060,14 +1066,8 @@ gotd_dispatch_listener(int fd, short event, void *arg)
 	}
 
 	if (event & EV_WRITE) {
-		n = msgbuf_write(&ibuf->w);
-		if (n == -1 && errno != EAGAIN)
+		if (imsgbuf_write(ibuf) == -1)
 			fatal("msgbuf_write");
-		if (n == 0) {
-			/* Connection closed. */
-			shut = 1;
-			goto done;
-		}
 	}
 
 	for (;;) {
@@ -1137,7 +1137,7 @@ gotd_dispatch_notifier(int fd, short event, void *arg)
 		fatalx("%s: unexpected fd %d", __func__, fd);
 
 	if (event & EV_READ) {
-		if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN)
+		if ((n = imsgbuf_read(ibuf)) == -1)
 			fatal("imsg_read error");
 		if (n == 0) {
 			/* Connection closed. */
@@ -1147,14 +1147,8 @@ gotd_dispatch_notifier(int fd, short event, void *arg)
 	}
 
 	if (event & EV_WRITE) {
-		n = msgbuf_write(&ibuf->w);
-		if (n == -1 && errno != EAGAIN)
+		if (imsgbuf_write(ibuf) == -1)
 			fatal("msgbuf_write");
-		if (n == 0) {
-			/* Connection closed. */
-			shut = 1;
-			goto done;
-		}
 	}
 
 	for (;;) {
@@ -1216,7 +1210,7 @@ gotd_dispatch_auth_child(int fd, short event, void *ar
 		fatalx("cannot find auth child process for fd %d", fd);
 
 	if (event & EV_READ) {
-		if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN)
+		if ((n = imsgbuf_read(ibuf)) == -1)
 			fatal("imsg_read error");
 		if (n == 0) {
 			/* Connection closed. */
@@ -1226,13 +1220,8 @@ gotd_dispatch_auth_child(int fd, short event, void *ar
 	}
 
 	if (event & EV_WRITE) {
-		n = msgbuf_write(&ibuf->w);
-		if (n == -1 && errno != EAGAIN)
+		if (imsgbuf_write(ibuf) == -1)
 			fatal("msgbuf_write");
-		if (n == 0) {
-			/* Connection closed. */
-			shut = 1;
-		}
 		goto done;
 	}
 
@@ -1362,8 +1351,8 @@ connect_session(struct gotd_client *client)
 	 * We are no longer interested in messages from this client.
 	 * Further client requests will be handled by the session process.
 	 */
-	msgbuf_clear(&client->iev.ibuf.w);
-	imsg_clear(&client->iev.ibuf);
+	imsgbuf_clear(&client->iev.ibuf);
+	imsgbuf_clear(&client->iev.ibuf);
 	event_del(&client->iev.ev);
 	client->fd = -1; /* will be closed via copy in client->iev.ibuf.fd */
 
@@ -1390,7 +1379,7 @@ gotd_dispatch_client_session(int fd, short event, void
 	}
 
 	if (event & EV_READ) {
-		if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN)
+		if ((n = imsgbuf_read(ibuf)) == -1)
 			fatal("imsg_read error");
 		if (n == 0) {
 			/* Connection closed. */
@@ -1400,14 +1389,8 @@ gotd_dispatch_client_session(int fd, short event, void
 	}
 
 	if (event & EV_WRITE) {
-		n = msgbuf_write(&ibuf->w);
-		if (n == -1 && errno != EAGAIN)
+		if (imsgbuf_write(ibuf) == -1)
 			fatal("msgbuf_write");
-		if (n == 0) {
-			/* Connection closed. */
-			shut = 1;
-			goto done;
-		}
 	}
 
 	proc = client->session;
@@ -1551,7 +1534,7 @@ gotd_dispatch_repo_child(int fd, short event, void *ar
 	}
 
 	if (event & EV_READ) {
-		if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN)
+		if ((n = imsgbuf_read(ibuf)) == -1)
 			fatal("imsg_read error");
 		if (n == 0) {
 			/* Connection closed. */
@@ -1561,14 +1544,8 @@ gotd_dispatch_repo_child(int fd, short event, void *ar
 	}
 
 	if (event & EV_WRITE) {
-		n = msgbuf_write(&ibuf->w);
-		if (n == -1 && errno != EAGAIN)
+		if (imsgbuf_write(ibuf) == -1)
 			fatal("msgbuf_write");
-		if (n == 0) {
-			/* Connection closed. */
-			shut = 1;
-			goto done;
-		}
 	}
 
 	proc = client->repo;
@@ -1724,7 +1701,9 @@ start_listener(char *argv0, const char *confpath, int 
 
 	proc->pid = start_child(proc->type, NULL, argv0, confpath,
 	    proc->pipe[1], daemonize, verbosity);
-	imsg_init(&proc->iev.ibuf, proc->pipe[0]);
+	if (imsgbuf_init(&proc->iev.ibuf, proc->pipe[0]) == -1)
+		fatal("imsgbuf_init");
+	imsgbuf_allow_fdpass(&proc->iev.ibuf);
 	proc->iev.handler = gotd_dispatch_listener;
 	proc->iev.events = EV_READ;
 	proc->iev.handler_arg = NULL;
@@ -1753,7 +1732,9 @@ start_notifier(char *argv0, const char *confpath, int 
 
 	proc->pid = start_child(proc->type, NULL, argv0, confpath,
 	    proc->pipe[1], daemonize, verbosity);
-	imsg_init(&proc->iev.ibuf, proc->pipe[0]);
+	if (imsgbuf_init(&proc->iev.ibuf, proc->pipe[0]) == -1)
+		fatal("imsgbuf_init");
+	imsgbuf_allow_fdpass(&proc->iev.ibuf);
 	proc->iev.handler = gotd_dispatch_notifier;
 	proc->iev.events = EV_READ;
 	proc->iev.handler_arg = NULL;
@@ -1793,7 +1774,9 @@ start_session_child(struct gotd_client *client, struct
 		fatal("socketpair");
 	proc->pid = start_child(proc->type, proc->repo_path, argv0,
 	    confpath, proc->pipe[1], daemonize, verbosity);
-	imsg_init(&proc->iev.ibuf, proc->pipe[0]);
+	if (imsgbuf_init(&proc->iev.ibuf, proc->pipe[0]) == -1)
+		fatal("imsgbuf_init");
+	imsgbuf_allow_fdpass(&proc->iev.ibuf);
 	log_debug("proc %s %s is on fd %d",
 	    gotd_proc_names[proc->type], proc->repo_path,
 	    proc->pipe[0]);
@@ -1839,7 +1822,9 @@ start_repo_child(struct gotd_client *client, enum gotd
 		fatal("socketpair");
 	proc->pid = start_child(proc->type, proc->repo_path, argv0,
 	    confpath, proc->pipe[1], daemonize, verbosity);
-	imsg_init(&proc->iev.ibuf, proc->pipe[0]);
+	if (imsgbuf_init(&proc->iev.ibuf, proc->pipe[0]) == -1)
+		fatal("imsgbuf_init");
+	imsgbuf_allow_fdpass(&proc->iev.ibuf);
 	log_debug("proc %s %s is on fd %d",
 	    gotd_proc_names[proc->type], proc->repo_path,
 	    proc->pipe[0]);
@@ -1894,7 +1879,9 @@ start_auth_child(struct gotd_client *client, int requi
 		fatal("socketpair");
 	proc->pid = start_child(proc->type, proc->repo_path, argv0,
 	    confpath, proc->pipe[1], daemonize, verbosity);
-	imsg_init(&proc->iev.ibuf, proc->pipe[0]);
+	if (imsgbuf_init(&proc->iev.ibuf, proc->pipe[0]) == -1)
+		fatal("imsgbuf_init");
+	imsgbuf_allow_fdpass(&proc->iev.ibuf);
 	log_debug("proc %s %s is on fd %d",
 	    gotd_proc_names[proc->type], proc->repo_path,
 	    proc->pipe[0]);
@@ -2406,7 +2393,7 @@ main(int argc, char **argv)
 		if (imsg_compose(imsgbuf, GOTD_IMSG_SECRETS, 0, 0, -1,
 		    &n, sizeof(n)) == -1)
 			fatal("imsg_compose GOTD_IMSG_SECRETS");
-		if (imsg_flush(imsgbuf))
+		if (imsgbuf_flush(imsgbuf))
 			fatal("imsg_flush");
 
 		for (i = 0; i < n; ++i) {
@@ -2432,7 +2419,7 @@ main(int argc, char **argv)
 			if (imsg_composev(imsgbuf, GOTD_IMSG_SECRET,
 			    0, 0, -1, iov, 5) == -1)
 				fatal("imsg_composev GOTD_IMSG_SECRET");
-			if (imsg_flush(imsgbuf))
+			if (imsgbuf_flush(imsgbuf))
 				fatal("imsg_flush");
 		}
 
blob - fdd5ca3395a4522727127660cef701281c8cb81c
blob + 70e29314e3d052419d24ae178f9fb9dc0240c57c
--- gotd/gotd.h
+++ gotd/gotd.h
@@ -500,7 +500,6 @@ const struct got_error *gotd_parse_url(char **, char *
 
 /* imsg.c */
 const struct got_error *gotd_imsg_flush(struct imsgbuf *);
-const struct got_error *gotd_imsg_recv(struct imsg *, struct imsgbuf *, size_t);
 const struct got_error *gotd_imsg_poll_recv(struct imsg *, struct imsgbuf *,
     size_t);
 const struct got_error *gotd_imsg_recv_error(uint32_t *client_id,
blob - 992bb231e5877b491084348026d517514e382805
blob + f72cdc28b61a77090d5ece2306dfd7d0a31df51c
--- gotd/imsg.c
+++ gotd/imsg.c
@@ -62,24 +62,22 @@ gotd_imsg_flush(struct imsgbuf *ibuf)
 {
 	const struct got_error *err = NULL;
 
-	while (ibuf->w.queued > 0) {
+	while (imsgbuf_queuelen(ibuf) > 0) {
 		err = got_poll_fd(ibuf->fd, POLLOUT, INFTIM);
 		if (err)
 			break;
 
-		if (imsg_flush(ibuf) == -1) {
-			if (errno != EAGAIN) {
-				imsg_clear(ibuf);
-				err = got_error_from_errno("imsg_flush");
-				break;
-			}
+		if (imsgbuf_write(ibuf) == -1) {
+			err = got_error_from_errno("imsgbuf_flush");
+			break;
 		}
 	}
 
 	return err;
 }
 
-const struct got_error *
+#include <stdlib.h>
+static const struct got_error *
 gotd_imsg_recv(struct imsg *imsg, struct imsgbuf *ibuf, size_t min_datalen)
 {
 	ssize_t n;
@@ -89,17 +87,16 @@ gotd_imsg_recv(struct imsg *imsg, struct imsgbuf *ibuf
 		return got_error_from_errno("imsg_get");
 
 	if (n == 0) {
-		n = imsg_read(ibuf);
-		if (n == -1) {
-			if (errno == EAGAIN)
-				return got_error(GOT_ERR_PRIVSEP_READ);
+		n = imsgbuf_read(ibuf);
+		if (n == -1)
 			return got_error_from_errno("imsg_read");
-		}
 		if (n == 0)
 			return got_error(GOT_ERR_EOF);
 		n = imsg_get(ibuf, imsg);
 		if (n == -1)
 			return got_error_from_errno("imsg_get");
+		if (n == 0)
+			abort();
 	}
 
 	if (imsg->hdr.len < IMSG_HEADER_SIZE + min_datalen)
@@ -181,7 +178,7 @@ void
 gotd_imsg_event_add(struct gotd_imsgev *iev)
 {
 	iev->events = EV_READ;
-	if (iev->ibuf.w.queued)
+	if (imsgbuf_queuelen(&iev->ibuf))
 		iev->events |= EV_WRITE;
 
 	event_del(&iev->ev);
blob - 095d9d88dc8766d8898eddbfe0a5b3be4387afd8
blob + 7a23c27e7e5ef2c9a06d9c08285a201e761e0f14
--- gotd/listen.c
+++ gotd/listen.c
@@ -394,18 +394,15 @@ listen_dispatch(int fd, short event, void *arg)
 	int shut = 0;
 
 	if (event & EV_READ) {
-		if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN)
+		if ((n = imsgbuf_read(ibuf)) == -1)
 			fatal("imsg_read error");
 		if (n == 0)	/* Connection closed. */
 			shut = 1;
 	}
 
 	if (event & EV_WRITE) {
-		n = msgbuf_write(&ibuf->w);
-		if (n == -1 && errno != EAGAIN)
+		if (imsgbuf_write(ibuf) == -1)
 			fatal("msgbuf_write");
-		if (n == 0)	/* Connection closed. */
-			shut = 1;
 	}
 
 	for (;;) {
@@ -465,7 +462,9 @@ listen_main(const char *title, int gotd_socket,
 	signal_add(&evsighup, NULL);
 	signal_add(&evsigusr1, NULL);
 
-	imsg_init(&iev.ibuf, GOTD_FILENO_MSG_PIPE);
+	if (imsgbuf_init(&iev.ibuf, GOTD_FILENO_MSG_PIPE) == -1)
+		fatal("imsgbuf_init");
+	imsgbuf_allow_fdpass(&iev.ibuf);
 	iev.handler = listen_dispatch;
 	iev.events = EV_READ;
 	iev.handler_arg = NULL;
blob - b0a45b3e8c3fd8674394a2e9ccc32197ad110797
blob + ef7e05f8ec82dd6ce60b7a0f2dc52b9341ec2657
--- gotd/notify.c
+++ gotd/notify.c
@@ -381,7 +381,7 @@ notify_dispatch_session(int fd, short event, void *arg
 	struct imsg imsg;
 
 	if (event & EV_READ) {
-		if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN)
+		if ((n = imsgbuf_read(ibuf)) == -1)
 			fatal("imsg_read error");
 		if (n == 0) {
 			/* Connection closed. */
@@ -391,11 +391,9 @@ notify_dispatch_session(int fd, short event, void *arg
 	}
 
 	if (event & EV_WRITE) {
-		n = msgbuf_write(&ibuf->w);
-		if (n == -1 && errno != EAGAIN && errno != EPIPE)
-			fatal("msgbuf_write");
-		if (n == 0 || (n == -1 && errno == EPIPE)) {
-			/* Connection closed. */
+		if (imsgbuf_write(ibuf) == -1) {
+			if (errno != EPIPE)
+				fatal("imsgbuf_write");
 			shut = 1;
 			goto done;
 		}
@@ -430,7 +428,7 @@ done:
 
 		/* This pipe is dead. Remove its event handler */
 		event_del(&iev->ev);
-		imsg_clear(&iev->ibuf);
+		imsgbuf_clear(&iev->ibuf);
 
 		session = find_session_by_fd(fd);
 		if (session)
@@ -460,7 +458,11 @@ recv_session(struct imsg *imsg)
 	}
 
 	session->id = get_session_id();
-	imsg_init(&session->iev.ibuf, fd);
+	if (imsgbuf_init(&session->iev.ibuf, fd) == -1) {
+		close(fd);
+		return got_error_from_errno("imsgbuf_init");
+	}
+	imsgbuf_allow_fdpass(&session->iev.ibuf);
 	session->iev.handler = notify_dispatch_session;
 	session->iev.events = EV_READ;
 	session->iev.handler_arg = NULL;
@@ -511,7 +513,7 @@ notify_dispatch(int fd, short event, void *arg)
 	struct gotd_secret *s;
 
 	if (event & EV_READ) {
-		if ((n = imsg_read(imsgbuf)) == -1 && errno != EAGAIN)
+		if ((n = imsgbuf_read(imsgbuf)) == -1)
 			fatal("imsg_read error");
 		if (n == 0) {
 			/* Connection closed. */
@@ -521,14 +523,8 @@ notify_dispatch(int fd, short event, void *arg)
 	}
 
 	if (event & EV_WRITE) {
-		n = msgbuf_write(&imsgbuf->w);
-		if (n == -1 && errno != EAGAIN)
+		if (imsgbuf_write(imsgbuf) == -1)
 			fatal("msgbuf_write");
-		if (n == 0) {
-			/* Connection closed. */
-			shut = 1;
-			goto done;
-		}
 	}
 
 	for (;;) {
@@ -628,7 +624,10 @@ notify_main(const char *title, struct gotd_repolist *r
 	signal_add(&evsighup, NULL);
 	signal_add(&evsigusr1, NULL);
 
-	imsg_init(&gotd_notify.parent_iev.ibuf, GOTD_FILENO_MSG_PIPE);
+	if (imsgbuf_init(&gotd_notify.parent_iev.ibuf, GOTD_FILENO_MSG_PIPE)
+	    == -1)
+		fatal("imsgbuf_init");
+	imsgbuf_allow_fdpass(&gotd_notify.parent_iev.ibuf);
 	gotd_notify.parent_iev.handler = notify_dispatch;
 	gotd_notify.parent_iev.events = EV_READ;
 	gotd_notify.parent_iev.handler_arg = NULL;
blob - a729ba6cc63a9ea335701d415c19ad7346b7e1d9
blob + 76f3da1876604e0cb3f74affa07b5a60239ed6f0
--- gotd/repo_read.c
+++ gotd/repo_read.c
@@ -282,7 +282,9 @@ list_refs(struct imsg *imsg)
 	if (client->fd == -1)
 		return got_error(GOT_ERR_PRIVSEP_NO_FD);
 
-	imsg_init(&ibuf, client->fd);
+	if (imsgbuf_init(&ibuf, client->fd) == -1)
+		return got_error_from_errno("imsgbuf_init");
+	imsgbuf_allow_fdpass(&ibuf);
 
 	err = got_ref_list(&refs, repo_read.repo, "",
 	    got_ref_cmp_by_name, NULL);
@@ -362,7 +364,7 @@ list_refs(struct imsg *imsg)
 	err = gotd_imsg_flush(&ibuf);
 done:
 	got_ref_list_free(&refs);
-	imsg_clear(&ibuf);
+	imsgbuf_clear(&ibuf);
 	return err;
 }
 
@@ -418,7 +420,9 @@ recv_want(struct imsg *imsg)
 	    got_object_id_hex(&id, hex, sizeof(hex)))
 		log_debug("client wants %s", hex);
 
-	imsg_init(&ibuf, client->fd);
+	if (imsgbuf_init(&ibuf, client->fd) == -1)
+		return got_error_from_errno("imsgbuf_init");
+	imsgbuf_allow_fdpass(&ibuf);
 
 	err = got_object_get_type(&obj_type, repo_read.repo, &id);
 	if (err)
@@ -435,7 +439,7 @@ recv_want(struct imsg *imsg)
 	}
 
 	gotd_imsg_send_ack(&id, &ibuf, PROC_REPO_READ, repo_read.pid);
-	imsg_clear(&ibuf);
+	imsgbuf_clear(&ibuf);
 	return err;
 }
 
@@ -463,7 +467,9 @@ recv_have(struct imsg *imsg)
 	    got_object_id_hex(&id, hex, sizeof(hex)))
 		log_debug("client has %s", hex);
 
-	imsg_init(&ibuf, client->fd);
+	if (imsgbuf_init(&ibuf, client->fd) == -1)
+		return got_error_from_errno("imsgbuf_init");
+	imsgbuf_allow_fdpass(&ibuf);
 
 	err = got_object_get_type(&obj_type, repo_read.repo, &id);
 	if (err) {
@@ -490,7 +496,7 @@ recv_have(struct imsg *imsg)
 
 	gotd_imsg_send_ack(&id, &ibuf, PROC_REPO_READ, repo_read.pid);
 done:
-	imsg_clear(&ibuf);
+	imsgbuf_clear(&ibuf);
 	return err;
 }
 
@@ -617,7 +623,9 @@ send_packfile(struct imsg *imsg, struct gotd_imsgev *i
 	if (client->delta_cache_fd == -1 || client->pack_pipe == -1)
 		return got_error(GOT_ERR_PRIVSEP_NO_FD);
 
-	imsg_init(&ibuf, client->fd);
+	if (imsgbuf_init(&ibuf, client->fd) == -1)
+		return got_error_from_errno("imsgbuf_init");
+	imsgbuf_allow_fdpass(&ibuf);
 
 	delta_cache = fdopen(client->delta_cache_fd, "w+");
 	if (delta_cache == NULL) {
@@ -661,7 +669,7 @@ done:
 	client->delta_cache_fd = -1;
 	if (delta_cache != NULL && fclose(delta_cache) == EOF && err == NULL)
 		err = got_error_from_errno("fclose");
-	imsg_clear(&ibuf);
+	imsgbuf_clear(&ibuf);
 	free(want_ids.ids);
 	free(have_ids.ids);
 	return err;
@@ -679,18 +687,15 @@ repo_read_dispatch_session(int fd, short event, void *
 	struct repo_read_client *client = &repo_read_client;
 
 	if (event & EV_READ) {
-		if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN)
+		if ((n = imsgbuf_read(ibuf)) == -1)
 			fatal("imsg_read error");
 		if (n == 0)	/* Connection closed. */
 			shut = 1;
 	}
 
 	if (event & EV_WRITE) {
-		n = msgbuf_write(&ibuf->w);
-		if (n == -1 && errno != EAGAIN)
+		if (imsgbuf_write(ibuf) == -1)
 			fatal("msgbuf_write");
-		if (n == 0)	/* Connection closed. */
-			shut = 1;
 	}
 
 	while (err == NULL && check_cancelled(NULL) == NULL) {
@@ -777,7 +782,9 @@ recv_connect(struct imsg *imsg)
 	if (repo_read.session_fd == -1)
 		return got_error(GOT_ERR_PRIVSEP_NO_FD);
 
-	imsg_init(&iev->ibuf, repo_read.session_fd);
+	if (imsgbuf_init(&iev->ibuf, repo_read.session_fd) == -1)
+		return got_error_from_errno("imsgbuf_init");
+	imsgbuf_allow_fdpass(&iev->ibuf);
 	iev->handler = repo_read_dispatch_session;
 	iev->events = EV_READ;
 	iev->handler_arg = NULL;
@@ -800,18 +807,15 @@ repo_read_dispatch(int fd, short event, void *arg)
 	struct repo_read_client *client = &repo_read_client;
 
 	if (event & EV_READ) {
-		if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN)
+		if ((n = imsgbuf_read(ibuf)) == -1)
 			fatal("imsg_read error");
 		if (n == 0)	/* Connection closed. */
 			shut = 1;
 	}
 
 	if (event & EV_WRITE) {
-		n = msgbuf_write(&ibuf->w);
-		if (n == -1 && errno != EAGAIN)
+		if (imsgbuf_write(ibuf) == -1)
 			fatal("msgbuf_write");
-		if (n == 0)	/* Connection closed. */
-			shut = 1;
 	}
 
 	while (err == NULL && check_cancelled(NULL) == NULL) {
@@ -897,7 +901,11 @@ repo_read_main(const char *title, const char *repo_pat
 	signal(SIGPIPE, SIG_IGN);
 	signal(SIGHUP, SIG_IGN);
 
-	imsg_init(&iev.ibuf, GOTD_FILENO_MSG_PIPE);
+	if (imsgbuf_init(&iev.ibuf, GOTD_FILENO_MSG_PIPE) == -1) {
+		err = got_error_from_errno("imsgbuf_init");
+		goto done;
+	}
+	imsgbuf_allow_fdpass(&iev.ibuf);
 	iev.handler = repo_read_dispatch;
 	iev.events = EV_READ;
 	iev.handler_arg = NULL;
blob - 5248bb34c552d1020f0713b91affb6991385c95f
blob + 697e0fffca31dd7a8ab687783615fdb47305f994
--- gotd/repo_write.c
+++ gotd/repo_write.c
@@ -274,7 +274,9 @@ list_refs(struct imsg *imsg)
 	client->nref_new = 0;
 	client->nref_move = 0;
 
-	imsg_init(&ibuf, client->fd);
+	if (imsgbuf_init(&ibuf, client->fd) == -1)
+		return got_error_from_errno("imsgbuf_init");
+	imsgbuf_allow_fdpass(&ibuf);
 
 	err = got_ref_list(&refs, repo_write.repo, "",
 	    got_ref_cmp_by_name, NULL);
@@ -320,7 +322,7 @@ list_refs(struct imsg *imsg)
 	err = gotd_imsg_flush(&ibuf);
 done:
 	got_ref_list_free(&refs);
-	imsg_clear(&ibuf);
+	imsgbuf_clear(&ibuf);
 	return err;
 }
 
@@ -629,11 +631,15 @@ recv_ref_update(struct imsg *imsg)
 	if (datalen != sizeof(iref) + iref.name_len)
 		return got_error(GOT_ERR_PRIVSEP_LEN);
 
-	imsg_init(&ibuf, client->fd);
+	if (imsgbuf_init(&ibuf, client->fd))
+		return got_error_from_errno("imsgbuf_init");
+	imsgbuf_allow_fdpass(&ibuf);
 
 	refname = strndup(imsg->data + sizeof(iref), iref.name_len);
-	if (refname == NULL)
-		return got_error_from_errno("strndup");
+	if (refname == NULL) {
+		err = got_error_from_errno("strndup");
+		goto done;
+	}
 
 	ref_update = calloc(1, sizeof(*ref_update));
 	if (ref_update == NULL) {
@@ -1163,7 +1169,9 @@ report_pack_status(const struct got_error *unpack_err)
 	const char *unpack_ok = "unpack ok\n";
 	size_t len;
 
-	imsg_init(&ibuf, client->fd);
+	if (imsgbuf_init(&ibuf, client->fd))
+		return got_error_from_errno("imsgbuf_init");
+	imsgbuf_allow_fdpass(&ibuf);
 
 	if (unpack_err)
 		istatus.reason_len = strlen(unpack_err->msg);
@@ -1193,7 +1201,7 @@ report_pack_status(const struct got_error *unpack_err)
 
 	err = gotd_imsg_flush(&ibuf);
 done:
-	imsg_clear(&ibuf);
+	imsgbuf_clear(&ibuf);
 	return err;
 }
 
@@ -1232,8 +1240,10 @@ recv_packfile(int *have_packfile, struct imsg *imsg)
 	pack = &client->pack;
 	memset(pack, 0, sizeof(*pack));
 	pack->fd = imsg_get_fd(imsg);
-	if (pack->fd == -1)
-		return got_error(GOT_ERR_PRIVSEP_NO_FD);
+	if (pack->fd == -1) {
+		err = got_error(GOT_ERR_PRIVSEP_NO_FD);
+		goto done;
+	}
 
 	err = got_delta_cache_alloc(&pack->delta_cache);
 	if (err)
@@ -2164,18 +2174,15 @@ repo_write_dispatch_session(int fd, short event, void 
 	int shut = 0, have_packfile = 0;
 
 	if (event & EV_READ) {
-		if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN)
+		if ((n = imsgbuf_read(ibuf)) == -1)
 			fatal("imsg_read error");
 		if (n == 0)	/* Connection closed. */
 			shut = 1;
 	}
 
 	if (event & EV_WRITE) {
-		n = msgbuf_write(&ibuf->w);
-		if (n == -1 && errno != EAGAIN)
+		if (imsgbuf_write(ibuf) == -1)
 			fatal("msgbuf_write");
-		if (n == 0)	/* Connection closed. */
-			shut = 1;
 	}
 
 	for (;;) {
@@ -2297,7 +2304,9 @@ recv_connect(struct imsg *imsg)
 	if (repo_write.session_fd == -1)
 		return got_error(GOT_ERR_PRIVSEP_NO_FD);
 
-	imsg_init(&iev->ibuf, repo_write.session_fd);
+	if (imsgbuf_init(&iev->ibuf, repo_write.session_fd) == -1)
+		return got_error_from_errno("imsgbuf_init");
+	imsgbuf_allow_fdpass(&iev->ibuf);
 	iev->handler = repo_write_dispatch_session;
 	iev->events = EV_READ;
 	iev->handler_arg = NULL;
@@ -2320,7 +2329,7 @@ repo_write_dispatch(int fd, short event, void *arg)
 	struct repo_write_client *client = &repo_write_client;
 
 	if (event & EV_READ) {
-		if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN)
+		if ((n = imsgbuf_read(ibuf)) == -1)
 			fatal("imsg_read error");
 		if (n == 0) {	/* Connection closed. */
 			shut = 1;
@@ -2329,13 +2338,8 @@ repo_write_dispatch(int fd, short event, void *arg)
 	}
 
 	if (event & EV_WRITE) {
-		n = msgbuf_write(&ibuf->w);
-		if (n == -1 && errno != EAGAIN)
+		if (imsgbuf_write(ibuf) == -1)
 			fatal("msgbuf_write");
-		if (n == 0) {	/* Connection closed. */
-			shut = 1;
-			goto done;
-		}
 	}
 
 	while (err == NULL) {
@@ -2427,7 +2431,11 @@ repo_write_main(const char *title, const char *repo_pa
 	signal(SIGPIPE, SIG_IGN);
 	signal(SIGHUP, SIG_IGN);
 
-	imsg_init(&iev.ibuf, GOTD_FILENO_MSG_PIPE);
+	if (imsgbuf_init(&iev.ibuf, GOTD_FILENO_MSG_PIPE) == -1) {
+		err = got_error_from_errno("imsgbuf_init");
+		goto done;
+	}
+	imsgbuf_allow_fdpass(&iev.ibuf);
 	iev.handler = repo_write_dispatch;
 	iev.events = EV_READ;
 	iev.handler_arg = NULL;
blob - 6e4b3757b5948f6a34ee747fdf5d7c03d32f9eed
blob + d39251b60acc58450371d8714d902209d7e8d630
--- gotd/session_read.c
+++ gotd/session_read.c
@@ -105,7 +105,7 @@ disconnect(struct gotd_session_client *client)
 	    GOTD_IMSG_DISCONNECT, PROC_SESSION_READ, -1, NULL, 0) == -1)
 		log_warn("imsg compose DISCONNECT");
 
-	imsg_clear(&gotd_session.repo_child_iev.ibuf);
+	imsgbuf_clear(&gotd_session.repo_child_iev.ibuf);
 	event_del(&gotd_session.repo_child_iev.ev);
 	evtimer_del(&client->tmo);
 	close(client->fd);
@@ -134,9 +134,12 @@ disconnect_on_error(struct gotd_session_client *client
 
 	if (err->code != GOT_ERR_EOF) {
 		log_warnx("uid %d: %s", client->euid, err->msg);
-		imsg_init(&ibuf, client->fd);
-		gotd_imsg_send_error(&ibuf, 0, PROC_SESSION_READ, err);
-		imsg_clear(&ibuf);
+		if (imsgbuf_init(&ibuf, client->fd) == -1) {
+			log_warn("imsgbuf_init");
+		} else {
+			gotd_imsg_send_error(&ibuf, 0, PROC_SESSION_READ, err);
+			imsgbuf_clear(&ibuf);
+		}
 	}
 
 	disconnect(client);
@@ -201,7 +204,7 @@ session_dispatch_repo_child(int fd, short event, void 
 	struct imsg imsg;
 
 	if (event & EV_READ) {
-		if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN)
+		if ((n = imsgbuf_read(ibuf)) == -1)
 			fatal("imsg_read error");
 		if (n == 0) {
 			/* Connection closed. */
@@ -211,14 +214,8 @@ session_dispatch_repo_child(int fd, short event, void 
 	}
 
 	if (event & EV_WRITE) {
-		n = msgbuf_write(&ibuf->w);
-		if (n == -1 && errno != EAGAIN)
+		if (imsgbuf_write(ibuf) == -1)
 			fatal("msgbuf_write");
-		if (n == 0) {
-			/* Connection closed. */
-			shut = 1;
-			goto done;
-		}
 	}
 
 	for (;;) {
@@ -459,51 +456,41 @@ session_dispatch_client(int fd, short events, void *ar
 	ssize_t n;
 
 	if (events & EV_WRITE) {
-		while (ibuf->w.queued) {
-			n = msgbuf_write(&ibuf->w);
-			if (n == -1 && errno != EAGAIN) {
-				err = got_error_from_errno("imsg_flush");
-				disconnect_on_error(client, err);
-				return;
-			}
-			if (n == 0) {
-				/* Connection closed. */
-				err = got_error(GOT_ERR_EOF);
-				disconnect_on_error(client, err);
-				return;
-			}
+		if (imsgbuf_write(ibuf) == -1) {
+			err = got_error_from_errno("imsg_flush");
+			disconnect_on_error(client, err);
+			return;
 		}
 
-		if (client->flush_disconnect) {
+		if (imsgbuf_queuelen(ibuf) == 0 &&
+		    client->flush_disconnect) {
 			disconnect(client);
 			return;
 		}
 	}
 
-	if ((events & EV_READ) == 0)
-		return;
-
-	memset(&imsg, 0, sizeof(imsg));
+	if (events & EV_READ) {
+		n = imsgbuf_read(ibuf);
+		if (n == -1) {
+			err = got_error_from_errno("imsgbuf_read");
+			disconnect_on_error(client, err);
+			return;
+		}
+		if (n == 0) {
+			 err = got_error(GOT_ERR_EOF);
+			 disconnect_on_error(client, err);
+			 return;
+		}
+	}
 
 	while (err == NULL) {
-		err = gotd_imsg_recv(&imsg, ibuf, 0);
-		if (err) {
-			if (err->code == GOT_ERR_PRIVSEP_READ)
-				err = NULL;
-			else if (err->code == GOT_ERR_EOF &&
-			    gotd_session.state ==
-			    GOTD_STATE_EXPECT_CAPABILITIES) {
-				/*
-				 * The client has closed its socket before
-				 * sending its capability announcement.
-				 * This can happen when Git clients have
-				 * no ref-updates to send.
-				 */
-				disconnect_on_error(client, err);
-				return;
-			}
+		n = imsg_get(ibuf, &imsg);
+		if (n == -1) {
+			err = got_error_from_errno("imsg_get");
 			break;
 		}
+		if (n == 0)
+			break;
 
 		evtimer_del(&client->tmo);
 
@@ -684,7 +671,9 @@ recv_connect(struct imsg *imsg)
 	if (client->username == NULL)
 		return got_error_from_errno("strndup");
 
-	imsg_init(&client->iev.ibuf, client->fd);
+	if (imsgbuf_init(&client->iev.ibuf, client->fd) == -1)
+		return got_error_from_errno("imsgbuf_init");
+	imsgbuf_allow_fdpass(&client->iev.ibuf);
 	client->iev.handler = session_dispatch_client;
 	client->iev.events = EV_READ;
 	client->iev.handler_arg = NULL;
@@ -726,7 +715,11 @@ recv_repo_child(struct imsg *imsg)
 	if (fd == -1)
 		return got_error(GOT_ERR_PRIVSEP_NO_FD);
 
-	imsg_init(&gotd_session.repo_child_iev.ibuf, fd);
+	if (imsgbuf_init(&gotd_session.repo_child_iev.ibuf, fd)) {
+		close(fd);
+		return got_error_from_errno("imsgbuf_init");
+	}
+	imsgbuf_allow_fdpass(&gotd_session.repo_child_iev.ibuf);
 	gotd_session.repo_child_iev.handler = session_dispatch_repo_child;
 	gotd_session.repo_child_iev.events = EV_READ;
 	gotd_session.repo_child_iev.handler_arg = NULL;
@@ -753,7 +746,7 @@ session_dispatch(int fd, short event, void *arg)
 	struct imsg imsg;
 
 	if (event & EV_READ) {
-		if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN)
+		if ((n = imsgbuf_read(ibuf)) == -1)
 			fatal("imsg_read error");
 		if (n == 0) {
 			/* Connection closed. */
@@ -763,8 +756,8 @@ session_dispatch(int fd, short event, void *arg)
 	}
 
 	if (event & EV_WRITE) {
-		n = msgbuf_write(&ibuf->w);
-		if (n == -1 && errno != EAGAIN)
+		n = imsgbuf_write(ibuf);
+		if (n == -1)
 			fatal("msgbuf_write");
 		if (n == 0) {
 			/* Connection closed. */
@@ -843,7 +836,11 @@ session_read_main(const char *title, const char *repo_
 	    sizeof(gotd_session.request_timeout));
 	gotd_session.repo_cfg = repo_cfg;
 
-	imsg_init(&gotd_session.notifier_iev.ibuf, -1);
+	if (imsgbuf_init(&gotd_session.notifier_iev.ibuf, -1) == -1) {
+		err = got_error_from_errno("imsgbuf_init");
+		goto done;
+	}
+	imsgbuf_allow_fdpass(&gotd_session.notifier_iev.ibuf);
 
 	err = got_repo_open(&gotd_session.repo, repo_path, NULL, pack_fds);
 	if (err)
@@ -879,7 +876,12 @@ session_read_main(const char *title, const char *repo_
 	gotd_session_client.delta_cache_fd = -1;
 	gotd_session_client.accept_flush_pkt = 1;
 
-	imsg_init(&gotd_session.parent_iev.ibuf, GOTD_FILENO_MSG_PIPE);
+	if (imsgbuf_init(&gotd_session.parent_iev.ibuf, GOTD_FILENO_MSG_PIPE)
+	    == -1) {
+		err = got_error_from_errno("imsgbuf_init");
+		goto done;
+	}
+	imsgbuf_allow_fdpass(&gotd_session.parent_iev.ibuf);
 	gotd_session.parent_iev.handler = session_dispatch;
 	gotd_session.parent_iev.events = EV_READ;
 	gotd_session.parent_iev.handler_arg = NULL;
blob - 35c0f7ee43ce29e0246b866883fa599ac9f1f81c
blob + 7d1bc4205b658904dd18617645e79b0df371cf6f
--- gotd/session_write.c
+++ gotd/session_write.c
@@ -116,7 +116,7 @@ disconnect(struct gotd_session_client *client)
 	    GOTD_IMSG_DISCONNECT, PROC_SESSION_WRITE, -1, NULL, 0) == -1)
 		log_warn("imsg compose DISCONNECT");
 
-	imsg_clear(&gotd_session.repo_child_iev.ibuf);
+	imsgbuf_clear(&gotd_session.repo_child_iev.ibuf);
 	event_del(&gotd_session.repo_child_iev.ev);
 	evtimer_del(&client->tmo);
 	close(client->fd);
@@ -145,9 +145,13 @@ disconnect_on_error(struct gotd_session_client *client
 
 	if (err->code != GOT_ERR_EOF) {
 		log_warnx("uid %d: %s", client->euid, err->msg);
-		imsg_init(&ibuf, client->fd);
-		gotd_imsg_send_error(&ibuf, 0, PROC_SESSION_WRITE, err);
-		imsg_clear(&ibuf);
+		if (imsgbuf_init(&ibuf, client->fd) == -1) {
+			log_warn("imsgbuf_init");
+		} else {
+			gotd_imsg_send_error(&ibuf, 0, PROC_SESSION_WRITE,
+			    err);
+			imsgbuf_clear(&ibuf);
+		}
 	}
 
 	disconnect(client);
@@ -867,7 +871,7 @@ session_dispatch_repo_child(int fd, short event, void 
 	struct imsg imsg;
 
 	if (event & EV_READ) {
-		if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN)
+		if ((n = imsgbuf_read(ibuf)) == -1)
 			fatal("imsg_read error");
 		if (n == 0) {
 			/* Connection closed. */
@@ -877,14 +881,8 @@ session_dispatch_repo_child(int fd, short event, void 
 	}
 
 	if (event & EV_WRITE) {
-		n = msgbuf_write(&ibuf->w);
-		if (n == -1 && errno != EAGAIN)
+		if (imsgbuf_write(ibuf) == -1)
 			fatal("msgbuf_write");
-		if (n == 0) {
-			/* Connection closed. */
-			shut = 1;
-			goto done;
-		}
 	}
 
 	for (;;) {
@@ -1209,59 +1207,50 @@ session_dispatch_client(int fd, short events, void *ar
 	ssize_t n;
 
 	if (events & EV_WRITE) {
-		while (ibuf->w.queued) {
-			n = msgbuf_write(&ibuf->w);
-			if (n == -1 && errno == EPIPE) {
-				/*
-				 * The client has closed its socket.
-				 * This can happen when Git clients are
-				 * done sending pack file data.
-				 */
-				msgbuf_clear(&ibuf->w);
-				continue;
-			} else if (n == -1 && errno != EAGAIN) {
-				err = got_error_from_errno("imsg_flush");
-				disconnect_on_error(client, err);
-				return;
-			}
-			if (n == 0) {
-				/* Connection closed. */
-				err = got_error(GOT_ERR_EOF);
-				disconnect_on_error(client, err);
+		if (imsgbuf_write(ibuf) == -1) {
+			/*
+			 * The client has closed its socket.  This can
+			 * happen when Git clients are done sending
+			 * pack file data.
+			 */
+			if (errno == EPIPE) {
+				disconnect(client);
 				return;
 			}
+			err = got_error_from_errno("imsgbuf_flush");
+			disconnect_on_error(client, err);
+			return;
 		}
 
-		if (client->flush_disconnect) {
+		if (imsgbuf_queuelen(ibuf) == 0 &&
+		    client->flush_disconnect) {
 			disconnect(client);
 			return;
 		}
 	}
 
-	if ((events & EV_READ) == 0)
-		return;
+	if (events & EV_READ) {
+		n = imsgbuf_read(ibuf);
+		if (n == -1) {
+			err = got_error_from_errno("imsgbuf_read");
+			disconnect_on_error(client, err);
+			return;
+		}
+		if (n == 0) {
+			err = got_error(GOT_ERR_EOF);
+			disconnect_on_error(client, err);
+			return;
+		}
+	}
 
-	memset(&imsg, 0, sizeof(imsg));
-
 	while (err == NULL) {
-		err = gotd_imsg_recv(&imsg, ibuf, 0);
-		if (err) {
-			if (err->code == GOT_ERR_PRIVSEP_READ)
-				err = NULL;
-			else if (err->code == GOT_ERR_EOF &&
-			    gotd_session.state ==
-			    GOTD_STATE_EXPECT_CAPABILITIES) {
-				/*
-				 * The client has closed its socket before
-				 * sending its capability announcement.
-				 * This can happen when Git clients have
-				 * no ref-updates to send.
-				 */
-				disconnect_on_error(client, err);
-				return;
-			}
+		n = imsg_get(ibuf, &imsg);
+		if (n == -1) {
+			err = got_error_from_errno("imsg_get");
 			break;
 		}
+		if (n == 0)
+			break;
 
 		evtimer_del(&client->tmo);
 
@@ -1418,7 +1407,9 @@ recv_connect(struct imsg *imsg)
 	if (client->username == NULL)
 		return got_error_from_errno("strndup");
 
-	imsg_init(&client->iev.ibuf, client->fd);
+	if (imsgbuf_init(&client->iev.ibuf, client->fd) == -1)
+		return got_error_from_errno("imsgbuf_init");
+	imsgbuf_allow_fdpass(&client->iev.ibuf);
 	client->iev.handler = session_dispatch_client;
 	client->iev.events = EV_READ;
 	client->iev.handler_arg = NULL;
@@ -1444,7 +1435,7 @@ session_dispatch_notifier(int fd, short event, void *a
 	struct gotd_session_notif *notif;
 
 	if (event & EV_READ) {
-		if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN)
+		if ((n = imsgbuf_read(ibuf)) == -1)
 			fatal("imsg_read error");
 		if (n == 0) {
 			/* Connection closed. */
@@ -1454,14 +1445,8 @@ session_dispatch_notifier(int fd, short event, void *a
 	}
 
 	if (event & EV_WRITE) {
-		n = msgbuf_write(&ibuf->w);
-		if (n == -1 && errno != EAGAIN)
+		if (imsgbuf_write(ibuf) == -1)
 			fatal("msgbuf_write");
-		if (n == 0) {
-			/* Connection closed. */
-			shut = 1;
-			goto done;
-		}
 	}
 
 	for (;;) {
@@ -1502,8 +1487,7 @@ done:
 	} else {
 		/* This pipe is dead. Remove its event handler */
 		event_del(&iev->ev);
-		imsg_clear(&iev->ibuf);
-		imsg_init(&iev->ibuf, -1);
+		imsgbuf_clear(&iev->ibuf);
 	}
 }
 
@@ -1530,7 +1514,11 @@ recv_notifier(struct imsg *imsg)
 	if (fd == -1)
 		return NULL; /* notifications unused */
 
-	imsg_init(&iev->ibuf, fd);
+	if (imsgbuf_init(&iev->ibuf, fd) == -1) {
+		close(fd);
+		return got_error_from_errno("imsgbuf_init");
+	}
+	imsgbuf_allow_fdpass(&iev->ibuf);
 	iev->handler = session_dispatch_notifier;
 	iev->events = EV_READ;
 	iev->handler_arg = NULL;
@@ -1570,7 +1558,11 @@ recv_repo_child(struct imsg *imsg)
 	if (fd == -1)
 		return got_error(GOT_ERR_PRIVSEP_NO_FD);
 
-	imsg_init(&gotd_session.repo_child_iev.ibuf, fd);
+	if (imsgbuf_init(&gotd_session.repo_child_iev.ibuf, fd) == -1) {
+		close(fd);
+		return got_error_from_errno("imsgbuf_init");
+	}
+	imsgbuf_allow_fdpass(&gotd_session.repo_child_iev.ibuf);
 	gotd_session.repo_child_iev.handler = session_dispatch_repo_child;
 	gotd_session.repo_child_iev.events = EV_READ;
 	gotd_session.repo_child_iev.handler_arg = NULL;
@@ -1597,7 +1589,7 @@ session_dispatch(int fd, short event, void *arg)
 	struct imsg imsg;
 
 	if (event & EV_READ) {
-		if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN)
+		if ((n = imsgbuf_read(ibuf)) == -1)
 			fatal("imsg_read error");
 		if (n == 0) {
 			/* Connection closed. */
@@ -1607,14 +1599,8 @@ session_dispatch(int fd, short event, void *arg)
 	}
 
 	if (event & EV_WRITE) {
-		n = msgbuf_write(&ibuf->w);
-		if (n == -1 && errno != EAGAIN)
+		if (imsgbuf_write(ibuf) == -1)
 			fatal("msgbuf_write");
-		if (n == 0) {
-			/* Connection closed. */
-			shut = 1;
-			goto done;
-		}
 	}
 
 	for (;;) {
@@ -1692,7 +1678,11 @@ session_write_main(const char *title, const char *repo
 	    sizeof(gotd_session.request_timeout));
 	gotd_session.repo_cfg = repo_cfg;
 
-	imsg_init(&gotd_session.notifier_iev.ibuf, -1);
+	if (imsgbuf_init(&gotd_session.notifier_iev.ibuf, -1) == -1) {
+		err = got_error_from_errno("imsgbuf_init");
+		goto done;
+	}
+	imsgbuf_allow_fdpass(&gotd_session.notifier_iev.ibuf);
 
 	err = got_repo_open(&gotd_session.repo, repo_path, NULL, pack_fds);
 	if (err)
@@ -1728,7 +1718,12 @@ session_write_main(const char *title, const char *repo
 	gotd_session_client.delta_cache_fd = -1;
 	gotd_session_client.accept_flush_pkt = 1;
 
-	imsg_init(&gotd_session.parent_iev.ibuf, GOTD_FILENO_MSG_PIPE);
+	if (imsgbuf_init(&gotd_session.parent_iev.ibuf, GOTD_FILENO_MSG_PIPE)
+	    == -1) {
+		err = got_error_from_errno("imsgbuf_init");
+		goto done;
+	}
+	imsgbuf_allow_fdpass(&gotd_session.parent_iev.ibuf);
 	gotd_session.parent_iev.handler = session_dispatch;
 	gotd_session.parent_iev.events = EV_READ;
 	gotd_session.parent_iev.handler_arg = NULL;
blob - e1898650c1c8e70d57548d096c1488b8d157641b
blob + df0bdb7345fb1eed068c93d28ff3765b72dda2c7
--- gotwebd/config.c
+++ gotwebd/config.c
@@ -162,7 +162,7 @@ config_getsock(struct gotwebd *env, struct imsg *imsg)
 int
 config_setfd(struct gotwebd *env)
 {
-	int i, j, ret, fd;
+	int i, j, fd;
 
 	log_debug("%s: Allocating %d file descriptors",
 	    __func__, PRIV_FDS__MAX + GOTWEB_PACK_NUM_TEMPFILES);
@@ -176,10 +176,7 @@ config_setfd(struct gotwebd *env)
 			    IMSG_CFG_FD, 0, -1, fd, NULL, 0) == -1)
 				fatal("imsg_compose_event IMSG_CFG_FD");
 
-			do {
-				ret = imsg_flush(&env->iev_server[j].ibuf);
-			} while (ret == -1 && errno == EAGAIN);
-			if (ret == -1)
+			if (imsgbuf_flush(&env->iev_server[j].ibuf) == -1)
 				fatal("imsg_flush");
 			imsg_event_add(&env->iev_server[j]);
 		}
blob - 792b6cd2d9a880c6eda669664af5e7f61c4b8126
blob + ee7af401c35b0ddf92ce3dde4847b3bbac6ca1e2
--- gotwebd/gotwebd.c
+++ gotwebd/gotwebd.c
@@ -60,12 +60,12 @@ void
 imsg_event_add(struct imsgev *iev)
 {
 	if (iev->handler == NULL) {
-		imsg_flush(&iev->ibuf);
+		imsgbuf_flush(&iev->ibuf);
 		return;
 	}
 
 	iev->events = EV_READ;
-	if (iev->ibuf.w.queued)
+	if (imsgbuf_queuelen(&iev->ibuf))
 		iev->events |= EV_WRITE;
 
 	event_del(&iev->ev);
@@ -105,10 +105,7 @@ main_compose_sockets(struct gotwebd *env, uint32_t typ
 
 		/* prevent fd exhaustion */
 		if (d != -1) {
-			do {
-				ret = imsg_flush(&env->iev_server[i].ibuf);
-			} while (ret == -1 && errno == EAGAIN);
-			if (ret == -1)
+			if (imsgbuf_flush(&env->iev_server[i].ibuf) == -1)
 				goto err;
 			imsg_event_add(&env->iev_server[i]);
 		}
@@ -144,16 +141,14 @@ gotwebd_dispatch_sockets(int fd, short event, void *ar
 	ibuf = &iev->ibuf;
 
 	if (event & EV_READ) {
-		if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN)
+		if ((n = imsgbuf_read(ibuf)) == -1)
 			fatal("imsg_read error");
 		if (n == 0)	/* Connection closed */
 			shut = 1;
 	}
 	if (event & EV_WRITE) {
-		if ((n = msgbuf_write(&ibuf->w)) == -1 && errno != EAGAIN)
+		if (imsgbuf_write(ibuf) == -1)
 			fatal("msgbuf_write");
-		if (n == 0)	/* Connection closed */
-			shut = 1;
 	}
 
 	for (;;) {
@@ -225,7 +220,9 @@ spawn_socket_process(struct gotwebd *env, const char *
 		break;
 	default:	/* parent */
 		close(p[0]);
-		imsg_init(&env->iev_server[n].ibuf, p[1]);
+		if (imsgbuf_init(&env->iev_server[n].ibuf, p[1]) == -1)
+			fatal("imsgbuf_init");
+		imsgbuf_allow_fdpass(&env->iev_server[n].ibuf);
 		env->iev_server[n].handler = gotwebd_dispatch_sockets;
 		env->iev_server[n].data = &env->iev_server[n];
 		event_set(&env->iev_server[n].ev, p[1], EV_READ,
@@ -484,7 +481,7 @@ gotwebd_shutdown(void)
 
 	for (i = 0; i < env->nserver; ++i) {
 		event_del(&env->iev_server[i].ev);
-		imsg_clear(&env->iev_server[i].ibuf);
+		imsgbuf_clear(&env->iev_server[i].ibuf);
 		close(env->iev_server[i].ibuf.fd);
 		env->iev_server[i].ibuf.fd = -1;
 	}
blob - e4e422a7568c7bafebdb57d3377c40636ff1945b
blob + 7663af4588aefb8de1d6370b1dbb0d302d3e5f76
--- gotwebd/sockets.c
+++ gotwebd/sockets.c
@@ -98,7 +98,9 @@ sockets(struct gotwebd *env, int fd)
 
 	if ((env->iev_parent = malloc(sizeof(*env->iev_parent))) == NULL)
 		fatal("malloc");
-	imsg_init(&env->iev_parent->ibuf, fd);
+	if (imsgbuf_init(&env->iev_parent->ibuf, fd) == -1)
+		fatal("imsgbuf_init");
+	imsgbuf_allow_fdpass(&env->iev_parent->ibuf);
 	env->iev_parent->handler = sockets_dispatch_main;
 	env->iev_parent->data = env->iev_parent;
 	event_set(&env->iev_parent->ev, fd, EV_READ, sockets_dispatch_main,
@@ -257,16 +259,14 @@ sockets_dispatch_main(int fd, short event, void *arg)
 	ibuf = &iev->ibuf;
 
 	if (event & EV_READ) {
-		if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN)
+		if ((n = imsgbuf_read(ibuf)) == -1)
 			fatal("imsg_read error");
 		if (n == 0)	/* Connection closed */
 			shut = 1;
 	}
 	if (event & EV_WRITE) {
-		if ((n = msgbuf_write(&ibuf->w)) == -1 && errno != EAGAIN)
-			fatal("msgbuf_write");
-		if (n == 0)	/* Connection closed */
-			shut = 1;
+		if (imsgbuf_write(ibuf) == -1)
+			fatal("imsgbuf_write");
 	}
 
 	for (;;) {
blob - b75dde1c3fc3d4e86f60fe5fa47a7f09526358bf
blob + 33b9d44eaba31b5cde942038314805a373eab474
--- lib/error.c
+++ lib/error.c
@@ -258,6 +258,8 @@ const struct got_error *
 got_error(int code)
 {
 	size_t i;
+
+	if (code == GOT_ERR_PRIVSEP_NO_FD) abort();
 
 	for (i = 0; i < nitems(got_errors); i++) {
 		if (code == got_errors[i].code)
blob - 1fbe141ad4fdf719e9063bc6be0a064606e32eeb
blob + d6fcc31ded7e98bfd09f6f92081aa5e857628837
--- lib/fetch.c
+++ lib/fetch.c
@@ -268,7 +268,11 @@ got_fetch_pack(struct got_object_id **pack_hash, struc
 		err = got_error_from_errno("close");
 		goto done;
 	}
-	imsg_init(&fetchibuf, imsg_fetchfds[0]);
+	if (imsgbuf_init(&fetchibuf, imsg_fetchfds[0]) == -1) {
+		err = got_error_from_errno("imsgbuf_init");
+		goto done;
+	}
+	imsgbuf_allow_fdpass(&fetchibuf);
 	nfetchfd = dup(fetchfd);
 	if (nfetchfd == -1) {
 		err = got_error_from_errno("dup");
@@ -461,7 +465,11 @@ got_fetch_pack(struct got_object_id **pack_hash, struc
 		err = got_error_from_errno("close");
 		goto done;
 	}
-	imsg_init(&idxibuf, imsg_idxfds[0]);
+	if (imsgbuf_init(&idxibuf, imsg_idxfds[0]) == -1) {
+		err = got_error_from_errno("imsgbuf_init");
+		goto done;
+	}
+	imsgbuf_allow_fdpass(&idxibuf);
 
 	npackfd = dup(packfd);
 	if (npackfd == -1) {
blob - d9b43df9dfbb89553d6f95931f5ef24cb8e2d9cf
blob + add1155ed67009205f50bb3ebbdbd496647217cd
--- lib/load.c
+++ lib/load.c
@@ -479,7 +479,11 @@ got_repo_load(FILE *in, struct got_pathlist_head *refs
 		goto done;
 	}
 	imsg_idxfds[1] = -1;
-	imsg_init(&idxibuf, imsg_idxfds[0]);
+	if (imsgbuf_init(&idxibuf, imsg_idxfds[0]) == -1) {
+		err = got_error_from_errno("imsgbuf_init");
+		goto done;
+	}
+	imsgbuf_allow_fdpass(&idxibuf);
 
 	err = got_privsep_send_index_pack_req(&idxibuf, &id, packfd);
 	if (err)
blob - b1018f015b86359c153c793f5897fe2b68a21ae3
blob + ed0b19ffc5bae4fb3669d045bd44144e267275ab
--- lib/object_open_privsep.c
+++ lib/object_open_privsep.c
@@ -379,7 +379,14 @@ start_child(struct got_repository *repo, int type)
 
 	repo->privsep_children[type].imsg_fd = imsg_fds[0];
 	repo->privsep_children[type].pid = pid;
-	imsg_init(ibuf, imsg_fds[0]);
+	if (imsgbuf_init(ibuf, imsg_fds[0]) == -1) {
+		err = got_error_from_errno("imsgbuf_init");
+		close(imsg_fds[0]);
+		free(ibuf);
+		return err;
+	}
+	imsgbuf_allow_fdpass(ibuf);
+
 	repo->privsep_children[type].ibuf = ibuf;
 
 	return NULL;
blob - 392472bce98fd2f6da0c0089c8185d3e2fb76257
blob + 5b83d584098fac8855f9938cbe05d3425d7c6bde
--- lib/pack.c
+++ lib/pack.c
@@ -782,7 +782,13 @@ got_pack_start_privsep_child(struct got_pack *pack, st
 	}
 	pack->privsep_child->imsg_fd = imsg_fds[0];
 	pack->privsep_child->pid = pid;
-	imsg_init(ibuf, imsg_fds[0]);
+	if (imsgbuf_init(ibuf, imsg_fds[0]) == -1) {
+		err = got_error_from_errno("imsgbuf_init");
+		close(imsg_fds[0]);
+		goto done;
+	}
+	imsgbuf_allow_fdpass(ibuf);
+
 	pack->privsep_child->ibuf = ibuf;
 
 	err = got_privsep_init_pack_child(ibuf, pack, packidx);
@@ -819,7 +825,7 @@ pack_stop_privsep_child(struct got_pack *pack)
 	err = got_privsep_wait_for_child(pack->privsep_child->pid);
 	if (close_err && err == NULL)
 		err = close_err;
-	imsg_clear(pack->privsep_child->ibuf);
+	imsgbuf_clear(pack->privsep_child->ibuf);
 	free(pack->privsep_child->ibuf);
 	free(pack->privsep_child);
 	pack->privsep_child = NULL;
blob - 199fbf39d0ab1b0f251632eac6e1152e9b8d27ae
blob + 0e7c4171ee06743bd3b8004b3c0b3158aa3118da
--- lib/patch.c
+++ lib/patch.c
@@ -1075,7 +1075,11 @@ got_patch(int fd, struct got_worktree *worktree, struc
 		goto done;
 	}
 	imsg_fds[1] = -1;
-	imsg_init(ibuf, imsg_fds[0]);
+	if (imsgbuf_init(ibuf, imsg_fds[0]) == -1) {
+		err = got_error_from_errno("imsgbuf_init");
+		goto done;
+	}
+	imsgbuf_allow_fdpass(ibuf);
 
 	err = send_patch(ibuf, fd);
 	fd = -1;
@@ -1150,7 +1154,7 @@ done:
 	if (fd != -1 && close(fd) == -1 && err == NULL)
 		err = got_error_from_errno("close");
 	if (ibuf != NULL)
-		imsg_clear(ibuf);
+		imsgbuf_clear(ibuf);
 	if (imsg_fds[0] != -1 && close(imsg_fds[0]) == -1 && err == NULL)
 		err = got_error_from_errno("close");
 	if (imsg_fds[1] != -1 && close(imsg_fds[1]) == -1 && err == NULL)
blob - ba76f7fe713a805f77718060b1d7ab0e18377246
blob + 64d704e994ba8ed1e51bc799989fa95c1b58c180
--- lib/privsep.c
+++ lib/privsep.c
@@ -73,12 +73,9 @@ read_imsg(struct imsgbuf *ibuf)
 		return err;
 	}
 
-	n = imsg_read(ibuf);
-	if (n == -1) {
-		if (errno == EAGAIN) /* Could be a file-descriptor leak. */
-			return got_error(GOT_ERR_PRIVSEP_NO_FD);
+	n = imsgbuf_read(ibuf);
+	if (n == -1)
 		return got_error(GOT_ERR_PRIVSEP_READ);
-	}
 	if (n == 0)
 		return got_error(GOT_ERR_PRIVSEP_PIPE);
 
@@ -184,11 +181,10 @@ got_privsep_send_error(struct imsgbuf *ibuf, const str
 		return;
 	}
 
-	ret = imsg_flush(ibuf);
+	ret = imsgbuf_flush(ibuf);
 	if (ret == -1) {
 		fprintf(stderr, "%s: error %d \"%s\": imsg_flush: %s\n",
 		    getprogname(), err->code, err->msg, strerror(errno));
-		imsg_clear(ibuf);
 		return;
 	}
 }
@@ -202,10 +198,8 @@ flush_imsg(struct imsgbuf *ibuf)
 	if (err)
 		return err;
 
-	if (imsg_flush(ibuf) == -1) {
-		imsg_clear(ibuf);
+	if (imsgbuf_flush(ibuf) == -1)
 		return got_error_from_errno("imsg_flush");
-	}
 
 	return NULL;
 }
@@ -219,14 +213,21 @@ got_privsep_flush_imsg(struct imsgbuf *ibuf)
 const struct got_error *
 got_privsep_send_stop(int fd)
 {
+	const struct got_error *err = NULL;
 	struct imsgbuf ibuf;
 
-	imsg_init(&ibuf, fd);
+	if (imsgbuf_init(&ibuf, fd) == -1)
+		return got_error_from_errno("imsgbuf_init");
 
-	if (imsg_compose(&ibuf, GOT_IMSG_STOP, 0, 0, -1, NULL, 0) == -1)
-		return got_error_from_errno("imsg_compose STOP");
+	if (imsg_compose(&ibuf, GOT_IMSG_STOP, 0, 0, -1, NULL, 0) == -1) {
+		err = got_error_from_errno("imsg_compose STOP");
+		goto done;
+	}
 
-	return flush_imsg(&ibuf);
+	err = flush_imsg(&ibuf);
+ done:
+	imsgbuf_clear(&ibuf);
+	return err;
 }
 
 const struct got_error *
blob - fbd9ee7326aed4db2e259b9834e9dde9dc4fada1
blob + 7f63a506515ee1d8a67b0ae928d5421def842e96
--- lib/read_gitconfig_privsep.c
+++ lib/read_gitconfig_privsep.c
@@ -107,7 +107,11 @@ got_repo_read_gitconfig(int *gitconfig_repository_form
 		goto done;
 	}
 	imsg_fds[1] = -1;
-	imsg_init(ibuf, imsg_fds[0]);
+	if (imsgbuf_init(ibuf, imsg_fds[0]) == -1) {
+		err = got_error_from_errno("imsgbuf_init");
+		goto done;
+	}
+	imsgbuf_allow_fdpass(ibuf);
 
 	err = got_privsep_send_gitconfig_parse_req(ibuf, fd);
 	if (err)
blob - 3fcaa54542762f35cd1254c5af1d6e8c73100b77
blob + 65a45d6c4b0370327816d24b4647648c52f66e46
--- lib/read_gotconfig_privsep.c
+++ lib/read_gotconfig_privsep.c
@@ -89,7 +89,11 @@ got_gotconfig_read(struct got_gotconfig **conf, const 
 		goto done;
 	}
 	imsg_fds[1] = -1;
-	imsg_init(ibuf, imsg_fds[0]);
+	if (imsgbuf_init(ibuf, imsg_fds[0]) == -1) {
+		err = got_error_from_errno("imsgbuf_init");
+		goto done;
+	}
+	imsgbuf_allow_fdpass(ibuf);
 
 	err = got_privsep_send_gotconfig_parse_req(ibuf, fd);
 	if (err)
blob - e3ccf0e40499cc4a59881e8b6acb10735537f667
blob + f50a14103f7f51da97f88f3efea4286d891e3e65
--- lib/repository.c
+++ lib/repository.c
@@ -881,7 +881,7 @@ got_repo_close(struct got_repository *repo)
 	for (i = 0; i < nitems(repo->privsep_children); i++) {
 		if (repo->privsep_children[i].imsg_fd == -1)
 			continue;
-		imsg_clear(repo->privsep_children[i].ibuf);
+		imsgbuf_clear(repo->privsep_children[i].ibuf);
 		free(repo->privsep_children[i].ibuf);
 		err = got_privsep_send_stop(repo->privsep_children[i].imsg_fd);
 		if (err && err->code == GOT_ERR_EOF)
blob - 7c31b2fe91e06a2c69dab4fb449f2bf62b1e09f1
blob + c09e9252dd552a6939292b6dd5653ee79ef2090b
--- lib/repository_admin.c
+++ lib/repository_admin.c
@@ -357,7 +357,11 @@ got_repo_index_pack(FILE *packfile, struct got_object_
 		err = got_error_from_errno("close");
 		goto done;
 	}
-	imsg_init(&idxibuf, imsg_idxfds[0]);
+	if (imsgbuf_init(&idxibuf, imsg_idxfds[0]) == -1) {
+		err = got_error_from_errno("imsgbuf_init");
+		goto done;
+	}
+	imsgbuf_allow_fdpass(&idxibuf);
 
 	npackfd = dup(fileno(packfile));
 	if (npackfd == -1) {
blob - 8e3078201dcc7c840868221ffba208e7f60aac25
blob + f8e87fabb17857a5abf28d64d34330a17b6aefc7
--- lib/send.c
+++ lib/send.c
@@ -476,7 +476,11 @@ got_send_pack(const char *remote_name, struct got_path
 		goto done;
 	}
 	imsg_sendfds[1] = -1;
-	imsg_init(&sendibuf, imsg_sendfds[0]);
+	if (imsgbuf_init(&sendibuf, imsg_sendfds[0]) == -1) {
+		err = got_error_from_errno("imsgbuf_init");
+		goto done;
+	}
+	imsgbuf_allow_fdpass(&sendibuf);
 	nsendfd = dup(sendfd);
 	if (nsendfd == -1) {
 		err = got_error_from_errno("dup");
blob - a3aeb1c2501fd8cce22b87ef6768bad08a6fe599
blob + d770d8d6875a0453058bfebd8736a669d71396be
--- lib/serve.c
+++ lib/serve.c
@@ -788,7 +788,9 @@ serve_read(int infd, int outfd, int gotd_sock, const c
 	int packfd = -1;
 	size_t pack_chunksize;
 
-	imsg_init(&ibuf, gotd_sock);
+	if (imsgbuf_init(&ibuf, gotd_sock) == -1)
+		return got_error_from_errno("imsgbuf_init");
+	imsgbuf_allow_fdpass(&ibuf);
 
 	err = announce_refs(outfd, &ibuf, 1, repo_path, chattygot);
 	if (err)
@@ -929,7 +931,7 @@ serve_read(int infd, int outfd, int gotd_sock, const c
 		}
 	}
 done:
-	imsg_clear(&ibuf);
+	imsgbuf_clear(&ibuf);
 	if (packfd != -1 && close(packfd) == -1 && err == NULL)
 		err = got_error_from_errno("close");
 	if (err)
@@ -1258,7 +1260,9 @@ serve_write(int infd, int outfd, int gotd_sock, const 
 	struct imsg imsg;
 	int report_status = 0;
 
-	imsg_init(&ibuf, gotd_sock);
+	if (imsgbuf_init(&ibuf, gotd_sock) == -1)
+		return got_error_from_errno("imsgbuf_init");
+
 	memset(&imsg, 0, sizeof(imsg));
 
 	err = announce_refs(outfd, &ibuf, 0, repo_path, chattygot);
@@ -1376,7 +1380,7 @@ serve_write(int infd, int outfd, int gotd_sock, const 
 		imsg_free(&imsg);
 	}
 done:
-	imsg_clear(&ibuf);
+	imsgbuf_clear(&ibuf);
 	if (err)
 		echo_error(err, outfd, chattygot);
 	return err;
blob - 7a03f43fd34cc5b92ead9494b1c592a98b9b4c0b
blob + 4609f911246baf51f38f91041444e8cb23311d9a
--- libexec/got-fetch-pack/got-fetch-pack.c
+++ libexec/got-fetch-pack/got-fetch-pack.c
@@ -877,7 +877,12 @@ main(int argc, char **argv)
 	TAILQ_INIT(&wanted_branches);
 	TAILQ_INIT(&wanted_refs);
 
-	imsg_init(&ibuf, GOT_IMSG_FD_CHILD);
+	if (imsgbuf_init(&ibuf, GOT_IMSG_FD_CHILD) == -1) {
+		warn("imsgbuf_init");
+		return 1;
+	}
+	imsgbuf_allow_fdpass(&ibuf);
+
 #ifndef PROFILE
 	/* revoke access to most system calls */
 	if (pledge("stdio recvfd", NULL) == -1) {
blob - cc2cd7ee7882f8bf0a997b3e00e36419a15d76fc
blob + e7b041c41a36e5ab990a86d621f4e250a2599450
--- libexec/got-index-pack/got-index-pack.c
+++ libexec/got-index-pack/got-index-pack.c
@@ -20,6 +20,7 @@
 #include <sys/mman.h>
 #include <sys/uio.h>
 
+#include <err.h>
 #include <sha1.h>
 #include <sha2.h>
 #include <stdint.h>
@@ -106,7 +107,11 @@ main(int argc, char **argv)
 	if (err)
 		goto done;
 
-	imsg_init(&ibuf, GOT_IMSG_FD_CHILD);
+	if (imsgbuf_init(&ibuf, GOT_IMSG_FD_CHILD) == -1) {
+		warn("imsgbuf_init");
+		return 1;
+	}
+	imsgbuf_allow_fdpass(&ibuf);
 #ifndef PROFILE
 	/* revoke access to most system calls */
 	if (pledge("stdio recvfd", NULL) == -1) {
blob - bace23fd1f0b2eb7a3e6033e9af5afea04a7d486
blob + 2b3a4dc46f7cae5b09ff63b72b2a847ab4a4c172
--- libexec/got-read-blob/got-read-blob.c
+++ libexec/got-read-blob/got-read-blob.c
@@ -19,6 +19,7 @@
 #include <sys/uio.h>
 #include <sys/time.h>
 
+#include <err.h>
 #include <stdint.h>
 #include <imsg.h>
 #include <limits.h>
@@ -58,7 +59,11 @@ main(int argc, char *argv[])
 
 	signal(SIGINT, catch_sigint);
 
-	imsg_init(&ibuf, GOT_IMSG_FD_CHILD);
+	if (imsgbuf_init(&ibuf, GOT_IMSG_FD_CHILD) == -1) {
+		warn("imsgbuf_init");
+		return 1;
+	}
+	imsgbuf_allow_fdpass(&ibuf);
 
 #ifndef PROFILE
 	/* revoke access to most system calls */
@@ -202,7 +207,7 @@ done:
 			break;
 	}
 
-	imsg_clear(&ibuf);
+	imsgbuf_clear(&ibuf);
 	if (err) {
 		if (!sigint_received && err->code != GOT_ERR_PRIVSEP_PIPE) {
 			fprintf(stderr, "%s: %s\n", getprogname(), err->msg);
blob - d3d7dc3684a3c02b02da73a5df88a62ba3f3a9c4
blob + 3d142fc066baece919ade964e448f393a7e6441f
--- libexec/got-read-commit/got-read-commit.c
+++ libexec/got-read-commit/got-read-commit.c
@@ -19,6 +19,7 @@
 #include <sys/uio.h>
 #include <sys/time.h>
 
+#include <err.h>
 #include <stdint.h>
 #include <imsg.h>
 #include <limits.h>
@@ -59,7 +60,11 @@ main(int argc, char *argv[])
 
 	signal(SIGINT, catch_sigint);
 
-	imsg_init(&ibuf, GOT_IMSG_FD_CHILD);
+	if (imsgbuf_init(&ibuf, GOT_IMSG_FD_CHILD) == -1) {
+		warn("imsgbuf_init");
+		return 1;
+	}
+	imsgbuf_allow_fdpass(&ibuf);
 
 #ifndef PROFILE
 	/* revoke access to most system calls */
@@ -123,7 +128,7 @@ done:
 			break;
 	}
 
-	imsg_clear(&ibuf);
+	imsgbuf_clear(&ibuf);
 	if (err) {
 		if (!sigint_received && err->code != GOT_ERR_PRIVSEP_PIPE) {
 			fprintf(stderr, "%s: %s\n", getprogname(), err->msg);
blob - c5c7e195b363bff1a04075a31e61fe76a6330b8d
blob + 8b423f6fe3ea45c4a4c618387c5b36a91bce632c
--- libexec/got-read-gitconfig/got-read-gitconfig.c
+++ libexec/got-read-gitconfig/got-read-gitconfig.c
@@ -19,6 +19,7 @@
 #include <sys/uio.h>
 #include <sys/time.h>
 
+#include <err.h>
 #include <stdint.h>
 #include <imsg.h>
 #include <limits.h>
@@ -140,7 +141,6 @@ send_gitconfig_remotes(struct imsgbuf *ibuf, struct go
 		return got_error_from_errno("imsg_compose GITCONFIG_REMOTES");
 
 	err = got_privsep_flush_imsg(ibuf);
-	imsg_clear(ibuf);
 	if (err)
 		return err;
 
@@ -347,7 +347,11 @@ main(int argc, char *argv[])
 #endif
 	signal(SIGINT, catch_sigint);
 
-	imsg_init(&ibuf, GOT_IMSG_FD_CHILD);
+	if (imsgbuf_init(&ibuf, GOT_IMSG_FD_CHILD) == -1) {
+		warn("imsgbuf_init");
+		return 1;
+	}
+	imsgbuf_allow_fdpass(&ibuf);
 
 #ifndef PROFILE
 	/* revoke access to most system calls */
@@ -432,7 +436,7 @@ main(int argc, char *argv[])
 			break;
 	}
 
-	imsg_clear(&ibuf);
+	imsgbuf_clear(&ibuf);
 	if (err) {
 		if (!sigint_received && err->code != GOT_ERR_PRIVSEP_PIPE) {
 			fprintf(stderr, "%s: %s\n", getprogname(), err->msg);
blob - 44ae987193ae5d6ef34fa98f645dd0ab17cb8338
blob + 5fc036bc57573c9e17ab302b84edcfad28d83b01
--- libexec/got-read-gotconfig/got-read-gotconfig.c
+++ libexec/got-read-gotconfig/got-read-gotconfig.c
@@ -19,6 +19,7 @@
 #include <sys/uio.h>
 #include <sys/time.h>
 
+#include <err.h>
 #include <stdint.h>
 #include <imsg.h>
 #include <limits.h>
@@ -245,7 +246,6 @@ send_gotconfig_remotes(struct imsgbuf *ibuf,
 		return got_error_from_errno("imsg_compose GOTCONFIG_REMOTES");
 
 	err = got_privsep_flush_imsg(ibuf);
-	imsg_clear(ibuf);
 	if (err)
 		return err;
 
@@ -491,7 +491,11 @@ main(int argc, char *argv[])
 #endif
 	signal(SIGINT, catch_sigint);
 
-	imsg_init(&ibuf, GOT_IMSG_FD_CHILD);
+	if (imsgbuf_init(&ibuf, GOT_IMSG_FD_CHILD) == -1) {
+		warn("imsgbuf_init");
+		return 1;
+	}
+	imsgbuf_allow_fdpass(&ibuf);
 
 #ifndef PROFILE
 	/* revoke access to most system calls */
@@ -603,7 +607,7 @@ main(int argc, char *argv[])
 			break;
 	}
 
-	imsg_clear(&ibuf);
+	imsgbuf_clear(&ibuf);
 	if (err) {
 		if (!sigint_received && err->code != GOT_ERR_PRIVSEP_PIPE) {
 			fprintf(stderr, "%s: %s\n", getprogname(), err->msg);
blob - 18d9dcc1df01508d3904bf3d5d072fc2b0d52438
blob + 4a4d2db38f1cd8a82995d5703ac44ec29f4edacc
--- libexec/got-read-object/got-read-object.c
+++ libexec/got-read-object/got-read-object.c
@@ -19,6 +19,7 @@
 #include <sys/uio.h>
 #include <sys/time.h>
 
+#include <err.h>
 #include <stdint.h>
 #include <imsg.h>
 #include <limits.h>
@@ -98,7 +99,11 @@ main(int argc, char *argv[])
 
 	signal(SIGINT, catch_sigint);
 
-	imsg_init(&ibuf, GOT_IMSG_FD_CHILD);
+	if (imsgbuf_init(&ibuf, GOT_IMSG_FD_CHILD) == -1) {
+		warn("imsgbuf_init");
+		return 1;
+	}
+	imsgbuf_allow_fdpass(&ibuf);
 
 #ifndef PROFILE
 	/* revoke access to most system calls */
@@ -203,7 +208,7 @@ done:
 			break;
 	}
 
-	imsg_clear(&ibuf);
+	imsgbuf_clear(&ibuf);
 	if (err) {
 		if(!sigint_received && err->code != GOT_ERR_PRIVSEP_PIPE) {
 			fprintf(stderr, "%s: %s\n", getprogname(), err->msg);
blob - 81b54dbe2110510c5611bd7e3319a1dabea52172
blob + a4f3b4fb3382a935b01f5eab1734eec5dc9d1259
--- libexec/got-read-pack/got-read-pack.c
+++ libexec/got-read-pack/got-read-pack.c
@@ -21,6 +21,7 @@
 #include <sys/time.h>
 #include <sys/mman.h>
 
+#include <err.h>
 #include <inttypes.h>
 #include <limits.h>
 #include <signal.h>
@@ -1991,7 +1992,11 @@ main(int argc, char *argv[])
 
 	signal(SIGINT, catch_sigint);
 
-	imsg_init(&ibuf, GOT_IMSG_FD_CHILD);
+	if (imsgbuf_init(&ibuf, GOT_IMSG_FD_CHILD) == -1) {
+		warn("imsgbuf_init");
+		return 1;
+	}
+	imsgbuf_allow_fdpass(&ibuf);
 
 	err = got_object_cache_init(&objcache, GOT_OBJECT_CACHE_TYPE_OBJ);
 	if (err) {
@@ -2141,7 +2146,7 @@ main(int argc, char *argv[])
 	if (pack)
 		got_pack_close(pack);
 	got_object_cache_close(&objcache);
-	imsg_clear(&ibuf);
+	imsgbuf_clear(&ibuf);
 	if (basefile && fclose(basefile) == EOF && err == NULL)
 		err = got_error_from_errno("fclose");
 	if (accumfile && fclose(accumfile) == EOF && err == NULL)
blob - 20e04c262b6e74cc897f1c49f7478f7d0c6b1022
blob + fc59997853341c35bfc256bfabc86d7d3a5ff89f
--- libexec/got-read-patch/got-read-patch.c
+++ libexec/got-read-patch/got-read-patch.c
@@ -39,6 +39,7 @@
 #include <sys/queue.h>
 #include <sys/uio.h>
 
+#include <err.h>
 #include <ctype.h>
 #include <limits.h>
 #include <paths.h>
@@ -646,7 +647,11 @@ main(int argc, char **argv)
 		sleep(1);
 #endif
 
-	imsg_init(&ibuf, GOT_IMSG_FD_CHILD);
+	if (imsgbuf_init(&ibuf, GOT_IMSG_FD_CHILD) == -1) {
+		warn("imsgbuf_init");
+		return 1;
+	}
+	imsgbuf_allow_fdpass(&ibuf);
 #ifndef PROFILE
 	/* revoke access to most system calls */
 	if (pledge("stdio recvfd", NULL) == -1) {
blob - 3a54429e45deb222fd8c58bd8d8d2683a06edd3c
blob + e9423888bbee99416b1a6b394a4268bbee9d2d4f
--- libexec/got-read-tag/got-read-tag.c
+++ libexec/got-read-tag/got-read-tag.c
@@ -19,6 +19,7 @@
 #include <sys/uio.h>
 #include <sys/time.h>
 
+#include <err.h>
 #include <stdint.h>
 #include <imsg.h>
 #include <limits.h>
@@ -58,7 +59,11 @@ main(int argc, char *argv[])
 
 	signal(SIGINT, catch_sigint);
 
-	imsg_init(&ibuf, GOT_IMSG_FD_CHILD);
+	if (imsgbuf_init(&ibuf, GOT_IMSG_FD_CHILD) == -1) {
+		warn("imsgbuf_init");
+		return 1;
+	}
+	imsgbuf_allow_fdpass(&ibuf);
 
 #ifndef PROFILE
 	/* revoke access to most system calls */
@@ -122,7 +127,7 @@ done:
 			break;
 	}
 
-	imsg_clear(&ibuf);
+	imsgbuf_clear(&ibuf);
 	if (err) {
 		if (!sigint_received && err->code != GOT_ERR_PRIVSEP_PIPE) {
 			fprintf(stderr, "%s: %s\n", getprogname(), err->msg);
blob - a9cd911be347a80abbe31861aa07381dcd97ccc0
blob + 40092a0767a00fe66a6806fbb3a29f08e99ce38b
--- libexec/got-read-tree/got-read-tree.c
+++ libexec/got-read-tree/got-read-tree.c
@@ -19,6 +19,7 @@
 #include <sys/uio.h>
 #include <sys/time.h>
 
+#include <err.h>
 #include <stdint.h>
 #include <imsg.h>
 #include <limits.h>
@@ -61,7 +62,11 @@ main(int argc, char *argv[])
 
 	signal(SIGINT, catch_sigint);
 
-	imsg_init(&ibuf, GOT_IMSG_FD_CHILD);
+	if (imsgbuf_init(&ibuf, GOT_IMSG_FD_CHILD) == -1) {
+		warn("imsgbuf_init");
+		return 1;
+	}
+	imsgbuf_allow_fdpass(&ibuf);
 
 #ifndef PROFILE
 	/* revoke access to most system calls */
@@ -128,7 +133,7 @@ done:
 	}
 
 	free(entries);
-	imsg_clear(&ibuf);
+	imsgbuf_clear(&ibuf);
 	if (err) {
 		if (!sigint_received && err->code != GOT_ERR_PRIVSEP_PIPE) {
 			fprintf(stderr, "%s: %s\n", getprogname(), err->msg);
blob - 766bbf50e670a6b1b65024ee61042ffb95cc792b
blob + a7c50b87c17ac2535d33e80601a9f5b054441d8f
--- libexec/got-send-pack/got-send-pack.c
+++ libexec/got-send-pack/got-send-pack.c
@@ -21,6 +21,7 @@
 #include <sys/time.h>
 #include <sys/stat.h>
 
+#include <err.h>
 #include <stdint.h>
 #include <errno.h>
 #include <imsg.h>
@@ -628,7 +629,11 @@ main(int argc, char **argv)
 	TAILQ_INIT(&refs);
 	TAILQ_INIT(&delete_refs);
 
-	imsg_init(&ibuf, GOT_IMSG_FD_CHILD);
+	if (imsgbuf_init(&ibuf, GOT_IMSG_FD_CHILD) == -1) {
+		warn("imsgbuf_init");
+		return 1;
+	}
+	imsgbuf_allow_fdpass(&ibuf);
 #ifndef PROFILE
 	/* revoke access to most system calls */
 	if (pledge("stdio recvfd", NULL) == -1) {