~ubuntu-branches/ubuntu/trusty/postfix/trusty-proposed

« back to all changes in this revision

Viewing changes to src/util/dict_dbm.c

  • Committer: Bazaar Package Importer
  • Author(s): LaMont Jones
  • Date: 2005-02-27 09:33:07 UTC
  • Revision ID: james.westby@ubuntu.com-20050227093307-cn789t27ibnlh6tf
Tags: upstream-2.1.5
ImportĀ upstreamĀ versionĀ 2.1.5

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*++
 
2
/* NAME
 
3
/*      dict_dbm 3
 
4
/* SUMMARY
 
5
/*      dictionary manager interface to DBM files
 
6
/* SYNOPSIS
 
7
/*      #include <dict_dbm.h>
 
8
/*
 
9
/*      DICT    *dict_dbm_open(path, open_flags, dict_flags)
 
10
/*      const char *name;
 
11
/*      const char *path;
 
12
/*      int     open_flags;
 
13
/*      int     dict_flags;
 
14
/* DESCRIPTION
 
15
/*      dict_dbm_open() opens the named DBM database and makes it available
 
16
/*      via the generic interface described in dict_open(3).
 
17
/* DIAGNOSTICS
 
18
/*      Fatal errors: cannot open file, file write error, out of memory.
 
19
/* SEE ALSO
 
20
/*      dict(3) generic dictionary manager
 
21
/*      ndbm(3) data base subroutines
 
22
/* LICENSE
 
23
/* .ad
 
24
/* .fi
 
25
/*      The Secure Mailer license must be distributed with this software.
 
26
/* AUTHOR(S)
 
27
/*      Wietse Venema
 
28
/*      IBM T.J. Watson Research
 
29
/*      P.O. Box 704
 
30
/*      Yorktown Heights, NY 10598, USA
 
31
/*--*/
 
32
 
 
33
#include "sys_defs.h"
 
34
 
 
35
#ifdef HAS_DBM
 
36
 
 
37
/* System library. */
 
38
 
 
39
#include <sys/stat.h>
 
40
#ifdef PATH_NDBM_H
 
41
#include PATH_NDBM_H
 
42
#else
 
43
#include <ndbm.h>
 
44
#endif
 
45
#ifdef R_FIRST
 
46
#error "Error: you are including the Berkeley DB version of ndbm.h"
 
47
#error "To build with Postfix NDBM support, delete the Berkeley DB ndbm.h file"
 
48
#endif
 
49
#include <string.h>
 
50
#include <unistd.h>
 
51
 
 
52
/* Utility library. */
 
53
 
 
54
#include "msg.h"
 
55
#include "mymalloc.h"
 
56
#include "htable.h"
 
57
#include "iostuff.h"
 
58
#include "vstring.h"
 
59
#include "myflock.h"
 
60
#include "stringops.h"
 
61
#include "dict.h"
 
62
#include "dict_dbm.h"
 
63
 
 
64
/* Application-specific. */
 
65
 
 
66
typedef struct {
 
67
    DICT    dict;                       /* generic members */
 
68
    DBM    *dbm;                        /* open database */
 
69
} DICT_DBM;
 
70
 
 
71
/* dict_dbm_lookup - find database entry */
 
72
 
 
73
static const char *dict_dbm_lookup(DICT *dict, const char *name)
 
