]> git.ipfire.org Git - thirdparty/postfix.git/commitdiff
postfix-3.11-20250709
authorWietse Z Venema <wietse@porcupine.org>
Wed, 9 Jul 2025 05:00:00 +0000 (00:00 -0500)
committerViktor Dukhovni <ietf-dane@dukhovni.org>
Thu, 10 Jul 2025 07:44:56 +0000 (17:44 +1000)
29 files changed:
postfix/HISTORY
postfix/proto/stop.double-history
postfix/proto/stop.spell-cc
postfix/src/global/data_redirect.c
postfix/src/global/mail_version.h
postfix/src/global/maps.c
postfix/src/global/mynetworks.c
postfix/src/global/namadr_list.in
postfix/src/global/namadr_list.ref
postfix/src/global/own_inet_addr.c
postfix/src/global/server_acl.c
postfix/src/proxymap/proxymap.c
postfix/src/testing/Makefile.in
postfix/src/util/Makefile.in
postfix/src/util/dict.c
postfix/src/util/dict.h
postfix/src/util/dict_alloc.c
postfix/src/util/dict_debug.c
postfix/src/util/dict_debug_test.ref
postfix/src/util/dict_debug_test.sh [changed mode: 0644->0755]
postfix/src/util/dict_inline.c
postfix/src/util/dict_open.c
postfix/src/util/dict_pipe.c
postfix/src/util/dict_seq.ref
postfix/src/util/dict_test.c
postfix/src/util/dict_thash.c
postfix/src/util/dict_union.c
postfix/src/util/dict_utf8_test.ref
postfix/src/util/match_list.c

index f103cac513aee38c172312fef3a5afdef3352325..0f2e25b15df3d5f6c697357a849b437aedd5f8a9 100644 (file)
@@ -29309,3 +29309,39 @@ Apologies for any names omitted.
        util/dict_regexp.c, util/dict_sdbm.c, util/dict_sockmap.c,
        util/dict_static.c, util/dict_surrogate.c, util/dict_tcp.c,
        util/dict_thash.c, util/dict_union.c, util/dict_unix.c.
+
+20250626
+
+       Cleanup: removed explicit dictionary life-cycle management
+       complexity from dict_xxx_open() and maps_create(), and
+       centralized it under the generic dict_open() API. Files:
+       global/maps.c, util/dict.c, util/dict.h, util/dict_alloc.c,
+       util/dict_debug.c, util/dict_inline.c, util/dict_open.c,
+       util/dict_pipe.c, util/dict_test.c, util/dict_thash.c,
+       util/dict_union.c.
+
+20250627
+
+       Temporary workaround to avoid a potential breaking change:
+       allow the proxymap server to continue registering a dictionary
+       under a legacy name, in addition to the preferred name that
+       it is registered under by dict_open(). Files: util/dict.[hc],
+       proxymap/proxymap.c.
+
+       Cleanup: some unused test binaries failed to build.  Files:
+       global/own_inet_addr.c, global/data_redirect.c,
+       global/mynetworks.c.
+
+20250608
+
+       Cosmetic changes with better identifier names. Files:
+       util/dict.[hc], util/dict_alloc.c, util/dict_pipe.c,
+       util/dict_union.c
+
+       Cleanup: simplified the match_list parser, and added a unit
+       test for !maptype:mapname. Files: util/match_list.c,
+       global/namaddr_list.in, global/namaddr_list.ref.
+
+       Cleanup: simplified the rule parser in global/server_acl.c.
+
+       Unbroke dict_debug Valgrind checks. File: util/dict_debug_test.sh.
index 2fc207315ccf713252df27cc3ad070f35e0d6271..cf5da64051c77af0a13a2c47a24d17e5201d019d 100644 (file)
@@ -177,3 +177,5 @@ proto  proto COMPATIBILITY_README html
  smtp smtp c tlsproxy tlsproxy c proto postconf proto 
  rhansen rhansen org Files proto DATABASE_README html 
  postconf Makefile in postconf postconf c 
+ dict_open Files util dict hc proxymap proxymap c 
+ proxymap proxymap c 
index 506c2d2c8c1ae38a8b9e873dcc90d0a939fa93a5..10ffc77e927e103cbf05b8b87c77043ff876c85c 100644 (file)
@@ -1864,3 +1864,4 @@ DGST
 DIGEST
 OSSL
 ossl
+deduplicates
index 1a8fe0f9cb3ed8d2771c34fdef6cb2069a6132ec..b303e2b48f2cfb98b014ba90d640ffcac8293af5 100644 (file)
@@ -227,7 +227,7 @@ int     main(int argc, char **argv)
            vstream_fflush(VSTREAM_OUT);
            continue;
        }
