]> git.ipfire.org Git - thirdparty/postfix.git/commitdiff
postfix-3.11-20251124
authorWietse Z Venema <wietse@porcupine.org>
Mon, 24 Nov 2025 05:00:00 +0000 (00:00 -0500)
committerViktor Dukhovni <ietf-dane@dukhovni.org>
Wed, 26 Nov 2025 01:56:17 +0000 (12:56 +1100)
postfix/HISTORY
postfix/html/postmulti.1.html
postfix/man/man1/postmulti.1
postfix/proto/stop.double-history
postfix/proto/stop.spell-cc
postfix/proto/stop.spell-history
postfix/src/global/mail_version.h
postfix/src/postmulti/Makefile.in
postfix/src/postmulti/fake_strcmp.c [new file with mode: 0644]
postfix/src/postmulti/postmulti.c

index 21f71bbfd6ecf82e8dbd40bb94f0a38fb43cb140..437e2877003f834840b019b983a1c3d4c1443f98 100644 (file)
@@ -29054,7 +29054,7 @@ Apologies for any names omitted.
        after an I/O error on an existing connection. Reported by
        Oleksandr Kozmenko. File: xsasl/xsasl_dovecot_server.c.
 
-20240315
+20250315
 
        Code health: two typos canceled each other's effect. Fix
        by Michael Tokarev. No change in compiler output. File:
@@ -29807,7 +29807,7 @@ Apologies for any names omitted.
        for TLS feature status logging. Files: smtp/smtp_connect.c,
        proto/postconf.proto.
 
-20240924
+20250924
  
        TLSRPT Workaround: when policies[*].policy.policy-type is
        "no-policy-found", report the TLSRPT policy domain name as
@@ -29947,7 +29947,7 @@ Apologies for any names omitted.
        -L ssl-debug" will decode TLS handshake messages. Viktor
        Dukhovni. File: posttls-finger/posttls-finger.c
 
-20241031
+20251031
 
        Bugfix (defect introduced: Postfix 3.10, date 20250117):
        support for "TLS-Required: no" broke client-side TLS wrappermode
@@ -30070,6 +30070,15 @@ Apologies for any names omitted.
 20251122
 
        Feature: basic JSON output support with "postalias -j" and
-       "postmap -j". See respective manpages for details. Files:
+       "postmap -j". See respective manpages for details.  Added
+       basic tests to verify the output format. Files:
        postmap/Makefile.in, postmap/postmap.c, postalias/Makefile.in,
        postalias/postalias.c.
+
+20251124
+
+       Feature: basic JSON output support with "postmulti -jl".
+       The schema is documented in the updated postmulti(1) manpage.
+       Added basic tests to verify the output formats for original
+       and JSON output. Files: postmulti/postmulti.c,
+       postmulti/fake_strcmp.c, postmulti/Makefile.in.
index 6ae21956813b55402c818891c8bb1efb4bd2bf8b..47eec47cbfe564b046e7c82893c16b3ce38c852f 100644 (file)
@@ -17,7 +17,7 @@ POSTMULTI(1)                                                      POSTMULTI(1)
 
    <b>Iterator mode:</b>
 
-       <b>postmulti -l</b> [<b>-aRv</b>] [<b>-g</b> <i>group</i>] [<b>-i</b> <i>name</i>]
+       <b>postmulti -l</b> [<b>-ajRv</b>] [<b>-g</b> <i>group</i>] [<b>-i</b> <i>name</i>]
 
        <b>postmulti -p</b> [<b>-av</b>] [<b>-g</b> <i>group</i>] [<b>-i</b> <i>name</i>] <i>postfix-command...</i>
 
@@ -97,6 +97,8 @@ POSTMULTI(1)                                                      POSTMULTI(1)
               This option cannot be used with <b>-p</b>.
 
 <b>List mode</b>
+       <b>-j</b>     Produce JSON output. See JSON OBJECT FORMAT below.
+
        <b>-l</b>     List Postfix instances with their instance name, instance  group
               name, enable/disable status and configuration directory.
 