74
{
 
75
    DICT_DBM *dict_dbm = (DICT_DBM *) dict;
 
76
    datum   dbm_key;
 
77
    datum   dbm_value;
 
78
    static VSTRING *buf;
 
79
    const char *result = 0;
 
80
 
 
81
    dict_errno = 0;
 
82
 
 
83
    /*
 
84
     * Acquire an exclusive lock.
 
85
     */
 
86
    if ((dict->flags & DICT_FLAG_LOCK)
 
87
        && myflock(dict->lock_fd, INTERNAL_LOCK, MYFLOCK_OP_SHARED) < 0)
 
88
        msg_fatal("%s: lock dictionary: %m", dict_dbm->dict.name);
 
89
 
 
90
    /*
 
91
     * See if this DBM file was written with one null byte appended to key
 
92
     * and value.
 
93
     */
 
94
    if (dict->flags & DICT_FLAG_TRY1NULL) {
 
95
        dbm_key.dptr = (void *) name;
 
96
        dbm_key.dsize = strlen(name) + 1;
 
97
        dbm_value = dbm_fetch(dict_dbm->dbm, dbm_key);
 
98
        if (dbm_value.dptr != 0) {
 
99
            dict->flags &= ~DICT_FLAG_TRY0NULL;
 
100
            result = dbm_value.dptr;
 
101
        }
 
102
    }
 
103
 
 
104
    /*
 
105
     * See if this DBM file was written with no null byte appended to key and
 
106
     * value.
 
107
     */
 
108
    if (result == 0 && (dict->flags & DICT_FLAG_TRY0NULL)) {
 
109
        dbm_key.dptr = (void *) name;
 
110
        dbm_key.dsize = strlen(name);
 
111
        dbm_value = dbm_fetch(dict_dbm->dbm, dbm_key);
 
112
        if (dbm_value.dptr != 0) {
 
113
            if (buf == 0)
 
114
                buf = vstring_alloc(10);
 
115
            vstring_strncpy(buf, dbm_value.dptr, dbm_value.dsize);
 
116
            dict->flags &= ~DICT_FLAG_TRY1NULL;
 
117
            result = vstring_str(buf);
 
118
        }
 
119
    }
 
120
 
 
121
    /*
 
122
     * Release the exclusive lock.
 
123
     */
 
124
    if ((dict->flags & DICT_FLAG_LOCK)
 
125
        && myflock(dict->lock_fd, INTERNAL_LOCK, MYFLOCK_OP_NONE) < 0)
 
126
        msg_fatal("%s: unlock dictionary: %m", dict_dbm->dict.name);
 
127
 
 
128
    return (result);
 
129
}
 
130
 
 
131
/* dict_dbm_update - add or update database entry */
 
132
 
 
133
static void dict_dbm_update(DICT *dict, const char *name, const char *value)
 
134
{
 
135
    DICT_DBM *dict_dbm = (DICT_DBM *) dict;
 
136
    datum   dbm_key;
 
137
    datum   dbm_value;
 
138
    int     status;
 
139
 
 
140
    dbm_key.dptr = (void *) name;
 
141
    dbm_value.dptr = (void *) value;
 
142
    dbm_key.dsize = strlen(name);
 
143
    dbm_value.dsize = strlen(value);
 
144
 
 
145
    /*
 
146
     * If undecided about appending a null byte to key and value, choose a
 
147
     * default depending on the platform.
 
148
     */
 
149
    if ((dict->flags & DICT_FLAG_TRY1NULL)
 
150
        && (dict->flags & DICT_FLAG_TRY0NULL)) {
 
151
#ifdef DBM_NO_TRAILING_NULL
 
152
        dict->flags &= ~DICT_FLAG_TRY1NULL;
 
153
#else
 
154
        dict->flags &= ~DICT_FLAG_TRY0NULL;
 
155
#endif
 
156
    }
 
157
 
 
158
    /*
 
159
     * Optionally append a null byte to key and value.
 
160
     */
 
161
    if (dict->flags & DICT_FLAG_TRY1NULL) {
 
162
        dbm_key.dsize++;
 
163
        dbm_value.dsize++;
 
164
    }
 
165
 
 
166
    /*
 
167
     * Acquire an exclusive lock.
 
168
     */
 
169
    if ((dict->flags & DICT_FLAG_LOCK)
 
170
        && myflock(dict->lock_fd, INTERNAL_LOCK, MYFLOCK_OP_EXCLUSIVE) < 0)
 
171
        msg_fatal("%s: lock dictionary: %m", dict_dbm->dict.name);
 
172
 
 
173
    /*
 
174
     * Do the update.
 
175
     */
 
176
    if ((status = dbm_store(dict_dbm->dbm, dbm_key, dbm_value,
 
177
     (dict->flags & DICT_FLAG_DUP_REPLACE) ? DBM_REPLACE : DBM_INSERT)) < 0)
 
178
        msg_fatal("error writing DBM database %s: %m", dict_dbm->dict.name);
 
179
    if (status) {
 
180
        if (dict->flags & DICT_FLAG_DUP_IGNORE)
 
181
             /* void */ ;
 
182
        else if (dict->flags & DICT_FLAG_DUP_WARN)
 
183
            msg_warn("%s: duplicate entry: \"%s\"", dict_dbm->dict.name, name);
 
184
        else
 
185
            msg_fatal("%s: duplicate entry: \"%s\"", dict_dbm->dict.name, name);
 
186
    }
 
187
 
 
188
    /*
 
189
     * Release the exclusive lock.
 
190
     */
 
191
    if ((dict->flags & DICT_FLAG_LOCK)
 
192
        && myflock(dict->lock_fd, INTERNAL_LOCK, MYFLOCK_OP_NONE) < 0)
 
193
        msg_fatal("%s: unlock dictionary: %m", dict_dbm->dict.name);
 
194
}
 
