Commit Diff


commit - 4913b479eed668b06a90b1c150b71065a6e2ce36
commit + bc10f6a5fcbae63627490a4c11e13a34b1c2525a
blob - cf5c375e1ddc25eb54258080abae9d56e86fede8
blob + d535286a058e3ccba133933c570bdaaea01fe5aa
--- ChangeLog
+++ ChangeLog
@@ -1,5 +1,7 @@
 2021-07-12  Omar Polo  <op@omarpolo.com>
 
+	* util.c (dispatch_imsg): refactor: all imsgs are completely asynchronous
+
 	* minibuffer.c (minibuffer_taint_hist): bugfix: allow editing minibuffer history
 
 2021-07-10  Omar Polo  <op@omarpolo.com>
blob - 963419e60ea0dd6f1e0c50729256dc3ed9d47f2e
blob + 881651984cd91e5d9f826a8a9a68219e43f3a436
--- fs.c
+++ fs.c
@@ -45,10 +45,9 @@ static void		 handle_session_start(struct imsg*, size_
 static void		 handle_session_tab(struct imsg*, size_t);
 static void		 handle_session_end(struct imsg*, size_t);
 static void		 handle_dispatch_imsg(int, short, void*);
+static int		 fs_send_ui(int, uint32_t, int, const void *, uint16_t);
 
-static struct event		 imsgev;
-static struct imsgbuf		*ibuf;
-
+static struct imsgev		*iev_ui;
 static FILE			*session;
 
 static char	bookmark_file[PATH_MAX];
@@ -85,22 +84,19 @@ serve_bookmarks(uint32_t peerid)
 		t = "# Bookmarks\n\n"
 		    "No bookmarks yet!\n"
 		    "Create ~/.telescope/bookmarks.gmi or use `bookmark-page'.\n";
-		imsg_compose(ibuf, IMSG_BUF, peerid, 0, -1, t, strlen(t));
-		imsg_compose(ibuf, IMSG_EOF, peerid, 0, -1, NULL, 0);
-		imsg_flush(ibuf);
+		fs_send_ui(IMSG_BUF, peerid, -1, t, strlen(t));
+		fs_send_ui(IMSG_EOF, peerid, -1, NULL, 0);
 		return;
 	}
 
 	for (;;) {
 		r = fread(buf, 1, sizeof(buf), f);
-		imsg_compose(ibuf, IMSG_BUF, peerid, 0, -1, buf, r);
-		imsg_flush(ibuf);
+		fs_send_ui(IMSG_BUF, peerid, -1, buf, r);
 		if (r != sizeof(buf))
 			break;
 	}
 
-	imsg_compose(ibuf, IMSG_EOF, peerid, 0, -1, NULL, 0);
-	imsg_flush(ibuf);
+	fs_send_ui(IMSG_EOF, peerid, -1, NULL, 0);
 
 	fclose(f);
 }
@@ -108,10 +104,8 @@ serve_bookmarks(uint32_t peerid)
 static void
 send_page(struct imsg *imsg, const char *page)
 {
-	imsg_compose(ibuf, IMSG_BUF, imsg->hdr.peerid, 0, -1,
-	    page, strlen(page));
-	imsg_compose(ibuf, IMSG_EOF, imsg->hdr.peerid, 0, -1, NULL, 0);
-	imsg_flush(ibuf);
+	fs_send_ui(IMSG_BUF, imsg->hdr.peerid, -1, page, strlen(page));
+	fs_send_ui(IMSG_EOF, imsg->hdr.peerid, -1, NULL, 0);
 }
 
 static void
@@ -137,9 +131,8 @@ handle_get(struct imsg *imsg, size_t datalen)
 		send_page(imsg, about_new);
 	} else {
 		p = "# not found!\n";
-		imsg_compose(ibuf, IMSG_BUF, imsg->hdr.peerid, 0, -1, p, strlen(p));
-		imsg_compose(ibuf, IMSG_EOF, imsg->hdr.peerid, 0, -1, NULL, 0);
-		imsg_flush(ibuf);
+		fs_send_ui(IMSG_BUF, imsg->hdr.peerid, -1, p, strlen(p));
+		fs_send_ui(IMSG_EOF, imsg->hdr.peerid, -1, NULL, 0);
 	}
 }
 