-       target = mystrtokq(&bufp, " \t");
+       target = mystrtokq(&bufp, " \t", CHARS_BRACE);
        junk = mystrtok(&bufp, " \t");
        if (strcmp(cmd, "file") == 0 && target && !junk) {
            data_redirect_file(result, target);
index d8c67baf7ae3f855edcdbf78c90f1a3dd0ab648b..f19314a407d5cb7aa34135c474d0e861748285e6 100644 (file)
@@ -20,7 +20,7 @@
   * Patches change both the patchlevel and the release date. Snapshots have no
   * patchlevel; they change the release date only.
   */
-#define MAIL_RELEASE_DATE      "20250624"
+#define MAIL_RELEASE_DATE      "20250709"
 #define MAIL_VERSION_NUMBER    "3.11"
 
 #ifdef SNAPSHOT
index 592ec91d93a1e05c01ee8742f7a34886c3775acd..7fb33ed99975ce6e61d7908b036342d2ce223137 100644 (file)
@@ -132,7 +132,6 @@ MAPS   *maps_create(const char *title, const char *map_names, int dict_flags)
     static char parens[] = CHARS_BRACE;
     MAPS   *maps;
     char   *map_type_name;
-    VSTRING *map_type_name_flags;
     DICT   *dict;
 
     /*
@@ -149,23 +148,17 @@ MAPS   *maps_create(const char *title, const char *map_names, int dict_flags)
      */
     if (*map_names) {
        bufp = temp = mystrdup(map_names);
-       map_type_name_flags = vstring_alloc(10);
 
 #define OPEN_FLAGS     O_RDONLY
 
        while ((map_type_name = mystrtokq(&bufp, sep, parens)) != 0) {
-           dict_make_registered_name(map_type_name_flags, map_type_name,
-                                     OPEN_FLAGS, dict_flags);
-           if ((dict = dict_handle(vstring_str(map_type_name_flags))) == 0)
-               dict = dict_open(map_type_name, OPEN_FLAGS, dict_flags);
+           dict = dict_open(map_type_name, OPEN_FLAGS, dict_flags);
            if ((dict->flags & dict_flags) != dict_flags)
                msg_panic("%s: map %s has flags 0%o, want flags 0%o",
                          myname, map_type_name, dict->flags, dict_flags);
-           dict_register(vstring_str(map_type_name_flags), dict);
-           argv_add(maps->argv, vstring_str(map_type_name_flags), ARGV_END);
+           argv_add(maps->argv, dict->reg_name, ARGV_END);
        }
        myfree(temp);
-       vstring_free(map_type_name_flags);
     }
     return (maps);
 }
@@ -299,12 +292,16 @@ const char *maps_file_find(MAPS *maps, const char *name, int flags)
 
 MAPS   *maps_free(MAPS *maps)
 {
+    const char *myname = "maps_free";
+    DICT   *dict;
     char  **map_name;
 
     for (map_name = maps->argv->argv; *map_name; map_name++) {
        if (msg_verbose)
            msg_info("maps_free: %s", *map_name);
-       dict_unregister(*map_name);
+       if ((dict = dict_handle(*map_name)) == 0)
+           msg_panic("%s: dictionary not found: %s", myname, *map_name);
+       dict_close(dict);
     }
     myfree(maps->title);
     argv_free(maps->argv);
index 007c046cd6ffb4ef46442ca649df0484d6721003..75cc61701cf221be93b26b70410ac1967c04514b 100644 (file)
@@ -315,12 +315,9 @@ const char *mynetworks_host(void)
 #ifdef TEST
 #include <inet_proto.h>
 
-char   *var_inet_interfaces;
-char   *var_mynetworks_style;
-
 int     main(int argc, char **argv)
 {
-    INET_PROTO_INFO *proto_info;
+    const INET_PROTO_INFO *proto_info;
 
     if (argc != 4)
        msg_fatal("usage: %s protocols mask_style interface_list (e.g. \"all subnet all\")",
index a21516b6d0004fa7e5b91dd958327948291a7e6b..191f28ec149deee96f0ea1f2cc608b72bbdc4e95 100644 (file)
@@ -50,3 +50,9 @@ ${SHLIB_ENV} ${VALGRIND} ./namadr_list /tmp/junk foo 1.2.3.4
 ${SHLIB_ENV} ${VALGRIND} ./namadr_list /tmp/junk bar 1.2.3.4
 ${SHLIB_ENV} ${VALGRIND} ./namadr_list /tmp/junk baz 1.2.3.4
 ${SHLIB_ENV} ${VALGRIND} ./namadr_list /tmp/junk fool 1.2.3.4
+echo 'foo !inline:{
+    { bar = yes}
+  } baz' >junk; mv junk /tmp
+${SHLIB_ENV} ${VALGRIND} ./namadr_list /tmp/junk foo 1.2.3.4
+${SHLIB_ENV} ${VALGRIND} ./namadr_list /tmp/junk bar 1.2.3.4
+${SHLIB_ENV} ${VALGRIND} ./namadr_list /tmp/junk baz 1.2.3.4
index b108786eb2972a59a4e4ab416a22cc50a511f136..88bceadd623722227635923e9f96963d4ebaaaf2 100644 (file)
@@ -59,3 +59,6 @@ foo/1.2.3.4: YES
 bar/1.2.3.4: YES
 baz/1.2.3.4: YES
 fool/1.2.3.4: NO
+foo/1.2.3.4: YES
+bar/1.2.3.4: NO
+baz/1.2.3.4: YES
index ee5959f34ebb0db1f52d83a2fa39fb4f69a6c066..7c1c37b1b775fb853076c627bc31741e819bdaaf 100644 (file)
@@ -294,11 +294,9 @@ static void inet_addr_list_print(INET_ADDR_LIST *list)
     }
 }
 
-char   *var_inet_interfaces;
-
 int     main(int argc, char **argv)
 {
-    INET_PROTO_INFO *proto_info;
+    const INET_PROTO_INFO *proto_info;
     INET_ADDR_LIST *list;
 
     if (argc != 3)
index e8eb25bb272b880ea98f5b0ab42f322bb912db18..9e702d74d9bbc4c316f7a6ef6bb6942ca536bfc3 100644 (file)
@@ -64,6 +64,9 @@
 /*     Google, Inc.
 /*     111 8th Avenue
 /*     New York, NY 10011, USA
+/*
+/*     Wietse Venema
+/*     porcupine.org
 /*--*/
 
 /* System library. */
@@ -141,10 +144,9 @@ SERVER_ACL *server_acl_parse(const char *extern_acl, const char *origin)
                argv_add(intern_acl, SERVER_ACL_NAME_DUNNO, (char *) 0);
                break;
            } else {
-               if (dict_handle(acl) == 0)
-                   dict_register(acl, dict_open(acl, O_RDONLY, DICT_FLAG_LOCK
-                                                | DICT_FLAG_FOLD_FIX
-                                                | DICT_FLAG_UTF8_REQUEST));
+               acl = dict_open(acl, O_RDONLY, DICT_FLAG_LOCK
+                               | DICT_FLAG_FOLD_FIX
+                               | DICT_FLAG_UTF8_REQUEST)->reg_name;
            }
        }
        argv_add(intern_acl, acl, (char *) 0);
