This reuses the existing proxy object created by the dict_debug()
function.
---
proto/DATABASE_README.html | 13 +++++++
src/postconf/postconf.c | 9 +++++
src/proxymap/Makefile.in | 1 +
src/proxymap/proxymap.c | 2 ++
src/util/Makefile.in | 12 +++++--
src/util/dict_debug.c | 66 ++++++++++++++++++++++++++++++++----
src/util/dict_debug.h | 34 +++++++++++++++++++
src/util/dict_debug_test.ref | 51 ++++++++++++++++++++++++++++
src/util/dict_debug_test.sh | 25 ++++++++++++++
src/util/dict_open.c | 2 ++
10 files changed, 207 insertions(+), 8 deletions(-)
create mode 100644 src/util/dict_debug.h
create mode 100644 src/util/dict_debug_test.ref
create mode 100755 src/util/dict_debug_test.sh
diff --git a/proto/DATABASE_README.html b/proto/DATABASE_README.html
index 42a54ac7c..4182bb8c8 100644
--- a/proto/DATABASE_README.html
+++ b/proto/DATABASE_README.html
@@ -293,6 +293,19 @@ databases are maintained by Postfix daemons. The lookup
table name
as used in "dbm:table" is the database file name without the ".dir"
or ".pag" suffix. </dd>
+<dt> <b>debug</b> </dt>
+
+<dd>
+<p> An adapter for another table that causes all accesses to be
+logged. Example usage: "debug:hash:/etc/postfix/example". The
+formats of the log messages are unspecified and subject to change.
+Warning: If a query or the underlying table contains sensitive
+information (such as a password), that information might be
+logged. </p>
+
+<p> This feature is available with Postfix 3.11 and later. </p>
+</dd>
+
<dt> <b>environ</b> </dt>
<dd> The UNIX process environment array. The lookup key is the
diff --git a/src/postconf/postconf.c b/src/postconf/postconf.c
index 74f13b2cd..a4d4c8490 100644
--- a/src/postconf/postconf.c
+++ b/src/postconf/postconf.c
@@ -252,6 +252,15 @@
/* .IP \fBdbm\fR
/* An indexed file type based on hashing. Available on systems
/* with support for DBM databases.
+/* .IP \fBdebug\fR
+/* An adapter for another table that causes all accesses to be
+/* logged. Example usage: \fBdebug:hash:/etc/postfix/example\fR.
+/* The formats of the log messages are unspecified and subject to
+/* change. Warning: If a query or the underlying table contains
+/* sensitive information (such as a password), that information
+/* might be logged.
+/*
+/* This feature is available with Postfix 3.11 and later.
/* .IP \fBenviron\fR
/* The UNIX process environment array. The lookup key is the
/* environment variable name; the table name is ignored. Originally
diff --git a/src/proxymap/Makefile.in b/src/proxymap/Makefile.in
index efe6e0c87..29dec6a74 100644
--- a/src/proxymap/Makefile.in
+++ b/src/proxymap/Makefile.in
@@ -52,6 +52,7 @@ proxymap.o: ../../include/argv.h
proxymap.o: ../../include/attr.h
proxymap.o: ../../include/check_arg.h
proxymap.o: ../../include/dict.h
+proxymap.o: ../../include/dict_debug.h
proxymap.o: ../../include/dict_pipe.h
proxymap.o: ../../include/dict_proxy.h
proxymap.o: ../../include/dict_union.h
diff --git a/src/proxymap/proxymap.c b/src/proxymap/proxymap.c
index c0af411f6..9fc1159e8 100644
--- a/src/proxymap/proxymap.c
+++ b/src/proxymap/proxymap.c
@@ -232,6 +232,7 @@
#include <htable.h>
#include <stringops.h>
#include <dict.h>
+#include <dict_debug.h>
#include <dict_pipe.h>
#include <dict_union.h>
@@ -307,6 +308,7 @@ static char *get_nested_dict_name(char *type_name)
} *prefix, prefixes[] = {
DICT_TYPE_UNION ":", (sizeof(DICT_TYPE_UNION ":") - 1),
DICT_TYPE_PIPE ":", (sizeof(DICT_TYPE_PIPE ":") - 1),
+ DICT_TYPE_DEBUG ":", (sizeof(DICT_TYPE_DEBUG ":") - 1),
};
#define COUNT_OF(x) (sizeof(x)/sizeof((x)[0]))
diff --git a/src/util/Makefile.in b/src/util/Makefile.in
index 7c99ae23a..b9d70dbd2 100644
--- a/src/util/Makefile.in
+++ b/src/util/Makefile.in
@@ -104,7 +104,8 @@ MAP_OBJ = dict_pcre.o dict_cdb.o dict_lmdb.o
dict_sdbm.o slmdb.o \
mkmap_cdb.o mkmap_lmdb.o mkmap_sdbm.o
HDRS = argv.h attr.h attr_clnt.h auto_clnt.h base64_code.h binhash.h \
chroot_uid.h cidr_match.h clean_env.h connect.h ctable.h dict.h \
- dict_cdb.h dict_cidr.h dict_db.h dict_dbm.h dict_env.h dict_ht.h \
+ dict_cdb.h dict_cidr.h dict_db.h dict_dbm.h dict_debug.h dict_env.h \
+ dict_ht.h \
dict_lmdb.h dict_ni.h dict_nis.h dict_nisplus.h dict_pcre.h
dict_regexp.h \
dict_sdbm.h dict_static.h dict_tcp.h dict_unix.h dir_forest.h \
events.h exec_command.h find_inet.h fsspace.h fullname.h \
@@ -658,7 +659,7 @@ dict_tests: all dict_test \
dict_pipe_test dict_regexp_file_test dict_cidr_file_test \
dict_static_file_test dict_random_test dict_random_file_test \
dict_inline_file_test dict_stream_test dict_inline_regexp_test \
- dict_inline_cidr_test
+ dict_inline_cidr_test dict_debug_test
dict_pcre_tests: dict_pcre_test miss_endif_pcre_test dict_pcre_file_test \
dict_inline_pcre_test
@@ -1113,6 +1114,11 @@ dict_inline_cidr_test: dict_open dict_inline_cidr.ref
diff dict_inline_cidr.ref dict_inline_cidr.tmp
rm -f dict_inline_cidr.tmp
+dict_debug_test: dict_open dict_debug_test.sh dict_debug_test.ref
+ $(SHLIB_ENV) ./dict_debug_test.sh >dict_debug_test.tmp 2>&1
+ diff dict_debug_test.ref dict_debug_test.tmp
+ rm -f dict_debug_test.tmp
+
find_inet_test: find_inet find_inet.ref
$(SHLIB_ENV) ${VALGRIND} ./find_inet >find_inet.tmp 2>&1
diff find_inet.ref find_inet.tmp
@@ -1545,6 +1551,7 @@ dict_debug.o: argv.h
dict_debug.o: check_arg.h
dict_debug.o: dict.h
dict_debug.o: dict_debug.c
+dict_debug.o: dict_debug.h
dict_debug.o: msg.h
dict_debug.o: myflock.h
dict_debug.o: mymalloc.h
@@ -1672,6 +1679,7 @@ dict_open.o: dict_cdb.h
dict_open.o: dict_cidr.h
dict_open.o: dict_db.h
dict_open.o: dict_dbm.h
+dict_open.o: dict_debug.h
dict_open.o: dict_env.h
dict_open.o: dict_fail.h
dict_open.o: dict_ht.h
diff --git a/src/util/dict_debug.c b/src/util/dict_debug.c
index 46634d40c..1ea67240e 100644
--- a/src/util/dict_debug.c
+++ b/src/util/dict_debug.c
@@ -11,6 +11,13 @@
/*
/* DICT *DICT_DEBUG(dict_handle)
/* DICT *dict_handle;
+/*
+/* #include <dict_debug.h>
+/*
+/* DICT *dict_debug_open(name, open_flags, dict_flags);
+/* const char *name;
+/* int open_flags;
+/* int dict_flags;
/* DESCRIPTION
/* dict_debug() encapsulates the given dictionary object and returns
/* a proxy object that logs all access to the encapsulated object.
@@ -21,6 +28,12 @@
/* the object's debugging flag is not set, and that otherwise encapsulates
/* the object with dict_debug(). This macro simplifies usage by avoiding
/* clumsy expressions. The macro evaluates its argument multiple times.
+/*
+/* dict_debug_open() is similar to dict_debug() except the underlying
+/* table's handle is registered as \fIname\fR, which has the form
+/* "\fItype\fB:\fIname\fR". If no such registration exists yet, the
+/* underlying table is first opened as if by a call to
+/* dict_open(\fIname\fR, \fIopen_flags\fR, \fIdict_flags\fR).
/* DIAGNOSTICS
/* Fatal errors: out of memory.
/* LICENSE
@@ -32,6 +45,8 @@
/* IBM T.J. Watson Research
/* P.O. Box 704
/* Yorktown Heights, NY 10598, USA
+/*
+/* Richard Hansen <[email protected]> (dict_debug_open(), © 2025)
/*--*/
/* System libraries. */
@@ -43,12 +58,14 @@
#include <msg.h>
#include <mymalloc.h>
#include <dict.h>
+#include "dict_debug.h"
/* Application-specific. */
typedef struct {
DICT dict; /* the proxy service */
DICT *real_dict; /* encapsulated object */
+ char *reg; /* real_dict registration, or null */
} DICT_DEBUG;
/* dict_debug_lookup - log lookup operation */
@@ -62,7 +79,11 @@ static const char *dict_debug_lookup(DICT *dict, const char
*key)
real_dict->flags = dict->flags;
result = dict_get(real_dict, key);
dict->flags = real_dict->flags;
- msg_info("%s:%s lookup: \"%s\" = \"%s\"", dict->type, dict->name, key,
+ msg_info("%s%s%s lookup: \"%s\" = \"%s\"",
+ dict_debug->reg ? dict_debug->reg : dict->type,
+ dict_debug->reg ? "" : ":",
+ dict_debug->reg ? "" : dict->name,
+ key,
result ? result : real_dict->error ? "error" : "not_found");
DICT_ERR_VAL_RETURN(dict, real_dict->error, result);
}
@@ -78,7 +99,10 @@ static int dict_debug_update(DICT *dict, const char *key,
const char *value)
real_dict->flags = dict->flags;
result = dict_put(real_dict, key, value);
dict->flags = real_dict->flags;
- msg_info("%s:%s update: \"%s\" = \"%s\": %s", dict->type, dict->name,
+ msg_info("%s%s%s update: \"%s\" = \"%s\": %s",
+ dict_debug->reg ? dict_debug->reg : dict->type,
+ dict_debug->reg ? "" : ":",
+ dict_debug->reg ? "" : dict->name,
key, value, result == 0 ? "success" : real_dict->error ?
"error" : "failed");
DICT_ERR_VAL_RETURN(dict, real_dict->error, result);
@@ -95,7 +119,11 @@ static int dict_debug_delete(DICT *dict, const char *key)
real_dict->flags = dict->flags;
result = dict_del(real_dict, key);
dict->flags = real_dict->flags;
- msg_info("%s:%s delete: \"%s\": %s", dict->type, dict->name, key,
+ msg_info("%s%s%s delete: \"%s\": %s",
+ dict_debug->reg ? dict_debug->reg : dict->type,
+ dict_debug->reg ? "" : ":",
+ dict_debug->reg ? "" : dict->name,
+ key,
result == 0 ? "success" : real_dict->error ?
"error" : "failed");
DICT_ERR_VAL_RETURN(dict, real_dict->error, result);
@@ -114,10 +142,16 @@ static int dict_debug_sequence(DICT *dict, int function,
result = dict_seq(real_dict, function, key, value);
dict->flags = real_dict->flags;
if (result == 0)
- msg_info("%s:%s sequence: \"%s\" = \"%s\"", dict->type, dict->name,
+ msg_info("%s%s%s sequence: \"%s\" = \"%s\"",
+ dict_debug->reg ? dict_debug->reg : dict->type,
+ dict_debug->reg ? "" : ":",
+ dict_debug->reg ? "" : dict->name,
*key, *value);
else
- msg_info("%s:%s sequence: found EOF", dict->type, dict->name);
+ msg_info("%s%s%s sequence: found EOF",
+ dict_debug->reg ? dict_debug->reg : dict->type,
+ dict_debug->reg ? "" : ":",
+ dict_debug->reg ? "" : dict->name);
DICT_ERR_VAL_RETURN(dict, real_dict->error, result);
}
@@ -127,7 +161,11 @@ static void dict_debug_close(DICT *dict)
{
DICT_DEBUG *dict_debug = (DICT_DEBUG *) dict;
- dict_close(dict_debug->real_dict);
+ if (dict_debug->reg != 0) {
+ dict_unregister(dict_debug->reg);
+ myfree(dict_debug->reg);
+ } else
+ dict_close(dict_debug->real_dict);
dict_free(dict);
}
@@ -146,5 +184,21 @@ DICT *dict_debug(DICT *real_dict)
dict_debug->dict.sequence = dict_debug_sequence;
dict_debug->dict.close = dict_debug_close;
dict_debug->real_dict = real_dict;
+ dict_debug->reg = 0;
return (&dict_debug->dict);
}
+
+DICT *dict_debug_open(const char *name, int open_flags, int dict_flags)
+{
+ static const char myname[] = "dict_debug_open";
+ if (msg_verbose)
+ msg_info("%s: %s", myname, name);
+ DICT *real_dict = dict_handle(name);
+ if (real_dict == 0)
+ real_dict = dict_open(name, open_flags, dict_flags);
+ dict_register(name, real_dict);
+ DICT_DEBUG *dd = (DICT_DEBUG *) dict_debug(real_dict);
+ dd->reg = mystrdup(name);
+ dd->dict.owner = real_dict->owner;
+ return &dd->dict;
+}
diff --git a/src/util/dict_debug.h b/src/util/dict_debug.h
new file mode 100644
index 000000000..bbf56c6c6
--- /dev/null
+++ b/src/util/dict_debug.h
@@ -0,0 +1,34 @@
+#ifndef _DICT_DEBUG_H_INCLUDED_
+#define _DICT_DEBUG_H_INCLUDED_
+
+/*++
+/* NAME
+/* dict_debug 3h
+/* SUMMARY
+/* dictionary manager interface for "debug" tables
+/* SYNOPSIS
+/* #include <dict_debug.h>
+/* DESCRIPTION
+/* .nf
+
+ /*
+ * Utility library.
+ */
+#include <dict.h>
+
+ /*
+ * External interface.
+ */
+#define DICT_TYPE_DEBUG "debug"
+
+extern DICT *dict_debug_open(const char *, int, int);
+
+/* LICENSE
+/* .ad
+/* .fi
+/* The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/* Richard Hansen <[email protected]> (© 2025)
+/*--*/
+
+#endif
diff --git a/src/util/dict_debug_test.ref b/src/util/dict_debug_test.ref
new file mode 100644
index 000000000..0c31d1ab1
--- /dev/null
+++ b/src/util/dict_debug_test.ref
@@ -0,0 +1,51 @@
++ ./dict_open debug: read
+./dict_open: fatal: open dictionary: expecting "type:name" form instead of
"debug:"
++ ./dict_open debug:missing_colon_and_name read
+./dict_open: fatal: open dictionary: expecting "type:name" form instead of
"missing_colon_and_name"
++ ./dict_open debug:static:{space in name} read
+owner=trusted (uid=2147483647)
+> get k
+./dict_open: static:{space in name} lookup: "k" = "space in name"
+k=space in name
++ ./dict_open debug:debug:static:value read
+owner=trusted (uid=2147483647)
+> get k
+./dict_open: static:value lookup: "k" = "value"
+./dict_open: debug:static:value lookup: "k" = "value"
+k=value
++ ./dict_open debug:internal:name write
+owner=trusted (uid=2147483647)
+> get k
+./dict_open: internal:name lookup: "k" = "not_found"
+k: not found
+> put k=v
+./dict_open: internal:name update: "k" = "v": success
+> get k
+./dict_open: internal:name lookup: "k" = "v"
+k=v
+> first
+./dict_open: internal:name sequence: "k" = "v"
+k=v
+> next
+./dict_open: internal:name sequence: found EOF
+not found
+> del k
+./dict_open: internal:name delete: "k": success
+k: deleted
+> get k
+./dict_open: internal:name lookup: "k" = "not_found"
+k: not found
+> del k
+./dict_open: internal:name delete: "k": failed
+k: not found
+> first
+./dict_open: internal:name sequence: found EOF
+not found
++ ./dict_open debug:fail:{oh no} read
+owner=trusted (uid=2147483647)
+> get k
+./dict_open: fail:{oh no} lookup: "k" = "error"
+k: error
+> first
+./dict_open: fail:{oh no} sequence: found EOF
+error
diff --git a/src/util/dict_debug_test.sh b/src/util/dict_debug_test.sh
new file mode 100755
index 000000000..a8c051d80
--- /dev/null
+++ b/src/util/dict_debug_test.sh
@@ -0,0 +1,25 @@
+#!/bin/sh
+set -ex
+! ${VALGRIND} ./dict_open 'debug:' read </dev/null || exit 1
+! ${VALGRIND} ./dict_open 'debug:missing_colon_and_name' read </dev/null ||
exit 1
+${VALGRIND} ./dict_open 'debug:static:{space in name}' read <<EOF
+get k
+EOF
+${VALGRIND} ./dict_open 'debug:debug:static:value' read <<EOF
+get k
+EOF
+${VALGRIND} ./dict_open 'debug:internal:name' write <<EOF
+get k
+put k=v
+get k
+first
+next
+del k
+get k
+del k
+first
+EOF
+${VALGRIND} ./dict_open 'debug:fail:{oh no}' read <<EOF
+get k
+first
+EOF
diff --git a/src/util/dict_open.c b/src/util/dict_open.c
index c3b90d497..bc42c06fc 100644
--- a/src/util/dict_open.c
+++ b/src/util/dict_open.c
@@ -337,6 +337,7 @@
#include <msg.h>
#include <dict.h>
#include <dict_cdb.h>
+#include <dict_debug.h>
#include <dict_env.h>
#include <dict_unix.h>
#include <dict_tcp.h>
@@ -415,6 +416,7 @@ static const DICT_OPEN_INFO dict_open_info[] = {
DICT_TYPE_LMDB, dict_lmdb_open, mkmap_lmdb_open,
#endif
#endif /* !USE_DYNAMIC_MAPS */
+ DICT_TYPE_DEBUG, dict_debug_open, 0,
0,
};
--
2.49.0
_______________________________________________
Postfix-devel mailing list -- [email protected]
To unsubscribe send an email to [email protected]