commit - 518fc9bf283a8410fd83a49addb3662f8b119498
commit + c2b405718eabe04c7ac3fa5916c1951227ab99fe
blob - 877e4d8fa52533fe457b51eefc3aed259e36e6a5
blob + bb6d62aaf04d48053d8c07f0eeab918d402677c8
--- gotd/Makefile
+++ gotd/Makefile
object_open_io.c object_parse.c opentemp.c pack.c path.c \
read_gitconfig.c read_gotconfig.c reference.c repository.c \
hash.c sigs.c pack_create_io.c pollfd.c reference_parse.c \
- repo_imsg.c pack_index.c session.c object_qid.c
+ repo_imsg.c pack_index.c session.c object_qid.c notify.c
CLEANFILES = parse.h
blob - b46b5573bc4b7b42b5b311fec8206262a1111da3
blob + b3a4fae0b273aa937461ce7fe139b1410c594ab5
--- gotd/gotd.c
+++ gotd/gotd.c
#include "session.h"
#include "repo_read.h"
#include "repo_write.h"
+#include "notify.h"
#ifndef nitems
#define nitems(_a) (sizeof((_a)) / sizeof((_a)[0]))
"session_write",
"repo_read",
"repo_write",
- "gitwrapper"
+ "gitwrapper",
+ "notify"
};
static void
case PROC_REPO_WRITE:
argv[argc++] = (char *)"-W";
break;
+ case PROC_NOTIFY:
+ argv[argc++] = (char *)"-N";
+ break;
default:
fatalx("invalid process id %d", proc_id);
}
rl.rlim_cur = rl.rlim_max;
setrlimit(RLIMIT_DATA, &rl);
}
+
+static void
+unveil_notification_helpers(void)
+{
+ const char *helpers[] = {
+ GOTD_PATH_PROG_NOTIFY_EMAIL,
+ GOTD_PATH_PROG_NOTIFY_HTTP,
+ };
+ size_t i;
+ for (i = 0; i < nitems(helpers); i++) {
+ if (unveil(helpers[i], "x") == 0)
+ continue;
+ fatal("unveil %s", helpers[i]);
+ }
+
+ if (unveil(NULL, NULL) == -1)
+ fatal("unveil");
+}
+
int
main(int argc, char **argv)
{
case 'n':
noaction = 1;
break;
+ case 'N':
+ proc_id = PROC_NOTIFY;
+ break;
case 'P':
repo_path = realpath(optarg, NULL);
if (repo_path == NULL)
fatalx("repository path not specified");
snprintf(title, sizeof(title), "%s %s",
gotd_proc_names[proc_id], repo_path);
+ } else if (proc_id == PROC_NOTIFY) {
+ snprintf(title, sizeof(title), "%s %s",
+ gotd_proc_names[proc_id], repo_path);
} else
fatal("invalid process id %d", proc_id);
&repo->protected_tag_namespaces,
&repo->protected_branch_namespaces,
&repo->protected_branches);
+ /* NOTREACHED */
+ exit(0);
+ case PROC_NOTIFY:
+#ifndef PROFILE
+ if (pledge("stdio proc exec unveil", NULL) == -1)
+ err(1, "pledge");
+#endif
+ /*
+ * Limit "exec" promise to notification helpers via unveil(2).
+ */
+ unveil_notification_helpers();
+
+ notify_main(title);
/* NOTREACHED */
exit(0);
default:
blob - acb40dee8cd351b48669c3c3247c42ed5f44501b
blob + bd50b7baeef6f944713df1b7a9cd66e274d6b537
--- gotd/gotd.h
+++ gotd/gotd.h
#define GOTD_CONF_PATH "/etc/gotd.conf"
#define GOTD_EMPTY_PATH "/var/empty"
+#ifndef GOT_LIBEXECDIR
+#define GOT_LIBEXECDIR /usr/libexec
+#endif
+
+#define GOTD_STRINGIFY(x) #x
+#define GOTD_STRINGVAL(x) GOTD_STRINGIFY(x)
+
+#define GOTD_PROG_NOTIFY_EMAIL got-notify-email
+#define GOTD_PROG_NOTIFY_HTTP got-notify-http
+
+#define GOTD_PATH_PROG_NOTIFY_EMAIL \
+ GOTD_STRINGVAL(GOT_LIBEXECDIR) "/" \
+ GOTD_STRINGVAL(GOTD_PROG_NOTIFY_EMAIL)
+#define GOTD_PATH_PROG_NOTIFY_HTTP \
+ GOTD_STRINGVAL(GOT_LIBEXECDIR) "/" \
+ GOTD_STRINGVAL(GOTD_PROG_NOTIFY_HTTP)
+
#define GOTD_MAXCLIENTS 1024
#define GOTD_MAX_CONN_PER_UID 4
#define GOTD_FD_RESERVE 5
PROC_REPO_READ,
PROC_REPO_WRITE,
PROC_GITWRAPPER,
+ PROC_NOTIFY,
PROC_MAX,
};
/* Auth child process. */
GOTD_IMSG_AUTHENTICATE,
GOTD_IMSG_ACCESS_GRANTED,
+
+ /* Notify child process. */
+ GOTD_IMSG_NOTIFY_READY,
};
/* Structure for GOTD_IMSG_ERROR. */
blob - /dev/null
blob + 929e63947c237f8baaeb6b5c9a27980e7054685c (mode 644)
--- /dev/null
+++ gotd/notify.c
+/*
+ * Copyright (c) Stefan Sperling <stsp@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/tree.h>
+#include <sys/socket.h>
+
+#include <errno.h>
+#include <event.h>
+#include <limits.h>
+#include <sha1.h>
+#include <sha2.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <imsg.h>
+#include <unistd.h>
+
+#include "got_error.h"
+#include "got_path.h"
+
+#include "gotd.h"
+#include "log.h"
+#include "notify.h"
+
+static struct gotd_notify {
+ pid_t pid;
+ const char *title;
+ struct gotd_imsgev parent_iev;
+} gotd_notify;
+
+void gotd_notify_sighdlr(int sig, short event, void *arg);
+static void gotd_notify_shutdown(void);
+
+void
+gotd_notify_sighdlr(int sig, short event, void *arg)
+{
+ /*
+ * Normal signal handler rules don't apply because libevent
+ * decouples for us.
+ */
+
+ switch (sig) {
+ case SIGHUP:
+ log_info("%s: ignoring SIGHUP", __func__);
+ break;
+ case SIGUSR1:
+ log_info("%s: ignoring SIGUSR1", __func__);
+ break;
+ case SIGTERM:
+ case SIGINT:
+ gotd_notify_shutdown();
+ /* NOTREACHED */
+ break;
+ default:
+ fatalx("unexpected signal");
+ }
+}
+
+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) {
+ 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 {
+ /* This pipe is dead. Remove its event handler */
+ event_del(&iev->ev);
+ event_loopexit(NULL);
+ }
+
+}
+
+void
+notify_main(const char *title)
+{
+ const struct got_error *err = NULL;
+ struct event evsigint, evsigterm, evsighup, evsigusr1;
+
+ gotd_notify.title = title;
+ gotd_notify.pid = getpid();
+
+ signal_set(&evsigint, SIGINT, gotd_notify_sighdlr, NULL);
+ signal_set(&evsigterm, SIGTERM, gotd_notify_sighdlr, NULL);
+ signal_set(&evsighup, SIGHUP, gotd_notify_sighdlr, NULL);
+ signal_set(&evsigusr1, SIGUSR1, gotd_notify_sighdlr, NULL);
+ signal(SIGPIPE, SIG_IGN);
+
+ signal_add(&evsigint, NULL);
+ signal_add(&evsigterm, NULL);
+ signal_add(&evsighup, NULL);
+ signal_add(&evsigusr1, NULL);
+
+ imsg_init(&gotd_notify.parent_iev.ibuf, GOTD_FILENO_MSG_PIPE);
+ gotd_notify.parent_iev.handler = notify_dispatch;
+ gotd_notify.parent_iev.events = EV_READ;
+ 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();
+}
+
+void
+gotd_notify_shutdown(void)
+{
+ log_debug("shutting down");
+ exit(0);
+}
blob - /dev/null
blob + 5ba452e52c2a67baf8c8220364b01201f02427a0 (mode 644)
--- /dev/null
+++ gotd/notify.h
+/*
+ * Copyright (c) 2023 Stefan Sperling <stsp@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+void notify_main(const char *);