From 2458360e75aa46091f60c16e041c07bffe2edefb Mon Sep 17 00:00:00 2001
From: Jonathan McDowell <noodles@earth.li>
Date: Mon, 6 Jun 2016 21:54:42 +0100
Subject: [PATCH] Properly isolate database backend configuration

While database backends have had private context for some time they've
all been using the same configuration details from the global config
structure. Create a new DB specific config structure and initialise
a single instance from the config file. Also modify the DB backend
initialise functions to take this config structure as a parameter.
This will allow in the future for multiple different backends (whether
the same type or different) to be included at the same time.
---
 add.c           |   2 +-
 gpgwww.c        |   2 +-
 hashquery.c     |   2 +-
 keyd.c          |   2 +-
 keydb.h         |   5 ++
 keydb_db4.c     |  38 ++++++++--------
 keydb_dynamic.c |  18 ++++----
 keydb_file.c    |   5 +-
 keydb_fs.c      | 119 ++++++++++++++++++++++++++++--------------------
 keydb_hkp.c     |   6 ++-
 keydb_keyd.c    |   3 +-
 keydb_pg.c      |   3 +-
 lookup.c        |   2 +-
 maxpath.c       |   2 +-
 onak-conf.c     |  95 +++++++++++++++++++++-----------------
 onak-conf.h     |  44 ++++++++++--------
 onak.c          |   6 +--
 sixdegrees.c    |   2 +-
 wotsap.c        |   2 +-
 19 files changed, 203 insertions(+), 155 deletions(-)

diff --git a/add.c b/add.c
index 4e03719..877af11 100644
--- a/add.c
+++ b/add.c
@@ -89,7 +89,7 @@ int main(int argc, char *argv[])
 				fclose(stderr);
 			}
 			catchsignals();
