-TDICT_DB
-TDICT_DBM
-TDICT_DEBUG
--TDICT_DL_INFO
-TDICT_ENV
-TDICT_FAIL
-TDICT_HT
-TDICT_NIS
-TDICT_NISPLUS
-TDICT_NODE
+-TDICT_OPEN_EXTEND_FN
+-TDICT_OPEN_FN
-TDICT_OPEN_INFO
-TDICT_OWNER
-TDICT_PCRE
-TDSN_FILTER
-TDSN_SPLIT
-TDSN_STAT
+-TDYMAP_INFO
-TEC_GROUP
-TEC_KEY
-TEDIT_FILE
-TMKMAP
-TMKMAP_DB
-TMKMAP_DBM
+-TMKMAP_OPEN_EXTEND_FN
+-TMKMAP_OPEN_FN
-TMKMAP_OPEN_INFO
-TMKMAP_SDBM
-TMSG_STATS
poorly reflected their purpose. "DEAD" is replaced with
"FORBIDDEN" (no I/O allowed) and "BAD" is replaced with
"THROTTLED" (anything that causes the queue manager to back
- off from some destination). Files: smtp.h, smtp_coonnect.c,
+ off from some destination). Files: smtp.h, smtp_connect.c,
smtp_proto.c, smtp_trouble.c.
Cleanup: enable SMTP connection cache lookup by destination
print/view README-format files are in the INSTALL file).
Documentation: updated INSTALL instructions and RELEASE_NOTES.
+
+20140512
+
+ Portability: Berkeley DB6 support. File: util/dict_db.c.
+
+20140514
+
+ Cleanup: replace #ifdefs with hard-coded dynamicmaps calls
+ with an extension mechanism. Files: util/dict.h, util/dict_open.c,
+ global/dynamicmaps.[hc], global/mkmap.h, global/mkmap_open.c.
+
+20140515
+
+ Bugfix (introduced: 20140320): missing initialization.
+ Viktor Dukhovni. File pipe/pipe.c.
+
+ Cleanup: mkmap_open() now caches a dynamically-loaded
+ function. This is useful because postmap/postalias may open
+ the same database type multiple times. Files: global/mkmap.h,
+ global/mkmap_open.c.
sendmail_path_macro=DEF_SENDMAIL_PATH
shlib_directory_macro=DEF_SHLIB_DIR
+# shlib_directory is checked here because "no" is not a good answer.
+# Instead, build with "shared=no".
+
for parm_name in command_directory config_directory daemon_directory \
data_directory mailq_path newaliases_path \
queue_directory sendmail_path shlib_directory
cat ../../conf/makedefs.out $? >$@
fsstone: fsstone.o $(LIBS)
- $(CC) $(CFLAGS) -o $@ fsstone.o $(LIBS) $(SYSLIBS)
+ $(CC) $(CFLAGS) $(SHLIB_RPATH) -o $@ fsstone.o $(LIBS) $(SYSLIBS)
test: $(TESTPROG)
match_service.c mail_conf_nint.c addr_match_list.c mail_conf_nbool.c \
smtp_reply_footer.c safe_ultostr.c verify_sender_addr.c \
dict_memcache.c mail_version.c memcache_proto.c server_acl.c \
- mkmap_fail.c haproxy_srvr.c dsn_filter.c
+ mkmap_fail.c haproxy_srvr.c dsn_filter.c dynamicmaps.c
OBJS = abounce.o anvil_clnt.o been_here.o bounce.o bounce_log.o \
canon_addr.o cfg_parser.o cleanup_strerror.o cleanup_strflags.o \
clnt_stream.o conv_time.o db_common.o debug_peer.o debug_process.o \
match_service.o mail_conf_nint.o addr_match_list.o mail_conf_nbool.o \
smtp_reply_footer.o safe_ultostr.o verify_sender_addr.o \
dict_memcache.o mail_version.o memcache_proto.o server_acl.o \
- mkmap_fail.o haproxy_srvr.o dsn_filter.o
+ mkmap_fail.o haproxy_srvr.o dsn_filter.o dynamicmaps.o
HDRS = abounce.h anvil_clnt.h been_here.h bounce.h bounce_log.h \
canon_addr.h cfg_parser.h cleanup_user.h clnt_stream.h config.h \
conv_time.h db_common.h debug_peer.h debug_process.h defer.h \
fold_addr.h header_body_checks.h data_redirect.h match_service.h \
addr_match_list.h smtp_reply_footer.h safe_ultostr.h \
verify_sender_addr.h dict_memcache.h memcache_proto.h server_acl.h \
- haproxy_srvr.h dsn_filter.h
+ haproxy_srvr.h dsn_filter.h dynamicmaps.h
TESTSRC = rec2stream.c stream2rec.c recdump.c
DEFS = -I. -I$(INC_DIR) -D$(SYSTYPE)
CFLAGS = $(DEBUG) $(OPT) $(DEFS)
dsn_util.o: ../../include/vstring.h
dsn_util.o: dsn_util.c
dsn_util.o: dsn_util.h
+dynamicmaps.o: ../../include/argv.h
+dynamicmaps.o: ../../include/dict.h
+dynamicmaps.o: ../../include/load_lib.h
+dynamicmaps.o: ../../include/msg.h
+dynamicmaps.o: ../../include/mvect.h
+dynamicmaps.o: ../../include/myflock.h
+dynamicmaps.o: ../../include/mymalloc.h
+dynamicmaps.o: ../../include/sys_defs.h
+dynamicmaps.o: ../../include/vbuf.h
+dynamicmaps.o: ../../include/vstream.h
+dynamicmaps.o: ../../include/vstring.h
+dynamicmaps.o: ../../include/vstring_vstream.h
+dynamicmaps.o: dynamicmaps.c
+dynamicmaps.o: dynamicmaps.h
+dynamicmaps.o: mkmap.h
ehlo_mask.o: ../../include/name_mask.h
ehlo_mask.o: ../../include/sys_defs.h
ehlo_mask.o: ../../include/vbuf.h
mail_date.o: mail_date.h
mail_dict.o: ../../include/argv.h
mail_dict.o: ../../include/dict.h
-mail_dict.o: ../../include/dynamicmaps.h
mail_dict.o: ../../include/msg.h
mail_dict.o: ../../include/myflock.h
mail_dict.o: ../../include/mymalloc.h
mail_dict.o: dict_pgsql.h
mail_dict.o: dict_proxy.h
mail_dict.o: dict_sqlite.h
+mail_dict.o: dynamicmaps.h
mail_dict.o: mail_dict.c
mail_dict.o: mail_dict.h
mail_dict.o: mail_params.h
mkmap_open.o: ../../include/dict_cdb.h
mkmap_open.o: ../../include/dict_db.h
mkmap_open.o: ../../include/dict_dbm.h
-mkmap_open.o: ../../include/dynamicmaps.h
mkmap_open.o: ../../include/dict_fail.h
mkmap_open.o: ../../include/dict_lmdb.h
mkmap_open.o: ../../include/dict_sdbm.h
+mkmap_open.o: ../../include/htable.h
mkmap_open.o: ../../include/msg.h
mkmap_open.o: ../../include/myflock.h
mkmap_open.o: ../../include/mymalloc.h
--- /dev/null
+/*++
+/* NAME
+/* dynamicmaps 3
+/* SUMMARY
+/* load dictionaries dynamically
+/* SYNOPSIS
+/* #include <dynamicmaps.h>
+/*
+/* void dymap_init(const char *path)
+/*
+/* ARGV *dymap_list(ARGV *map_names)
+/* DESCRIPTION
+/* This module reads the dynamicmaps.cf file and performs
+/* run-time loading of Postfix dictionaries. Each dynamicmaps.cf
+/* entry specifies the name of a dictionary type, the pathname
+/* of a shared-library object, the name of an "open" function
+/* for access to individual dictionary entries, and optionally
+/* the name of a "mkmap" function for bulk-mode dictionary
+/* creation.
+/*
+/* dymap_init() must be called at least once before any other
+/* functions in this module. This function reads the specified
+/* configuration file which is in dynamicmaps.cf format, hooks
+/* itself into the dict_open(), dict_mapames(), and mkmap_open()
+/* functions, and may be called multiple times during a process
+/* lifetime, but only the last-read dynamicmaps content will be
+/* remembered.
+/*
+/* dymap_list() appends to its argument the names of dictionary
+/* types available in dynamicmaps.cf.
+/* SEE ALSO
+/* load_lib(3) low-level run-time linker adapter
+/* DIAGNOSTICS
+/* Fatal errors: memory allocation problem, dictionary or
+/* dictionary function not available. Panic: invalid use.
+/* LICENSE
+/* .ad
+/* .fi
+/* The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/* LaMont Jones
+/* Hewlett-Packard Company
+/* 3404 Harmony Road
+/* Fort Collins, CO 80528, USA
+/*
+/* Wietse Venema
+/* IBM T.J. Watson Research
+/* P.O. Box 704
+/* Yorktown Heights, NY 10598, USA
+/*--*/
+
+ /*
+ * System library.
+ */
+#include <sys_defs.h>
+#include <sys/stat.h>
+#include <string.h>
+
+ /*
+ * Utility library.
+ */
+#include <msg.h>
+#include <mymalloc.h>
+#include <argv.h>
+#include <dict.h>
+#include <load_lib.h>
+#include <vstring.h>
+#include <vstream.h>
+#include <vstring_vstream.h>
+#include <mvect.h>
+
+ /*
+ * Global library.
+ */
+#include <mkmap.h>
+#include <dynamicmaps.h>
+
+#ifdef USE_DYNAMIC_LIBS
+
+ /*
+ * Contents of one dynamicmaps.cf entry.
+ */
+typedef struct {
+ const char *dict_type; /* database type name */
+ const char *soname; /* shared-object file name */
+ const char *open_name; /* dict_xx_open() function name */
+ const char *mkmap_name; /* mkmap_xx_open() function name */
+} DYMAP_INFO;
+
+static DYMAP_INFO *dymap_info;
+static DICT_OPEN_EXTEND_FN saved_dict_open_hook = 0;
+static MKMAP_OPEN_EXTEND_FN saved_mkmap_open_hook = 0;
+static DICT_MAPNAMES_EXTEND_FN saved_dict_mapnames_hook = 0;
+
+#define STREQ(x, y) (strcmp((x), (y)) == 0)
+
+/* dymap_find - find dynamicmaps.cf metadata */
+
+static DYMAP_INFO *dymap_find(const char *dict_type)
+{
+ static const char myname[] = "dymap_find";
+ DYMAP_INFO *dp;
+
+ if (!dymap_info)
+ msg_panic("%s: dlinfo==NULL", myname);
+
+ for (dp = dymap_info; dp->dict_type; dp++) {
+ if (STREQ(dp->dict_type, dict_type))
+ return dp;
+ }
+ return (0);
+}
+
+/* dymap_open_lookup - look up "dict_foo_open" function */
+
+static DICT_OPEN_FN dymap_open_lookup(const char *dict_type)
+{
+ struct stat st;
+ LIB_FN fn[2];
+ DICT_OPEN_FN dict_open_fn;
+ DYMAP_INFO *dp;
+
+ /*
+ * Respect the hook nesting order.
+ */
+ if (saved_dict_open_hook != 0
+ && (dict_open_fn = saved_dict_open_hook(dict_type)) != 0)
+ return (dict_open_fn);
+
+ /*
+ * Allow for graceful degradation when a database is unavailable. This
+ * allows daemon processes to continue handling email with reduced
+ * functionality.
+ */
+ if ((dp = dymap_find(dict_type)) == 0
+ || stat(dp->soname, &st) < 0
+ || dp->open_name == 0)
+ return (0);
+ if (st.st_uid != 0 || (st.st_mode & (S_IWGRP | S_IWOTH)) != 0) {
+ msg_warn("%s: file must be writable only by root", dp->soname);
+ return (0);
+ }
+ fn[0].name = dp->open_name;
+ fn[0].ptr = (void **) &dict_open_fn;
+ fn[1].name = NULL;
+ load_library_symbols(dp->soname, fn, NULL);
+ return (dict_open_fn);
+}
+
+/* dymap_mkmap_lookup - look up "mkmap_foo_open" function */
+
+static MKMAP_OPEN_FN dymap_mkmap_lookup(const char *dict_type)
+{
+ struct stat st;
+ LIB_FN fn[2];
+ MKMAP_OPEN_FN mkmap_open_fn;
+ DYMAP_INFO *dp;
+
+ /*
+ * Respect the hook nesting order.
+ */
+ if (saved_mkmap_open_hook != 0
+ && (mkmap_open_fn = saved_mkmap_open_hook(dict_type)) != 0)
+ return (mkmap_open_fn);
+
+ /*
+ * All errors are fatal. If we can't create the requetsed database, then
+ * graceful degradation is not useful.
+ */
+ dp = dymap_find(dict_type);
+ if (!dp)
+ msg_fatal("unsupported dictionary type: %s. "
+ "Is the postfix-%s package installed?",
+ dict_type, dict_type);
+ if (!dp->mkmap_name)
+ msg_fatal("unsupported dictionary type: %s does not support "
+ "bulk-mode creation.", dict_type);
+ if (stat(dp->soname, &st) < 0)
+ msg_fatal("unsupported dictionary type: %s (%s not found). "
+ "Is the postfix-%s package installed?",
+ dict_type, dp->soname, dict_type);
+ if (st.st_uid != 0 || (st.st_mode & (S_IWGRP | S_IWOTH)) != 0)
+ msg_fatal("%s: file must be writable only by root", dp->soname);
+ fn[0].name = dp->mkmap_name;
+ fn[0].ptr = (void **) &mkmap_open_fn;
+ fn[1].name = NULL;
+ load_library_symbols(dp->soname, fn, NULL);
+ return (mkmap_open_fn);
+}
+
+/* dymap_list - enumerate dynamically-linked database type names */
+
+static ARGV *dymap_list(ARGV *map_names)
+{
+ static const char myname[] = "dymap_list";
+ DYMAP_INFO *dp;
+
+ if (!dymap_info)
+ msg_panic("%s: dlinfo==NULL", myname);
+
+ /*
+ * Respect the hook nesting order.
+ */
+ if (saved_dict_mapnames_hook != 0)
+ map_names = saved_dict_mapnames_hook(map_names);
+
+ if (map_names == 0)
+ map_names = argv_alloc(2);
+ for (dp = dymap_info; dp->dict_type; dp++) {
+ argv_add(map_names, dp->dict_type, ARGV_END);
+ }
+ return (map_names);
+}
+
+/* dymap_init - initialize dictionary type to soname etc. mapping */
+
+void dymap_init(const char *path)
+{
+ VSTREAM *conf_fp;
+ VSTRING *buf;
+ char *cp;
+ ARGV *argv;
+ static MVECT vector;
+ int nelm = 0;
+ int linenum = 0;
+ static int hooks_done = 0;
+ struct stat st;
+
+ if (dymap_info != 0)
+ mvect_free(&vector);
+
+ dymap_info =
+ (DYMAP_INFO *) mvect_alloc(&vector, sizeof(DYMAP_INFO), 3, 0, 0);
+
+ /* Silently ignore missing dynamic maps file. */
+ if ((conf_fp = vstream_fopen(path, O_RDONLY, 0)) != 0) {
+ if (fstat(vstream_fileno(conf_fp), &st) < 0)
+ msg_fatal("%s: fstat failed; %m", path);
+ if (st.st_uid != 0 || (st.st_mode & (S_IWGRP | S_IWOTH)) != 0)
+ msg_fatal("%s: file must be writable only by root", path);
+ buf = vstring_alloc(100);
+ while (vstring_get_nonl(buf, conf_fp) != VSTREAM_EOF) {
+ cp = vstring_str(buf);
+ linenum++;
+ if (*cp == '#' || *cp == '\0')
+ continue;
+ argv = argv_split(cp, " \t");
+ if (argv->argc != 3 && argv->argc != 4) {
+ msg_fatal("%s: Expected \"dict_type .so-name open-function"
+ " [mkmap-function]\" at line %d", path, linenum);
+ }
+ if (STREQ(argv->argv[0], "*")) {
+ msg_warn("%s: wildcard dynamic map entry no longer supported.",
+ path);
+ continue;
+ }
+ if (argv->argv[1][0] != '/') {
+ msg_fatal("%s: .so name must begin with a \"/\" at line %d",
+ path, linenum);
+ }
+ if (nelm >= vector.nelm) {
+ dymap_info = (DYMAP_INFO *) mvect_realloc(&vector, vector.nelm + 3);
+ }
+ dymap_info[nelm].dict_type = mystrdup(argv->argv[0]);
+ dymap_info[nelm].soname = mystrdup(argv->argv[1]);
+ dymap_info[nelm].open_name = mystrdup(argv->argv[2]);
+ if (argv->argc == 4)
+ dymap_info[nelm].mkmap_name = mystrdup(argv->argv[3]);
+ else
+ dymap_info[nelm].mkmap_name = NULL;
+ nelm++;
+ argv_free(argv);
+ if (hooks_done == 0) {
+ hooks_done = 1;
+ saved_dict_open_hook = dict_open_extend(dymap_open_lookup);
+ saved_mkmap_open_hook = mkmap_open_extend(dymap_mkmap_lookup);
+ saved_dict_mapnames_hook = dict_mapnames_extend(dymap_list);
+ }
+ }
+ vstring_free(buf);
+ vstream_fclose(conf_fp);
+ }
+ if (nelm >= vector.nelm) {
+ dymap_info = (DYMAP_INFO *) mvect_realloc(&vector, vector.nelm + 1);
+ }
+ dymap_info[nelm].dict_type = NULL;
+ dymap_info[nelm].soname = NULL;
+ dymap_info[nelm].open_name = NULL;
+ dymap_info[nelm].mkmap_name = NULL;
+}
+
+#endif
/* DESCRIPTION
/* .nf
- /*
- * Utility library.
- */
-#include <argv.h>
-
/*
* External interface.
*/
#ifdef USE_DYNAMIC_LIBS
-typedef DICT *(*dymap_open_t) (const char *, int, int);
-typedef void *(*dymap_mkmap_t) (const char *);
-
extern void dymap_init(const char *);
-extern ARGV *dymap_list(ARGV *);
-extern dymap_open_t dymap_get_open_fn(const char *);
-extern dymap_mkmap_t dymap_get_mkmap_fn(const char *);
#endif
/* LICENSE
* Patches change both the patchlevel and the release date. Snapshots have no
* patchlevel; they change the release date only.
*/
-#define MAIL_RELEASE_DATE "20140508"
+#define MAIL_RELEASE_DATE "20140516"
#define MAIL_VERSION_NUMBER "2.12"
#ifdef SNAPSHOT
* acquire the lock after the DB/DBM initialization.
*/
typedef struct MKMAP {
- struct DICT *(*open) (const char *, int, int); /* dict_xx_open() */
+ DICT_OPEN_FN open; /* dict_xx_open() */
struct 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 */
+ int multi_writer; /* multi-writer safe */
} MKMAP;
extern MKMAP *mkmap_open(const char *, const char *, int, int);
extern MKMAP *mkmap_proxy_open(const char *);
extern MKMAP *mkmap_fail_open(const char *);
+typedef MKMAP *(*MKMAP_OPEN_FN) (const char *);
+typedef MKMAP_OPEN_FN (*MKMAP_OPEN_EXTEND_FN) (const char *);
+extern void mkmap_open_register(const char *, MKMAP_OPEN_FN);
+extern MKMAP_OPEN_EXTEND_FN mkmap_open_extend(MKMAP_OPEN_EXTEND_FN);
+
/* LICENSE
/* .ad
/* .fi
/* 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;
/*
/* 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
/* 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.
+/* 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
/*
/* 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
/* Utility library. */
#include <msg.h>
+#include <htable.h>
#include <dict.h>
#include <dict_db.h>
#include <dict_cdb.h>
#include <sigdelay.h>
#include <mymalloc.h>
-#ifdef USE_DYNAMIC_LIBS
-#include <dynamicmaps.h>
-#endif
-
/* Global library. */
#include "mkmap.h"
/*
* Information about available database types. Here, we list only those map
- * types that support "create" operations.
+ * types that support "bulk create" operations.
*
- * We use a different table (in dict_open.c) when querying maps.
+ * 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 *(*before_open) (const char *);
+ MKMAP_OPEN_FN before_open;
} MKMAP_OPEN_INFO;
-static const MKMAP_OPEN_INFO mkmap_types[] = {
- DICT_TYPE_PROXY, mkmap_proxy_open,
+static const MKMAP_OPEN_INFO mkmap_open_info[] = {
#ifdef HAS_CDB
DICT_TYPE_CDB, mkmap_cdb_open,
#endif
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)
+{
+ 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, (char *) mp);
+}
+
+/* mkmap_open_register - register dictionary type */
+
+void mkmap_open_register(const char *type, MKMAP_OPEN_FN open_fn)
+{
+ 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, (char *) 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
{
MKMAP *mkmap;
const MKMAP_OPEN_INFO *mp;
-
-#ifdef USE_DYNAMIC_LIBS
- MKMAP_OPEN_INFO oi;
-
-#endif
+ MKMAP_OPEN_FN open_fn;
/*
* Find out what map type to use.
*/
- for (mp = mkmap_types; /* void */ ; mp++) {
- if (mp->type == 0) {
-#ifdef USE_DYNAMIC_LIBS
- /* Either returns a handle, or raises a fatal error */
- oi.before_open =
- (MKMAP *(*) (const char *)) dymap_get_mkmap_fn(type);
- oi.type = type;
- mp = &oi;
- break;
-#else
- msg_fatal("unsupported map type for this operation: %s", type);
-#endif
+ 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 (strcmp(type, mp->type) == 0)
- break;
+ if (mp == 0)
+ msg_fatal("unsupported map type for this operation: %s", type);
}
if (msg_verbose)
msg_info("open %s %s", type, path);
single_server_main(argc, argv, pipe_service,
MAIL_SERVER_TIME_TABLE, time_table,
+ MAIL_SERVER_STR_TABLE, str_table,
MAIL_SERVER_PRE_INIT, pre_init,
MAIL_SERVER_POST_INIT, drop_privileges,
MAIL_SERVER_PRE_ACCEPT, pre_accept,
cat ../../conf/makedefs.out $? >$@
smtp-sink: smtp-sink.o $(LIBS)
- $(CC) $(CFLAGS) -o $@ smtp-sink.o $(LIBS) $(SYSLIBS)
+ $(CC) $(CFLAGS) $(SHLIB_RPATH) -o $@ smtp-sink.o $(LIBS) $(SYSLIBS)
smtp-source: smtp-source.o $(LIBS)
- $(CC) $(CFLAGS) -o $@ smtp-source.o $(LIBS) $(SYSLIBS)
+ $(CC) $(CFLAGS) $(SHLIB_RPATH) -o $@ smtp-source.o $(LIBS) $(SYSLIBS)
qmqp-sink: qmqp-sink.o $(LIBS)
- $(CC) $(CFLAGS) -o $@ qmqp-sink.o $(LIBS) $(SYSLIBS)
+ $(CC) $(CFLAGS) $(SHLIB_RPATH) -o $@ qmqp-sink.o $(LIBS) $(SYSLIBS)
qmqp-source: qmqp-source.o $(LIBS)
- $(CC) $(CFLAGS) -o $@ qmqp-source.o $(LIBS) $(SYSLIBS)
+ $(CC) $(CFLAGS) $(SHLIB_RPATH) -o $@ qmqp-source.o $(LIBS) $(SYSLIBS)
test: $(TESTPROG)
readlline.c ring.c safe_getenv.c safe_open.c \
sane_accept.c sane_connect.c sane_link.c sane_rename.c \
sane_socketpair.c sane_time.c scan_dir.c set_eugid.c set_ugid.c \
- load_lib.c dynamicmaps.c \
+ load_lib.c \
sigdelay.c skipblanks.c sock_addr.c spawn_command.c split_at.c \
split_nameval.c stat_as.c strcasecmp.c stream_connect.c \
stream_listen.c stream_recv_fd.c stream_send_fd.c stream_trigger.c \
host_port.o htable.o inet_addr_host.o inet_addr_list.o \
inet_addr_local.o inet_connect.o inet_listen.o inet_proto.o \
inet_trigger.o line_wrap.o lowercase.o lstat_as.o mac_expand.o \
- load_lib.o dynamicmaps.o \
+ load_lib.o \
mac_parse.o make_dirs.o mask_addr.o match_list.o match_ops.o msg.o \
msg_output.o msg_syslog.o msg_vstream.o mvect.o myaddrinfo.o myflock.o \
mymalloc.o myrand.o mystrtok.o name_code.o name_mask.o netstring.o \
mymalloc.h myrand.h name_code.h name_mask.h netstring.h nvtable.h \
open_as.h open_lock.h percentm.h posix_signals.h readlline.h ring.h \
safe.h safe_open.h sane_accept.h sane_connect.h sane_fsops.h \
- load_lib.h dynamicmaps.h \
+ load_lib.h \
sane_socketpair.h sane_time.h scan_dir.h set_eugid.h set_ugid.h \
sigdelay.h sock_addr.h spawn_command.h split_at.h stat_as.h \
stringops.h sys_defs.h timed_connect.h timed_wait.h trigger.h \
load_file.o: vbuf.h
load_file.o: vstream.h
load_file.o: warn_stat.h
+load_lib.o: load_lib.c
+load_lib.o: load_lib.h
+load_lib.o: msg.h
+load_lib.o: sys_defs.h
lowercase.o: lowercase.c
lowercase.o: stringops.h
lowercase.o: sys_defs.h
* Interface for dictionary types.
*/
extern ARGV *dict_mapnames(void);
+typedef ARGV *(*DICT_MAPNAMES_EXTEND_FN) (ARGV *);
+extern DICT_MAPNAMES_EXTEND_FN dict_mapnames_extend(DICT_MAPNAMES_EXTEND_FN);
+
/*
* High-level interface, with logical dictionary names.
/*
* Low-level interface, with physical dictionary handles.
*/
+typedef DICT *(*DICT_OPEN_FN) (const char *, int, int);
+typedef DICT_OPEN_FN (*DICT_OPEN_EXTEND_FN) (const char *);
extern DICT *dict_open(const char *, int, int);
extern DICT *dict_open3(const char *, const char *, int, int);
-extern void dict_open_register(const char *, DICT *(*) (const char *, int, int));
+extern void dict_open_register(const char *, DICT_OPEN_FN);
+extern DICT_OPEN_EXTEND_FN dict_open_extend(DICT_OPEN_EXTEND_FN);
#define dict_get(dp, key) ((const char *) (dp)->lookup((dp), (key)))
#define dict_put(dp, key, val) (dp)->update((dp), (key), (val))
#define DONT_CLOBBER DB_NOOVERWRITE
#endif
-#if (DB_VERSION_MAJOR == 2 && DB_VERSION_MINOR < 6)
+#if (DB_VERSION_MAJOR == 2 && DB_VERSION_MINOR <= 6)
#define DICT_DB_CURSOR(db, curs) (db)->cursor((db), NULL, (curs))
#else
#define DICT_DB_CURSOR(db, curs) (db)->cursor((db), NULL, (curs), 0)
msg_fatal("set DB cache size %d: %m", dict_db_cache_size);
if (type == DB_HASH && db->set_h_nelem(db, DICT_DB_NELM) != 0)
msg_fatal("set DB hash element count %d: %m", DICT_DB_NELM);
-#if DB_VERSION_MAJOR == 5 || (DB_VERSION_MAJOR == 4 && DB_VERSION_MINOR > 0)
+#if DB_VERSION_MAJOR == 6 || DB_VERSION_MAJOR == 5 || \
+ (DB_VERSION_MAJOR == 4 && DB_VERSION_MINOR > 0)
if ((errno = db->open(db, 0, db_path, 0, type, db_flags, 0644)) != 0)
FREE_RETURN(dict_surrogate(class, path, open_flags, dict_flags,
"open database %s: %m", db_path));
/* void dict_close(dict)
/* DICT *dict;
/*
+/* typedef DICT *(*DICT_OPEN_FN) (const char *, int, int);
+/*
/* dict_open_register(type, open)
-/* char *type;
-/* DICT *(*open) (const char *, int, int);
+/* const char *type;
+/* DICT_OPEN_FN open;
+/*
+/* typedef DICT_OPEN_FN (*DICT_OPEN_EXTEND_FN)(const char *type);
+/*
+/* DICT_OPEN_EXTEND_FN dict_open_extend(call_back)
+/* DICT_OPEN_EXTEND_FN call_back;
/*
/* ARGV *dict_mapnames()
/*
+/* typedef ARGV *(*DICT_MAPNAMES_EXTEND_FN)(ARGV *names);
+/*
+/* DICT_MAPNAMES_EXTEND_FN dict_mapnames_extend(call_back)
+/* DICT_MAPNAMES_EXTEND_FN call_back;
+/*
/* int dict_isjmp(dict)
/* DICT *dict;
/*
/*
/* dict_open_register() adds support for a new dictionary type.
/*
+/* dict_open_extend() registers a call-back function that looks
+/* up the dictionary open() function for a type that is not
+/* registered, or null in case of error. The result value is
+/* the last previously-registered call-back or null.
+/*
/* dict_mapnames() returns a sorted list with the names of all available
/* dictionary types.
/*
+/* dict_mapnames_extend() registers a call-back function that
+/* enumerates additional dictionary type names. The result
+/* will be sorted by dict_mapnames(). The result value
+/* is the last previously-registered call-back or null.
+/*
/* dict_setjmp() saves processing context and makes that context
/* available for use with dict_longjmp(). Normally, dict_setjmp()
/* returns zero. A non-zero result means that dict_setjmp()
#include <split_at.h>
#include <htable.h>
#include <myflock.h>
-#include <dynamicmaps.h>
/*
* lookup table for available map types.
*/
typedef struct {
char *type;
- struct DICT *(*open) (const char *, int, int);
+ DICT_OPEN_FN open;
} DICT_OPEN_INFO;
static const DICT_OPEN_INFO dict_open_info[] = {
static HTABLE *dict_open_hash;
+ /*
+ * Extension hooks.
+ */
+static DICT_OPEN_EXTEND_FN dict_open_extend_hook;
+static DICT_MAPNAMES_EXTEND_FN dict_mapnames_extend_hook;
+
/* dict_open_init - one-off initialization */
static void dict_open_init(void)
{
const char *myname = "dict_open";
DICT_OPEN_INFO *dp;
+ DICT_OPEN_FN open_fn;
DICT *dict;
if (*dict_type == 0 || *dict_name == 0)
if (dict_open_hash == 0)
dict_open_init();
if ((dp = (DICT_OPEN_INFO *) htable_find(dict_open_hash, dict_type)) == 0) {
-#ifdef USE_DYNAMIC_LIBS
- dymap_open_t open_fn;
-
- if ((open_fn = dymap_get_open_fn(dict_type)) == 0)
+ if (dict_open_extend_hook != 0
+ && (open_fn = dict_open_extend_hook(dict_type)) != 0) {
+ dict_open_register(dict_type, open_fn);
+ dp = (DICT_OPEN_INFO *) htable_find(dict_open_hash, dict_type);
+ }
+ if (dp == 0)
return (dict_surrogate(dict_type, dict_name, open_flags, dict_flags,
"unsupported dictionary type: %s", dict_type));
- dict_open_register(dict_type, open_fn);
- dp = (DICT_OPEN_INFO *) htable_find(dict_open_hash, dict_type);
-#else
- return (dict_surrogate(dict_type, dict_name, open_flags, dict_flags,
- "unsupported dictionary type: %s", dict_type));
-#endif
}
if ((dict = dp->open(dict_name, open_flags, dict_flags)) == 0)
return (dict_surrogate(dict_type, dict_name, open_flags, dict_flags,
/* dict_open_register - register dictionary type */
-void dict_open_register(const char *type,
- DICT *(*open) (const char *, int, int))
+void dict_open_register(const char *type, DICT_OPEN_FN open)
{
const char *myname = "dict_open_register";
DICT_OPEN_INFO *dp;
+ HTABLE_INFO *ht;
if (dict_open_hash == 0)
dict_open_init();
if (htable_find(dict_open_hash, type))
msg_panic("%s: dictionary type exists: %s", myname, type);
dp = (DICT_OPEN_INFO *) mymalloc(sizeof(*dp));
- dp->type = mystrdup(type);
dp->open = open;
- htable_enter(dict_open_hash, dp->type, (char *) dp);
+ ht = htable_enter(dict_open_hash, type, (char *) dp);
+ dp->type = ht->key;
+}
+
+/* dict_open_extend - register alternate dictionary search routine */
+
+DICT_OPEN_EXTEND_FN dict_open_extend(DICT_OPEN_EXTEND_FN new_cb)
+{
+ DICT_OPEN_EXTEND_FN old_cb;
+
+ old_cb = dict_open_extend_hook;
+ dict_open_extend_hook = new_cb;
+ return (old_cb);
}
/* dict_sort_alpha_cpp - qsort() callback */
dp = (DICT_OPEN_INFO *) ht[0]->value;
argv_add(mapnames, dp->type, ARGV_END);
}
-#ifdef USE_DYNAMIC_LIBS
- dymap_list(mapnames);
-#endif
+ if (dict_mapnames_extend_hook != 0)
+ (void) dict_mapnames_extend_hook(mapnames);
qsort((void *) mapnames->argv, mapnames->argc, sizeof(mapnames->argv[0]),
dict_sort_alpha_cpp);
myfree((char *) ht_info);
return mapnames;
}
+/* dict_mapnames_extend - register alternate dictionary type list routine */
+
+DICT_MAPNAMES_EXTEND_FN dict_mapnames_extend(DICT_MAPNAMES_EXTEND_FN new_cb)
+{
+ DICT_MAPNAMES_EXTEND_FN old_cb;
+
+ old_cb = dict_mapnames_extend_hook;
+ dict_mapnames_extend_hook = new_cb;
+ return (old_cb);
+}
+
#ifdef TEST
/*
+++ /dev/null
-/*++
-/* NAME
-/* dynamicmaps 3
-/* SUMMARY
-/* load dictionaries dynamically
-/* SYNOPSIS
-/* #include <dynamicmaps.h>
-/*
-/* typedef void *(*dymap_mkmap_t) (const char *)
-/* typedef DICT *(*dymap_open_t) (const char *, int, int)
-/*
-/* void dymap_init(const char *path)
-/*
-/* ARGV *dymap_list(ARGV *map_names)
-/*
-/* dymap_open_t dymap_get_open_fn(const char *dict_type)
-/*
-/* dymap_mkmap_t dymap_get_mkmap_fn(const char *dict_type)
-/* DESCRIPTION
-/* This module reads the dynamicmaps.cf file and performs
-/* run-time loading of Postfix dictionaries. Each dynamicmaps.cf
-/* entry specifies the name of a dictionary type, the pathname
-/* of a shared-library object, the name of an "open" function
-/* for access to individual dictionary entries, and optionally
-/* the name of a "mkmap" function for bulk-mode dictionary
-/* creation.
-/*
-/* dymap_init() must be called at least once before any other
-/* functions in this module. This function reads the specified
-/* configuration file which is in dynamicmaps.cf format, and
-/* may be called multiple times during a process lifetime.
-/*
-/* dymap_list() appends to its argument the names of dictionary
-/* types available in dynamicmaps.cf.
-/*
-/* dymap_get_open_fn() loads the specified dictionary and
-/* returns a function pointer to its "open" function.
-/*
-/* dymap_get_mkmap_fn() loads the specified dictionary and
-/* returns a function pointer to its "mkmap" function.
-/* SEE ALSO
-/* load_lib(3) low-level run-time linker adapter
-/* DIAGNOSTICS
-/* Fatal errors: memory allocation problem, dictionary or
-/* dictionary function not available. Panic: invalid use.
-/* LICENSE
-/* .ad
-/* .fi
-/* The Secure Mailer license must be distributed with this software.
-/* AUTHOR(S)
-/* LaMont Jones
-/* Hewlett-Packard Company
-/* 3404 Harmony Road
-/* Fort Collins, CO 80528, USA
-/*
-/* Wietse Venema
-/* IBM T.J. Watson Research
-/* P.O. Box 704
-/* Yorktown Heights, NY 10598, USA
-/*--*/
-
- /*
- * System library.
- */
-#include <sys_defs.h>
-#include <sys/stat.h>
-#include <string.h>
-
- /*
- * Utility library.
- */
-#include <msg.h>
-#include <mymalloc.h>
-#include <argv.h>
-#include <dict.h>
-#include <load_lib.h>
-#include <dynamicmaps.h>
-#include <load_lib.h>
-#include <vstring.h>
-#include <vstream.h>
-#include <vstring_vstream.h>
-#include <mvect.h>
-
-#ifdef USE_DYNAMIC_LIBS
-
- /*
- * Contents of one dynamicmaps.cf entry.
- */
-typedef struct {
- const char *dict_type;
- const char *soname;
- const char *open_name;
- const char *mkmap_name;
-} DICT_DL_INFO;
-
-static DICT_DL_INFO *dict_dlinfo;
-
-#define STREQ(x, y) (strcmp((x), (y)) == 0)
-
-/* dymap_init - initialize dictionary type to soname etc. mapping */
-
-void dymap_init(const char *path)
-{
- VSTREAM *conf_fp;
- VSTRING *buf;
- char *cp;
- ARGV *argv;
- static MVECT vector;
- int nelm = 0;
- int linenum = 0;
-
- if (dict_dlinfo != 0)
- mvect_free(&vector);
-
- dict_dlinfo =
- (DICT_DL_INFO *) mvect_alloc(&vector, sizeof(DICT_DL_INFO), 3, 0, 0);
-
- /* Silently ignore missing dynamic maps file. */
- if ((conf_fp = vstream_fopen(path, O_RDONLY, 0)) != 0) {
- buf = vstring_alloc(100);
- while (vstring_get_nonl(buf, conf_fp) != VSTREAM_EOF) {
- cp = vstring_str(buf);
- linenum++;
- if (*cp == '#' || *cp == '\0')
- continue;
- argv = argv_split(cp, " \t");
- if (argv->argc != 3 && argv->argc != 4) {
- msg_fatal("%s: Expected \"dict_type .so-name open-function"
- " [mkmap-function]\" at line %d", path, linenum);
- }
- if (STREQ(argv->argv[0], "*")) {
- msg_warn("%s: wildcard dynamic map entry no longer supported.",
- path);
- continue;
- }
- if (argv->argv[1][0] != '/') {
- msg_fatal("%s: .so name must begin with a \"/\" at line %d",
- path, linenum);
- }
- if (nelm >= vector.nelm) {
- dict_dlinfo = (DICT_DL_INFO *) mvect_realloc(&vector, vector.nelm + 3);
- }
- dict_dlinfo[nelm].dict_type = mystrdup(argv->argv[0]);
- dict_dlinfo[nelm].soname = mystrdup(argv->argv[1]);
- dict_dlinfo[nelm].open_name = mystrdup(argv->argv[2]);
- if (argv->argc == 4)
- dict_dlinfo[nelm].mkmap_name = mystrdup(argv->argv[3]);
- else
- dict_dlinfo[nelm].mkmap_name = NULL;
- nelm++;
- argv_free(argv);
- }
- vstring_free(buf);
- vstream_fclose(conf_fp);
- }
- if (nelm >= vector.nelm) {
- dict_dlinfo = (DICT_DL_INFO *) mvect_realloc(&vector, vector.nelm + 1);
- }
- dict_dlinfo[nelm].dict_type = NULL;
- dict_dlinfo[nelm].soname = NULL;
- dict_dlinfo[nelm].open_name = NULL;
- dict_dlinfo[nelm].mkmap_name = NULL;
-}
-
-/* dymap_list - enumerate dynamically-linked database type names */
-
-ARGV *dymap_list(ARGV *map_names)
-{
- static const char myname[] = "dymap_list";
- DICT_DL_INFO *dl;
-
- if (!dict_dlinfo)
- msg_panic("%s: dlinfo==NULL", myname);
- if (map_names == 0)
- map_names = argv_alloc(2);
- for (dl = dict_dlinfo; dl->dict_type; dl++) {
- argv_add(map_names, dl->dict_type, ARGV_END);
- }
- return (map_names);
-}
-
-/* dymap_find - find dynamically-linked database metadata */
-
-static DICT_DL_INFO *dymap_find(const char *dict_type)
-{
- static const char myname[] = "dymap_find";
- DICT_DL_INFO *dp;
-
- if (!dict_dlinfo)
- msg_panic("%s: dlinfo==NULL", myname);
-
- for (dp = dict_dlinfo; dp->dict_type; dp++) {
- if (STREQ(dp->dict_type, dict_type))
- return dp;
- }
- return (0);
-}
-
-/* dymap_get_open_fn - look up "dict_foo_open" function */
-
-dymap_open_t dymap_get_open_fn(const char *dict_type)
-{
- struct stat st;
- LIB_FN fn[2];
- dymap_open_t open_fn;
- DICT_DL_INFO *dl;
-
- if ((dl = dymap_find(dict_type)) == 0
- || stat(dl->soname, &st) < 0
- || dl->open_name == 0)
- return (0);
- fn[0].name = dl->open_name;
- fn[0].ptr = (void **) &open_fn;
- fn[1].name = NULL;
- load_library_symbols(dl->soname, fn, NULL);
- return (open_fn);
-}
-
-/* dymap_get_mkmap_fn - look up "mkmap_foo_open" function */
-
-dymap_mkmap_t dymap_get_mkmap_fn(const char *dict_type)
-{
- struct stat st;
- LIB_FN fn[2];
- dymap_mkmap_t mkmap_fn;
- DICT_DL_INFO *dl;
-
- dl = dymap_find(dict_type);
- if (!dl)
- msg_fatal("unsupported dictionary type: %s. "
- "Is the postfix-%s package installed?",
- dict_type, dict_type);
- if (stat(dl->soname, &st) < 0) {
- msg_fatal("unsupported dictionary type: %s (%s not found). "
- "Is the postfix-%s package installed?",
- dict_type, dl->soname, dict_type);
- }
- if (!dl->mkmap_name)
- msg_fatal("unsupported dictionary type: %s does not support "
- "bulk-mode creation.", dict_type);
- fn[0].name = dl->mkmap_name;
- fn[0].ptr = (void **) &mkmap_fn;
- fn[1].name = NULL;
- load_library_symbols(dl->soname, fn, NULL);
- return (mkmap_fn);
-}
-
-#endif