chunking_advertise_hosts, and smtp transport option hosts_try_chunking
for control.
+ 8. LMDB lookup support, as Experimental. Patch supplied by Andrew Colin Kissa.
+
Version 4.87
------------
Note that non-RFC-documented field names and data types are used.
+LMDB Lookup support
+-------------------
+LMDB is an ultra-fast, ultra-compact, crash-proof key-value embedded data store.
+It is modeled loosely on the BerkeleyDB API. You shoul read about the feature
+set as well as operation modes at https://symas.com/products/lightning-memory-mapped-database/
+
+LMDB single key lookup support is provided by linking to the LMDB C library.
+The current implementation does not support writing to the LMDB database.
+
+Visit https://github.com/LMDB/lmdb to download the library or find it in your
+operating systems package repository.
+
+If building from source, this description assumes that headers will be in
+/usr/local/include, and that the libraries are in /usr/local/lib.
+
+1. In order to build exim with LMDB lookup support add or uncomment
+
+EXPERIMENTAL_LMDB=yes
+
+to your Local/Makefile. (Re-)build/install exim. exim -d should show
+Experimental_LMDB in the line "Support for:".
+
+EXPERIMENTAL_LMDB=yes
+LDFLAGS += -llmdb
+# CFLAGS += -I/usr/local/include
+# LDFLAGS += -L/usr/local/lib
+
+The first line sets the feature to include the correct code, and
+the second line says to link the LMDB libraries into the
+exim binary. The commented out lines should be uncommented if you
+built LMDB from source and installed in the default location.
+Adjust the paths if you installed them elsewhere, but you do not
+need to uncomment them if an rpm (or you) installed them in the
+package controlled locations (/usr/include and /usr/lib).
+
+2. Create your LMDB files, you can use the mdb_load utility which is
+part of the LMDB distribution our your favourite language bindings.
+
+3. Add the single key lookups to your exim.conf file, example lookups
+are below.
+
+${lookup{$sender_address_domain}lmdb{/var/lib/baruwa/data/db/relaydomains.mdb}{$value}}
+${lookup{$sender_address_domain}lmdb{/var/lib/baruwa/data/db/relaydomains.mdb}{$value}fail}
+${lookup{$sender_address_domain}lmdb{/var/lib/baruwa/data/db/relaydomains.mdb}}
--------------------------------------------------------------
cd lookups
# Makefile is generated
for f in README cdb.c dbmdb.c dnsdb.c dsearch.c ibase.c ldap.h ldap.c \
- lsearch.c mysql.c redis.c nis.c nisplus.c oracle.c passwd.c \
+ lmdb.c lsearch.c mysql.c redis.c nis.c nisplus.c oracle.c passwd.c \
pgsql.c spf.c sqlite.c testdb.c whoson.c \
lf_functions.h lf_check_file.c lf_quote.c lf_sqlperform.c
do
OBJ="${OBJ} spf.o"
+if want_experimental LMDB
+then
+ OBJ="${OBJ} lmdb.o"
+fi
+
echo "MODS = $MODS"
echo "OBJ = $OBJ"
# Uncomment the following to include extra information in fail DSN message (bounces)
# EXPERIMENTAL_DSN_INFO=yes
+# Uncomment the following to add LMDB lookup support
+# You need to have LMDB installed on your system (https://github.com/LMDB/lmdb)
+# Depending on where it is installed you may have to edit the CFLAGS and LDFLAGS lines.
+# EXPERIMENTAL_LMDB=yes
+# CFLAGS += -I/usr/local/include
+# LDFLAGS += -llmdb
+
###############################################################################
# THESE ARE THINGS YOU MIGHT WANT TO SPECIFY #
###############################################################################
#define EXPERIMENTAL_DCC
#define EXPERIMENTAL_DSN_INFO
#define EXPERIMENTAL_DMARC
+#define EXPERIMENTAL_LMDB
#define EXPERIMENTAL_SPF
#define EXPERIMENTAL_SRS
#if defined(LOOKUP_REDIS) && LOOKUP_REDIS!=2
extern lookup_module_info redis_lookup_module_info;
#endif
+#if defined(EXPERIMENTAL_LMDB)
+extern lookup_module_info lmdb_lookup_module_info;
+#endif
#if defined(EXPERIMENTAL_SPF)
extern lookup_module_info spf_lookup_module_info;
#endif
addlookupmodule(NULL, &redis_lookup_module_info);
#endif
+#ifdef EXPERIMENTAL_LMDB
+ addlookupmodule(NULL, &lmdb_lookup_module_info);
+#endif
+
#ifdef EXPERIMENTAL_SPF
addlookupmodule(NULL, &spf_lookup_module_info);
#endif
#ifdef SUPPORT_SOCKS
fprintf(f, " SOCKS");
#endif
+#ifdef EXPERIMENTAL_LMDB
+ fprintf(f, " Experimental_LMDB");
+#endif
#ifdef EXPERIMENTAL_SPF
fprintf(f, " Experimental_SPF");
#endif
#if defined(LOOKUP_LDAP) && LOOKUP_LDAP!=2
fprintf(f, " ldap ldapdn ldapm");
#endif
+#ifdef EXPERIMENTAL_LMDB
+ fprintf(f, " lmdb");
+#endif
#if defined(LOOKUP_MYSQL) && LOOKUP_MYSQL!=2
fprintf(f, " mysql");
#endif
dsearch.o: $(PHDRS) dsearch.c
ibase.o: $(PHDRS) ibase.c
ldap.o: $(PHDRS) ldap.c
+lmdb.o: $(PHDRS) lmdb.c
lsearch.o: $(PHDRS) lsearch.c
mysql.o: $(PHDRS) mysql.c
nis.o: $(PHDRS) nis.c
dsearch.so: $(PHDRS) dsearch.c
ibase.so: $(PHDRS) ibase.c
ldap.so: $(PHDRS) ldap.c
+lmdb.so: $(PHDRS) lmdb.c
lsearch.so: $(PHDRS) lsearch.c
mysql.so: $(PHDRS) mysql.c
nis.so: $(PHDRS) nis.c
--- /dev/null
+/*************************************************
+* Exim - an Internet mail transport agent *
+*************************************************/
+
+/* Copyright (c) University of Cambridge 2016 */
+/* See the file NOTICE for conditions of use and distribution. */
+
+#include "../exim.h"
+
+#ifdef EXPERIMENTAL_LMDB
+
+#include <lmdb.h>
+
+typedef struct lmdbstrct
+{
+MDB_txn *txn;
+MDB_dbi db_dbi;
+} Lmdbstrct;
+
+
+/*************************************************
+* Open entry point *
+*************************************************/
+
+static void *
+lmdb_open(uschar * filename, uschar ** errmsg)
+{
+MDB_env * db_env = NULL;
+Lmdbstrct * lmdb_p;
+int ret, save_errno;
+const uschar * errstr;
+
+lmdb_p = store_get(sizeof(Lmdbstrct));
+lmdb_p->txn = NULL;
+
+if ((ret = mdb_env_create(&db_env)))
+ {
+ errstr = US"create environment";
+ goto bad;
+ }
+
+if ((ret = mdb_env_open(db_env, CS filename, MDB_NOSUBDIR|MDB_RDONLY, 0660)))
+ {
+ errstr = US"open environment";
+ goto bad;
+ }
+
+if ((ret = mdb_txn_begin(db_env, NULL, MDB_RDONLY, &lmdb_p->txn)))
+ {
+ errstr = US"start transaction";
+ goto bad;
+ }
+
+if ((ret = mdb_open(lmdb_p->txn, NULL, 0, &lmdb_p->db_dbi)))
+ {
+ errstr = US"open database";
+ goto bad;
+ }
+
+return lmdb_p;
+
+bad:
+ save_errno = errno;
+ if (lmdb_p->txn) mdb_txn_abort(lmdb_p->txn);
+ if (db_env) mdb_env_close(db_env);
+ *errmsg = string_sprintf("LMDB: Unable to %s: %s", errstr, mdb_strerror(ret));
+ errno = save_errno;
+ return NULL;
+}
+
+
+/*************************************************
+* Find entry point *
+*************************************************/
+
+static int
+lmdb_find(void * handle, uschar * filename,
+ const uschar * keystring, int length, uschar ** result, uschar ** errmsg,
+ uint * do_cache)
+{
+int ret;
+MDB_val dbkey, data;
+Lmdbstrct * lmdb_p = handle;
+
+dbkey.mv_data = CS keystring;
+dbkey.mv_size = length;
+
+DEBUG(D_lookup) debug_printf("LMDB: lookup key: %s\n", (char *)keystring);
+
+if ((ret = mdb_get(lmdb_p->txn, lmdb_p->db_dbi, &dbkey, &data)) == 0)
+ {
+ *result = string_copyn(US data.mv_data, data.mv_size);
+ DEBUG(D_lookup) debug_printf("LMDB: lookup result: %s\n", *result);
+ return OK;
+ }
+else if (ret == MDB_NOTFOUND)
+ {
+ *errmsg = US"LMDB: lookup, no data found";
+ DEBUG(D_lookup) debug_printf("%s\n", *errmsg);
+ return FAIL;
+ }
+else
+ {
+ *errmsg = string_sprintf("LMDB: lookup error: %s", mdb_strerror(ret));
+ DEBUG(D_lookup) debug_printf("%s\n", *errmsg);
+ return DEFER;
+ }
+}
+
+
+/*************************************************
+* Close entry point *
+*************************************************/
+
+static void
+lmdb_close(void * handle)
+{
+Lmdbstrct * lmdb_p = handle;
+MDB_env * db_env = mdb_txn_env(lmdb_p->txn);
+mdb_txn_abort(lmdb_p->txn);
+mdb_env_close(db_env);
+}
+
+
+/*************************************************
+* Version reporting entry point *
+*************************************************/
+
+#include "../version.h"
+
+void
+lmdb_version_report(FILE * f)
+{
+fprintf(f, "Library version: LMDB: Compile: %d.%d.%d\n",
+ MDB_VERSION_MAJOR, MDB_VERSION_MINOR, MDB_VERSION_PATCH);
+#ifdef DYNLOOKUP
+fprintf(f, " Exim version %s\n", EXIM_VERSION_STR);
+#endif
+}
+
+static lookup_info lmdb_lookup_info = {
+ US"lmdb", /* lookup name */
+ lookup_absfile, /* query-style lookup */
+ lmdb_open, /* open function */
+ NULL, /* no check function */
+ lmdb_find, /* find function */
+ lmdb_close, /* close function */
+ NULL, /* tidy function */
+ NULL, /* quoting function */
+ lmdb_version_report /* version reporting */
+};
+
+#ifdef DYNLOOKUP
+# define lmdb_lookup_module_info _lookup_module_info
+#endif /* DYNLOOKUP */
+
+static lookup_info *_lookup_list[] = { &lmdb_lookup_info };
+lookup_module_info lmdb_lookup_module_info = { LOOKUP_MODULE_INFO_MAGIC, _lookup_list, 1 };
+
+#endif /* EXPERIMENTAL_LMDB */
--- /dev/null
+#!/usr/bin/env python
+import os
+import lmdb
+
+if os.path.exists('2800.mdb'):
+ os.unlink('2800.mdb')
+
+env = lmdb.open('2800.mdb', subdir=False);
+with env.begin(write=True) as txn:
+ txn.put('first', 'data for first')
+ txn.put('second', 'A=1 B=2')
+ txn.put('third', 'A1:B2:C3')
+ cursor = txn.cursor()
+ for key, value in cursor:
+ print key, "=>", value
--- /dev/null
+first: data for first
+second: A=1 B=2
+third: A1:B2:C3
--- /dev/null
+# Exim test configuration 2800
+
+.include DIR/aux-var/std_conf_prefix
+
+primary_hostname = myhost.test.ex
+
+# ----- Main settings -----
+
+# End
--- /dev/null
+# lmdb lookup
+exim -be
+${lookup{first}lmdb{DIR/aux-fixed/TESTNUM.mdb}}
+${lookup{first}lmdb{DIR/aux-fixed/TESTNUM.mdb}{$value}fail}
+${lookup{fail}lmdb{DIR/aux-fixed/TESTNUM.mdb}{$value}{failure value}}
+${lookup{fail}lmdb{DIR/aux-fixed/TESTNUM.mdb}{$value}fail}
+${lookup{second}lmdb{DIR/aux-fixed/TESTNUM.mdb}{$value}fail}
+${extract{A}{${lookup{second}lmdb{DIR/aux-fixed/TESTNUM.mdb}{$value}fail}}}
+${extract{B}{${lookup{second}lmdb{DIR/aux-fixed/TESTNUM.mdb}{$value}fail}}}
+${extract{C}{${lookup{second}lmdb{DIR/aux-fixed/TESTNUM.mdb}{$value}fail}}{$value}fail}
+****
--- /dev/null
+lookup lmdb
--- /dev/null
+> data for first
+> data for first
+> failure value
+> Failed: "lookup" failed and "fail" requested
+> A=1 B=2
+> 1
+> 2
+> Failed: "extract" failed and "fail" requested
+>