Cleanup: the netstring client sets or clears errno to improve
error messages from its callers. File: util/netstring.c.
+
+20250109
+
+ Bugfix (defect introduced: Postfix 2.6): fixed the parsing
+ of multiple commas in a multi_instance_directories parameter
+ value. Michael Tokarev. Files: conf/postfix-script,
+ conf/post-install, conf/postfix-wrapper, proto/postfix-wrapper.
+
+ Cleanup: replace static result buffer with per-instance buffer.
+ Michael Tokarev. File: util/dict_cdb.c.
+
+ Feature: first/next iterator support for cdb: tables, if
+ built with tinycdb. Michael Tokarev. Wietse added a test
+ and documentation. Files: util/dict_cdb.c proto/CDB_README.html,
+ postmap/Makefile.in.
maintained with the postmap(1) or postalias(1) command. The DATABASE_README
document has general information about Postfix databases.
-CDB support is available with Postfix 2.2 and later releases. This document
-describes how to build Postfix with CDB support.
+You can use "cdb:" tables wherever you can use read-only "hash", "btree" or
+"lmdb" tables with the following limitations:
+
+ * CDB databases cannot be larger than 4GB on LP64 and ILP32 systems, because
+ the CDB library API uses unsigned integers for file offsets.
+
+ * The "p\bpo\bos\bst\btm\bma\bap\bp -\b-i\bi" (incremental record insertion) and "p\bpo\bos\bst\btm\bma\bap\bp -\b-d\bd"
+ (incremental record deletion) command-line options are not available. For
+ the same reason the "cdb:" map type cannot be used to for persistent
+ caches, such as the address verification cache for the verify(8) service,
+ the TLS session cache for the tlsmgr(8) service, or the dynamic allowlist
+ for postscreen(8).
+
+ * The "sequence" operation ("p\bpo\bos\bst\btm\bma\bap\bp -\b-s\bs" or "p\bpo\bos\bst\bta\bal\bli\bia\bas\bs -\b-s\bs") is available only
+ wen Postfix is built with tinycdb by Michael Tokarev, not with the original
+ cdb library by Daniel Bernstein.
+
+CDB support is available with Postfix 2.2 and later releases. The remainder of
+this document describes how to build Postfix with CDB support.
B\bBu\bui\bil\bld\bdi\bin\bng\bg P\bPo\bos\bst\btf\bfi\bix\bx w\bwi\bit\bth\bh C\bCD\bDB\bB s\bsu\bup\bpp\bpo\bor\brt\bt
database library dependencies. And that was exactly what dynamic database
client loading was meant to avoid.
-After Postfix has been built with cdb support, you can use "cdb" tables
-wherever you can use read-only "hash", "btree" or "dbm" tables. However, the
-"p\bpo\bos\bst\btm\bma\bap\bp -\b-i\bi" (incremental record insertion) and "p\bpo\bos\bst\btm\bma\bap\bp -\b-d\bd" (incremental
-record deletion) command-line options are not available. For the same reason
-the "cdb" map type cannot be used to store the persistent address verification
-cache for the verify(8) service, or to store TLS session information for the
-tlsmgr(8) service.
-
relay_recipient_maps empty should default to 'no valid
recipients'. Subject to compatibility level.
+ Replace static result buffers with per-instance buffers in
+ dict_unix.c, dict_ni*c.
+
The Milter 'quarantine' action should be reported with a
call-back function, instead of setting the Milter default
reply. However, we still need the existing 'reply' based
instances=`test ! -f $def_config_directory/main.cf ||
$POSTCONF -qc $def_config_directory -h multi_instance_directories |
- sed 's/,/ /'` || exit 1
+ sed 'y/,/ /'` || exit 1
update_shared_files=1
for name in $instances
instances=`test ! -f $def_config_directory/main.cf ||
$command_directory/postconf -qc $def_config_directory \
- -h multi_instance_directories | sed 's/,/ /'` || {
+ -h multi_instance_directories | sed 'y/,/ /'` || {
$FATAL cannot execute $command_directory/postconf!
exit 1
}
# Canonicalize the instance directory list. The list is specified
# in startup order.
-instance_dirs=`$POSTCONF -h multi_instance_directories | sed 's/,/ /'` ||
+instance_dirs=`$POSTCONF -h multi_instance_directories | sed 'y/,/ /'` ||
exit 1
case "$1" in
or <a href="postalias.1.html">postalias(1)</a> command. The <a href="DATABASE_README.html">DATABASE_README</a> document has general
information about Postfix databases. </p>
+<p> You can use "<a href="CDB_README.html">cdb</a>:" tables wherever you can use read-only "hash",
+"btree" or "lmdb" tables with the following limitations: </p>
+
+<ul>
+
+<li> <p> CDB databases cannot be larger than 4GB on LP64 and ILP32
+systems, because the CDB library API uses unsigned integers for
+file offsets. </p>
+
+<li> <p> The "<b>postmap -i</b>" (incremental record insertion) and
+"<b>postmap -d</b>" (incremental record deletion) command-line
+options are not available. For the same reason the "<a href="CDB_README.html">cdb</a>:" map type
+cannot be used to for persistent caches, such as the address
+verification cache for the <a href="verify.8.html">verify(8)</a> service, the TLS session cache
+for the <a href="tlsmgr.8.html">tlsmgr(8)</a> service, or the dynamic allowlist for <a href="postscreen.8.html">postscreen(8)</a>.
+</p>
+
+<li> <p> The "sequence" operation ("<b>postmap -s</b>" or "<b>postalias
+-s</b>") is available only wen Postfix is built with tinycdb by
+Michael Tokarev, not with the original cdb library by Daniel Bernstein.
+</p>
+
+</ul>
+
<p> CDB support is available with Postfix 2.2 and later releases.
-This document describes how to build Postfix with CDB support. </p>
+The remainder of this document describes how to build Postfix with
+CDB support. </p>
<h2>Building Postfix with CDB support</h2>
what dynamic database client loading was meant to avoid. </p>
</blockquote>
-
-<p> After Postfix has been built with cdb support, you can use
-"cdb" tables wherever you can use read-only "hash", "btree" or
-"dbm" tables. However, the "<b>postmap -i</b>" (incremental record
-insertion) and "<b>postmap -d</b>" (incremental record deletion)
-command-line options are not available. For the same reason the
-"cdb" map type cannot be used to store the persistent address
-verification cache for the <a href="verify.8.html">verify(8)</a> service, or to store
-TLS session information for the <a href="tlsmgr.8.html">tlsmgr(8)</a> service. </p>
POSTCONF=$<a href="postconf.5.html#command_directory">command_directory</a>/postconf
POSTFIX=$<a href="postconf.5.html#command_directory">command_directory</a>/postfix
instance_dirs=`$POSTCONF -h <a href="postconf.5.html#multi_instance_directories">multi_instance_directories</a> |
- sed 's/,/ /'` || exit 1
+ sed 'y/,/ /'` || exit 1
err=0
for dir in $<a href="postconf.5.html#config_directory">config_directory</a> $instance_dirs
POSTCONF=$command_directory/postconf
POSTFIX=$command_directory/postfix
instance_dirs=\`$POSTCONF \-h multi_instance_directories |
- sed 's/,/ /'\` || exit 1
+ sed 'y/,/ /'\` || exit 1
err=0
for dir in $config_directory $instance_dirs
or postalias(1) command. The DATABASE_README document has general
information about Postfix databases. </p>
+<p> You can use "cdb:" tables wherever you can use read-only "hash",
+"btree" or "lmdb" tables with the following limitations: </p>
+
+<ul>
+
+<li> <p> CDB databases cannot be larger than 4GB on LP64 and ILP32
+systems, because the CDB library API uses unsigned integers for
+file offsets. </p>
+
+<li> <p> The "<b>postmap -i</b>" (incremental record insertion) and
+"<b>postmap -d</b>" (incremental record deletion) command-line
+options are not available. For the same reason the "cdb:" map type
+cannot be used to for persistent caches, such as the address
+verification cache for the verify(8) service, the TLS session cache
+for the tlsmgr(8) service, or the dynamic allowlist for postscreen(8).
+</p>
+
+<li> <p> The "sequence" operation ("<b>postmap -s</b>" or "<b>postalias
+-s</b>") is available only wen Postfix is built with tinycdb by
+Michael Tokarev, not with the original cdb library by Daniel Bernstein.
+</p>
+
+</ul>
+
<p> CDB support is available with Postfix 2.2 and later releases.
-This document describes how to build Postfix with CDB support. </p>
+The remainder of this document describes how to build Postfix with
+CDB support. </p>
<h2>Building Postfix with CDB support</h2>
what dynamic database client loading was meant to avoid. </p>
</blockquote>
-
-<p> After Postfix has been built with cdb support, you can use
-"cdb" tables wherever you can use read-only "hash", "btree" or
-"dbm" tables. However, the "<b>postmap -i</b>" (incremental record
-insertion) and "<b>postmap -d</b>" (incremental record deletion)
-command-line options are not available. For the same reason the
-"cdb" map type cannot be used to store the persistent address
-verification cache for the verify(8) service, or to store
-TLS session information for the tlsmgr(8) service. </p>
# POSTCONF=$command_directory/postconf
# POSTFIX=$command_directory/postfix
# instance_dirs=\`$POSTCONF -h multi_instance_directories |
-# sed 's/,/ /'\` || exit 1
+# sed 'y/,/ /'\` || exit 1
#
# err=0
# for dir in $config_directory $instance_dirs
atext
qp
cntrl
+TINYCDB
+getdata
* Patches change both the patchlevel and the release date. Snapshots have no
* patchlevel; they change the release date only.
*/
-#define MAIL_RELEASE_DATE "20250107"
+#define MAIL_RELEASE_DATE "20250109"
#define MAIL_VERSION_NUMBER "3.10"
#ifdef SNAPSHOT
cp $(PROG) ../../bin
tests: test1 test2 fail_test quote_test file_test lmdb_abb_test \
- lmdb_bulk_test lmdb_incr_test
+ lmdb_bulk_test lmdb_incr_test cdb_bulk_test
root_tests:
diff file_test.ref file_test.tmp
rm -f file_test.tmp file_test_map.* postmap-file-1 postmap-file-2
+cdb_bulk_test: $(PROG)
+ rm -f cdb_bulk.cdb main.cf
+ tr A-Z a-z < /usr/share/dict/words| \
+ sed -e 's/.*/& &/' -e 10000q| LANG=C sort -u >cdb_bulk
+ touch -t 197101010000 main.cf
+ ($(SHLIB_ENV) $(VALGRIND) ./postmap -c . cdb:cdb_bulk; \
+ $(SHLIB_ENV) $(VALGRIND) ./postmap -s cdb:cdb_bulk | \
+ LANG=C sort > cdb_bulk.tmp)
+ cmp cdb_bulk cdb_bulk.tmp
+ rm -f cdb_bulk cdb_bulk.tmp cdb_bulk.cdb main.cf
+
lmdb_abb_test: $(PROG) lmdb_abb lmdb_abb.ref
rm -f lmdb_abb.lmdb
($(SHLIB_ENV) $(VALGRIND) ./postmap lmdb:lmdb_abb; \
tr A-Z a-z < /usr/share/dict/words| \
sed -e 's/.*/& &/' -e 10000q| LANG=C sort -u >lmdb_retry
echo lmdb_map_size=10240 >main.cf
+ touch -t 197101010000 main.cf
($(SHLIB_ENV) $(VALGRIND) ./postmap -c . lmdb:lmdb_retry; \
$(SHLIB_ENV) $(VALGRIND) ./postmap -s lmdb:lmdb_retry | \
LANG=C sort > lmdb_retry.tmp)
tr A-Z a-z < /usr/share/dict/words| \
sed -e 's/.*/& &/' -e 1000q| LANG=C sort -u >lmdb_retry
echo lmdb_map_size=10240 >main.cf
+ touch -t 197101010000 main.cf
($(SHLIB_ENV) $(VALGRIND) ./postmap -ic . lmdb:lmdb_retry <lmdb_retry; \
$(SHLIB_ENV) $(VALGRIND) ./postmap -s lmdb:lmdb_retry | \
LANG=C sort > lmdb_retry.tmp)
typedef struct {
DICT dict; /* generic members */
struct cdb cdb; /* cdb structure */
+ VSTRING *val_buf; /* value result */
+#ifdef TINYCDB_VERSION
+ VSTRING *key_buf; /* key result */
+ unsigned seq_cptr; /* current sequence pointer */
+#endif
} DICT_CDBQ; /* query interface */
typedef struct {
char *tmp_path; /* temporary pathname (.tmp) */
} DICT_CDBM; /* rebuild interface */
+/* dict_cdbq_getdata - get data out of the cdb using given buffer */
+
+static const char *dict_cdbq_get_data(DICT_CDBQ *dict_cdbq,
+ VSTRING **bufp, unsigned len, unsigned pos)
+{
+ VSTRING *buf = *bufp;
+
+ if (!buf)
+ buf = *bufp = vstring_alloc(len < 20 ? 20 : len);
+ VSTRING_RESET(buf);
+ VSTRING_SPACE(buf, len);
+
+ if (cdb_read(&dict_cdbq->cdb, vstring_str(buf), len, pos) < 0)
+ msg_fatal("error reading %s: %m", dict_cdbq->dict.name);
+ vstring_set_payload_size(buf, len);
+ VSTRING_TERMINATE(buf);
+ return vstring_str(buf);
+}
+
/* dict_cdbq_lookup - find database entry, query mode */
static const char *dict_cdbq_lookup(DICT *dict, const char *name)
{
DICT_CDBQ *dict_cdbq = (DICT_CDBQ *) dict;
- unsigned vlen;
int status = 0;
- static char *buf;
- static unsigned len;
const char *result = 0;
dict->error = 0;
msg_fatal("error reading %s: %m", dict->name);
if (status) {
- vlen = cdb_datalen(&dict_cdbq->cdb);
- if (len < vlen) {
- if (buf == 0)
- buf = mymalloc(vlen + 1);
- else
- buf = myrealloc(buf, vlen + 1);
- len = vlen;
- }
- if (cdb_read(&dict_cdbq->cdb, buf, vlen,
- cdb_datapos(&dict_cdbq->cdb)) < 0)
- msg_fatal("error reading %s: %m", dict->name);
- buf[vlen] = '\0';
- result = buf;
+ result = dict_cdbq_get_data(dict_cdbq, &dict_cdbq->val_buf,
+ cdb_datalen(&dict_cdbq->cdb), cdb_datapos(&dict_cdbq->cdb));
}
/* No locking so not release the lock. */
return (result);
}
+#ifdef TINYCDB_VERSION
+
+/* dict_cdbq_sequence - traverse the dictionary */
+
+static int dict_cdbq_sequence(DICT *dict, int function,
+ const char **key, const char **value)
+{
+ const char *myname = "dict_cdbq_sequence";
+ DICT_CDBQ *dict_cdbq = (DICT_CDBQ *) dict;
+ int status;
+
+ switch (function) {
+ case DICT_SEQ_FUN_FIRST:
+ cdb_seqinit(&dict_cdbq->seq_cptr, &dict_cdbq->cdb);
+ break;
+ case DICT_SEQ_FUN_NEXT:
+ if (!dict_cdbq->seq_cptr)
+ msg_panic("%s: %s: no cursor", myname, dict_cdbq->dict.name);
+ break;
+ default:
+ msg_panic("%s: invalid function %d", myname, function);
+ }
+
+ status = cdb_seqnext(&dict_cdbq->seq_cptr, &dict_cdbq->cdb);
+
+ if (status < 0)
+ msg_fatal("error seeking %s: %m", dict_cdbq->dict.name);
+
+ if (!status) {
+ dict_cdbq->seq_cptr = 0;
+ return -1; /* not found */
+ }
+ *key = dict_cdbq_get_data(dict_cdbq, &dict_cdbq->key_buf,
+ cdb_keylen(&dict_cdbq->cdb), cdb_keypos(&dict_cdbq->cdb));
+ *value = dict_cdbq_get_data(dict_cdbq, &dict_cdbq->val_buf,
+ cdb_datalen(&dict_cdbq->cdb), cdb_datapos(&dict_cdbq->cdb));
+
+ return 0;
+}
+
+#endif /* TINYCDB_VERSION */
+
/* dict_cdbq_close - close data base, query mode */
static void dict_cdbq_close(DICT *dict)
close(dict->stat_fd);
if (dict->fold_buf)
vstring_free(dict->fold_buf);
+ if (dict_cdbq->val_buf)
+ vstring_free(dict_cdbq->val_buf);
+#ifdef TINYCDB_VERSION
+ if (dict_cdbq->key_buf)
+ vstring_free(dict_cdbq->key_buf);
+#endif
dict_free(dict);
}
dict_cdbq = (DICT_CDBQ *) dict_alloc(DICT_TYPE_CDB,
cdb_path, sizeof(*dict_cdbq));
+ dict_cdbq->val_buf = 0;
#if defined(TINYCDB_VERSION)
+ dict_cdbq->key_buf = 0;
+ dict_cdbq->seq_cptr = 0;
if (cdb_init(&(dict_cdbq->cdb), fd) != 0)
msg_fatal("dict_cdbq_open: unable to init %s: %m", cdb_path);
+ dict_cdbq->dict.sequence = dict_cdbq_sequence;
#else
cdb_init(&(dict_cdbq->cdb), fd);
#endif