@@ -212,8 +214,9 @@ int     server_acl_eval(const char *client_addr, SERVER_ACL * intern_acl,
                if (ret != SERVER_ACL_ACT_DUNNO)
                    return (ret);
            } else if (dict->error != 0) {
-               msg_warn("%s: %s: table lookup error -- ignoring the remainder "
-                        "of this access list", origin, acl);
+               msg_warn("%s: %s:%s: table lookup error -- ignoring the "
+                        "remainder of this access list", origin, dict->type,
+                        dict->name);
                return (SERVER_ACL_ACT_ERROR);
            }
        } else if (STREQ(acl, SERVER_ACL_NAME_DUNNO)) {
index c0af411f61944e51cb2394b78b8a45c80153c67b..3a00c33c1f21de0ccb9419280dc30b77f6300ccf 100644 (file)
@@ -843,6 +843,14 @@ int     main(int argc, char **argv)
      */
     MAIL_VERSION_STAMP_ALLOCATE;
 
+    /*
+     * Workaround for programs that make explicit dict_register() calls with
+     * a table that is already registered under a different name. This is
+     * safe only in programs that do not unregister or close a table that is
+     * registered with multiple names.
+     */
+    dict_allow_multiple_dict_register_names = 1;
+
     /*
      * XXX When invoked with the master.cf service name "proxywrite", the
      * proxymap daemon will allow update requests. To update a table that is
index 16375e2d065391cb1e06d48f42e8c96fd9c69e9d..0377886f1f72a3fc63a743295743ef89b35a4359 100644 (file)
@@ -58,6 +58,8 @@ lib_update: $(LIB_DIR)/$(LIB) $(HDRS) $(MOCK_OBJ)
          cmp -s $$i $(LIB_DIR)/$$i 2>/dev/null || cp $$i $(LIB_DIR); \
        done
 
+tests:
+
 clean:
        rm -f $(LIB_SO) *.o
 
index ed1df23edf61867275ce17c0cd3eecf9fd28d83e..b021648113db47abc359fb34b1cc06660d113eb6 100644 (file)
@@ -659,13 +659,13 @@ tests: all valid_hostname_test mac_expand_test dict_test unescape_test \
        binhash_test argv_test inet_prefix_top_test printable_test \
        valid_utf8_string_test readlline_test quote_for_json_test \
        normalize_ws_test valid_uri_scheme_test clean_ascii_cntrl_space_test \
-       test_normalize_v4mapped_addr test_ossl_digest test_dict_pipe
+       test_normalize_v4mapped_addr test_ossl_digest test_dict_pipe \
        test_dict_union
  
 dict_tests: all dict_test \
        dict_pcre_tests dict_cidr_test dict_thash_test dict_static_test \
        dict_inline_test dict_utf8_test dict_regexp_test \
-       dict_regexp_file_test dict_cidr_file_test \
+       dict_regexp_file_test dict_cidr_file_test dict_seq_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_debug_test
@@ -811,7 +811,7 @@ split_qnameval_test: split_qnameval update
 
 dict_seq_test: dict_open testdb dict_seq.in dict_seq.ref
        rm -f testdb.db testdb.dir testdb.pag
-       $(SHLIB_ENV) ${VALGRIND} ./dict_open hash:testdb create sync < dict_seq.in 2>&1 | sed 's/uid=[0-9][0-9][0-9]*/uid=USER/' > dict_seq.tmp
+       ${HTABLE_FIX} $(SHLIB_ENV) ${VALGRIND} ./dict_open internal:testdb create sync_update < dict_seq.in 2>&1 | sed 's/uid=[0-9][0-9][0-9]*/uid=USER/' > dict_seq.tmp
        diff dict_seq.ref dict_seq.tmp
        rm -f testdb.db testdb.dir testdb.pag dict_seq.tmp
 
index 5fcf8429403cbe34fe2915034953dadb1fec7f38..87dee602f4cf94957eab9ff5eefabcf920ff001d 100644 (file)
 /*     const char *type_name,
 /*     int     open_flags,
 /*     int     dict_flags)
+/*
+/*     char    *dict_make_registered_name4(
+/*     VSTRING *out,
+/*     const char *type,
+/*     const char *name,
+/*     int     open_flags,
+/*     int     dict_flags)
+/*
+/*     int     dict_allow_multiple_dict_register_names;
 /* DESCRIPTION
 /*     This module maintains a collection of name-value dictionaries.
 /*     Each dictionary has its own name and has its own methods to read
 /*     dict_flags_mask() returns the bitmask for the specified
 /*     comma/space-separated dictionary flag names.
 /*
-/*     dict_make_registered_name() formats a dictionary type:name and
-/*     (initial) flag values for use in dict_register() calls.
+/*     dict_make_registered_name*() format a dictionary type, name,
+/*     and (initial) flag values for use in dict_register() calls.
 /*     This encourages consistent sharing of dictionary instances that
 /*     have the exact same type:name and (initial) flags. The result
 /*     value is the string value of the \fIout\fR VSTRING buffer.
+/*
+/*     dict_allow_multiple_dict_register_names enables a temporary
+/*     workaround for programs that make explicit dict_register()
+/*     calls with a table that is already registered under a different
+/*     name. Setting this to non-zero allows a dictionary to be
+/*     registered under multiple names. This workaround is safe only
+/*     in programs that do not unregister or close a table that is
+/*     registered with multiple names.
 /* TRUST AND PROVENANCE
 /* .ad
 /* .fi
@@ -322,8 +339,24 @@ typedef struct {
        dict = node->dict; \
 } while (0)
 
+ /*
+  * Workaround for programs that make explicit dict_register() calls with
+  * tables that are already registered under a different name. This is safe
+  * only in programs that do not unregister or close a table that is
+  * registered with multiple names.
+  */
+int     dict_allow_multiple_dict_register_names = 0;
+
 #define STR(x) vstring_str(x)
 
+/* dict_register_close - trigger dictionary cleanup */
+
+static void dict_register_close(DICT *dict)
+{
+    /* This will eventually call dict->saved_lose(). */
+    dict_unregister(dict->reg_name);
+}
+
 /* dict_register - make association with dictionary */
 
 void    dict_register(const char *dict_name, DICT *dict_info)
@@ -331,6 +364,15 @@ void    dict_register(const char *dict_name, DICT *dict_info)
     const char *myname = "dict_register";
     DICT_NODE *node;
 
+    /*
+     * Enforce referential integrity.
+     */
+    if (dict_allow_multiple_dict_register_names == 0
+       && dict_info->reg_name && strcmp(dict_name, dict_info->reg_name) != 0)
+       msg_panic("%s: '%s:%s' is already registered under '%s' and cannot "
+                 "also be registered under '%s'", myname, dict_info->type,
+                 dict_info->name, dict_info->reg_name, dict_name);
+
     if (dict_table == 0)
        dict_table = htable_create(0);
     if ((node = dict_node(dict_name)) == 0) {
@@ -338,6 +380,9 @@ void    dict_register(const char *dict_name, DICT *dict_info)
        node->dict = dict_info;
        node->refcount = 0;
        htable_enter(dict_table, dict_name, (void *) node);
+       dict_info->reg_name = mystrdup(dict_name);
+       dict_info->saved_close = dict_info->close;
+       dict_info->close = dict_register_close;
     } else if (dict_info != node->dict)
        msg_fatal("%s: dictionary name exists: %s", myname, dict_name);
     node->refcount++;
@@ -361,7 +406,9 @@ static void dict_node_free(void *ptr)
     DICT_NODE *node = (DICT_NODE *) ptr;
     DICT   *dict = node->dict;
 
-    if (dict->close)
+    if (dict->saved_close)                     /* managed by dict_register() */
+       dict->saved_close(dict);
+    else
        dict->close(dict);
     myfree((void *) node);
 }
