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