@@ -301,6 +303,31 @@ POSTMULTI(1)                                                      POSTMULTI(1)
        <b>-v</b>     Enable  verbose  logging  for  debugging  purposes.  Multiple <b>-v</b>
               options make the software increasingly verbose.
 
+<b><a name="json_object_format">JSON OBJECT FORMAT</a></b>
+       The output consists of a sequence of lines. Each line contains one JSON
+       object  that  represents settings in a corresponding instance's <a href="postconf.5.html">main.cf</a>
+       file.
+
+       Object members have string values unless indicated otherwise.  Programs
+       should ignore members that are not listed here, as members may be added
+       over time.
+
+       <b>name</b>   The value of the corresponding <b><a href="postconf.5.html#multi_instance_name">multi_instance_name</a></b> parameter, or
+              "<b>-</b>" if no name is specified.
+
+       <b>group</b>  The  value  of the corresponding <b><a href="postconf.5.html#multi_instance_group">multi_instance_group</a></b> parameter,
+              or "<b>-</b>" if no group is specified.
+
+       <b>enabled</b>
+              Either "<b>y</b>"  or  "<b>n</b>",  depending  on  whether  the  corresponding
+              <b><a href="postconf.5.html#multi_instance_enable">multi_instance_enable</a></b> parameter value is "<b>yes</b>" or "<b>no</b>".
+
+              Note:   this   reports   "<b>y</b>"   for   a  primary  instance,  when
+              multi-instance support is not enabled.
+
+       <b><a href="postconf.5.html#config_directory">config_directory</a></b>
+              The value of the corresponding <b><a href="postconf.5.html#config_directory">config_directory</a></b> parameter.
+
 <b><a name="environment">ENVIRONMENT</a></b>
        The <a href="postmulti.1.html"><b>postmulti</b>(1)</a> command exports the  following  environment  variables
        before executing the requested <i>command</i> for a given instance:
index 96c597e9437d4a6800878485611d0c17362aa099..45273c80414f35a559227c6f5106ce46e97a38b7 100644 (file)
@@ -17,7 +17,7 @@ Postfix multi\-instance manager
 .ti -4
 \fBIterator mode:\fR
 
-\fBpostmulti\fR \fB\-l\fR [\fB\-aRv\fR] [\fB\-g \fIgroup\fR]
+\fBpostmulti\fR \fB\-l\fR [\fB\-ajRv\fR] [\fB\-g \fIgroup\fR]
 [\fB\-i \fIname\fR]
 
 \fBpostmulti\fR \fB\-p\fR [\fB\-av\fR] [\fB\-g \fIgroup\fR]
@@ -119,6 +119,8 @@ This option cannot be used with \fB\-p\fR.
 .nf
 .ad
 .fi
+.IP \fB\-j\fR
+Produce JSON output. See JSON OBJECT FORMAT below.
 .IP \fB\-l\fR
 List Postfix instances with their instance name, instance
 group name, enable/disable status and configuration directory.
@@ -355,6 +357,33 @@ started etc. with "postfix \-c config\-directory start".
 Enable verbose logging for debugging purposes. Multiple
 \fB\-v\fR options make the software increasingly verbose.
 .RE
+.SH "JSON OBJECT FORMAT"
+.na
+.nf
+.ad
+.fi
+The output consists of a sequence of lines. Each line contains
+one JSON object that represents settings in a corresponding
+instance's main.cf file.
+
+Object members have string values unless indicated otherwise.
+Programs should ignore members that are not listed here, as
+members may be added over time.
+.IP \fBname\fR
+The value of the corresponding \fBmulti_instance_name\fR
+parameter, or "\fB\-\fR" if no name is specified.
+.IP \fBgroup\fR
+The value of the corresponding \fBmulti_instance_group\fR
+parameter, or "\fB\-\fR" if no group is specified.
+.IP \fBenabled\fR
+Either "\fBy\fR" or "\fBn\fR", depending on whether the
+corresponding \fBmulti_instance_enable\fR parameter value is
+"\fByes\fR" or "\fBno\fR".
+.sp
+Note: this reports "\fBy\fR" for a primary instance, when
+multi\-instance support is not enabled.
+.IP \fBconfig_directory\fR
+The value of the corresponding \fBconfig_directory\fR parameter.
 .SH "ENVIRONMENT"
 .na
 .nf
