Commit Diff


commit - 7ee326cb704951ec81c37a236a98d8a62f755ad4
commit + f6020519d156c726a30a1be69ab736aa4bdf3d38
blob - b3a4fae0b273aa937461ce7fe139b1410c594ab5
blob + 532bc19306dddc32e7c2236a2b958a02e9c0bf8b
--- gotd/gotd.c
+++ gotd/gotd.c
@@ -1118,6 +1118,74 @@ done:
 }
 
 static void
+gotd_dispatch_notifier(int fd, short event, void *arg)
+{
+	struct gotd_imsgev *iev = arg;
+	struct imsgbuf *ibuf = &iev->ibuf;
+	struct gotd_child_proc *proc = gotd.notify_proc;
+	ssize_t n;
+	int shut = 0;
+	struct imsg imsg;
+
+	if (proc->iev.ibuf.fd != fd)
+		fatalx("%s: unexpected fd %d", __func__, fd);
+
+	if (event & EV_READ) {
+		if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN)
+			fatal("imsg_read error");
+		if (n == 0) {
+			/* Connection closed. */
+			shut = 1;
+			goto done;
+		}
+	}
+
+	if (event & EV_WRITE) {
+		n = msgbuf_write(&ibuf->w);
+		if (n == -1 && errno != EAGAIN)
+			fatal("msgbuf_write");
+		if (n == 0) {
+			/* Connection closed. */
+			shut = 1;
+			goto done;
+		}
+	}
+
+	for (;;) {
+		if ((n = imsg_get(ibuf, &imsg)) == -1)
+			fatal("%s: imsg_get error", __func__);
+		if (n == 0)	/* No more messages. */
+			break;
+
+		switch (imsg.hdr.type) {
+		default:
+			log_debug("unexpected imsg %d", imsg.hdr.type);
+			break;
+		}
+
+		imsg_free(&imsg);
+	}
+done:
+	if (!shut) {
+		gotd_imsg_event_add(iev);
+	} else {
+		/* This pipe is dead. Remove its event handler */
+		event_del(&iev->ev);
+
+		/*
+		 * Do not exit all of gotd if the notification handler dies.
+		 * We can continue operating without notifications until an
+		 * operator intervenes.
+		 */
+		log_warnx("notify child process (pid %d) closed its imsg pipe "
+		    "unexpectedly", proc->pid);
+		gotd.notify_proc = NULL;
+		imsg_clear(&iev->ibuf);
+		free(proc);
+	}
+}
+
+static void
 gotd_dispatch_auth_child(int fd, short event, void *arg)
 {
 	const struct got_error *err = NULL;
@@ -1410,6 +1478,30 @@ done:
 	}
 }
 
