commit 202d187071957eec318c523e66455e0f804ff869 from: Stefan Sperling date: Mon Mar 11 09:49:10 2024 UTC connect session and notifier via a dedicated pipe and fix proc wiring commit - 6490a1e911df9c4f629efb6a0c6a24ab9dc392dc commit + 202d187071957eec318c523e66455e0f804ff869 blob - ac01cf603e2090b0c97aa789ce265be2acd8eda7 blob + 815ba754b8247de6076160659c86feb748fab436 --- gotd/gotd.c +++ gotd/gotd.c @@ -301,6 +301,9 @@ proc_done(struct gotd_child_proc *proc) disconnect(client); } + if (proc == gotd.notify_proc) + gotd.notify_proc = NULL; + evtimer_del(&proc->tmo); if (proc->iev.ibuf.fd != -1) { @@ -1179,9 +1182,7 @@ done: */ log_warnx("notify child process (pid %d) closed its imsg pipe " "unexpectedly", proc->pid); - gotd.notify_proc = NULL; - imsg_clear(&iev->ibuf); - free(proc); + proc_done(proc); } } @@ -1479,26 +1480,36 @@ done: } static const struct got_error * -connect_notifier(struct gotd_client *client) +connect_notifier_and_session(struct gotd_client *client) { const struct got_error *err = NULL; - int s; + struct gotd_imsgev *session_iev = &client->session->iev; + int pipe[2]; - 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.notify_proc == NULL) + return NULL; - if (gotd_imsg_compose_event(&client->session->iev, - GOTD_IMSG_CONNECT_NOTIFIER, PROC_GOTD, s, NULL, 0) == -1) { + if (socketpair(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, + PF_UNSPEC, pipe) == -1) + return got_error_from_errno("socketpair"); + + /* Pass notifier pipe to session . */ + if (gotd_imsg_compose_event(session_iev, GOTD_IMSG_CONNECT_NOTIFIER, + PROC_GOTD, pipe[0], NULL, 0) == -1) { err = got_error_from_errno("imsg compose CONNECT_NOTIFIER"); - if (s != -1) - close(s); + close(pipe[0]); + close(pipe[1]); return err; } + /* Pass session pipe to notifier. */ + if (gotd_imsg_compose_event(&gotd.notify_proc->iev, + GOTD_IMSG_CONNECT_SESSION, PROC_GOTD, pipe[1], NULL, 0) == -1) { + err = got_error_from_errno("imsg compose CONNECT_SESSION"); + close(pipe[1]); + return err; + } + return NULL; } @@ -1565,7 +1576,7 @@ gotd_dispatch_repo_child(int fd, short event, void *ar err = connect_session(client); if (err) break; - err = connect_notifier(client); + err = connect_notifier_and_session(client); if (err) break; err = connect_repo_child(client, proc); @@ -1728,6 +1739,8 @@ start_notifier(char *argv0, const char *confpath, int proc->iev.handler = gotd_dispatch_notifier; proc->iev.events = EV_READ; proc->iev.handler_arg = NULL; + event_set(&proc->iev.ev, proc->iev.ibuf.fd, EV_READ, + gotd_dispatch_notifier, &proc->iev); gotd.notify_proc = proc; } @@ -2219,7 +2232,7 @@ main(int argc, char **argv) exit(0); case PROC_NOTIFY: #ifndef PROFILE - if (pledge("stdio proc exec unveil", NULL) == -1) + if (pledge("stdio proc exec recvfd unveil", NULL) == -1) err(1, "pledge"); #endif /* @@ -2239,8 +2252,10 @@ 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); + if (gotd.notify_proc) { + evtimer_set(&gotd.notify_proc->tmo, kill_proc_timeout, + gotd.notify_proc); + } apply_unveil_selfexec(); @@ -2258,6 +2273,8 @@ main(int argc, char **argv) signal_add(&evsigchld, NULL); gotd_imsg_event_add(&gotd.listen_proc->iev); + if (gotd.notify_proc) + gotd_imsg_event_add(&gotd.notify_proc->iev); event_dispatch(); blob - 97b77d29898f9e0859f385d519bf8e52600928f0 blob + 52cfe7948d93903861aec5c5f4f85960643c6444 --- gotd/gotd.h +++ gotd/gotd.h @@ -248,6 +248,7 @@ enum gotd_imsg_type { /* Notify child process. */ GOTD_IMSG_CONNECT_NOTIFIER, + GOTD_IMSG_CONNECT_SESSION, GOTD_IMSG_NOTIFY, }; blob - d8541cd4c711e5330e5a012fabb0dcc19ef71550 blob + 63b435eac961cc3e4ec6d6960946eedd84176f65 --- gotd/notify.c +++ gotd/notify.c @@ -22,6 +22,7 @@ #include #include +#include #include #include #include @@ -39,6 +40,10 @@ #include "log.h" #include "notify.h" +#ifndef nitems +#define nitems(_a) (sizeof((_a)) / sizeof((_a)[0])) +#endif + static struct gotd_notify { pid_t pid; const char *title; @@ -47,10 +52,92 @@ static struct gotd_notify { const char *default_sender; } gotd_notify; -void gotd_notify_sighdlr(int sig, short event, void *arg); +struct gotd_notify_session { + STAILQ_ENTRY(gotd_notify_session) entry; + uint32_t id; + struct gotd_imsgev iev; +}; +STAILQ_HEAD(gotd_notify_sessions, gotd_notify_session); + +static struct gotd_notify_sessions gotd_notify_sessions[GOTD_CLIENT_TABLE_SIZE]; +static SIPHASH_KEY sessions_hash_key; + static void gotd_notify_shutdown(void); -void +static uint64_t +session_hash(uint32_t session_id) +{ + return SipHash24(&sessions_hash_key, &session_id, sizeof(session_id)); +} + +static void +add_session(struct gotd_notify_session *session) +{ + uint64_t slot; + + slot = session_hash(session->id) % nitems(gotd_notify_sessions); + STAILQ_INSERT_HEAD(&gotd_notify_sessions[slot], session, entry); +} + +static struct gotd_notify_session * +find_session(uint32_t session_id) +{ + uint64_t slot; + struct gotd_notify_session *s; + + slot = session_hash(session_id) % nitems(gotd_notify_sessions); + STAILQ_FOREACH(s, &gotd_notify_sessions[slot], entry) { + if (s->id == session_id) + return s; + } + + return NULL; +} + +static struct gotd_notify_session * +find_session_by_fd(int fd) +{ + uint64_t slot; + struct gotd_notify_session *s; + + for (slot = 0; slot < nitems(gotd_notify_sessions); slot++) { + STAILQ_FOREACH(s, &gotd_notify_sessions[slot], entry) { + if (s->iev.ibuf.fd == fd) + return s; + } + + } + + return NULL; +} + + +static void +remove_session(struct gotd_notify_session *session) +{ + uint64_t slot; + + slot = session_hash(session->id) % nitems(gotd_notify_sessions); + STAILQ_REMOVE(&gotd_notify_sessions[slot], session, + gotd_notify_session, entry); + free(session); +} + +static uint32_t +get_session_id(void) +{ + int duplicate = 0; + uint32_t id; + + do { + id = arc4random(); + duplicate = (find_session(id) != NULL); + } while (duplicate || id == 0); + + return id; +} + +static void gotd_notify_sighdlr(int sig, short event, void *arg) { /* @@ -202,7 +289,7 @@ send_notifications(struct imsg *imsg) } static void -notify_dispatch(int fd, short event, void *arg) +notify_dispatch_session(int fd, short event, void *arg) { struct gotd_imsgev *iev = arg; struct imsgbuf *ibuf = &iev->ibuf; @@ -242,8 +329,104 @@ notify_dispatch(int fd, short event, void *arg) switch (imsg.hdr.type) { case GOTD_IMSG_NOTIFY: err = send_notifications(&imsg); - if (err) - break; + break; + default: + log_debug("unexpected imsg %d", imsg.hdr.type); + break; + } + imsg_free(&imsg); + + if (err) + log_warnx("%s: %s", __func__, err->msg); + } +done: + if (!shut) { + gotd_imsg_event_add(iev); + } else { + struct gotd_notify_session *session; + + /* This pipe is dead. Remove its event handler */ + event_del(&iev->ev); + imsg_clear(&iev->ibuf); + + session = find_session_by_fd(fd); + if (session) + remove_session(session); + } +} + +static const struct got_error * +recv_session(struct imsg *imsg) +{ + struct gotd_notify_session *session; + size_t datalen; + + datalen = imsg->hdr.len - IMSG_HEADER_SIZE; + if (datalen != 0) + return got_error(GOT_ERR_PRIVSEP_LEN); + + if (imsg->fd == -1) + return got_error(GOT_ERR_PRIVSEP_NO_FD); + + session = calloc(1, sizeof(*session)); + if (session == NULL) + return got_error_from_errno("calloc"); + + session->id = get_session_id(); + imsg_init(&session->iev.ibuf, imsg->fd); + session->iev.handler = notify_dispatch_session; + session->iev.events = EV_READ; + session->iev.handler_arg = NULL; + event_set(&session->iev.ev, session->iev.ibuf.fd, EV_READ, + notify_dispatch_session, &session->iev); + gotd_imsg_event_add(&session->iev); + add_session(session); + + return NULL; +} + +static void +notify_dispatch(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 (;;) { + const struct got_error *err = NULL; + + if ((n = imsg_get(ibuf, &imsg)) == -1) + fatal("%s: imsg_get error", __func__); + if (n == 0) /* No more messages. */ + break; + + switch (imsg.hdr.type) { + case GOTD_IMSG_CONNECT_SESSION: + err = recv_session(&imsg); + break; default: log_debug("unexpected imsg %d", imsg.hdr.type); break; @@ -271,6 +454,8 @@ notify_main(const char *title, struct gotd_repolist *r const struct got_error *err = NULL; struct event evsigint, evsigterm, evsighup, evsigusr1; + arc4random_buf(&sessions_hash_key, sizeof(sessions_hash_key)); + gotd_notify.title = title; gotd_notify.repos = repos; gotd_notify.default_sender = default_sender; @@ -293,6 +478,7 @@ notify_main(const char *title, struct gotd_repolist *r 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); + gotd_imsg_event_add(&gotd_notify.parent_iev); event_dispatch();