1/*++
2/* NAME
3/*	mkmap_open 3
4/* SUMMARY
5/*	create or rewrite database, generic interface
6/* SYNOPSIS
7/*	#include <mkmap.h>
8/*
9/*	MKMAP	*mkmap_open(type, path, open_flags, dict_flags)
10/*	char	*type;
11/*	char	*path;
12/*	int	open_flags;
13/*	int	dict_flags;
14/*
15/*	void	mkmap_append(mkmap, key, value, lineno)
16/*	MKMAP	*mkmap;
17/*	char	*key;
18/*	char	*value;
19/*	int	lineno;
20/*
21/*	void	mkmap_close(mkmap)
22/*	MKMAP	*mkmap;
23/* DESCRIPTION
24/*	This module implements support for creating Postfix databases.
25/*	It is a dict(3) wrapper that adds global locking to dict-level
26/*	routines where appropriate.
27/*
28/*	mkmap_open() creates or truncates the named database, after
29/*	appending the appropriate suffixes to the specified filename.
30/*	Before the database is updated, it is locked for exclusive
31/*	access, and signal delivery is suspended.
32/*	See dict(3) for a description of \fBopen_flags\fR and \fBdict_flags\fR.
33/*	All errors are fatal.
34/*
35/*	mkmap_append() appends the named (key, value) pair to the
36/*	database. Update errors are fatal; duplicate keys are ignored
37/*	(but a warning is issued).
38/*	\fBlineno\fR is used for diagnostics.
39/*
40/*	mkmap_close() closes the database, releases any locks,
41/*	and resumes signal delivery. All errors are fatal.
42/* SEE ALSO
43/*	sigdelay(3) suspend/resume signal delivery
44/* LICENSE
45/* .ad
46/* .fi
47/*	The Secure Mailer license must be distributed with this software.
48/* AUTHOR(S)
49/*	Wietse Venema
50/*	IBM T.J. Watson Research
51/*	P.O. Box 704
52/*	Yorktown Heights, NY 10598, USA
53/*--*/
54
55/* System library. */
56
57#include <sys_defs.h>
58#include <unistd.h>
59#include <string.h>
60
61/* Utility library. */
62
63#include <msg.h>
64#include <dict.h>
65#include <dict_db.h>
66#include <dict_cdb.h>
67#include <dict_dbm.h>
68#include <dict_lmdb.h>
69#include <dict_sdbm.h>
70#include <dict_proxy.h>
71#include <dict_fail.h>
72#include <sigdelay.h>
73#include <mymalloc.h>
74
75/* Global library. */
76
77#include "mkmap.h"
78
79 /*
80  * Information about available database types. Here, we list only those map
81  * types that support "create" operations.
82  *
83  * We use a different table (in dict_open.c) when querying maps.
84  */
85typedef struct {
86    char   *type;
87    MKMAP  *(*before_open) (const char *);
88} MKMAP_OPEN_INFO;
89
90static const MKMAP_OPEN_INFO mkmap_types[] = {
91    DICT_TYPE_PROXY, mkmap_proxy_open,
92#ifdef HAS_CDB
93    DICT_TYPE_CDB, mkmap_cdb_open,
94#endif
95#ifdef HAS_SDBM
96    DICT_TYPE_SDBM, mkmap_sdbm_open,
97#endif
98#ifdef HAS_DBM
99    DICT_TYPE_DBM, mkmap_dbm_open,
100#endif
101#ifdef HAS_DB
102    DICT_TYPE_HASH, mkmap_hash_open,
103    DICT_TYPE_BTREE, mkmap_btree_open,
104#endif
105#ifdef HAS_LMDB
106    DICT_TYPE_LMDB, mkmap_lmdb_open,
107#endif
108    DICT_TYPE_FAIL, mkmap_fail_open,
109    0,
110};
111
112/* mkmap_append - append entry to map */
113
114#undef mkmap_append
115
116void    mkmap_append(MKMAP *mkmap, const char *key, const char *value)
117{
118    DICT   *dict = mkmap->dict;
119
120    if (dict_put(dict, key, value) != 0 && dict->error != 0)
121	msg_fatal("%s:%s: update failed", dict->type, dict->name);
122}
123
124/* mkmap_close - close database */
125
126void    mkmap_close(MKMAP *mkmap)
127{
128
129    /*
130     * Close the database.
131     */
132    dict_close(mkmap->dict);
133
134    /*
135     * Do whatever special processing is needed after closing the database,
136     * such as releasing a global exclusive lock on the database file.
137     * Individual Postfix dict modules implement locking only for individual
138     * record operations, because most Postfix applications don't need global
139     * exclusive locks.
140     */
141    if (mkmap->after_close)
142	mkmap->after_close(mkmap);
143
144    /*
145     * Resume signal delivery.
146     */
147    if (mkmap->multi_writer == 0)
148	sigresume();
149
150    /*
151     * Cleanup.
152     */
153    myfree((char *) mkmap);
154}
155
156/* mkmap_open - create or truncate database */
157
158MKMAP  *mkmap_open(const char *type, const char *path,
159		           int open_flags, int dict_flags)
160{
161    MKMAP  *mkmap;
162    const MKMAP_OPEN_INFO *mp;
163
164    /*
165     * Find out what map type to use.
166     */
167    for (mp = mkmap_types; /* void */ ; mp++) {
168	if (mp->type == 0)
169	    msg_fatal("unsupported map type for this operation: %s", type);
170	if (strcmp(type, mp->type) == 0)
171	    break;
172    }
173    if (msg_verbose)
174	msg_info("open %s %s", type, path);
175
176    /*
177     * Do whatever before-open initialization is needed, such as acquiring a
178     * global exclusive lock on an existing database file. Individual Postfix
179     * dict modules implement locking only for individual record operations,
180     * because most Postfix applications don't need global exclusive locks.
181     */
182    mkmap = mp->before_open(path);
183
184    /*
185     * Delay signal delivery, so that we won't leave the database in an
186     * inconsistent state if we can avoid it.
187     */
188    sigdelay();
189
190    /*
191     * Truncate the database upon open, and update it. Read-write mode is
192     * needed because the underlying routines read as well as write. We
193     * explicitly clobber lock_fd to trigger a fatal error when a map wants
194     * to unlock the database after individual transactions: that would
195     * result in race condition problems. We clobbber stat_fd as well,
196     * because that, too, is used only for individual-transaction clients.
197     */
198    mkmap->dict = mkmap->open(path, open_flags, dict_flags);
199    mkmap->dict->lock_fd = -1;			/* XXX just in case */
200    mkmap->dict->stat_fd = -1;			/* XXX just in case */
201    mkmap->dict->flags |= DICT_FLAG_DUP_WARN;
202    mkmap->multi_writer = (mkmap->dict->flags & DICT_FLAG_MULTI_WRITER);
203
204    /*
205     * Do whatever post-open initialization is needed, such as acquiring a
206     * global exclusive lock on a database file that did not exist.
207     * Individual Postfix dict modules implement locking only for individual
208     * record operations, because most Postfix applications don't need global
209     * exclusive locks.
210     */
211    if (mkmap->after_open)
212	mkmap->after_open(mkmap);
213
214    /*
215     * Resume signal delivery if multi-writer safe.
216     */
217    if (mkmap->multi_writer)
218	sigresume();
219
220    return (mkmap);
221}
222