1/*++ 2/* NAME 3/* dict_db 3 4/* SUMMARY 5/* dictionary manager interface to DB files 6/* SYNOPSIS 7/* #include <dict_db.h> 8/* 9/* int dict_db_cache_size; 10/* 11/* DICT *dict_hash_open(path, open_flags, dict_flags) 12/* const char *path; 13/* int open_flags; 14/* int dict_flags; 15/* 16/* DICT *dict_btree_open(path, open_flags, dict_flags) 17/* const char *path; 18/* int open_flags; 19/* int dict_flags; 20/* DESCRIPTION 21/* dict_XXX_open() opens the specified DB database. The result is 22/* a pointer to a structure that can be used to access the dictionary 23/* using the generic methods documented in dict_open(3). 24/* 25/* The dict_db_cache_size variable specifies a non-default per-table 26/* I/O buffer size. The default buffer size is adequate for reading. 27/* For better performance while creating a large table, specify a large 28/* buffer size before opening the file. 29/* 30/* Arguments: 31/* .IP path 32/* The database pathname, not including the ".db" suffix. 33/* .IP open_flags 34/* Flags passed to dbopen(). 35/* .IP dict_flags 36/* Flags used by the dictionary interface. 37/* SEE ALSO 38/* dict(3) generic dictionary manager 39/* DIAGNOSTICS 40/* Fatal errors: cannot open file, write error, out of memory. 41/* LICENSE 42/* .ad 43/* .fi 44/* The Secure Mailer license must be distributed with this software. 45/* AUTHOR(S) 46/* Wietse Venema 47/* IBM T.J. Watson Research 48/* P.O. Box 704 49/* Yorktown Heights, NY 10598, USA 50/*--*/ 51 52#include "sys_defs.h" 53 54#ifdef HAS_DB 55 56/* System library. */ 57 58#include <sys/stat.h> 59#include <limits.h> 60#ifdef PATH_DB_H 61#include PATH_DB_H 62#else 63#include <db.h> 64#endif 65#include <string.h> 66#include <unistd.h> 67#include <errno.h> 68 69#if defined(_DB_185_H_) && defined(USE_FCNTL_LOCK) 70#error "Error: this system must not use the db 1.85 compatibility interface" 71#endif 72 73#ifndef DB_VERSION_MAJOR 74#define DB_VERSION_MAJOR 1 75#define DICT_DB_GET(db, key, val, flag) db->get(db, key, val, flag) 76#define DICT_DB_PUT(db, key, val, flag) db->put(db, key, val, flag) 77#define DICT_DB_DEL(db, key, flag) db->del(db, key, flag) 78#define DICT_DB_SYNC(db, flag) db->sync(db, flag) 79#define DICT_DB_CLOSE(db) db->close(db) 80#define DONT_CLOBBER R_NOOVERWRITE 81#endif 82 83#if DB_VERSION_MAJOR > 1 84#define DICT_DB_GET(db, key, val, flag) sanitize(db->get(db, 0, key, val, flag)) 85#define DICT_DB_PUT(db, key, val, flag) sanitize(db->put(db, 0, key, val, flag)) 86#define DICT_DB_DEL(db, key, flag) sanitize(db->del(db, 0, key, flag)) 87#define DICT_DB_SYNC(db, flag) ((errno = db->sync(db, flag)) ? -1 : 0) 88#define DICT_DB_CLOSE(db) ((errno = db->close(db, 0)) ? -1 : 0) 89#define DONT_CLOBBER DB_NOOVERWRITE 90#endif 91 92#if (DB_VERSION_MAJOR == 2 && DB_VERSION_MINOR < 6) 93#define DICT_DB_CURSOR(db, curs) (db)->cursor((db), NULL, (curs)) 94#else 95#define DICT_DB_CURSOR(db, curs) (db)->cursor((db), NULL, (curs), 0) 96#endif 97 98#ifndef DB_FCNTL_LOCKING 99#define DB_FCNTL_LOCKING 0 100#endif 101 102/* Utility library. */ 103 104#include "msg.h" 105#include "mymalloc.h" 106#include "vstring.h" 107#include "stringops.h" 108#include "iostuff.h" 109#include "myflock.h" 110#include "dict.h" 111#include "dict_db.h" 112#include "warn_stat.h" 113 114/* Application-specific. */ 115 116typedef struct { 117 DICT dict; /* generic members */ 118 DB *db; /* open db file */ 119#if DB_VERSION_MAJOR > 1 120 DBC *cursor; /* dict_db_sequence() */ 121#endif 122 VSTRING *key_buf; /* key result */ 123 VSTRING *val_buf; /* value result */ 124} DICT_DB; 125 126#define SCOPY(buf, data, size) \ 127 vstring_str(vstring_strncpy(buf ? buf : (buf = vstring_alloc(10)), data, size)) 128 129 /* 130 * You can override the default dict_db_cache_size setting before calling 131 * dict_hash_open() or dict_btree_open(). This is done in mkmap_db_open() to 132 * set a larger memory pool for database (re)builds. 133 * 134 * XXX This should be specified via the DICT interface so that it becomes an 135 * object property, instead of being specified by poking a global variable 136 * so that it becomes a class property. 137 */ 138int dict_db_cache_size = (128 * 1024); /* 128K default memory pool */ 139 140#define DICT_DB_NELM 4096 141 142#if DB_VERSION_MAJOR > 1 143 144/* sanitize - sanitize db_get/put/del result */ 145 146static int sanitize(int status) 147{ 148 149 /* 150 * XXX This is unclean but avoids a lot of clutter elsewhere. Categorize 151 * results into non-fatal errors (i.e., errors that we can deal with), 152 * success, or fatal error (i.e., all other errors). 153 */ 154 switch (status) { 155 156 case DB_NOTFOUND: /* get, del */ 157 case DB_KEYEXIST: /* put */ 158 return (1); /* non-fatal */ 159 160 case 0: 161 return (0); /* success */ 162 163 case DB_KEYEMPTY: /* get, others? */ 164 status = EINVAL; 165 /* FALLTHROUGH */ 166 default: 167 errno = status; 168 return (-1); /* fatal */ 169 } 170} 171 172#endif 173 174/* dict_db_lookup - find database entry */ 175 176static const char *dict_db_lookup(DICT *dict, const char *name) 177{ 178 DICT_DB *dict_db = (DICT_DB *) dict; 179 DB *db = dict_db->db; 180 DBT db_key; 181 DBT db_value; 182 int status; 183 const char *result = 0; 184 185 dict->error = 0; 186 187 /* 188 * Sanity check. 189 */ 190 if ((dict->flags & (DICT_FLAG_TRY1NULL | DICT_FLAG_TRY0NULL)) == 0) 191 msg_panic("dict_db_lookup: no DICT_FLAG_TRY1NULL | DICT_FLAG_TRY0NULL flag"); 192 193 memset(&db_key, 0, sizeof(db_key)); 194 memset(&db_value, 0, sizeof(db_value)); 195 196 /* 197 * Optionally fold the key. 198 */ 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)); 204 } 205 206 /* 207 * Acquire a shared lock. 208 */ 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); 212 213 /* 214 * See if this DB file was written with one null byte appended to key and 215 * value. 216 */ 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); 225 } 226 } 227 228 /* 229 * See if this DB file was written with no null byte appended to key and 230 * value. 231 */ 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); 240 } 241 } 242 243 /* 244 * Release the shared lock. 245 */ 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); 249 250 return (result); 251} 252 253/* dict_db_update - add or update database entry */ 254 255static int dict_db_update(DICT *dict, const char *name, const char *value) 256{ 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; 262 263 dict->error = 0; 264 265 /* 266 * Sanity check. 267 */ 268 if ((dict->flags & (DICT_FLAG_TRY1NULL | DICT_FLAG_TRY0NULL)) == 0) 269 msg_panic("dict_db_update: no DICT_FLAG_TRY1NULL | DICT_FLAG_TRY0NULL flag"); 270 271 /* 272 * Optionally fold the key. 273 */ 274 if (dict->flags & DICT_FLAG_FOLD_FIX) { 275 if (dict->fold_buf == 0) 276 dict->fold_buf = vstring_alloc(10); 277 vstring_strcpy(dict->fold_buf, name); 278 name = lowercase(vstring_str(dict->fold_buf)); 279 } 280 memset(&db_key, 0, sizeof(db_key)); 281 memset(&db_value, 0, sizeof(db_value)); 282 db_key.data = (void *) name; 283 db_value.data = (void *) value; 284 db_key.size = strlen(name); 285 db_value.size = strlen(value); 286 287 /* 288 * If undecided about appending a null byte to key and value, choose a 289 * default depending on the platform. 290 */ 291 if ((dict->flags & DICT_FLAG_TRY1NULL) 292 && (dict->flags & DICT_FLAG_TRY0NULL)) { 293#ifdef DB_NO_TRAILING_NULL 294 dict->flags &= ~DICT_FLAG_TRY1NULL; 295#else 296 dict->flags &= ~DICT_FLAG_TRY0NULL; 297#endif 298 } 299 300 /* 301 * Optionally append a null byte to key and value. 302 */ 303 if (dict->flags & DICT_FLAG_TRY1NULL) { 304 db_key.size++; 305 db_value.size++; 306 } 307 308 /* 309 * Acquire an exclusive lock. 310 */ 311 if ((dict->flags & DICT_FLAG_LOCK) 312 && myflock(dict->lock_fd, INTERNAL_LOCK, MYFLOCK_OP_EXCLUSIVE) < 0) 313 msg_fatal("%s: lock dictionary: %m", dict_db->dict.name); 314 315 /* 316 * Do the update. 317 */ 318 if ((status = DICT_DB_PUT(db, &db_key, &db_value, 319 (dict->flags & DICT_FLAG_DUP_REPLACE) ? 0 : DONT_CLOBBER)) < 0) 320 msg_fatal("error writing %s: %m", dict_db->dict.name); 321 if (status) { 322 if (dict->flags & DICT_FLAG_DUP_IGNORE) 323 /* void */ ; 324 else if (dict->flags & DICT_FLAG_DUP_WARN) 325 msg_warn("%s: duplicate entry: \"%s\"", dict_db->dict.name, name); 326 else 327 msg_fatal("%s: duplicate entry: \"%s\"", dict_db->dict.name, name); 328 } 329 if (dict->flags & DICT_FLAG_SYNC_UPDATE) 330 if (DICT_DB_SYNC(db, 0) < 0) 331 msg_fatal("%s: flush dictionary: %m", dict_db->dict.name); 332 333 /* 334 * Release the exclusive lock. 335 */ 336 if ((dict->flags & DICT_FLAG_LOCK) 337 && myflock(dict->lock_fd, INTERNAL_LOCK, MYFLOCK_OP_NONE) < 0) 338 msg_fatal("%s: unlock dictionary: %m", dict_db->dict.name); 339 340 return (status); 341} 342 343/* delete one entry from the dictionary */ 344 345static int dict_db_delete(DICT *dict, const char *name) 346{ 347 DICT_DB *dict_db = (DICT_DB *) dict; 348 DB *db = dict_db->db; 349 DBT db_key; 350 int status = 1; 351 int flags = 0; 352 353 dict->error = 0; 354 355 /* 356 * Sanity check. 357 */ 358 if ((dict->flags & (DICT_FLAG_TRY1NULL | DICT_FLAG_TRY0NULL)) == 0) 359 msg_panic("dict_db_delete: no DICT_FLAG_TRY1NULL | DICT_FLAG_TRY0NULL flag"); 360 361 /* 362 * Optionally fold the key. 363 */ 364 if (dict->flags & DICT_FLAG_FOLD_FIX) { 365 if (dict->fold_buf == 0) 366 dict->fold_buf = vstring_alloc(10); 367 vstring_strcpy(dict->fold_buf, name); 368 name = lowercase(vstring_str(dict->fold_buf)); 369 } 370 memset(&db_key, 0, sizeof(db_key)); 371 372 /* 373 * Acquire an exclusive lock. 374 */ 375 if ((dict->flags & DICT_FLAG_LOCK) 376 && myflock(dict->lock_fd, INTERNAL_LOCK, MYFLOCK_OP_EXCLUSIVE) < 0) 377 msg_fatal("%s: lock dictionary: %m", dict_db->dict.name); 378 379 /* 380 * See if this DB file was written with one null byte appended to key and 381 * value. 382 */ 383 if (dict->flags & DICT_FLAG_TRY1NULL) { 384 db_key.data = (void *) name; 385 db_key.size = strlen(name) + 1; 386 if ((status = DICT_DB_DEL(db, &db_key, flags)) < 0) 387 msg_fatal("error deleting from %s: %m", dict_db->dict.name); 388 if (status == 0) 389 dict->flags &= ~DICT_FLAG_TRY0NULL; 390 } 391 392 /* 393 * See if this DB file was written with no null byte appended to key and 394 * value. 395 */ 396 if (status > 0 && (dict->flags & DICT_FLAG_TRY0NULL)) { 397 db_key.data = (void *) name; 398 db_key.size = strlen(name); 399 if ((status = DICT_DB_DEL(db, &db_key, flags)) < 0) 400 msg_fatal("error deleting from %s: %m", dict_db->dict.name); 401 if (status == 0) 402 dict->flags &= ~DICT_FLAG_TRY1NULL; 403 } 404 if (dict->flags & DICT_FLAG_SYNC_UPDATE) 405 if (DICT_DB_SYNC(db, 0) < 0) 406 msg_fatal("%s: flush dictionary: %m", dict_db->dict.name); 407 408 /* 409 * Release the exclusive lock. 410 */ 411 if ((dict->flags & DICT_FLAG_LOCK) 412 && myflock(dict->lock_fd, INTERNAL_LOCK, MYFLOCK_OP_NONE) < 0) 413 msg_fatal("%s: unlock dictionary: %m", dict_db->dict.name); 414 415 return status; 416} 417 418/* dict_db_sequence - traverse the dictionary */ 419 420static int dict_db_sequence(DICT *dict, int function, 421 const char **key, const char **value) 422{ 423 const char *myname = "dict_db_sequence"; 424 DICT_DB *dict_db = (DICT_DB *) dict; 425 DB *db = dict_db->db; 426 DBT db_key; 427 DBT db_value; 428 int status = 0; 429 int db_function; 430 431 dict->error = 0; 432 433#if DB_VERSION_MAJOR > 1 434 435 /* 436 * Initialize. 437 */ 438 memset(&db_key, 0, sizeof(db_key)); 439 memset(&db_value, 0, sizeof(db_value)); 440 441 /* 442 * Determine the function. 443 */ 444 switch (function) { 445 case DICT_SEQ_FUN_FIRST: 446 if (dict_db->cursor == 0) 447 DICT_DB_CURSOR(db, &(dict_db->cursor)); 448 db_function = DB_FIRST; 449 break; 450 case DICT_SEQ_FUN_NEXT: 451 if (dict_db->cursor == 0) 452 msg_panic("%s: no cursor", myname); 453 db_function = DB_NEXT; 454 break; 455 default: 456 msg_panic("%s: invalid function %d", myname, function); 457 } 458 459 /* 460 * Acquire a shared lock. 461 */ 462 if ((dict->flags & DICT_FLAG_LOCK) 463 && myflock(dict->lock_fd, INTERNAL_LOCK, MYFLOCK_OP_SHARED) < 0) 464 msg_fatal("%s: lock dictionary: %m", dict_db->dict.name); 465 466 /* 467 * Database lookup. 468 */ 469 status = 470 dict_db->cursor->c_get(dict_db->cursor, &db_key, &db_value, db_function); 471 if (status != 0 && status != DB_NOTFOUND) 472 msg_fatal("error [%d] seeking %s: %m", status, dict_db->dict.name); 473 474 /* 475 * Release the shared lock. 476 */ 477 if ((dict->flags & DICT_FLAG_LOCK) 478 && myflock(dict->lock_fd, INTERNAL_LOCK, MYFLOCK_OP_NONE) < 0) 479 msg_fatal("%s: unlock dictionary: %m", dict_db->dict.name); 480 481 if (status == 0) { 482 483 /* 484 * Copy the result so it is guaranteed null terminated. 485 */ 486 *key = SCOPY(dict_db->key_buf, db_key.data, db_key.size); 487 *value = SCOPY(dict_db->val_buf, db_value.data, db_value.size); 488 } 489 return (status); 490#else 491 492 /* 493 * determine the function 494 */ 495 switch (function) { 496 case DICT_SEQ_FUN_FIRST: 497 db_function = R_FIRST; 498 break; 499 case DICT_SEQ_FUN_NEXT: 500 db_function = R_NEXT; 501 break; 502 default: 503 msg_panic("%s: invalid function %d", myname, function); 504 } 505 506 /* 507 * Acquire a shared lock. 508 */ 509 if ((dict->flags & DICT_FLAG_LOCK) 510 && myflock(dict->lock_fd, INTERNAL_LOCK, MYFLOCK_OP_SHARED) < 0) 511 msg_fatal("%s: lock dictionary: %m", dict_db->dict.name); 512 513 if ((status = db->seq(db, &db_key, &db_value, db_function)) < 0) 514 msg_fatal("error seeking %s: %m", dict_db->dict.name); 515 516 /* 517 * Release the shared lock. 518 */ 519 if ((dict->flags & DICT_FLAG_LOCK) 520 && myflock(dict->lock_fd, INTERNAL_LOCK, MYFLOCK_OP_NONE) < 0) 521 msg_fatal("%s: unlock dictionary: %m", dict_db->dict.name); 522 523 if (status == 0) { 524 525 /* 526 * Copy the result so that it is guaranteed null terminated. 527 */ 528 *key = SCOPY(dict_db->key_buf, db_key.data, db_key.size); 529 *value = SCOPY(dict_db->val_buf, db_value.data, db_value.size); 530 } 531 return status; 532#endif 533} 534 535/* dict_db_close - close data base */ 536 537static void dict_db_close(DICT *dict) 538{ 539 DICT_DB *dict_db = (DICT_DB *) dict; 540 541#if DB_VERSION_MAJOR > 1 542 if (dict_db->cursor) 543 dict_db->cursor->c_close(dict_db->cursor); 544#endif 545 if (DICT_DB_SYNC(dict_db->db, 0) < 0) 546 msg_fatal("flush database %s: %m", dict_db->dict.name); 547 548 /* 549 * With some Berkeley DB implementations, close fails with a bogus ENOENT 550 * error, while it reports no errors with put+sync, no errors with 551 * del+sync, and no errors with the sync operation just before this 552 * comment. This happens in programs that never fork and that never share 553 * the database with other processes. The bogus close error has been 554 * reported for programs that use the first/next iterator. Instead of 555 * making Postfix look bad because it reports errors that other programs 556 * ignore, I'm going to report the bogus error as a non-error. 557 */ 558 if (DICT_DB_CLOSE(dict_db->db) < 0) 559 msg_info("close database %s: %m (possible Berkeley DB bug)", 560 dict_db->dict.name); 561 if (dict_db->key_buf) 562 vstring_free(dict_db->key_buf); 563 if (dict_db->val_buf) 564 vstring_free(dict_db->val_buf); 565 if (dict->fold_buf) 566 vstring_free(dict->fold_buf); 567 dict_free(dict); 568} 569 570/* dict_db_open - open data base */ 571 572static DICT *dict_db_open(const char *class, const char *path, int open_flags, 573 int type, void *tweak, int dict_flags) 574{ 575 DICT_DB *dict_db; 576 struct stat st; 577 DB *db = 0; 578 char *db_path = 0; 579 int lock_fd = -1; 580 int dbfd; 581 582#if DB_VERSION_MAJOR > 1 583 int db_flags; 584 585#endif 586 587 /* 588 * Mismatches between #include file and library are a common cause for 589 * trouble. 590 */ 591#if DB_VERSION_MAJOR > 1 592 int major_version; 593 int minor_version; 594 int patch_version; 595 596 (void) db_version(&major_version, &minor_version, &patch_version); 597 if (major_version != DB_VERSION_MAJOR || minor_version != DB_VERSION_MINOR) 598 return (dict_surrogate(class, path, open_flags, dict_flags, 599 "incorrect version of Berkeley DB: " 600 "compiled against %d.%d.%d, run-time linked against %d.%d.%d", 601 DB_VERSION_MAJOR, DB_VERSION_MINOR, DB_VERSION_PATCH, 602 major_version, minor_version, patch_version)); 603 if (msg_verbose) { 604 msg_info("Compiled against Berkeley DB: %d.%d.%d\n", 605 DB_VERSION_MAJOR, DB_VERSION_MINOR, DB_VERSION_PATCH); 606 msg_info("Run-time linked against Berkeley DB: %d.%d.%d\n", 607 major_version, minor_version, patch_version); 608 } 609#else 610 if (msg_verbose) 611 msg_info("Compiled against Berkeley DB version 1"); 612#endif 613 614 db_path = concatenate(path, ".db", (char *) 0); 615 616 /* 617 * Note: DICT_FLAG_LOCK is used only by programs that do fine-grained (in 618 * the time domain) locking while accessing individual database records. 619 * 620 * Programs such as postmap/postalias use their own large-grained (in the 621 * time domain) locks while rewriting the entire file. 622 * 623 * XXX DB version 4.1 will not open a zero-length file. This means we must 624 * open an existing file without O_CREAT|O_TRUNC, and that we must let 625 * db_open() create a non-existent file for us. 626 */ 627#define LOCK_OPEN_FLAGS(f) ((f) & ~(O_CREAT|O_TRUNC)) 628#define FREE_RETURN(e) do { \ 629 DICT *_dict = (e); if (db) DICT_DB_CLOSE(db); \ 630 if (db_path) myfree(db_path); return (_dict); \ 631 } while (0) 632 633 if (dict_flags & DICT_FLAG_LOCK) { 634 if ((lock_fd = open(db_path, LOCK_OPEN_FLAGS(open_flags), 0644)) < 0) { 635 if (errno != ENOENT) 636 FREE_RETURN(dict_surrogate(class, path, open_flags, dict_flags, 637 "open database %s: %m", db_path)); 638 } else { 639 if (myflock(lock_fd, INTERNAL_LOCK, MYFLOCK_OP_SHARED) < 0) 640 msg_fatal("shared-lock database %s for open: %m", db_path); 641 } 642 } 643 644 /* 645 * Use the DB 1.x programming interface. This is the default interface 646 * with 4.4BSD systems. It is also available via the db_185 compatibility 647 * interface, but that interface does not have the undocumented feature 648 * that we need to make file locking safe with POSIX fcntl() locking. 649 */ 650#if DB_VERSION_MAJOR < 2 651 if ((db = dbopen(db_path, open_flags, 0644, type, tweak)) == 0) 652 FREE_RETURN(dict_surrogate(class, path, open_flags, dict_flags, 653 "open database %s: %m", db_path)); 654 dbfd = db->fd(db); 655#endif 656 657 /* 658 * Use the DB 2.x programming interface. Jump a couple extra hoops. 659 */ 660#if DB_VERSION_MAJOR == 2 661 db_flags = DB_FCNTL_LOCKING; 662 if (open_flags == O_RDONLY) 663 db_flags |= DB_RDONLY; 664 if (open_flags & O_CREAT) 665 db_flags |= DB_CREATE; 666 if (open_flags & O_TRUNC) 667 db_flags |= DB_TRUNCATE; 668 if ((errno = db_open(db_path, type, db_flags, 0644, 0, tweak, &db)) != 0) 669 FREE_RETURN(dict_surrogate(class, path, open_flags, dict_flags, 670 "open database %s: %m", db_path)); 671 if (db == 0) 672 msg_panic("db_open null result"); 673 if ((errno = db->fd(db, &dbfd)) != 0) 674 msg_fatal("get database file descriptor: %m"); 675#endif 676 677 /* 678 * Use the DB 3.x programming interface. Jump even more hoops. 679 */ 680#if DB_VERSION_MAJOR > 2 681 db_flags = DB_FCNTL_LOCKING; 682 if (open_flags == O_RDONLY) 683 db_flags |= DB_RDONLY; 684 if (open_flags & O_CREAT) 685 db_flags |= DB_CREATE; 686 if (open_flags & O_TRUNC) 687 db_flags |= DB_TRUNCATE; 688 if ((errno = db_create(&db, 0, 0)) != 0) 689 msg_fatal("create DB database: %m"); 690 if (db == 0) 691 msg_panic("db_create null result"); 692 if ((errno = db->set_cachesize(db, 0, dict_db_cache_size, 0)) != 0) 693 msg_fatal("set DB cache size %d: %m", dict_db_cache_size); 694 if (type == DB_HASH && db->set_h_nelem(db, DICT_DB_NELM) != 0) 695 msg_fatal("set DB hash element count %d: %m", DICT_DB_NELM); 696#if DB_VERSION_MAJOR == 5 || (DB_VERSION_MAJOR == 4 && DB_VERSION_MINOR > 0) 697 if ((errno = db->open(db, 0, db_path, 0, type, db_flags, 0644)) != 0) 698 FREE_RETURN(dict_surrogate(class, path, open_flags, dict_flags, 699 "open database %s: %m", db_path)); 700#elif (DB_VERSION_MAJOR == 3 || DB_VERSION_MAJOR == 4) 701 if ((errno = db->open(db, db_path, 0, type, db_flags, 0644)) != 0) 702 FREE_RETURN(dict_surrogate(class, path, open_flags, dict_flags, 703 "open database %s: %m", db_path)); 704#else 705#error "Unsupported Berkeley DB version" 706#endif 707 if ((errno = db->fd(db, &dbfd)) != 0) 708 msg_fatal("get database file descriptor: %m"); 709#endif 710 if ((dict_flags & DICT_FLAG_LOCK) && lock_fd >= 0) { 711 if (myflock(lock_fd, INTERNAL_LOCK, MYFLOCK_OP_NONE) < 0) 712 msg_fatal("unlock database %s for open: %m", db_path); 713 if (close(lock_fd) < 0) 714 msg_fatal("close database %s: %m", db_path); 715 } 716 dict_db = (DICT_DB *) dict_alloc(class, db_path, sizeof(*dict_db)); 717 dict_db->dict.lookup = dict_db_lookup; 718 dict_db->dict.update = dict_db_update; 719 dict_db->dict.delete = dict_db_delete; 720 dict_db->dict.sequence = dict_db_sequence; 721 dict_db->dict.close = dict_db_close; 722 dict_db->dict.lock_fd = dbfd; 723 dict_db->dict.stat_fd = dbfd; 724 if (fstat(dict_db->dict.stat_fd, &st) < 0) 725 msg_fatal("dict_db_open: fstat: %m"); 726 dict_db->dict.mtime = st.st_mtime; 727 dict_db->dict.owner.uid = st.st_uid; 728 dict_db->dict.owner.status = (st.st_uid != 0); 729 730 /* 731 * Warn if the source file is newer than the indexed file, except when 732 * the source file changed only seconds ago. 733 */ 734 if ((dict_flags & DICT_FLAG_LOCK) != 0 735 && stat(path, &st) == 0 736 && st.st_mtime > dict_db->dict.mtime 737 && st.st_mtime < time((time_t *) 0) - 100) 738 msg_warn("database %s is older than source file %s", db_path, path); 739 740 close_on_exec(dict_db->dict.lock_fd, CLOSE_ON_EXEC); 741 close_on_exec(dict_db->dict.stat_fd, CLOSE_ON_EXEC); 742 dict_db->dict.flags = dict_flags | DICT_FLAG_FIXED; 743 if ((dict_flags & (DICT_FLAG_TRY1NULL | DICT_FLAG_TRY0NULL)) == 0) 744 dict_db->dict.flags |= (DICT_FLAG_TRY1NULL | DICT_FLAG_TRY0NULL); 745 if (dict_flags & DICT_FLAG_FOLD_FIX) 746 dict_db->dict.fold_buf = vstring_alloc(10); 747 dict_db->db = db; 748#if DB_VERSION_MAJOR > 1 749 dict_db->cursor = 0; 750#endif 751 dict_db->key_buf = 0; 752 dict_db->val_buf = 0; 753 754 myfree(db_path); 755 return (DICT_DEBUG (&dict_db->dict)); 756} 757 758/* dict_hash_open - create association with data base */ 759 760DICT *dict_hash_open(const char *path, int open_flags, int dict_flags) 761{ 762#if DB_VERSION_MAJOR < 2 763 HASHINFO tweak; 764 765 memset((char *) &tweak, 0, sizeof(tweak)); 766 tweak.nelem = DICT_DB_NELM; 767 tweak.cachesize = dict_db_cache_size; 768#endif 769#if DB_VERSION_MAJOR == 2 770 DB_INFO tweak; 771 772 memset((char *) &tweak, 0, sizeof(tweak)); 773 tweak.h_nelem = DICT_DB_NELM; 774 tweak.db_cachesize = dict_db_cache_size; 775#endif 776#if DB_VERSION_MAJOR > 2 777 void *tweak; 778 779 tweak = 0; 780#endif 781 return (dict_db_open(DICT_TYPE_HASH, path, open_flags, DB_HASH, 782 (void *) &tweak, dict_flags)); 783} 784 785/* dict_btree_open - create association with data base */ 786 787DICT *dict_btree_open(const char *path, int open_flags, int dict_flags) 788{ 789#if DB_VERSION_MAJOR < 2 790 BTREEINFO tweak; 791 792 memset((char *) &tweak, 0, sizeof(tweak)); 793 tweak.cachesize = dict_db_cache_size; 794#endif 795#if DB_VERSION_MAJOR == 2 796 DB_INFO tweak; 797 798 memset((char *) &tweak, 0, sizeof(tweak)); 799 tweak.db_cachesize = dict_db_cache_size; 800#endif 801#if DB_VERSION_MAJOR > 2 802 void *tweak; 803 804 tweak = 0; 805#endif 806 807 return (dict_db_open(DICT_TYPE_BTREE, path, open_flags, DB_BTREE, 808 (void *) &tweak, dict_flags)); 809} 810 811#endif 812