Hintsdb: tidy coding for DB create
[exim.git] / src / src / dbfn.c
1 /*************************************************
2 *     Exim - an Internet mail transport agent    *
3 *************************************************/
4
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 */
9
10
11 #include "exim.h"
12
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> */
16 #define PATHLEN 256
17
18
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.
24
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.
28
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
31 sequentially.
32
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.
39
40 API:
41   dbfn_open
42   dbfn_close
43   dbfn_read_with_length
44   dbfn_read_enforce_length
45   dbfn_write
46   dbfn_delete
47   dbfn_scan                             unused; ifdeffout out
48
49 Users:
50   ACL ratelimit & seen conditions
51   delivery retry handling
52   delivery serialization
53   TLS session resumption
54   peer capability cache
55   callout & quota cache
56 */
57
58
59
60 /*************************************************
61 *          Open and lock a database file         *
62 *************************************************/
63
64 /* Used for accessing Exim's hints databases.
65
66 Arguments:
67   name     The single-component name of one of Exim's database files.
68   flags    Either O_RDONLY or O_RDWR, indicating the type of open required;
69              O_RDWR implies "create if necessary"
70   dbblock  Points to an open_db block to be filled in.
71   lof      If TRUE, write to the log for actual open failures (locking failures
72            are always logged).
73   panic    If TRUE, panic on failure to create the db directory
74
75 Returns:   NULL if the open failed, or the locking failed. After locking
76            failures, errno is zero.
77
78            On success, dbblock is returned. This contains the dbm pointer and
79            the fd of the locked lock file.
80 */
81
82 open_db *
83 dbfn_open(const uschar * name, int flags, open_db * dbblock,
84   BOOL lof, BOOL panic)
85 {
86 int rc, save_errno;
87 BOOL read_only = flags & O_RDONLY;
88 flock_t lock_data;
89 uschar dirname[PATHLEN], filename[PATHLEN];
90
91 DEBUG(D_hints_lookup) acl_level++;
92
93 /* The first thing to do is to open a separate file on which to lock. This
94 ensures that Exim has exclusive use of the database before it even tries to
95 open it. Early versions tried to lock on the open database itself, but that
96 gave rise to mysterious problems from time to time - it was suspected that some
97 DB libraries "do things" on their open() calls which break the interlocking.
98 The lock file is never written to, but we open it for writing so we can get a
99 write lock if required. If it does not exist, we create it. This is done
100 separately so we know when we have done it, because when running as root we
101 need to change the ownership - see the bottom of this function. We also try to
102 make the directory as well, just in case. We won't be doing this many times
103 unnecessarily, because usually the lock file will be there. If the directory
104 exists, there is no error. */
105
106 snprintf(CS dirname, sizeof(dirname), "%s/db", spool_directory);
107 snprintf(CS filename, sizeof(filename), "%s/%s.lockfile", dirname, name);
108
109 priv_drop_temp(exim_uid, exim_gid);
110 if ((dbblock->lockfd = Uopen(filename, O_RDWR, EXIMDB_LOCKFILE_MODE)) < 0)
111   {
112   (void)directory_make(spool_directory, US"db", EXIMDB_DIRECTORY_MODE, panic);
113   dbblock->lockfd = Uopen(filename, O_RDWR|O_CREAT, EXIMDB_LOCKFILE_MODE);
114   }
115 priv_restore();
116
117 if (dbblock->lockfd < 0)
118   {
119   log_write(0, LOG_MAIN, "%s",
120     string_open_failed("database lock file %s", filename));
121   errno = 0;      /* Indicates locking failure */
122   DEBUG(D_hints_lookup) acl_level--;
123   return NULL;
124   }
125
126 /* Now we must get a lock on the opened lock file; do this with a blocking
127 lock that times out. */
128
129 lock_data.l_type = read_only? F_RDLCK : F_WRLCK;
130 lock_data.l_whence = lock_data.l_start = lock_data.l_len = 0;
131
132 DEBUG(D_hints_lookup|D_retry|D_route|D_deliver)
133   debug_printf_indent("locking %s\n", filename);
134
135 sigalrm_seen = FALSE;
136 ALARM(EXIMDB_LOCK_TIMEOUT);
137 rc = fcntl(dbblock->lockfd, F_SETLKW, &lock_data);
138 ALARM_CLR(0);
139
140 if (sigalrm_seen) errno = ETIMEDOUT;
141 if (rc < 0)
142   {
143   log_write(0, LOG_MAIN|LOG_PANIC, "Failed to get %s lock for %s: %s",
144     read_only ? "read" : "write", filename,
145     errno == ETIMEDOUT ? "timed out" : strerror(errno));
146   (void)close(dbblock->lockfd);
147   errno = 0;       /* Indicates locking failure */
148   DEBUG(D_hints_lookup) acl_level--;
149   return NULL;
150   }
151
152 DEBUG(D_hints_lookup) debug_printf_indent("locked  %s\n", filename);
153
154 /* At this point we have an opened and locked separate lock file, that is,
155 exclusive access to the database, so we can go ahead and open it. If we are
156 expected to create it, don't do so at first, again so that we can detect
157 whether we need to change its ownership (see comments about the lock file
158 above.) There have been regular reports of crashes while opening hints
159 databases - often this is caused by non-matching db.h and the library. To make
160 it easy to pin this down, there are now debug statements on either side of the
161 open call. */
162
163 flags &= O_RDONLY | O_RDWR;
164 snprintf(CS filename, sizeof(filename), "%s/%s", dirname, name);
165
166 priv_drop_temp(exim_uid, exim_gid);
167 dbblock->dbptr = exim_dbopen(filename, dirname, flags, EXIMDB_MODE);
168 if (!dbblock->dbptr && errno == ENOENT && flags == O_RDWR)
169   {
170   DEBUG(D_hints_lookup)
171     debug_printf_indent("%s appears not to exist: trying to create\n", filename);
172   dbblock->dbptr = exim_dbopen(filename, dirname, flags|O_CREAT, EXIMDB_MODE);
173   }
174 save_errno = errno;
175 priv_restore();
176
177 /* If the open has failed, return NULL, leaving errno set. If lof is TRUE,
178 log the event - also for debugging - but debug only if the file just doesn't
179 exist. */
180
181 if (!dbblock->dbptr)
182   {
183   errno = save_errno;
184   if (lof && save_errno != ENOENT)
185     log_write(0, LOG_MAIN, "%s", string_open_failed("DB file %s",
186         filename));
187   else
188     DEBUG(D_hints_lookup)
189       debug_printf_indent("%s\n", CS string_open_failed("DB file %s",
190           filename));
191   (void)close(dbblock->lockfd);
192   errno = save_errno;
193   DEBUG(D_hints_lookup) acl_level--;
194   return NULL;
195   }
196
197 DEBUG(D_hints_lookup)
198   debug_printf_indent("opened hints database %s: flags=%s\n", filename,
199     flags == O_RDONLY ? "O_RDONLY"
200     : flags == O_RDWR ? "O_RDWR"
201     : "??");
202
203 /* Pass back the block containing the opened database handle and the open fd
204 for the lock. */
205
206 return dbblock;
207 }
208
209
210
211
212 /*************************************************
213 *         Unlock and close a database file       *
214 *************************************************/
215
216 /* Closing a file automatically unlocks it, so after closing the database, just
217 close the lock file.
218
219 Argument: a pointer to an open database block
220 Returns:  nothing
221 */
222
223 void
224 dbfn_close(open_db *dbblock)
225 {
226 exim_dbclose(dbblock->dbptr);
227 (void)close(dbblock->lockfd);
228 DEBUG(D_hints_lookup)
229   { debug_printf_indent("closed hints database and lockfile\n"); acl_level--; }
230 }
231
232
233
234
235 /*************************************************
236 *             Read from database file            *
237 *************************************************/
238
239 /* Passing back the pointer unchanged is useless, because there is
240 no guarantee of alignment. Since all the records used by Exim need
241 to be properly aligned to pick out the timestamps, etc., we might as
242 well do the copying centrally here.
243
244 Most calls don't need the length, so there is a macro called dbfn_read which
245 has two arguments; it calls this function adding NULL as the third.
246
247 Arguments:
248   dbblock   a pointer to an open database block
249   key       the key of the record to be read
250   length    a pointer to an int into which to return the length, if not NULL
251
252 Returns: a pointer to the retrieved record, or
253          NULL if the record is not found
254 */
255
256 void *
257 dbfn_read_with_length(open_db * dbblock, const uschar * key, int * length)
258 {
259 void * yield;
260 EXIM_DATUM key_datum, result_datum;
261 int klen = Ustrlen(key) + 1;
262 uschar * key_copy = store_get(klen, key);
263 unsigned dlen;
264
265 memcpy(key_copy, key, klen);
266
267 DEBUG(D_hints_lookup) debug_printf_indent("dbfn_read: key=%s\n", key);
268
269 exim_datum_init(&key_datum);         /* Some DBM libraries require the datum */
270 exim_datum_init(&result_datum);      /* to be cleared before use. */
271 exim_datum_data_set(&key_datum, key_copy);
272 exim_datum_size_set(&key_datum, klen);
273
274 if (!exim_dbget(dbblock->dbptr, &key_datum, &result_datum))
275   {
276   DEBUG(D_hints_lookup) debug_printf_indent("dbfn_read: null return\n");
277   return NULL;
278   }
279
280 /* Assume the data store could have been tainted.  Properly, we should
281 store the taint status with the data. */
282
283 dlen = exim_datum_size_get(&result_datum);
284 yield = store_get(dlen, GET_TAINTED);
285 memcpy(yield, exim_datum_data_get(&result_datum), dlen);
286 DEBUG(D_hints_lookup) debug_printf_indent("dbfn_read: size %u return\n", dlen);
287 if (length) *length = dlen;
288
289 exim_datum_free(&result_datum);    /* Some DBM libs require freeing */
290 return yield;
291 }
292
293
294 /* Read a record.  If the length is not as expected then delete it, write
295 an error log line, delete the record and return NULL.
296 Use this for fixed-size records (so not retry or wait records).
297
298 Arguments:
299   dbblock   a pointer to an open database block
300   key       the key of the record to be read
301   length    the expected record length
302
303 Returns: a pointer to the retrieved record, or
304          NULL if the record is not found/bad
305 */
306
307 void *
308 dbfn_read_enforce_length(open_db * dbblock, const uschar * key, size_t length)
309 {
310 int rlen;
311 void * yield = dbfn_read_with_length(dbblock, key, &rlen);
312
313 if (yield)
314   {
315   if (rlen == length) return yield;
316   log_write(0, LOG_MAIN|LOG_PANIC, "Bad db record size for '%s'", key);
317   dbfn_delete(dbblock, key);
318   }
319 return NULL;
320 }
321
322
323 /*************************************************
324 *             Write to database file             *
325 *************************************************/
326
327 /*
328 Arguments:
329   dbblock   a pointer to an open database block
330   key       the key of the record to be written
331   ptr       a pointer to the record to be written
332   length    the length of the record to be written
333
334 Returns:    the yield of the underlying dbm or db "write" function. If this
335             is dbm, the value is zero for OK.
336 */
337
338 int
339 dbfn_write(open_db *dbblock, const uschar *key, void *ptr, int length)
340 {
341 EXIM_DATUM key_datum, value_datum;
342 dbdata_generic *gptr = (dbdata_generic *)ptr;
343 int klen = Ustrlen(key) + 1;
344 uschar * key_copy = store_get(klen, key);
345
346 memcpy(key_copy, key, klen);
347 gptr->time_stamp = time(NULL);
348
349 DEBUG(D_hints_lookup)
350   debug_printf_indent("dbfn_write: key=%s datalen %d\n", key, length);
351
352 exim_datum_init(&key_datum);         /* Some DBM libraries require the datum */
353 exim_datum_init(&value_datum);       /* to be cleared before use. */
354 exim_datum_data_set(&key_datum, key_copy);
355 exim_datum_size_set(&key_datum, klen);
356 exim_datum_data_set(&value_datum, ptr);
357 exim_datum_size_set(&value_datum, length);
358 return exim_dbput(dbblock->dbptr, &key_datum, &value_datum);
359 }
360
361
362
363 /*************************************************
364 *           Delete record from database file     *
365 *************************************************/
366
367 /*
368 Arguments:
369   dbblock    a pointer to an open database block
370   key        the key of the record to be deleted
371
372 Returns: the yield of the underlying dbm or db "delete" function.
373 */
374
375 int
376 dbfn_delete(open_db *dbblock, const uschar *key)
377 {
378 int klen = Ustrlen(key) + 1;
379 uschar * key_copy = store_get(klen, key);
380 EXIM_DATUM key_datum;
381
382 DEBUG(D_hints_lookup) debug_printf_indent("dbfn_delete: key=%s\n", key);
383
384 memcpy(key_copy, key, klen);
385 exim_datum_init(&key_datum);         /* Some DBM libraries require clearing */
386 exim_datum_data_set(&key_datum, key_copy);
387 exim_datum_size_set(&key_datum, klen);
388 return exim_dbdel(dbblock->dbptr, &key_datum);
389 }
390
391
392
393 #ifdef notdef
394 /* XXX This appears to be unused.  There's a separate implementation
395 in dbutils.c for dumpdb and fixdb, using the same underlying support.
396 */
397
398 /*************************************************
399 *         Scan the keys of a database file       *
400 *************************************************/
401
402 /*
403 Arguments:
404   dbblock  a pointer to an open database block
405   start    TRUE if starting a new scan
406            FALSE if continuing with the current scan
407   cursor   a pointer to a pointer to a cursor anchor, for those dbm libraries
408            that use the notion of a cursor
409
410 Returns:   the next record from the file, or
411            NULL if there are no more
412 */
413
414 uschar *
415 dbfn_scan(open_db *dbblock, BOOL start, EXIM_CURSOR **cursor)
416 {
417 EXIM_DATUM key_datum, value_datum;
418 uschar *yield;
419
420 DEBUG(D_hints_lookup) debug_printf_indent("dbfn_scan\n");
421
422 /* Some dbm require an initialization */
423
424 if (start) *cursor = exim_dbcreate_cursor(dbblock->dbptr);
425
426 exim_datum_init(&key_datum);         /* Some DBM libraries require the datum */
427 exim_datum_init(&value_datum);       /* to be cleared before use. */
428
429 yield = exim_dbscan(dbblock->dbptr, &key_datum, &value_datum, start, *cursor)
430   ? US exim_datum_data_get(&key_datum) : NULL;
431
432 /* Some dbm require a termination */
433
434 if (!yield) exim_dbdelete_cursor(*cursor);
435 return yield;
436 }
437 #endif
438
439
440
441 /*************************************************
442 **************************************************
443 *             Stand-alone test program           *
444 **************************************************
445 *************************************************/
446
447 #ifdef STAND_ALONE
448
449 int
450 main(int argc, char **cargv)
451 {
452 open_db dbblock[8];
453 int max_db = sizeof(dbblock)/sizeof(open_db);
454 int current = -1;
455 int showtime = 0;
456 int i;
457 dbdata_wait *dbwait = NULL;
458 uschar **argv = USS cargv;
459 uschar buffer[256];
460 uschar structbuffer[1024];
461
462 if (argc != 2)
463   {
464   printf("Usage: test_dbfn directory\n");
465   printf("The subdirectory called \"db\" in the given directory is used for\n");
466   printf("the files used in this test program.\n");
467   return 1;
468   }
469
470 /* Initialize */
471
472 spool_directory = argv[1];
473 debug_selector = D_all - D_memory;
474 debug_file = stderr;
475 big_buffer = malloc(big_buffer_size);
476
477 for (i = 0; i < max_db; i++) dbblock[i].dbptr = NULL;
478
479 printf("\nExim's db functions tester: interface type is %s\n", EXIM_DBTYPE);
480 printf("DBM library: ");
481
482 #ifdef DB_VERSION_STRING
483 printf("Berkeley DB: %s\n", DB_VERSION_STRING);
484 #elif defined(BTREEVERSION) && defined(HASHVERSION)
485   #ifdef USE_DB
486   printf("probably Berkeley DB version 1.8x (native mode)\n");
487   #else
488   printf("probably Berkeley DB version 1.8x (compatibility mode)\n");
489   #endif
490 #elif defined(_DBM_RDONLY) || defined(dbm_dirfno)
491 printf("probably ndbm\n");
492 #elif defined(USE_TDB)
493 printf("using tdb\n");
494 #else
495   #ifdef USE_GDBM
496   printf("probably GDBM (native mode)\n");
497   #else
498   printf("probably GDBM (compatibility mode)\n");
499   #endif
500 #endif
501
502 /* Test the functions */
503
504 printf("\nTest the functions\n> ");
505
506 while (Ufgets(buffer, 256, stdin) != NULL)
507   {
508   int len = Ustrlen(buffer);
509   int count = 1;
510   clock_t start = 1;
511   clock_t stop = 0;
512   uschar *cmd = buffer;
513   while (len > 0 && isspace((uschar)buffer[len-1])) len--;
514   buffer[len] = 0;
515
516   if (isdigit((uschar)*cmd))
517     {
518     count = Uatoi(cmd);
519     while (isdigit((uschar)*cmd)) cmd++;
520     Uskip_whitespace(&cmd);
521     }
522
523   if (Ustrncmp(cmd, "open", 4) == 0)
524     {
525     int i;
526     open_db *odb;
527     uschar *s = cmd + 4;
528     Uskip_whitespace(&s);
529
530     for (i = 0; i < max_db; i++)
531       if (dbblock[i].dbptr == NULL) break;
532
533     if (i >= max_db)
534       {
535       printf("Too many open databases\n> ");
536       continue;
537       }
538
539     start = clock();
540     odb = dbfn_open(s, O_RDWR, dbblock + i, TRUE, TRUE);
541     stop = clock();
542
543     if (odb)
544       {
545       current = i;
546       printf("opened %d\n", current);
547       }
548     /* Other error cases will have written messages */
549     else if (errno == ENOENT)
550       {
551       printf("open failed: %s%s\n", strerror(errno),
552         #ifdef USE_DB
553         " (or other Berkeley DB error)"
554         #else
555         ""
556         #endif
557         );
558       }
559     }
560
561   else if (Ustrncmp(cmd, "write", 5) == 0)
562     {
563     int rc = 0;
564     uschar * key = cmd + 5, * data;
565
566     if (current < 0)
567       {
568       printf("No current database\n");
569       continue;
570       }
571
572     Uskip_whitespace(&key);
573     data = key;
574     Uskip_nonwhite(&data);
575     *data++ = '\0';
576     Uskip_whitespace(&data);
577
578     dbwait = (dbdata_wait *)(&structbuffer);
579     Ustrcpy(dbwait->text, data);
580
581     start = clock();
582     while (count-- > 0)
583       rc = dbfn_write(dbblock + current, key, dbwait,
584         Ustrlen(data) + sizeof(dbdata_wait));
585     stop = clock();
586     if (rc != 0) printf("Failed: %s\n", strerror(errno));
587     }
588
589   else if (Ustrncmp(cmd, "read", 4) == 0)
590     {
591     uschar * key = cmd + 4;
592     if (current < 0)
593       {
594       printf("No current database\n");
595       continue;
596       }
597     Uskip_whitespace(&key);
598     start = clock();
599     while (count-- > 0)
600       dbwait = (dbdata_wait *)dbfn_read_with_length(dbblock+ current, key, NULL);
601     stop = clock();
602     printf("%s\n", (dbwait == NULL)? "<not found>" : CS dbwait->text);
603     }
604
605   else if (Ustrncmp(cmd, "delete", 6) == 0)
606     {
607     uschar * key = cmd + 6;
608     if (current < 0)
609       {
610       printf("No current database\n");
611       continue;
612       }
613     Uskip_whitespace(&key);
614     dbfn_delete(dbblock + current, key);
615     }
616
617   else if (Ustrncmp(cmd, "scan", 4) == 0)
618     {
619     EXIM_CURSOR *cursor;
620     BOOL startflag = TRUE;
621     uschar *key;
622     uschar keybuffer[256];
623     if (current < 0)
624       {
625       printf("No current database\n");
626       continue;
627       }
628     start = clock();
629     while ((key = dbfn_scan(dbblock + current, startflag, &cursor)) != NULL)
630       {
631       startflag = FALSE;
632       Ustrcpy(keybuffer, key);
633       dbwait = (dbdata_wait *)dbfn_read_with_length(dbblock + current,
634         keybuffer, NULL);
635       printf("%s: %s\n", keybuffer, dbwait->text);
636       }
637     stop = clock();
638     printf("End of scan\n");
639     }
640
641   else if (Ustrncmp(cmd, "close", 5) == 0)
642     {
643     uschar * s = cmd + 5;
644     Uskip_whitespace(&s);
645     i = Uatoi(s);
646     if (i >= max_db || dbblock[i].dbptr == NULL) printf("Not open\n"); else
647       {
648       start = clock();
649       dbfn_close(dbblock + i);
650       stop = clock();
651       dbblock[i].dbptr = NULL;
652       if (i == current) current = -1;
653       }
654     }
655
656   else if (Ustrncmp(cmd, "file", 4) == 0)
657     {
658     uschar * s = cmd + 4;
659     Uskip_whitespace(&s);
660     i = Uatoi(s);
661     if (i >= max_db || dbblock[i].dbptr == NULL) printf("Not open\n");
662       else current = i;
663     }
664
665   else if (Ustrncmp(cmd, "time", 4) == 0)
666     {
667     showtime = ~showtime;
668     printf("Timing %s\n", showtime? "on" : "off");
669     }
670
671   else if (Ustrcmp(cmd, "q") == 0 || Ustrncmp(cmd, "quit", 4) == 0) break;
672
673   else if (Ustrncmp(cmd, "help", 4) == 0)
674     {
675     printf("close  [<number>]              close file [<number>]\n");
676     printf("delete <key>                   remove record from current file\n");
677     printf("file   <number>                make file <number> current\n");
678     printf("open   <name>                  open db file\n");
679     printf("q[uit]                         exit program\n");
680     printf("read   <key>                   read record from current file\n");
681     printf("scan                           scan current file\n");
682     printf("time                           time display on/off\n");
683     printf("write  <key> <rest-of-line>    write record to current file\n");
684     }
685
686   else printf("Eh?\n");
687
688   if (showtime && stop >= start)
689     printf("start=%d stop=%d difference=%d\n", (int)start, (int)stop,
690      (int)(stop - start));
691
692   printf("> ");
693   }
694
695 for (i = 0; i < max_db; i++)
696   {
697   if (dbblock[i].dbptr != NULL)
698     {
699     printf("\nClosing %d", i);
700     dbfn_close(dbblock + i);
701     }
702   }
703
704 printf("\n");
705 return 0;
706 }
707
708 #endif
709
710 /* End of dbfn.c */
711 /* vi: aw ai sw=2
712 */