Sync usage with man page.
[netbsd-mini2440.git] / external / ibm-public / postfix / dist / src / util / dict_sdbm.c
blob1e32a88f5ffb3ad4ee7a8bb2843fea95c3585b6f
1 /* $NetBSD$ */
3 /*++
4 /* NAME
5 /* dict_sdbm 3
6 /* SUMMARY
7 /* dictionary manager interface to SDBM files
8 /* SYNOPSIS
9 /* #include <dict_sdbm.h>
11 /* DICT *dict_sdbm_open(path, open_flags, dict_flags)
12 /* const char *name;
13 /* const char *path;
14 /* int open_flags;
15 /* int dict_flags;
16 /* DESCRIPTION
17 /* dict_sdbm_open() opens the named SDBM database and makes it available
18 /* via the generic interface described in dict_open(3).
19 /* DIAGNOSTICS
20 /* Fatal errors: cannot open file, file write error, out of memory.
21 /* SEE ALSO
22 /* dict(3) generic dictionary manager
23 /* sdbm(3) data base subroutines
24 /* LICENSE
25 /* .ad
26 /* .fi
27 /* The Secure Mailer license must be distributed with this software.
28 /* AUTHOR(S)
29 /* Wietse Venema
30 /* IBM T.J. Watson Research
31 /* P.O. Box 704
32 /* Yorktown Heights, NY 10598, USA
33 /*--*/
35 #include "sys_defs.h"
37 /* System library. */
39 #include <sys/stat.h>
40 #include <string.h>
41 #include <unistd.h>
42 #ifdef HAS_SDBM
43 #include <sdbm.h>
44 #endif
46 /* Utility library. */
48 #include <msg.h>
49 #include <mymalloc.h>
50 #include <htable.h>
51 #include <iostuff.h>
52 #include <vstring.h>
53 #include <myflock.h>
54 #include <stringops.h>
55 #include <dict.h>
56 #include <dict_sdbm.h>
58 #ifdef HAS_SDBM
60 /* Application-specific. */
62 typedef struct {
63 DICT dict; /* generic members */
64 SDBM *dbm; /* open database */
65 VSTRING *key_buf; /* key buffer */
66 VSTRING *val_buf; /* result buffer */
67 } DICT_SDBM;
69 #define SCOPY(buf, data, size) \
70 vstring_str(vstring_strncpy(buf ? buf : (buf = vstring_alloc(10)), data, size))
72 /* dict_sdbm_lookup - find database entry */
74 static const char *dict_sdbm_lookup(DICT *dict, const char *name)
76 DICT_SDBM *dict_sdbm = (DICT_SDBM *) dict;
77 datum dbm_key;
78 datum dbm_value;
79 const char *result = 0;
82 * Sanity check.
84 if ((dict->flags & (DICT_FLAG_TRY1NULL | DICT_FLAG_TRY0NULL)) == 0)
85 msg_panic("dict_sdbm_lookup: no DICT_FLAG_TRY1NULL | DICT_FLAG_TRY0NULL flag");
87 dict_errno = 0;
90 * Optionally fold the key.
92 if (dict->flags & DICT_FLAG_FOLD_FIX) {
93 if (dict->fold_buf == 0)
94 dict->fold_buf = vstring_alloc(10);
95 vstring_strcpy(dict->fold_buf, name);
96 name = lowercase(vstring_str(dict->fold_buf));
100 * Acquire an exclusive lock.
102 if ((dict->flags & DICT_FLAG_LOCK)
103 && myflock(dict->lock_fd, INTERNAL_LOCK, MYFLOCK_OP_SHARED) < 0)
104 msg_fatal("%s: lock dictionary: %m", dict_sdbm->dict.name);
107 * See if this DBM file was written with one null byte appended to key
108 * and value.
110 if (dict->flags & DICT_FLAG_TRY1NULL) {
111 dbm_key.dptr = (void *) name;
112 dbm_key.dsize = strlen(name) + 1;
113 dbm_value = sdbm_fetch(dict_sdbm->dbm, dbm_key);
114 if (dbm_value.dptr != 0) {
115 dict->flags &= ~DICT_FLAG_TRY0NULL;
116 result = SCOPY(dict_sdbm->val_buf, dbm_value.dptr, dbm_value.dsize);
121 * See if this DBM file was written with no null byte appended to key and
122 * value.
124 if (result == 0 && (dict->flags & DICT_FLAG_TRY0NULL)) {
125 dbm_key.dptr = (void *) name;
126 dbm_key.dsize = strlen(name);
127 dbm_value = sdbm_fetch(dict_sdbm->dbm, dbm_key);
128 if (dbm_value.dptr != 0) {
129 dict->flags &= ~DICT_FLAG_TRY1NULL;
130 result = SCOPY(dict_sdbm->val_buf, dbm_value.dptr, dbm_value.dsize);
135 * Release the exclusive lock.
137 if ((dict->flags & DICT_FLAG_LOCK)
138 && myflock(dict->lock_fd, INTERNAL_LOCK, MYFLOCK_OP_NONE) < 0)
139 msg_fatal("%s: unlock dictionary: %m", dict_sdbm->dict.name);
141 return (result);
144 /* dict_sdbm_update - add or update database entry */
146 static void dict_sdbm_update(DICT *dict, const char *name, const char *value)
148 DICT_SDBM *dict_sdbm = (DICT_SDBM *) dict;
149 datum dbm_key;
150 datum dbm_value;
151 int status;
154 * Sanity check.
156 if ((dict->flags & (DICT_FLAG_TRY1NULL | DICT_FLAG_TRY0NULL)) == 0)
157 msg_panic("dict_sdbm_update: no DICT_FLAG_TRY1NULL | DICT_FLAG_TRY0NULL flag");
160 * Optionally fold the key.
162 if (dict->flags & DICT_FLAG_FOLD_FIX) {
163 if (dict->fold_buf == 0)
164 dict->fold_buf = vstring_alloc(10);
165 vstring_strcpy(dict->fold_buf, name);
166 name = lowercase(vstring_str(dict->fold_buf));
168 dbm_key.dptr = (void *) name;
169 dbm_value.dptr = (void *) value;
170 dbm_key.dsize = strlen(name);
171 dbm_value.dsize = strlen(value);
174 * If undecided about appending a null byte to key and value, choose a
175 * default depending on the platform.
177 if ((dict->flags & DICT_FLAG_TRY1NULL)
178 && (dict->flags & DICT_FLAG_TRY0NULL)) {
179 #ifdef DBM_NO_TRAILING_NULL
180 dict->flags &= ~DICT_FLAG_TRY1NULL;
181 #else
182 dict->flags &= ~DICT_FLAG_TRY0NULL;
183 #endif
187 * Optionally append a null byte to key and value.
189 if (dict->flags & DICT_FLAG_TRY1NULL) {
190 dbm_key.dsize++;
191 dbm_value.dsize++;
195 * Acquire an exclusive lock.
197 if ((dict->flags & DICT_FLAG_LOCK)
198 && myflock(dict->lock_fd, INTERNAL_LOCK, MYFLOCK_OP_EXCLUSIVE) < 0)
199 msg_fatal("%s: lock dictionary: %m", dict_sdbm->dict.name);
202 * Do the update.
204 if ((status = sdbm_store(dict_sdbm->dbm, dbm_key, dbm_value,
205 (dict->flags & DICT_FLAG_DUP_REPLACE) ? DBM_REPLACE : DBM_INSERT)) < 0)
206 msg_fatal("error writing SDBM database %s: %m", dict_sdbm->dict.name);
207 if (status) {
208 if (dict->flags & DICT_FLAG_DUP_IGNORE)
209 /* void */ ;
210 else if (dict->flags & DICT_FLAG_DUP_WARN)
211 msg_warn("%s: duplicate entry: \"%s\"", dict_sdbm->dict.name, name);
212 else
213 msg_fatal("%s: duplicate entry: \"%s\"", dict_sdbm->dict.name, name);
217 * Release the exclusive lock.
219 if ((dict->flags & DICT_FLAG_LOCK)
220 && myflock(dict->lock_fd, INTERNAL_LOCK, MYFLOCK_OP_NONE) < 0)
221 msg_fatal("%s: unlock dictionary: %m", dict_sdbm->dict.name);
224 /* dict_sdbm_delete - delete one entry from the dictionary */
226 static int dict_sdbm_delete(DICT *dict, const char *name)
228 DICT_SDBM *dict_sdbm = (DICT_SDBM *) dict;
229 datum dbm_key;
230 int status = 1;
233 * Sanity check.
235 if ((dict->flags & (DICT_FLAG_TRY1NULL | DICT_FLAG_TRY0NULL)) == 0)
236 msg_panic("dict_sdbm_delete: no DICT_FLAG_TRY1NULL | DICT_FLAG_TRY0NULL flag");
239 * Optionally fold the key.
241 if (dict->flags & DICT_FLAG_FOLD_FIX) {
242 if (dict->fold_buf == 0)
243 dict->fold_buf = vstring_alloc(10);
244 vstring_strcpy(dict->fold_buf, name);
245 name = lowercase(vstring_str(dict->fold_buf));
249 * Acquire an exclusive lock.
251 if ((dict->flags & DICT_FLAG_LOCK)
252 && myflock(dict->lock_fd, INTERNAL_LOCK, MYFLOCK_OP_EXCLUSIVE) < 0)
253 msg_fatal("%s: lock dictionary: %m", dict_sdbm->dict.name);
256 * See if this DBM file was written with one null byte appended to key
257 * and value.
259 if (dict->flags & DICT_FLAG_TRY1NULL) {
260 dbm_key.dptr = (void *) name;
261 dbm_key.dsize = strlen(name) + 1;
262 sdbm_clearerr(dict_sdbm->dbm);
263 if ((status = sdbm_delete(dict_sdbm->dbm, dbm_key)) < 0) {
264 if (sdbm_error(dict_sdbm->dbm) != 0)/* fatal error */
265 msg_fatal("error deleting from %s: %m", dict_sdbm->dict.name);
266 status = 1; /* not found */
267 } else {
268 dict->flags &= ~DICT_FLAG_TRY0NULL; /* found */
273 * See if this DBM file was written with no null byte appended to key and
274 * value.
276 if (status > 0 && (dict->flags & DICT_FLAG_TRY0NULL)) {
277 dbm_key.dptr = (void *) name;
278 dbm_key.dsize = strlen(name);
279 sdbm_clearerr(dict_sdbm->dbm);
280 if ((status = sdbm_delete(dict_sdbm->dbm, dbm_key)) < 0) {
281 if (sdbm_error(dict_sdbm->dbm) != 0)/* fatal error */
282 msg_fatal("error deleting from %s: %m", dict_sdbm->dict.name);
283 status = 1; /* not found */
284 } else {
285 dict->flags &= ~DICT_FLAG_TRY1NULL; /* found */
290 * Release the exclusive lock.
292 if ((dict->flags & DICT_FLAG_LOCK)
293 && myflock(dict->lock_fd, INTERNAL_LOCK, MYFLOCK_OP_NONE) < 0)
294 msg_fatal("%s: unlock dictionary: %m", dict_sdbm->dict.name);
296 return (status);
299 /* traverse the dictionary */
301 static int dict_sdbm_sequence(DICT *dict, const int function,
302 const char **key, const char **value)
304 const char *myname = "dict_sdbm_sequence";
305 DICT_SDBM *dict_sdbm = (DICT_SDBM *) dict;
306 datum dbm_key;
307 datum dbm_value;
310 * Acquire a shared lock.
312 if ((dict->flags & DICT_FLAG_LOCK)
313 && myflock(dict->lock_fd, INTERNAL_LOCK, MYFLOCK_OP_SHARED) < 0)
314 msg_fatal("%s: lock dictionary: %m", dict_sdbm->dict.name);
317 * Determine and execute the seek function. It returns the key.
319 sdbm_clearerr(dict_sdbm->dbm);
320 switch (function) {
321 case DICT_SEQ_FUN_FIRST:
322 dbm_key = sdbm_firstkey(dict_sdbm->dbm);
323 break;
324 case DICT_SEQ_FUN_NEXT:
325 dbm_key = sdbm_nextkey(dict_sdbm->dbm);
326 break;
327 default:
328 msg_panic("%s: invalid function: %d", myname, function);
331 if (dbm_key.dptr != 0 && dbm_key.dsize > 0) {
334 * Copy the key so that it is guaranteed null terminated.
336 *key = SCOPY(dict_sdbm->key_buf, dbm_key.dptr, dbm_key.dsize);
339 * Fetch the corresponding value.
341 dbm_value = sdbm_fetch(dict_sdbm->dbm, dbm_key);
343 if (dbm_value.dptr != 0 && dbm_value.dsize > 0) {
346 * Copy the value so that it is guaranteed null terminated.
348 *value = SCOPY(dict_sdbm->val_buf, dbm_value.dptr, dbm_value.dsize);
349 } else {
352 * Determine if we have hit the last record or an error
353 * condition.
355 if (sdbm_error(dict_sdbm->dbm))
356 msg_fatal("error seeking %s: %m", dict_sdbm->dict.name);
357 return (1); /* no error: eof/not found
358 * (should not happen!) */
360 } else {
363 * Determine if we have hit the last record or an error condition.
365 if (sdbm_error(dict_sdbm->dbm))
366 msg_fatal("error seeking %s: %m", dict_sdbm->dict.name);
367 return (1); /* no error: eof/not found */
371 * Release the shared lock.
373 if ((dict->flags & DICT_FLAG_LOCK)
374 && myflock(dict->lock_fd, INTERNAL_LOCK, MYFLOCK_OP_NONE) < 0)
375 msg_fatal("%s: unlock dictionary: %m", dict_sdbm->dict.name);
377 return (0);
380 /* dict_sdbm_close - disassociate from data base */
382 static void dict_sdbm_close(DICT *dict)
384 DICT_SDBM *dict_sdbm = (DICT_SDBM *) dict;
386 sdbm_close(dict_sdbm->dbm);
387 if (dict_sdbm->key_buf)
388 vstring_free(dict_sdbm->key_buf);
389 if (dict_sdbm->val_buf)
390 vstring_free(dict_sdbm->val_buf);
391 if (dict->fold_buf)
392 vstring_free(dict->fold_buf);
393 dict_free(dict);
396 /* dict_sdbm_open - open SDBM data base */
398 DICT *dict_sdbm_open(const char *path, int open_flags, int dict_flags)
400 DICT_SDBM *dict_sdbm;
401 struct stat st;
402 SDBM *dbm;
403 char *dbm_path;
404 int lock_fd;
407 * Note: DICT_FLAG_LOCK is used only by programs that do fine-grained (in
408 * the time domain) locking while accessing individual database records.
410 * Programs such as postmap/postalias use their own large-grained (in the
411 * time domain) locks while rewriting the entire file.
413 if (dict_flags & DICT_FLAG_LOCK) {
414 dbm_path = concatenate(path, ".dir", (char *) 0);
415 if ((lock_fd = open(dbm_path, open_flags, 0644)) < 0)
416 msg_fatal("open database %s: %m", dbm_path);
417 if (myflock(lock_fd, INTERNAL_LOCK, MYFLOCK_OP_SHARED) < 0)
418 msg_fatal("shared-lock database %s for open: %m", dbm_path);
422 * XXX sdbm_open() has no const in prototype.
424 if ((dbm = sdbm_open((char *) path, open_flags, 0644)) == 0)
425 msg_fatal("open database %s.{dir,pag}: %m", path);
427 if (dict_flags & DICT_FLAG_LOCK) {
428 if (myflock(lock_fd, INTERNAL_LOCK, MYFLOCK_OP_NONE) < 0)
429 msg_fatal("unlock database %s for open: %m", dbm_path);
430 if (close(lock_fd) < 0)
431 msg_fatal("close database %s: %m", dbm_path);
433 dict_sdbm = (DICT_SDBM *) dict_alloc(DICT_TYPE_SDBM, path, sizeof(*dict_sdbm));
434 dict_sdbm->dict.lookup = dict_sdbm_lookup;
435 dict_sdbm->dict.update = dict_sdbm_update;
436 dict_sdbm->dict.delete = dict_sdbm_delete;
437 dict_sdbm->dict.sequence = dict_sdbm_sequence;
438 dict_sdbm->dict.close = dict_sdbm_close;
439 dict_sdbm->dict.lock_fd = sdbm_dirfno(dbm);
440 dict_sdbm->dict.stat_fd = sdbm_pagfno(dbm);
441 if (fstat(dict_sdbm->dict.stat_fd, &st) < 0)
442 msg_fatal("dict_sdbm_open: fstat: %m");
443 dict_sdbm->dict.mtime = st.st_mtime;
446 * Warn if the source file is newer than the indexed file, except when
447 * the source file changed only seconds ago.
449 if ((dict_flags & DICT_FLAG_LOCK) != 0
450 && stat(path, &st) == 0
451 && st.st_mtime > dict_sdbm->dict.mtime
452 && st.st_mtime < time((time_t *) 0) - 100)
453 msg_warn("database %s is older than source file %s", dbm_path, path);
455 close_on_exec(sdbm_pagfno(dbm), CLOSE_ON_EXEC);
456 close_on_exec(sdbm_dirfno(dbm), CLOSE_ON_EXEC);
457 dict_sdbm->dict.flags = dict_flags | DICT_FLAG_FIXED;
458 if ((dict_flags & (DICT_FLAG_TRY0NULL | DICT_FLAG_TRY1NULL)) == 0)
459 dict_sdbm->dict.flags |= (DICT_FLAG_TRY0NULL | DICT_FLAG_TRY1NULL);
460 if (dict_flags & DICT_FLAG_FOLD_FIX)
461 dict_sdbm->dict.fold_buf = vstring_alloc(10);
462 dict_sdbm->dbm = dbm;
463 dict_sdbm->key_buf = 0;
464 dict_sdbm->val_buf = 0;
466 if ((dict_flags & DICT_FLAG_LOCK))
467 myfree(dbm_path);
469 return (DICT_DEBUG (&dict_sdbm->dict));
472 #endif