@@ -644,9 +691,9 @@ static const NAME_MASK dict_mask[] = {
     "fixed", DICT_FLAG_FIXED,          /* fixed key map */
     "pattern", DICT_FLAG_PATTERN,      /* keys are patterns */
     "lock", DICT_FLAG_LOCK,            /* lock before access */
-    "replace", DICT_FLAG_DUP_REPLACE,  /* if file, replace dups */
+    "dup_replace", DICT_FLAG_DUP_REPLACE,      /* if file, replace dups */
     "sync_update", DICT_FLAG_SYNC_UPDATE,      /* if file, sync updates */
-    /*"debug", DICT_FLAG_DEBUG,                /* log access */
+    /* "debug", DICT_FLAG_DEBUG,               /* log access */
     "no_regsub", DICT_FLAG_NO_REGSUB,  /* disallow regexp substitution */
     "no_proxy", DICT_FLAG_NO_PROXY,    /* disallow proxy mapping */
     "no_unauth", DICT_FLAG_NO_UNAUTH,  /* disallow unauthenticated data */
@@ -690,3 +737,14 @@ char   *dict_make_registered_name(VSTRING *out, const char *type_name,
                                type_name, open_flags,
                                dict_flags_str(dict_flags))));
 }
+
+/* dict_make_registered_name4 - format registry name for consistent sharing */
+
+char   *dict_make_registered_name4(VSTRING *out, const char *type,
+                                          const char *name,
+                                          int open_flags, int dict_flags)
+{
+    return (STR(vstring_sprintf(out, "%s:%s(%o,%s)",
+                               type, name, open_flags,
+                               dict_flags_str(dict_flags))));
+}
index 780f5c06ddd29605a4908a0bebfb914c51a0ef54..595f18ed005dae26d459da44f468188179e6ff41 100644 (file)
@@ -97,6 +97,8 @@ typedef struct DICT {
     struct DICT_UTF8_BACKUP *utf8_backup;      /* see below */
     struct VSTRING *file_buf;          /* dict_file_to_buf() */
     struct VSTRING *file_b64;          /* dict_file_to_b64() */
+    char   *reg_name;                  /* managed by dict_register() */
+    void    (*saved_close) (struct DICT *);    /* managed by dict_register() */
 } DICT;
 
 extern DICT *dict_alloc(const char *, const char *, ssize_t);
