alexkarle.com

Source for alexkarle.com
git clone git://git.alexkarle.com/alexkarle.com.git
Log | Files | Refs | README | LICENSE

commit 9af62fa24d3216841e0dd8c43a517a195d61ef13 (patch)
parent 7037d26e0ad3e84dd44d9f726a4368ebd9d4e9ca
Author: Alex Karle <alex@alexkarle.com>
Date:   Sat, 24 Apr 2021 00:28:31 -0400

kiosk: Add MVP ssh kiosk program to browse blog via ssh+mandoc

I'm really excited (if a bit nervous about the security implications)
about this kiosk program. I've always thought it'd be cool to browse my
site via mandoc(1), so I wrote a simple shell-like program called
'kiosk' that lists the man pages in the site and asks which you'd like
to read (with some other options to quit, list, or see help).

This isn't 100% ready to go live (as a public SSH option), but it's at a
state where it's worth checking in!

Diffstat:
M.gitignore | 3+++
MMakefile | 9++++++++-
Asrc/kiosk.c | 107+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 118 insertions(+), 1 deletion(-)

diff --git a/.gitignore b/.gitignore @@ -6,3 +6,6 @@ atom.xml # jam-tuesday hits generated by jam-tuesday/stats.sh jam-tuesday/greatest-hits + +# binaries +bin/ diff --git a/Makefile b/Makefile @@ -9,8 +9,11 @@ SETS != find jam-tuesday -name '[01][0-9]-*' # of the abbreviated version (@ suppresses the command in make) HIDE = @ +CC = cc +CFLAGS = -g -O2 -Wall -Wpedantic -Wextra + .PHONY: build -build: $(HTML) atom.xml jam-tuesday/greatest-hits +build: $(HTML) atom.xml jam-tuesday/greatest-hits bin/kiosk .PHONY: clean clean: @@ -25,6 +28,10 @@ atom.xml: blog.7 genatom.sh jam-tuesday/greatest-hits: $(SETS) jam-tuesday/stats.sh (date; echo; ./jam-tuesday/stats.sh) > $@ +bin/kiosk: src/kiosk.c + mkdir -p bin + $(CC) $(CFLAGS) -DMANDIR="\"`pwd`\"" $< -o $@ + $(HTML): Makefile .SUFFIXES: .7 .html diff --git a/src/kiosk.c b/src/kiosk.c @@ -0,0 +1,107 @@ +#include <stdio.h> +#include <dirent.h> +#include <string.h> +#include <stdlib.h> +#include <limits.h> + +int list(void) { + DIR *dirp = opendir(MANDIR); + struct dirent *dp; + int n = 0; + while ((dp = readdir(dirp)) != NULL) { + size_t len = strlen(dp->d_name); + if (len < 3) + continue; + if (strcmp(dp->d_name + (len - 2), ".7") == 0) { + dp->d_name[len - 2] = '\0'; /* truncate extension */ + printf("%2d: %s(7)\n", ++n, dp->d_name); + } + } + closedir(dirp); + printf(" l: list\n"); + printf(" h: help\n"); + printf(" q: quit\n"); + return n; +} + +void help(void) { + printf( + "Welcome to alexkarle.com's SSH Kiosk!\n" + "\n" + "Here you'll find all the mdoc(7) contents of my blog, rendered\n" + "in their original form via mandoc(1).\n" + "\n" + "Currently, due to security concerns, only the blog posts are\n" + "browsable (and no shell access is given).\n" + "\n" + "If you think this is cool, I'd love to hear from you!\n" + "Drop me a line at alex@alexkarle.com!\n" + ); + +} + +/* TODO: have list() read into memory so we don't readdir each time! */ +void mandoc(int choice) { + DIR *dirp = opendir(MANDIR); + struct dirent *dp; + int i = 0; + while ((dp = readdir(dirp)) != NULL) { + size_t len = strlen(dp->d_name); + if (len < 3) + continue; + if (strcmp(dp->d_name + (len - 2), ".7") == 0) { + if (++i == choice) { + char *cmd_base = "mandoc -l"; + char cmd[sizeof(cmd_base) + PATH_MAX + 2]; + sprintf(cmd, "%s %s/%s", cmd_base, MANDIR, dp->d_name); + system(cmd); + break; + } + } + } + closedir(dirp); +} + +void prompt(int n) { + printf("choice> "); + fflush(stdout); + + /* NOTE: Read from /dev/tty instead of stdin to prevent + * simple DDOS via < /dev/random */ + + FILE *tty = fopen("/dev/tty", "r"); + char *line = NULL; + size_t line_len; + ssize_t nread = getline(&line, &line_len, tty); + if (nread == -1 || strcmp(line, "q\n") == 0) { + printf("Goodbye!\n"); + exit(0); + } + fclose(tty); + + int choice; + if (strcmp(line, "l\n") == 0) { + list(); + } else if (strcmp(line, "h\n") == 0) { + help(); + } else if (nread == 1) { + /* must be blank line... no-op */ + } else if (sscanf(line, "%d\n", &choice) == 0) { + printf("Bad input: %s", line); + } else if (choice > n || choice <= 0) { + printf("Choice %d out of bounds\n", choice); + } else { + mandoc(choice); + } + + free(line); +} + +int main(void) { + int n = list(); + setenv("MANPAGER", "less", 0); + setenv("LESSSECURE", "1", 1); + for(;;) + prompt(n); + return 0; +}