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