Roel van Meer writes:
What I am actually trying to do is a lookup with a single key in two maps.
Maybe stackmap or concatmap?
Now, if you specify two maps somewhere, and the first map returns a result,
there is no lookup done in the second map. With concatmap, both lookups
would happen, and the combined result would be returned.
Largely based on the code of pipemap, I now have something that does this.
It goes by the name of joinmap.
/etc/postfix/a:
a res-a
c res-ca
d res-d
/etc/postfix/b:
b res-b
c res-cb
d res-d
# postmap -q a 'joinmap:!hash:/etc/postfix/a!hash:/etc/postfix/b'
res-a
# postmap -q b 'joinmap:!hash:/etc/postfix/a!hash:/etc/postfix/b'
res-b
# postmap -q c 'joinmap:!hash:/etc/postfix/a!hash:/etc/postfix/b'
res-ca,res-cb
# postmap -q d 'joinmap:!hash:/etc/postfix/a!hash:/etc/postfix/b'
res-d,res-d
# postmap -q e 'joinmap:!hash:/etc/postfix/a!hash:/etc/postfix/b'
So far it does what I need: I can now use the output of two ldap lookups in
virtual_alias_maps.
The patch is just code though. If you're interested, I can write some
documentation to make it more complete. And although it seems to work on my
test server, it hasn't been tested very thoroughly yet..
Thanks again (it's always a pleasure to work with Postfix),
Roel
diff -ruN a/.indent.pro b/.indent.pro
--- a/.indent.pro 2014-07-18 01:09:15.000000000 +0200
+++ b/.indent.pro 2014-09-11 12:29:49.298253498 +0200
@@ -77,6 +77,7 @@
-TDICT_DEBUG
-TDICT_ENV
-TDICT_FAIL
+-TDICT_JOIN
-TDICT_HT
-TDICT_LDAP
-TDICT_LMDB
diff -ruN b/src/util/Makefile.in c/src/util/Makefile.in
--- b/src/util/Makefile.in 2014-07-21 01:21:23.000000000 +0200
+++ c/src/util/Makefile.in 2014-09-11 11:54:25.868238221 +0200
@@ -38,7 +38,7 @@
dict_fail.c msg_rate_delay.c dict_surrogate.c warn_stat.c \
dict_sockmap.c line_number.c recv_pass_attr.c pass_accept.c \
poll_fd.c timecmp.c slmdb.c dict_pipe.c dict_random.c \
- valid_utf8_hostname.c midna.c
+ valid_utf8_hostname.c midna.c dict_join.c
OBJS = alldig.o allprint.o argv.o argv_split.o attr_clnt.o attr_print0.o \
attr_print64.o attr_print_plain.o attr_scan0.o attr_scan64.o \
attr_scan_plain.o auto_clnt.o base64_code.o basename.o binhash.o \
@@ -78,7 +78,7 @@
dict_fail.o msg_rate_delay.o dict_surrogate.o warn_stat.o \
dict_sockmap.o line_number.o recv_pass_attr.o pass_accept.o \
poll_fd.o timecmp.o $(NON_PLUGIN_MAP_OBJ) dict_pipe.o dict_random.o \
- valid_utf8_hostname.o midna.o
+ valid_utf8_hostname.o midna.o dict_join.o
# MAP_OBJ is for maps that may be dynamically loaded with dynamicmaps.cf.
# When hard-linking these, makedefs sets NON_PLUGIN_MAP_OBJ=$(MAP_OBJ),
# otherwise it sets the PLUGIN_* macros.
@@ -107,7 +107,7 @@
edit_file.h dict_cache.h dict_thash.h ip_match.h nbbio.h base32_code.h \
dict_fail.h warn_stat.h dict_sockmap.h line_number.h timecmp.h \
slmdb.h compat_va_copy.h dict_pipe.h dict_random.h \
- valid_utf8_hostname.h midna.h
+ valid_utf8_hostname.h midna.h dict_join.h
TESTSRC = fifo_open.c fifo_rdwr_bug.c fifo_rdonly_bug.c select_bug.c \
stream_test.c dup2_pass_on_exec.c
DEFS = -I. -D$(SYSTYPE)
@@ -1043,6 +1043,19 @@
dict_ht.o: vbuf.h
dict_ht.o: vstream.h
dict_ht.o: vstring.h
+dict_join.o: argv.h
+dict_join.o: dict.h
+dict_join.o: dict_join.c
+dict_join.o: dict_join.h
+dict_join.o: htable.h
+dict_join.o: msg.h
+dict_join.o: myflock.h
+dict_join.o: mymalloc.h
+dict_join.o: stringops.h
+dict_join.o: sys_defs.h
+dict_join.o: vbuf.h
+dict_join.o: vstream.h
+dict_join.o: vstring.h
dict_lmdb.o: argv.h
dict_lmdb.o: dict.h
dict_lmdb.o: dict_lmdb.c
@@ -1094,6 +1107,7 @@
dict_open.o: dict_env.h
dict_open.o: dict_fail.h
dict_open.o: dict_ht.h
+dict_open.o: dict_join.h
dict_open.o: dict_lmdb.h
dict_open.o: dict_ni.h
dict_open.o: dict_nis.h
diff -ruN b/src/util/dict_join.c c/src/util/dict_join.c
--- b/src/util/dict_join.c 1970-01-01 01:00:00.000000000 +0100
+++ c/src/util/dict_join.c 2014-09-11 12:02:19.538242235 +0200
@@ -0,0 +1,195 @@
+/*++
+/* NAME
+/* dict_join 3
+/* SUMMARY
+/* dictionary manager interface for pipelined tables
+/* SYNOPSIS
+/* #include <dict_join.h>
+/*
+/* DICT *dict_join_open(name, open_flags, dict_flags)
+/* const char *name;
+/* int open_flags;
+/* int dict_flags;
+/* DESCRIPTION
+/* dict_join_open() opens a pipeline of one or more tables.
+/* Example: "\fBpipemap:\fI!type_1:name_1! ... !type_n:name_n\fR".
+/*
+/* Each "pipemap:" query is given to the first table. Each
+/* lookup result becomes the query for the next table in the
+/* pipeline, and the last table produces the final result.
+/* When any table lookup produces no result, the pipeline
+/* produces no result.
+/*
+/* The first ASCII character after "pipemap:" will be used as
+/* the separator between the lookup tables that follow (do not
+/* use space, ",", ":" or non-ASCII).
+/*
+/* The open_flags and dict_flags arguments are passed on to
+/* the underlying dictionaries.
+/* SEE ALSO
+/* dict(3) generic dictionary manager
+/* 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 <string.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include "mymalloc.h"
+#include "htable.h"
+#include "dict.h"
+#include "dict_join.h"
+#include "stringops.h"
+#include "vstring.h"
+
+/* Application-specific. */
+
+typedef struct {
+ DICT dict; /* generic members */
+ ARGV *map_join; /* pipelined tables */
+ VSTRING *qr_buf; /* query/reply buffer */
+} DICT_JOIN;
+
+#define STR(x) vstring_str(x)
+
+/* dict_join_lookup - search pipelined tables */
+
+static const char *dict_join_lookup(DICT *dict, const char *query)
+{
+ const char myname[] = "dict_join_lookup";
+ DICT_JOIN *dict_join = (DICT_JOIN *) dict;
+ DICT *map;
+ char **cpp;
+ char *dict_type_name;
+ const char *result = 0;
+
+ vstring_strcpy(dict_join->qr_buf, query);
+ for (cpp = dict_join->map_join->argv; (dict_type_name = *cpp) != 0; cpp++)
{
+ if ((map = dict_handle(dict_type_name)) == 0)
+ msg_panic("%s: dictionary \"%s\" not found", myname,
dict_type_name);
+ if ((result = dict_get(map, STR(dict_join->qr_buf))) == 0)
+ DICT_ERR_VAL_RETURN(dict, map->error, result);
+ vstring_strcpy(dict_join->qr_buf, result);
+ }
+ DICT_ERR_VAL_RETURN(dict, DICT_ERR_NONE, STR(dict_join->qr_buf));
+}
+
+/* dict_join_close - disassociate from pipelined tables */
+
+static void dict_join_close(DICT *dict)
+{
+ DICT_JOIN *dict_join = (DICT_JOIN *) dict;
+ char **cpp;
+ char *dict_type_name;
+
+ for (cpp = dict_join->map_join->argv; (dict_type_name = *cpp) != 0; cpp++)
+ dict_unregister(dict_type_name);
+ argv_free(dict_join->map_join);
+ vstring_free(dict_join->qr_buf);
+ dict_free(dict);
+}
+
+/* dict_join_open - open pipelined tables */
+
+DICT *dict_join_open(const char *name, int open_flags, int dict_flags)
+{
+ const char myname[] = "dict_join_open";
+ DICT_JOIN *dict_join;
+ char *saved_name = 0;
+ char *dict_type_name;
+ ARGV *argv = 0;
+ char **cpp;
+ DICT *dict;
+ int match_flags = 0;
+ struct DICT_OWNER aggr_owner;
+ char delim[2];
+
+ /*
+ * Clarity first. Let the optimizer worry about redundant code.
+ */
+#define DICT_JOIN_RETURN(x) do { \
+ if (saved_name != 0) \
+ myfree(saved_name); \
+ if (argv != 0) \
+ argv_free(argv); \
+ return (x); \
+ } while (0)
+
+ /*
+ * Sanity checks.
+ */
+ if (open_flags != O_RDONLY)
+ DICT_JOIN_RETURN(dict_surrogate(DICT_TYPE_JOIN, name,
+ open_flags, dict_flags,
+ "%s:%s map requires O_RDONLY access mode",
+ DICT_TYPE_JOIN, name));
+ if (name[0] == ':')
+ DICT_JOIN_RETURN(dict_surrogate(DICT_TYPE_JOIN, name,
+ open_flags, dict_flags,
+ "invalid list delimiter \"%c\" in \"%s:%s\"",
+ name[0], DICT_TYPE_JOIN, name));
+
+ /*
+ * Split the table name on the user-specified delimiter.
+ */
+ delim[0] = name[0]; /* XXX ASCII delimiter
*/
+ delim[1] = 0;
+ saved_name = mystrdup(name + 1); /* XXX ASCII delimiter */
+ if (*saved_name == 0)
+ DICT_JOIN_RETURN(dict_surrogate(DICT_TYPE_JOIN, name,
+ open_flags, dict_flags,
+ "bad syntax: \"%s:%s\"; "
+ "need \"%s:%stype:name%s...\"",
+ DICT_TYPE_JOIN, name,
+ DICT_TYPE_JOIN, delim, delim));
+
+ /*
+ * The least-trusted table in the pipeline determines the over-all trust
+ * level. The first table determines the pattern-matching flags.
+ */
+ DICT_OWNER_AGGREGATE_INIT(aggr_owner);
+ argv = argv_split(saved_name, delim);
+ for (cpp = argv->argv; (dict_type_name = *cpp) != 0; cpp++) {
+ if (msg_verbose)
+ msg_info("%s: %s", myname, dict_type_name);
+ if (strchr(dict_type_name, ':') == 0)
+ DICT_JOIN_RETURN(dict_surrogate(DICT_TYPE_JOIN, name,
+ open_flags, dict_flags,
+ "bad syntax: \"%s:%s\"; "
+ "need \"%s:%stype:name%s...\"",
+ DICT_TYPE_JOIN, name,
+ DICT_TYPE_JOIN, delim, delim));
+ if ((dict = dict_handle(dict_type_name)) == 0)
+ dict = dict_open(dict_type_name, open_flags, dict_flags);
+ dict_register(dict_type_name, dict);
+ DICT_OWNER_AGGREGATE_UPDATE(aggr_owner, dict->owner);
+ if (cpp == argv->argv)
+ match_flags = dict->flags & (DICT_FLAG_FIXED | DICT_FLAG_PATTERN);
+ }
+
+ /*
+ * Bundle up the result.
+ */
+ dict_join =
+ (DICT_JOIN *) dict_alloc(DICT_TYPE_JOIN, name, sizeof(*dict_join));
+ dict_join->dict.lookup = dict_join_lookup;
+ dict_join->dict.close = dict_join_close;
+ dict_join->dict.flags = dict_flags | match_flags;
+ dict_join->dict.owner = aggr_owner;
+ dict_join->qr_buf = vstring_alloc(100);
+ dict_join->map_join = argv;
+ argv = 0;
+ DICT_JOIN_RETURN(DICT_DEBUG (&dict_join->dict));
+}
diff -ruN b/src/util/dict_join.h c/src/util/dict_join.h
--- b/src/util/dict_join.h 1970-01-01 01:00:00.000000000 +0100
+++ c/src/util/dict_join.h 2014-09-11 12:03:06.138242563 +0200
@@ -0,0 +1,37 @@
+#ifndef _DICT_JOIN_H_INCLUDED_
+#define _DICT_JOIN_H_INCLUDED_
+
+/*++
+/* NAME
+/* dict_join 3h
+/* SUMMARY
+/* dictionary manager interface for joined tables
+/* SYNOPSIS
+/* #include <dict_join.h>
+/* DESCRIPTION
+/* .nf
+
+ /*
+ * Utility library.
+ */
+#include <dict.h>
+
+ /*
+ * External interface.
+ */
+#define DICT_TYPE_JOIN "joinmap"
+
+extern DICT *dict_join_open(const char *, int, int);
+
+/* 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
+/*--*/
+
+#endif
diff -ruN b/src/util/dict_open.c c/src/util/dict_open.c
--- b/src/util/dict_open.c 2014-06-30 20:26:57.000000000 +0200
+++ c/src/util/dict_open.c 2014-09-11 12:03:46.898242860 +0200
@@ -300,6 +300,7 @@
#include <dict_fail.h>
#include <dict_pipe.h>
#include <dict_random.h>
+#include <dict_join.h>
#include <stringops.h>
#include <split_at.h>
#include <htable.h>
@@ -343,6 +344,7 @@
DICT_TYPE_SOCKMAP, dict_sockmap_open,
DICT_TYPE_FAIL, dict_fail_open,
DICT_TYPE_PIPE, dict_pipe_open,
+ DICT_TYPE_JOIN, dict_join_open,
#ifdef DICT_TYPE_PIPE_LEGACY
DICT_TYPE_PIPE_LEGACY, dict_pipe_open,
#endif
diff -ruN c/src/util/dict_join.c d/src/util/dict_join.c
--- c/src/util/dict_join.c 2014-09-11 12:02:19.538242235 +0200
+++ d/src/util/dict_join.c 2014-09-11 14:09:08.548294332 +0200
@@ -59,7 +59,7 @@
typedef struct {
DICT dict; /* generic members */
ARGV *map_join; /* pipelined tables */
- VSTRING *qr_buf; /* query/reply buffer */
+ VSTRING *re_buf; /* reply buffer */
} DICT_JOIN;
#define STR(x) vstring_str(x)
@@ -75,15 +75,20 @@
char *dict_type_name;
const char *result = 0;
- vstring_strcpy(dict_join->qr_buf, query);
+ VSTRING_RESET(dict_join->re_buf);
for (cpp = dict_join->map_join->argv; (dict_type_name = *cpp) != 0; cpp++)
{
if ((map = dict_handle(dict_type_name)) == 0)
msg_panic("%s: dictionary \"%s\" not found", myname,
dict_type_name);
- if ((result = dict_get(map, STR(dict_join->qr_buf))) == 0)
- DICT_ERR_VAL_RETURN(dict, map->error, result);
- vstring_strcpy(dict_join->qr_buf, result);
+ if ((result = dict_get(map, query)) == 0)
+ continue;
+ if (VSTRING_LEN(dict_join->re_buf) > 0)
+ VSTRING_ADDCH(dict_join->re_buf, ',');
+ vstring_strcat(dict_join->re_buf, result);
}
- DICT_ERR_VAL_RETURN(dict, DICT_ERR_NONE, STR(dict_join->qr_buf));
+ DICT_ERR_VAL_RETURN(dict, DICT_ERR_NONE,
+ VSTRING_LEN(dict_join->re_buf) > 0
+ ? STR(dict_join->re_buf)
+ : 0);
}
/* dict_join_close - disassociate from pipelined tables */
@@ -97,7 +102,7 @@
for (cpp = dict_join->map_join->argv; (dict_type_name = *cpp) != 0; cpp++)
dict_unregister(dict_type_name);
argv_free(dict_join->map_join);
- vstring_free(dict_join->qr_buf);
+ vstring_free(dict_join->re_buf);
dict_free(dict);
}
@@ -188,7 +193,7 @@
dict_join->dict.close = dict_join_close;
dict_join->dict.flags = dict_flags | match_flags;
dict_join->dict.owner = aggr_owner;
- dict_join->qr_buf = vstring_alloc(100);
+ dict_join->re_buf = vstring_alloc(100);
dict_join->map_join = argv;
argv = 0;
DICT_JOIN_RETURN(DICT_DEBUG (&dict_join->dict));