No empty .Rs/.Re
[netbsd-mini2440.git] / external / ibm-public / postfix / dist / src / util / dict_db.c
blob94107241b760951f5eef6969f9f6316a115d304c
1 /* $NetBSD$ */
3 /*++
4 /* NAME
5 /* dict_db 3
6 /* SUMMARY
7 /* dictionary manager interface to DB files
8 /* SYNOPSIS
9 /* #include <dict_db.h>
11 /* int dict_db_cache_size;
13 /* DICT *dict_hash_open(path, open_flags, dict_flags)
14 /* const char *path;
15 /* int open_flags;
16 /* int dict_flags;
18 /* DICT *dict_btree_open(path, open_flags, dict_flags)
19 /* const char *path;
20 /* int open_flags;
21 /* int dict_flags;
22 /* DESCRIPTION
23 /* dict_XXX_open() opens the specified DB database. The result is
24 /* a pointer to a structure that can be used to access the dictionary
25 /* using the generic methods documented in dict_open(3).
27 /* The dict_db_cache_size variable specifies a non-default per-table
28 /* I/O buffer size. The default buffer size is adequate for reading.
29 /* For better performance while creating a large table, specify a large
30 /* buffer size before opening the file.
32 /* Arguments:
33 /* .IP path
34 /* The database pathname, not including the ".db" suffix.
35 /* .IP open_flags
36 /* Flags passed to dbopen().
37 /* .IP dict_flags
38 /* Flags used by the dictionary interface.
39 /* SEE ALSO
40 /* dict(3) generic dictionary manager
41 /* DIAGNOSTICS
42 /* Fatal errors: cannot open file, write error, out of memory.
43 /* LICENSE
44 /* .ad
45 /* .fi
46 /* The Secure Mailer license must be distributed with this software.
47 /* AUTHOR(S)
48 /* Wietse Venema
49 /* IBM T.J. Watson Research
50 /* P.O. Box 704
51 /* Yorktown Heights, NY 10598, USA
52 /*--*/
54 #include "sys_defs.h"
56 #ifdef HAS_DB
58 /* System library. */
60 #include <sys/stat.h>
61 #include <limits.h>
62 #ifdef PATH_DB_H
63 #include PATH_DB_H
64 #else
65 #include <db.h>
66 #endif
67 #include <string.h>
68 #include <unistd.h>
69 #include <errno.h>
71 #if defined(_DB_185_H_) && defined(USE_FCNTL_LOCK)
72 #error "Error: this system must not use the db 1.85 compatibility interface"
73 #endif
75 #ifndef DB_VERSION_MAJOR
76 #define DB_VERSION_MAJOR 1
77 #define DICT_DB_GET(db, key, val, flag) db->get(db, key, val, flag)
78 #define DICT_DB_PUT(db, key, val, flag) db->put(db, key, val, flag)
79 #define DICT_DB_DEL(db, key, flag) db->del(db, key, flag)
80 #define DICT_DB_SYNC(db, flag) db->sync(db, flag)
81 #define DICT_DB_CLOSE(db) db->close(db)
82 #define DONT_CLOBBER R_NOOVERWRITE
83 #endif
85 #if DB_VERSION_MAJOR > 1
86 #define DICT_DB_GET(db, key, val, flag) sanitize(db->get(db, 0, key, val, flag))
87 #define DICT_DB_PUT(db, key, val, flag) sanitize(db->put(db, 0, key, val, flag))
88 #define DICT_DB_DEL(db, key, flag) sanitize(db->del(db, 0, key, flag))
89 #define DICT_DB_SYNC(db, flag) ((errno = db->sync(db, flag)) ? -1 : 0)
90 #define DICT_DB_CLOSE(db) ((errno = db->close(db, 0)) ? -1 : 0)
91 #define DONT_CLOBBER DB_NOOVERWRITE
92 #endif
94 #if (DB_VERSION_MAJOR == 2 && DB_VERSION_MINOR < 6)
95 #define DICT_DB_CURSOR(db, curs) (db)->cursor((db), NULL, (curs))
96 #else
97 #define DICT_DB_CURSOR(db, curs) (db)->cursor((db), NULL, (curs), 0)
98 #endif
100 #ifndef DB_FCNTL_LOCKING
101 #define DB_FCNTL_LOCKING 0
102 #endif
104 /* Utility library. */
106 #include "msg.h"
107 #include "mymalloc.h"
108 #include "vstring.h"
109 #include "stringops.h"
110 #include "iostuff.h"
111 #include "myflock.h"
112 #include "dict.h"
113 #include "dict_db.h"
115 /* Application-specific. */
117 typedef struct {
118 DICT dict; /* generic members */
119 DB *db; /* open db file */
120 #if DB_VERSION_MAJOR > 1
121 DBC *cursor; /* dict_db_sequence() */
122 #endif
123 VSTRING *key_buf; /* key result */
124 VSTRING *val_buf; /* value result */
125 } DICT_DB;
127 #define SCOPY(buf, data, size) \
128 vstring_str(vstring_strncpy(buf ? buf : (buf = vstring_alloc(10)), data, size))
131 * You can override the default dict_db_cache_size setting before calling
132 * dict_hash_open() or dict_btree_open(). This is done in mkmap_db_open() to
133 * set a larger memory pool for database (re)builds.
135 * XXX This should be specified via the DICT interface so that it becomes an
136 * object property, instead of being specified by poking a global variable
137 * so that it becomes a class property.
139 int dict_db_cache_size = (128 * 1024); /* 128K default memory pool */
141 #define DICT_DB_NELM 4096
143 #if DB_VERSION_MAJOR > 1
145 /* sanitize - sanitize db_get/put/del result */
147 static int sanitize(int status)
151 * XXX This is unclean but avoids a lot of clutter elsewhere. Categorize
152 * results into non-fatal errors (i.e., errors that we can deal with),
153 * success, or fatal error (i.e., all other errors).
155 switch (status) {
157 case DB_NOTFOUND: /* get, del */
158 case DB_KEYEXIST: /* put */
159 return (1); /* non-fatal */
161 case 0:
162 return (0); /* success */
164 case DB_KEYEMPTY: /* get, others? */
165 status = EINVAL;
166 /* FALLTHROUGH */
167 default:
168 errno = status;
169 return (-1); /* fatal */
173 #endif
175 /* dict_db_lookup - find database entry */
177 static const char *dict_db_lookup(DICT *dict, const char *name)
179 DICT_DB *dict_db = (DICT_DB *) dict;
180 DB *db = dict_db->db;
181 DBT db_key;
182 DBT db_value;
183 int status;
184 const char *result = 0;
187 * Sanity check.
189 if ((dict->flags & (DICT_FLAG_TRY1NULL | DICT_FLAG_TRY0NULL)) == 0)
190 msg_panic("dict_db_lookup: no DICT_FLAG_TRY1NULL | DICT_FLAG_TRY0NULL flag");
192 dict_errno = 0;
193 memset(&db_key, 0, sizeof(db_key));
194 memset(&db_value, 0, sizeof(db_value));
197 * Optionally fold the key.
199 if (dict->flags & DICT_FLAG_FOLD_FIX) {
200 if (dict->fold_buf == 0)
201 dict->fold_buf = vstring_alloc(10);
202 vstring_strcpy(dict->fold_buf, name);
203 name = lowercase(vstring_str(dict->fold_buf));
207 * Acquire a shared lock.
209 if ((dict->flags & DICT_FLAG_LOCK)
210 && myflock(dict->lock_fd, INTERNAL_LOCK, MYFLOCK_OP_SHARED) < 0)
211 msg_fatal("%s: lock dictionary: %m", dict_db->dict.name);
214 * See if this DB file was written with one null byte appended to key and
215 * value.
217 if (dict->flags & DICT_FLAG_TRY1NULL) {
218 db_key.data = (void *) name;
219 db_key.size = strlen(name) + 1;
220 if ((status = DICT_DB_GET(db, &db_key, &db_value, 0)) < 0)
221 msg_fatal("error reading %s: %m", dict_db->dict.name);
222 if (status == 0) {
223 dict->flags &= ~DICT_FLAG_TRY0NULL;
224 result = SCOPY(dict_db->val_buf, db_value.data, db_value.size);
229 * See if this DB file was written with no null byte appended to key and
230 * value.
232 if (result == 0 && (dict->flags & DICT_FLAG_TRY0NULL)) {
233 db_key.data = (void *) name;
234 db_key.size = strlen(name);
235 if ((status = DICT_DB_GET(db, &db_key, &db_value, 0)) < 0)
236 msg_fatal("error reading %s: %m", dict_db->dict.name);
237 if (status == 0) {
238 dict->flags &= ~DICT_FLAG_TRY1NULL;
239 result = SCOPY(dict_db->val_buf, db_value.data, db_value.size);
244 * Release the shared lock.
246 if ((dict->flags & DICT_FLAG_LOCK)
247 && myflock(dict->lock_fd, INTERNAL_LOCK, MYFLOCK_OP_NONE) < 0)
248 msg_fatal("%s: unlock dictionary: %m", dict_db->dict.name);
250 return (result);
253 /* dict_db_update - add or update database entry */
255 static void dict_db_update(DICT *dict, const char *name, const char *value)
257 DICT_DB *dict_db = (DICT_DB *) dict;
258 DB *db = dict_db->db;
259 DBT db_key;
260 DBT db_value;
261 int status;
264 * Sanity check.
266 if ((dict->flags & (DICT_FLAG_TRY1NULL | DICT_FLAG_TRY0NULL)) == 0)
267 msg_panic("dict_db_update: no DICT_FLAG_TRY1NULL | DICT_FLAG_TRY0NULL flag");
270 * Optionally fold the key.
272 if (dict->flags & DICT_FLAG_FOLD_FIX) {
273 if (dict->fold_buf == 0)
274 dict->fold_buf = vstring_alloc(10);
275 vstring_strcpy(dict->fold_buf, name);
276 name = lowercase(vstring_str(dict->fold_buf));
278 memset(&db_key, 0, sizeof(db_key));
279 memset(&db_value, 0, sizeof(db_value));
280 db_key.data = (void *) name;
281 db_value.data = (void *) value;
282 db_key.size = strlen(name);
283 db_value.size = strlen(value);
286 * If undecided about appending a null byte to key and value, choose a
287 * default depending on the platform.
289 if ((dict->flags & DICT_FLAG_TRY1NULL)
290 && (dict->flags & DICT_FLAG_TRY0NULL)) {
291 #ifdef DB_NO_TRAILING_NULL
292 dict->flags &= ~DICT_FLAG_TRY1NULL;
293 #else
294 dict->flags &= ~DICT_FLAG_TRY0NULL;
295 #endif
299 * Optionally append a null byte to key and value.
301 if (dict->flags & DICT_FLAG_TRY1NULL) {
302 db_key.size++;
303 db_value.size++;
307 * Acquire an exclusive lock.
309 if ((dict->flags & DICT_FLAG_LOCK)
310 && myflock(dict->lock_fd, INTERNAL_LOCK, MYFLOCK_OP_EXCLUSIVE) < 0)
311 msg_fatal("%s: lock dictionary: %m", dict_db->dict.name);
314 * Do the update.
316 if ((status = DICT_DB_PUT(db, &db_key, &db_value,
317 (dict->flags & DICT_FLAG_DUP_REPLACE) ? 0 : DONT_CLOBBER)) < 0)
318 msg_fatal("error writing %s: %m", dict_db->dict.name);
319 if (status) {
320 if (dict->flags & DICT_FLAG_DUP_IGNORE)
321 /* void */ ;
322 else if (dict->flags & DICT_FLAG_DUP_WARN)
323 msg_warn("%s: duplicate entry: \"%s\"", dict_db->dict.name, name);
324 else
325 msg_fatal("%s: duplicate entry: \"%s\"", dict_db->dict.name, name);
327 if (dict->flags & DICT_FLAG_SYNC_UPDATE)
328 if (DICT_DB_SYNC(db, 0) < 0)
329 msg_fatal("%s: flush dictionary: %m", dict_db->dict.name);
332 * Release the exclusive lock.
334 if ((dict->flags & DICT_FLAG_LOCK)
335 && myflock(dict->lock_fd, INTERNAL_LOCK, MYFLOCK_OP_NONE) < 0)
336 msg_fatal("%s: unlock dictionary: %m", dict_db->dict.name);
339 /* delete one entry from the dictionary */
341 static int dict_db_delete(DICT *dict, const char *name)
343 DICT_DB *dict_db = (DICT_DB *) dict;
344 DB *db = dict_db->db;
345 DBT db_key;
346 int status = 1;
347 int flags = 0;
350 * Sanity check.
352 if ((dict->flags & (DICT_FLAG_TRY1NULL | DICT_FLAG_TRY0NULL)) == 0)
353 msg_panic("dict_db_delete: no DICT_FLAG_TRY1NULL | DICT_FLAG_TRY0NULL flag");
356 * Optionally fold the key.
358 if (dict->flags & DICT_FLAG_FOLD_FIX) {
359 if (dict->fold_buf == 0)
360 dict->fold_buf = vstring_alloc(10);
361 vstring_strcpy(dict->fold_buf, name);
362 name = lowercase(vstring_str(dict->fold_buf));
364 memset(&db_key, 0, sizeof(db_key));
367 * Acquire an exclusive lock.
369 if ((dict->flags & DICT_FLAG_LOCK)
370 && myflock(dict->lock_fd, INTERNAL_LOCK, MYFLOCK_OP_EXCLUSIVE) < 0)
371 msg_fatal("%s: lock dictionary: %m", dict_db->dict.name);
374 * See if this DB file was written with one null byte appended to key and
375 * value.
377 if (dict->flags & DICT_FLAG_TRY1NULL) {
378 db_key.data = (void *) name;
379 db_key.size = strlen(name) + 1;
380 if ((status = DICT_DB_DEL(db, &db_key, flags)) < 0)
381 msg_fatal("error deleting from %s: %m", dict_db->dict.name);
382 if (status == 0)
383 dict->flags &= ~DICT_FLAG_TRY0NULL;
387 * See if this DB file was written with no null byte appended to key and
388 * value.
390 if (status > 0 && (dict->flags & DICT_FLAG_TRY0NULL)) {
391 db_key.data = (void *) name;
392 db_key.size = strlen(name);
393 if ((status = DICT_DB_DEL(db, &db_key, flags)) < 0)
394 msg_fatal("error deleting from %s: %m", dict_db->dict.name);
395 if (status == 0)
396 dict->flags &= ~DICT_FLAG_TRY1NULL;
398 if (dict->flags & DICT_FLAG_SYNC_UPDATE)
399 if (DICT_DB_SYNC(db, 0) < 0)
400 msg_fatal("%s: flush dictionary: %m", dict_db->dict.name);
403 * Release the exclusive lock.
405 if ((dict->flags & DICT_FLAG_LOCK)
406 && myflock(dict->lock_fd, INTERNAL_LOCK, MYFLOCK_OP_NONE) < 0)
407 msg_fatal("%s: unlock dictionary: %m", dict_db->dict.name);
409 return status;
412 /* dict_db_sequence - traverse the dictionary */
414 static int dict_db_sequence(DICT *dict, int function,
415 const char **key, const char **value)
417 const char *myname = "dict_db_sequence";
418 DICT_DB *dict_db = (DICT_DB *) dict;
419 DB *db = dict_db->db;
420 DBT db_key;
421 DBT db_value;
422 int status = 0;
423 int db_function;
425 #if DB_VERSION_MAJOR > 1
428 * Initialize.
430 dict_errno = 0;
431 memset(&db_key, 0, sizeof(db_key));
432 memset(&db_value, 0, sizeof(db_value));
435 * Determine the function.
437 switch (function) {
438 case DICT_SEQ_FUN_FIRST:
439 if (dict_db->cursor == 0)
440 DICT_DB_CURSOR(db, &(dict_db->cursor));
441 db_function = DB_FIRST;
442 break;
443 case DICT_SEQ_FUN_NEXT:
444 if (dict_db->cursor == 0)
445 msg_panic("%s: no cursor", myname);
446 db_function = DB_NEXT;
447 break;
448 default:
449 msg_panic("%s: invalid function %d", myname, function);
453 * Acquire a shared lock.
455 if ((dict->flags & DICT_FLAG_LOCK)
456 && myflock(dict->lock_fd, INTERNAL_LOCK, MYFLOCK_OP_SHARED) < 0)
457 msg_fatal("%s: lock dictionary: %m", dict_db->dict.name);
460 * Database lookup.
462 status =
463 dict_db->cursor->c_get(dict_db->cursor, &db_key, &db_value, db_function);
464 if (status != 0 && status != DB_NOTFOUND)
465 msg_fatal("error [%d] seeking %s: %m", status, dict_db->dict.name);
468 * Release the shared lock.
470 if ((dict->flags & DICT_FLAG_LOCK)
471 && myflock(dict->lock_fd, INTERNAL_LOCK, MYFLOCK_OP_NONE) < 0)
472 msg_fatal("%s: unlock dictionary: %m", dict_db->dict.name);
474 if (status == 0) {
477 * Copy the result so it is guaranteed null terminated.
479 *key = SCOPY(dict_db->key_buf, db_key.data, db_key.size);
480 *value = SCOPY(dict_db->val_buf, db_value.data, db_value.size);
482 return (status);
483 #else
486 * determine the function
488 switch (function) {
489 case DICT_SEQ_FUN_FIRST:
490 db_function = R_FIRST;
491 break;
492 case DICT_SEQ_FUN_NEXT:
493 db_function = R_NEXT;
494 break;
495 default:
496 msg_panic("%s: invalid function %d", myname, function);
500 * Acquire a shared lock.
502 if ((dict->flags & DICT_FLAG_LOCK)
503 && myflock(dict->lock_fd, INTERNAL_LOCK, MYFLOCK_OP_SHARED) < 0)
504 msg_fatal("%s: lock dictionary: %m", dict_db->dict.name);
506 if ((status = db->seq(db, &db_key, &db_value, db_function)) < 0)
507 msg_fatal("error seeking %s: %m", dict_db->dict.name);
510 * Release the shared lock.
512 if ((dict->flags & DICT_FLAG_LOCK)
513 && myflock(dict->lock_fd, INTERNAL_LOCK, MYFLOCK_OP_NONE) < 0)
514 msg_fatal("%s: unlock dictionary: %m", dict_db->dict.name);
516 if (status == 0) {
519 * Copy the result so that it is guaranteed null terminated.
521 *key = SCOPY(dict_db->key_buf, db_key.data, db_key.size);
522 *value = SCOPY(dict_db->val_buf, db_value.data, db_value.size);
524 return status;
525 #endif
528 /* dict_db_close - close data base */
530 static void dict_db_close(DICT *dict)
532 DICT_DB *dict_db = (DICT_DB *) dict;
534 #if DB_VERSION_MAJOR > 1
535 if (dict_db->cursor)
536 dict_db->cursor->c_close(dict_db->cursor);
537 #endif
538 if (DICT_DB_SYNC(dict_db->db, 0) < 0)
539 msg_fatal("flush database %s: %m", dict_db->dict.name);
540 if (DICT_DB_CLOSE(dict_db->db) < 0)
541 msg_fatal("close database %s: %m", dict_db->dict.name);
542 if (dict_db->key_buf)
543 vstring_free(dict_db->key_buf);
544 if (dict_db->val_buf)
545 vstring_free(dict_db->val_buf);
546 if (dict->fold_buf)
547 vstring_free(dict->fold_buf);
548 dict_free(dict);
551 /* dict_db_open - open data base */
553 static DICT *dict_db_open(const char *class, const char *path, int open_flags,
554 int type, void *tweak, int dict_flags)
556 DICT_DB *dict_db;
557 struct stat st;
558 DB *db;
559 char *db_path;
560 int lock_fd = -1;
561 int dbfd;
563 #if DB_VERSION_MAJOR > 1
564 int db_flags;
566 #endif
569 * Mismatches between #include file and library are a common cause for
570 * trouble.
572 #if DB_VERSION_MAJOR > 1
573 int major_version;
574 int minor_version;
575 int patch_version;
577 (void) db_version(&major_version, &minor_version, &patch_version);
578 if (major_version != DB_VERSION_MAJOR || minor_version != DB_VERSION_MINOR)
579 msg_fatal("incorrect version of Berkeley DB: "
580 "compiled against %d.%d.%d, run-time linked against %d.%d.%d",
581 DB_VERSION_MAJOR, DB_VERSION_MINOR, DB_VERSION_PATCH,
582 major_version, minor_version, patch_version);
583 if (msg_verbose) {
584 msg_info("Compiled against Berkeley DB: %d.%d.%d\n",
585 DB_VERSION_MAJOR, DB_VERSION_MINOR, DB_VERSION_PATCH);
586 msg_info("Run-time linked against Berkeley DB: %d.%d.%d\n",
587 major_version, minor_version, patch_version);
589 #else
590 if (msg_verbose)
591 msg_info("Compiled against Berkeley DB version 1");
592 #endif
594 db_path = concatenate(path, ".db", (char *) 0);
597 * Note: DICT_FLAG_LOCK is used only by programs that do fine-grained (in
598 * the time domain) locking while accessing individual database records.
600 * Programs such as postmap/postalias use their own large-grained (in the
601 * time domain) locks while rewriting the entire file.
603 * XXX DB version 4.1 will not open a zero-length file. This means we must
604 * open an existing file without O_CREAT|O_TRUNC, and that we must let
605 * db_open() create a non-existent file for us.
607 #define LOCK_OPEN_FLAGS(f) ((f) & ~(O_CREAT|O_TRUNC))
609 if (dict_flags & DICT_FLAG_LOCK) {
610 if ((lock_fd = open(db_path, LOCK_OPEN_FLAGS(open_flags), 0644)) < 0) {
611 if (errno != ENOENT)
612 msg_fatal("open database %s: %m", db_path);
613 } else {
614 if (myflock(lock_fd, INTERNAL_LOCK, MYFLOCK_OP_SHARED) < 0)
615 msg_fatal("shared-lock database %s for open: %m", db_path);
620 * Use the DB 1.x programming interface. This is the default interface
621 * with 4.4BSD systems. It is also available via the db_185 compatibility
622 * interface, but that interface does not have the undocumented feature
623 * that we need to make file locking safe with POSIX fcntl() locking.
625 #if DB_VERSION_MAJOR < 2
626 if ((db = dbopen(db_path, open_flags, 0644, type, tweak)) == 0)
627 msg_fatal("open database %s: %m", db_path);
628 dbfd = db->fd(db);
629 #endif
632 * Use the DB 2.x programming interface. Jump a couple extra hoops.
634 #if DB_VERSION_MAJOR == 2
635 db_flags = DB_FCNTL_LOCKING;
636 if (open_flags == O_RDONLY)
637 db_flags |= DB_RDONLY;
638 if (open_flags & O_CREAT)
639 db_flags |= DB_CREATE;
640 if (open_flags & O_TRUNC)
641 db_flags |= DB_TRUNCATE;
642 if ((errno = db_open(db_path, type, db_flags, 0644, 0, tweak, &db)) != 0)
643 msg_fatal("open database %s: %m", db_path);
644 if (db == 0)
645 msg_panic("db_open null result");
646 if ((errno = db->fd(db, &dbfd)) != 0)
647 msg_fatal("get database file descriptor: %m");
648 #endif
651 * Use the DB 3.x programming interface. Jump even more hoops.
653 #if DB_VERSION_MAJOR > 2
654 db_flags = DB_FCNTL_LOCKING;
655 if (open_flags == O_RDONLY)
656 db_flags |= DB_RDONLY;
657 if (open_flags & O_CREAT)
658 db_flags |= DB_CREATE;
659 if (open_flags & O_TRUNC)
660 db_flags |= DB_TRUNCATE;
661 if ((errno = db_create(&db, 0, 0)) != 0)
662 msg_fatal("create DB database: %m");
663 if (db == 0)
664 msg_panic("db_create null result");
665 if ((errno = db->set_cachesize(db, 0, dict_db_cache_size, 0)) != 0)
666 msg_fatal("set DB cache size %d: %m", dict_db_cache_size);
667 if (type == DB_HASH && db->set_h_nelem(db, DICT_DB_NELM) != 0)
668 msg_fatal("set DB hash element count %d: %m", DICT_DB_NELM);
669 #if (DB_VERSION_MAJOR == 4 && DB_VERSION_MINOR > 0)
670 if ((errno = db->open(db, 0, db_path, 0, type, db_flags, 0644)) != 0)
671 msg_fatal("open database %s: %m", db_path);
672 #elif (DB_VERSION_MAJOR == 3 || DB_VERSION_MAJOR == 4)
673 if ((errno = db->open(db, db_path, 0, type, db_flags, 0644)) != 0)
674 msg_fatal("open database %s: %m", db_path);
675 #else
676 #error "Unsupported Berkeley DB version"
677 #endif
678 if ((errno = db->fd(db, &dbfd)) != 0)
679 msg_fatal("get database file descriptor: %m");
680 #endif
681 if ((dict_flags & DICT_FLAG_LOCK) && lock_fd >= 0) {
682 if (myflock(lock_fd, INTERNAL_LOCK, MYFLOCK_OP_NONE) < 0)
683 msg_fatal("unlock database %s for open: %m", db_path);
684 if (close(lock_fd) < 0)
685 msg_fatal("close database %s: %m", db_path);
687 dict_db = (DICT_DB *) dict_alloc(class, db_path, sizeof(*dict_db));
688 dict_db->dict.lookup = dict_db_lookup;
689 dict_db->dict.update = dict_db_update;
690 dict_db->dict.delete = dict_db_delete;
691 dict_db->dict.sequence = dict_db_sequence;
692 dict_db->dict.close = dict_db_close;
693 dict_db->dict.lock_fd = dbfd;
694 dict_db->dict.stat_fd = dbfd;
695 if (fstat(dict_db->dict.stat_fd, &st) < 0)
696 msg_fatal("dict_db_open: fstat: %m");
697 dict_db->dict.mtime = st.st_mtime;
700 * Warn if the source file is newer than the indexed file, except when
701 * the source file changed only seconds ago.
703 if ((dict_flags & DICT_FLAG_LOCK) != 0
704 && stat(path, &st) == 0
705 && st.st_mtime > dict_db->dict.mtime
706 && st.st_mtime < time((time_t *) 0) - 100)
707 msg_warn("database %s is older than source file %s", db_path, path);
709 close_on_exec(dict_db->dict.lock_fd, CLOSE_ON_EXEC);
710 close_on_exec(dict_db->dict.stat_fd, CLOSE_ON_EXEC);
711 dict_db->dict.flags = dict_flags | DICT_FLAG_FIXED;
712 if ((dict_flags & (DICT_FLAG_TRY1NULL | DICT_FLAG_TRY0NULL)) == 0)
713 dict_db->dict.flags |= (DICT_FLAG_TRY1NULL | DICT_FLAG_TRY0NULL);
714 if (dict_flags & DICT_FLAG_FOLD_FIX)
715 dict_db->dict.fold_buf = vstring_alloc(10);
716 dict_db->db = db;
717 #if DB_VERSION_MAJOR > 1
718 dict_db->cursor = 0;
719 #endif
720 dict_db->key_buf = 0;
721 dict_db->val_buf = 0;
723 myfree(db_path);
724 return (DICT_DEBUG (&dict_db->dict));
727 /* dict_hash_open - create association with data base */
729 DICT *dict_hash_open(const char *path, int open_flags, int dict_flags)
731 #if DB_VERSION_MAJOR < 2
732 HASHINFO tweak;
734 memset((char *) &tweak, 0, sizeof(tweak));
735 tweak.nelem = DICT_DB_NELM;
736 tweak.cachesize = dict_db_cache_size;
737 #endif
738 #if DB_VERSION_MAJOR == 2
739 DB_INFO tweak;
741 memset((char *) &tweak, 0, sizeof(tweak));
742 tweak.h_nelem = DICT_DB_NELM;
743 tweak.db_cachesize = dict_db_cache_size;
744 #endif
745 #if DB_VERSION_MAJOR > 2
746 void *tweak;
748 tweak = 0;
749 #endif
750 return (dict_db_open(DICT_TYPE_HASH, path, open_flags, DB_HASH,
751 (void *) &tweak, dict_flags));
754 /* dict_btree_open - create association with data base */
756 DICT *dict_btree_open(const char *path, int open_flags, int dict_flags)
758 #if DB_VERSION_MAJOR < 2
759 BTREEINFO tweak;
761 memset((char *) &tweak, 0, sizeof(tweak));
762 tweak.cachesize = dict_db_cache_size;
763 #endif
764 #if DB_VERSION_MAJOR == 2
765 DB_INFO tweak;
767 memset((char *) &tweak, 0, sizeof(tweak));
768 tweak.db_cachesize = dict_db_cache_size;
769 #endif
770 #if DB_VERSION_MAJOR > 2
771 void *tweak;
773 tweak = 0;
774 #endif
776 return (dict_db_open(DICT_TYPE_BTREE, path, open_flags, DB_BTREE,
777 (void *) &tweak, dict_flags));
780 #endif