@@ -169,8 +162,7 @@ handle_bookmark_page(struct imsg *imsg, size_t datalen
 
 	res = 0;
 end:
-	imsg_compose(ibuf, IMSG_BOOKMARK_OK, 0, 0, -1, &res, sizeof(res));
-	imsg_flush(ibuf);
+	fs_send_ui(IMSG_BOOKMARK_OK, 0, -1, &res, sizeof(res));
 }
 
 static void
@@ -195,9 +187,8 @@ handle_save_cert(struct imsg *imsg, size_t datalen)
 
 	res = 0;
 end:
-	imsg_compose(ibuf, IMSG_SAVE_CERT_OK, imsg->hdr.peerid, 0, -1,
+	fs_send_ui(IMSG_SAVE_CERT_OK, imsg->hdr.peerid, -1,
 	    &res, sizeof(res));
-	imsg_flush(ibuf);
 }
 
 static void
@@ -257,9 +248,8 @@ handle_update_cert(struct imsg *imsg, size_t datalen)
 	res = rename(sfn, known_hosts_file) != -1;
 
 end:
-	imsg_compose(ibuf, IMSG_UPDATE_CERT_OK, imsg->hdr.peerid, 0, -1,
+	fs_send_ui(IMSG_UPDATE_CERT_OK, imsg->hdr.peerid, -1,
 	    &res, sizeof(res));
-	imsg_flush(ibuf);
 }
 
 static void
@@ -274,13 +264,11 @@ handle_file_open(struct imsg *imsg, size_t datalen)
 
 	if ((fd = open(path, O_WRONLY | O_TRUNC | O_CREAT, 0644)) == -1) {
 		e = strerror(errno);
-		imsg_compose(ibuf, IMSG_FILE_OPENED, imsg->hdr.peerid, 0, -1,
+		fs_send_ui(IMSG_FILE_OPENED, imsg->hdr.peerid, -1,
 		    e, strlen(e)+1);
 	} else
-		imsg_compose(ibuf, IMSG_FILE_OPENED, imsg->hdr.peerid, 0, fd,
+		fs_send_ui(IMSG_FILE_OPENED, imsg->hdr.peerid, fd,
 		    NULL, 0);
-
-	imsg_flush(ibuf);
 }
 
 static void
@@ -320,10 +308,18 @@ handle_session_end(struct imsg *imsg, size_t datalen)
 static void
 handle_dispatch_imsg(int fd, short ev, void *d)
 {
-	struct imsgbuf	*ibuf = d;
-	dispatch_imsg(ibuf, handlers, sizeof(handlers));
+	struct imsgev	*iev = d;
+	dispatch_imsg(iev, ev, handlers, sizeof(handlers));
 }
 
+static int
+fs_send_ui(int type, uint32_t peerid, int fd, const void *data,
+    uint16_t datalen)
+{
+	return imsg_compose_event(iev_ui, type, peerid, 0, fd,
+	    data, datalen);
+}
+
 int
 fs_init(void)
 {
@@ -358,11 +354,15 @@ fs_main(void)
 
 	event_init();
 
-	if ((ibuf = calloc(1, sizeof(*ibuf))) == NULL)
+	/* Setup pipe and event handler to the main process */
+	if ((iev_ui = malloc(sizeof(*iev_ui))) == NULL)
 		die();
-	imsg_init(ibuf, 3);
-	event_set(&imsgev, ibuf->fd, EV_READ | EV_PERSIST, handle_dispatch_imsg, ibuf);
-	event_add(&imsgev, NULL);
+	imsg_init(&iev_ui->ibuf, 3);
+	iev_ui->handler = handle_dispatch_imsg;
+	iev_ui->events = EV_READ;
+	event_set(&iev_ui->ev, iev_ui->ibuf.fd, iev_ui->events,
+	    iev_ui->handler, iev_ui);
+	event_add(&iev_ui->ev, NULL);
 
 	sandbox_fs_process();
 
blob - 65cfc482332d0226e5db475c9eeca9d9d607f5dd
blob + 80b58738cec69d9ab8eb9bca16c88a19139b2c70
--- gemini.c
+++ gemini.c
@@ -36,9 +36,8 @@
 # include <asr.h>
 #endif
 
-static struct event		 imsgev;
+static struct imsgev		*iev_ui;
 static struct tls_config	*tlsconf;
-static struct imsgbuf		*ibuf;
 
 struct req;
 
@@ -72,6 +71,8 @@ static void		 handle_stop(struct imsg*, size_t);
 static void		 handle_quit(struct imsg*, size_t);
 static void		 handle_dispatch_imsg(int, short, void*);
 
+static int		 net_send_ui(int, uint32_t, const void *, uint16_t);
+
 /* TODO: making this customizable */
 struct timeval timeout_for_handshake = { 5, 0 };
 
@@ -292,8 +293,7 @@ close_conn(int fd, short ev, void *d)
 static void
 close_with_err(struct req *req, const char *err)
 {
-	imsg_compose(ibuf, IMSG_ERR, req->id, 0, -1, err, strlen(err)+1);
-	imsg_flush(ibuf);
+	net_send_ui(IMSG_ERR, req->id, err, strlen(err)+1);
 	close_conn(0, 0, req);
 }
 
@@ -355,8 +355,7 @@ do_handshake(int fd, short ev, void *d)
 		close_with_errf(req, "handshake failed: %s", tls_error(req->ctx));
 		return;
 	}
-	imsg_compose(ibuf, IMSG_CHECK_CERT, req->id, 0, -1, hash, strlen(hash)+1);
-	imsg_flush(ibuf);
+	net_send_ui(IMSG_CHECK_CERT, req->id, hash, strlen(hash)+1);
 }
 
 static void
