diff options
Diffstat (limited to 'src/misc/skabus-dynteed.c')
-rw-r--r-- | src/misc/skabus-dynteed.c | 305 |
1 files changed, 305 insertions, 0 deletions
diff --git a/src/misc/skabus-dynteed.c b/src/misc/skabus-dynteed.c new file mode 100644 index 0000000..8b0e292 --- /dev/null +++ b/src/misc/skabus-dynteed.c @@ -0,0 +1,305 @@ +/* ISC license. */ + +#include <skalibs/nonposix.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/uio.h> +#include <sys/socket.h> /* shutdown */ +#include <unistd.h> +#include <fcntl.h> +#include <errno.h> +#include <signal.h> +#include <skalibs/types.h> +#include <skalibs/siovec.h> +#include <skalibs/allreadwrite.h> +#include <skalibs/sgetopt.h> +#include <skalibs/error.h> +#include <skalibs/bufalloc.h> +#include <skalibs/buffer.h> +#include <skalibs/strerr2.h> +#include <skalibs/tai.h> +#include <skalibs/getpeereid.h> +#include <skalibs/djbunix.h> +#include <skalibs/sig.h> +#include <skalibs/iopause.h> +#include <skalibs/selfpipe.h> +#include <skalibs/cdb.h> +#include <s6/accessrules.h> + +#define USAGE "skabus-dynteed [ -d fdsocket ] [ -c maxconn ] [ -1 ] [ -t timeout ] [ -T lameducktimeout ] [ -i rulesdir | -x rulesfile ]" +#define dieusage() strerr_dieusage(100, USAGE) ; +#define dienomem() strerr_diefu1sys(111, "stralloc_catb") ; +#define die() strerr_dief1sys(101, "unexpected error") ; + +#define SKABUS_DYNTEE_MAX 1000 + +static int cont = 1 ; +static tain_t lameduckdeadline ; + +static unsigned int rulestype = 0 ; +static char const *rules = 0 ; +static int cdbfd = -1 ; +static struct cdb cdbmap = CDB_ZERO ; + +typedef struct client_s client_t, *client_t_ref ; +struct client_s +{ + unsigned int xindex ; + tain_t deadline ; + bufalloc ba ; +} ; + +static void client_free (client_t *c) +{ + fd_close(bufalloc_fd(&c->ba)) ; + bufalloc_free(&c->ba) ; +} + +static void handle_signals (void) +{ + for (;;) switch (selfpipe_read()) + { + case -1 : strerr_diefu1sys(111, "selfpipe_read()") ; + case 0 : return ; + case SIGTERM : + { + if (cont) + { + cont = 0 ; + tain_add_g(&lameduckdeadline, &lameduckdeadline) ; + } + break ; + } + case SIGHUP : + { + int fd ; + struct cdb c = CDB_ZERO ; + if (rulestype != 2) break ; + fd = open_readb(rules) ; + if (fd < 0) break ; + if (cdb_init(&c, fd) < 0) + { + fd_close(fd) ; + break ; + } + cdb_free(&cdbmap) ; + fd_close(cdbfd) ; + cdbfd = fd ; + cdbmap = c ; + } + break ; + default : break ; + } +} + +static inline int new_connection (int fd) +{ + s6_accessrules_params_t params = S6_ACCESSRULES_PARAMS_ZERO ; + uid_t uid ; + gid_t gid ; + if (!rulestype) return 1 ; + if (getpeereid(fd, &uid, &gid) < 0) + { + strerr_warnwu1sys("getpeereid") ; + return 0 ; + } + if ((rulestype == 1 ? s6_accessrules_uidgid_fs(uid, gid, rules, ¶ms) : s6_accessrules_uidgid_cdb(uid, gid, &cdbmap, ¶ms)) != S6_ACCESSRULES_ALLOW) + return 0 ; + s6_accessrules_params_free(¶ms) ; + return 1 ; +} + +int main (int argc, char const *const *argv, char const *const *envp) +{ + tain_t readtto ; + int spfd ; + int flag1 = 0 ; + unsigned int maxconn = 40 ; + unsigned int fdsocket = 3 ; + PROG = "skabus-dynteed" ; + + { + subgetopt_t l = SUBGETOPT_ZERO ; + unsigned int t = 0, T = 0 ; + for (;;) + { + int opt = subgetopt_r(argc, argv, "d:1c:i:x:t:", &l) ; + if (opt == -1) break ; + switch (opt) + { + case 'd' : if (!uint0_scan(l.arg, &fdsocket)) dieusage() ; break ; + case '1' : flag1 = 1 ; break ; + case 'i' : rules = l.arg ; rulestype = 1 ; break ; + case 'x' : rules = l.arg ; rulestype = 2 ; break ; + case 't' : if (!uint0_scan(l.arg, &t)) dieusage() ; break ; + case 'T' : if (!uint0_scan(l.arg, &T)) dieusage() ; break ; + case 'c' : if (!uint0_scan(l.arg, &maxconn)) dieusage() ; break ; + default : dieusage() ; + } + } + argc -= l.ind ; argv += l.ind ; + if (t) tain_from_millisecs(&readtto, t) ; + else readtto = tain_infinite_relative ; + if (T) tain_from_millisecs(&lameduckdeadline, T) ; + else lameduckdeadline = tain_infinite_relative ; + } + if (maxconn > SKABUS_DYNTEE_MAX) maxconn = SKABUS_DYNTEE_MAX ; + if (!maxconn) maxconn = 1 ; + { + struct stat st ; + if (fstat(fdsocket, &st) < 0) strerr_diefu1sys(111, "fstat socket descriptor") ; + if (!S_ISSOCK(st.st_mode)) strerr_dief1x(100, "descriptor is not a socket") ; + } + if (flag1) + { + if (fcntl(1, F_GETFD) < 0) + strerr_dief1sys(100, "called with option -1 but stdout said") ; + } + else close(1) ; + if (ndelay_on(0) < 0) strerr_diefu1sys(111, "set stdin non-blocking") ; + spfd = selfpipe_init() ; + if (spfd < 0) strerr_diefu1sys(111, "selfpipe_init") ; + if (sig_ignore(SIGPIPE) < 0) strerr_diefu1sys(111, "ignore SIGPIPE") ; + { + sigset_t set ; + sigemptyset(&set) ; + sigaddset(&set, SIGTERM) ; + sigaddset(&set, SIGHUP) ; + if (selfpipe_trapset(&set) < 0) strerr_diefu1sys(111, "trap signals") ; + } + + if (rulestype == 2) + { + cdbfd = open_readb(rules) ; + if (cdbfd < 0) strerr_diefu3sys(111, "open ", rules, " for reading") ; + if (cdb_init(&cdbmap, cdbfd) < 0) + strerr_diefu2sys(111, "cdb_init ", rules) ; + } + + { + unsigned int numconn = 0 ; + client_t clients[maxconn] ; + + if (flag1) + { + fd_write(1, "\n", 1) ; + fd_close(1) ; + } + tain_now_g() ; + + for (;;) + { + tain_t deadline ; + int r = 2 ; + iopause_fd x[2 + cont + numconn] ; + x[0].fd = spfd ; + x[0].events = IOPAUSE_READ ; + x[1].fd = fdsocket ; + x[1].events = (cont && (numconn < maxconn)) ? IOPAUSE_READ : 0 ; + if (cont) + { + tain_add_g(&deadline, &tain_infinite_relative) ; + x[2].fd = 0 ; + x[2].events = IOPAUSE_READ ; + r++ ; + } + else deadline = lameduckdeadline ; + for (unsigned int i = 0 ; i < numconn ; i++) if (bufalloc_len(&clients[i].ba)) + { + if (tain_less(&clients[i].deadline, &deadline)) deadline = clients[i].deadline ; + x[r].fd = bufalloc_fd(&clients[i].ba) ; + x[r].events = IOPAUSE_WRITE ; + clients[i].xindex = r++ ; + } + + r = iopause_g(x, r, &deadline) ; + if (r < 0) strerr_diefu1sys(111, "iopause") ; + + if (!r) + { + for (unsigned int i = 0 ; i < numconn ; i++) + { + if (bufalloc_len(&clients[i].ba) && !tain_future(&clients[i].deadline)) + { + client_free(clients + i) ; + clients[i--] = clients[--numconn] ; + } + } + if (!(cont || (numconn && tain_future(&lameduckdeadline)))) break ; + continue ; + } + + if (x[0].revents & IOPAUSE_READ) handle_signals() ; + + for (unsigned int i = 0 ; i < numconn ; i++) + { + if (x[clients[i].xindex].revents & (IOPAUSE_WRITE | IOPAUSE_EXCEPT)) + { + if (!bufalloc_flush(&clients[i].ba) && !error_isagain(errno)) + { + client_free(clients + i) ; + clients[i--] = clients[--numconn] ; + } + } + } + + if (cont && x[1].revents & IOPAUSE_READ) + { + int fd = ipc_accept_nb(fdsocket, 0, 0, 0) ; + if (fd < 0) + { + if (!error_isagain(errno)) strerr_diefu1sys(111, "accept") ; + } + else if (!new_connection(fd)) fd_close(fd) ; + else if (shutdown(fd, SHUT_RD) < 0) + { + fd_close(fd) ; + strerr_warnwu1sys("shutdown client connection for reading - aborting it") ; + } + else + { + bufalloc_init(&clients[numconn].ba, &fd_write, fd) ; + tain_copynow(&clients[numconn].deadline) ; + numconn++ ; + } + } + + if (cont && x[2].revents & IOPAUSE_READ) + { + ssize_t r = sanitize_read(buffer_fill(buffer_0)) ; + if (r < 0) + { + fd_close(0) ; + cont = 0 ; + tain_add_g(&lameduckdeadline, &lameduckdeadline) ; + if (errno != EPIPE) strerr_warnwu1sys("read from stdin") ; + } + if (r > 0) + { + struct iovec v[2] ; + buffer_rpeek(buffer_0, v) ; + for (unsigned int i = 0 ; i < numconn ; i++) + { + if (!bufalloc_putv(&clients[i].ba, v, 2)) dienomem() ; + if (!tain_future(&clients[i].deadline)) tain_add_g(&clients[i].deadline, &readtto) ; + } + buffer_rseek(buffer_0, siovec_len(v, 2)) ; + } + } + + if (!cont) + { + for (unsigned int i = 0 ; i < numconn ; i++) + { + if (!bufalloc_len(&clients[i].ba)) + { + client_free(clients + i) ; + clients[i--] = clients[--numconn] ; + } + } + if (!numconn) break ; + } + } + } + return 0 ; +} |