commit f6020519d156c726a30a1be69ab736aa4bdf3d38 from: Stefan Sperling date: Mon Mar 11 09:49:09 2024 UTC start notifier and handle events for it 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;