index 3ad72a5f501d08754c2a94d7d3130f36e51ab57b..c882e8ed6365e50f6ad3c034e589234c1dc43e0f 100644 (file)
@@ -254,3 +254,4 @@ proto  proto REQUIRETLS_README html global mail_params hc
  postmap Makefile in postmap postmap c postalias Makefile in 
  postalias postalias c 
  postconf postconf hc postconf postconf_main c 
+ and JSON output Files postmulti postmulti c 
index 425dfb3f43d1624f32ce91ae059daf704e21ee8f..b4af84a6f27c490bfdfd26afd0f3891a3d56c609 100644 (file)
@@ -1889,3 +1889,4 @@ dfhHjnopqvx
 +joqvx
 fhHjoqvx
 joqvx
+ajRv
index 8713df0fb3f3ed315e58702010b85a0244aa199d..1730b5d2c349fa62ab708adfccf002291f8a57af 100644 (file)
@@ -117,3 +117,4 @@ YP
 Natalenko
 nocertmatch
 pgnd
+jl
index 772baf79fd3a45d4ff4c52983ab3de700f7b4e57..260b40159007b902e7d93578df4155f7704c82bb 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      "20251122"
+#define MAIL_RELEASE_DATE      "20251124"
 #define MAIL_VERSION_NUMBER    "3.11"
 
 #ifdef SNAPSHOT
index 61383109e134a25da4587944e437da4382598109..bb84a46d7ec99cee2b04ac07176a9ae3c578898a 100644 (file)
@@ -1,5 +1,5 @@
 SHELL  = /bin/sh
-SRCS   = postmulti.c 
+SRCS   = postmulti.c fake_strcmp.c
 OBJS   = postmulti.o 
 HDRS   = 
 TESTSRC        = 
@@ -11,6 +11,7 @@ TESTPROG=
 PROG   = postmulti
 LIBS   = ../../lib/lib$(LIB_PREFIX)global$(LIB_SUFFIX) \
        ../../lib/lib$(LIB_PREFIX)util$(LIB_SUFFIX)
+LIB_SO = fake_strcmp.so
 
 .c.o:; $(CC) $(CFLAGS) -c $*.c
 
@@ -24,7 +25,44 @@ Makefile: Makefile.in
 
 test:  $(TESTPROG)
 
-tests:
+# Force strcmp(var_config_dir, DEF_CONFIG_DIR) to always succeeed. Outside
+# tests, postmulti MUST abort when their values differ.
+fake_strcmp.so: fake_strcmp.c
+       $(CC) $(CFLAGS) -fPIC -shared -o $@ fake_strcmp.c $(LIBS)
+
+tests: single_test json_tests
+
+# Single instance, original output format.
+single_test: $(PROG) fake_strcmp.so
+       @echo; echo RUN single_test
+       rm -f main.cf single_test.tmp
+       echo config_directory = . >>main.cf
+       echo command_directory = ../../bin >> main.cf
+       echo daemon_directory = ../../libexec >> main.cf
+       echo meta_directory = . >> main.cf
+       echo shlib_directory = ../../lib >> main.cf
+       printf "%-15s %-15s %-9s %s\n" - - y . >single_test.tmp
+       $(SHLIB_ENV) ${VALGRIND} LD_PRELOAD=./fake_strcmp.so MAIL_CONFIG=. \
+           ./$(PROG) -l | diff single_test.tmp -
+       rm -f main.cf single_test.tmp
+       @echo PASS single_test
+
+json_tests: json_single_test
+
+# Single instance, JSON output format.
+json_single_test: $(PROG) fake_strcmp.so
+       @echo; echo RUN json_single_test
+       rm -f main.cf json_single_test.tmp
+       echo config_directory = . >>main.cf
+       echo command_directory = ../../bin >> main.cf
+       echo daemon_directory = ../../libexec >> main.cf
+       echo meta_directory = . >> main.cf
+       echo shlib_directory = ../../lib >> main.cf
+       echo "{{\"name\": \"-\"},{\"group\": \"-\"},{\"enabled\": \"y\"},{\"config_directory\": \".\"}}" >> json_single_test.tmp
+       $(SHLIB_ENV) ${VALGRIND} LD_PRELOAD=./fake_strcmp.so MAIL_CONFIG=. \
+           ./$(PROG) -lj | diff json_single_test.tmp -
+       rm -f main.cf json_single_test.tmp
+       @echo PASS json_single_test
 
 root_tests:
 
