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