1 /*************************************************
2 * Exim - an Internet mail transport agent *
3 *************************************************/
5 /* Copyright (c) The Exim Maintainers 2020 - 2024 */
6 /* Copyright (c) University of Cambridge 1995 - 2018 */
7 /* See the file NOTICE for conditions of use and distribution. */
8 /* SPDX-License-Identifier: GPL-2.0-or-later */
13 /* We have buffers holding path names for database files.
14 PATH_MAX could be used here, but would be wasting memory, as we deal
15 with database files like $spooldirectory/db/<name> */
19 /* Functions for accessing Exim's hints database, which consists of a number of
20 different DBM files. This module does not contain code for reading DBM files
21 for (e.g.) alias expansion. That is all contained within the general search
22 functions. As Exim now has support for several DBM interfaces, all the relevant
23 functions are called as inlinable functions from an included file.
25 All the data in Exim's database is in the nature of *hints*. Therefore it
26 doesn't matter if it gets destroyed by accident. These functions are not
27 supposed to implement a "safe" database.
29 Keys are passed in as C strings, and the terminating zero *is* used when
30 building the dbm files. This just makes life easier when scanning the files
33 Synchronization is required on the database files, and this is achieved by
34 means of locking on independent lock files. (Earlier attempts to lock on the
35 DBM files themselves were never completely successful.) Since callers may in
36 general want to do more than one read or write while holding the lock, there
37 are separate open and close functions. However, the calling modules should
38 arrange to hold the locks for the bare minimum of time.
44 dbfn_read_enforce_length
47 dbfn_scan unused; ifdeffout out
50 ACL ratelimit & seen conditions
51 delivery retry handling
52 delivery serialization
53 TLS session resumption
60 /*************************************************
61 * Open and lock a database file *
62 *************************************************/
64 /* Ensure the directory for the DB is present */
67 db_dir_make(BOOL panic)
69 (void) directory_make(spool_directory, US"db", EXIMDB_DIRECTORY_MODE, panic);
73 /* Lock a file to protect the DB. Return TRUE for success */
76 lockfile_take(open_db * dbblock, const uschar * filename, BOOL rdonly, BOOL panic)
79 int rc, * fdp = &dbblock->lockfd;
81 priv_drop_temp(exim_uid, exim_gid);
82 if ((*fdp = Uopen(filename, O_RDWR, EXIMDB_LOCKFILE_MODE)) < 0)
85 *fdp = Uopen(filename, O_RDWR|O_CREAT, EXIMDB_LOCKFILE_MODE);
91 log_write(0, LOG_MAIN, "%s",
92 string_open_failed("database lock file %s", filename));
93 errno = 0; /* Indicates locking failure */
97 /* Now we must get a lock on the opened lock file; do this with a blocking
98 lock that times out. */
100 lock_data.l_type = rdonly ? F_RDLCK : F_WRLCK;
101 lock_data.l_whence = lock_data.l_start = lock_data.l_len = 0;
103 DEBUG(D_hints_lookup|D_retry|D_route|D_deliver)
104 debug_printf_indent("locking %s\n", filename);
106 sigalrm_seen = FALSE;
107 ALARM(EXIMDB_LOCK_TIMEOUT);
108 rc = fcntl(*fdp, F_SETLKW, &lock_data);
111 if (sigalrm_seen) errno = ETIMEDOUT;
114 log_write(0, LOG_MAIN|LOG_PANIC, "Failed to get %s lock for %s: %s",
115 rdonly ? "read" : "write", filename,
116 errno == ETIMEDOUT ? "timed out" : strerror(errno));
117 (void)close(*fdp); *fdp = -1;
118 errno = 0; /* Indicates locking failure */
122 DEBUG(D_hints_lookup) debug_printf_indent("locked %s\n", filename);
126 /* Used for accessing Exim's hints databases.
129 name The single-component name of one of Exim's database files.
130 flags Either O_RDONLY or O_RDWR, indicating the type of open required;
131 O_RDWR implies "create if necessary"
132 dbblock Points to an open_db block to be filled in.
133 lof If TRUE, write to the log for actual open failures (locking failures
135 panic If TRUE, panic on failure to create the db directory
137 Returns: NULL if the open failed, or the locking failed. After locking
138 failures, errno is zero.
140 On success, dbblock is returned. This contains the dbm pointer and
141 the fd of the locked lock file.
145 dbfn_open(const uschar * name, int flags, open_db * dbblock,
146 BOOL lof, BOOL panic)
150 uschar dirname[PATHLEN], filename[PATHLEN];
152 DEBUG(D_hints_lookup) acl_level++;
154 /* The first thing to do is to open a separate file on which to lock. This
155 ensures that Exim has exclusive use of the database before it even tries to
156 open it. Early versions tried to lock on the open database itself, but that
157 gave rise to mysterious problems from time to time - it was suspected that some
158 DB libraries "do things" on their open() calls which break the interlocking.
159 The lock file is never written to, but we open it for writing so we can get a
160 write lock if required. If it does not exist, we create it. This is done
161 separately so we know when we have done it, because when running as root we
162 need to change the ownership - see the bottom of this function. We also try to
163 make the directory as well, just in case. We won't be doing this many times
164 unnecessarily, because usually the lock file will be there. If the directory
165 exists, there is no error. */
167 snprintf(CS dirname, sizeof(dirname), "%s/db", spool_directory);
168 snprintf(CS filename, sizeof(filename), "%s/%s.lockfile", dirname, name);
170 dbblock->lockfd = -1;
171 if (!exim_lockfile_needed())
175 if (!lockfile_take(dbblock, filename, flags == O_RDONLY, panic))
177 DEBUG(D_hints_lookup) acl_level--;
182 /* At this point we have an opened and locked separate lock file, that is,
183 exclusive access to the database, so we can go ahead and open it. If we are
184 expected to create it, don't do so at first, again so that we can detect
185 whether we need to change its ownership (see comments about the lock file
186 above.) There have been regular reports of crashes while opening hints
187 databases - often this is caused by non-matching db.h and the library. To make
188 it easy to pin this down, there are now debug statements on either side of the
191 flags &= O_RDONLY | O_RDWR;
192 snprintf(CS filename, sizeof(filename), "%s/%s", dirname, name);
194 priv_drop_temp(exim_uid, exim_gid);
195 dbblock->dbptr = exim_dbopen(filename, dirname, flags, EXIMDB_MODE);
196 if (!dbblock->dbptr && errno == ENOENT && flags == O_RDWR)
198 DEBUG(D_hints_lookup)
199 debug_printf_indent("%s appears not to exist: trying to create\n", filename);
200 dbblock->dbptr = exim_dbopen(filename, dirname, flags|O_CREAT, EXIMDB_MODE);
205 /* If the open has failed, return NULL, leaving errno set. If lof is TRUE,
206 log the event - also for debugging - but debug only if the file just doesn't
212 if (lof && save_errno != ENOENT)
213 log_write(0, LOG_MAIN, "%s", string_open_failed("DB file %s",
216 DEBUG(D_hints_lookup)
217 debug_printf_indent("%s\n", CS string_open_failed("DB file %s",
219 (void)close(dbblock->lockfd);
220 dbblock->lockfd = -1;
222 DEBUG(D_hints_lookup) acl_level--;
226 DEBUG(D_hints_lookup)
227 debug_printf_indent("opened hints database %s: flags=%s\n", filename,
228 flags == O_RDONLY ? "O_RDONLY"
229 : flags == O_RDWR ? "O_RDWR"
232 /* Pass back the block containing the opened database handle and the open fd
240 /* Only for transaction-capable DB types. Open without locking or
241 starting a transaction. "lof" and "panic" always true; read/write mode.
245 dbfn_open_multi(const uschar * name, int flags, open_db * dbblock)
249 uschar dirname[PATHLEN], filename[PATHLEN];
251 DEBUG(D_hints_lookup) acl_level++;
253 dbblock->lockfd = -1;
256 snprintf(CS dirname, sizeof(dirname), "%s/db", spool_directory);
257 snprintf(CS filename, sizeof(filename), "%s/%s", dirname, name);
259 priv_drop_temp(exim_uid, exim_gid);
260 dbblock->dbptr = exim_dbopen_multi(filename, dirname, flags, EXIMDB_MODE);
261 if (!dbblock->dbptr && errno == ENOENT && flags == O_RDWR)
263 DEBUG(D_hints_lookup)
264 debug_printf_indent("%s appears not to exist: trying to create\n", filename);
265 dbblock->dbptr = exim_dbopen_multi(filename, dirname, O_RDWR|O_CREAT, EXIMDB_MODE);
270 /* If the open has failed, return NULL, leaving errno set. If lof is TRUE,
271 log the event - also for debugging - but debug only if the file just doesn't
277 if (save_errno != ENOENT)
278 log_write(0, LOG_MAIN, "%s", string_open_failed("DB file %s",
281 DEBUG(D_hints_lookup)
282 debug_printf_indent("%s\n", CS string_open_failed("DB file %s",
285 DEBUG(D_hints_lookup) acl_level--;
289 DEBUG(D_hints_lookup) debug_printf_indent(
290 "opened hints database %s for transactions: NOLOCK flags=O_RDWR\n", filename);
292 /* Pass back the block containing the opened database handle */
299 dbfn_transaction_start(open_db * dbp)
301 DEBUG(D_hints_lookup) debug_printf_indent("dbfn_transaction_start\n");
302 return exim_dbtransaction_start(dbp->dbptr);
305 dbfn_transaction_commit(open_db * dbp)
307 DEBUG(D_hints_lookup) debug_printf_indent("dbfn_transaction_commit\n");
308 exim_dbtransaction_commit(dbp->dbptr);
313 /*************************************************
314 * Unlock and close a database file *
315 *************************************************/
317 /* Closing a file automatically unlocks it, so after closing the database, just
318 close the lock file if there was one.
320 Argument: a pointer to an open database block
325 dbfn_close(open_db * dbp)
327 int * fdp = &dbp->lockfd;
329 exim_dbclose(dbp->dbptr);
330 if (*fdp >= 0) (void)close(*fdp);
331 DEBUG(D_hints_lookup)
333 debug_printf_indent("closed hints database%s\n",
334 *fdp < 0 ? "" : " and lockfile");
342 dbfn_close_multi(open_db * dbp)
344 exim_dbclose_multi(dbp->dbptr);
345 DEBUG(D_hints_lookup)
347 debug_printf_indent("closed hints database\n");
355 /*************************************************
356 * Read from database file *
357 *************************************************/
359 /* Passing back the pointer unchanged is useless, because there is
360 no guarantee of alignment. Since all the records used by Exim need
361 to be properly aligned to pick out the timestamps, etc., we might as
362 well do the copying centrally here.
364 Most calls don't need the length, so there is a macro called dbfn_read which
365 has two arguments; it calls this function adding NULL as the third.
368 dbblock a pointer to an open database block
369 key the key of the record to be read
370 length a pointer to an int into which to return the length, if not NULL
372 Returns: a pointer to the retrieved record, or
373 NULL if the record is not found
377 dbfn_read_with_length(open_db * dbblock, const uschar * key, int * length)
380 EXIM_DATUM key_datum, result_datum;
381 int klen = Ustrlen(key) + 1;
382 uschar * key_copy = store_get(klen, key);
385 memcpy(key_copy, key, klen);
387 DEBUG(D_hints_lookup) debug_printf_indent("dbfn_read: key=%s\n", key);
389 exim_datum_init(&key_datum); /* Some DBM libraries require the datum */
390 exim_datum_init(&result_datum); /* to be cleared before use. */
391 exim_datum_data_set(&key_datum, key_copy);
392 exim_datum_size_set(&key_datum, klen);
394 if (!exim_dbget(dbblock->dbptr, &key_datum, &result_datum))
396 DEBUG(D_hints_lookup) debug_printf_indent("dbfn_read: null return\n");
400 /* Assume the data store could have been tainted. Properly, we should
401 store the taint status with the data. */
403 dlen = exim_datum_size_get(&result_datum);
404 yield = store_get(dlen, GET_TAINTED);
405 memcpy(yield, exim_datum_data_get(&result_datum), dlen);
406 DEBUG(D_hints_lookup) debug_printf_indent("dbfn_read: size %u return\n", dlen);
407 if (length) *length = dlen;
409 exim_datum_free(&result_datum); /* Some DBM libs require freeing */
414 /* Read a record. If the length is not as expected then delete it, write
415 an error log line, delete the record and return NULL.
416 Use this for fixed-size records (so not retry or wait records).
419 dbblock a pointer to an open database block
420 key the key of the record to be read
421 length the expected record length
423 Returns: a pointer to the retrieved record, or
424 NULL if the record is not found/bad
428 dbfn_read_enforce_length(open_db * dbblock, const uschar * key, size_t length)
431 void * yield = dbfn_read_with_length(dbblock, key, &rlen);
435 if (rlen == length) return yield;
436 log_write(0, LOG_MAIN|LOG_PANIC, "Bad db record size for '%s'", key);
437 dbfn_delete(dbblock, key);
443 /*************************************************
444 * Write to database file *
445 *************************************************/
449 dbblock a pointer to an open database block
450 key the key of the record to be written
451 ptr a pointer to the record to be written
452 length the length of the record to be written
454 Returns: the yield of the underlying dbm or db "write" function. If this
455 is dbm, the value is zero for OK.
459 dbfn_write(open_db *dbblock, const uschar *key, void *ptr, int length)
461 EXIM_DATUM key_datum, value_datum;
462 dbdata_generic *gptr = (dbdata_generic *)ptr;
463 int klen = Ustrlen(key) + 1;
464 uschar * key_copy = store_get(klen, key);
466 memcpy(key_copy, key, klen);
467 gptr->time_stamp = time(NULL);
469 DEBUG(D_hints_lookup)
470 debug_printf_indent("dbfn_write: key=%s datalen %d\n", key, length);
472 exim_datum_init(&key_datum); /* Some DBM libraries require the datum */
473 exim_datum_init(&value_datum); /* to be cleared before use. */
474 exim_datum_data_set(&key_datum, key_copy);
475 exim_datum_size_set(&key_datum, klen);
476 exim_datum_data_set(&value_datum, ptr);
477 exim_datum_size_set(&value_datum, length);
478 return exim_dbput(dbblock->dbptr, &key_datum, &value_datum);
483 /*************************************************
484 * Delete record from database file *
485 *************************************************/
489 dbblock a pointer to an open database block
490 key the key of the record to be deleted
492 Returns: the yield of the underlying dbm or db "delete" function.
496 dbfn_delete(open_db *dbblock, const uschar *key)
498 int klen = Ustrlen(key) + 1;
499 uschar * key_copy = store_get(klen, key);
500 EXIM_DATUM key_datum;
502 DEBUG(D_hints_lookup) debug_printf_indent("dbfn_delete: key=%s\n", key);
504 memcpy(key_copy, key, klen);
505 exim_datum_init(&key_datum); /* Some DBM libraries require clearing */
506 exim_datum_data_set(&key_datum, key_copy);
507 exim_datum_size_set(&key_datum, klen);
508 return exim_dbdel(dbblock->dbptr, &key_datum);
514 /* XXX This appears to be unused. There's a separate implementation
515 in dbutils.c for dumpdb and fixdb, using the same underlying support.
518 /*************************************************
519 * Scan the keys of a database file *
520 *************************************************/
524 dbblock a pointer to an open database block
525 start TRUE if starting a new scan
526 FALSE if continuing with the current scan
527 cursor a pointer to a pointer to a cursor anchor, for those dbm libraries
528 that use the notion of a cursor
530 Returns: the next record from the file, or
531 NULL if there are no more
535 dbfn_scan(open_db *dbblock, BOOL start, EXIM_CURSOR **cursor)
537 EXIM_DATUM key_datum, value_datum;
540 DEBUG(D_hints_lookup) debug_printf_indent("dbfn_scan\n");
542 /* Some dbm require an initialization */
544 if (start) *cursor = exim_dbcreate_cursor(dbblock->dbptr);
546 exim_datum_init(&key_datum); /* Some DBM libraries require the datum */
547 exim_datum_init(&value_datum); /* to be cleared before use. */
549 yield = exim_dbscan(dbblock->dbptr, &key_datum, &value_datum, start, *cursor)
550 ? US exim_datum_data_get(&key_datum) : NULL;
552 /* Some dbm require a termination */
554 if (!yield) exim_dbdelete_cursor(*cursor);
561 /*************************************************
562 **************************************************
563 * Stand-alone test program *
564 **************************************************
565 *************************************************/
570 main(int argc, char **cargv)
573 int max_db = sizeof(dbblock)/sizeof(open_db);
577 dbdata_wait *dbwait = NULL;
578 uschar **argv = USS cargv;
580 uschar structbuffer[1024];
584 printf("Usage: test_dbfn directory\n");
585 printf("The subdirectory called \"db\" in the given directory is used for\n");
586 printf("the files used in this test program.\n");
592 spool_directory = argv[1];
593 debug_selector = D_all - D_memory;
595 big_buffer = malloc(big_buffer_size);
597 for (i = 0; i < max_db; i++) dbblock[i].dbptr = NULL;
599 printf("\nExim's db functions tester: interface type is %s\n", EXIM_DBTYPE);
600 printf("DBM library: ");
602 #ifdef DB_VERSION_STRING
603 printf("Berkeley DB: %s\n", DB_VERSION_STRING);
604 #elif defined(BTREEVERSION) && defined(HASHVERSION)
606 printf("probably Berkeley DB version 1.8x (native mode)\n");
608 printf("probably Berkeley DB version 1.8x (compatibility mode)\n");
610 #elif defined(_DBM_RDONLY) || defined(dbm_dirfno)
611 printf("probably ndbm\n");
612 #elif defined(USE_TDB)
613 printf("using tdb\n");
616 printf("probably GDBM (native mode)\n");
618 printf("probably GDBM (compatibility mode)\n");
622 /* Test the functions */
624 printf("\nTest the functions\n> ");
626 while (Ufgets(buffer, 256, stdin) != NULL)
628 int len = Ustrlen(buffer);
632 uschar *cmd = buffer;
633 while (len > 0 && isspace((uschar)buffer[len-1])) len--;
636 if (isdigit((uschar)*cmd))
639 while (isdigit((uschar)*cmd)) cmd++;
640 Uskip_whitespace(&cmd);
643 if (Ustrncmp(cmd, "open", 4) == 0)
648 Uskip_whitespace(&s);
650 for (i = 0; i < max_db; i++)
651 if (dbblock[i].dbptr == NULL) break;
655 printf("Too many open databases\n> ");
660 odb = dbfn_open(s, O_RDWR, dbblock + i, TRUE, TRUE);
666 printf("opened %d\n", current);
668 /* Other error cases will have written messages */
669 else if (errno == ENOENT)
671 printf("open failed: %s%s\n", strerror(errno),
673 " (or other Berkeley DB error)"
681 else if (Ustrncmp(cmd, "write", 5) == 0)
684 uschar * key = cmd + 5, * data;
688 printf("No current database\n");
692 Uskip_whitespace(&key);
694 Uskip_nonwhite(&data);
696 Uskip_whitespace(&data);
698 dbwait = (dbdata_wait *)(&structbuffer);
699 Ustrcpy(dbwait->text, data);
703 rc = dbfn_write(dbblock + current, key, dbwait,
704 Ustrlen(data) + sizeof(dbdata_wait));
706 if (rc != 0) printf("Failed: %s\n", strerror(errno));
709 else if (Ustrncmp(cmd, "read", 4) == 0)
711 uschar * key = cmd + 4;
714 printf("No current database\n");
717 Uskip_whitespace(&key);
720 dbwait = (dbdata_wait *)dbfn_read_with_length(dbblock+ current, key, NULL);
722 printf("%s\n", (dbwait == NULL)? "<not found>" : CS dbwait->text);
725 else if (Ustrncmp(cmd, "delete", 6) == 0)
727 uschar * key = cmd + 6;
730 printf("No current database\n");
733 Uskip_whitespace(&key);
734 dbfn_delete(dbblock + current, key);
737 else if (Ustrncmp(cmd, "scan", 4) == 0)
740 BOOL startflag = TRUE;
742 uschar keybuffer[256];
745 printf("No current database\n");
749 while ((key = dbfn_scan(dbblock + current, startflag, &cursor)) != NULL)
752 Ustrcpy(keybuffer, key);
753 dbwait = (dbdata_wait *)dbfn_read_with_length(dbblock + current,
755 printf("%s: %s\n", keybuffer, dbwait->text);
758 printf("End of scan\n");
761 else if (Ustrncmp(cmd, "close", 5) == 0)
763 uschar * s = cmd + 5;
764 Uskip_whitespace(&s);
766 if (i >= max_db || dbblock[i].dbptr == NULL) printf("Not open\n"); else
769 dbfn_close(dbblock + i);
771 dbblock[i].dbptr = NULL;
772 if (i == current) current = -1;
776 else if (Ustrncmp(cmd, "file", 4) == 0)
778 uschar * s = cmd + 4;
779 Uskip_whitespace(&s);
781 if (i >= max_db || dbblock[i].dbptr == NULL) printf("Not open\n");
785 else if (Ustrncmp(cmd, "time", 4) == 0)
787 showtime = ~showtime;
788 printf("Timing %s\n", showtime? "on" : "off");
791 else if (Ustrcmp(cmd, "q") == 0 || Ustrncmp(cmd, "quit", 4) == 0) break;
793 else if (Ustrncmp(cmd, "help", 4) == 0)
795 printf("close [<number>] close file [<number>]\n");
796 printf("delete <key> remove record from current file\n");
797 printf("file <number> make file <number> current\n");
798 printf("open <name> open db file\n");
799 printf("q[uit] exit program\n");
800 printf("read <key> read record from current file\n");
801 printf("scan scan current file\n");
802 printf("time time display on/off\n");
803 printf("write <key> <rest-of-line> write record to current file\n");
806 else printf("Eh?\n");
808 if (showtime && stop >= start)
809 printf("start=%d stop=%d difference=%d\n", (int)start, (int)stop,
810 (int)(stop - start));
815 for (i = 0; i < max_db; i++)
817 if (dbblock[i].dbptr != NULL)
819 printf("\nClosing %d", i);
820 dbfn_close(dbblock + i);