summaryrefslogtreecommitdiffstats
path: root/src/global/mkmap_open.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/global/mkmap_open.c')
-rw-r--r--src/global/mkmap_open.c313
1 files changed, 313 insertions, 0 deletions
diff --git a/src/global/mkmap_open.c b/src/global/mkmap_open.c
new file mode 100644
index 0000000..9d15eec
--- /dev/null
+++ b/src/global/mkmap_open.c
@@ -0,0 +1,313 @@
+/*++
+/* NAME
+/* mkmap_open 3
+/* SUMMARY
+/* create or rewrite database, generic interface
+/* SYNOPSIS
+/* #include <mkmap.h>
+/*
+/* 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 <sys_defs.h>
+#include <unistd.h>
+#include <string.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <htable.h>
+#include <dict.h>
+#include <dict_db.h>
+#include <dict_cdb.h>
+#include <dict_dbm.h>
+#include <dict_lmdb.h>
+#include <dict_sdbm.h>
+#include <dict_proxy.h>
+#include <dict_fail.h>
+#include <sigdelay.h>
+#include <mymalloc.h>
+#include <stringops.h>
+
+/* 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);
+}