No empty .Rs/.Re
[netbsd-mini2440.git] / external / ibm-public / postfix / dist / src / util / dict_dbm.c
bloba58691781b7753a4862fdea195e2fd7a74eadfdf
1 /* $NetBSD$ */
3 /*++
4 /* NAME
5 /* dict_dbm 3
6 /* SUMMARY
7 /* dictionary manager interface to DBM files
8 /* SYNOPSIS
9 /* #include <dict_dbm.h>
11 /* DICT *dict_dbm_open(path, open_flags, dict_flags)
12 /* const char *name;
13 /* const char *path;
14 /* int open_flags;
15 /* int dict_flags;
16 /* DESCRIPTION
17 /* dict_dbm_open() opens the named DBM database and makes it available
18 /* via the generic interface described in dict_open(3).
19 /* DIAGNOSTICS
20 /* Fatal errors: cannot open file, file write error, out of memory.
21 /* SEE ALSO
22 /* dict(3) generic dictionary manager
23 /* ndbm(3) data base subroutines
24 /* LICENSE
25 /* .ad
26 /* .fi
27 /* The Secure Mailer license must be distributed with this software.
28 /* AUTHOR(S)
29 /* Wietse Venema
30 /* IBM T.J. Watson Research
31 /* P.O. Box 704
32 /* Yorktown Heights, NY 10598, USA
33 /*--*/
35 #include "sys_defs.h"
37 #ifdef HAS_DBM
39 /* System library. */
41 #include <sys/stat.h>
42 #ifdef PATH_NDBM_H
43 #include PATH_NDBM_H
44 #else
45 #include <ndbm.h>
46 #endif
47 #ifdef R_FIRST
48 #error "Error: you are including the Berkeley DB version of ndbm.h"
49 #error "To build with Postfix NDBM support, delete the Berkeley DB ndbm.h file"
50 #endif
51 #include <string.h>
52 #include <unistd.h>
54 /* Utility library. */
56 #include "msg.h"
57 #include "mymalloc.h"
58 #include "htable.h"
59 #include "iostuff.h"
60 #include "vstring.h"
61 #include "myflock.h"
62 #include "stringops.h"
63 #include "dict.h"
64 #include "dict_dbm.h"
66 /* Application-specific. */
68 typedef struct {
69 DICT dict; /* generic members */
70 DBM *dbm; /* open database */
71 VSTRING *key_buf; /* key buffer */
72 VSTRING *val_buf; /* result buffer */
73 } DICT_DBM;
75 #define SCOPY(buf, data, size) \
76 vstring_str(vstring_strncpy(buf ? buf : (buf = vstring_alloc(10)), data, size))
78 /* dict_dbm_lookup - find database entry */
80 static const char *dict_dbm_lookup(DICT *dict, const char *name)
82 DICT_DBM *dict_dbm = (DICT_DBM *) dict;
83 datum dbm_key;
84 datum dbm_value;
85 const char *result = 0;
88 * Sanity check.
90 if ((dict->flags & (DICT_FLAG_TRY1NULL | DICT_FLAG_TRY0NULL)) == 0)
91 msg_panic("dict_dbm_lookup: no DICT_FLAG_TRY1NULL | DICT_FLAG_TRY0NULL flag");
93 dict_errno = 0;
96 * Optionally fold the key.
98 if (dict->flags & DICT_FLAG_FOLD_FIX) {
99 if (dict->fold_buf == 0)
100 dict->fold_buf = vstring_alloc(10);
101 vstring_strcpy(dict->fold_buf, name);
102 name = lowercase(vstring_str(dict->fold_buf));
106 * Acquire an exclusive lock.
108 if ((dict->flags & DICT_FLAG_LOCK)
109 && myflock(dict->lock_fd, INTERNAL_LOCK, MYFLOCK_OP_SHARED) < 0)
110 msg_fatal("%s: lock dictionary: %m", dict_dbm->dict.name);
113 * See if this DBM file was written with one null byte appended to key
114 * and value.
116 if (dict->flags & DICT_FLAG_TRY1NULL) {
117 dbm_key.dptr = (void *) name;
118 dbm_key.dsize = strlen(name) + 1;
119 dbm_value = dbm_fetch(dict_dbm->dbm, dbm_key);
120 if (dbm_value.dptr != 0) {
121 dict->flags &= ~DICT_FLAG_TRY0NULL;
122 result = SCOPY(dict_dbm->val_buf, dbm_value.dptr, dbm_value.dsize);
127 * See if this DBM file was written with no null byte appended to key and
128 * value.
130 if (result == 0 && (dict->flags & DICT_FLAG_TRY0NULL)) {
131 dbm_key.dptr = (void *) name;
132 dbm_key.dsize = strlen(name);
133 dbm_value = dbm_fetch(dict_dbm->dbm, dbm_key);
134 if (dbm_value.dptr != 0) {
135 dict->flags &= ~DICT_FLAG_TRY1NULL;
136 result = SCOPY(dict_dbm->val_buf, dbm_value.dptr, dbm_value.dsize);
141 * Release the exclusive lock.
143 if ((dict->flags & DICT_FLAG_LOCK)
144 && myflock(dict->lock_fd, INTERNAL_LOCK, MYFLOCK_OP_NONE) < 0)
145 msg_fatal("%s: unlock dictionary: %m", dict_dbm->dict.name);
147 return (result);
150 /* dict_dbm_update - add or update database entry */
152 static void dict_dbm_update(DICT *dict, const char *name, const char *value)
154 DICT_DBM *dict_dbm = (DICT_DBM *) dict;
155 datum dbm_key;
156 datum dbm_value;
157 int status;
160 * Sanity check.
162 if ((dict->flags & (DICT_FLAG_TRY1NULL | DICT_FLAG_TRY0NULL)) == 0)
163 msg_panic("dict_dbm_update: no DICT_FLAG_TRY1NULL | DICT_FLAG_TRY0NULL flag");
166 * Optionally fold the key.
168 if (dict->flags & DICT_FLAG_FOLD_FIX) {
169 if (dict->fold_buf == 0)
170 dict->fold_buf = vstring_alloc(10);
171 vstring_strcpy(dict->fold_buf, name);
172 name = lowercase(vstring_str(dict->fold_buf));
175 dbm_key.dptr = (void *) name;
176 dbm_value.dptr = (void *) value;
177 dbm_key.dsize = strlen(name);
178 dbm_value.dsize = strlen(value);
181 * If undecided about appending a null byte to key and value, choose a
182 * default depending on the platform.
184 if ((dict->flags & DICT_FLAG_TRY1NULL)
185 && (dict->flags & DICT_FLAG_TRY0NULL)) {
186 #ifdef DBM_NO_TRAILING_NULL
187 dict->flags &= ~DICT_FLAG_TRY1NULL;
188 #else
189 dict->flags &= ~DICT_FLAG_TRY0NULL;
190 #endif
194 * Optionally append a null byte to key and value.
196 if (dict->flags & DICT_FLAG_TRY1NULL) {
197 dbm_key.dsize++;
198 dbm_value.dsize++;
202 * Acquire an exclusive lock.
204 if ((dict->flags & DICT_FLAG_LOCK)
205 && myflock(dict->lock_fd, INTERNAL_LOCK, MYFLOCK_OP_EXCLUSIVE) < 0)
206 msg_fatal("%s: lock dictionary: %m", dict_dbm->dict.name);
209 * Do the update.
211 if ((status = dbm_store(dict_dbm->dbm, dbm_key, dbm_value,
212 (dict->flags & DICT_FLAG_DUP_REPLACE) ? DBM_REPLACE : DBM_INSERT)) < 0)
213 msg_fatal("error writing DBM database %s: %m", dict_dbm->dict.name);
214 if (status) {
215 if (dict->flags & DICT_FLAG_DUP_IGNORE)
216 /* void */ ;
217 else if (dict->flags & DICT_FLAG_DUP_WARN)
218 msg_warn("%s: duplicate entry: \"%s\"", dict_dbm->dict.name, name);
219 else
220 msg_fatal("%s: duplicate entry: \"%s\"", dict_dbm->dict.name, name);
224 * Release the exclusive lock.
226 if ((dict->flags & DICT_FLAG_LOCK)
227 && myflock(dict->lock_fd, INTERNAL_LOCK, MYFLOCK_OP_NONE) < 0)
228 msg_fatal("%s: unlock dictionary: %m", dict_dbm->dict.name);
231 /* dict_dbm_delete - delete one entry from the dictionary */
233 static int dict_dbm_delete(DICT *dict, const char *name)
235 DICT_DBM *dict_dbm = (DICT_DBM *) dict;
236 datum dbm_key;
237 int status = 1;
240 * Sanity check.
242 if ((dict->flags & (DICT_FLAG_TRY1NULL | DICT_FLAG_TRY0NULL)) == 0)
243 msg_panic("dict_dbm_delete: no DICT_FLAG_TRY1NULL | DICT_FLAG_TRY0NULL flag");
246 * Optionally fold the key.
248 if (dict->flags & DICT_FLAG_FOLD_FIX) {
249 if (dict->fold_buf == 0)
250 dict->fold_buf = vstring_alloc(10);
251 vstring_strcpy(dict->fold_buf, name);
252 name = lowercase(vstring_str(dict->fold_buf));
256 * Acquire an exclusive lock.
258 if ((dict->flags & DICT_FLAG_LOCK)
259 && myflock(dict->lock_fd, INTERNAL_LOCK, MYFLOCK_OP_EXCLUSIVE) < 0)
260 msg_fatal("%s: lock dictionary: %m", dict_dbm->dict.name);
263 * See if this DBM file was written with one null byte appended to key
264 * and value.
266 if (dict->flags & DICT_FLAG_TRY1NULL) {
267 dbm_key.dptr = (void *) name;
268 dbm_key.dsize = strlen(name) + 1;
269 dbm_clearerr(dict_dbm->dbm);
270 if ((status = dbm_delete(dict_dbm->dbm, dbm_key)) < 0) {
271 if (dbm_error(dict_dbm->dbm) != 0) /* fatal error */
272 msg_fatal("error deleting from %s: %m", dict_dbm->dict.name);
273 status = 1; /* not found */
274 } else {
275 dict->flags &= ~DICT_FLAG_TRY0NULL; /* found */
280 * See if this DBM file was written with no null byte appended to key and
281 * value.
283 if (status > 0 && (dict->flags & DICT_FLAG_TRY0NULL)) {
284 dbm_key.dptr = (void *) name;
285 dbm_key.dsize = strlen(name);
286 dbm_clearerr(dict_dbm->dbm);
287 if ((status = dbm_delete(dict_dbm->dbm, dbm_key)) < 0) {
288 if (dbm_error(dict_dbm->dbm) != 0) /* fatal error */
289 msg_fatal("error deleting from %s: %m", dict_dbm->dict.name);
290 status = 1; /* not found */
291 } else {
292 dict->flags &= ~DICT_FLAG_TRY1NULL; /* found */
297 * Release the exclusive lock.
299 if ((dict->flags & DICT_FLAG_LOCK)
300 && myflock(dict->lock_fd, INTERNAL_LOCK, MYFLOCK_OP_NONE) < 0)
301 msg_fatal("%s: unlock dictionary: %m", dict_dbm->dict.name);
303 return (status);
306 /* traverse the dictionary */
308 static int dict_dbm_sequence(DICT *dict, int function,
309 const char **key, const char **value)
311 const char *myname = "dict_dbm_sequence";
312 DICT_DBM *dict_dbm = (DICT_DBM *) dict;
313 datum dbm_key;
314 datum dbm_value;
317 * Acquire a shared lock.
319 if ((dict->flags & DICT_FLAG_LOCK)
320 && myflock(dict->lock_fd, INTERNAL_LOCK, MYFLOCK_OP_SHARED) < 0)
321 msg_fatal("%s: lock dictionary: %m", dict_dbm->dict.name);
324 * Determine and execute the seek function. It returns the key.
326 switch (function) {
327 case DICT_SEQ_FUN_FIRST:
328 dbm_key = dbm_firstkey(dict_dbm->dbm);
329 break;
330 case DICT_SEQ_FUN_NEXT:
331 dbm_key = dbm_nextkey(dict_dbm->dbm);
332 break;
333 default:
334 msg_panic("%s: invalid function: %d", myname, function);
337 if (dbm_key.dptr != 0 && dbm_key.dsize > 0) {
340 * Copy the key so that it is guaranteed null terminated.
342 *key = SCOPY(dict_dbm->key_buf, dbm_key.dptr, dbm_key.dsize);
345 * Fetch the corresponding value.
347 dbm_value = dbm_fetch(dict_dbm->dbm, dbm_key);
349 if (dbm_value.dptr != 0 && dbm_value.dsize > 0) {
352 * Copy the value so that it is guaranteed null terminated.
354 *value = SCOPY(dict_dbm->val_buf, dbm_value.dptr, dbm_value.dsize);
355 } else {
358 * Determine if we have hit the last record or an error
359 * condition.
361 if (dbm_error(dict_dbm->dbm))
362 msg_fatal("error seeking %s: %m", dict_dbm->dict.name);
363 return (1); /* no error: eof/not found
364 * (should not happen!) */
366 } else {
369 * Determine if we have hit the last record or an error condition.
371 if (dbm_error(dict_dbm->dbm))
372 msg_fatal("error seeking %s: %m", dict_dbm->dict.name);
373 return (1); /* no error: eof/not found */
377 * Release the shared lock.
379 if ((dict->flags & DICT_FLAG_LOCK)
380 && myflock(dict->lock_fd, INTERNAL_LOCK, MYFLOCK_OP_NONE) < 0)
381 msg_fatal("%s: unlock dictionary: %m", dict_dbm->dict.name);
383 return (0);
386 /* dict_dbm_close - disassociate from data base */
388 static void dict_dbm_close(DICT *dict)
390 DICT_DBM *dict_dbm = (DICT_DBM *) dict;
392 dbm_close(dict_dbm->dbm);
393 if (dict_dbm->key_buf)
394 vstring_free(dict_dbm->key_buf);
395 if (dict_dbm->val_buf)
396 vstring_free(dict_dbm->val_buf);
397 if (dict->fold_buf)
398 vstring_free(dict->fold_buf);
399 dict_free(dict);
402 /* dict_dbm_open - open DBM data base */
404 DICT *dict_dbm_open(const char *path, int open_flags, int dict_flags)
406 DICT_DBM *dict_dbm;
407 struct stat st;
408 DBM *dbm;
409 char *dbm_path;
410 int lock_fd;
413 * Note: DICT_FLAG_LOCK is used only by programs that do fine-grained (in
414 * the time domain) locking while accessing individual database records.
416 * Programs such as postmap/postalias use their own large-grained (in the
417 * time domain) locks while rewriting the entire file.
419 if (dict_flags & DICT_FLAG_LOCK) {
420 dbm_path = concatenate(path, ".dir", (char *) 0);
421 if ((lock_fd = open(dbm_path, open_flags, 0644)) < 0)
422 msg_fatal("open database %s: %m", dbm_path);
423 if (myflock(lock_fd, INTERNAL_LOCK, MYFLOCK_OP_SHARED) < 0)
424 msg_fatal("shared-lock database %s for open: %m", dbm_path);
428 * XXX SunOS 5.x has no const in dbm_open() prototype.
430 if ((dbm = dbm_open((char *) path, open_flags, 0644)) == 0)
431 msg_fatal("open database %s.{dir,pag}: %m", path);
433 if (dict_flags & DICT_FLAG_LOCK) {
434 if (myflock(lock_fd, INTERNAL_LOCK, MYFLOCK_OP_NONE) < 0)
435 msg_fatal("unlock database %s for open: %m", dbm_path);
436 if (close(lock_fd) < 0)
437 msg_fatal("close database %s: %m", dbm_path);
439 dict_dbm = (DICT_DBM *) dict_alloc(DICT_TYPE_DBM, path, sizeof(*dict_dbm));
440 dict_dbm->dict.lookup = dict_dbm_lookup;
441 dict_dbm->dict.update = dict_dbm_update;
442 dict_dbm->dict.delete = dict_dbm_delete;
443 dict_dbm->dict.sequence = dict_dbm_sequence;
444 dict_dbm->dict.close = dict_dbm_close;
445 dict_dbm->dict.lock_fd = dbm_dirfno(dbm);
446 dict_dbm->dict.stat_fd = dbm_pagfno(dbm);
447 if (dict_dbm->dict.lock_fd == dict_dbm->dict.stat_fd)
448 msg_fatal("open database %s: cannot support GDBM", path);
449 if (fstat(dict_dbm->dict.stat_fd, &st) < 0)
450 msg_fatal("dict_dbm_open: fstat: %m");
451 dict_dbm->dict.mtime = st.st_mtime;
454 * Warn if the source file is newer than the indexed file, except when
455 * the source file changed only seconds ago.
457 if ((dict_flags & DICT_FLAG_LOCK) != 0
458 && stat(path, &st) == 0
459 && st.st_mtime > dict_dbm->dict.mtime
460 && st.st_mtime < time((time_t *) 0) - 100)
461 msg_warn("database %s is older than source file %s", dbm_path, path);
463 close_on_exec(dbm_pagfno(dbm), CLOSE_ON_EXEC);
464 close_on_exec(dbm_dirfno(dbm), CLOSE_ON_EXEC);
465 dict_dbm->dict.flags = dict_flags | DICT_FLAG_FIXED;
466 if ((dict_flags & (DICT_FLAG_TRY0NULL | DICT_FLAG_TRY1NULL)) == 0)
467 dict_dbm->dict.flags |= (DICT_FLAG_TRY0NULL | DICT_FLAG_TRY1NULL);
468 if (dict_flags & DICT_FLAG_FOLD_FIX)
469 dict_dbm->dict.fold_buf = vstring_alloc(10);
470 dict_dbm->dbm = dbm;
471 dict_dbm->key_buf = 0;
472 dict_dbm->val_buf = 0;
474 if ((dict_flags & DICT_FLAG_LOCK))
475 myfree(dbm_path);
477 return (DICT_DEBUG (&dict_dbm->dict));
480 #endif