4066751983d1c30e15059acf5d660c6089ce36e0
[users/jgh/exim.git] / src / src / lookups / lmdb.c
1 /*************************************************
2 *     Exim - an Internet mail transport agent    *
3 *************************************************/
4
5 /* Copyright (c) University of Cambridge 2016 - 2018 */
6 /* Copyright (c) The Exim Maintainers 2020 */
7 /* See the file NOTICE for conditions of use and distribution. */
8
9 #include "../exim.h"
10
11 #ifdef EXPERIMENTAL_LMDB
12
13 #include <lmdb.h>
14
15 typedef struct lmdbstrct
16 {
17 MDB_txn *txn;
18 MDB_dbi db_dbi;
19 } Lmdbstrct;
20
21
22 /*************************************************
23 *              Open entry point                  *
24 *************************************************/
25
26 static void *
27 lmdb_open(const uschar * filename, uschar ** errmsg)
28 {
29 MDB_env * db_env = NULL;
30 Lmdbstrct * lmdb_p;
31 int ret, save_errno;
32 const uschar * errstr;
33
34 lmdb_p = store_get(sizeof(Lmdbstrct), FALSE);
35 lmdb_p->txn = NULL;
36
37 if ((ret = mdb_env_create(&db_env)))
38   {
39   errstr = US"create environment";
40   goto bad;
41   }
42
43 if ((ret = mdb_env_open(db_env, CS filename, MDB_NOSUBDIR|MDB_RDONLY, 0660)))
44   {
45   errstr = string_sprintf("open environment with %s", filename);
46   goto bad;
47   }
48
49 if ((ret = mdb_txn_begin(db_env, NULL, MDB_RDONLY, &lmdb_p->txn)))
50   {
51   errstr = US"start transaction";
52   goto bad;
53   }
54
55 if ((ret = mdb_open(lmdb_p->txn, NULL, 0, &lmdb_p->db_dbi)))
56   {
57   errstr = US"open database";
58   goto bad;
59   }
60
61 return lmdb_p;
62
63 bad:
64   save_errno = errno;
65   if (lmdb_p->txn) mdb_txn_abort(lmdb_p->txn);
66   if (db_env) mdb_env_close(db_env);
67   *errmsg = string_sprintf("LMDB: Unable to %s: %s", errstr,  mdb_strerror(ret));
68   errno = save_errno;
69   return NULL;
70 }
71
72
73 /*************************************************
74 *              Find entry point                  *
75 *************************************************/
76
77 static int
78 lmdb_find(void * handle, const uschar * filename,
79     const uschar * keystring, int length, uschar ** result, uschar ** errmsg,
80     uint * do_cache, const uschar * opts)
81 {
82 int ret;
83 MDB_val dbkey, data;
84 Lmdbstrct * lmdb_p = handle;
85
86 dbkey.mv_data = CS keystring;
87 dbkey.mv_size = length;
88
89 DEBUG(D_lookup) debug_printf_indent("LMDB: lookup key: %s\n", CS keystring);
90
91 if ((ret = mdb_get(lmdb_p->txn, lmdb_p->db_dbi, &dbkey, &data)) == 0)
92   {
93   *result = string_copyn(US data.mv_data, data.mv_size);
94   DEBUG(D_lookup) debug_printf_indent("LMDB: lookup result: %s\n", *result);
95   return OK;
96   }
97 else if (ret == MDB_NOTFOUND)
98   {
99   *errmsg = US"LMDB: lookup, no data found";
100   DEBUG(D_lookup) debug_printf_indent("%s\n", *errmsg);
101   return FAIL;
102   }
103 else
104   {
105   *errmsg = string_sprintf("LMDB: lookup error: %s", mdb_strerror(ret));
106   DEBUG(D_lookup) debug_printf_indent("%s\n", *errmsg);
107   return DEFER;
108   }
109 }
110
111
112 /*************************************************
113 *              Close entry point                 *
114 *************************************************/
115
116 static void
117 lmdb_close(void * handle)
118 {
119 Lmdbstrct * lmdb_p = handle;
120 MDB_env * db_env = mdb_txn_env(lmdb_p->txn);
121 mdb_txn_abort(lmdb_p->txn);
122 mdb_env_close(db_env);
123 }
124
125
126 /*************************************************
127 *         Version reporting entry point          *
128 *************************************************/
129
130 #include "../version.h"
131
132 void
133 lmdb_version_report(FILE * f)
134 {
135 fprintf(f, "Library version: LMDB: Compile: %d.%d.%d\n",
136     MDB_VERSION_MAJOR, MDB_VERSION_MINOR, MDB_VERSION_PATCH);
137 #ifdef DYNLOOKUP
138 fprintf(f, "                        Exim version %s\n", EXIM_VERSION_STR);
139 #endif
140 }
141
142 static lookup_info lmdb_lookup_info = {
143   .name = US"lmdb",                     /* lookup name */
144   .type = lookup_absfile,               /* query-style lookup */
145   .open = lmdb_open,                    /* open function */
146   .check = NULL,                        /* no check function */
147   .find = lmdb_find,                    /* find function */
148   .close = lmdb_close,                  /* close function */
149   .tidy = NULL,                         /* tidy function */
150   .quote = NULL,                        /* quoting function */
151   .version_report = lmdb_version_report           /* version reporting */
152 };
153
154 #ifdef DYNLOOKUP
155 # define lmdb_lookup_module_info _lookup_module_info
156 #endif /* DYNLOOKUP */
157
158 static lookup_info *_lookup_list[] = { &lmdb_lookup_info };
159 lookup_module_info lmdb_lookup_module_info = { LOOKUP_MODULE_INFO_MAGIC, _lookup_list, 1 };
160
161 #endif /* EXPERIMENTAL_LMDB */