commit - b546f5e4a2345f89a875526edc57cf729564decf
commit + d93ecf7dc6dd75bc506128cc5666c1b76761e0a0
blob - c10b6a5df87a4c719d3bb926aff2316341052ac3
blob + 150c4e0f6fded4edfa8704bb486d56939d38c114
--- gotd/Makefile
+++ gotd/Makefile
PROG= gotd
SRCS= gotd.c auth.c repo_read.c repo_write.c log.c privsep_stub.c \
- imsg.c parse.y pack_create.c ratelimit.c deltify.c \
+ listen.c imsg.c parse.y pack_create.c ratelimit.c deltify.c \
bloom.c buf.c date.c deflate.c delta.c delta_cache.c error.c \
gitconfig.c gotconfig.c inflate.c lockfile.c murmurhash2.c \
object.c object_cache.c object_create.c object_idset.c \
blob - e18e72315e0f9d45caece8de0bbff6a0b31fadf9
blob + 3c62987cb615e5a1580f36670da87e9eecf8ea1c
--- gotd/gotd.c
+++ gotd/gotd.c
#include "gotd.h"
#include "log.h"
+#include "listen.h"
#include "auth.h"
#include "repo_read.h"
#include "repo_write.h"
static SIPHASH_KEY clients_hash_key;
volatile int client_cnt;
static struct timeval timeout = { 3600, 0 };
-static int inflight;
static struct gotd gotd;
void gotd_sighdlr(int sig, short event, void *arg);
return NULL;
}
-static int
-accept_reserve(int fd, struct sockaddr *addr, socklen_t *addrlen,
- int reserve, volatile int *counter)
-{
- int ret;
-
- if (getdtablecount() + reserve +
- ((*counter + 1) * GOTD_FD_NEEDED) >= getdtablesize()) {
- log_debug("inflight fds exceeded");
- errno = EMFILE;
- return -1;
- }
-
- if ((ret = accept4(fd, addr, addrlen,
- SOCK_NONBLOCK | SOCK_CLOEXEC)) > -1) {
- (*counter)++;
- }
-
- return ret;
-}
-
static uint64_t
client_hash(uint32_t client_id)
{
}
return NULL;
-}
-
-static uint32_t
-get_client_id(void)
-{
- int duplicate = 0;
- uint32_t id;
-
- do {
- id = arc4random();
- duplicate = (find_client(id) != NULL);
- } while (duplicate || id == 0);
-
- return id;
}
static struct gotd_child_proc *
{
struct gotd_imsg_disconnect idisconnect;
struct gotd_child_proc *proc = get_client_proc(client);
+ struct gotd_child_proc *listen_proc = &gotd.procs[0];
uint64_t slot;
log_debug("uid %d: disconnecting", client->euid);
&idisconnect, sizeof(idisconnect)) == -1)
log_warn("imsg compose DISCONNECT");
}
+
+ if (gotd_imsg_compose_event(&listen_proc->iev,
+ GOTD_IMSG_DISCONNECT, PROC_GOTD, -1,
+ &idisconnect, sizeof(idisconnect)) == -1)
+ log_warn("imsg compose DISCONNECT");
+
slot = client_hash(client->id) % nitems(gotd_clients);
STAILQ_REMOVE(&gotd_clients[slot], client, gotd_client, entry);
imsg_clear(&client->iev.ibuf);
}
free(client->capabilities);
free(client);
- inflight--;
client_cnt--;
}
disconnect(client);
}
-static void
-gotd_accept(int fd, short event, void *arg)
+static const struct got_error *
+recv_connect(uint32_t *client_id, struct imsg *imsg)
{
- struct sockaddr_storage ss;
- struct timeval backoff;
- socklen_t len;
+ const struct got_error *err = NULL;
+ struct gotd_imsg_connect iconnect;
+ size_t datalen;
int s = -1;
struct gotd_client *client = NULL;
uid_t euid;
gid_t egid;
- backoff.tv_sec = 1;
- backoff.tv_usec = 0;
+ *client_id = 0;
- if (event_add(&gotd.ev, NULL) == -1) {
- log_warn("event_add");
- return;
- }
- if (event & EV_TIMEOUT)
- return;
-
- len = sizeof(ss);
-
- s = accept_reserve(fd, (struct sockaddr *)&ss, &len, GOTD_FD_RESERVE,
- &inflight);
+ datalen = imsg->hdr.len - IMSG_HEADER_SIZE;
+ if (datalen != sizeof(iconnect))
+ return got_error(GOT_ERR_PRIVSEP_LEN);
+ memcpy(&iconnect, imsg->data, sizeof(iconnect));
+ s = imsg->fd;
if (s == -1) {
- switch (errno) {
- case EINTR:
- case EWOULDBLOCK:
- case ECONNABORTED:
- return;
- case EMFILE:
- case ENFILE:
- event_del(&gotd.ev);
- evtimer_add(&gotd.pause, &backoff);
- return;
- default:
- log_warn("%s: accept", __func__);
- return;
- }
+ err = got_error(GOT_ERR_PRIVSEP_NO_FD);
+ goto done;
}
- if (client_cnt >= GOTD_MAXCLIENTS)
- goto err;
+ if (find_client(iconnect.client_id)) {
+ err = got_error_msg(GOT_ERR_CLIENT_ID, "duplicate client ID");
+ goto done;
+ }
if (getpeereid(s, &euid, &egid) == -1) {
- log_warn("%s: getpeereid", __func__);
- goto err;
+ err = got_error_from_errno("getpeerid");
+ goto done;
}
client = calloc(1, sizeof(*client));
if (client == NULL) {
- log_warn("%s: calloc", __func__);
- goto err;
+ err = got_error_from_errno("calloc");
+ goto done;
}
+ *client_id = iconnect.client_id;
+
client->state = GOTD_STATE_EXPECT_LIST_REFS;
- client->id = get_client_id();
+ client->id = iconnect.client_id;
client->fd = s;
s = -1;
client->delta_cache_fd = -1;
add_client(client);
log_debug("%s: new client uid %d connected on fd %d", __func__,
client->euid, client->fd);
- return;
-err:
- inflight--;
- if (s != -1)
- close(s);
- free(client);
-}
+done:
+ if (err) {
+ struct gotd_child_proc *listen_proc = &gotd.procs[0];
+ struct gotd_imsg_disconnect idisconnect;
-static void
-gotd_accept_paused(int fd, short event, void *arg)
-{
- event_add(&gotd.ev, NULL);
+ idisconnect.client_id = client->id;
+ if (gotd_imsg_compose_event(&listen_proc->iev,
+ GOTD_IMSG_DISCONNECT, PROC_GOTD, -1,
+ &idisconnect, sizeof(idisconnect)) == -1)
+ log_warn("imsg compose DISCONNECT");
+
+ if (s != -1)
+ close(s);
+ }
+
+ return err;
}
static const char *gotd_proc_names[PROC_MAX] = {
"parent",
+ "listen",
"repo_read",
"repo_write"
};
struct gotd_child_proc *client_proc;
int ret = 0;
- client_proc = get_client_proc(client);
- if (client_proc == NULL)
- fatalx("no process found for uid %d", client->euid);
-
- if (proc->pid != client_proc->pid) {
- kill_proc(proc, 1);
- log_warnx("received message from PID %d for uid %d, while "
- "PID %d is the process serving this user",
- proc->pid, client->euid, client_proc->pid);
- return 0;
+ if (proc->type == PROC_REPO_READ || proc->type == PROC_REPO_WRITE) {
+ client_proc = get_client_proc(client);
+ if (client_proc == NULL)
+ fatalx("no process found for uid %d", client->euid);
+ if (proc->pid != client_proc->pid) {
+ kill_proc(proc, 1);
+ log_warnx("received message from PID %d for uid %d, "
+ "while PID %d is the process serving this user",
+ proc->pid, client->euid, client_proc->pid);
+ return 0;
+ }
}
switch (imsg->hdr.type) {
case GOTD_IMSG_ERROR:
ret = 1;
break;
+ case GOTD_IMSG_CONNECT:
+ if (proc->type != PROC_LISTEN) {
+ err = got_error_fmt(GOT_ERR_BAD_PACKET,
+ "new connection for uid %d from PID %d "
+ "which is not the listen process",
+ proc->pid, client->euid);
+ } else
+ ret = 1;
+ break;
case GOTD_IMSG_PACKFILE_DONE:
err = ensure_proc_is_reading(client, proc);
if (err)
do_disconnect = 1;
err = gotd_imsg_recv_error(&client_id, &imsg);
break;
+ case GOTD_IMSG_CONNECT:
+ err = recv_connect(&client_id, &imsg);
+ break;
case GOTD_IMSG_PACKFILE_DONE:
do_disconnect = 1;
err = recv_packfile_done(&client_id, &imsg);
argv[argc++] = argv0;
switch (proc_id) {
+ case PROC_LISTEN:
+ argv[argc++] = (char *)"-L";
+ break;
case PROC_REPO_READ:
argv[argc++] = (char *)"-R";
break;
argv[argc++] = (char *)"-f";
argv[argc++] = (char *)confpath;
- argv[argc++] = (char *)"-P";
- argv[argc++] = (char *)chroot_path;
+ if (chroot_path) {
+ argv[argc++] = (char *)"-P";
+ argv[argc++] = (char *)chroot_path;
+ }
if (!daemonize)
argv[argc++] = (char *)"-d";
}
static void
+start_listener(char *argv0, const char *confpath, int daemonize, int verbosity)
+{
+ struct gotd_child_proc *proc = &gotd.procs[0];
+
+ proc->type = PROC_LISTEN;
+
+ 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;
+ proc->iev.events = EV_READ;
+ proc->iev.handler_arg = NULL;
+}
+
+static void
start_repo_children(struct gotd *gotd, char *argv0, const char *confpath,
int daemonize, int verbosity)
{
struct gotd_child_proc *proc;
int i;
- /*
- * XXX For now, use one reader and one writer per repository.
- * This should be changed to N readers + M writers.
- */
- gotd->nprocs = gotd->nrepos * 2;
- gotd->procs = calloc(gotd->nprocs, sizeof(*gotd->procs));
- if (gotd->procs == NULL)
- fatal("calloc");
- for (i = 0; i < gotd->nprocs; i++) {
+ for (i = 1; i < gotd->nprocs; i++) {
if (repo == NULL)
repo = TAILQ_FIRST(&gotd->repos);
proc = &gotd->procs[i];
- if (i < gotd->nrepos)
+ if (i - 1 < gotd->nrepos)
proc->type = PROC_REPO_READ;
else
proc->type = PROC_REPO_WRITE;
log_init(1, LOG_DAEMON); /* Log to stderr until daemonized. */
- while ((ch = getopt(argc, argv, "df:nP:RvW")) != -1) {
+ while ((ch = getopt(argc, argv, "df:LnP:RvW")) != -1) {
switch (ch) {
case 'd':
daemonize = 0;
case 'f':
confpath = optarg;
break;
+ case 'L':
+ proc_id = PROC_LISTEN;
+ break;
case 'n':
noaction = 1;
break;
gotd.unix_group_name);
}
- if (proc_id == PROC_GOTD &&
+ if (proc_id == PROC_LISTEN &&
!got_path_is_absolute(gotd.unix_socket_path))
fatalx("bad unix socket path \"%s\": must be an absolute path",
gotd.unix_socket_path);
return 0;
if (proc_id == PROC_GOTD) {
+ gotd.pid = getpid();
+ snprintf(title, sizeof(title), "%s", gotd_proc_names[proc_id]);
+ /*
+ * Start a listener and repository readers/writers.
+ * XXX For now, use one reader and one writer per repository.
+ * This should be changed to N readers + M writers.
+ */
+ gotd.nprocs = 1 + gotd.nrepos * 2;
+ gotd.procs = calloc(gotd.nprocs, sizeof(*gotd.procs));
+ if (gotd.procs == NULL)
+ fatal("calloc");
+ start_listener(argv0, confpath, daemonize, verbosity);
+ start_repo_children(&gotd, argv0, confpath, daemonize,
+ verbosity);
+ arc4random_buf(&clients_hash_key, sizeof(clients_hash_key));
+ if (daemonize && daemon(1, 0) == -1)
+ fatal("daemon");
+ } else if (proc_id == PROC_LISTEN) {
+ snprintf(title, sizeof(title), "%s", gotd_proc_names[proc_id]);
if (verbosity) {
log_info("socket: %s", gotd.unix_socket_path);
log_info("user: %s", pw->pw_name);
fatal("cannot listen on unix socket %s",
gotd.unix_socket_path);
}
- }
-
- if (proc_id == PROC_GOTD) {
- gotd.pid = getpid();
- snprintf(title, sizeof(title), "%s", gotd_proc_names[proc_id]);
- start_repo_children(&gotd, argv0, confpath, daemonize,
- verbosity);
- arc4random_buf(&clients_hash_key, sizeof(clients_hash_key));
- if (daemonize && daemon(0, 0) == -1)
+ if (chroot(GOTD_EMPTY_PATH) == -1)
+ fatal("chroot");
+ if (chdir("/") == -1)
+ fatal("chdir(\"/\")");
+ if (daemonize && daemon(1, 0) == -1)
fatal("daemon");
} else if (proc_id == PROC_REPO_READ || proc_id == PROC_REPO_WRITE) {
error = got_repo_pack_fds_open(&pack_fds);
err(1, "pledge");
#endif
break;
+ case PROC_LISTEN:
+#ifndef PROFILE
+ if (pledge("stdio sendfd unix", NULL) == -1)
+ err(1, "pledge");
+#endif
+ listen_main(title, fd);
+ /* NOTREACHED */
+ break;
case PROC_REPO_READ:
#ifndef PROFILE
if (pledge("stdio rpath recvfd", NULL) == -1)
signal_add(&evsighup, NULL);
signal_add(&evsigusr1, NULL);
- event_set(&gotd.ev, fd, EV_READ | EV_PERSIST, gotd_accept, NULL);
- if (event_add(&gotd.ev, NULL))
- fatalx("event add");
- evtimer_set(&gotd.pause, gotd_accept_paused, NULL);
+ gotd_imsg_event_add(&gotd.procs[0].iev);
event_dispatch();
- if (fd != -1)
- close(fd);
if (pack_fds)
got_repo_pack_fds_close(pack_fds);
free(repo_path);
blob - 8773697218bbd2c0f2f0b4320d20311c0ff48300
blob + 9ec3939ca07f41efabccdf15c8ed41e0a606fe86
--- gotd/gotd.h
+++ gotd/gotd.h
#define GOTD_UNIX_GROUP "_gotsh"
#define GOTD_USER "_gotd"
#define GOTD_CONF_PATH "/etc/gotd.conf"
+#define GOTD_EMPTY_PATH "/var/empty"
#define GOTD_MAXCLIENTS 1024
#define GOTD_FD_RESERVE 5
enum gotd_procid {
PROC_GOTD = 0,
+ PROC_LISTEN,
PROC_REPO_READ,
PROC_REPO_WRITE,
PROC_MAX,
struct gotd_repolist repos;
int nrepos;
int verbosity;
- struct event ev;
- struct event pause;
struct gotd_child_proc *procs;
int nprocs;
};
GOTD_IMSG_REF_UPDATE_NG, /* Update was not good. */
GOTD_IMSG_REFS_UPDATED, /* The server proccessed all ref updates. */
- /* Client is disconnecting. */
+ /* Client connections. */
GOTD_IMSG_DISCONNECT,
+ GOTD_IMSG_CONNECT,
};
/* Structure for GOTD_IMSG_ERROR. */
uint32_t client_id;
};
+/* Structure for GOTD_IMSG_CONNECT. */
+struct gotd_imsg_connect {
+ uint32_t client_id;
+};
+
int parse_config(const char *, enum gotd_procid, struct gotd *);
/* imsg.c */
blob - /dev/null
blob + 96427e857a4b693de2dec2665df312e682a43f73 (mode 644)
--- /dev/null
+++ gotd/listen.c
+/*
+ * Copyright (c) 2022 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/socket.h>
+#include <sys/uio.h>
+
+#include <errno.h>
+#include <event.h>
+#include <siphash.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <imsg.h>
+#include <limits.h>
+#include <sha1.h>
+#include <signal.h>
+#include <unistd.h>
+
+#include "got_error.h"
+
+#include "gotd.h"
+#include "log.h"
+#include "listen.h"
+
+#ifndef nitems
+#define nitems(_a) (sizeof((_a)) / sizeof((_a)[0]))
+#endif
+
+struct gotd_listen_client {
+ STAILQ_ENTRY(gotd_listen_client) entry;
+ uint32_t id;
+ int fd;
+};
+STAILQ_HEAD(gotd_listen_clients, gotd_listen_client);
+
+static struct gotd_listen_clients gotd_listen_clients[GOTD_CLIENT_TABLE_SIZE];
+static SIPHASH_KEY clients_hash_key;
+static volatile int listen_client_cnt;
+static int inflight;
+
+static struct {
+ pid_t pid;
+ const char *title;
+ int fd;
+ struct gotd_imsgev iev;
+ struct gotd_imsgev pause;
+} gotd_listen;
+
+static int inflight;
+
+static void listen_shutdown(void);
+
+static void
+listen_sighdlr(int sig, short event, void *arg)
+{
+ /*
+ * Normal signal handler rules don't apply because libevent
+ * decouples for us.
+ */
+
+ switch (sig) {
+ case SIGHUP:
+ break;
+ case SIGUSR1:
+ break;
+ case SIGTERM:
+ case SIGINT:
+ listen_shutdown();
+ /* NOTREACHED */
+ break;
+ default:
+ fatalx("unexpected signal");
+ }
+}
+
+static uint64_t
+client_hash(uint32_t client_id)
+{
+ return SipHash24(&clients_hash_key, &client_id, sizeof(client_id));
+}
+
+static void
+add_client(struct gotd_listen_client *client)
+{
+ uint64_t slot = client_hash(client->id) % nitems(gotd_listen_clients);
+ STAILQ_INSERT_HEAD(&gotd_listen_clients[slot], client, entry);
+ listen_client_cnt++;
+}
+
+static struct gotd_listen_client *
+find_client(uint32_t client_id)
+{
+ uint64_t slot;
+ struct gotd_listen_client *c;
+
+ slot = client_hash(client_id) % nitems(gotd_listen_clients);
+ STAILQ_FOREACH(c, &gotd_listen_clients[slot], entry) {
+ if (c->id == client_id)
+ return c;
+ }
+
+ return NULL;
+}
+
+static uint32_t
+get_client_id(void)
+{
+ int duplicate = 0;
+ uint32_t id;
+
+ do {
+ id = arc4random();
+ duplicate = (find_client(id) != NULL);
+ } while (duplicate || id == 0);
+
+ return id;
+}
+
+static const struct got_error *
+disconnect(struct gotd_listen_client *client)
+{
+ uint64_t slot;
+ int client_fd;
+
+ log_debug("client on fd %d disconnecting", client->fd);
+
+ slot = client_hash(client->id) % nitems(gotd_listen_clients);
+ STAILQ_REMOVE(&gotd_listen_clients[slot], client,
+ gotd_listen_client, entry);
+ client_fd = client->fd;
+ free(client);
+ inflight--;
+ listen_client_cnt--;
+ if (close(client_fd) == -1)
+ return got_error_from_errno("close");
+
+ return NULL;
+}
+
+static int
+accept_reserve(int fd, struct sockaddr *addr, socklen_t *addrlen,
+ int reserve, volatile int *counter)
+{
+ int ret;
+
+ if (getdtablecount() + reserve +
+ ((*counter + 1) * GOTD_FD_NEEDED) >= getdtablesize()) {
+ log_debug("inflight fds exceeded");
+ errno = EMFILE;
+ return -1;
+ }
+
+ if ((ret = accept4(fd, addr, addrlen,
+ SOCK_NONBLOCK | SOCK_CLOEXEC)) > -1) {
+ (*counter)++;
+ }
+
+ return ret;
+}
+
+static void
+gotd_accept_paused(int fd, short event, void *arg)
+{
+ event_add(&gotd_listen.iev.ev, NULL);
+}
+
+static void
+gotd_accept(int fd, short event, void *arg)
+{
+ struct gotd_imsgev *iev = arg;
+ struct sockaddr_storage ss;
+ struct timeval backoff;
+ socklen_t len;
+ int s = -1;
+ struct gotd_listen_client *client = NULL;
+ struct gotd_imsg_connect iconn;
+
+ backoff.tv_sec = 1;
+ backoff.tv_usec = 0;
+
+ if (event_add(&gotd_listen.iev.ev, NULL) == -1) {
+ log_warn("event_add");
+ return;
+ }
+ if (event & EV_TIMEOUT)
+ return;
+
+ len = sizeof(ss);
+
+ /* Other backoff conditions apart from EMFILE/ENFILE? */
+ s = accept_reserve(fd, (struct sockaddr *)&ss, &len, GOTD_FD_RESERVE,
+ &inflight);
+ if (s == -1) {
+ switch (errno) {
+ case EINTR:
+ case EWOULDBLOCK:
+ case ECONNABORTED:
+ return;
+ case EMFILE:
+ case ENFILE:
+ event_del(&gotd_listen.iev.ev);
+ evtimer_add(&gotd_listen.pause.ev, &backoff);
+ return;
+ default:
+ log_warn("accept");
+ return;
+ }
+ }
+
+ if (listen_client_cnt >= GOTD_MAXCLIENTS)
+ goto err;
+
+ client = calloc(1, sizeof(*client));
+ if (client == NULL) {
+ log_warn("%s: calloc", __func__);
+ goto err;
+ }
+ client->id = get_client_id();
+ client->fd = s;
+ s = -1;
+ add_client(client);
+ log_debug("%s: new client connected on fd %d", __func__, client->fd);
+
+ memset(&iconn, 0, sizeof(iconn));
+ iconn.client_id = client->id;
+ s = dup(client->fd);
+ if (s == -1) {
+ log_warn("%s: dup", __func__);
+ goto err;
+ }
+ if (gotd_imsg_compose_event(iev, GOTD_IMSG_CONNECT, PROC_LISTEN, s,
+ &iconn, sizeof(iconn)) == -1) {
+ log_warn("imsg compose CONNECT");
+ goto err;
+ }
+
+ return;
+err:
+ inflight--;
+ if (client)
+ disconnect(client);
+ if (s != -1)
+ close(s);
+}
+
+static const struct got_error *
+recv_disconnect(struct imsg *imsg)
+{
+ struct gotd_imsg_disconnect idisconnect;
+ size_t datalen;
+ struct gotd_listen_client *client = NULL;
+
+ datalen = imsg->hdr.len - IMSG_HEADER_SIZE;
+ if (datalen != sizeof(idisconnect))
+ return got_error(GOT_ERR_PRIVSEP_LEN);
+ memcpy(&idisconnect, imsg->data, sizeof(idisconnect));
+
+ log_debug("client disconnecting");
+
+ client = find_client(idisconnect.client_id);
+ if (client == NULL)
+ return got_error(GOT_ERR_CLIENT_ID);
+
+ return disconnect(client);
+}
+
+static void
+listen_dispatch(int fd, short event, void *arg)
+{
+ const struct got_error *err = NULL;
+ struct gotd_imsgev *iev = arg;
+ struct imsgbuf *ibuf = &iev->ibuf;
+ struct imsg imsg;
+ ssize_t n;
+ int shut = 0;
+
+ if (event & EV_READ) {
+ if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN)
+ fatal("imsg_read error");
+ if (n == 0) /* Connection closed. */
+ shut = 1;
+ }
+
+ if (event & EV_WRITE) {
+ n = msgbuf_write(&ibuf->w);
+ if (n == -1 && errno != EAGAIN)
+ fatal("msgbuf_write");
+ if (n == 0) /* Connection closed. */
+ shut = 1;
+ }
+
+ for (;;) {
+ if ((n = imsg_get(ibuf, &imsg)) == -1)
+ fatal("%s: imsg_get", __func__);
+ if (n == 0) /* No more messages. */
+ break;
+
+ switch (imsg.hdr.type) {
+ case GOTD_IMSG_DISCONNECT:
+ err = recv_disconnect(&imsg);
+ if (err)
+ log_warnx("%s: disconnect: %s",
+ gotd_listen.title, err->msg);
+ break;
+ default:
+ log_debug("%s: unexpected imsg %d", gotd_listen.title,
+ imsg.hdr.type);
+ break;
+ }
+
+ imsg_free(&imsg);
+ }
+
+ if (!shut) {
+ gotd_imsg_event_add(iev);
+ } else {
+ /* This pipe is dead. Remove its event handler */
+ event_del(&iev->ev);
+ event_loopexit(NULL);
+ }
+}
+
+void
+listen_main(const char *title, int gotd_socket)
+{
+ struct gotd_imsgev iev;
+ struct event evsigint, evsigterm, evsighup, evsigusr1;
+
+ gotd_listen.title = title;
+ gotd_listen.pid = getpid();
+ gotd_listen.fd = gotd_socket;
+
+ signal_set(&evsigint, SIGINT, listen_sighdlr, NULL);
+ signal_set(&evsigterm, SIGTERM, listen_sighdlr, NULL);
+ signal_set(&evsighup, SIGHUP, listen_sighdlr, NULL);
+ signal_set(&evsigusr1, SIGUSR1, listen_sighdlr, NULL);
+ signal(SIGPIPE, SIG_IGN);
+
+ signal_add(&evsigint, NULL);
+ signal_add(&evsigterm, NULL);
+ signal_add(&evsighup, NULL);
+ signal_add(&evsigusr1, NULL);
+
+ imsg_init(&iev.ibuf, GOTD_FILENO_MSG_PIPE);
+ iev.handler = listen_dispatch;
+ iev.events = EV_READ;
+ iev.handler_arg = NULL;
+ event_set(&iev.ev, iev.ibuf.fd, EV_READ, listen_dispatch, &iev);
+ if (event_add(&iev.ev, NULL) == -1)
+ fatalx("event add");
+
+ event_set(&gotd_listen.iev.ev, gotd_listen.fd, EV_READ | EV_PERSIST,
+ gotd_accept, &iev);
+ if (event_add(&gotd_listen.iev.ev, NULL))
+ fatalx("event add");
+ evtimer_set(&gotd_listen.pause.ev, gotd_accept_paused, NULL);
+
+ event_dispatch();
+
+ listen_shutdown();
+}
+
+static void
+listen_shutdown(void)
+{
+ log_debug("%s: shutting down", gotd_listen.title);
+
+ if (gotd_listen.fd != -1)
+ close(gotd_listen.fd);
+
+ exit(0);
+}
blob - /dev/null
blob + 5ecdb0084e77470530899068dc16cb0f553ead7b (mode 644)
--- /dev/null
+++ gotd/listen.h
+/*
+ * Copyright (c) 2022 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 listen_main(const char *title, int gotd_socket);
blob - 4fd5767663f9fecdb247c6ed37f3865d7bb9a480
blob + f3ea139f65f7d8cf2b35c0d63a1ba9fc62aef542
--- gotd/parse.y
+++ gotd/parse.y
;
main : UNIX_SOCKET STRING {
- if (gotd_proc_id == PROC_GOTD) {
+ if (gotd_proc_id == PROC_LISTEN) {
if (strlcpy(gotd->unix_socket_path, $2,
sizeof(gotd->unix_socket_path)) >=
sizeof(gotd->unix_socket_path)) {