summaryrefslogtreecommitdiffstats
path: root/src/global/mkmap_open.c
blob: 9d15eec30a27f0557c590458e1cddc165ad45e01 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
/*++
/* NAME
/*	mkmap_open 3
/* SUMMARY
/*	create or rewrite database, generic interface
/* SYNOPSIS
/*	#include <mkmap.h>
/*
/*	typedef struct MKMAP {
/*	    DICT_OPEN_FN open;				/* dict_xx_open() */
/*	    DICT *dict;					/* dict_xx_open() result */
/*	    void    (*after_open) (struct MKMAP *);	/* may be null */
/*	    void    (*after_close) (struct MKMAP *);	/* may be null */
/*	    int     multi_writer;			/* multi-writer safe */
/*	} MKMAP;
/*
/*	MKMAP	*mkmap_open(type, path, open_flags, dict_flags)
/*	char	*type;
/*	char	*path;
/*	int	open_flags;
/*	int	dict_flags;
/*
/*	void	mkmap_append(mkmap, key, value, lineno)
/*	MKMAP	*mkmap;
/*	char	*key;
/*	char	*value;
/*	int	lineno;
/*
/*	void	mkmap_close(mkmap)
/*	MKMAP	*mkmap;
/*
/*	typedef MKMAP *(*MKMAP_OPEN_FN) (const char *);
/*	typedef MKMAP_OPEN_FN *(*MKMAP_OPEN_EXTEND_FN) (const char *);
/*
/*	void	mkmap_open_register(type, open_fn)
/*	const char *type;
/*	MKMAP_OPEN_FN open_fn;
/*
/*	MKMAP_OPEN_EXTEND_FN mkmap_open_extend(call_back)
/*	MKMAP_OPEN_EXTEND_FN call_back;
/* DESCRIPTION
/*	This module implements support for creating Postfix databases.
/*	It is a dict(3) wrapper that adds global locking to dict-level
/*	routines where appropriate.
/*
/*	mkmap_open() creates or truncates the named database, after
/*	appending the appropriate suffixes to the specified filename.
/*	Before the database is updated, it is locked for exclusive
/*	access, and signal delivery is suspended.
/*	See dict(3) for a description of \fBopen_flags\fR and
/*	\fBdict_flags\fR.  All errors are fatal.
/*
/*	mkmap_append() appends the named (key, value) pair to the
/*	database. Update errors are fatal; duplicate keys are ignored
/*	(but a warning is issued).
/*	\fBlineno\fR is used for diagnostics.
/*
/*	mkmap_close() closes the database, releases any locks,
/*	and resumes signal delivery. All errors are fatal.
/*
/*	mkmap_open_register() adds support for a new database type.
/*
/*	mkmap_open_extend() registers a call-back function that looks
/*	up the mkmap open() function for a database type that is not
/*	registered, or null in case of error. The result value is the
/*	last previously-registered call-back or null. A mkmap open()
/*	function is cached after it is looked up through this extension
/*	mechanism.
/* SEE ALSO
/*	sigdelay(3) suspend/resume signal delivery
/* LICENSE
/* .ad
/* .fi
/*	The Secure Mailer license must be distributed with this software.
/* AUTHOR(S)
/*	Wietse Venema
/*	IBM T.J. Watson Research
/*	P.O. Box 704
/*	Yorktown Heights, NY 10598, USA
/*--*/

/* System library. */

#include <sys_defs.h>
#include <unistd.h>
#include <string.h>

/* Utility library. */

#include <msg.h>
#include <htable.h>
#include <dict.h>
#include <dict_db.h>
#include <dict_cdb.h>
#include <dict_dbm.h>
#include <dict_lmdb.h>
#include <dict_sdbm.h>
#include <dict_proxy.h>
#include <dict_fail.h>
#include <sigdelay.h>
#include <mymalloc.h>
#include <stringops.h>

/* Global library. */

#include "mkmap.h"

 /*
  * Information about available database types. Here, we list only those map
  * types that support "bulk create" operations.
  * 
  * We use a different table (in dict_open.c and mail_dict.c) when querying maps
  * or when making incremental updates.
  */
typedef struct {
    const char *type;
    MKMAP_OPEN_FN before_open;
} MKMAP_OPEN_INFO;

static const MKMAP_OPEN_INFO mkmap_open_info[] = {
#ifndef USE_DYNAMIC_MAPS
#ifdef HAS_CDB
    DICT_TYPE_CDB, mkmap_cdb_open,
#endif
#ifdef HAS_SDBM
    DICT_TYPE_SDBM, mkmap_sdbm_open,
#endif
#ifdef HAS_LMDB
    DICT_TYPE_LMDB, mkmap_lmdb_open,
#endif
#endif					/* !USE_DYNAMIC_MAPS */
#ifdef HAS_DBM
    DICT_TYPE_DBM, mkmap_dbm_open,
#endif
#ifdef HAS_DB
    DICT_TYPE_HASH, mkmap_hash_open,
    DICT_TYPE_BTREE, mkmap_btree_open,
#endif
    DICT_TYPE_FAIL, mkmap_fail_open,
    0,
};

static HTABLE *mkmap_open_hash;

static MKMAP_OPEN_EXTEND_FN mkmap_open_extend_hook = 0;

/* mkmap_open_init - one-off initialization */

