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