5
/* dictionary manager interface to DBM files
7
/* #include <dict_dbm.h>
9
/* DICT *dict_dbm_open(path, open_flags, dict_flags)
15
/* dict_dbm_open() opens the named DBM database and makes it available
16
/* via the generic interface described in dict_open(3).
18
/* Fatal errors: cannot open file, file write error, out of memory.
20
/* dict(3) generic dictionary manager
21
/* ndbm(3) data base subroutines
25
/* The Secure Mailer license must be distributed with this software.
28
/* IBM T.J. Watson Research
30
/* Yorktown Heights, NY 10598, USA
46
#error "Error: you are including the Berkeley DB version of ndbm.h"
47
#error "To build with Postfix NDBM support, delete the Berkeley DB ndbm.h file"
52
/* Utility library. */
60
#include "stringops.h"
64
/* Application-specific. */
67
DICT dict; /* generic members */
68
DBM *dbm; /* open database */
71
/* dict_dbm_lookup - find database entry */
73
static const char *dict_dbm_lookup(DICT *dict, const char *name)
75
DICT_DBM *dict_dbm = (DICT_DBM *) dict;
79
const char *result = 0;
84
* Acquire an exclusive lock.
86
if ((dict->flags & DICT_FLAG_LOCK)
87
&& myflock(dict->lock_fd, INTERNAL_LOCK, MYFLOCK_OP_SHARED) < 0)
88
msg_fatal("%s: lock dictionary: %m", dict_dbm->dict.name);
91
* See if this DBM file was written with one null byte appended to key
94
if (dict->flags & DICT_FLAG_TRY1NULL) {
95
dbm_key.dptr = (void *) name;
96
dbm_key.dsize = strlen(name) + 1;
97
dbm_value = dbm_fetch(dict_dbm->dbm, dbm_key);
98
if (dbm_value.dptr != 0) {
99
dict->flags &= ~DICT_FLAG_TRY0NULL;
100
result = dbm_value.dptr;
105
* See if this DBM file was written with no null byte appended to key and
108
if (result == 0 && (dict->flags & DICT_FLAG_TRY0NULL)) {
109
dbm_key.dptr = (void *) name;
110
dbm_key.dsize = strlen(name);
111
dbm_value = dbm_fetch(dict_dbm->dbm, dbm_key);
112
if (dbm_value.dptr != 0) {
114
buf = vstring_alloc(10);
115
vstring_strncpy(buf, dbm_value.dptr, dbm_value.dsize);
116
dict->flags &= ~DICT_FLAG_TRY1NULL;
117
result = vstring_str(buf);
122
* Release the exclusive lock.
124
if ((dict->flags & DICT_FLAG_LOCK)
125
&& myflock(dict->lock_fd, INTERNAL_LOCK, MYFLOCK_OP_NONE) < 0)
126
msg_fatal("%s: unlock dictionary: %m", dict_dbm->dict.name);
131
/* dict_dbm_update - add or update database entry */
133
static void dict_dbm_update(DICT *dict, const char *name, const char *value)
135
DICT_DBM *dict_dbm = (DICT_DBM *) dict;
140
dbm_key.dptr = (void *) name;
141
dbm_value.dptr = (void *) value;
142
dbm_key.dsize = strlen(name);
143
dbm_value.dsize = strlen(value);
146
* If undecided about appending a null byte to key and value, choose a
147
* default depending on the platform.
149
if ((dict->flags & DICT_FLAG_TRY1NULL)
150
&& (dict->flags & DICT_FLAG_TRY0NULL)) {
151
#ifdef DBM_NO_TRAILING_NULL
152
dict->flags &= ~DICT_FLAG_TRY1NULL;
154
dict->flags &= ~DICT_FLAG_TRY0NULL;
159
* Optionally append a null byte to key and value.
161
if (dict->flags & DICT_FLAG_TRY1NULL) {
167
* Acquire an exclusive lock.
169
if ((dict->flags & DICT_FLAG_LOCK)
170
&& myflock(dict->lock_fd, INTERNAL_LOCK, MYFLOCK_OP_EXCLUSIVE) < 0)
171
msg_fatal("%s: lock dictionary: %m", dict_dbm->dict.name);
176
if ((status = dbm_store(dict_dbm->dbm, dbm_key, dbm_value,
177
(dict->flags & DICT_FLAG_DUP_REPLACE) ? DBM_REPLACE : DBM_INSERT)) < 0)
178
msg_fatal("error writing DBM database %s: %m", dict_dbm->dict.name);
180
if (dict->flags & DICT_FLAG_DUP_IGNORE)
182
else if (dict->flags & DICT_FLAG_DUP_WARN)
183
msg_warn("%s: duplicate entry: \"%s\"", dict_dbm->dict.name, name);
185
msg_fatal("%s: duplicate entry: \"%s\"", dict_dbm->dict.name, name);
189
* Release the exclusive lock.
191
if ((dict->flags & DICT_FLAG_LOCK)
192
&& myflock(dict->lock_fd, INTERNAL_LOCK, MYFLOCK_OP_NONE) < 0)
193
msg_fatal("%s: unlock dictionary: %m", dict_dbm->dict.name);
196
/* dict_dbm_delete - delete one entry from the dictionary */
198
static int dict_dbm_delete(DICT *dict, const char *name)
200
DICT_DBM *dict_dbm = (DICT_DBM *) dict;
206
* Acquire an exclusive lock.
208
if ((dict->flags & DICT_FLAG_LOCK)
209
&& myflock(dict->lock_fd, INTERNAL_LOCK, MYFLOCK_OP_EXCLUSIVE) < 0)
210
msg_fatal("%s: lock dictionary: %m", dict_dbm->dict.name);
213
* See if this DBM file was written with one null byte appended to key
216
if (dict->flags & DICT_FLAG_TRY1NULL) {
217
dbm_key.dptr = (void *) name;
218
dbm_key.dsize = strlen(name) + 1;
219
dbm_clearerr(dict_dbm->dbm);
220
if ((status = dbm_delete(dict_dbm->dbm, dbm_key)) < 0) {
221
if (dbm_error(dict_dbm->dbm) != 0) /* fatal error */
222
msg_fatal("error deleting from %s: %m", dict_dbm->dict.name);
223
status = 1; /* not found */
225
dict->flags &= ~DICT_FLAG_TRY0NULL; /* found */
230
* See if this DBM file was written with no null byte appended to key and
233
if (status > 0 && (dict->flags & DICT_FLAG_TRY0NULL)) {
234
dbm_key.dptr = (void *) name;
235
dbm_key.dsize = strlen(name);
236
dbm_clearerr(dict_dbm->dbm);
237
if ((status = dbm_delete(dict_dbm->dbm, dbm_key)) < 0) {
238
if (dbm_error(dict_dbm->dbm) != 0) /* fatal error */
239
msg_fatal("error deleting from %s: %m", dict_dbm->dict.name);
240
status = 1; /* not found */
242
dict->flags &= ~DICT_FLAG_TRY1NULL; /* found */
247
* Release the exclusive lock.
249
if ((dict->flags & DICT_FLAG_LOCK)
250
&& myflock(dict->lock_fd, INTERNAL_LOCK, MYFLOCK_OP_NONE) < 0)
251
msg_fatal("%s: unlock dictionary: %m", dict_dbm->dict.name);
256
/* traverse the dictionary */
258
static int dict_dbm_sequence(DICT *dict, int function,
259
const char **key, const char **value)
261
char *myname = "dict_dbm_sequence";
262
DICT_DBM *dict_dbm = (DICT_DBM *) dict;
266
static VSTRING *key_buf;
267
static VSTRING *value_buf;
270
* Acquire an exclusive lock.
272
if ((dict->flags & DICT_FLAG_LOCK)
273
&& myflock(dict->lock_fd, INTERNAL_LOCK, MYFLOCK_OP_EXCLUSIVE) < 0)
274
msg_fatal("%s: lock dictionary: %m", dict_dbm->dict.name);
277
* Determine and execute the seek function. It returns the key.
280
case DICT_SEQ_FUN_FIRST:
281
dbm_key = dbm_firstkey(dict_dbm->dbm);
283
case DICT_SEQ_FUN_NEXT:
284
dbm_key = dbm_nextkey(dict_dbm->dbm);
287
msg_panic("%s: invalid function: %d", myname, function);
291
* Release the exclusive lock.
293
if ((dict->flags & DICT_FLAG_LOCK)
294
&& myflock(dict->lock_fd, INTERNAL_LOCK, MYFLOCK_OP_NONE) < 0)
295
msg_fatal("%s: unlock dictionary: %m", dict_dbm->dict.name);
297
if (dbm_key.dptr != 0 && dbm_key.dsize > 0) {
300
* See if this DB file was written with one null byte appended to key
301
* an d value or not. If necessary, copy the key.
303
if (((char *) dbm_key.dptr)[dbm_key.dsize - 1] == 0) {
307
key_buf = vstring_alloc(10);
308
vstring_strncpy(key_buf, dbm_key.dptr, dbm_key.dsize);
309
*key = vstring_str(key_buf);
313
* Fetch the corresponding value.
315
dbm_value = dbm_fetch(dict_dbm->dbm, dbm_key);
317
if (dbm_value.dptr != 0 && dbm_value.dsize > 0) {
320
* See if this DB file was written with one null byte appended to
321
* key and value or not. If necessary, copy the key.
323
if (((char *) dbm_value.dptr)[dbm_value.dsize - 1] == 0) {
324
*value = dbm_value.dptr;
327
value_buf = vstring_alloc(10);
328
vstring_strncpy(value_buf, dbm_value.dptr, dbm_value.dsize);
329
*value = vstring_str(value_buf);
334
* Determine if we have hit the last record or an error
337
if (dbm_error(dict_dbm->dbm))
338
msg_fatal("error seeking %s: %m", dict_dbm->dict.name);
339
return (1); /* no error: eof/not found
340
* (should not happen!) */
345
* Determine if we have hit the last record or an error condition.
347
if (dbm_error(dict_dbm->dbm))
348
msg_fatal("error seeking %s: %m", dict_dbm->dict.name);
349
return (1); /* no error: eof/not found */
354
/* dict_dbm_close - disassociate from data base */
356
static void dict_dbm_close(DICT *dict)
358
DICT_DBM *dict_dbm = (DICT_DBM *) dict;
360
dbm_close(dict_dbm->dbm);
364
/* dict_dbm_open - open DBM data base */
366
DICT *dict_dbm_open(const char *path, int open_flags, int dict_flags)
375
* Note: DICT_FLAG_LOCK is used only by programs that do fine-grained (in
376
* the time domain) locking while accessing individual database records.
378
* Programs such as postmap/postalias use their own large-grained (in the
379
* time domain) locks while rewriting the entire file.
381
if (dict_flags & DICT_FLAG_LOCK) {
382
dbm_path = concatenate(path, ".dir", (char *) 0);
383
if ((lock_fd = open(dbm_path, open_flags, 0644)) < 0)
384
msg_fatal("open database %s: %m", dbm_path);
385
if (myflock(lock_fd, INTERNAL_LOCK, MYFLOCK_OP_SHARED) < 0)
386
msg_fatal("shared-lock database %s for open: %m", dbm_path);
390
* XXX SunOS 5.x has no const in dbm_open() prototype.
392
if ((dbm = dbm_open((char *) path, open_flags, 0644)) == 0)
393
msg_fatal("open database %s.{dir,pag}: %m", path);
395
if (dict_flags & DICT_FLAG_LOCK) {
396
if (myflock(lock_fd, INTERNAL_LOCK, MYFLOCK_OP_NONE) < 0)
397
msg_fatal("unlock database %s for open: %m", dbm_path);
398
if (close(lock_fd) < 0)
399
msg_fatal("close database %s: %m", dbm_path);
401
dict_dbm = (DICT_DBM *) dict_alloc(DICT_TYPE_DBM, path, sizeof(*dict_dbm));
402
dict_dbm->dict.lookup = dict_dbm_lookup;
403
dict_dbm->dict.update = dict_dbm_update;
404
dict_dbm->dict.delete = dict_dbm_delete;
405
dict_dbm->dict.sequence = dict_dbm_sequence;
406
dict_dbm->dict.close = dict_dbm_close;
407
dict_dbm->dict.lock_fd = dbm_dirfno(dbm);
408
dict_dbm->dict.stat_fd = dbm_pagfno(dbm);
409
if (dict_dbm->dict.lock_fd == dict_dbm->dict.stat_fd)
410
msg_fatal("open database %s: cannot support GDBM", path);
411
if (fstat(dict_dbm->dict.stat_fd, &st) < 0)
412
msg_fatal("dict_dbm_open: fstat: %m");
413
dict_dbm->dict.mtime = st.st_mtime;
416
* Warn if the source file is newer than the indexed file, except when
417
* the source file changed only seconds ago.
419
if ((dict_flags & DICT_FLAG_LOCK) != 0
420
&& stat(path, &st) == 0
421
&& st.st_mtime > dict_dbm->dict.mtime
422
&& st.st_mtime < time((time_t *) 0) - 100)
423
msg_warn("database %s is older than source file %s", dbm_path, path);
425
close_on_exec(dbm_pagfno(dbm), CLOSE_ON_EXEC);
426
close_on_exec(dbm_dirfno(dbm), CLOSE_ON_EXEC);
427
dict_dbm->dict.flags = dict_flags | DICT_FLAG_FIXED;
428
if ((dict_flags & (DICT_FLAG_TRY0NULL | DICT_FLAG_TRY1NULL)) == 0)
429
dict_dbm->dict.flags |= (DICT_FLAG_TRY0NULL | DICT_FLAG_TRY1NULL);
432
if ((dict_flags & DICT_FLAG_LOCK))
435
return (DICT_DEBUG (&dict_dbm->dict));