@@ -445,9 +444,8 @@ parse_reply(struct req *req)
 	*e = '\0';
 	e++;
 	len = e - req->buf;
-	imsg_compose(ibuf, IMSG_GOT_CODE, req->id, 0, -1, &code, sizeof(code));
-	imsg_compose(ibuf, IMSG_GOT_META, req->id, 0, -1, req->buf, len);
-	imsg_flush(ibuf);
+	net_send_ui(IMSG_GOT_CODE, req->id, &code, sizeof(code));
+	net_send_ui(IMSG_GOT_META, req->id, req->buf, len);
 
 	if (20 <= code && code < 30)
 		advance_buf(req, len+1); /* skip \n too */
@@ -468,9 +466,8 @@ copy_body(int fd, short ev, void *d)
 
 	for (;;) {
 		if (req->off != 0) {
-			imsg_compose(ibuf, IMSG_BUF, req->id, 0, -1,
+			net_send_ui(IMSG_BUF, req->id,
 			    req->buf, req->off);
-			imsg_flush(ibuf);
 			req->off = 0;
 		}
 
@@ -482,8 +479,7 @@ copy_body(int fd, short ev, void *d)
 			yield_w(req, copy_body, NULL);
 			return;
 		case 0:
-			imsg_compose(ibuf, IMSG_EOF, req->id, 0, -1, NULL, 0);
-			imsg_flush(ibuf);
+			net_send_ui(IMSG_EOF, req->id, NULL, 0);
 			close_conn(0, 0, req);
 			return;
 		default:
@@ -564,10 +560,18 @@ handle_quit(struct imsg *imsg, size_t datalen)
 static void
 handle_dispatch_imsg(int fd, short ev, void *d)
 {
-	struct imsgbuf	*ibuf = d;
-	dispatch_imsg(ibuf, handlers, sizeof(handlers));
+	struct imsgev	*iev = d;
+	dispatch_imsg(iev, ev, handlers, sizeof(handlers));
 }
 
+static int
+net_send_ui(int type, uint32_t peerid, const void *data,
+    uint16_t datalen)
+{
+	return imsg_compose_event(iev_ui, type, peerid, 0, -1,
+	    data, datalen);
+}
+
 int
 client_main(void)
 {
@@ -583,11 +587,14 @@ client_main(void)
 	event_init();
 
 	/* Setup pipe and event handler to the main process */
-	if ((ibuf = calloc(1, sizeof(*ibuf))) == NULL)
+	if ((iev_ui = malloc(sizeof(*iev_ui))) == NULL)
 		die();
-	imsg_init(ibuf, 3);
-	event_set(&imsgev, ibuf->fd, EV_READ | EV_PERSIST, handle_dispatch_imsg, ibuf);
-	event_add(&imsgev, NULL);
+	imsg_init(&iev_ui->ibuf, 3);
+	iev_ui->handler = handle_dispatch_imsg;
+	iev_ui->events = EV_READ;
+	event_set(&iev_ui->ev, iev_ui->ibuf.fd, iev_ui->events,
+	    iev_ui->handler, iev_ui);
+	event_add(&iev_ui->ev, NULL);
 
 	sandbox_net_process();
 
blob - 3a5e9032c73ddc1d011e95b68f1361f62e2d7bbc
blob + 537ed46ae73149851e1f2ca7ec85fc37b1cc6dde
--- telescope.c
+++ telescope.c
@@ -12,7 +12,8 @@
 #include "telescope.h"
 #include "ui.h"
 
-struct event		 netev, fsev;
+static struct imsgev	*iev_fs, *iev_net;
+
 struct tabshead		 tabshead = TAILQ_HEAD_INITIALIZER(tabshead);
 struct proxylist	 proxies = TAILQ_HEAD_INITIALIZER(proxies);
 
@@ -29,8 +30,6 @@ static struct proto protos[] = {
 	{ NULL, NULL },
 };
 
-static struct imsgbuf	*netibuf, *fsibuf;
-
 static void		 die(void) __attribute__((__noreturn__));
 static struct tab	*tab_by_id(uint32_t);
 static void		 handle_imsg_err(struct imsg*, size_t);
@@ -51,6 +50,8 @@ static void		 handle_dispatch_imsg(int, short, void*);
 static void		 load_page_from_str(struct tab*, const char*);
 static void		 do_load_url(struct tab*, const char*);
 static pid_t		 start_child(enum telescope_process, const char *, int);
+static int		 ui_send_net(int, uint32_t, const void *, uint16_t);
+static int		 ui_send_fs(int, uint32_t, const void *, uint16_t);
 
 static imsg_handlerfn *handlers[] = {
 	[IMSG_ERR] = handle_imsg_err,
@@ -143,9 +144,7 @@ handle_imsg_check_cert(struct imsg *imsg, size_t datal
 		}
 		strlcpy(e->hash, hash, sizeof(e->hash));
 		tofu_add(&certs, e);
-		imsg_compose(fsibuf, IMSG_SAVE_CERT, tab->id, 0, -1,
-		    e, sizeof(*e));
-		imsg_flush(fsibuf);
+		ui_send_fs(IMSG_SAVE_CERT, tab->id, e, sizeof(*e));
 	} else
 		tofu_res = !strcmp(hash, e->hash);
 
@@ -157,9 +156,8 @@ handle_imsg_check_cert(struct imsg *imsg, size_t datal
 		else
 			tab->trust = TS_TRUSTED;
 
-		imsg_compose(netibuf, IMSG_CERT_STATUS, imsg->hdr.peerid, 0, -1,
+		ui_send_net(IMSG_CERT_STATUS, imsg->hdr.peerid,
 		    &tofu_res, sizeof(tofu_res));
-		imsg_flush(netibuf);
 	} else {
 		tab->trust = TS_UNTRUSTED;
 		load_page_from_str(tab, "# Certificate mismatch\n");
@@ -173,9 +171,8 @@ handle_imsg_check_cert(struct imsg *imsg, size_t datal
 static void
 handle_check_cert_user_choice(int accept, struct tab *tab)
 {
-	imsg_compose(netibuf, IMSG_CERT_STATUS, tab->id, 0, -1,
-	    &accept, sizeof(accept));
-	imsg_flush(netibuf);
+	ui_send_net(IMSG_CERT_STATUS, tab->id, &accept,
+	    sizeof(accept));
 
 	if (accept) {
 		/*
@@ -222,8 +219,7 @@ handle_maybe_save_new_cert(int accept, struct tab *tab
 		strlcat(e->domain, port, sizeof(e->domain));
 	}
 	strlcpy(e->hash, tab->cert, sizeof(e->hash));
-	imsg_compose(fsibuf, IMSG_UPDATE_CERT, 0, 0, -1, e, sizeof(*e));
-	imsg_flush(fsibuf);
+	ui_send_fs(IMSG_UPDATE_CERT, 0, e, sizeof(*e));
 
 	tofu_update(&certs, e);
 
@@ -298,8 +294,7 @@ handle_imsg_got_meta(struct imsg *imsg, size_t datalen
 		ui_require_input(tab, tab->code == 11);
 	} else if (tab->code == 20) {
 		if (setup_parser_for(tab)) {
-			imsg_compose(netibuf, IMSG_PROCEED, tab->id, 0, -1, NULL, 0);
-			imsg_flush(netibuf);
+			ui_send_net(IMSG_PROCEED, tab->id, NULL, 0);
 		} else {
 			load_page_from_str(tab, err_pages[UNKNOWN_TYPE_OR_CSET]);
 			ui_yornp("Can't display page, wanna save?",
@@ -341,8 +336,7 @@ handle_save_page_path(const char *path, unsigned int t
 	tab = tab_by_id(tabid);
 	tab->path = strdup(path);
 
-	imsg_compose(fsibuf, IMSG_FILE_OPEN, tabid, 0, -1, path, strlen(path)+1);
-	imsg_flush(fsibuf);
+	ui_send_fs(IMSG_FILE_OPEN, tabid, path, strlen(path)+1);
 }
 
 static void
@@ -369,8 +363,7 @@ handle_imsg_file_opened(struct imsg *imsg, size_t data
 		free(page);
 	} else {
 		tab->fd = imsg->fd;
-		imsg_compose(netibuf, IMSG_PROCEED, tab->id, 0, -1, NULL, 0);
-		imsg_flush(netibuf);
+		ui_send_net(IMSG_PROCEED, tab->id, NULL, 0);
 	}
 }
 
@@ -479,8 +472,8 @@ handle_imsg_update_cert_ok(struct imsg *imsg, size_t d
 static void
 handle_dispatch_imsg(int fd, short ev, void *d)
 {
-	struct imsgbuf	*ibuf = d;
-	dispatch_imsg(ibuf, handlers, sizeof(handlers));
+	struct imsgev	*iev = d;
+	dispatch_imsg(iev, ev, handlers, sizeof(handlers));
 }
 
 static void
@@ -503,9 +496,8 @@ load_about_url(struct tab *tab, const char *url)
 
 	gemtext_initparser(&tab->buffer.page);
 
-	imsg_compose(fsibuf, IMSG_GET, tab->id, 0, -1,
+	ui_send_fs(IMSG_GET, tab->id,
 	    tab->hist_cur->h, strlen(tab->hist_cur->h)+1);
-	imsg_flush(fsibuf);
 }
 
 void
@@ -525,9 +517,8 @@ load_gemini_url(struct tab *tab, const char *url)
 
 	req.proto = PROTO_GEMINI;
 
-	imsg_compose(netibuf, IMSG_GET_RAW, tab->id, 0, -1,
+	ui_send_net(IMSG_GET_RAW, tab->id,
 	    &req, sizeof(req));
-	imsg_flush(netibuf);
 }
 
 void
@@ -548,9 +539,8 @@ load_via_proxy(struct tab *tab, const char *url, struc
 
 	req.proto = p->proto;
 
-	imsg_compose(netibuf, IMSG_GET_RAW, tab->id, 0, -1,
+	ui_send_net(IMSG_GET_RAW, tab->id,
 	    &req, sizeof(req));
-	imsg_flush(netibuf);
 }
 
 static void
@@ -646,8 +636,7 @@ load_next_page(struct tab *tab)
 void
 stop_tab(struct tab *tab)
 {
-	imsg_compose(netibuf, IMSG_STOP, tab->id, 0, -1, NULL, 0);
-	imsg_flush(netibuf);
+	ui_send_net(IMSG_STOP, tab->id, NULL, 0);
 
 	if (tab->fd != -1) {
 		close(tab->fd);
@@ -661,8 +650,8 @@ stop_tab(struct tab *tab)
 void
 add_to_bookmarks(const char *str)
 {
-	imsg_compose(fsibuf, IMSG_BOOKMARK_PAGE, 0, 0, -1, str, strlen(str)+1);
-	imsg_flush(fsibuf);
+	ui_send_fs(IMSG_BOOKMARK_PAGE, 0,
+	    str, strlen(str)+1);
 }
 
 void
@@ -670,17 +659,14 @@ save_session(void)
 {
 	struct tab *tab;
 
-	imsg_compose(fsibuf, IMSG_SESSION_START, 0, 0, -1, NULL, 0);
-	imsg_flush(fsibuf);
+	ui_send_fs(IMSG_SESSION_START, 0, NULL, 0);
 
 	TAILQ_FOREACH(tab, &tabshead, tabs) {
-		imsg_compose(fsibuf, IMSG_SESSION_TAB, 0, 0, -1,
+		ui_send_fs(IMSG_SESSION_TAB, 0,
 		    tab->hist_cur->h, strlen(tab->hist_cur->h)+1);
-		imsg_flush(fsibuf);
 	}
 
-	imsg_compose(fsibuf, IMSG_SESSION_END, 0, 0, -1, NULL, 0);
-	imsg_flush(fsibuf);
+	ui_send_fs(IMSG_SESSION_END, 0, NULL, 0);
 }
 
 static void
@@ -726,6 +712,21 @@ start_child(enum telescope_process p, const char *argv
 	err(1, "execvp(%s)", argv0);
 }
 
+static int
+ui_send_net(int type, uint32_t peerid, const void *data,
+    uint16_t datalen)
+{
+	return imsg_compose_event(iev_net, type, peerid, 0, -1, data,
+	    datalen);
+}
+
+static int
+ui_send_fs(int type, uint32_t peerid, const void *data, uint16_t datalen)
+{
+	return imsg_compose_event(iev_fs, type, peerid, 0, -1, data,
+	    datalen);
+}
+
 static void __attribute__((noreturn))
 usage(int r)
 {
@@ -737,8 +738,8 @@ usage(int r)
 int
 main(int argc, char * const *argv)
 {
-	struct imsgbuf	 net_ibuf, fs_ibuf;
-	int		 net_fds[2], fs_fds[2];
+	struct imsgev	 net_ibuf, fs_ibuf;
+	int		 pipe2net[2], pipe2fs[2];
 	int		 ch, configtest = 0, fail = 0;
 	int		 has_url = 0;
 	int		 proc = -1;
@@ -818,17 +819,19 @@ main(int argc, char * const *argv)
 	}
 
 	/* Start children. */
-	if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, fs_fds) == -1)
+	if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, pipe2fs) == -1)
 		err(1, "socketpair");
-	start_child(PROC_FS, argv0, fs_fds[1]);
-	imsg_init(&fs_ibuf, fs_fds[0]);
-	fsibuf = &fs_ibuf;
+	start_child(PROC_FS, argv0, pipe2fs[1]);
+	imsg_init(&fs_ibuf.ibuf, pipe2fs[0]);
+	iev_fs = &fs_ibuf;
+	iev_fs->handler = handle_dispatch_imsg;
 
-	if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, net_fds) == -1)
+	if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, pipe2net) == -1)
 		err(1, "socketpair");
