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