195
 
 
196
/* dict_dbm_delete - delete one entry from the dictionary */
 
197
 
 
198
static int dict_dbm_delete(DICT *dict, const char *name)
 
199
{
 
200
    DICT_DBM *dict_dbm = (DICT_DBM *) dict;
 
201
    datum   dbm_key;
 
202
    int     status = 1;
 
203
    int     flags = 0;
 
204
 
 
205
    /*
 
206
     * Acquire an exclusive lock.
 
207
     */
 
208
    if ((dict->flags & DICT_FLAG_LOCK)
 
209
        && myflock(dict->lock_fd, INTERNAL_LOCK, MYFLOCK_OP_EXCLUSIVE) < 0)
 
210
        msg_fatal("%s: lock dictionary: %m", dict_dbm->dict.name);
 
211
 
 
212
    /*
 
213
     * See if this DBM file was written with one null byte appended to key
 
214
     * and value.
 
215
     */
 
216
    if (dict->flags & DICT_FLAG_TRY1NULL) {
 
217
        dbm_key.dptr = (void *) name;
 
218
        dbm_key.dsize = strlen(name) + 1;
 
219
        dbm_clearerr(dict_dbm->dbm);
 
220
        if ((status = dbm_delete(dict_dbm->dbm, dbm_key)) < 0) {
 
221
            if (dbm_error(dict_dbm->dbm) != 0)  /* fatal error */
 
222
                msg_fatal("error deleting from %s: %m", dict_dbm->dict.name);
 
223
            status = 1;                         /* not found */
 
224
        } else {
 
225
            dict->flags &= ~DICT_FLAG_TRY0NULL; /* found */
 
226
        }
 
227
    }
 
228
 
 
229
    /*
 
230
     * See if this DBM file was written with no null byte appended to key and
 
231
     * value.
 
232
     */
 
233
    if (status > 0 && (dict->flags & DICT_FLAG_TRY0NULL)) {
 
234
        dbm_key.dptr = (void *) name;
 
235
        dbm_key.dsize = strlen(name);
 
236
        dbm_clearerr(dict_dbm->dbm);
 
237
        if ((status = dbm_delete(dict_dbm->dbm, dbm_key)) < 0) {
 
238
            if (dbm_error(dict_dbm->dbm) != 0)  /* fatal error */
 
239
                msg_fatal("error deleting from %s: %m", dict_dbm->dict.name);
 
240
            status = 1;                         /* not found */
 
241
        } else {
 
242
            dict->flags &= ~DICT_FLAG_TRY1NULL; /* found */
 
243
        }
 
244
    }
 
245
 
 
246
    /*
 
247
     * Release the exclusive 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_dbm->dict.name);
 
252
 
 
253
    return (status);
 
254
}
 
255
 
 
256
/* traverse the dictionary */
 
257
 
 
258
static int dict_dbm_sequence(DICT *dict, int function,
 
259
                                     const char **key, const char **value)
 
260
{
 
261
    char   *myname = "dict_dbm_sequence";
 
262
    DICT_DBM *dict_dbm = (DICT_DBM *) dict;
 
263
    datum   dbm_key;
 
264
    datum   dbm_value;
 
265
    int     status = 0;
 
266
    static VSTRING *key_buf;
 
267
    static VSTRING *value_buf;
 
268
 
 
269
    /*
 
270
     * Acquire an exclusive lock.
 
271
     */
 
272
    if ((dict->flags & DICT_FLAG_LOCK)
 
273
        && myflock(dict->lock_fd, INTERNAL_LOCK, MYFLOCK_OP_EXCLUSIVE) < 0)
 
274
        msg_fatal("%s: lock dictionary: %m", dict_dbm->dict.name);
 
275
 
 
276
    /*
 
277
     * Determine and execute the seek function. It returns the key.
 
278
     */
 
279
    switch (function) {
 
280
    case DICT_SEQ_FUN_FIRST:
 
281
        dbm_key = dbm_firstkey(dict_dbm->dbm);
 
282
        break;
 
283
    case DICT_SEQ_FUN_NEXT:
 
284
        dbm_key = dbm_nextkey(dict_dbm->dbm);
 
285
        break;
 
286
    default:
 
287
        msg_panic("%s: invalid function: %d", myname, function);
 
288
    }
 
289
 
 
290
    /*
 
291
     * Release the exclusive lock.
 
292
     */
 
293
    if ((dict->flags & DICT_FLAG_LOCK)
 
294
        && myflock(dict->lock_fd, INTERNAL_LOCK, MYFLOCK_OP_NONE) < 0)
 
295
        msg_fatal("%s: unlock dictionary: %m", dict_dbm->dict.name);
 
296
 
 
297
    if (dbm_key.dptr != 0 && dbm_key.dsize > 0) {
 
298
 
 
299
        /*
 
300
         * See if this DB file was written with one null byte appended to key
 
301
         * an d value or not. If necessary, copy the key.
 
302
         */
 
303
        if (((char *) dbm_key.dptr)[dbm_key.dsize - 1] == 0) {
 
304
            *key = dbm_key.dptr;
 
305
        } else {
 
306
            if (key_buf == 0)
 
307
                key_buf = vstring_alloc(10);
 
308
            vstring_strncpy(key_buf, dbm_key.dptr, dbm_key.dsize);
 
309
            *key = vstring_str(key_buf);
 
310
        }
 
311
 
 
312
        /*
 
313
         * Fetch the corresponding value.
 
314
         */
 
315
        dbm_value = dbm_fetch(dict_dbm->dbm, dbm_key);
 
316
 
 
317
        if (dbm_value.dptr != 0 && dbm_value.dsize > 0) {
 
318
 
 
319
            /*
 
320
             * See if this DB file was written with one null byte appended to
 
321
             * key and value or not. If necessary, copy the key.
 
322
             */
 
323
            if (((char *) dbm_value.dptr)[dbm_value.dsize - 1] == 0) {
 
324
                *value = dbm_value.dptr;
 
325
            } else {
 
326
                if (value_buf == 0)
 
327
                    value_buf = vstring_alloc(10);
 
328
                vstring_strncpy(value_buf, dbm_value.dptr, dbm_value.dsize);
 
329
                *value = vstring_str(value_buf);
 
330
            }
 
331
        } else {
 
332
 
 
333
            /*
 
334
             * Determine if we have hit the last record or an error
 
335
             * condition.
 
336
             */
 
337
            if (dbm_error(dict_dbm->dbm))
 
338
                msg_fatal("error seeking %s: %m", dict_dbm->dict.name);
 
339
            return (1);                         /* no error: eof/not found
 
340
                                                 * (should not happen!) */
 
341
        }
 
342
    } else {
 
343
 
 
344
        /*
 
345
         * Determine if we have hit the last record or an error condition.
 
346
         */
 
347
        if (dbm_error(dict_dbm->dbm))
 
348
            msg_fatal("error seeking %s: %m", dict_dbm->dict.name);
 
349
        return (1);                             /* no error: eof/not found */
 
350
    }
 
351
    return (0);
 
352
}
 
353
 
 
354
/* dict_dbm_close - disassociate from data base */
 
355
 
 
356
static void dict_dbm_close(DICT *dict)
 
357
{
 
358
    DICT_DBM *dict_dbm = (DICT_DBM *) dict;
 
359
 
 
360
    dbm_close(dict_dbm->dbm);
 
361
    dict_free(dict);
 
362
}
 
363
 
 
364
/* dict_dbm_open - open DBM data base */
 
365
 
 
366
DICT   *dict_dbm_open(const char *path, int open_flags, int dict_flags)
 
367
{
 
368
    DICT_DBM *dict_dbm;
 
369
    struct stat st;
 
370
    DBM    *dbm;
 
371
    char   *dbm_path;
 
372
    int     lock_fd;
 
373
 
 
374
    /*
 
375
     * Note: DICT_FLAG_LOCK is used only by programs that do fine-grained (in
 
376
     * the time domain) locking while accessing individual database records.
 
377
     * 
 
378
     * Programs such as postmap/postalias use their own large-grained (in the
 
379
     * time domain) locks while rewriting the entire file.
 
380
     */
 
381
    if (dict_flags & DICT_FLAG_LOCK) {
 
382
        dbm_path = concatenate(path, ".dir", (char *) 0);
 
383
        if ((lock_fd = open(dbm_path, open_flags, 0644)) < 0)
 
384
            msg_fatal("open database %s: %m", dbm_path);
 
385
        if (myflock(lock_fd, INTERNAL_LOCK, MYFLOCK_OP_SHARED) < 0)
 
386
            msg_fatal("shared-lock database %s for open: %m", dbm_path);
 
387
    }
 
388
 
 
389
    /*
 
390
     * XXX SunOS 5.x has no const in dbm_open() prototype.
 
391
     */
 
392
    if ((dbm = dbm_open((char *) path, open_flags, 0644)) == 0)
 
393
        msg_fatal("open database %s.{dir,pag}: %m", path);
 
394
 
 
395
    if (dict_flags & DICT_FLAG_LOCK) {
 
396
        if (myflock(lock_fd, INTERNAL_LOCK, MYFLOCK_OP_NONE) < 0)
 
397
            msg_fatal("unlock database %s for open: %m", dbm_path);
 
398
        if (close(lock_fd) < 0)
 
399
            msg_fatal("close database %s: %m", dbm_path);
 
400
    }
 
401
    dict_dbm = (DICT_DBM *) dict_alloc(DICT_TYPE_DBM, path, sizeof(*dict_dbm));
 
402
    dict_dbm->dict.lookup = dict_dbm_lookup;
 
403
    dict_dbm->dict.update = dict_dbm_update;
 
404
    dict_dbm->dict.delete = dict_dbm_delete;
 
405
    dict_dbm->dict.sequence = dict_dbm_sequence;
 
406
    dict_dbm->dict.close = dict_dbm_close;
 
407
    dict_dbm->dict.lock_fd = dbm_dirfno(dbm);
 
408
    dict_dbm->dict.stat_fd = dbm_pagfno(dbm);
 
409
    if (dict_dbm->dict.lock_fd == dict_dbm->dict.stat_fd)
 
410
        msg_fatal("open database %s: cannot support GDBM", path);
 
411
    if (fstat(dict_dbm->dict.stat_fd, &st) < 0)
 
412
        msg_fatal("dict_dbm_open: fstat: %m");
 
413
    dict_dbm->dict.mtime = st.st_mtime;
 
414
 
 
415
    /*
 
416
     * Warn if the source file is newer than the indexed file, except when
 
417
     * the source file changed only seconds ago.
 
418
     */
 
419
    if ((dict_flags & DICT_FLAG_LOCK) != 0
 
420
        && stat(path, &st) == 0
 
421
        && st.st_mtime > dict_dbm->dict.mtime
 
422
        && st.st_mtime < time((time_t *) 0) - 100)
 
423
        msg_warn("database %s is older than source file %s", dbm_path, path);
 
424
 
 
425
    close_on_exec(dbm_pagfno(dbm), CLOSE_ON_EXEC);
 
426
    close_on_exec(dbm_dirfno(dbm), CLOSE_ON_EXEC);
 
427
    dict_dbm->dict.flags = dict_flags | DICT_FLAG_FIXED;
 
428
    if ((dict_flags & (DICT_FLAG_TRY0NULL | DICT_FLAG_TRY1NULL)) == 0)
 
429
        dict_dbm->dict.flags |= (DICT_FLAG_TRY0NULL | DICT_FLAG_TRY1NULL);
 
430
    dict_dbm->dbm = dbm;
 
431
 
 
432
    if ((dict_flags & DICT_FLAG_LOCK))
 
433
        myfree(dbm_path);
 
434
 
 
435
    return (DICT_DEBUG (&dict_dbm->dict));
 
436
}
 
437
 
 
438
#endif