+static const struct got_error *
+connect_notifier(struct gotd_client *client)
+{
+	const struct got_error *err = NULL;
+	int s;
+
+	if (gotd.notify_proc) {
+		s = dup(gotd.notify_proc->pipe[0]);
+		if (s == -1)
+			return got_error_from_errno("dup");
+	} else
+		s = -1;
+
+	if (gotd_imsg_compose_event(&client->session->iev,
+	    GOTD_IMSG_CONNECT_NOTIFIER, PROC_GOTD, s, NULL, 0) == -1) {
+		err = got_error_from_errno("imsg compose CONNECT_NOTIFIER");
+		if (s != -1)
+			close(s);
+		return err;
+	}
+
+	return NULL;
+}
+
 static void
 gotd_dispatch_repo_child(int fd, short event, void *arg)
 {
@@ -1473,6 +1565,9 @@ gotd_dispatch_repo_child(int fd, short event, void *ar
 			err = connect_session(client);
 			if (err)
 				break;
+			err = connect_notifier(client);
+			if (err)
+				break;
 			err = connect_repo_child(client, proc);
 			break;
 		default:
@@ -1608,6 +1703,35 @@ start_listener(char *argv0, const char *confpath, int 
 	gotd.listen_proc = proc;
 }
 
+static void
+start_notifier(char *argv0, const char *confpath, int daemonize, int verbosity)
+{
+	struct gotd_child_proc *proc;
+
+	proc = calloc(1, sizeof(*proc));
+	if (proc == NULL)
+		fatal("calloc");
+
+	TAILQ_INSERT_HEAD(&procs, proc, entry);
+
+	/* proc->tmo is initialized in main() after event_init() */
+
+	proc->type = PROC_NOTIFY;
+
+	if (socketpair(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK,
+	    PF_UNSPEC, proc->pipe) == -1)
+		fatal("socketpair");
+
+	proc->pid = start_child(proc->type, NULL, argv0, confpath,
+	    proc->pipe[1], daemonize, verbosity);
+	imsg_init(&proc->iev.ibuf, proc->pipe[0]);
+	proc->iev.handler = gotd_dispatch_notifier;
+	proc->iev.events = EV_READ;
+	proc->iev.handler_arg = NULL;
+
+	gotd.notify_proc = proc;
+}
+
 static const struct got_error *
 start_session_child(struct gotd_client *client, struct gotd_repo *repo,
     char *argv0, const char *confpath, int daemonize, int verbosity)
@@ -1953,6 +2077,7 @@ main(int argc, char **argv)
 			fatal("daemon");
 		gotd.pid = getpid();
 		start_listener(argv0, confpath, daemonize, verbosity);
+		start_notifier(argv0, confpath, daemonize, verbosity);
 	} else if (proc_id == PROC_LISTEN) {
 		snprintf(title, sizeof(title), "%s", gotd_proc_names[proc_id]);
 		if (verbosity) {
@@ -2104,6 +2229,8 @@ main(int argc, char **argv)
 
 	evtimer_set(&gotd.listen_proc->tmo, kill_proc_timeout,
 	    gotd.listen_proc);
+	evtimer_set(&gotd.notify_proc->tmo, kill_proc_timeout,
+	    gotd.notify_proc);
 
 	apply_unveil_selfexec();
 
blob - e34d36cf3f78fa3e6dbc1962c74f9995d0a44e1a
blob + 536e8f08bcba8d30704aa3b6ee94d5a1bb340368
--- gotd/gotd.h
+++ gotd/gotd.h
@@ -170,6 +170,8 @@ struct gotd {
 	struct gotd_repolist repos;
 	int nrepos;
 	struct gotd_child_proc *listen_proc;
+	struct gotd_child_proc *notify_proc;
+	int notifications_enabled;
 	struct timeval request_timeout;
 	struct timeval auth_timeout;
 	struct gotd_uid_connection_limit *connection_limits;
@@ -245,7 +247,8 @@ enum gotd_imsg_type {
 	GOTD_IMSG_ACCESS_GRANTED,
 
 	/* Notify child process. */
-	GOTD_IMSG_NOTIFY_READY,
+	GOTD_IMSG_CONNECT_NOTIFIER,
+	GOTD_IMSG_NOTIFY,
 };
 
 /* Structure for GOTD_IMSG_ERROR. */
blob - 929e63947c237f8baaeb6b5c9a27980e7054685c
blob + 78359bad4c5f1afe2fbc0dbf0c5a37663f8056b8
--- gotd/notify.c
+++ gotd/notify.c
@@ -156,14 +156,9 @@ notify_main(const char *title)
 	gotd_notify.parent_iev.handler_arg = NULL;
 	event_set(&gotd_notify.parent_iev.ev, gotd_notify.parent_iev.ibuf.fd,
 	    EV_READ, notify_dispatch, &gotd_notify.parent_iev);
-	if (gotd_imsg_compose_event(&gotd_notify.parent_iev,
-	    GOTD_IMSG_NOTIFY_READY, PROC_NOTIFY, -1, NULL, 0) == -1) {
-		err = got_error_from_errno("imsg compose NOTIFY_READY");
-		goto done;
-	}
 
 	event_dispatch();
-done:
+
 	if (err)
 		log_warnx("%s: %s", title, err->msg);
 	gotd_notify_shutdown();
blob - 402b1ccfef8728b954ba0339814fdc2e1b9c7ba4
blob + 1f2ffba1e002969197e6b483aaa711da48333150
--- gotd/session.c
+++ gotd/session.c
@@ -61,6 +61,7 @@ static struct gotd_session {
 	int *pack_fds;
 	int *temp_fds;
 	struct gotd_imsgev parent_iev;
+	struct gotd_imsgev notifier_iev;
 	struct timeval request_timeout;
 	enum gotd_procid proc_id;
 } gotd_session;
@@ -1318,8 +1319,95 @@ recv_connect(struct imsg *imsg)
 
 	return NULL;
 }
+
+static void
+session_dispatch_notifier(int fd, short event, void *arg)
+{
+	struct gotd_imsgev *iev = arg;
+	struct imsgbuf *ibuf = &iev->ibuf;
+	ssize_t n;
+	int shut = 0;
+	struct imsg imsg;
+
+	if (event & EV_READ) {
+		if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN)
+			fatal("imsg_read error");
+		if (n == 0) {
+			/* Connection closed. */
+			shut = 1;
+			goto done;
+		}
+	}
+
+	if (event & EV_WRITE) {
+		n = msgbuf_write(&ibuf->w);
+		if (n == -1 && errno != EAGAIN)
+			fatal("msgbuf_write");
+		if (n == 0) {
+			/* Connection closed. */
+			shut = 1;
+			goto done;
+		}
+	}
 
+	for (;;) {
+		if ((n = imsg_get(ibuf, &imsg)) == -1)
+			fatal("%s: imsg_get error", __func__);
+		if (n == 0)	/* No more messages. */
+			break;
+
+		switch (imsg.hdr.type) {
+		default:
+			log_debug("unexpected imsg %d", imsg.hdr.type);
+			break;
+		}
+
+		imsg_free(&imsg);
+	}
+done:
+	if (!shut) {
+		gotd_imsg_event_add(iev);
+	} else {
+		/* This pipe is dead. Remove its event handler */
+		event_del(&iev->ev);
+		imsg_clear(&iev->ibuf);
+		imsg_init(&iev->ibuf, -1);
+	}
+}
+
 static const struct got_error *
+recv_notifier(struct imsg *imsg)
+{
+	struct gotd_imsgev *iev = &gotd_session.notifier_iev;
+	struct gotd_session_client *client = &gotd_session_client;
+	size_t datalen;
+
+	if (client->state != GOTD_STATE_EXPECT_LIST_REFS)
+		return got_error(GOT_ERR_PRIVSEP_MSG);
+
+	/* We should already have received a pipe to the listener. */
+	if (client->fd == -1)
+		return got_error(GOT_ERR_PRIVSEP_MSG);
+
+	datalen = imsg->hdr.len - IMSG_HEADER_SIZE;
+	if (datalen != 0)
+		return got_error(GOT_ERR_PRIVSEP_LEN);
+
+	if (imsg->fd == -1)
+		return NULL; /* notifications unused */
+
+	imsg_init(&iev->ibuf, imsg->fd);
+	iev->handler = session_dispatch_notifier;
+	iev->events = EV_READ;
+	iev->handler_arg = NULL;
+	event_set(&iev->ev, iev->ibuf.fd, EV_READ,
+	    session_dispatch_notifier, iev);
+	gotd_imsg_event_add(iev);
+
+	return NULL;
+}
+
+static const struct got_error *
 recv_repo_child(struct imsg *imsg)
 {
 	struct gotd_imsg_connect_repo_child ichild;
@@ -1420,6 +1508,9 @@ session_dispatch(int fd, short event, void *arg)
 		case GOTD_IMSG_DISCONNECT:
 			do_disconnect = 1;
 			break;
+		case GOTD_IMSG_CONNECT_NOTIFIER:
+			err = recv_notifier(&imsg);
+			break;
 		case GOTD_IMSG_CONNECT_REPO_CHILD:
 			err = recv_repo_child(&imsg);
 			if (err)
@@ -1469,6 +1560,8 @@ session_main(const char *title, const char *repo_path,
 	    sizeof(gotd_session.request_timeout));
 	gotd_session.proc_id = proc_id;
 
+	imsg_init(&gotd_session.notifier_iev.ibuf, -1);
+
 	err = got_repo_open(&gotd_session.repo, repo_path, NULL, pack_fds);
 	if (err)
 		goto done;