about summary refs log tree commit diff homepage
diff options
context:
space:
mode:
authorJune McEnroe <june@causal.agency>2019-01-06 15:16:01 -0500
committerJune McEnroe <june@causal.agency>2019-01-06 15:16:01 -0500
commitc9350ab570b386f6db075ca15aa5b1b8d2e8685e (patch)
tree62a820ed0574933bab60b4afb014acd856a1ab8c
parentAdd cap_rights_limit calls to client and server (diff)
downloadtorus-c9350ab570b386f6db075ca15aa5b1b8d2e8685e.tar.gz
torus-c9350ab570b386f6db075ca15aa5b1b8d2e8685e.zip
Add kcgi support to image
-rw-r--r--README7
-rw-r--r--image.c116
-rw-r--r--kcgi.mk3
-rw-r--r--torus.112
4 files changed, 127 insertions, 11 deletions
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 <june@causal.agency>
+/* Copyright (C) 2018, 2019  C. McEnroe <june@causal.agency>
  *
  * 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 <err.h>
 #include <fcntl.h>
+#include <stdbool.h>
 #include <stdint.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -25,6 +26,21 @@
 #include <sysexits.h>
 #include <unistd.h>
 
+#ifdef __FreeBSD__
+#include <sys/capsicum.h>
+#endif
+
+#ifdef HAVE_KCGI
+#include <sys/types.h>
+#include <stdarg.h>
+#include <stdint.h>
+#include <kcgi.h>
+#endif
+
+// XXX: Include this after kcgi.h to avoid conflicts.
+// <https://github.com/kristapsdz/kcgi/pull/58>
+#include <stdnoreturn.h>
+
 #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 .