From: Stefan Sperling Subject: gotd listen process To: gameoftrees@openbsd.org Date: Fri, 9 Dec 2022 11:26:14 +0100 Add a gotd "listen" process which watches the unix socket and passes new client connections to the parent. This is a first step towards better privsep. By itself this doesn't buy us much. A lot more code remains to be written in order to untangle things properly. ok? diff 30eb1ad6b8cdffd307215e60396bdcd58e7ccf17 ecdc4b4ea93107606601c584c90cb821d32d76a3 commit - 30eb1ad6b8cdffd307215e60396bdcd58e7ccf17 commit + ecdc4b4ea93107606601c584c90cb821d32d76a3 blob - c10b6a5df87a4c719d3bb926aff2316341052ac3 blob + 150c4e0f6fded4edfa8704bb486d56939d38c114 --- gotd/Makefile +++ gotd/Makefile @@ -8,7 +8,7 @@ SRCS= gotd.c auth.c repo_read.c repo_write.c log.c pr 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 @@ -58,6 +58,7 @@ #include "gotd.h" #include "log.h" +#include "listen.h" #include "auth.h" #include "repo_read.h" #include "repo_write.h" @@ -91,7 +92,6 @@ static int inflight; 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); @@ -188,27 +188,6 @@ static int 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) { @@ -238,20 +217,6 @@ static uint32_t 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 * get_client_proc(struct gotd_client *client) { @@ -334,6 +299,7 @@ disconnect(struct gotd_client *client) { 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); @@ -345,6 +311,12 @@ disconnect(struct gotd_client *client) &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); @@ -365,7 +337,6 @@ disconnect(struct gotd_client *client) } free(client->capabilities); free(client); - inflight--; client_cnt--; } @@ -1231,65 +1202,50 @@ static void 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; + datalen = imsg->hdr.len - IMSG_HEADER_SIZE; + if (datalen != sizeof(iconnect)) + return got_error(GOT_ERR_PRIVSEP_LEN); + memcpy(&iconnect, imsg->data, sizeof(iconnect)); - len = sizeof(ss); - - s = accept_reserve(fd, (struct sockaddr *)&ss, &len, GOTD_FD_RESERVE, - &inflight); - + 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; @@ -1311,22 +1267,27 @@ gotd_accept(int fd, short event, void *arg) 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" }; @@ -1452,22 +1413,32 @@ verify_imsg_src(struct gotd_client *client, struct got 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) @@ -1897,6 +1868,9 @@ gotd_dispatch(int fd, short event, void *arg) 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); @@ -1992,6 +1966,9 @@ start_child(enum gotd_procid proc_id, const char *chro argv[argc++] = argv0; switch (proc_id) { + case PROC_LISTEN: + argv[argc++] = (char *)"-L"; + break; case PROC_REPO_READ: argv[argc++] = (char *)"-R"; break; @@ -2005,8 +1982,10 @@ start_child(enum gotd_procid proc_id, const char *chro 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"; @@ -2021,6 +2000,25 @@ start_repo_children(struct gotd *gotd, char *argv0, co } 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) { @@ -2028,19 +2026,11 @@ start_repo_children(struct gotd *gotd, char *argv0, co 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; @@ -2105,7 +2095,7 @@ main(int argc, char **argv) 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; @@ -2113,6 +2103,9 @@ main(int argc, char **argv) case 'f': confpath = optarg; break; + case 'L': + proc_id = PROC_LISTEN; + break; case 'n': noaction = 1; break; @@ -2182,7 +2175,7 @@ main(int argc, char **argv) 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); @@ -2191,6 +2184,25 @@ main(int argc, char **argv) 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); @@ -2203,15 +2215,11 @@ main(int argc, char **argv) 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); @@ -2252,6 +2260,14 @@ main(int argc, char **argv) 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) @@ -2288,15 +2304,10 @@ main(int argc, char **argv) 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 @@ -20,6 +20,7 @@ #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 @@ -31,6 +32,7 @@ enum gotd_procid { enum gotd_procid { PROC_GOTD = 0, + PROC_LISTEN, PROC_REPO_READ, PROC_REPO_WRITE, PROC_MAX, @@ -113,8 +115,6 @@ struct gotd { struct gotd_repolist repos; int nrepos; int verbosity; - struct event ev; - struct event pause; struct gotd_child_proc *procs; int nprocs; }; @@ -169,8 +169,9 @@ enum gotd_imsg_type { 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. */ @@ -399,6 +400,11 @@ int parse_config(const char *, enum gotd_procid, struc 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 @@ -0,0 +1,388 @@ +/* + * Copyright (c) 2022 Stefan Sperling + * + * 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 +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 @@ -0,0 +1,17 @@ +/* + * Copyright (c) 2022 Stefan Sperling + * + * 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 @@ -136,7 +136,7 @@ main : UNIX_SOCKET STRING { ; 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)) {