-			dbctx = config.dbinit(false);
+			dbctx = config.dbinit(config.backend, false);
 			
 			count = cleankeys(keys);
 			logthing(LOGTHING_INFO, "%d keys cleaned.",
diff --git a/gpgwww.c b/gpgwww.c
index 6615887..d126168 100644
--- a/gpgwww.c
+++ b/gpgwww.c
@@ -183,7 +183,7 @@ int main(int argc, char *argv[])
 	readconfig(NULL);
 	initlogthing("gpgwww", config.logfile);
 	catchsignals();
-	dbctx = config.dbinit(true);
+	dbctx = config.dbinit(config.backend, true);
 	inithash();
 	logthing(LOGTHING_NOTICE, "Looking for path from 0x%016" PRIX64
 			" to 0x%016"
diff --git a/hashquery.c b/hashquery.c
index 965eaf9..44434c1 100644
--- a/hashquery.c
+++ b/hashquery.c
@@ -90,7 +90,7 @@ int main(int argc, char *argv[])
 	}
 
 	catchsignals();
-	dbctx = config.dbinit(false);
+	dbctx = config.dbinit(config.backend, false);
 
 	if (dbctx->fetch_key_skshash == NULL) {
 		dbctx->cleanupdb(dbctx);
diff --git a/keyd.c b/keyd.c
index b73ff28..7fb7eab 100644
--- a/keyd.c
+++ b/keyd.c
@@ -662,7 +662,7 @@ int main(int argc, char *argv[])
 		maxfd = fd;
 		memset(clients, -1, sizeof (clients));
 
-		dbctx = config.dbinit(false);
+		dbctx = config.dbinit(config.backend, false);
 
 		logthing(LOGTHING_NOTICE, "Accepting connections.");
 		while (!cleanup() && select(maxfd + 1, &rfds, NULL, NULL, NULL) != -1) {
diff --git a/keydb.h b/keydb.h
index 6c1bcf5..d7515c8 100644
--- a/keydb.h
+++ b/keydb.h
@@ -207,6 +207,11 @@ struct onak_dbctx {
 			void (*iterfunc)(void *ctx,
 			struct openpgp_publickey *key),	void *ctx);
 
+/**
+ * @brief Configuration file information for this backend instance
+ */
+	struct onak_db_config *config;
+
 /**
  * @brief Private backend context information.
  */
diff --git a/keydb_db4.c b/keydb_db4.c
index b2d34c5..e09d653 100644
--- a/keydb_db4.c
+++ b/keydb_db4.c
@@ -161,8 +161,9 @@ static void db4_endtrans(struct onak_dbctx *dbctx)
  *	we're running with a newer version of db4 than the database was
  *	created with.
  */
-static int db4_upgradedb(struct onak_db4_dbctx *privctx)
+static int db4_upgradedb(struct onak_dbctx *dbctx)
 {
+	struct onak_db4_dbctx *privctx = (struct onak_db4_dbctx *) dbctx->priv;
 	DB *curdb = NULL;
 	int ret;
 	int i;
@@ -171,7 +172,7 @@ static int db4_upgradedb(struct onak_db4_dbctx *privctx)
 	struct stat statbuf;
 	ssize_t written;
 
-	snprintf(buf, sizeof(buf) - 1, "%s/%s", config.db_dir,
+	snprintf(buf, sizeof(buf) - 1, "%s/%s", dbctx->config->location,
 			DB4_UPGRADE_FILE);
 	lockfile_fd = open(buf, O_RDWR | O_CREAT | O_EXCL, 0600);
 	if (lockfile_fd < 0) {
@@ -190,7 +191,7 @@ static int db4_upgradedb(struct onak_db4_dbctx *privctx)
 	if (written != strlen(buf)) {
 		logthing(LOGTHING_CRITICAL, "Couldn't write PID to lockfile: "
 				"%s", strerror(errno));
-		snprintf(buf, sizeof(buf) - 1, "%s/%s", config.db_dir,
+		snprintf(buf, sizeof(buf) - 1, "%s/%s", dbctx->config->location,
 				DB4_UPGRADE_FILE);
 		unlink(buf);
 		return -1;
@@ -200,14 +201,14 @@ static int db4_upgradedb(struct onak_db4_dbctx *privctx)
 	ret = db_env_create(&privctx->dbenv, 0);
 	if (ret == 0) {
 		privctx->dbenv->set_errcall(privctx->dbenv, &db4_errfunc);
-		privctx->dbenv->remove(privctx->dbenv, config.db_dir, 0);
+		privctx->dbenv->remove(privctx->dbenv, dbctx->config->location, 0);
 		privctx->dbenv = NULL;
 	}
 	for (i = 0; i < privctx->numdbs; i++) {
 		ret = db_create(&curdb, NULL, 0);
 		if (ret == 0) {
 			snprintf(buf, sizeof(buf) - 1, "%s/keydb.%d.db",
-				config.db_dir, i);
+				dbctx->config->location, i);
 			logthing(LOGTHING_DEBUG, "Upgrading %s", buf);
 			curdb->upgrade(curdb, buf, 0);
 			curdb->close(curdb, 0);
@@ -220,7 +221,7 @@ static int db4_upgradedb(struct onak_db4_dbctx *privctx)
 
 	ret = db_create(&curdb, NULL, 0);
 	if (ret == 0) {
-		snprintf(buf, sizeof(buf) - 1, "%s/worddb", config.db_dir);
+		snprintf(buf, sizeof(buf) - 1, "%s/worddb", dbctx->config->location);
 		logthing(LOGTHING_DEBUG, "Upgrading %s", buf);
 		curdb->upgrade(curdb, buf, 0);
 		curdb->close(curdb, 0);
@@ -232,7 +233,7 @@ static int db4_upgradedb(struct onak_db4_dbctx *privctx)
 
 	ret = db_create(&curdb, NULL, 0);
 	if (ret == 0) {
-		snprintf(buf, sizeof(buf) - 1, "%s/id32db", config.db_dir);
+		snprintf(buf, sizeof(buf) - 1, "%s/id32db", dbctx->config->location);
 		logthing(LOGTHING_DEBUG, "Upgrading %s", buf);
 		curdb->upgrade(curdb, buf, 0);
 		curdb->close(curdb, 0);
@@ -244,7 +245,7 @@ static int db4_upgradedb(struct onak_db4_dbctx *privctx)
 
 	ret = db_create(&curdb, NULL, 0);
 	if (ret == 0) {
-		snprintf(buf, sizeof(buf) - 1, "%s/id64db", config.db_dir);
+		snprintf(buf, sizeof(buf) - 1, "%s/id64db", dbctx->config->location);
 		logthing(LOGTHING_DEBUG, "Upgrading %s", buf);
 		curdb->upgrade(curdb, buf, 0);
 		curdb->close(curdb, 0);
@@ -256,7 +257,7 @@ static int db4_upgradedb(struct onak_db4_dbctx *privctx)
 
 	ret = db_create(&curdb, NULL, 0);
 	if (ret == 0) {
-		snprintf(buf, sizeof(buf) - 1, "%s/skshashdb", config.db_dir);
+		snprintf(buf, sizeof(buf) - 1, "%s/skshashdb", dbctx->config->location);
 		logthing(LOGTHING_DEBUG, "Upgrading %s", buf);
 		curdb->upgrade(curdb, buf, 0);
 		curdb->close(curdb, 0);
@@ -268,7 +269,7 @@ static int db4_upgradedb(struct onak_db4_dbctx *privctx)
 
 	ret = db_create(&curdb, NULL, 0);
 	if (ret == 0) {
-		snprintf(buf, sizeof(buf) - 1, "%s/subkeydb", config.db_dir);
+		snprintf(buf, sizeof(buf) - 1, "%s/subkeydb", dbctx->config->location);
 		logthing(LOGTHING_DEBUG, "Upgrading %s", buf);
 		curdb->upgrade(curdb, buf, 0);
 		curdb->close(curdb, 0);
@@ -278,7 +279,7 @@ static int db4_upgradedb(struct onak_db4_dbctx *privctx)
 			db_strerror(ret));
 	}
 
-	snprintf(buf, sizeof(buf) - 1, "%s/%s", config.db_dir,
+	snprintf(buf, sizeof(buf) - 1, "%s/%s", dbctx->config->location,
 			DB4_UPGRADE_FILE);
 	unlink(buf);
 
@@ -1626,7 +1627,7 @@ static void db4_cleanupdb(struct onak_dbctx *dbctx)
  *	this file are called in order to allow the DB to be initialized ready
  *	for access.
  */
-struct onak_dbctx *keydb_db4_init(bool readonly)
+struct onak_dbctx *keydb_db4_init(struct onak_db_config *dbcfg, bool readonly)
 {
 	char       buf[1024];
 	FILE      *numdb = NULL;
@@ -1642,6 +1643,7 @@ struct onak_dbctx *keydb_db4_init(bool readonly)
 	if (dbctx == NULL) {
 		return NULL;
 	}
+	dbctx->config = dbcfg;
 	dbctx->priv = privctx = calloc(1, sizeof(*privctx));
 	if (privctx == NULL) {
 		free(dbctx);
@@ -1651,7 +1653,7 @@ struct onak_dbctx *keydb_db4_init(bool readonly)
 	/* Default to 16 key data DBs */
 	privctx->numdbs = 16;
 
-	snprintf(buf, sizeof(buf) - 1, "%s/%s", config.db_dir,
+	snprintf(buf, sizeof(buf) - 1, "%s/%s", dbcfg->location,
 			DB4_UPGRADE_FILE);
 	ret = stat(buf, &statbuf);
 	while ((ret == 0) || (errno != ENOENT)) {
@@ -1666,7 +1668,7 @@ struct onak_dbctx *keydb_db4_init(bool readonly)
 	}
 	ret = 0;
 
-	snprintf(buf, sizeof(buf) - 1, "%s/num_keydb", config.db_dir);
+	snprintf(buf, sizeof(buf) - 1, "%s/num_keydb", dbcfg->location);
 	numdb = fopen(buf, "r");
 	if (numdb != NULL) {
 		if (fgets(buf, sizeof(buf), numdb) != NULL) {
@@ -1730,7 +1732,7 @@ struct onak_dbctx *keydb_db4_init(bool readonly)
 	}
 
 	if (ret == 0) {
-		ret = privctx->dbenv->open(privctx->dbenv, config.db_dir,
+		ret = privctx->dbenv->open(privctx->dbenv, dbcfg->location,
 				DB_INIT_LOG | DB_INIT_MPOOL | DB_INIT_LOCK |
 				DB_INIT_TXN |
 				DB_CREATE,
@@ -1739,7 +1741,7 @@ struct onak_dbctx *keydb_db4_init(bool readonly)
 		if (ret == DB_VERSION_MISMATCH) {
 			privctx->dbenv->close(privctx->dbenv, 0);
 			privctx->dbenv = NULL;
-			ret = db4_upgradedb(privctx);
+			ret = db4_upgradedb(dbctx);
 			if (ret == 0) {
 				ret = db_env_create(&privctx->dbenv, 0);
 			}
@@ -1749,7 +1751,7 @@ struct onak_dbctx *keydb_db4_init(bool readonly)
 				privctx->dbenv->set_lk_detect(privctx->dbenv,
 					DB_LOCK_DEFAULT);
 				ret = privctx->dbenv->open(privctx->dbenv,
-					config.db_dir,
+					dbcfg->location,
 					DB_INIT_LOG | DB_INIT_MPOOL |
 					DB_INIT_LOCK | DB_INIT_TXN |
 					DB_CREATE | DB_RECOVER,
@@ -1768,7 +1770,7 @@ struct onak_dbctx *keydb_db4_init(bool readonly)
 		if (ret != 0) {
 			logthing(LOGTHING_CRITICAL,
 					"Error opening db environment: %s (%s)",
-					config.db_dir,
+					dbcfg->location,
 					db_strerror(ret));
 			if (privctx->dbenv != NULL) {
 				privctx->dbenv->close(privctx->dbenv, 0);
diff --git a/keydb_dynamic.c b/keydb_dynamic.c
index 1e7d6b2..040a045 100644
--- a/keydb_dynamic.c
+++ b/keydb_dynamic.c
@@ -206,12 +206,13 @@ static void dynamic_cleanupdb(struct onak_dbctx *dbctx)
 	}
 }
 
-struct onak_dbctx *keydb_dynamic_init(bool readonly)
+struct onak_dbctx *keydb_dynamic_init(struct onak_db_config *dbcfg,
+		bool readonly)
 {
 	struct onak_dbctx *dbctx;
 	char *soname;
 	char *initname;
-	struct onak_dbctx *(*backend_init)(bool);
+	struct onak_dbctx *(*backend_init)(struct onak_db_config *, bool);
 	struct onak_dynamic_dbctx *privctx;
 
 	dbctx = malloc(sizeof(struct onak_dbctx));
@@ -220,6 +221,7 @@ struct onak_dbctx *keydb_dynamic_init(bool readonly)
 		return NULL;
 	}
 
+	dbctx->config = dbcfg;
 	dbctx->priv = privctx = malloc(sizeof(struct onak_dynamic_dbctx));
 	if (dbctx->priv == NULL) {
 		free(dbctx);
@@ -237,21 +239,21 @@ struct onak_dbctx *keydb_dynamic_init(bool readonly)
 	}
 
 	if (config.backends_dir == NULL) {
-		soname = malloc(strlen(config.db_backend)
+		soname = malloc(strlen(dbcfg->type)
 			+ strlen("./libkeydb_")
 			+ strlen(".so")
 			+ 1);
 
-		sprintf(soname, "./libkeydb_%s.so", config.db_backend);
+		sprintf(soname, "./libkeydb_%s.so", dbcfg->type);
 	} else {
-		soname = malloc(strlen(config.db_backend)
+		soname = malloc(strlen(dbcfg->type)
 			+ strlen("/libkeydb_")
 			+ strlen(".so")
 			+ strlen(config.backends_dir)
 			+ 1);
 
 		sprintf(soname, "%s/libkeydb_%s.so", config.backends_dir,
-			config.db_backend);
+			dbcfg->type);
 	}
 
 	logthing(LOGTHING_INFO, "Loading dynamic backend: %s", soname);
@@ -270,7 +272,7 @@ struct onak_dbctx *keydb_dynamic_init(bool readonly)
 			+ strlen("keydb_")
 			+ strlen("_init")
 			+ 1);
-	sprintf(initname, "keydb_%s_init", config.db_backend);
+	sprintf(initname, "keydb_%s_init", dbcfg->type);
 
 	*(void **) (&backend_init) = dlsym(privctx->backend_handle, initname);
 	free(initname);
@@ -286,7 +288,7 @@ struct onak_dbctx *keydb_dynamic_init(bool readonly)
 	free(soname);
 	soname = NULL;
 
-	privctx->loadeddbctx = backend_init(readonly);
+	privctx->loadeddbctx = backend_init(dbcfg, readonly);
 
 	if (privctx->loadeddbctx != NULL) {
 		dbctx->cleanupdb = dynamic_cleanupdb;
diff --git a/keydb_file.c b/keydb_file.c
index e0ed610..69c2b99 100644
--- a/keydb_file.c
+++ b/keydb_file.c
@@ -270,7 +270,7 @@ static void file_cleanupdb(struct onak_dbctx *dbctx)
  *
  *	This is just a no-op for flat file access.
  */
-struct onak_dbctx *keydb_file_init(bool readonly)
+struct onak_dbctx *keydb_file_init(struct onak_db_config *dbcfg, bool readonly)
 {
 	struct onak_dbctx *dbctx;
 
@@ -279,7 +279,8 @@ struct onak_dbctx *keydb_file_init(bool readonly)
 		return NULL;
 	}
 
-	dbctx->priv = strdup(config.db_dir);
+	dbctx->config = dbcfg;
+	dbctx->priv = strdup(dbcfg->location);
 
 	dbctx->cleanupdb		= file_cleanupdb;
 	dbctx->starttrans		= file_starttrans;
diff --git a/keydb_fs.c b/keydb_fs.c
index 2779297..9a7b2ec 100644
--- a/keydb_fs.c
+++ b/keydb_fs.c
@@ -72,41 +72,43 @@ static uint32_t calchash(uint8_t * ptr)
 }
 
 
-static void keypath(char *buffer, size_t length, uint64_t _keyid)
+static void keypath(char *buffer, size_t length, uint64_t _keyid,
+		char *basepath)
 {
 	uint64_t keyid = _keyid << 32;
 	snprintf(buffer, length, "%s/key/%02X/%02X/%08X/%016" PRIX64,
-		 config.db_dir, (uint8_t) ((keyid >> 56) & 0xFF),
+		 basepath, (uint8_t) ((keyid >> 56) & 0xFF),
 		 (uint8_t) ((keyid >> 48) & 0xFF),
 		 (uint32_t) (keyid >> 32), _keyid);
 }
 
-static void keydir(char *buffer, size_t length, uint64_t _keyid)
+static void keydir(char *buffer, size_t length, uint64_t _keyid,
+		char *basepath)
 {
 	uint64_t keyid = _keyid << 32;
-	snprintf(buffer, length, "%s/key/%02X/%02X/%08X", config.db_dir,
+	snprintf(buffer, length, "%s/key/%02X/%02X/%08X", basepath,
 		 (uint8_t) ((keyid >> 56) & 0xFF),
 		 (uint8_t) ((keyid >> 48) & 0xFF),
 		 (uint32_t) (keyid >> 32));
 }
 
-static void prove_path_to(uint64_t keyid, char *what)
+static void prove_path_to(uint64_t keyid, char *what, char *basepath)
 {
 	static char buffer[PATH_MAX];
-	snprintf(buffer, sizeof(buffer), "%s/%s", config.db_dir, what);
+	snprintf(buffer, sizeof(buffer), "%s/%s", basepath, what);
 	mkdir(buffer, 0777);
 
-	snprintf(buffer, sizeof(buffer), "%s/%s/%02X", config.db_dir, what,
+	snprintf(buffer, sizeof(buffer), "%s/%s/%02X", basepath, what,
 		 (uint8_t) ((keyid >> 24) & 0xFF));
 	mkdir(buffer, 0777);
 
-	snprintf(buffer, sizeof(buffer), "%s/%s/%02X/%02X", config.db_dir,
+	snprintf(buffer, sizeof(buffer), "%s/%s/%02X/%02X", basepath,
 		 what,
 		 (uint8_t) ((keyid >> 24) & 0xFF),
 		 (uint8_t) ((keyid >> 16) & 0xFF));
 	mkdir(buffer, 0777);
 
-	snprintf(buffer, sizeof(buffer), "%s/%s/%02X/%02X/%08X", config.db_dir,
+	snprintf(buffer, sizeof(buffer), "%s/%s/%02X/%02X/%08X", basepath,
 		 what,
 		 (uint8_t) ((keyid >> 24) & 0xFF),
 		 (uint8_t) ((keyid >> 16) & 0xFF), (uint32_t) (keyid));
@@ -114,45 +116,48 @@ static void prove_path_to(uint64_t keyid, char *what)
 }
 
 static void wordpath(char *buffer, size_t length, char *word, uint32_t hash,
-		uint64_t keyid)
+		uint64_t keyid, char *basepath)
 {
 	snprintf(buffer, length, "%s/words/%02X/%02X/%08X/%s/%016" PRIX64,
-		 config.db_dir, (uint8_t) ((hash >> 24) & 0xFF),
+		 basepath, (uint8_t) ((hash >> 24) & 0xFF),
 		 (uint8_t) ((hash >> 16) & 0xFF), hash, word, keyid);
 }
 
-static void worddir(char *buffer, size_t length, char *word, uint32_t hash)
+static void worddir(char *buffer, size_t length, char *word, uint32_t hash,
+		char *basepath)
 {
-	snprintf(buffer, length, "%s/words/%02X/%02X/%08X/%s", config.db_dir,
+	snprintf(buffer, length, "%s/words/%02X/%02X/%08X/%s", basepath,
 		 (uint8_t) ((hash >> 24) & 0xFF),
 		 (uint8_t) ((hash >> 16) & 0xFF), hash, word);
 }
 
-static void subkeypath(char *buffer, size_t length, uint64_t subkey)
+static void subkeypath(char *buffer, size_t length, uint64_t subkey,
+		char *basepath)
 {
 	snprintf(buffer, length, "%s/subkeys/%02X/%02X/%08X/%016" PRIX64,
-		 config.db_dir,
+		 basepath,
 		 (uint8_t) ((subkey >> 24) & 0xFF),
 		 (uint8_t) ((subkey >> 16) & 0xFF),
 		 (uint32_t) (subkey & 0xFFFFFFFF),
 		 subkey);
 }
 
-static void subkeydir(char *buffer, size_t length, uint64_t subkey)
+static void subkeydir(char *buffer, size_t length, uint64_t subkey,
+		char *basepath)
 {
 	snprintf(buffer, length, "%s/subkeys/%02X/%02X/%08X",
-		 config.db_dir,
+		 basepath,
 		 (uint8_t) ((subkey >> 24) & 0xFF),
 		 (uint8_t) ((subkey >> 16) & 0xFF),
 		 (uint32_t) (subkey & 0xFFFFFFFF));
 }
 
 static void skshashpath(char *buffer, size_t length,
-		const struct skshash *hash)
+		const struct skshash *hash, char *basepath)
 {
 	snprintf(buffer, length, "%s/skshash/%02X/%02X/%02X%02X%02X%02X/"
 		"%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X",
-		 config.db_dir,
+		 basepath,
 		 hash->hash[0], hash->hash[1],
 		 hash->hash[0], hash->hash[1], hash->hash[2], hash->hash[3],
 		 hash->hash[4], hash->hash[5], hash->hash[6], hash->hash[7],
@@ -207,7 +212,7 @@ static uint64_t fs_getfullkeyid(struct onak_dbctx *dbctx, uint64_t keyid)
 	struct dirent *de = NULL;
 	uint64_t ret = 0;
 
-	keydir(buffer, sizeof(buffer), keyid);
+	keydir(buffer, sizeof(buffer), keyid, dbctx->config->location);
 
 	d = opendir(buffer);
 	if (d) {
@@ -221,7 +226,8 @@ static uint64_t fs_getfullkeyid(struct onak_dbctx *dbctx, uint64_t keyid)
 	}
 
 	if (ret == 0) {
-		subkeydir(buffer, sizeof(buffer), keyid);
+		subkeydir(buffer, sizeof(buffer), keyid,
+			dbctx->config->location);
 
 		d = opendir(buffer);
 		if (d) {
@@ -259,10 +265,11 @@ static int fs_fetch_key_id(struct onak_dbctx *dbctx,
 	if ((keyid >> 32) == 0)
 		keyid = fs_getfullkeyid(dbctx, keyid);
 
-	keypath(buffer, sizeof(buffer), keyid);
+	keypath(buffer, sizeof(buffer), keyid, dbctx->config->location);
 	fd = open(buffer, O_RDONLY);
 	if (fd == -1 && errno == ENOENT) {
-		subkeypath(buffer, sizeof(buffer), keyid);
+		subkeypath(buffer, sizeof(buffer), keyid,
+			dbctx->config->location);
 		fd = open(buffer, O_RDONLY);
 	}
 
@@ -312,8 +319,8 @@ static int fs_store_key(struct onak_dbctx *dbctx,
 	if (!intrans)
 		fs_starttrans(dbctx);
 
-	prove_path_to(keyid, "key");
-	keypath(buffer, sizeof(buffer), keyid);
+	prove_path_to(keyid, "key", dbctx->config->location);
+	keypath(buffer, sizeof(buffer), keyid, dbctx->config->location);
 
 	if ((fd =
 	     open(buffer, O_WRONLY | (update ? O_TRUNC : O_CREAT),
@@ -334,12 +341,13 @@ static int fs_store_key(struct onak_dbctx *dbctx,
 		wl = wordlist = makewordlistfromkey(wordlist, publickey);
 		while (wl) {
 			uint32_t hash = calchash((uint8_t *) (wl->object));
-			prove_path_to(hash, "words");
+			prove_path_to(hash, "words", dbctx->config->location);
 
-			worddir(wbuffer, sizeof(wbuffer), wl->object, hash);
+			worddir(wbuffer, sizeof(wbuffer), wl->object, hash,
+				dbctx->config->location);
 			mkdir(wbuffer, 0777);
 			wordpath(wbuffer, sizeof(wbuffer), wl->object, hash,
-				keyid);
+				keyid, dbctx->config->location);
 			link(buffer, wbuffer);
 
 			wl = wl->next;
@@ -351,11 +359,14 @@ static int fs_store_key(struct onak_dbctx *dbctx,
 		while (subkeyids != NULL && subkeyids[i].length != 0) {
 			keyid = fingerprint2keyid(&subkeyids[i]);
 
-			prove_path_to(keyid, "subkeys");
+			prove_path_to(keyid, "subkeys",
+				dbctx->config->location);
 
-			subkeydir(wbuffer, sizeof(wbuffer), keyid);
+			subkeydir(wbuffer, sizeof(wbuffer), keyid,
+				dbctx->config->location);
 			mkdir(wbuffer, 0777);
-			subkeypath(wbuffer, sizeof(wbuffer), keyid);
+			subkeypath(wbuffer, sizeof(wbuffer), keyid,
+				dbctx->config->location);
 			link(buffer, wbuffer);
 
 			i++;
@@ -368,8 +379,9 @@ static int fs_store_key(struct onak_dbctx *dbctx,
 		get_skshash(publickey, &hash);
 		hashid = (hash.hash[0] << 24) + (hash.hash[1] << 16) +
 				(hash.hash[2] << 8) + hash.hash[3];
-		prove_path_to(hashid, "skshash");
-		skshashpath(wbuffer, sizeof(wbuffer), &hash);
+		prove_path_to(hashid, "skshash", dbctx->config->location);
+		skshashpath(wbuffer, sizeof(wbuffer), &hash,
+			dbctx->config->location);
 		link(buffer, wbuffer);
 	}
 
@@ -410,10 +422,10 @@ static int fs_delete_key(struct onak_dbctx *dbctx, uint64_t keyid, bool intrans)
 			 "Wordlist for key %016" PRIX64 " done", keyid);
 		while (wl) {
 			uint32_t hash = calchash((uint8_t *) (wl->object));
-			prove_path_to(hash, "words");
+			prove_path_to(hash, "words", dbctx->config->location);
 
 			wordpath(buffer, sizeof(buffer), wl->object, hash,
-				keyid);
+				keyid, dbctx->config->location);
 			unlink(buffer);
 
 			wl = wl->next;
@@ -423,9 +435,11 @@ static int fs_delete_key(struct onak_dbctx *dbctx, uint64_t keyid, bool intrans)
 		i = 0;
 		while (subkeyids != NULL && subkeyids[i].length != 0) {
 			subkeyid = fingerprint2keyid(&subkeyids[i]);
-			prove_path_to(subkeyid, "subkeys");
+			prove_path_to(subkeyid, "subkeys",
+				dbctx->config->location);
 
-			subkeypath(buffer, sizeof(buffer), subkeyid);
+			subkeypath(buffer, sizeof(buffer), subkeyid,
+				dbctx->config->location);
 			unlink(buffer);
 
 			i++;
@@ -436,11 +450,12 @@ static int fs_delete_key(struct onak_dbctx *dbctx, uint64_t keyid, bool intrans)
 		}
 
 		get_skshash(pk, &hash);
-		skshashpath(buffer, sizeof(buffer), &hash);
+		skshashpath(buffer, sizeof(buffer), &hash,
+			dbctx->config->location);
 		unlink(buffer);
 	}
 
-	keypath(buffer, sizeof(buffer), keyid);
+	keypath(buffer, sizeof(buffer), keyid, dbctx->config->location);
 	unlink(buffer);
 
 	if (!intrans)
@@ -448,7 +463,8 @@ static int fs_delete_key(struct onak_dbctx *dbctx, uint64_t keyid, bool intrans)
 	return 1;
 }
 
-static struct ll *internal_get_key_by_word(char *word, struct ll *mct)
+static struct ll *internal_get_key_by_word(char *word, struct ll *mct,
+		char *basepath)
 {
 	struct ll *keys = NULL;
 	DIR *d = NULL;
@@ -456,7 +472,7 @@ static struct ll *internal_get_key_by_word(char *word, struct ll *mct)
 	uint32_t hash = calchash((uint8_t *) (word));
 	struct dirent *de;
 
-	worddir(buffer, sizeof(buffer), word, hash);
+	worddir(buffer, sizeof(buffer), word, hash, basepath);
 	d = opendir(buffer);
 	logthing(LOGTHING_DEBUG, "Scanning for word %s in dir %s", word,
 		 buffer);
@@ -503,7 +519,8 @@ static int fs_fetch_key_text(struct onak_dbctx *dbctx,
 	searchtext = strdup(search);
 	wl = wordlist = makewordlist(wordlist, searchtext);
 
-	keylist = internal_get_key_by_word(wordlist->object, NULL);
+	keylist = internal_get_key_by_word(wordlist->object, NULL,
+		dbctx->config->location);
 
 	if (!keylist) {
 		llfree(wordlist, NULL);
@@ -515,7 +532,8 @@ static int fs_fetch_key_text(struct onak_dbctx *dbctx,
 	wl = wl->next;
 	while (wl) {
 		struct ll *nkl =
-		    internal_get_key_by_word(wl->object, keylist);
+		    internal_get_key_by_word(wl->object, keylist,
+			dbctx->config->location);
 		if (!nkl) {
 			llfree(wordlist, NULL);
 			llfree(keylist, free);
@@ -564,7 +582,7 @@ static int fs_fetch_key_skshash(struct onak_dbctx *dbctx,
 	int ret = 0, fd;
 	struct openpgp_packet_list *packets = NULL;
 
-	skshashpath(buffer, sizeof(buffer), hash);
+	skshashpath(buffer, sizeof(buffer), hash, dbctx->config->location);
 	if ((fd = open(buffer, O_RDONLY)) != -1) {
 		read_openpgp_stream(file_fetchchar, &fd, &packets, 0);
 		parse_keys(packets, publickey);
@@ -618,7 +636,7 @@ static void fs_cleanupdb(struct onak_dbctx *dbctx)
 /**
  *	initdb - Initialize the key database.
  */
-struct onak_dbctx *keydb_fs_init(bool readonly)
+struct onak_dbctx *keydb_fs_init(struct onak_db_config *dbcfg, bool readonly)
 {
 	char buffer[PATH_MAX];
 	struct onak_dbctx *dbctx;
@@ -628,6 +646,7 @@ struct onak_dbctx *keydb_fs_init(bool readonly)
 	if (dbctx == NULL) {
 		return NULL;
 	}
+	dbctx->config = dbcfg;
 	dbctx->priv = privctx = malloc(sizeof(*privctx));
 	if (privctx == NULL) {
 		free(dbctx);
@@ -636,19 +655,19 @@ struct onak_dbctx *keydb_fs_init(bool readonly)
 
 	privctx->lockfile_readonly = readonly;
 
-	snprintf(buffer, sizeof(buffer), "%s/.lock", config.db_dir);
+	snprintf(buffer, sizeof(buffer), "%s/.lock", dbcfg->location);
 
-	if (access(config.db_dir, R_OK | W_OK | X_OK) == -1) {
+	if (access(dbcfg->location, R_OK | W_OK | X_OK) == -1) {
 		if (errno != ENOENT) {
 			logthing(LOGTHING_CRITICAL,
 				 "Unable to access keydb_fs root of '%s'. (%s)",
-				 config.db_dir, strerror(errno));
+				 dbcfg->location, strerror(errno));
 			exit(1);	/* Lacking rwx on the key dir */
 		}
-		mkdir(config.db_dir, 0777);
+		mkdir(dbcfg->location, 0777);
 		privctx->lockfile_fd = open(buffer, O_RDWR | O_CREAT, 0600);
 	}
-	if (chdir(config.db_dir) == -1) {
+	if (chdir(dbcfg->location) == -1) {
 		/* Shouldn't happen after the above */
 		logthing(LOGTHING_CRITICAL,
 			"Couldn't change to database directory: %s",
diff --git a/keydb_hkp.c b/keydb_hkp.c
index 0c6fd5a..6707ae6 100644
--- a/keydb_hkp.c
+++ b/keydb_hkp.c
@@ -34,6 +34,7 @@
 #include "version.h"
 
 struct onak_hkp_dbctx {
+	struct onak_db_config *config; /* Our DB config info */
 	CURL *curl;
 	char hkpbase[1024];
 };
@@ -346,7 +347,7 @@ static void hkp_cleanupdb(struct onak_dbctx *dbctx)
  *
  *	We initialize CURL here.
  */
-struct onak_dbctx *keydb_hkp_init(bool readonly)
+struct onak_dbctx *keydb_hkp_init(struct onak_db_config *dbcfg, bool readonly)
 {
 	struct onak_dbctx *dbctx;
 	struct onak_hkp_dbctx *privctx;
@@ -357,6 +358,7 @@ struct onak_dbctx *keydb_hkp_init(bool readonly)
 		return NULL;
 	}
 
+	dbctx->config = dbcfg;
 	dbctx->priv = privctx = malloc(sizeof(*privctx));
 	dbctx->cleanupdb		= hkp_cleanupdb;
 	dbctx->starttrans		= hkp_starttrans;
@@ -373,7 +375,7 @@ struct onak_dbctx *keydb_hkp_init(bool readonly)
 	dbctx->getfullkeyid		= generic_getfullkeyid;
 	dbctx->iterate_keys		= hkp_iterate_keys;
 
-	if (!hkp_parse_url(privctx, config.db_dir)) {
+	if (!hkp_parse_url(privctx, dbcfg->location)) {
 		exit(EXIT_FAILURE);
 	}
 	curl_global_init(CURL_GLOBAL_DEFAULT);
diff --git a/keydb_keyd.c b/keydb_keyd.c
index 33d2c21..6836e9a 100644
--- a/keydb_keyd.c
+++ b/keydb_keyd.c
@@ -502,7 +502,7 @@ static void keyd_cleanupdb(struct onak_dbctx *dbctx)
  *	this file are called in order to allow the DB to be initialized ready
  *	for access.
  */
-struct onak_dbctx *keydb_keyd_init(bool readonly)
+struct onak_dbctx *keydb_keyd_init(struct onak_db_config *dbcfg, bool readonly)
 {
 	struct sockaddr_un sock;
 	uint32_t	   cmd = KEYD_CMD_UNKNOWN;
@@ -515,6 +515,7 @@ struct onak_dbctx *keydb_keyd_init(bool readonly)
 	if (dbctx == NULL) {
 		return NULL;
 	}
+	dbctx->config = dbcfg;
 
 	keyd_fd = socket(PF_UNIX, SOCK_STREAM, 0);
 	if (keyd_fd < 0) {
diff --git a/keydb_pg.c b/keydb_pg.c
index ca7e35b..d599afe 100644
--- a/keydb_pg.c
+++ b/keydb_pg.c
@@ -666,7 +666,7 @@ static void pg_cleanupdb(struct onak_dbctx *dbctx)
  *	this file are called in order to allow the DB to be initialized ready
  *	for access.
  */
-struct onak_dbctx *keydb_pg_init(bool readonly)
+struct onak_dbctx *keydb_pg_init(struct onak_db_config *dbcfg, bool readonly)
 {
 	struct onak_dbctx *dbctx;
 	PGconn *dbconn;
@@ -675,6 +675,7 @@ struct onak_dbctx *keydb_pg_init(bool readonly)
 	if (dbctx == NULL) {
 		return NULL;
 	}
+	dbctx->config = dbcfg;
 
 	dbconn = PQsetdbLogin(config.pg_dbhost, // host
 			NULL, // port
diff --git a/lookup.c b/lookup.c
index 12eb5a2..23aa96e 100644
--- a/lookup.c
+++ b/lookup.c
@@ -215,7 +215,7 @@ int main(int argc, char *argv[])
 		readconfig(NULL);
 		initlogthing("lookup", config.logfile);
 		catchsignals();
-		dbctx = config.dbinit(false);
+		dbctx = config.dbinit(config.backend, false);
 		switch (op) {
 		case OP_GET:
 		case OP_HGET:
diff --git a/maxpath.c b/maxpath.c
index 0932df3..0e5d632 100644
--- a/maxpath.c
+++ b/maxpath.c
@@ -102,7 +102,7 @@ int main(int argc, char *argv[])
 	readconfig(configfile);
 	free(configfile);
 	initlogthing("maxpath", config.logfile);
-	dbctx = config.dbinit(true);
+	dbctx = config.dbinit(config.backend, true);
 	if (dbctx != NULL) {
 		inithash();
 		findmaxpath(dbctx, 30);
diff --git a/onak-conf.c b/onak-conf.c
index 9a8a471..982d23b 100644
--- a/onak-conf.c
+++ b/onak-conf.c
@@ -28,7 +28,7 @@
 #include "log.h"
 #include "onak-conf.h"
 
-extern struct onak_dbctx *DBINIT(bool readonly);
+extern struct onak_dbctx *DBINIT(struct onak_db_config *dbcfg, bool readonly);
 
 /*
  *	config - Runtime configuration for onak.
@@ -47,28 +47,15 @@ struct onak_config config = {
 	.use_keyd = false,
 	.sock_dir = ".",
 
-	/*
-	 * Options for directory backends.
-	 */
-	.db_dir = NULL,
-
-	/*
-	 * Options for the Postgres backend.
-	 */
-	.pg_dbhost = NULL,
-	.pg_dbname = NULL,
-	.pg_dbuser = NULL,
-	.pg_dbpass = NULL,
-
-	/*
-	 * Options for dynamic backends.
-	 */
-	.db_backend = NULL,
+	.backends = NULL,
 	.backends_dir = NULL,
 
 	.dbinit = DBINIT,
 
 	.check_sighash = true,
+
+	.bin_dir = NULL,
+	.mail_dir = NULL,
 };
 
 bool parsebool(char *str, bool fallback)
@@ -95,6 +82,7 @@ void readconfig(const char *configfile) {
 	int   i;
 	char *dir, *conf;
 	size_t len;
+	struct onak_db_config *backend;
 
 	curline[1023] = 0;
 	if (configfile == NULL) {
@@ -126,6 +114,11 @@ void readconfig(const char *configfile) {
 			return;
 		}
 
+		/* Add a single DB configuration */
+		backend = calloc(1, sizeof(*backend));
+		config.backend = backend;
+		config.backends = lladd(NULL, backend);
+
 		while (!feof(conffile)) {
 			for (i = strlen(curline) - 1;
 					i >= 0 && isspace(curline[i]);
@@ -138,7 +131,7 @@ void readconfig(const char *configfile) {
 			 * Comment line, ignore.
 			 */
 		} else if (!strncmp("db_dir ", curline, 7)) {
-			config.db_dir = strdup(&curline[7]);
+			backend->location = strdup(&curline[7]);
 		} else if (!strncmp("debug ", curline, 6)) {
 			/*
 			 * Not supported yet; ignore for compatibility with
@@ -171,13 +164,13 @@ void readconfig(const char *configfile) {
 		} else if (!strncmp("max_reply_keys ", curline, 15)) {
 			config.maxkeys = atoi(&curline[15]);
 		} else if (!strncmp("pg_dbhost ", curline, 10)) {
-			config.pg_dbhost = strdup(&curline[10]);
+			backend->hostname = strdup(&curline[10]);
 		} else if (!strncmp("pg_dbname ", curline, 10)) {
-			config.pg_dbname = strdup(&curline[10]);
+			backend->location = strdup(&curline[10]);
 		} else if (!strncmp("pg_dbuser ", curline, 10)) {
-			config.pg_dbuser = strdup(&curline[10]);
+			backend->username = strdup(&curline[10]);
 		} else if (!strncmp("pg_dbpass ", curline, 10)) {
-			config.pg_dbpass = strdup(&curline[10]);
+			backend->password = strdup(&curline[10]);
 		} else if (!strncmp("syncsite ", curline, 9)) {
 			config.syncsites =
 				lladd(config.syncsites, strdup(&curline[9]));
@@ -195,6 +188,8 @@ void readconfig(const char *configfile) {
 			 * Not applicable; ignored for compatibility with pksd.
 			 */
 		} else if (!strncmp("db_backend ", curline, 11)) {
+			backend->type = strdup(&curline[11]);
+			backend->name = strdup(&curline[11]);
 			config.db_backend = strdup(&curline[11]);
 		} else if (!strncmp("backends_dir ", curline, 13)) {
 			config.backends_dir = strdup(&curline[13]);
@@ -225,7 +220,41 @@ void readconfig(const char *configfile) {
 	}
 }
 
+void cleanupdbconfig(void *object)
+{
+	struct onak_db_config *dbconfig = (struct onak_db_config *) object;
+
+	if (dbconfig->name != NULL) {
+		free(dbconfig->name);
+		dbconfig->name = NULL;
+	}
+	if (dbconfig->type != NULL) {
+		free(dbconfig->type);
+		dbconfig->type = NULL;
+	}
+	if (dbconfig->location != NULL) {
+		free(dbconfig->location);
+		dbconfig->location = NULL;
+	}
+	if (dbconfig->hostname != NULL) {
+		free(dbconfig->hostname);
+		dbconfig->hostname = NULL;
+	}
+	if (dbconfig->username != NULL) {
+		free(dbconfig->username);
+		dbconfig->username = NULL;
+	}
+	if (dbconfig->password != NULL) {
+		free(dbconfig->password);
+		dbconfig->password = NULL;
+	}
+}
+
 void cleanupconfig(void) {
+	/* Free any defined DB backend configuration first */
+	llfree(config.backends, cleanupdbconfig);
+	config.backends = NULL;
+
 	if (config.thissite != NULL) {
 		free(config.thissite);
 		config.thissite = NULL;
@@ -238,26 +267,6 @@ void cleanupconfig(void) {
 		free(config.mta);
 		config.mta = NULL;
 	}
-	if (config.db_dir != NULL) {
-		free(config.db_dir);
-		config.db_dir = NULL;
-	}
-	if (config.pg_dbhost != NULL) {
-		free(config.pg_dbhost);
-		config.pg_dbhost = NULL;
-	}
-	if (config.pg_dbname != NULL) {
-		free(config.pg_dbname);
-		config.pg_dbname = NULL;
-	}
-	if (config.pg_dbuser != NULL) {
-		free(config.pg_dbuser);
-		config.pg_dbuser = NULL;
-	}
-	if (config.pg_dbpass != NULL) {
-		free(config.pg_dbpass);
-		config.pg_dbpass = NULL;
-	}
 	if (config.syncsites != NULL) {
 		llfree(config.syncsites, free);
 		config.syncsites = NULL;
diff --git a/onak-conf.h b/onak-conf.h
index 52439ab..0d937c2 100644
--- a/onak-conf.h
+++ b/onak-conf.h
@@ -23,6 +23,25 @@
 
 #include "keydb.h"
 
+/**
+ * @brief Backend database configuration.
+ *
+ */
+struct onak_db_config {
+	/** Name, as used to refer to individual backend instances */
+	char *name;
+	/** Backend type [e.g. db4, pg, fs, file] */
+	char *type;
+	/** Location information; directory for file backed, DB name for DBs */
+	char *location;
+	/** Database backend hostname, if appropriate */
+	char *hostname;
+	/** Database backend username, if appropriate */
+	char *username;
+	/** Database backend password, if appropriate */
+	char *password;
+};
+
 /**
  * @brief Runtime configuration for onak.
  *
@@ -51,24 +70,11 @@ struct onak_config {
 	/** The path to the directory the keyd socket lives in. */
 	char *sock_dir;
 
-	/*
-	 * Options for any database backend that needs a directory, be it the
-	 * file, fs or db4 options.
-	 */
-	/** The path to the directory containing the database files. */
-	char *db_dir;
-	
-	/*
-	 * Options for the Postgres backend.
-	 */
-	/** The host that Postgres is running on. */
-	char *pg_dbhost;
-	/** The database name. */
-	char *pg_dbname;
-	/** The user we should connect as. */
-	char *pg_dbuser;
-	/** The password for the user. */
-	char *pg_dbpass;
+	/** List of backend configurations */
+	struct ll *backends;
+
+	/* The default backend to use */
+	struct onak_db_config *backend;
 
 	/*
 	 * Options for the dynamic backend.
@@ -79,7 +85,7 @@ struct onak_config {
 	char *backends_dir;
 
 	/** Pointer to the initialisation function for our loaded DB backend */
-	struct onak_dbctx *(*dbinit)(bool);
+	struct onak_dbctx *(*dbinit)(struct onak_db_config *, bool);
 
 	/** Should we verify signature hashes match? */
 	bool check_sighash;
diff --git a/onak.c b/onak.c
index 0c4673d..08bdd39 100644
--- a/onak.c
+++ b/onak.c
@@ -207,7 +207,7 @@ int main(int argc, char *argv[])
 	if ((argc - optind) < 1) {
 		usage();
 	} else if (!strcmp("dump", argv[optind])) {
-		dbctx = config.dbinit(true);
+		dbctx = config.dbinit(config.backend, true);
 		dumpstate.count = dumpstate.filenum = 0;
 		dumpstate.maxcount = 100000;
 		dumpstate.fd = -1;
@@ -238,7 +238,7 @@ int main(int argc, char *argv[])
 			logthing(LOGTHING_INFO, "%d keys cleaned.",
 					result);
 
-			dbctx = config.dbinit(false);
+			dbctx = config.dbinit(config.backend, false);
 			logthing(LOGTHING_NOTICE, "Got %d new keys.",
 					dbctx->update_keys(dbctx, &keys,
 					false));
@@ -337,7 +337,7 @@ int main(int argc, char *argv[])
 				ishex = true;
 			}
 		}
-		dbctx = config.dbinit(false);
+		dbctx = config.dbinit(config.backend, false);
 		if (!strcmp("index", argv[optind])) {
 			find_keys(dbctx, search, keyid, &fingerprint, ishex,
 					isfp, dispfp, skshash,
diff --git a/sixdegrees.c b/sixdegrees.c
index b806c3e..3dd13ec 100644
--- a/sixdegrees.c
+++ b/sixdegrees.c
@@ -158,7 +158,7 @@ int main(int argc, char *argv[])
 	readconfig(configfile);
 	free(configfile);
 	initlogthing("sixdegrees", config.logfile);
-	dbctx = config.dbinit(true);
+	dbctx = config.dbinit(config.backend, true);
 	if (dbctx != NULL) {
 		inithash();
 		sixdegrees(dbctx, dbctx->getfullkeyid(dbctx, keyid));
diff --git a/wotsap.c b/wotsap.c
index 66dc946..ce25b02 100644
--- a/wotsap.c
+++ b/wotsap.c
@@ -203,7 +203,7 @@ int main(int argc, char *argv[])
 
 	readconfig(configfile);
 	initlogthing("wotsap", config.logfile);
-	dbctx = config.dbinit(true);
+	dbctx = config.dbinit(config.backend, true);
 	if (dbctx != NULL) {
 		inithash();
 		wotsap(dbctx, dbctx->getfullkeyid(dbctx, keyid),
-- 
2.39.5