static void mkmap_open_init(void)
{
    static const char myname[] = "mkmap_open_init";
    const MKMAP_OPEN_INFO *mp;

    if (mkmap_open_hash != 0)
	msg_panic("%s: multiple initialization", myname);
    mkmap_open_hash = htable_create(10);

    for (mp = mkmap_open_info; mp->type; mp++)
	htable_enter(mkmap_open_hash, mp->type, (void *) mp);
}

/* mkmap_open_register - register dictionary type */

void    mkmap_open_register(const char *type, MKMAP_OPEN_FN open_fn)
{
    static const char myname[] = "mkmap_open_register";
    MKMAP_OPEN_INFO *mp;
    HTABLE_INFO *ht;

    if (mkmap_open_hash == 0)
	mkmap_open_init();
    if (htable_find(mkmap_open_hash, type))
	msg_panic("%s: database type exists: %s", myname, type);
    mp = (MKMAP_OPEN_INFO *) mymalloc(sizeof(*mp));
    mp->before_open = open_fn;
    ht = htable_enter(mkmap_open_hash, type, (void *) mp);
    mp->type = ht->key;
}

/* mkmap_open_extend - register alternate lookup function */

MKMAP_OPEN_EXTEND_FN mkmap_open_extend(MKMAP_OPEN_EXTEND_FN new_cb)
{
    MKMAP_OPEN_EXTEND_FN old_cb;

    old_cb = mkmap_open_extend_hook;
    mkmap_open_extend_hook = new_cb;
    return (old_cb);
}

/* mkmap_append - append entry to map */

#undef mkmap_append

void    mkmap_append(MKMAP *mkmap, const char *key, const char *value)
{
    DICT   *dict = mkmap->dict;

    if (dict_put(dict, key, value) != 0 && dict->error != 0)
	msg_fatal("%s:%s: update failed", dict->type, dict->name);
}

/* mkmap_close - close database */

void    mkmap_close(MKMAP *mkmap)
{

    /*
     * Close the database.
     */
    dict_close(mkmap->dict);

    /*
     * Do whatever special processing is needed after closing the database,
     * such as releasing a global exclusive lock on the database file.
     * Individual Postfix dict modules implement locking only for individual
     * record operations, because most Postfix applications don't need global
     * exclusive locks.
     */
    if (mkmap->after_close)
	mkmap->after_close(mkmap);

    /*
     * Resume signal delivery.
     */
    if (mkmap->multi_writer == 0)
	sigresume();

    /*
     * Cleanup.
     */
    myfree((void *) mkmap);
}

/* mkmap_open - create or truncate database */

MKMAP  *mkmap_open(const char *type, const char *path,
		           int open_flags, int dict_flags)
{
    MKMAP  *mkmap;
    const MKMAP_OPEN_INFO *mp;
    MKMAP_OPEN_FN open_fn;

    /*
     * Find out what map type to use.
     */
    if (mkmap_open_hash == 0)
	mkmap_open_init();
    if ((mp = (MKMAP_OPEN_INFO *) htable_find(mkmap_open_hash, type)) == 0) {
	if (mkmap_open_extend_hook != 0 &&
	    (open_fn = mkmap_open_extend_hook(type)) != 0) {
	    mkmap_open_register(type, open_fn);
	    mp = (MKMAP_OPEN_INFO *) htable_find(mkmap_open_hash, type);
	}
	if (mp == 0)
	    msg_fatal("unsupported map type for this operation: %s", type);
    }
    if (msg_verbose)
	msg_info("open %s %s", type, path);

    /*
     * Do whatever before-open initialization is needed, such as acquiring a
     * global exclusive lock on an existing database file. Individual Postfix
     * dict modules implement locking only for individual record operations,
     * because most Postfix applications don't need global exclusive locks.
     */
    mkmap = mp->before_open(path);

    /*
     * Delay signal delivery, so that we won't leave the database in an
     * inconsistent state if we can avoid it.
     */
    sigdelay();

    /*
     * Truncate the database upon open, and update it. Read-write mode is
     * needed because the underlying routines read as well as write. We
     * explicitly clobber lock_fd to trigger a fatal error when a map wants
     * to unlock the database after individual transactions: that would
     * result in race condition problems. We clobbber stat_fd as well,
     * because that, too, is used only for individual-transaction clients.
     */
    mkmap->dict = mkmap->open(path, open_flags, dict_flags);
    mkmap->dict->lock_fd = -1;			/* XXX just in case */
    mkmap->dict->stat_fd = -1;			/* XXX just in case */
    mkmap->dict->flags |= DICT_FLAG_DUP_WARN;
    mkmap->multi_writer = (mkmap->dict->flags & DICT_FLAG_MULTI_WRITER);

    /*
     * Do whatever post-open initialization is needed, such as acquiring a
     * global exclusive lock on a database file that did not exist.
     * Individual Postfix dict modules implement locking only for individual
     * record operations, because most Postfix applications don't need global
     * exclusive locks.
     */
    if (mkmap->after_open)
	mkmap->after_open(mkmap);

    /*
     * Wrap the dictionary for UTF-8 syntax checks and casefolding.
     */
    if ((mkmap->dict->flags & DICT_FLAG_UTF8_ACTIVE) == 0
	&& DICT_NEED_UTF8_ACTIVATION(util_utf8_enable, dict_flags))
	mkmap->dict = dict_utf8_activate(mkmap->dict);

    /*
     * Resume signal delivery if multi-writer safe.
     */
    if (mkmap->multi_writer)
	sigresume();

    return (mkmap);
}