From c9350ab570b386f6db075ca15aa5b1b8d2e8685e Mon Sep 17 00:00:00 2001 From: Curtis McEnroe Date: Sun, 6 Jan 2019 15:16:01 -0500 Subject: Add kcgi support to image --- README | 7 ++-- image.c | 116 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----- kcgi.mk | 3 ++ torus.1 | 12 +++++++ 4 files changed, 127 insertions(+), 11 deletions(-) create mode 100644 kcgi.mk diff --git a/README b/README index 67c4dcc..f79c8be 100644 --- a/README +++ b/README @@ -6,7 +6,7 @@ NAME SYNOPSIS server [-d data] [-p pidfile] [-s sock] client [-h] [-s sock] - image [-d data] [-f font] [-x x] [-y y] + image [-k] [-d data] [-f font] [-x x] [-y y] meta merge data1 data2 data3 @@ -18,7 +18,8 @@ DESCRIPTION interface. image renders a tile from a data file using a PSF2 font to PNG on - standard output. + standard output. To build with kcgi(3) support, copy kcgi.mk to + config.mk. meta extracts metadata from a data file on standard input to CSV on standard ouput. The CSV fields are tileX, tileY, createTime, @@ -38,6 +39,8 @@ DESCRIPTION -h Write help page data to standard output and exit. + -k Run a FastCGI worker for use with kfcgi(8). + -p pidfile Daemonize and write PID to pidfile. Only available on FreeBSD. diff --git a/image.c b/image.c index 57e3a38..a55c3a0 100644 --- a/image.c +++ b/image.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2018 Curtis McEnroe +/* Copyright (C) 2018, 2019 C. McEnroe * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by @@ -16,6 +16,7 @@ #include #include +#include #include #include #include @@ -25,6 +26,21 @@ #include #include +#ifdef __FreeBSD__ +#include +#endif + +#ifdef HAVE_KCGI +#include +#include +#include +#include +#endif + +// XXX: Include this after kcgi.h to avoid conflicts. +// +#include + #include "png.h" #include "torus.h" @@ -105,12 +121,12 @@ static void tilesMap(const char *path) { if (error) err(EX_OSERR, "madvise"); } -static void render(uint32_t tileX, uint32_t tileY) { +static void render(FILE *stream, uint32_t tileX, uint32_t tileY) { uint32_t width = CellCols * font.glyph.width; uint32_t height = CellRows * font.glyph.height; - pngHead(stdout, width, height, 8, PNGIndexed); - pngPalette(stdout, (uint8_t *)Palette, sizeof(Palette)); + pngHead(stream, width, height, 8, PNGIndexed); + pngPalette(stream, (uint8_t *)Palette, sizeof(Palette)); uint8_t data[height][1 + width]; memset(data, PNGNone, sizeof(data)); @@ -137,21 +153,96 @@ static void render(uint32_t tileX, uint32_t tileY) { } } - pngData(stdout, (uint8_t *)data, sizeof(data)); - pngTail(stdout); + pngData(stream, (uint8_t *)data, sizeof(data)); + pngTail(stream); } +#ifdef HAVE_KCGI + +enum { KeyX, KeyY, KeysLen }; +static const struct kvalid Keys[KeysLen] = { + [KeyX] = { .name = "x", .valid = kvalid_int }, + [KeyY] = { .name = "y", .valid = kvalid_int }, +}; + +enum { PageIndex, PagesLen }; +static const char *Pages[PagesLen] = { + [PageIndex] = "index", +}; + +static noreturn void errkcgi(int eval, enum kcgi_err code, const char *str) { + errx(eval, "%s: %s", str, kcgi_strerror(code)); +} + +static int streamWrite(void *cookie, const char *buf, int len) { + struct kreq *req = cookie; + enum kcgi_err error = khttp_write(req, buf, (size_t)len); + if (error) errkcgi(EX_IOERR, error, "khttp_write"); + return len; +} + +static void worker(void) { + struct kfcgi *fcgi; + enum kcgi_err error = khttp_fcgi_init( + &fcgi, Keys, KeysLen, Pages, PagesLen, PageIndex + ); + if (error) errkcgi(EX_CONFIG, error, "khttp_fcgi_init"); + + for (;;) { + struct kreq req; + error = khttp_fcgi_parse(fcgi, &req); + if (error) errkcgi(EX_DATAERR, error, "khttp_fcgi_parse"); + + uint32_t tileX = TileInitX; + uint32_t tileY = TileInitY; + + for (size_t i = 0; i < req.fieldsz; ++i) { + if (req.fields[i].state != KPAIR_VALID) continue; + if (req.fields[i].keypos == KeyX) { + tileX = (uint32_t)req.fields[i].parsed.i % TileCols; + } else if (req.fields[i].keypos == KeyY) { + tileY = (uint32_t)req.fields[i].parsed.i % TileRows; + } + } + + error = khttp_head( + &req, kresps[KRESP_STATUS], "%s", khttps[KHTTP_200] + ); + if (error) errkcgi(EX_IOERR, error, "khttp_head"); + + error = khttp_head( + &req, kresps[KRESP_CONTENT_TYPE], "%s", kmimetypes[KMIME_IMAGE_PNG] + ); + if (error) errkcgi(EX_IOERR, error, "khttp_head"); + + error = khttp_body(&req); + if (error) errkcgi(EX_IOERR, error, "khttp_body"); + + FILE *stream = fwopen(&req, streamWrite); + if (!stream) err(EX_OSERR, "fwopen"); + + render(stream, tileX, tileY); + + fclose(stream); + khttp_free(&req); + } +} + +#endif /* HAVE_KCGI */ + int main(int argc, char *argv[]) { + bool kcgi = false; const char *fontPath = "default8x16.psfu"; const char *dataPath = "torus.dat"; uint32_t tileX = TileInitX; uint32_t tileY = TileInitY; int opt; - while (0 < (opt = getopt(argc, argv, "d:f:x:y:"))) { + while (0 < (opt = getopt(argc, argv, "d:f:kx:y:"))) { switch (opt) { break; case 'd': dataPath = optarg; break; case 'f': fontPath = optarg; + break; case 'k': kcgi = true; break; case 'x': tileX = strtoul(optarg, NULL, 0) % TileCols; break; case 'y': tileY = strtoul(optarg, NULL, 0) % TileRows; break; default: return EX_USAGE; @@ -161,7 +252,14 @@ int main(int argc, char *argv[]) { fontLoad(fontPath); tilesMap(dataPath); - render(tileX, tileY); +#ifdef __FreeBSD__ + int error = cap_enter(); + if (error) err(EX_OSERR, "cap_enter"); +#endif + +#ifdef HAVE_KCGI + if (kcgi) worker(); +#endif - return EX_OK; + render(stdout, tileX, tileY); } diff --git a/kcgi.mk b/kcgi.mk new file mode 100644 index 0000000..ddfea53 --- /dev/null +++ b/kcgi.mk @@ -0,0 +1,3 @@ +CFLAGS += -DHAVE_KCGI -I/usr/local/include +LDFLAGS += -L/usr/local/lib +LDLIBS += -lkcgi -lz diff --git a/torus.1 b/torus.1 index cf4f702..610ad9c 100644 --- a/torus.1 +++ b/torus.1 @@ -21,6 +21,7 @@ .Op Fl s Ar sock . .Nm image +.Op Fl k .Op Fl d Ar data .Op Fl f Ar font .Op Fl x Ar x @@ -51,6 +52,13 @@ interface. renders a tile from a data file using a PSF2 font to PNG on standard output. +To build with +.Xr kcgi 3 +support, +copy +.Pa kcgi.mk +to +.Pa config.mk . . .Pp .Nm meta @@ -98,6 +106,10 @@ The default path is .It Fl h Write help page data to standard output and exit. . +.It Fl k +Run a FastCGI worker for use with +.Xr kfcgi 8 . +. .It Fl p Ar pidfile Daemonize and write PID to .Ar pidfile . -- cgit 1.4.1