@@ -282,6 +284,15 @@ extern DICT *PRINTFLIKE(5, 6) dict_surrogate(const char *, const char *, int, in
   * type, name, open_flags, and (initial) dict_flags.
   */
 extern char *dict_make_registered_name(VSTRING *, const char *, int, int);
+extern char *dict_make_registered_name4(VSTRING *, const char *, const char *, int, int);
+
+ /*
+  * Workaround for programs that make explicit dict_register() calls with a
+  * table that is already registered under a different name. This is safe
+  * only in programs that do not unregister or close a table that is
+  * registered with multiple names.
+  */
+extern int dict_allow_multiple_dict_register_names;
 
  /*
   * This name is reserved for matchlist error handling.
index 3285a38e35758ecdcf036d83db5bb5be98616bd1..d780ecf970e50584efee066693ecc4d164d356ee 100644 (file)
@@ -141,6 +141,10 @@ DICT   *dict_alloc(const char *dict_type, const char *dict_name, ssize_t size)
 {
     DICT   *dict = (DICT *) mymalloc(size);
 
+    if (msg_verbose > 2)
+       msg_info("dict_alloc(\"%s\", \"%s\", %ld)",
+                dict_type, dict_name, (long) size);
+
     dict->type = mystrdup(dict_type);
     dict->name = mystrdup(dict_name);
     dict->flags = DICT_FLAG_FIXED;
@@ -162,6 +166,8 @@ DICT   *dict_alloc(const char *dict_type, const char *dict_name, ssize_t size)
     dict->utf8_backup = 0;
     dict->file_buf = 0;
     dict->file_b64 = 0;
+    dict->reg_name = 0;
+    dict->saved_close = 0;
     return dict;
 }
 
@@ -169,6 +175,11 @@ DICT   *dict_alloc(const char *dict_type, const char *dict_name, ssize_t size)
 
 void    dict_free(DICT *dict)
 {
+    if (msg_verbose > 2)
+       msg_info("dict_free type=\"%s\" name=\"%s\" reg_name=\"%s\")",
+                dict->type, dict->name, dict->reg_name ?
+                dict->reg_name : "(null)");
+
     myfree(dict->type);
     myfree(dict->name);
     if (dict->jbuf)
@@ -179,6 +190,8 @@ void    dict_free(DICT *dict)
        vstring_free(dict->file_buf);
     if (dict->file_b64)
        vstring_free(dict->file_b64);
+    if (dict->reg_name)
+       myfree(dict->reg_name);
     myfree((void *) dict);
 }
 
index 8e2af64fd8d9d3a0c926c38f4b50c75e7ee495be..8fde809e095f8bf914b67d76e3e205f286c25869 100644 (file)
@@ -128,8 +128,9 @@ static int dict_debug_sequence(DICT *dict, int function,
 
 static void dict_debug_close(DICT *dict)
 {
-    /* TODO(wietse) use the annotated name from dict_make_registered_name(). */
-    dict_unregister(dict->name);
+    DICT_DEBUG *dict_debug = (DICT_DEBUG *) dict;
+
+    dict_close(dict_debug->real_dict);
     dict_free(dict);
 }
 
@@ -143,21 +144,11 @@ DICT   *dict_debug_open(const char *name, int open_flags, int dict_flags)
        msg_info("%s: %s", myname, name);
 
     /*
-     * Reuse a previously registered table if present. This prevents a config
-     * containing both debug:foo:bar and foo:bar from creating two DICT
-     * objects for foo:bar.
-     * 
-     * TODO(wietse) use the annotated name from dict_make_registered_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);
-
-    /*
-     * Encapsulate the real dictionary.
+     * dict_open() will reuse a previously registered table if present. This
+     * prevents a config containing both debug:foo:bar and foo:bar from
+     * creating two DICT objects for foo:bar.
      */
+    DICT *real_dict = dict_open(name, open_flags, dict_flags);
     DICT_DEBUG *dict_debug = (DICT_DEBUG *) dict_alloc(DICT_TYPE_DEBUG, name,
                                                       sizeof(*dict_debug));
 
index 71e438f912d4961653608e4f37e3222e713550ce..04724960a12823167e77a370ce263bc81eb5bd30 100644 (file)
@@ -1,19 +1,19 @@
-+ ./dict_open debug: read
+>>> 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 '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
+>>> 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
+>>> 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
+>>> dict_open 'debug:internal:name' write
 owner=trusted (uid=2147483647)
 > get k
 ./dict_open: internal:name lookup: "k" = "not_found"
@@ -41,7 +41,7 @@ k: not found
 > first
 ./dict_open: internal:name sequence: found EOF
 not found
-+ ./dict_open 'debug:fail:{oh no}' read
+>>> dict_open 'debug:fail:{oh no}' read
 owner=trusted (uid=2147483647)
 > get k
 ./dict_open: fail:{oh no} lookup: "k" = "error"
old mode 100644 (file)
new mode 100755 (executable)
index a8c051d..a5c12a2
@@ -1,13 +1,23 @@
 #!/bin/sh
-set -ex
+set -e
+
+echo ">>> dict_open 'debug:' read"
 ! ${VALGRIND} ./dict_open 'debug:' read </dev/null || exit 1
+
+echo ">>> dict_open 'debug:missing_colon_and_name' read"
 ! ${VALGRIND} ./dict_open 'debug:missing_colon_and_name' read </dev/null || exit 1
+
+echo ">>> dict_open 'debug:static:{space in name}' read"
 ${VALGRIND} ./dict_open 'debug:static:{space in name}' read <<EOF
 get k
 EOF
+
+echo ">>> dict_open 'debug:debug:static:value' read"
 ${VALGRIND} ./dict_open 'debug:debug:static:value' read <<EOF
 get k
 EOF
+
+echo ">>> dict_open 'debug:internal:name' write"
 ${VALGRIND} ./dict_open 'debug:internal:name' write <<EOF
 get k
 put k=v
@@ -19,6 +29,8 @@ get k
 del k
 first
 EOF
+
+echo ">>> dict_open 'debug:fail:{oh no}' read"
 ${VALGRIND} ./dict_open 'debug:fail:{oh no}' read <<EOF
 get k
 first
index ca3c94de282fd76c176f69bb42ff1c30ff011f4f..d0bee90d77a5c01f5c3635f10b5b2d2daded4c29 100644 (file)
@@ -110,7 +110,7 @@ DICT   *dict_inline_open(const char *name, int open_flags, int dict_flags)
     /*
      * Reuse the "internal" dictionary type.
      */
