commit - 202d187071957eec318c523e66455e0f804ff869
commit + 4ddc9d7454d1838afd08981593a8b32332f3725f
blob - 52cfe7948d93903861aec5c5f4f85960643c6444
blob + 377311c81a12015a42f5c01d3783018e7e8175b3
--- gotd/gotd.h
+++ gotd/gotd.h
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
#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;
}
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;
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;
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 *
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 {
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);
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;
}
client->nref_updates--;
if (client->nref_updates == 0) {
send_refs_updated(client);
- client->flush_disconnect = 1;
+ if (STAILQ_EMPTY(¬ifications))
+ client->flush_disconnect = 1;
}
}
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)
{
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__);
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;
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);
}
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;
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);