summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Makefile2
-rw-r--r--config.ini13
-rw-r--r--config.sh24
-rwxr-xr-xconfigure1
-rw-r--r--include/config.h34
-rw-r--r--src/config.c141
-rw-r--r--src/ini.c4
-rw-r--r--src/main.c39
8 files changed, 248 insertions, 10 deletions
diff --git a/Makefile b/Makefile
index 89d8887..7d37afd 100644
--- a/Makefile
+++ b/Makefile
@@ -33,6 +33,6 @@ distclean: clean
install: all
mkdir -p $(BINDIR)
- install -Dm755 gmnisrv $(BINDIR)/gmnisrv
+ install -Dm755 gmnisrv $(DESTDIR)$(BINDIR)/gmnisrv
.PHONY: clean distclean docs install
diff --git a/config.ini b/config.ini
new file mode 100644
index 0000000..b5c6052
--- /dev/null
+++ b/config.ini
@@ -0,0 +1,13 @@
+# Space-separated list of hosts
+listen=0.0.0.0:1965 [::]:1965
+
+[:tls]
+# Path to store certificates on disk
+store=/var/lib/gemini/certs
+
+# Details for new certificates
+organization=gmnisrv user
+email=jsmith@example.org
+
+[localhost]
+root=/var/www
diff --git a/config.sh b/config.sh
index 5f15de3..e1dfa97 100644
--- a/config.sh
+++ b/config.sh
@@ -13,6 +13,19 @@ do
case "$arg" in
--prefix=*)
PREFIX=${arg#*=}
+ if [ "$PREFIX" = "/usr" ]
+ then
+ SYSCONFDIR=/etc
+ fi
+ ;;
+ --bindir=*)
+ BINDIR=${arg#*=}
+ ;;
+ --sysconfdir=*)
+ SYSCONFDIR=${arg#*=}
+ ;;
+ --mandir=*)
+ MANDIR=${arg#*=}
;;
esac
done
@@ -123,15 +136,18 @@ run_configure() {
LIBS=$LIBS
PREFIX=${PREFIX:-/usr/local}
OUTDIR=${outdir}
- _INSTDIR=\$(DESTDIR)\$(PREFIX)
- BINDIR?=${BINDIR:-\$(_INSTDIR)/bin}
- LIBDIR?=${LIBDIR:-\$(_INSTDIR)/lib}
- MANDIR?=${MANDIR:-\$(_INSTDIR)/share/man}
+ BINDIR?=${BINDIR:-\$(PREFIX)/bin}
+ SYSCONFDIR?=${SYSCONFDIR:-\$(PREFIX)/etc}
+ LIBDIR?=${LIBDIR:-\$(PREFIX)/lib}
+ MANDIR?=${MANDIR:-\$(PREFIX)/share/man}
+ VARLIBDIR?=${MANDIR:-\$(PREFIX)/var/lib}
CACHE=\$(OUTDIR)/cache
CFLAGS=${CFLAGS}
CFLAGS+=-Iinclude -I\$(OUTDIR)
CFLAGS+=-DPREFIX='"\$(PREFIX)"'
CFLAGS+=-DLIBDIR='"\$(LIBDIR)"'
+ CFLAGS+=-DVARLIBDIR='"\$(VARLIBDIR)"'
+ CFLAGS+=-DSYSCONFDIR='"\$(SYSCONFDIR)"'
all: ${all}
EOF
diff --git a/configure b/configure
index 324a58d..30c4cbb 100755
--- a/configure
+++ b/configure
@@ -4,6 +4,7 @@ eval ". $srcdir/config.sh"
gmnisrv() {
genrules gmnisrv \
+ src/config.c \
src/escape.c \
src/ini.c \
src/main.c \
diff --git a/include/config.h b/include/config.h
new file mode 100644
index 0000000..0c3d085
--- /dev/null
+++ b/include/config.h
@@ -0,0 +1,34 @@
+#ifndef GMNISRV_CONFIG
+#define GMNISRV_CONFIG
+#include <arpa/inet.h>
+
+struct gmnisrv_tls {
+ char *store;
+ char *organization;
+ char *email;
+};
+
+struct gmnisrv_host {
+ char *hostname;
+ char *root;
+ struct gmnisrv_host *next;
+};
+
+struct gmnisrv_bind {
+ int family;
+ char addr[sizeof(struct in6_addr)];
+ struct gmnisrv_bind *next;
+};
+
+struct gmnisrv_config {
+ struct gmnisrv_tls tls;
+ struct gmnisrv_host *hosts;
+ struct gmnisrv_bind *binds;
+};
+
+int load_config(struct gmnisrv_config *conf, const char *path);
+
+struct gmnisrv_host *gmnisrv_config_get_host(
+ struct gmnisrv_config *conf, const char *hostname);
+
+#endif
diff --git a/src/config.c b/src/config.c
new file mode 100644
index 0000000..847af6e
--- /dev/null
+++ b/src/config.c
@@ -0,0 +1,141 @@
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "config.h"
+#include "ini.h"
+
+struct gmnisrv_host *
+gmnisrv_config_get_host(struct gmnisrv_config *conf, const char *hostname)
+{
+ struct gmnisrv_host *host = conf->hosts;
+ while (host) {
+ if (strcmp(host->hostname, hostname) == 0) {
+ return host;
+ }
+ host = host->next;
+ }
+ return NULL;
+}
+
+static int
+parse_listen(struct gmnisrv_config *conf, const char *value)
+{
+ // TODO
+ (void)conf;
+ (void)value;
+ return 1;
+}
+
+static int
+conf_ini_handler(void *user, const char *section,
+ const char *name, const char *value)
+{
+ struct gmnisrv_config *conf = (struct gmnisrv_config *)user;
+ struct {
+ char *section;
+ char *name;
+ char **value;
+ } strvars[] = {
+ { ":tls", "store", &conf->tls.store },
+ { ":tls", "organization", &conf->tls.organization },
+ { ":tls", "email", &conf->tls.email },
+ };
+ struct {
+ char *section;
+ char *name;
+ int (*fn)(struct gmnisrv_config *conf, const char *value);
+ } fnvars[] = {
+ { "", "listen", &parse_listen, }
+ };
+
+ for (size_t i = 0; i < sizeof(strvars) / sizeof(strvars[0]); ++i) {
+ if (strcmp(strvars[i].section, section) != 0) {
+ continue;
+ }
+ if (strcmp(strvars[i].name, name) != 0) {
+ continue;
+ }
+ *strvars[i].value = strdup(value);
+ return 1;
+ }
+
+ for (size_t i = 0; i < sizeof(fnvars) / sizeof(fnvars[0]); ++i) {
+ if (strcmp(fnvars[i].section, section) != 0) {
+ continue;
+ }
+ if (strcmp(fnvars[i].name, name) != 0) {
+ continue;
+ }
+ return fnvars[i].fn(conf, value);
+ }
+
+ if (section[0] == '\0' || section[0] == ':') {
+ fprintf(stderr, "Unknown config option [%s]%s\n", section, name);
+ return 0;
+ }
+
+ struct gmnisrv_host *host = gmnisrv_config_get_host(conf, section);
+ if (!host) {
+ host = calloc(1, sizeof(struct gmnisrv_host));
+ host->hostname = strdup(section);
+ host->next = conf->hosts;
+ conf->hosts = host;
+ }
+
+ struct {
+ char *name;
+ char **value;
+ } host_strvars[] = {
+ { "root", &host->root },
+ };
+
+ for (size_t i = 0; i < sizeof(host_strvars) / sizeof(host_strvars[0]); ++i) {
+ if (strcmp(host_strvars[i].name, name) != 0) {
+ continue;
+ }
+ *host_strvars[i].value = strdup(value);
+ return 1;
+ }
+
+ fprintf(stderr, "Unknown config option [%s]%s\n", section, name);
+ return 0;
+}
+
+static int
+validate_config(struct gmnisrv_config *conf)
+{
+ if (!conf->binds) {
+ fprintf(stderr, "Error: config missing listen directive\n");
+ return 1;
+ }
+ if (!conf->hosts) {
+ fprintf(stderr, "Error: config defines no hosts\n");
+ return 1;
+ }
+ if (!conf->tls.store) {
+ fprintf(stderr, "Error: config defines no certificate store\n");
+ return 1;
+ }
+ return 0;
+}
+
+int
+load_config(struct gmnisrv_config *conf, const char *path)
+{
+ FILE *f = fopen(path, "r");
+ if (!f) {
+ fprintf(stderr, "Error opening %s: %s\n",
+ path, strerror(errno));
+ return 1;
+ }
+
+ int n = ini_parse_file(f, conf_ini_handler, conf);
+ fclose(f);
+ if (n != 0) {
+ fprintf(stderr, "at %s:%d\n", path, n);
+ return 1;
+ }
+
+ return validate_config(conf);
+}
diff --git a/src/ini.c b/src/ini.c
index 236684e..88ff0d1 100644
--- a/src/ini.c
+++ b/src/ini.c
@@ -20,8 +20,8 @@ https://github.com/benhoyt/inih
#include <stdlib.h>
#endif
-#define MAX_SECTION 50
-#define MAX_NAME 50
+#define MAX_SECTION 512
+#define MAX_NAME 512
/* Strip whitespace chars off end of given string, in place. Return s. */
static char* rstrip(char* s)
diff --git a/src/main.c b/src/main.c
index b581f42..b865b01 100644
--- a/src/main.c
+++ b/src/main.c
@@ -1,10 +1,43 @@
+#include <getopt.h>
#include <stdio.h>
+#include "config.h"
+
+static void
+usage(const char *argv_0)
+{
+ fprintf(stderr, "Usage: %s [-C path]\n", argv_0);
+}
int
main(int argc, char **argv)
{
- printf("Hello world!\n");
- (void)argc;
- (void)argv;
+ struct gmnisrv_config conf = {0};
+
+ char *confpath = SYSCONFDIR "/gmnisrv.ini";
+ int c;
+ while ((c = getopt(argc, argv, "C:h")) != -1) {
+ switch (c) {
+ case 'C':
+ confpath = optarg;
+ break;
+ case 'h':
+ usage(argv[0]);
+ return 0;
+ default:
+ fprintf(stderr, "Unknown flag %c\n", c);
+ usage(argv[0]);
+ return 1;
+ }
+ }
+ if (optind < argc) {
+ usage(argv[0]);
+ return 1;
+ }
+
+ int r = load_config(&conf, confpath);
+ if (r != 0) {
+ return r;
+ }
+
return 0;
}