-	start_child(PROC_NET, argv0, net_fds[1]);
-	imsg_init(&net_ibuf, net_fds[0]);
-	netibuf = &net_ibuf;
+	start_child(PROC_NET, argv0, pipe2net[1]);
+	imsg_init(&net_ibuf.ibuf, pipe2net[0]);
+	iev_net = &net_ibuf;
+	iev_net->handler = handle_dispatch_imsg;
 
 	setproctitle("ui");
 
@@ -839,13 +842,16 @@ main(int argc, char * const *argv)
 
 	event_init();
 
-	event_set(&netev, netibuf->fd, EV_READ | EV_PERSIST,
-	    handle_dispatch_imsg, netibuf);
-	event_add(&netev, NULL);
+	/* Setup event handlers for pipes to fs/net */
+	iev_fs->events = EV_READ;
+	event_set(&iev_fs->ev, iev_fs->ibuf.fd, iev_fs->events,
+	    iev_fs->handler, iev_fs);
+	event_add(&iev_fs->ev, NULL);
 
-	event_set(&fsev, fsibuf->fd, EV_READ | EV_PERSIST,
-	    handle_dispatch_imsg, fsibuf);
-	event_add(&fsev, NULL);
+	iev_net->events = EV_READ;
+	event_set(&iev_net->ev, iev_net->ibuf.fd, iev_net->events,
+	    iev_net->handler, iev_net);
+	event_add(&iev_net->ev, NULL);
 
 	if (ui_init()) {
 		load_last_session(session_new_tab_cb);
@@ -857,11 +863,8 @@ main(int argc, char * const *argv)
 		ui_end();
 	}
 
