commit 4ddc9d7454d1838afd08981593a8b32332f3725f from: Stefan Sperling date: Mon Mar 11 09:49:10 2024 UTC prepare session process for receiving notification content from repo_write commit - 202d187071957eec318c523e66455e0f804ff869 commit + 4ddc9d7454d1838afd08981593a8b32332f3725f blob - 52cfe7948d93903861aec5c5f4f85960643c6444 blob + 377311c81a12015a42f5c01d3783018e7e8175b3 --- gotd/gotd.h +++ gotd/gotd.h @@ -500,7 +500,22 @@ struct gotd_imsg_auth { uint32_t client_id; }; -/* Structure for GOTD_IMSG_NOTIFY. */ +/* Structures for GOTD_IMSG_NOTIFY. */ +enum gotd_notification_action { + GOTD_NOTIF_ACTION_CREATED, + GOTD_NOTIF_ACTION_REMOVED, + GOTD_NOTIF_ACTION_CHANGED +}; +/* IMSG_NOTIFY session <-> repo_write */ +struct gotd_imsg_notification_content { + uint32_t client_id; + enum gotd_notification_action action; + uint8_t old_id[SHA1_DIGEST_LENGTH]; + uint8_t new_id[SHA1_DIGEST_LENGTH]; + size_t refname_len; + /* Followed by refname_len data bytes. */ +}; +/* IMSG_NOTIFY session -> notify*/ struct gotd_imsg_notify { char repo_name[NAME_MAX]; char subject_line[64]; blob - bf6acf421f9500ff062de590c586d4fe239003e7 blob + 8427fbb413b6a974cd738fd557463719005ebac9 --- gotd/session.c +++ gotd/session.c @@ -53,6 +53,15 @@ #include "log.h" #include "session.h" +struct gotd_session_notif { + STAILQ_ENTRY(gotd_session_notif) entry; + enum gotd_notification_action action; + char *refname; + struct got_object_id old_id; + struct got_object_id new_id; + int fd; +}; +STAILQ_HEAD(gotd_session_notifications, gotd_session_notif) notifications; static struct gotd_session { pid_t pid; @@ -413,23 +422,24 @@ validate_namespace(const char *namespace) } static const struct got_error * -send_notification(struct got_object_id *old_id, struct got_object_id *new_id, +prepare_notification(struct got_object_id *old_id, struct got_object_id *new_id, struct got_repository *repo, struct got_reference *ref) { const struct got_error *err = NULL; struct gotd_session_client *client = &gotd_session_client; struct gotd_repo *repo_cfg = gotd_session.repo_cfg; - struct gotd_imsgev *iev = &gotd_session.notifier_iev; + struct gotd_imsgev *iev = &client->repo_child_iev; + struct gotd_imsg_notification_content req; struct got_pathlist_entry *pe; - struct gotd_imsg_notify inotify; + struct gotd_session_notif *notif; + struct ibuf *wbuf; + size_t len, refname_len; int fd = -1; - char hostname[HOST_NAME_MAX + 1]; - const char *action = ""; if (iev->ibuf.fd == -1) return NULL; /* notifications unused */ - memset(&inotify, 0, sizeof(inotify)); + memset(&req, 0, sizeof(req)); TAILQ_FOREACH(pe, &repo_cfg->notification_refs, entry) { const char *refname = pe->path; @@ -439,9 +449,6 @@ send_notification(struct got_object_id *old_id, struct if (pe == NULL && !TAILQ_EMPTY(&repo_cfg->notification_refs)) return NULL; - if (gethostname(hostname, sizeof(hostname)) == -1) - return got_error_from_errno("gethostname"); - TAILQ_FOREACH(pe, &repo_cfg->notification_ref_namespaces, entry) { const char *namespace = pe->path; @@ -460,30 +467,167 @@ send_notification(struct got_object_id *old_id, struct if (fd == -1) return got_error_from_errno("got_opentempfd"); + if (old_id) + memcpy(req.old_id, old_id->sha1, sizeof(req.old_id)); + if (new_id) + memcpy(req.new_id, new_id->sha1, sizeof(req.new_id)); + if (old_id == NULL) - action = "created"; + req.action = GOTD_NOTIF_ACTION_CREATED; else if (new_id == NULL) - action = "removed"; + req.action = GOTD_NOTIF_ACTION_REMOVED; else - action = "changed"; - - strlcpy(inotify.repo_name, gotd_session.repo_cfg->name, - sizeof(inotify.repo_name)); - - snprintf(inotify.subject_line, sizeof(inotify.subject_line), - "%s: %s: UID %d %s %s", hostname, gotd_session.repo_cfg->name, - client->euid, action, got_ref_get_name(ref)); - - dprintf(fd, "%s %s\n", action, got_ref_get_name(ref)); - - if (gotd_imsg_compose_event(iev, GOTD_IMSG_NOTIFY, PROC_SESSION_WRITE, - fd, &inotify, sizeof(inotify)) == -1) { - err = got_error_from_errno("imsg compose NOTIFY"); + req.action = GOTD_NOTIF_ACTION_CHANGED; + + notif = calloc(1, sizeof(*notif)); + if (notif == NULL) { + err = got_error_from_errno("calloc"); close(fd); return err; + } + notif->fd = dup(fd); + if (notif->fd == -1) { + err = got_error_from_errno("dup"); + goto done; + } + notif->action = req.action; + notif->refname = strdup(got_ref_get_name(ref)); + if (notif->refname == NULL) { + err = got_error_from_errno("strdup"); + goto done; } - return NULL; + refname_len = strlen(got_ref_get_name(ref)); + len = sizeof(struct gotd_session_notif) + refname_len; + wbuf = imsg_create(&iev->ibuf, GOTD_IMSG_NOTIFY, gotd_session.proc_id, + gotd_session.pid, len); + if (wbuf == NULL) { + err = got_error_from_errno("imsg_create NOTIFY"); + goto done; + } + + if (imsg_add(wbuf, &req, sizeof(req)) == -1) { + err = got_error_from_errno("imsg_add NOTIFY"); + goto done; + } + if (imsg_add(wbuf, got_ref_get_name(ref), refname_len) == -1) { + err = got_error_from_errno("imsg_add NOTIFY"); + goto done; + } + + STAILQ_INSERT_TAIL(¬ifications, notif, entry); + + wbuf->fd = fd; + fd = -1; + imsg_close(&iev->ibuf, wbuf); + gotd_imsg_event_add(iev); +done: + if (err && notif) { + if (notif->fd != -1 && close(notif->fd) == -1 && err == NULL) + err = got_error_from_errno("close"); + free(notif->refname); + free(notif); + } + if (fd != -1 && close(fd) == -1 && err == NULL) + err = got_error_from_errno("close"); + return err; +} + +static const struct got_error * +send_notification(struct gotd_session_client *client, struct imsg *imsg) +{ + const struct got_error *err = NULL; + struct gotd_imsgev *iev = &client->repo_child_iev; + struct gotd_session_notif *notif, *tmp; + struct gotd_imsg_notification_content icontent; + char *refname = NULL; + size_t datalen; + struct gotd_imsg_notify inotify; + char hostname[HOST_NAME_MAX + 1]; + + memset(&inotify, 0, sizeof(inotify)); + + if (imsg->fd == -1) + return got_error(GOT_ERR_PRIVSEP_NO_FD); + + datalen = imsg->hdr.len - IMSG_HEADER_SIZE; + if (datalen < sizeof(icontent)) + return got_error(GOT_ERR_PRIVSEP_LEN); + memcpy(&icontent, imsg->data, sizeof(icontent)); + if (datalen != sizeof(icontent) + icontent.refname_len) + return got_error(GOT_ERR_PRIVSEP_LEN); + refname = strndup(imsg->data + sizeof(icontent), icontent.refname_len); + if (refname == NULL) + return got_error_from_errno("strndup"); + + STAILQ_FOREACH_SAFE(notif, ¬ifications, entry, tmp) { + if (notif->action != icontent.action || + strcmp(notif->refname, refname) != 0) + continue; + if (notif->action == GOTD_NOTIF_ACTION_CREATED) { + if (memcmp(notif->new_id.sha1, icontent.new_id, + SHA1_DIGEST_LENGTH) != 0) + continue; + } else if (notif->action == GOTD_NOTIF_ACTION_REMOVED) { + if (memcmp(notif->old_id.sha1, icontent.old_id, + SHA1_DIGEST_LENGTH) != 0) + continue; + } else { + if (memcmp(notif->old_id.sha1, icontent.old_id, + SHA1_DIGEST_LENGTH) != 0 || + memcmp(notif->new_id.sha1, icontent.new_id, + SHA1_DIGEST_LENGTH) != 0) + continue; + } + + STAILQ_REMOVE(¬ifications, notif, gotd_session_notif, entry); + break; + } + + if (notif) { + const char *action; + + switch (notif->action) { + case GOTD_NOTIF_ACTION_CREATED: + action = "created"; + break; + case GOTD_NOTIF_ACTION_REMOVED: + action = "created"; + break; + case GOTD_NOTIF_ACTION_CHANGED: + action = "changed"; + break; + default: + err = got_error(GOT_ERR_PRIVSEP_MSG); + goto done; + } + + if (gethostname(hostname, sizeof(hostname)) == -1) { + err = got_error_from_errno("gethostname"); + goto done; + } + + strlcpy(inotify.repo_name, gotd_session.repo_cfg->name, + sizeof(inotify.repo_name)); + + snprintf(inotify.subject_line, sizeof(inotify.subject_line), + "%s: %s: UID %d %s %s", hostname, gotd_session.repo_cfg->name, + client->euid, action, notif->refname); + + if (gotd_imsg_compose_event(iev, GOTD_IMSG_NOTIFY, + PROC_SESSION_WRITE, imsg->fd, &inotify, sizeof(inotify)) + == -1) { + err = got_error_from_errno("imsg compose NOTIFY"); + goto done; + } + } else + log_warn("received notification content for unknown event"); +done: + if (err == NULL && STAILQ_EMPTY(¬ifications) && + client->nref_updates == 0) + client->flush_disconnect = 1; + free(refname); + return err; } static const struct got_error * @@ -541,7 +685,7 @@ update_ref(int *shut, struct gotd_session_client *clie err = got_ref_write(ref, repo); /* will lock/unlock */ if (err) goto done; - err = send_notification(NULL, &new_id, repo, ref); + err = prepare_notification(NULL, &new_id, repo, ref); if (err) goto done; } else { @@ -583,7 +727,7 @@ update_ref(int *shut, struct gotd_session_client *clie err = got_ref_delete(ref, repo); if (err) goto done; - err = send_notification(&old_id, NULL, repo, ref); + err = prepare_notification(&old_id, NULL, repo, ref); if (err) goto done; free(id); @@ -617,7 +761,7 @@ update_ref(int *shut, struct gotd_session_client *clie err = got_ref_write(ref, repo); if (err) goto done; - err = send_notification(&old_id, &new_id, repo, ref); + err = prepare_notification(&old_id, &new_id, repo, ref); if (err) goto done; } @@ -640,7 +784,8 @@ done: client->nref_updates--; if (client->nref_updates == 0) { send_refs_updated(client); - client->flush_disconnect = 1; + if (STAILQ_EMPTY(¬ifications)) + client->flush_disconnect = 1; } } @@ -659,6 +804,23 @@ done: return err; } +static const struct got_error * +recv_notification_content(uint32_t *client_id, struct imsg *imsg) +{ + struct gotd_imsg_notification_content inotif; + size_t datalen; + + log_debug("notification content received"); + + datalen = imsg->hdr.len - IMSG_HEADER_SIZE; + if (datalen != sizeof(inotif)) + return got_error(GOT_ERR_PRIVSEP_LEN); + memcpy(&inotif, imsg->data, sizeof(inotif)); + + *client_id = inotif.client_id; + return NULL; +} + static void session_dispatch_repo_child(int fd, short event, void *arg) { @@ -695,7 +857,7 @@ session_dispatch_repo_child(int fd, short event, void uint32_t client_id = 0; int do_disconnect = 0; int do_ref_updates = 0, do_ref_update = 0; - int do_packfile_install = 0; + int do_packfile_install = 0, do_notify = 0; if ((n = imsg_get(ibuf, &imsg)) == -1) fatal("%s: imsg_get error", __func__); @@ -726,6 +888,10 @@ session_dispatch_repo_child(int fd, short event, void if (err == NULL) do_ref_update = 1; break; + case GOTD_IMSG_NOTIFY: + err = recv_notification_content(&client_id, &imsg); + if (err == NULL) + do_notify = 1; default: log_debug("unexpected imsg %d", imsg.hdr.type); break; @@ -745,6 +911,8 @@ session_dispatch_repo_child(int fd, short event, void else if (do_ref_update) err = update_ref(&shut, client, gotd_session.repo->path, &imsg); + else if (do_notify) + err = send_notification(client, &imsg); if (err) log_warnx("uid %d: %s", client->euid, err->msg); } @@ -1648,6 +1816,8 @@ session_main(const char *title, const char *repo_path, const struct got_error *err = NULL; struct event evsigint, evsigterm, evsighup, evsigusr1; + STAILQ_INIT(¬ifications); + gotd_session.title = title; gotd_session.pid = getpid(); gotd_session.pack_fds = pack_fds; @@ -1710,7 +1880,19 @@ done: void gotd_session_shutdown(void) { + struct gotd_session_notif *notif; + log_debug("shutting down"); + + while (!STAILQ_EMPTY(¬ifications)) { + notif = STAILQ_FIRST(¬ifications); + STAILQ_REMOVE_HEAD(¬ifications, entry); + if (notif->fd != -1) + close(notif->fd); + free(notif->refname); + free(notif); + } + if (gotd_session.repo) got_repo_close(gotd_session.repo); got_repo_pack_fds_close(gotd_session.pack_fds);