-    dict = dict_open3(DICT_TYPE_HT, name, open_flags, dict_flags);
+    dict = dict_ht_open(name, open_flags, dict_flags);
     dict_type_override(dict, DICT_TYPE_INLINE);
     while ((nameval = mystrtokq(&cp, CHARS_COMMA_SP, CHARS_BRACE)) != 0) {
        if (nameval[0] == CHARS_BRACE[0])
index bc42c06fc0de1fee40d47e7ba9807060ed3d9e0e..83afcd9ea946beec9889393e1f654d385a9115a5 100644 (file)
 /*     const char *type;
 /* DESCRIPTION
 /*     This module implements a low-level interface to multiple
-/*     physical dictionary types.
+/*     dictionary types.
+/*
+/*     In addition to providing a mapping from type names to
+/*     implementations, this module deduplicates requests to open a
+/*     dictionary with the same fingerprint (type, name, and initial
+/*     flags), and manages the dictionary life cycle using reference
+/*     counts maintained with dict_(un)register().
+/*
+/*     The fingerprint, generated with dict_make_registered_name()
+/*     and available as DICT.reg_name, may be used in dict_handle()
+/*     calls.
 /*
 /*     dict_open() takes a type:name pair that specifies a dictionary type
 /*     and dictionary name, opens the dictionary, and returns a dictionary
 /*     Enable preliminary code for bulk-mode database updates.
 /*     The caller must create an exception handler with dict_jmp_alloc()
 /*     and must trap exceptions from the database client with dict_setjmp().
-/* .IP DICT_FLAG_DEBUG
-/*     Enable additional logging.
 /* .IP DICT_FLAG_UTF8_REQUEST
 /*     With util_utf8_enable != 0, require that lookup/update/delete
 /*     keys and values are valid UTF-8. Skip a lookup/update/delete
 /*
 /*     dict_type_override() changes the symbolic dictionary type.
 /*     This is used by dictionaries whose internals are based on
-/*     some other dictionary type.
+/*     some other dictionary type. dict_type_override() requires that
+/*     the dictionary is not already registered with dict_register(),
+/*     i.e., it must be the result from dict_xxx_open(), not from
+/*     dict_open(). If needed in the future, this limitation may
+/*     be lifted.
 /* DIAGNOSTICS
 /*     Fatal error: open error, unsupported dictionary type, attempt to
 /*     update non-writable dictionary.
@@ -481,19 +493,36 @@ DICT   *dict_open3(const char *dict_type, const char *dict_name,
 {
     const char *myname = "dict_open";
     const DICT_OPEN_INFO *dp;
+    VSTRING *reg_name = vstring_alloc(100);
     DICT   *dict;
 
+#define DICT_OPEN3_RETURN(d) do { \
+       DICT *_d = (d); \
+       dict_register(vstring_str(reg_name), _d); \
+       vstring_free(reg_name); \
+       return (_d); \
+    } while (0)
+
+    /*
+     * If the dictionary is already open, simply increase the reference count
+     * to update an existing life cycle.
+     */
+    dict_make_registered_name4(reg_name, dict_type, dict_name,
+                              open_flags, dict_flags);
+    if ((dict = dict_handle(vstring_str(reg_name))) != 0)
+       DICT_OPEN3_RETURN(dict);
     if (*dict_type == 0 || *dict_name == 0)
        msg_fatal("open dictionary: expecting \"type:name\" form instead of \"%s:%s\"",
                  dict_type, dict_name);
     if (NEED_DICT_OPEN_INIT())
        dict_open_init();
     if ((dp = dict_open_lookup(dict_type)) == 0)
-       return (dict_surrogate(dict_type, dict_name, open_flags, dict_flags,
-                            "unsupported dictionary type: %s", dict_type));
+       DICT_OPEN3_RETURN(dict_surrogate(dict_type, dict_name, open_flags,
+                dict_flags, "unsupported dictionary type: %s", dict_type));
     if ((dict = dp->dict_fn(dict_name, open_flags, dict_flags)) == 0)
-       return (dict_surrogate(dict_type, dict_name, open_flags, dict_flags,
-                           "cannot open %s:%s: %m", dict_type, dict_name));
+       DICT_OPEN3_RETURN(dict_surrogate(dict_type, dict_name, open_flags,
+               dict_flags, "cannot open %s:%s: %m", dict_type, dict_name));
     if (msg_verbose)
        msg_info("%s: %s:%s", myname, dict_type, dict_name);
     /* XXX The choice between wait-for-lock or no-wait is hard-coded. */
@@ -515,7 +544,8 @@ DICT   *dict_open3(const char *dict_type, const char *dict_name,
     if ((dict->flags & DICT_FLAG_UTF8_ACTIVE) == 0
        && DICT_NEED_UTF8_ACTIVATION(util_utf8_enable, dict_flags))
        dict = dict_utf8_activate(dict);
-    return (dict);
+    /* Register the result. */
+    DICT_OPEN3_RETURN(dict);
 }
 
 /* dict_open_register - register dictionary type */
@@ -601,6 +631,16 @@ DICT_MAPNAMES_EXTEND_FN dict_mapnames_extend(DICT_MAPNAMES_EXTEND_FN new_cb)
 
 void    dict_type_override(DICT *dict, const char *type)
 {
+
+    /*
+     * To lift this limitation, compute a new reg_name, and implement a move
+     * (copy+delete) operation from the old reg_name to the new one. Also
+     * handle the case that the new destination name is already in use. The
+     * above should be encapsulated in code adjacent to dict_register().
+     */
+    if (dict->reg_name)
+       msg_panic("%s: %s:%s is already registered",
+                 __func__, dict->type, dict->name);
     myfree(dict->type);
     dict->type = mystrdup(type);
 }
index 7a4b92437c6f5d269d95166c978564bcda8daa2a..d564b6f1ca540fdb2551a509181352dcabd6c650 100644 (file)
@@ -72,13 +72,13 @@ static const char *dict_pipe_lookup(DICT *dict, const char *query)
     DICT_PIPE *dict_pipe = (DICT_PIPE *) dict;
     DICT   *map;
     char  **cpp;
-    char   *dict_type_name;
+    char   *reg_name;
     const char *result = 0;
 
     vstring_strcpy(dict_pipe->qr_buf, query);
-    for (cpp = dict_pipe->map_pipe->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);
+    for (cpp = dict_pipe->map_pipe->argv; (reg_name = *cpp) != 0; cpp++) {
+       if ((map = dict_handle(reg_name)) == 0)
+           msg_panic("%s: dictionary \"%s\" not found", myname, reg_name);
        if ((result = dict_get(map, STR(dict_pipe->qr_buf))) == 0)
            DICT_ERR_VAL_RETURN(dict, map->error, result);
        vstring_strcpy(dict_pipe->qr_buf, result);
@@ -90,12 +90,17 @@ static const char *dict_pipe_lookup(DICT *dict, const char *query)
 
 static void dict_pipe_close(DICT *dict)
 {
+    static const char myname[] = "dict_pipe_close";
     DICT_PIPE *dict_pipe = (DICT_PIPE *) dict;
+    DICT   *map;
     char  **cpp;
-    char   *dict_type_name;
+    char   *reg_name;
 
-    for (cpp = dict_pipe->map_pipe->argv; (dict_type_name = *cpp) != 0; cpp++)
-       dict_unregister(dict_type_name);
+    for (cpp = dict_pipe->map_pipe->argv; (reg_name = *cpp) != 0; cpp++) {
+       if ((map = dict_handle(reg_name)) == 0)
+           msg_panic("%s: dictionary \"%s\" not found", myname, reg_name);
+       dict_close(map);
+    }
     argv_free(dict_pipe->map_pipe);
     vstring_free(dict_pipe->qr_buf);
     dict_free(dict);
@@ -115,8 +120,6 @@ DICT   *dict_pipe_open(const char *name, int open_flags, int dict_flags)
     int     match_flags = 0;
     struct DICT_OWNER aggr_owner;
     size_t  len;
-    VSTRING *reg_name_buf = vstring_alloc(100);
-    char   *reg_name;
 
     /*
      * Clarity first. Let the optimizer worry about redundant code.
@@ -126,8 +129,6 @@ DICT   *dict_pipe_open(const char *name, int open_flags, int dict_flags)
                myfree(saved_name); \
            if (argv != 0) \
                argv_free(argv); \
-           if (reg_name_buf != 0) \
-               vstring_free(reg_name_buf); \
            return (x); \
        } while (0)
 
@@ -176,12 +177,8 @@ DICT   *dict_pipe_open(const char *name, int open_flags, int dict_flags)
     for (cpp = argv->argv; (dict_type_name = *cpp) != 0; cpp++) {
        if (msg_verbose)
            msg_info("%s: %s", myname, dict_type_name);
-       reg_name = dict_make_registered_name(reg_name_buf, dict_type_name,
-                                            open_flags, dict_flags);
-       if ((dict = dict_handle(reg_name)) == 0)
-           dict = dict_open(dict_type_name, open_flags, dict_flags);
-       dict_register(reg_name, dict);
-       argv_replace_one(argv, cpp - argv->argv, reg_name);
+       dict = dict_open(dict_type_name, open_flags, dict_flags);
+       argv_replace_one(argv, cpp - argv->argv, dict->reg_name);
        DICT_OWNER_AGGREGATE_UPDATE(aggr_owner, dict->owner);
        if (cpp == argv->argv)
            match_flags = dict->flags & (DICT_FLAG_FIXED | DICT_FLAG_PATTERN);
@@ -196,8 +193,7 @@ DICT   *dict_pipe_open(const char *name, int open_flags, int dict_flags)
     dict_pipe->dict.close = dict_pipe_close;
     dict_pipe->dict.flags = dict_flags | match_flags;
     dict_pipe->dict.owner = aggr_owner;
-    dict_pipe->qr_buf = reg_name_buf;
-    reg_name_buf = 0;
+    dict_pipe->qr_buf = vstring_alloc(100);
     dict_pipe->map_pipe = argv;
     argv = 0;
     DICT_PIPE_RETURN(&dict_pipe->dict);
index ce7d744325bd5eb15459264ef0f073f682bf9897..fad42d90df3437d90b0703ad41459e073b25defa 100644 (file)
@@ -1,22 +1,18 @@
+owner=trusted (uid=USER)
 > put 5 5
-5=5
 > put 3 3
-3=3
 > put 4 4
-4=4
 > put 1 1
-1=1
 > put 2 2
-2=2
 > first
-4=4
-> next
 2=2
 > next
-5=5
-> next
 3=3
 > next
 1=1
 > next
+4=4
+> next
+5=5
+> next
 not found
index ead61b20c068a11d12f53f891d8cdafa596abc72..bd2cadd4dfdea6509854c5b30fe15cad413ee8f0 100644 (file)
@@ -86,7 +86,6 @@ void    dict_test(int argc, char **argv)
     dict_allow_surrogate = 1;
     util_utf8_enable = 1;
     dict = dict_open(dict_name, open_flags, dict_flags);
-    dict_register(dict_name, dict);
     vstream_printf("owner=%s (uid=%ld)\n",
                   dict->owner.status == DICT_OWNER_TRUSTED ? "trusted" :
                   dict->owner.status == DICT_OWNER_UNTRUSTED ? "untrusted" :
index e95a95739fcf1a432f7c88bfdd62f72c52f6dcc4..76b7e9fef6c43c5b5db7c8df166ca7ad4a7d0ff8 100644 (file)
@@ -110,7 +110,7 @@ DICT   *dict_thash_open(const char *path, int open_flags, int dict_flags)
        /*
         * Reuse the "internal" dictionary type.
         */
-       dict = dict_open3(DICT_TYPE_HT, path, open_flags, dict_flags);
+       dict = dict_ht_open(path, open_flags, dict_flags);
        dict_type_override(dict, DICT_TYPE_THASH);
 
        /*
index bdb367aa35555d8bb8636d2bf4446060a652b900..043abc0718cc566f1c3e36917a2c1c0b859eff07 100644 (file)
@@ -76,16 +76,16 @@ static const char *dict_union_lookup(DICT *dict, const char *query)
     DICT_UNION *dict_union = (DICT_UNION *) dict;
     DICT   *map;
     char  **cpp;
-    char   *dict_type_name;
+    char   *reg_name;
     const char *result = 0;
 
     /*
      * After Roel van Meer, postfix-users mailing list, Sept 2014.
      */
     VSTRING_RESET(dict_union->re_buf);
-    for (cpp = dict_union->map_union->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);
+    for (cpp = dict_union->map_union->argv; (reg_name = *cpp) != 0; cpp++) {
+       if ((map = dict_handle(reg_name)) == 0)
+           msg_panic("%s: dictionary \"%s\" not found", myname, reg_name);
        if ((result = dict_get(map, query)) != 0) {
            if (VSTRING_LEN(dict_union->re_buf) > 0)
                VSTRING_ADDCH(dict_union->re_buf, ',');
@@ -103,12 +103,17 @@ static const char *dict_union_lookup(DICT *dict, const char *query)
 
 static void dict_union_close(DICT *dict)
 {
+    static const char myname[] = "dict_union_close";
     DICT_UNION *dict_union = (DICT_UNION *) dict;
+    DICT   *map;
     char  **cpp;
-    char   *dict_type_name;
+    char   *reg_name;
 
-    for (cpp = dict_union->map_union->argv; (dict_type_name = *cpp) != 0; cpp++)
-       dict_unregister(dict_type_name);
+    for (cpp = dict_union->map_union->argv; (reg_name = *cpp) != 0; cpp++) {
+       if ((map = dict_handle(reg_name)) == 0)
+           msg_panic("%s: dictionary \"%s\" not found", myname, reg_name);
+       dict_close(map);
+    }
     argv_free(dict_union->map_union);
     vstring_free(dict_union->re_buf);
     dict_free(dict);
@@ -128,8 +133,6 @@ DICT   *dict_union_open(const char *name, int open_flags, int dict_flags)
     int     match_flags = 0;
     struct DICT_OWNER aggr_owner;
     size_t  len;
-    VSTRING *reg_name_buf = vstring_alloc(100);
-    char   *reg_name;
 
     /*
      * Clarity first. Let the optimizer worry about redundant code.
@@ -139,8 +142,6 @@ DICT   *dict_union_open(const char *name, int open_flags, int dict_flags)
                  myfree(saved_name); \
              if (argv != 0) \
                  argv_free(argv); \
-             if (reg_name_buf != 0) \
-                 vstring_free(reg_name_buf); \
              return (x); \
          } while (0)
 
@@ -189,12 +190,8 @@ DICT   *dict_union_open(const char *name, int open_flags, int dict_flags)
     for (cpp = argv->argv; (dict_type_name = *cpp) != 0; cpp++) {
        if (msg_verbose)
            msg_info("%s: %s", myname, dict_type_name);
-       reg_name = dict_make_registered_name(reg_name_buf, dict_type_name,
-                                            open_flags, dict_flags);
-       if ((dict = dict_handle(reg_name)) == 0)
-           dict = dict_open(dict_type_name, open_flags, dict_flags);
-       dict_register(reg_name, dict);
-       argv_replace_one(argv, cpp - argv->argv, reg_name);
+       dict = dict_open(dict_type_name, open_flags, dict_flags);
+       argv_replace_one(argv, cpp - argv->argv, dict->reg_name);
        DICT_OWNER_AGGREGATE_UPDATE(aggr_owner, dict->owner);
        if (cpp == argv->argv)
            match_flags = dict->flags & (DICT_FLAG_FIXED | DICT_FLAG_PATTERN);
@@ -209,8 +206,7 @@ DICT   *dict_union_open(const char *name, int open_flags, int dict_flags)
     dict_union->dict.close = dict_union_close;
     dict_union->dict.flags = dict_flags | match_flags;
     dict_union->dict.owner = aggr_owner;
-    dict_union->re_buf = reg_name_buf;
-    reg_name_buf = 0;
+    dict_union->re_buf = vstring_alloc(100);
     dict_union->map_union = argv;
     argv = 0;
     DICT_UNION_RETURN(&dict_union->dict);
index 7b825f9e4fd82b7d872fae5df48a8780a4607277..225f7c78720c425a2df8b860fcb89fe7b2c781e2 100644 (file)
@@ -1,6 +1,6 @@
 owner=trusted (uid=2147483647)
 > flags
-dict flags fixed|lock|replace|utf8_request|utf8_active
+dict flags fixed|lock|dup_replace|utf8_request|utf8_active
 > verbose
 > get foo
 foo: not found
index fa533ba6587a847d0afcf21995e3dd6f702c2f01..8ad97193a8b98506b360d68067367cc2d72f9d06 100644 (file)
@@ -124,7 +124,6 @@ static ARGV *match_list_parse(MATCH_LIST *match_list, ARGV *pat_list,
     char   *bp = string;
     char   *start;
     char   *item;
-    char   *map_type_name_flags;
     int     match;
 
     /*
@@ -170,12 +169,8 @@ static ARGV *match_list_parse(MATCH_LIST *match_list, ARGV *pat_list,
                    msg_fatal("%s: read file %s: %m", myname, item);
            }
        } else if (MATCH_DICTIONARY(item)) {    /* type:table */
-           vstring_sprintf(buf, "%s%s(%o,%s)", match ? "" : "!",
-                           item, OPEN_FLAGS, dict_flags_str(DICT_FLAGS));
-           map_type_name_flags = STR(buf) + (match == 0);
-           if (dict_handle(map_type_name_flags) == 0)
-               dict_register(map_type_name_flags,
-                             dict_open(item, OPEN_FLAGS, DICT_FLAGS));
+           vstring_sprintf(buf, "%s%s", match ? "" : "!",
+                        dict_open(item, OPEN_FLAGS, DICT_FLAGS)->reg_name);
            argv_add(pat_list, STR(buf), (char *) 0);
        } else {                                /* other pattern */
            casefold(match_list->fold_buf, match ?