-	imsg_compose(netibuf, IMSG_QUIT, 0, 0, -1, NULL, 0);
-	imsg_flush(netibuf);
+	ui_send_fs(IMSG_QUIT, 0, NULL, 0);
+	ui_send_net(IMSG_QUIT, 0, NULL, 0);
 
-	imsg_compose(fsibuf, IMSG_QUIT, 0, 0, -1, NULL, 0);
-	imsg_flush(fsibuf);
-
 	return 0;
 }
blob - 1384bbb676be2cec810813e94257bcbe9b8ff103
blob + 18b35c9587fc01b7b09a7b890ca79d2554a20783
--- telescope.h
+++ telescope.h
@@ -27,6 +27,13 @@
 #define MAX(a, b) ((a) > (b) ? (a) : (b))
 
 #define GEMINI_URL_LEN 1024
+
+struct imsgev {
+	struct imsgbuf	 ibuf;
+	void		(*handler)(int, short, void *);
+	struct event	 ev;
+	short		 events;
+};
 
 enum imsg_type {
 	/* ui <-> client/fs */
@@ -383,7 +390,8 @@ int		 mark_nonblock(int);
 int		 has_prefix(const char*, const char*);
 int		 unicode_isspace(uint32_t);
 int		 unicode_isgraph(uint32_t);
-void		 dispatch_imsg(struct imsgbuf*, imsg_handlerfn**, size_t);
+void		 dispatch_imsg(struct imsgev*, short, imsg_handlerfn**, size_t);
+int		 imsg_compose_event(struct imsgev *, uint16_t, uint32_t, pid_t, int, const void *, uint16_t);
 
 /* wrap.c */
 void		 erase_buffer(struct buffer *);
blob - 5d0267624c08d1c1f99cedfe2af9e7b944e563d8
blob + 9d51f36474be436c2bd10eae4ecd1f741a37ebd8
--- util.c
+++ util.c
@@ -22,6 +22,8 @@
 #include <stdlib.h>
 #include <unistd.h>
 
+static void	 imsg_event_add(struct imsgev *);
+
 int
 mark_nonblock(int fd)
 {
@@ -61,27 +63,47 @@ unicode_isgraph(uint32_t cp)
 	return 1;
 }
 
+static void
+imsg_event_add(struct imsgev *iev)
+{
+	iev->events = EV_READ;
+	if (iev->ibuf.w.queued)
+		iev->events |= EV_WRITE;
+
+	event_del(&iev->ev);
+	event_set(&iev->ev, iev->ibuf.fd, iev->events, iev->handler, iev);
+	event_add(&iev->ev, NULL);
+}
+
 void
-dispatch_imsg(struct imsgbuf *ibuf, imsg_handlerfn **handlers, size_t size)
+dispatch_imsg(struct imsgev *iev, short event, imsg_handlerfn **handlers,
+    size_t size)
 {
-	struct imsg	imsg;
-	size_t		datalen, i;
-	ssize_t		n;
+	struct imsgbuf	*ibuf;
+	struct imsg	 imsg;
+	size_t		 datalen, i;
+	ssize_t		 n;
 
-	if ((n = imsg_read(ibuf)) == -1) {
-		if (errno == EAGAIN || errno == EWOULDBLOCK)
-			return;
-                _exit(1);
+	ibuf = &iev->ibuf;
+
+	if (event & EV_READ) {
+		if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN)
+			err(1, "imsg_read error");
+		if (n == 0)
+			err(1, "connection closed");
 	}
+	if (event & EV_WRITE) {
+		if ((n = msgbuf_write(&ibuf->w)) == -1 && errno != EAGAIN)
+			err(1, "msgbuf_write");
+		if (n == 0)
+			err(1, "connection closed");
+	}
 
-	if (n == 0)
-		_exit(1);
-
 	for (;;) {
 		if ((n = imsg_get(ibuf, &imsg)) == -1)
 			_exit(1);
 		if (n == 0)
-			return;
+			break;
 		datalen = imsg.hdr.len - IMSG_HEADER_SIZE;
 		i = imsg.hdr.type;
 		if (i > (size / sizeof(imsg_handlerfn*)) || handlers[i] == NULL)
@@ -89,4 +111,19 @@ dispatch_imsg(struct imsgbuf *ibuf, imsg_handlerfn **h
 		handlers[i](&imsg, datalen);
 		imsg_free(&imsg);
 	}
+
+	imsg_event_add(iev);
 }
+
+int
+imsg_compose_event(struct imsgev *iev, uint16_t type, uint32_t peerid,
+    pid_t pid, int fd, const void *data, uint16_t datalen)
+{
+	int	ret;
+
+	if ((ret = imsg_compose(&iev->ibuf, type, peerid, pid, fd, data,
+	    datalen) != -1))
+		imsg_event_add(iev);
+
+	return ret;
+}