/*++ /* NAME /* mkmap_open 3 /* SUMMARY /* create or rewrite database, generic interface /* SYNOPSIS /* #include /* /* typedef struct MKMAP { /* DICT_OPEN_FN open; /* dict_xx_open() */ /* DICT *dict; /* dict_xx_open() result */ /* void (*after_open) (struct MKMAP *); /* may be null */ /* void (*after_close) (struct MKMAP *); /* may be null */ /* int multi_writer; /* multi-writer safe */ /* } MKMAP; /* /* MKMAP *mkmap_open(type, path, open_flags, dict_flags) /* char *type; /* char *path; /* int open_flags; /* int dict_flags; /* /* void mkmap_append(mkmap, key, value, lineno) /* MKMAP *mkmap; /* char *key; /* char *value; /* int lineno; /* /* void mkmap_close(mkmap) /* MKMAP *mkmap; /* /* typedef MKMAP *(*MKMAP_OPEN_FN) (const char *); /* typedef MKMAP_OPEN_FN *(*MKMAP_OPEN_EXTEND_FN) (const char *); /* /* void mkmap_open_register(type, open_fn) /* const char *type; /* MKMAP_OPEN_FN open_fn; /* /* MKMAP_OPEN_EXTEND_FN mkmap_open_extend(call_back) /* MKMAP_OPEN_EXTEND_FN call_back; /* DESCRIPTION /* This module implements support for creating Postfix databases. /* It is a dict(3) wrapper that adds global locking to dict-level /* routines where appropriate. /* /* mkmap_open() creates or truncates the named database, after /* appending the appropriate suffixes to the specified filename. /* Before the database is updated, it is locked for exclusive /* access, and signal delivery is suspended. /* See dict(3) for a description of \fBopen_flags\fR and /* \fBdict_flags\fR. All errors are fatal. /* /* mkmap_append() appends the named (key, value) pair to the /* database. Update errors are fatal; duplicate keys are ignored /* (but a warning is issued). /* \fBlineno\fR is used for diagnostics. /* /* mkmap_close() closes the database, releases any locks, /* and resumes signal delivery. All errors are fatal. /* /* mkmap_open_register() adds support for a new database type. /* /* mkmap_open_extend() registers a call-back function that looks /* up the mkmap open() function for a database type that is not /* registered, or null in case of error. The result value is the /* last previously-registered call-back or null. A mkmap open() /* function is cached after it is looked up through this extension /* mechanism. /* SEE ALSO /* sigdelay(3) suspend/resume signal delivery /* LICENSE /* .ad /* .fi /* The Secure Mailer license must be distributed with this software. /* AUTHOR(S) /* Wietse Venema /* IBM T.J. Watson Research /* P.O. Box 704 /* Yorktown Heights, NY 10598, USA /*--*/ /* System library. */ #include #include #include /* Utility library. */ #include #include #include #include #include #include #include #include #include #include #include #include #include /* Global library. */ #include "mkmap.h" /* * Information about available database types. Here, we list only those map * types that support "bulk create" operations. * * We use a different table (in dict_open.c and mail_dict.c) when querying maps * or when making incremental updates. */ typedef struct { const char *type; MKMAP_OPEN_FN before_open; } MKMAP_OPEN_INFO; static const MKMAP_OPEN_INFO mkmap_open_info[] = { #ifndef USE_DYNAMIC_MAPS #ifdef HAS_CDB DICT_TYPE_CDB, mkmap_cdb_open, #endif #ifdef HAS_SDBM DICT_TYPE_SDBM, mkmap_sdbm_open, #endif #ifdef HAS_LMDB DICT_TYPE_LMDB, mkmap_lmdb_open, #endif #endif /* !USE_DYNAMIC_MAPS */ #ifdef HAS_DBM DICT_TYPE_DBM, mkmap_dbm_open, #endif #ifdef HAS_DB DICT_TYPE_HASH, mkmap_hash_open, DICT_TYPE_BTREE, mkmap_btree_open, #endif DICT_TYPE_FAIL, mkmap_fail_open, 0, }; static HTABLE *mkmap_open_hash; static MKMAP_OPEN_EXTEND_FN mkmap_open_extend_hook = 0; /* mkmap_open_init - one-off initialization */ static void mkmap_open_init(void) { static const char myname[] = "mkmap_open_init"; const MKMAP_OPEN_INFO *mp; if (mkmap_open_hash != 0) msg_panic("%s: multiple initialization", myname); mkmap_open_hash = htable_create(10); for (mp = mkmap_open_info; mp->type; mp++) htable_enter(mkmap_open_hash, mp->type, (void *) mp); } /* mkmap_open_register - register dictionary type */ void mkmap_open_register(const char *type, MKMAP_OPEN_FN open_fn) { static const char myname[] = "mkmap_open_register"; MKMAP_OPEN_INFO *mp; HTABLE_INFO *ht; if (mkmap_open_hash == 0) mkmap_open_init(); if (htable_find(mkmap_open_hash, type)) msg_panic("%s: database type exists: %s", myname, type); mp = (MKMAP_OPEN_INFO *) mymalloc(sizeof(*mp)); mp->before_open = open_fn; ht = htable_enter(mkmap_open_hash, type, (void *) mp); mp->type = ht->key; } /* mkmap_open_extend - register alternate lookup function */ MKMAP_OPEN_EXTEND_FN mkmap_open_extend(MKMAP_OPEN_EXTEND_FN new_cb) { MKMAP_OPEN_EXTEND_FN old_cb; old_cb = mkmap_open_extend_hook; mkmap_open_extend_hook = new_cb; return (old_cb); } /* mkmap_append - append entry to map */ #undef mkmap_append void mkmap_append(MKMAP *mkmap, const char *key, const char *value) { DICT *dict = mkmap->dict; if (dict_put(dict, key, value) != 0 && dict->error != 0) msg_fatal("%s:%s: update failed", dict->type, dict->name); } /* mkmap_close - close database */ void mkmap_close(MKMAP *mkmap) { /* * Close the database. */ dict_close(mkmap->dict); /* * Do whatever special processing is needed after closing the database, * such as releasing a global exclusive lock on the database file. * Individual Postfix dict modules implement locking only for individual * record operations, because most Postfix applications don't need global * exclusive locks. */ if (mkmap->after_close) mkmap->after_close(mkmap); /* * Resume signal delivery. */ if (mkmap->multi_writer == 0) sigresume(); /* * Cleanup. */ myfree((void *) mkmap); } /* mkmap_open - create or truncate database */ MKMAP *mkmap_open(const char *type, const char *path, int open_flags, int dict_flags) { MKMAP *mkmap; const MKMAP_OPEN_INFO *mp; MKMAP_OPEN_FN open_fn; /* * Find out what map type to use. */ if (mkmap_open_hash == 0) mkmap_open_init(); if ((mp = (MKMAP_OPEN_INFO *) htable_find(mkmap_open_hash, type)) == 0) { if (mkmap_open_extend_hook != 0 && (open_fn = mkmap_open_extend_hook(type)) != 0) { mkmap_open_register(type, open_fn); mp = (MKMAP_OPEN_INFO *) htable_find(mkmap_open_hash, type); } if (mp == 0) msg_fatal("unsupported map type for this operation: %s", type); } if (msg_verbose) msg_info("open %s %s", type, path); /* * Do whatever before-open initialization is needed, such as acquiring a * global exclusive lock on an existing database file. Individual Postfix * dict modules implement locking only for individual record operations, * because most Postfix applications don't need global exclusive locks. */ mkmap = mp->before_open(path); /* * Delay signal delivery, so that we won't leave the database in an * inconsistent state if we can avoid it. */ sigdelay(); /* * Truncate the database upon open, and update it. Read-write mode is * needed because the underlying routines read as well as write. We * explicitly clobber lock_fd to trigger a fatal error when a map wants * to unlock the database after individual transactions: that would * result in race condition problems. We clobbber stat_fd as well, * because that, too, is used only for individual-transaction clients. */ mkmap->dict = mkmap->open(path, open_flags, dict_flags); mkmap->dict->lock_fd = -1; /* XXX just in case */ mkmap->dict->stat_fd = -1; /* XXX just in case */ mkmap->dict->flags |= DICT_FLAG_DUP_WARN; mkmap->multi_writer = (mkmap->dict->flags & DICT_FLAG_MULTI_WRITER); /* * Do whatever post-open initialization is needed, such as acquiring a * global exclusive lock on a database file that did not exist. * Individual Postfix dict modules implement locking only for individual * record operations, because most Postfix applications don't need global * exclusive locks. */ if (mkmap->after_open) mkmap->after_open(mkmap); /* * Wrap the dictionary for UTF-8 syntax checks and casefolding. */ if ((mkmap->dict->flags & DICT_FLAG_UTF8_ACTIVE) == 0 && DICT_NEED_UTF8_ACTIVATION(util_utf8_enable, dict_flags)) mkmap->dict = dict_utf8_activate(mkmap->dict); /* * Resume signal delivery if multi-writer safe. */ if (mkmap->multi_writer) sigresume(); return (mkmap); }