@@ -34,7 +72,7 @@ update: ../../bin/$(PROG)
        cp $(PROG) ../../bin
 
 clean:
-       rm -f *.o *core $(PROG) $(TESTPROG) junk
+       rm -f *.o *core $(PROG) $(TESTPROG) $(LIB_SO) junk
 
 tidy:  clean
 
diff --git a/postfix/src/postmulti/fake_strcmp.c b/postfix/src/postmulti/fake_strcmp.c
new file mode 100644 (file)
index 0000000..0f5da47
--- /dev/null
@@ -0,0 +1,42 @@
+/* System library. */
+#include <sys_defs.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <dlfcn.h>
+
+/* Utility library. */
+#include <msg.h>
+
+/* Global library. */
+#include <mail_params.h>
+
+static int (*real_strcmp) (const char *, const char *);
+
+/* find_real_func - libc lookup */
+
+static void *find_real_func(const char *name)
+{
+    void   *sym;
+
+    /*
+     * XXX Casting a data pointer into a function pointer is non-portable.
+     * Unfortunately, the dlfunc() function is available on FreeBSD but not
+     * on Linux or Solaris. This is a cosmetic issue except on systems with
+     * non-flat memory models.
+     */
+    if ((sym = dlsym(RTLD_NEXT, name)) == 0) {
+       fprintf(stderr, "preload error for %s: %s\n", name, dlerror());
+       exit(1);
+    }
+    return (sym);
+}
+
+int     strcmp(const char *s1, const char *s2)
+{
+    if (real_strcmp == 0)
+       real_strcmp = find_real_func("strcmp");
+    if (real_strcmp(DEF_CONFIG_DIR, s2) == 0)
+       return (0);
+    return real_strcmp(s1, s2);
+}
index ccb85afa869f8bbc7d7eee3874edab839b70b008..dddde25718025d4945704a85042d450e1f73874b 100644 (file)
@@ -13,7 +13,7 @@
 /* .ti -4
 /*     \fBIterator mode:\fR
 /*
-/*     \fBpostmulti\fR \fB-l\fR [\fB-aRv\fR] [\fB-g \fIgroup\fR]
+/*     \fBpostmulti\fR \fB-l\fR [\fB-ajRv\fR] [\fB-g \fIgroup\fR]
 /*     [\fB-i \fIname\fR]
 /*
 /*     \fBpostmulti\fR \fB-p\fR [\fB-av\fR] [\fB-g \fIgroup\fR]
 /* List mode
 /* .ad
 /* .fi
+/* .IP \fB-j\fR
+/*     Produce JSON output. See JSON OBJECT FORMAT below.
 /* .IP \fB-l\fR
 /*     List Postfix instances with their instance name, instance
 /*     group name, enable/disable status and configuration directory.
 /*     Enable verbose logging for debugging purposes. Multiple
 /*     \fB-v\fR options make the software increasingly verbose.
 /* .RE
+/* JSON OBJECT FORMAT
+/* .ad
+/* .fi
+/*     The output consists of a sequence of lines. Each line contains
+/*     one JSON object that represents settings in a corresponding
+/*     instance's main.cf file.
+/*
+/*     Object members have string values unless indicated otherwise.
+/*     Programs should ignore members that are not listed here, as
+/*     members may be added over time.
+/* .IP \fBname\fR
+/*     The value of the corresponding \fBmulti_instance_name\fR
+/*     parameter, or "\fB-\fR" if no name is specified.
+/* .IP \fBgroup\fR
+/*     The value of the corresponding \fBmulti_instance_group\fR
+/*     parameter, or "\fB-\fR" if no group is specified.
+/* .IP \fBenabled\fR
+/*     Either "\fBy\fR" or "\fBn\fR", depending on whether the
+/*     corresponding \fBmulti_instance_enable\fR parameter value is
+/*     "\fByes\fR" or "\fBno\fR".
+/* .sp
+/*     Note: this reports "\fBy\fR" for a primary instance, when
+/*     multi-instance support is not enabled.
+/* .IP \fBconfig_directory\fR
+/*     The value of the corresponding \fBconfig_directory\fR parameter.
 /* ENVIRONMENT
 /* .ad
 /* .fi
@@ -657,6 +684,12 @@ static int match_instance_selection(INSTANCE *, INST_SELECTION *);
 #define INSTANCE_NAME(i) ((i)->name ? (i)->name : (i)->config_dir)
 #define STR(buf)       vstring_str(buf)
 
+ /*
+  * JSON support.
+  */
+static int json_output;
+static VSTRING *json_buf;
+
 /* register_claim - register claim or bust */
 
 static void register_claim(const char *instance_path, const char *param_name,
@@ -1561,12 +1594,28 @@ static void list_instances(int iter_flags, INST_SELECTION *selection)
      */
     FOREACH_ITERATOR_INSTANCE(iter_flags, entry) {
        ip = RING_TO_INSTANCE(entry);
-       if (match_instance_selection(ip, selection))
-           vstream_printf("%-15s %-15s %-9s %s\n",
-                          ip->name ? ip->name : "-",
-                          ip->gname ? ip->gname : "-",
-                          ip->enabled ? "y" : "n",
-                          ip->config_dir);
+       if (match_instance_selection(ip, selection)) {
+           if (json_output == 0) {
+               vstream_printf("%-15s %-15s %-9s %s\n",
+                              ip->name ? ip->name : "-",
+                              ip->gname ? ip->gname : "-",
+                              ip->enabled ? "y" : "n",
+                              ip->config_dir);
+           } else {
+               vstream_printf("{{\"name\": \"%s\"},",
+                              quote_for_json(json_buf,
+                                           ip->name ? ip->name : "-", -1));
+               vstream_printf("{\"group\": \"%s\"},",
+                              quote_for_json(json_buf,
+                                          ip->gname ? ip->gname : "-", 1));
+               vstream_printf("{\"enabled\": \"%s\"},",
+                              quote_for_json(json_buf,
+                                             ip->enabled ? "y" : "n", 1));
+               vstream_printf("{\"config_directory\": \"%s\"}}\n",
+                              quote_for_json(json_buf,
+                                             ip->config_dir, -1));
+           }
+       }
     }
     if (vstream_fflush(VSTREAM_OUT))
        msg_fatal("error writing output: %m");
@@ -1734,7 +1783,7 @@ int     main(int argc, char **argv)
      * Parse switches. Move the above mail_conf_read() block after this loop,
      * if any command-line option can affect parameter processing.
      */
-    while ((ch = GETOPT(argc, argv, "ae:g:i:G:I:lpRvx")) > 0) {
+    while ((ch = GETOPT(argc, argv, "ae:g:i:jG:I:lpRvx")) > 0) {
        switch (ch) {
        default:
            usage(argv[0]);
@@ -1771,6 +1820,12 @@ int     main(int argc, char **argv)
            selection.type = INST_SEL_NAME;
            selection.name = optarg;
            break;
+       case 'j':
+           if (json_output == 0) {
+               json_output = 1;
+               json_buf = vstring_alloc(100);
+           }
+           break;
        case 'G':
            if (assignment.gname != 0)
                msg_fatal("Specify at most one '-G' option");
@@ -1845,6 +1900,8 @@ int     main(int argc, char **argv)
            msg_fatal("Parameter overrides not valid with '-e %s'",
                      EDIT_CMD_STR(cmd_mode));
     }
+    if (json_output && (cmd_mode & ITER_CMD_LIST) == 0)
+       msg_fatal("JSON output available only with '-l'");
 
     /*
      * Sanity checks.