Miscellaneous documentation fixes. Files: proto/postconf.proto,
mantools/postconf2html.
+
+20251120
+
+ Bugfix (defect introduced: Postfix 2.9, date: 20120307):
+ segfault with duplicate parameter name in "postconf -X" or
+ "postconf -#'. File: postconf/postconf_edit.c.
+
+20251121
+
+ Feature: postconf '-j' option for JSON output. Files:
+ postconf/postconf.[hc], postconf/postconf_main.c,
+ postconf/postconf_json.c, postconf/test8[0-7].ref,
+ postconf/Makefile.in, util/quote_for_json.c,
+ util/stringops.h.
+
+20251122
+
+ Feature: basic JSON output support with "postalias -j" and
+ "postmap -j". See respective manpages for details. Files:
+ postmap/Makefile.in, postmap/postmap.c, postalias/Makefile.in,
+ postalias/postalias.c.
postalias - Postfix alias database maintenance
<b><a name="synopsis">SYNOPSIS</a></b>
- <b>postalias</b> [<b>-Nfinoprsuvw</b>] [<b>-c</b> <i>config</i><b>_</b><i>dir</i>] [<b>-d</b> <i>key</i>] [<b>-q</b> <i>key</i>]
+ <b>postalias</b> [<b>-Nfijnoprsuvw</b>] [<b>-c</b> <i>config</i><b>_</b><i>dir</i>] [<b>-d</b> <i>key</i>] [<b>-q</b> <i>key</i>]
[<i>file</i><b>_</b><i>type</i>:]<i>file</i><b>_</b><i>name</i> ...
<b><a name="description">DESCRIPTION</a></b>
truncate an existing database. By default, <a href="postalias.1.html"><b>postalias</b>(1)</a> creates
a new database from the entries in <i>file</i><b>_</b><i>name</i>.
- <b>-N</b> Include the terminating null character that terminates lookup
- keys and values. By default, <a href="postalias.1.html"><b>postalias</b>(1)</a> does whatever is the
+ <b>-j</b> JSON output. Format the output from <b>-q</b> and <b>-s</b> as one <b>{"</b><i>key</i><b>":</b>
+ <b>"</b><i>value</i><b>"}</b> object per line.
+
+ This feature is available in Postfix version 3.11 and later.
+
+ <b>-N</b> Include the terminating null character that terminates lookup
+ keys and values. By default, <a href="postalias.1.html"><b>postalias</b>(1)</a> does whatever is the
default for the host operating system.
- <b>-n</b> Don't include the terminating null character that terminates
- lookup keys and values. By default, <a href="postalias.1.html"><b>postalias</b>(1)</a> does whatever
+ <b>-n</b> Don't include the terminating null character that terminates
+ lookup keys and values. By default, <a href="postalias.1.html"><b>postalias</b>(1)</a> does whatever
is the default for the host operating system.
- <b>-o</b> Do not release root privileges when processing a non-root input
+ <b>-o</b> Do not release root privileges when processing a non-root input
file. By default, <a href="postalias.1.html"><b>postalias</b>(1)</a> drops root privileges and runs as
the source file owner instead.
- <b>-p</b> Do not inherit the file access permissions from the input file
- when creating a new file. Instead, create a new file with
+ <b>-p</b> Do not inherit the file access permissions from the input file
+ when creating a new file. Instead, create a new file with
default access permissions (mode 0644).
- <b>-q</b> <i>key</i> Search the specified maps for <i>key</i> and write the first value
- found to the standard output stream. The exit status is zero
+ <b>-q</b> <i>key</i> Search the specified maps for <i>key</i> and write the first value
+ found to the standard output stream. The exit status is zero
when the requested information was found.
- Note: this performs a single query with the key as specified,
- and does not make iterative queries with substrings of the key
+ Note: this performs a single query with the key as specified,
+ and does not make iterative queries with substrings of the key
as described in the <a href="aliases.5.html">aliases(5)</a> manual page.
- If a key value of <b>-</b> is specified, the program reads key values
+ If a key value of <b>-</b> is specified, the program reads key values
from the standard input stream and writes one line of <i>key: value</i>
output for each key that was found. The exit status is zero when
at least one of the requested keys was found.
- <b>-r</b> When updating a table, do not complain about attempts to update
+ <b>-r</b> When updating a table, do not complain about attempts to update
existing entries, and make those updates anyway.
<b>-s</b> Retrieve all database elements, and write one line of <i>key: value</i>
- output for each element. The elements are printed in database
- order, which is not necessarily the same as the original input
- order. This feature is available in Postfix version 2.2 and
+ output for each element. The elements are printed in database
+ order, which is not necessarily the same as the original input
+ order. This feature is available in Postfix version 2.2 and
later, and is not available for all database types.
- <b>-u</b> Disable UTF-8 support. UTF-8 support is enabled by default when
- "<a href="postconf.5.html#smtputf8_enable">smtputf8_enable</a> = yes". It requires that keys and values are
+ <b>-u</b> Disable UTF-8 support. UTF-8 support is enabled by default when
+ "<a href="postconf.5.html#smtputf8_enable">smtputf8_enable</a> = yes". It requires that keys and values are
valid UTF-8 strings.
- <b>-v</b> Enable verbose logging for debugging purposes. Multiple <b>-v</b>
+ <b>-v</b> Enable verbose logging for debugging purposes. Multiple <b>-v</b>
options make the software increasingly verbose.
- <b>-w</b> When updating a table, do not complain about attempts to update
+ <b>-w</b> When updating a table, do not complain about attempts to update
existing entries, and ignore those attempts.
Arguments:
The database type. To find out what types are supported, use the
"<b>postconf -m</b>" command.
- The <a href="postalias.1.html"><b>postalias</b>(1)</a> command can query any supported file type, but
+ The <a href="postalias.1.html"><b>postalias</b>(1)</a> command can query any supported file type, but
it can create only the following file types:
- <b>btree</b> The output is a btree file, named <i>file</i><b>_</b><i>name</i><b>.db</b>. This is
+ <b>btree</b> The output is a btree file, named <i>file</i><b>_</b><i>name</i><b>.db</b>. This is
available on systems with support for <b>db</b> databases.
- <b>cdb</b> The output is one file named <i>file</i><b>_</b><i>name</i><b>.cdb</b>. This is
+ <b>cdb</b> The output is one file named <i>file</i><b>_</b><i>name</i><b>.cdb</b>. This is
available on systems with support for <b>cdb</b> databases.
<b>dbm</b> The output consists of two files, named <i>file</i><b>_</b><i>name</i><b>.pag</b> and
<i>file</i><b>_</b><i>name</i><b>.dir</b>. This is available on systems with support
for <b>dbm</b> databases.
- <b>fail</b> A table that reliably fails all requests. The lookup ta-
- ble name is used for logging only. This table exists to
+ <b>fail</b> A table that reliably fails all requests. The lookup ta-
+ ble name is used for logging only. This table exists to
simplify Postfix error tests.
<b>hash</b> The output is a hashed file, named <i>file</i><b>_</b><i>name</i><b>.db</b>. This is
available on systems with support for <b>db</b> databases.
- <b>lmdb</b> The output is a btree-based file, named <i>file</i><b>_</b><i>name</i><b>.lmdb</b>.
- <b>lmdb</b> supports concurrent writes and reads from different
+ <b>lmdb</b> The output is a btree-based file, named <i>file</i><b>_</b><i>name</i><b>.lmdb</b>.
+ <b>lmdb</b> supports concurrent writes and reads from different
processes, unlike other supported file-based tables.
- This is available on systems with support for <b>lmdb</b> data-
+ This is available on systems with support for <b>lmdb</b> data-
bases.
<b>sdbm</b> The output consists of two files, named <i>file</i><b>_</b><i>name</i><b>.pag</b> and
<i>file</i><b>_</b><i>name</i><b>.dir</b>. This is available on systems with support
for <b>sdbm</b> databases.
- When no <i>file</i><b>_</b><i>type</i> is specified, the software uses the database
- type specified via the <b><a href="postconf.5.html#default_database_type">default_database_type</a></b> configuration
- parameter. The default value for this parameter depends on the
+ When no <i>file</i><b>_</b><i>type</i> is specified, the software uses the database
+ type specified via the <b><a href="postconf.5.html#default_database_type">default_database_type</a></b> configuration
+ parameter. The default value for this parameter depends on the
host environment.
<i>file</i><b>_</b><i>name</i>
base.
<b><a name="diagnostics">DIAGNOSTICS</a></b>
- Problems are logged to the standard error stream and to <b>syslogd</b>(8) or
- <a href="postlogd.8.html"><b>postlogd</b>(8)</a>. No output means that no problems were detected. Duplicate
+ Problems are logged to the standard error stream and to <b>syslogd</b>(8) or
+ <a href="postlogd.8.html"><b>postlogd</b>(8)</a>. No output means that no problems were detected. Duplicate
entries are skipped and are flagged with a warning.
- <a href="postalias.1.html"><b>postalias</b>(1)</a> terminates with zero exit status in case of success
+ <a href="postalias.1.html"><b>postalias</b>(1)</a> terminates with zero exit status in case of success
(including successful "<b>postalias -q</b>" lookup) and terminates with
non-zero exit status in case of failure.
Enable verbose logging for debugging purposes.
<b><a name="configuration_parameters">CONFIGURATION PARAMETERS</a></b>
- The following <a href="postconf.5.html"><b>main.cf</b></a> parameters are especially relevant to this pro-
+ The following <a href="postconf.5.html"><b>main.cf</b></a> parameters are especially relevant to this pro-
gram.
- The text below provides only a parameter summary. See <a href="postconf.5.html"><b>postconf</b>(5)</a> for
+ The text below provides only a parameter summary. See <a href="postconf.5.html"><b>postconf</b>(5)</a> for
more details including examples.
<b><a href="postconf.5.html#alias_database">alias_database</a> (see 'postconf -d' output)</b>
- The alias databases for <a href="local.8.html"><b>local</b>(8)</a> delivery that are updated with
+ The alias databases for <a href="local.8.html"><b>local</b>(8)</a> delivery that are updated with
"<b>newaliases</b>" or with "<b>sendmail -bi</b>".
<b><a href="postconf.5.html#config_directory">config_directory</a> (see 'postconf -d' output)</b>
- The default location of the Postfix <a href="postconf.5.html">main.cf</a> and <a href="master.5.html">master.cf</a> con-
+ The default location of the Postfix <a href="postconf.5.html">main.cf</a> and <a href="master.5.html">master.cf</a> con-
figuration files.
<b><a href="postconf.5.html#berkeley_db_create_buffer_size">berkeley_db_create_buffer_size</a> (16777216)</b>
- The per-table I/O buffer size for programs that create Berkeley
+ The per-table I/O buffer size for programs that create Berkeley
DB hash or btree tables.
<b><a href="postconf.5.html#berkeley_db_read_buffer_size">berkeley_db_read_buffer_size</a> (131072)</b>
and <a href="postmap.1.html"><b>postmap</b>(1)</a> commands.
<b><a href="postconf.5.html#import_environment">import_environment</a> (see 'postconf -d' output)</b>
- The list of environment variables that a privileged Postfix
- process will import from a non-Postfix parent process, or
+ The list of environment variables that a privileged Postfix
+ process will import from a non-Postfix parent process, or
name=value environment overrides.
<b><a href="postconf.5.html#smtputf8_enable">smtputf8_enable</a> (yes)</b>
- Enable preliminary SMTPUTF8 support for the protocols described
+ Enable preliminary SMTPUTF8 support for the protocols described
in <a href="https://tools.ietf.org/html/rfc6531">RFC 6531</a>, <a href="https://tools.ietf.org/html/rfc6532">RFC 6532</a>, and <a href="https://tools.ietf.org/html/rfc6533">RFC 6533</a>.
<b><a href="postconf.5.html#syslog_facility">syslog_facility</a> (mail)</b>
The syslog facility of Postfix logging.
<b><a href="postconf.5.html#syslog_name">syslog_name</a> (see 'postconf -d' output)</b>
- A prefix that is prepended to the process name in syslog
+ A prefix that is prepended to the process name in syslog
records, so that, for example, "smtpd" becomes "prefix/smtpd".
Available in Postfix 2.11 and later:
<b><a name="synopsis">SYNOPSIS</a></b>
<b>Managing <a href="postconf.5.html">main.cf</a>:</b>
- <b>postconf</b> [<b>-dfhHnopqvx</b>] [<b>-c</b> <i>config</i><b>_</b><i>dir</i>] [<b>-C</b> <i>class,...</i>] [<i>parameter ...</i>]
+ <b>postconf</b> [<b>-dfhHjnopqvx</b>] [<b>-c</b> <i>config</i><b>_</b><i>dir</i>] [<b>-C</b> <i>class,...</i>] [<i>parameter ...</i>]
<b>postconf</b> [<b>-epv</b>] [<b>-c</b> <i>config</i><b>_</b><i>dir</i>] <i>parameter</i><b>=</b><i>value ...</i>
<b>Managing <a href="master.5.html">master.cf</a> service entries:</b>
- <b>postconf -M</b> [<b>-foqvx</b>] [<b>-c</b> <i>config</i><b>_</b><i>dir</i>] [<i>service</i>[<b>/</b><i>type</i>] <i>...</i>]
+ <b>postconf -M</b> [<b>-joqvx</b>] [<b>-c</b> <i>config</i><b>_</b><i>dir</i>] [<i>service</i>[<b>/</b><i>type</i>] <i>...</i>]
<b>postconf -M</b> [<b>-ev</b>] [<b>-c</b> <i>config</i><b>_</b><i>dir</i>] <i>service</i><b>/</b><i>type</i><b>=</b><i>value ...</i>
<b>Managing <a href="master.5.html">master.cf</a> service fields:</b>
- <b>postconf -F</b> [<b>-fhHoqvx</b>] [<b>-c</b> <i>config</i><b>_</b><i>dir</i>] [<i>service</i>[<b>/</b><i>type</i>[<b>/</b><i>field</i>]] <i>...</i>]
+ <b>postconf -F</b> [<b>-fhHjoqvx</b>] [<b>-c</b> <i>config</i><b>_</b><i>dir</i>] [<i>service</i>[<b>/</b><i>type</i>[<b>/</b><i>field</i>]] <i>...</i>]
<b>postconf -F</b> [<b>-ev</b>] [<b>-c</b> <i>config</i><b>_</b><i>dir</i>] <i>service</i><b>/</b><i>type</i><b>/</b><i>field</i><b>=</b><i>value ...</i>
<b>Managing <a href="master.5.html">master.cf</a> service parameters:</b>
- <b>postconf -P</b> [<b>-fhHoqvx</b>] [<b>-c</b> <i>config</i><b>_</b><i>dir</i>] [<i>service</i>[<b>/</b><i>type</i>[<b>/</b><i>parameter</i>]] <i>...</i>]
+ <b>postconf -P</b> [<b>-fhHjoqvx</b>] [<b>-c</b> <i>config</i><b>_</b><i>dir</i>] [<i>service</i>[<b>/</b><i>type</i>[<b>/</b><i>parameter</i>]]
+ <i>...</i>]
<b>postconf -P</b> [<b>-ev</b>] [<b>-c</b> <i>config</i><b>_</b><i>dir</i>] <i>service</i><b>/</b><i>type</i><b>/</b><i>parameter</i><b>=</b><i>value ...</i>
<b><a name="description">DESCRIPTION</a></b>
By default, the <a href="postconf.1.html"><b>postconf</b>(1)</a> command displays the values of <a href="postconf.5.html"><b>main.cf</b></a> con-
- figuration parameters, and warns about possible mis-typed parameter
- names (Postfix 2.9 and later). The command can also change <a href="postconf.5.html"><b>main.cf</b></a>
+ figuration parameters, and warns about possible mis-typed parameter
+ names (Postfix 2.9 and later). The command can also change <a href="postconf.5.html"><b>main.cf</b></a>
configuration parameter values, or display other configuration informa-
tion about the Postfix mail system.
Options:
- <b>-a</b> List the available SASL plug-in types for the Postfix SMTP
- server. The plug-in type is selected with the <b><a href="postconf.5.html#smtpd_sasl_type">smtpd_sasl_type</a></b>
- configuration parameter by specifying one of the names listed
+ <b>-a</b> List the available SASL plug-in types for the Postfix SMTP
+ server. The plug-in type is selected with the <b><a href="postconf.5.html#smtpd_sasl_type">smtpd_sasl_type</a></b>
+ configuration parameter by specifying one of the names listed
below.
- <b>cyrus</b> This server plug-in is available when Postfix is built
+ <b>cyrus</b> This server plug-in is available when Postfix is built
with Cyrus SASL support.
<b>dovecot</b>
This server plug-in uses the Dovecot authentication
- server, and is available when Postfix is built with any
+ server, and is available when Postfix is built with any
form of SASL support.
This feature is available with Postfix 2.3 and later.
- <b>-A</b> List the available SASL plug-in types for the Postfix SMTP
+ <b>-A</b> List the available SASL plug-in types for the Postfix SMTP
client. The plug-in type is selected with the <b><a href="postconf.5.html#smtp_sasl_type">smtp_sasl_type</a></b> or
<b><a href="postconf.5.html#lmtp_sasl_type">lmtp_sasl_type</a></b> configuration parameters by specifying one of the
names listed below.
- <b>cyrus</b> This client plug-in is available when Postfix is built
+ <b>cyrus</b> This client plug-in is available when Postfix is built
with Cyrus SASL support.
This feature is available with Postfix 2.3 and later.
<b>-b</b> [<i>template</i><b>_</b><i>file</i>]
Display the message text that appears at the beginning of deliv-
- ery status notification (DSN) messages, expanding $<b>name</b> expres-
+ ery status notification (DSN) messages, expanding $<b>name</b> expres-
sions with actual values as described in <a href="bounce.5.html"><b>bounce</b>(5)</a>.
- To override the <b><a href="postconf.5.html#bounce_template_file">bounce_template_file</a></b> parameter setting, specify
- a template file name at the end of the "<b>postconf -b</b>" command
- line. Specify an empty file name to display built-in templates
+ To override the <b><a href="postconf.5.html#bounce_template_file">bounce_template_file</a></b> parameter setting, specify
+ a template file name at the end of the "<b>postconf -b</b>" command
+ line. Specify an empty file name to display built-in templates
(in shell language: "").
This feature is available with Postfix 2.3 and later.
of the default configuration directory.
<b>-C</b> <i>class,...</i>
- When displaying <a href="postconf.5.html"><b>main.cf</b></a> parameters, select only parameters from
+ When displaying <a href="postconf.5.html"><b>main.cf</b></a> parameters, select only parameters from
the specified class(es):
<b>builtin</b>
This feature is available with Postfix 2.9 and later.
- <b>-d</b> Print <a href="postconf.5.html"><b>main.cf</b></a> default parameter settings instead of actual set-
- tings. Specify <b>-df</b> to fold long lines for human readability
+ <b>-d</b> Print <a href="postconf.5.html"><b>main.cf</b></a> default parameter settings instead of actual set-
+ tings. Specify <b>-df</b> to fold long lines for human readability
(Postfix 2.9 and later).
- <b>-e</b> Edit the <a href="postconf.5.html"><b>main.cf</b></a> configuration file, and update parameter set-
- tings with the "<i>name=value</i>" pairs on the <a href="postconf.1.html"><b>postconf</b>(1)</a> command
+ <b>-e</b> Edit the <a href="postconf.5.html"><b>main.cf</b></a> configuration file, and update parameter set-
+ tings with the "<i>name=value</i>" pairs on the <a href="postconf.1.html"><b>postconf</b>(1)</a> command
line.
- With <b>-M</b>, edit the <a href="master.5.html"><b>master.cf</b></a> configuration file, and replace one
- or more service entries with new values as specified with "<i>ser-</i>
+ With <b>-M</b>, edit the <a href="master.5.html"><b>master.cf</b></a> configuration file, and replace one
+ or more service entries with new values as specified with "<i>ser-</i>
<i>vice/type=value</i>" on the <a href="postconf.1.html"><b>postconf</b>(1)</a> command line.
- With <b>-F</b>, edit the <a href="master.5.html"><b>master.cf</b></a> configuration file, and replace one
- or more service fields with new values as specified with "<i>ser-</i>
- <i>vice/type/field=value</i>" on the <a href="postconf.1.html"><b>postconf</b>(1)</a> command line. Cur-
- rently, the "command" field contains the command name and com-
+ With <b>-F</b>, edit the <a href="master.5.html"><b>master.cf</b></a> configuration file, and replace one
+ or more service fields with new values as specified with "<i>ser-</i>
+ <i>vice/type/field=value</i>" on the <a href="postconf.1.html"><b>postconf</b>(1)</a> command line. Cur-
+ rently, the "command" field contains the command name and com-
mand arguments. This may change in the near future, so that the
"command" field contains only the command name, and a new "argu-
ments" pseudofield contains the command arguments.
- With <b>-P</b>, edit the <a href="master.5.html"><b>master.cf</b></a> configuration file, and add or
- update one or more service parameter settings (-o parame-
- ter=value settings) with new values as specified with "<i>ser-</i>
+ With <b>-P</b>, edit the <a href="master.5.html"><b>master.cf</b></a> configuration file, and add or
+ update one or more service parameter settings (-o parame-
+ ter=value settings) with new values as specified with "<i>ser-</i>
<i>vice/type/parameter=value</i>" on the <a href="postconf.1.html"><b>postconf</b>(1)</a> command line.
In all cases the file is copied to a temporary file then renamed
- into place. Specify quotes to protect special characters and
+ into place. Specify quotes to protect special characters and
whitespace on the <a href="postconf.1.html"><b>postconf</b>(1)</a> command line.
- The <b>-e</b> option is no longer needed with Postfix version 2.8 and
- later, as it is assumed whenever a value is specified (empty or
+ The <b>-e</b> option is no longer needed with Postfix version 2.8 and
+ later, as it is assumed whenever a value is specified (empty or
non-empty).
<b>-f</b> Fold long lines when printing <a href="postconf.5.html"><b>main.cf</b></a> or <a href="master.5.html"><b>master.cf</b></a> configuration
This feature is available with Postfix 2.9 and later.
<b>-F</b> Show <a href="master.5.html"><b>master.cf</b></a> per-entry field settings (by default all services
- and all fields), formatted as "<i>service/type/field=value</i>", one
+ and all fields), formatted as "<i>service/type/field=value</i>", one
per line. Specify <b>-Ff</b> to fold long lines.
- Specify one or more "<i>service/type/field</i>" instances on the <a href="postconf.1.html"><b>post-</b></a>
- <a href="postconf.1.html"><b>conf</b>(1)</a> command line to limit the output to fields of interest.
- Trailing parameter name or service type fields that are omitted
+ Specify one or more "<i>service/type/field</i>" instances on the <a href="postconf.1.html"><b>post-</b></a>
+ <a href="postconf.1.html"><b>conf</b>(1)</a> command line to limit the output to fields of interest.
+ Trailing parameter name or service type fields that are omitted
will be handled as "*" wildcard fields.
This feature is available with Postfix 2.11 and later.
- <b>-h</b> Show parameter or attribute values without the "<i>name</i> = " label
+ <b>-h</b> Show parameter or attribute values without the "<i>name</i> = " label
that normally precedes the value.
- <b>-H</b> Show parameter or attribute names without the " = <i>value</i>" that
+ <b>-H</b> Show parameter or attribute names without the " = <i>value</i>" that
normally follows the name.
This feature is available with Postfix 3.1 and later.
+ <b>-j</b> JSON output. Format <a href="postconf.5.html">main.cf</a> or <a href="master.5.html">master.cf</a> settings as one <b>{"</b><i>key</i><b>":</b>
+ <b>"</b><i>value</i><b>"}</b> object per line.
+
+ This feature is available with Postfix 3.11 and later.
+
<b>-l</b> List the names of all supported mailbox locking methods. Post-
fix supports the following methods:
postmap - Postfix lookup table management
<b><a name="synopsis">SYNOPSIS</a></b>
- <b>postmap</b> [<b>-bfFhimnNoprsuUvw</b>] [<b>-c</b> <i>config</i><b>_</b><i>dir</i>] [<b>-d</b> <i>key</i>] [<b>-q</b> <i>key</i>]
+ <b>postmap</b> [<b>-bfFhijmnNoprsuUvw</b>] [<b>-c</b> <i>config</i><b>_</b><i>dir</i>] [<b>-d</b> <i>key</i>] [<b>-q</b> <i>key</i>]
[<i>file</i><b>_</b><i>type</i>:]<i>file</i><b>_</b><i>name</i> ...
<b><a name="description">DESCRIPTION</a></b>
truncate an existing database. By default, <a href="postmap.1.html"><b>postmap</b>(1)</a> creates a
new database from the entries in <b>file_name</b>.
+ <b>-j</b> JSON output. Format the output from <b>-q</b> and <b>-s</b> as one <b>{"</b><i>key</i><b>":</b>
+ <b>"</b><i>value</i><b>"}</b> object per line.
+
+ This feature is available in Postfix version 3.11 and later.
+
<b>-m</b> Enable MIME parsing with "<b>-b</b>" and "<b>-h</b>".
This feature is available in Postfix version 2.6 and later.
- <b>-N</b> Include the terminating null character that terminates lookup
- keys and values. By default, <a href="postmap.1.html"><b>postmap</b>(1)</a> does whatever is the
+ <b>-N</b> Include the terminating null character that terminates lookup
+ keys and values. By default, <a href="postmap.1.html"><b>postmap</b>(1)</a> does whatever is the
default for the host operating system.
- <b>-n</b> Don't include the terminating null character that terminates
- lookup keys and values. By default, <a href="postmap.1.html"><b>postmap</b>(1)</a> does whatever is
+ <b>-n</b> Don't include the terminating null character that terminates
+ lookup keys and values. By default, <a href="postmap.1.html"><b>postmap</b>(1)</a> does whatever is
the default for the host operating system.
- <b>-o</b> Do not release root privileges when processing a non-root input
- file. By default, <a href="postmap.1.html"><b>postmap</b>(1)</a> drops root privileges and runs as
+ <b>-o</b> Do not release root privileges when processing a non-root input
+ file. By default, <a href="postmap.1.html"><b>postmap</b>(1)</a> drops root privileges and runs as
the source file owner instead.
- <b>-p</b> Do not inherit the file access permissions from the input file
- when creating a new file. Instead, create a new file with
+ <b>-p</b> Do not inherit the file access permissions from the input file
+ when creating a new file. Instead, create a new file with
default access permissions (mode 0644).
- <b>-q</b> <i>key</i> Search the specified maps for <i>key</i> and write the first value
- found to the standard output stream. The exit status is zero
+ <b>-q</b> <i>key</i> Search the specified maps for <i>key</i> and write the first value
+ found to the standard output stream. The exit status is zero
when the requested information was found.
- Note: this performs a single query with the key as specified,
- and does not make iterative queries with substrings of the key
- as described for <a href="access.5.html">access(5)</a>, <a href="canonical.5.html">canonical(5)</a>, <a href="transport.5.html">transport(5)</a>, <a href="virtual.5.html">vir-</a>
+ Note: this performs a single query with the key as specified,
+ and does not make iterative queries with substrings of the key
+ as described for <a href="access.5.html">access(5)</a>, <a href="canonical.5.html">canonical(5)</a>, <a href="transport.5.html">transport(5)</a>, <a href="virtual.5.html">vir-</a>
<a href="virtual.5.html">tual(5)</a> and other Postfix table-driven features.
- If a key value of <b>-</b> is specified, the program reads key values
- from the standard input stream and writes one line of <i>key value</i>
+ If a key value of <b>-</b> is specified, the program reads key values
+ from the standard input stream and writes one line of <i>key value</i>
output for each key that was found. The exit status is zero when
at least one of the requested keys was found.
- <b>-r</b> When updating a table, do not complain about attempts to update
+ <b>-r</b> When updating a table, do not complain about attempts to update
existing entries, and make those updates anyway.
- <b>-s</b> Retrieve all database elements, and write one line of <i>key value</i>
- output for each element. The elements are printed in database
- order, which is not necessarily the same as the original input
+ <b>-s</b> Retrieve all database elements, and write one line of <i>key value</i>
+ output for each element. The elements are printed in database
+ order, which is not necessarily the same as the original input
order.
- This feature is available in Postfix version 2.2 and later, and
+ This feature is available in Postfix version 2.2 and later, and
is not available for all database types.
- <b>-u</b> Disable UTF-8 support. UTF-8 support is enabled by default when
- "<a href="postconf.5.html#smtputf8_enable">smtputf8_enable</a> = yes". It requires that keys and values are
+ <b>-u</b> Disable UTF-8 support. UTF-8 support is enabled by default when
+ "<a href="postconf.5.html#smtputf8_enable">smtputf8_enable</a> = yes". It requires that keys and values are
valid UTF-8 strings.
<b>-U</b> With "<a href="postconf.5.html#smtputf8_enable">smtputf8_enable</a> = yes", force UTF-8 syntax checks with the
<b>-b</b> and <b>-h</b> options.
- <b>-v</b> Enable verbose logging for debugging purposes. Multiple <b>-v</b>
+ <b>-v</b> Enable verbose logging for debugging purposes. Multiple <b>-v</b>
options make the software increasingly verbose.
- <b>-w</b> When updating a table, do not complain about attempts to update
+ <b>-w</b> When updating a table, do not complain about attempts to update
existing entries, and ignore those attempts.
Arguments:
The <a href="postmap.1.html"><b>postmap</b>(1)</a> command can query any supported file type, but it
can create only the following file types:
- <b>btree</b> The output file is a btree file, named <i>file</i><b>_</b><i>name</i><b>.db</b>.
- This is available on systems with support for <b>db</b> data-
+ <b>btree</b> The output file is a btree file, named <i>file</i><b>_</b><i>name</i><b>.db</b>.
+ This is available on systems with support for <b>db</b> data-
bases.
- <b>cdb</b> The output consists of one file, named <i>file</i><b>_</b><i>name</i><b>.cdb</b>.
- This is available on systems with support for <b>cdb</b> data-
+ <b>cdb</b> The output consists of one file, named <i>file</i><b>_</b><i>name</i><b>.cdb</b>.
+ This is available on systems with support for <b>cdb</b> data-
bases.
<b>dbm</b> The output consists of two files, named <i>file</i><b>_</b><i>name</i><b>.pag</b> and
<i>file</i><b>_</b><i>name</i><b>.dir</b>. This is available on systems with support
for <b>dbm</b> databases.
- <b>fail</b> A table that reliably fails all requests. The lookup ta-
- ble name is used for logging only. This table exists to
+ <b>fail</b> A table that reliably fails all requests. The lookup ta-
+ ble name is used for logging only. This table exists to
simplify Postfix error tests.
- <b>hash</b> The output file is a hashed file, named <i>file</i><b>_</b><i>name</i><b>.db</b>.
- This is available on systems with support for <b>db</b> data-
+ <b>hash</b> The output file is a hashed file, named <i>file</i><b>_</b><i>name</i><b>.db</b>.
+ This is available on systems with support for <b>db</b> data-
bases.
- <b>lmdb</b> The output is a btree-based file, named <i>file</i><b>_</b><i>name</i><b>.lmdb</b>.
- <b>lmdb</b> supports concurrent writes and reads from different
+ <b>lmdb</b> The output is a btree-based file, named <i>file</i><b>_</b><i>name</i><b>.lmdb</b>.
+ <b>lmdb</b> supports concurrent writes and reads from different
processes, unlike other supported file-based tables.
- This is available on systems with support for <b>lmdb</b> data-
+ This is available on systems with support for <b>lmdb</b> data-
bases.
<b>sdbm</b> The output consists of two files, named <i>file</i><b>_</b><i>name</i><b>.pag</b> and
<i>file</i><b>_</b><i>name</i><b>.dir</b>. This is available on systems with support
for <b>sdbm</b> databases.
- When no <i>file</i><b>_</b><i>type</i> is specified, the software uses the database
- type specified via the <b><a href="postconf.5.html#default_database_type">default_database_type</a></b> configuration
+ When no <i>file</i><b>_</b><i>type</i> is specified, the software uses the database
+ type specified via the <b><a href="postconf.5.html#default_database_type">default_database_type</a></b> configuration
parameter.
<i>file</i><b>_</b><i>name</i>
base.
<b><a name="diagnostics">DIAGNOSTICS</a></b>
- Problems are logged to the standard error stream and to <b>syslogd</b>(8) or
+ Problems are logged to the standard error stream and to <b>syslogd</b>(8) or
<a href="postlogd.8.html"><b>postlogd</b>(8)</a>. No output means that no problems were detected. Duplicate
entries are skipped and are flagged with a warning.
<a href="postmap.1.html"><b>postmap</b>(1)</a> terminates with zero exit status in case of success (includ-
- ing successful "<b>postmap -q</b>" lookup) and terminates with non-zero exit
+ ing successful "<b>postmap -q</b>" lookup) and terminates with non-zero exit
status in case of failure.
<b><a name="environment">ENVIRONMENT</a></b>
Enable verbose logging for debugging purposes.
<b><a name="configuration_parameters">CONFIGURATION PARAMETERS</a></b>
- The following <a href="postconf.5.html"><b>main.cf</b></a> parameters are especially relevant to this pro-
- gram. The text below provides only a parameter summary. See <a href="postconf.5.html"><b>post-</b></a>
+ The following <a href="postconf.5.html"><b>main.cf</b></a> parameters are especially relevant to this pro-
+ gram. The text below provides only a parameter summary. See <a href="postconf.5.html"><b>post-</b></a>
<a href="postconf.5.html"><b>conf</b>(5)</a> for more details including examples.
<b><a href="postconf.5.html#berkeley_db_create_buffer_size">berkeley_db_create_buffer_size</a> (16777216)</b>
- The per-table I/O buffer size for programs that create Berkeley
+ The per-table I/O buffer size for programs that create Berkeley
DB hash or btree tables.
<b><a href="postconf.5.html#berkeley_db_read_buffer_size">berkeley_db_read_buffer_size</a> (131072)</b>
hash or btree tables.
<b><a href="postconf.5.html#config_directory">config_directory</a> (see 'postconf -d' output)</b>
- The default location of the Postfix <a href="postconf.5.html">main.cf</a> and <a href="master.5.html">master.cf</a> con-
+ The default location of the Postfix <a href="postconf.5.html">main.cf</a> and <a href="master.5.html">master.cf</a> con-
figuration files.
<b><a href="postconf.5.html#default_database_type">default_database_type</a> (see 'postconf -d' output)</b>
and <a href="postmap.1.html"><b>postmap</b>(1)</a> commands.
<b><a href="postconf.5.html#import_environment">import_environment</a> (see 'postconf -d' output)</b>
- The list of environment variables that a privileged Postfix
- process will import from a non-Postfix parent process, or
+ The list of environment variables that a privileged Postfix
+ process will import from a non-Postfix parent process, or
name=value environment overrides.
<b><a href="postconf.5.html#smtputf8_enable">smtputf8_enable</a> (yes)</b>
- Enable preliminary SMTPUTF8 support for the protocols described
+ Enable preliminary SMTPUTF8 support for the protocols described
in <a href="https://tools.ietf.org/html/rfc6531">RFC 6531</a>, <a href="https://tools.ietf.org/html/rfc6532">RFC 6532</a>, and <a href="https://tools.ietf.org/html/rfc6533">RFC 6533</a>.
<b><a href="postconf.5.html#syslog_facility">syslog_facility</a> (mail)</b>
The syslog facility of Postfix logging.
<b><a href="postconf.5.html#syslog_name">syslog_name</a> (see 'postconf -d' output)</b>
- A prefix that is prepended to the process name in syslog
+ A prefix that is prepended to the process name in syslog
records, so that, for example, "smtpd" becomes "prefix/smtpd".
Available in Postfix 2.11 and later:
.na
.nf
.fi
-\fBpostalias\fR [\fB\-Nfinoprsuvw\fR] [\fB\-c \fIconfig_dir\fR]
+\fBpostalias\fR [\fB\-Nfijnoprsuvw\fR] [\fB\-c \fIconfig_dir\fR]
[\fB\-d \fIkey\fR] [\fB\-q \fIkey\fR]
[\fIfile_type\fR:]\fIfile_name\fR ...
.SH DESCRIPTION
Incremental mode. Read entries from standard input and do not
truncate an existing database. By default, \fBpostalias\fR(1) creates
a new database from the entries in \fIfile_name\fR.
+.IP \fB\-j\fR
+JSON output. Format the output from \fB\-q\fR and \fB\-s\fR
+as one \fB{"\fIkey\fB": "\fIvalue\fB"}\fR object per line.
+.sp
+This feature is available in Postfix version 3.11 and later.
.IP \fB\-N\fR
Include the terminating null character that terminates lookup keys
and values. By default, \fBpostalias\fR(1) does whatever
.ti -4
\fBManaging main.cf:\fR
-\fBpostconf\fR [\fB\-dfhHnopqvx\fR] [\fB\-c \fIconfig_dir\fR]
+\fBpostconf\fR [\fB\-dfhHjnopqvx\fR] [\fB\-c \fIconfig_dir\fR]
[\fB\-C \fIclass,...\fR] [\fIparameter ...\fR]
\fBpostconf\fR [\fB\-epv\fR] [\fB\-c \fIconfig_dir\fR]
.ti -4
\fBManaging master.cf service entries:\fR
-\fBpostconf\fR \fB\-M\fR [\fB\-foqvx\fR] [\fB\-c \fIconfig_dir\fR]
+\fBpostconf\fR \fB\-M\fR [\fB\-joqvx\fR] [\fB\-c \fIconfig_dir\fR]
[\fIservice\fR[\fB/\fItype\fR]\fI ...\fR]
\fBpostconf\fR \fB\-M\fR [\fB\-ev\fR] [\fB\-c \fIconfig_dir\fR]
.ti -4
\fBManaging master.cf service fields:\fR
-\fBpostconf\fR \fB\-F\fR [\fB\-fhHoqvx\fR] [\fB\-c \fIconfig_dir\fR]
+\fBpostconf\fR \fB\-F\fR [\fB\-fhHjoqvx\fR] [\fB\-c \fIconfig_dir\fR]
[\fIservice\fR[\fB/\fItype\fR[\fB/\fIfield\fR]]\fI ...\fR]
\fBpostconf\fR \fB\-F\fR [\fB\-ev\fR] [\fB\-c \fIconfig_dir\fR]
.ti -4
\fBManaging master.cf service parameters:\fR
-\fBpostconf\fR \fB\-P\fR [\fB\-fhHoqvx\fR] [\fB\-c \fIconfig_dir\fR]
+\fBpostconf\fR \fB\-P\fR [\fB\-fhHjoqvx\fR] [\fB\-c \fIconfig_dir\fR]
[\fIservice\fR[\fB/\fItype\fR[\fB/\fIparameter\fR]]\fI ...\fR]
\fBpostconf\fR \fB\-P\fR [\fB\-ev\fR] [\fB\-c \fIconfig_dir\fR]
that normally follows the name.
This feature is available with Postfix 3.1 and later.
+.IP \fB\-j\fR
+JSON output. Format main.cf or master.cf settings as one
+\fB{"\fIkey\fB": "\fIvalue\fB"}\fR object per line.
+
+This feature is available with Postfix 3.11 and later.
.IP \fB\-l\fR
List the names of all supported mailbox locking methods.
Postfix supports the following methods:
.na
.nf
.fi
-\fBpostmap\fR [\fB\-bfFhimnNoprsuUvw\fR] [\fB\-c \fIconfig_dir\fR]
+\fBpostmap\fR [\fB\-bfFhijmnNoprsuUvw\fR] [\fB\-c \fIconfig_dir\fR]
[\fB\-d \fIkey\fR] [\fB\-q \fIkey\fR]
[\fIfile_type\fR:]\fIfile_name\fR ...
.SH DESCRIPTION
Incremental mode. Read entries from standard input and do not
truncate an existing database. By default, \fBpostmap\fR(1) creates
a new database from the entries in \fBfile_name\fR.
+.IP \fB\-j\fR
+JSON output. Format the output from \fB\-q\fR and \fB\-s\fR
+as one \fB{"\fIkey\fB": "\fIvalue\fB"}\fR object per line.
+.sp
+This feature is available in Postfix version 3.11 and later.
.IP \fB\-m\fR
Enable MIME parsing with "\fB\-b\fR" and "\fB\-h\fR".
.sp
smtpd smtpd c smtp smtp_connect c smtp smtp_proto c
util mac_expand ref proto postconf proto proto Makefile in
smtp lmtp_params c smtp smtp h smtp smtp_params c
+ postmap Makefile in postmap postmap c postalias Makefile in
+ postalias postalias c
+ postconf postconf hc postconf postconf_main c
Christophe
Kalt
stdlib
+Nfijnoprsuvw
+bfFhijmnNoprsuUvw
+dfhHjnopqvx
++fhHjoqvx
++joqvx
+fhHjoqvx
+joqvx
* Patches change both the patchlevel and the release date. Snapshots have no
* patchlevel; they change the release date only.
*/
-#define MAIL_RELEASE_DATE "20251118"
+#define MAIL_RELEASE_DATE "20251122"
#define MAIL_VERSION_NUMBER "3.11"
#ifdef SNAPSHOT
update: ../../bin/$(PROG)
-tests: test1 test2 fail_test mode_conflict_test
+tests: test1 test2 fail_test mode_conflict_test json_tests
root_tests:
diff mode_conflict_test.ref mode_conflict_test.tmp
rm -f mode_conflict_test.tmp main.cf
+json_tests: json_query_test json_queries_test json_sequence_test
+
+json_query_test: $(PROG)
+ # Exercise code in postalias_query().
+ @echo ; echo RUN json_query_test
+ echo '{"postmaster": "root"}' >json_query.tmp
+ $(SHLIB_ENV) ${VALGRIND} ./$(PROG) -jq postmaster \
+ 'inline:{{postmaster = root}}' | diff json_query.tmp -
+ ($(SHLIB_ENV) ${VALGRIND} ./$(PROG) -jq other \
+ 'inline:{{postmaster = root}}' || exit 0) | diff /dev/null -
+ rm -f json_query.tmp
+ @echo PASS json_query_test
+
+json_queries_test: $(PROG)
+ # Exercise json formatting in postalias_queries().
+ @echo ; echo RUN json_queries_test
+ echo '{"postmaster": "root"}' >json_queries.tmp
+ (echo postmaster; echo other) | $(SHLIB_ENV) ${VALGRIND} \
+ ./$(PROG) -jq - 'inline:{{postmaster = root}}' | \
+ diff json_queries.tmp -
+ rm -f json_queries.tmp
+ @echo PASS json_queries_test
+
+json_sequence_test: $(PROG)
+ # Exercise json formatting in postalias_seq().
+ @echo ; echo RUN json_sequence_test
+ rm -f json_sequence.tmp
+ (echo '{"postmaster": "root"}'; echo '{"root": "real-user"}') |\
+ sort >json_sequence.tmp
+ $(SHLIB_ENV) ${VALGRIND} ./$(PROG) -js \
+ 'inline:{{postmaster = root} {root=real-user}}' | sort | \
+ diff json_sequence.tmp -
+ rm -f json_sequence.tmp
+ @echo PASS json_sequence_test
+
../../bin/$(PROG): $(PROG)
cp $(PROG) ../../bin
/* Postfix alias database maintenance
/* SYNOPSIS
/* .fi
-/* \fBpostalias\fR [\fB-Nfinoprsuvw\fR] [\fB-c \fIconfig_dir\fR]
+/* \fBpostalias\fR [\fB-Nfijnoprsuvw\fR] [\fB-c \fIconfig_dir\fR]
/* [\fB-d \fIkey\fR] [\fB-q \fIkey\fR]
/* [\fIfile_type\fR:]\fIfile_name\fR ...
/* DESCRIPTION
/* Incremental mode. Read entries from standard input and do not
/* truncate an existing database. By default, \fBpostalias\fR(1) creates
/* a new database from the entries in \fIfile_name\fR.
+/* .IP \fB-j\fR
+/* JSON output. Format the output from \fB-q\fR and \fB-s\fR
+/* as one \fB{"\fIkey\fB": "\fIvalue\fB"}\fR object per line.
+/* .sp
+/* This feature is available in Postfix version 3.11 and later.
/* .IP \fB-N\fR
/* Include the terminating null character that terminates lookup keys
/* and values. By default, \fBpostalias\fR(1) does whatever
#define POSTALIAS_FLAG_SAVE_PERM (1<<1) /* copy access permission
* from source */
+ /*
+ * Global state.
+ */
+int json_output;
+VSTRING *json_key_buf;
+VSTRING *json_val_buf;
+
/* postalias - create or update alias database */
static void postalias(char *map_type, char *path_name, int postalias_flags,
msg_warn("table %s:%s should return NO RESULT in case of NOT FOUND",
dicts[n]->type, dicts[n]->name);
}
- vstream_printf("%s: %s\n", STR(keybuf), value);
+ if (json_output == 0)
+ vstream_printf("%s: %s\n", STR(keybuf), value);
+ else
+ vstream_printf("{\"%s\": \"%s\"}\n",
+ quote_for_json(json_key_buf, STR(keybuf), -1),
+ quote_for_json(json_val_buf, value, -1));
found = 1;
break;
}
msg_warn("table %s:%s should return NO RESULT in case of NOT FOUND",
map_type, map_name);
}
- vstream_printf("%s\n", value);
+ if (json_output == 0)
+ vstream_printf("%s\n", value);
+ else
+ vstream_printf("{\"%s\": \"%s\"}\n",
+ quote_for_json(json_key_buf, key, -1),
+ quote_for_json(json_val_buf, value, -1));
}
if (dict->error)
msg_fatal("table %s:%s: query error: %m", dict->type, dict->name);
msg_warn("table %s:%s should return NO RESULT in case of NOT FOUND",
map_type, map_name);
}
- vstream_printf("%s: %s\n", key, value);
+ if (json_output == 0)
+ vstream_printf("%s: %s\n", key, value);
+ else
+ vstream_printf("{\"%s\": \"%s\"}\n",
+ quote_for_json(json_key_buf, key, -1),
+ quote_for_json(json_val_buf, value, -1));
}
if (dict->error)
msg_fatal("table %s:%s: sequence error: %m", dict->type, dict->name);
/*
* Parse JCL.
*/
- while ((ch = GETOPT(argc, argv, "Nc:d:finopq:rsuvw")) > 0) {
+ while ((ch = GETOPT(argc, argv, "Nc:d:fijnopq:rsuvw")) > 0) {
switch (ch) {
default:
usage(argv[0]);
update = 1;
open_flags &= ~O_TRUNC;
break;
+ case 'j':
+ if (json_output == 0) {
+ json_output = 1;
+ json_key_buf = vstring_alloc(100);
+ json_val_buf = vstring_alloc(100);
+ }
+ break;
case 'n':
dict_flags |= DICT_FLAG_TRY0NULL;
dict_flags &= ~DICT_FLAG_TRY1NULL;
break;
}
}
+ if (json_output && !(sequence || query))
+ msg_fatal("option -j requires -q or -s");
mail_conf_read();
/* Enforce consistent operation of different Postfix parts. */
import_env = mail_parm_split(VAR_IMPORT_ENVIRON, var_import_environ);
test42 test43 test44 test45 test46 test47 test48 test49 test50 test51 \
test52 test53 test54 test55 test56 test57 test58 test59 test60 test61 \
test62 test63 test64 test65 test66 test67 test68 test69 test70 test71 \
- test72 test73 test74 test75 test76 test78 test79
+ test72 test73 test74 test75 test76 test78 test79 json_tests
+
+json_tests: test80 test81 test82 test83 test84 test85 test86 test87
root_tests:
diff /dev/null test79.tmp
rm -f main.cf master.cf test79.tmp
+# JSON for main.cf parameters - no $name expansion
+test80: $(PROG) test80.ref
+ rm -f main.cf master.cf
+ touch main.cf master.cf
+ echo 'mydestination=$$whatever' >> main.cf
+ echo whatever=example.com >> main.cf
+ touch -t 197601010000 main.cf
+ $(HTABLE_FIX) $(SHLIB_ENV) $(VALGRIND) ./$(PROG) -nc. -j >test80.tmp 2>&1
+ diff test80.ref test80.tmp
+ rm -f main.cf master.cf test80.tmp
+
+# JSON for main.cf parameters - with $name expansion
+test81: $(PROG) test81.ref
+ rm -f main.cf master.cf
+ touch main.cf master.cf
+ echo 'mydestination=$$whatever' >> main.cf
+ echo whatever=example.com >> main.cf
+ touch -t 197601010000 main.cf
+ $(HTABLE_FIX) $(SHLIB_ENV) $(VALGRIND) ./$(PROG) -nc. -jx >test81.tmp 2>&1
+ diff test81.ref test81.tmp
+ rm -f main.cf master.cf test81.tmp
+
+# JSON for master.cf entries - no $name expansion
+test82: $(PROG) test82.ref
+ rm -f main.cf master.cf
+ touch main.cf master.cf
+ echo 'mydestination=$$whatever' >> main.cf
+ echo whatever=example.com >> main.cf
+ touch -t 197601010000 main.cf
+ echo foo unix - n n - 0 other >> master.cf
+ echo ' -o mydestination=$$whatever' >> master.cf
+ echo ' -o whatever=example.net' >> master.cf
+ echo 12345 inet n n n - - spawn >> master.cf
+ echo ' -o { command_time_limit = 2 3 }' >> master.cf
+ echo ' user=nobody argv=/bin/sh -c { sleep 1 }' >> master.cf
+ touch -t 197601010000 master.cf
+ $(HTABLE_FIX) $(SHLIB_ENV) $(VALGRIND) ./$(PROG) -c. -Mj >test82.tmp 2>&1
+ diff test82.ref test82.tmp
+ rm -f main.cf master.cf test82.tmp
+
+# JSON for master.cf entries - with $name expansion
+test83: $(PROG) test83.ref
+ rm -f main.cf master.cf
+ touch main.cf master.cf
+ echo 'mydestination=$$whatever' >> main.cf
+ echo whatever=example.com >> main.cf
+ touch -t 197601010000 main.cf
+ echo foo unix - n n - 0 other >> master.cf
+ echo ' -o mydestination=$$whatever' >> master.cf
+ echo ' -o whatever=example.net' >> master.cf
+ touch -t 197601010000 master.cf
+ $(HTABLE_FIX) $(SHLIB_ENV) $(VALGRIND) ./$(PROG) -c. -Mjx >test83.tmp 2>&1
+ diff test83.ref test83.tmp
+ rm -f main.cf master.cf test83.tmp
+
clean:
rm -f *.o *core $(PROG) $(TESTPROG) junk $(MAKES) $(AUTOS) $(DUMMIES) \
$(TEST_TMP) $(DB_MAKES)
+# JSON for master.cf fields - no $name expansion
+test84: $(PROG) test84.ref
+ rm -f main.cf master.cf
+ touch main.cf master.cf
+ echo 'mydestination=$$whatever' >> main.cf
+ echo whatever=example.com >> main.cf
+ touch -t 197601010000 main.cf
+ echo foo unix - n n - 0 other >> master.cf
+ echo ' -o mydestination=$$whatever' >> master.cf
+ echo ' -o whatever=example.net' >> master.cf
+ echo 12345 inet n n n - - spawn >> master.cf
+ echo ' -o { command_time_limit = 2 3 }' >> master.cf
+ echo ' user=nobody argv=/bin/sh -c { sleep 1 }' >> master.cf
+ touch -t 197601010000 master.cf
+ $(HTABLE_FIX) $(SHLIB_ENV) $(VALGRIND) ./$(PROG) -c. -Fj >test84.tmp 2>&1
+ diff test84.ref test84.tmp
+ rm -f main.cf master.cf test84.tmp
+
+# JSON for master.cf fields - with $name expansion
+test85: $(PROG) test85.ref
+ rm -f main.cf master.cf
+ touch main.cf master.cf
+ echo 'mydestination=$$whatever' >> main.cf
+ echo whatever=example.com >> main.cf
+ touch -t 197601010000 main.cf
+ echo foo unix - n n - 0 other >> master.cf
+ echo ' -o mydestination=$$whatever' >> master.cf
+ echo ' -o whatever=example.net' >> master.cf
+ touch -t 197601010000 master.cf
+ $(HTABLE_FIX) $(SHLIB_ENV) $(VALGRIND) ./$(PROG) -c. -Fjx >test85.tmp 2>&1
+ diff test85.ref test85.tmp
+ rm -f main.cf master.cf test85.tmp
+
+# JSON for master.cf parameters - no $name expansion
+test86: $(PROG) test86.ref
+ rm -f main.cf master.cf
+ touch main.cf master.cf
+ echo 'mydestination=$$whatever' >> main.cf
+ echo whatever=example.com >> main.cf
+ touch -t 197601010000 main.cf
+ echo foo unix - n n - 0 other >> master.cf
+ echo ' -o mydestination=$$whatever' >> master.cf
+ echo ' -o whatever=example.net' >> master.cf
+ echo 12345 inet n n n - - spawn >> master.cf
+ echo ' -o { command_time_limit = 2 3 }' >> master.cf
+ echo ' user=nobody argv=/bin/sh -c { sleep 1 }' >> master.cf
+ touch -t 197601010000 master.cf
+ $(HTABLE_FIX) $(SHLIB_ENV) $(VALGRIND) ./$(PROG) -c. -Pj >test86.tmp 2>&1
+ diff test86.ref test86.tmp
+ rm -f main.cf master.cf test86.tmp
+
+# JSON for master.cf parameters - with $name expansion
+test87: $(PROG) test87.ref
+ rm -f main.cf master.cf
+ touch main.cf master.cf
+ echo 'mydestination=$$whatever' >> main.cf
+ echo whatever=example.com >> main.cf
+ touch -t 197601010000 main.cf
+ echo foo unix - n n - 0 other >> master.cf
+ echo ' -o mydestination=$$whatever' >> master.cf
+ echo ' -o whatever=example.net' >> master.cf
+ touch -t 197601010000 master.cf
+ $(HTABLE_FIX) $(SHLIB_ENV) $(VALGRIND) ./$(PROG) -c. -Pjx >test87.tmp 2>&1
+ diff test87.ref test87.tmp
+ rm -f main.cf master.cf test87.tmp
+
tidy: clean
depend: $(MAKES)
/* .ti -4
/* \fBManaging main.cf:\fR
/*
-/* \fBpostconf\fR [\fB-dfhHnopqvx\fR] [\fB-c \fIconfig_dir\fR]
+/* \fBpostconf\fR [\fB-dfhHjnopqvx\fR] [\fB-c \fIconfig_dir\fR]
/* [\fB-C \fIclass,...\fR] [\fIparameter ...\fR]
/*
/* \fBpostconf\fR [\fB-epv\fR] [\fB-c \fIconfig_dir\fR]
/* .ti -4
/* \fBManaging master.cf service entries:\fR
/*
-/* \fBpostconf\fR \fB-M\fR [\fB-foqvx\fR] [\fB-c \fIconfig_dir\fR]
+/* \fBpostconf\fR \fB-M\fR [\fB-joqvx\fR] [\fB-c \fIconfig_dir\fR]
/* [\fIservice\fR[\fB/\fItype\fR]\fI ...\fR]
/*
/* \fBpostconf\fR \fB-M\fR [\fB-ev\fR] [\fB-c \fIconfig_dir\fR]
/* .ti -4
/* \fBManaging master.cf service fields:\fR
/*
-/* \fBpostconf\fR \fB-F\fR [\fB-fhHoqvx\fR] [\fB-c \fIconfig_dir\fR]
+/* \fBpostconf\fR \fB-F\fR [\fB-fhHjoqvx\fR] [\fB-c \fIconfig_dir\fR]
/* [\fIservice\fR[\fB/\fItype\fR[\fB/\fIfield\fR]]\fI ...\fR]
/*
/* \fBpostconf\fR \fB-F\fR [\fB-ev\fR] [\fB-c \fIconfig_dir\fR]
/* .ti -4
/* \fBManaging master.cf service parameters:\fR
/*
-/* \fBpostconf\fR \fB-P\fR [\fB-fhHoqvx\fR] [\fB-c \fIconfig_dir\fR]
+/* \fBpostconf\fR \fB-P\fR [\fB-fhHjoqvx\fR] [\fB-c \fIconfig_dir\fR]
/* [\fIservice\fR[\fB/\fItype\fR[\fB/\fIparameter\fR]]\fI ...\fR]
/*
/* \fBpostconf\fR \fB-P\fR [\fB-ev\fR] [\fB-c \fIconfig_dir\fR]
/* that normally follows the name.
/*
/* This feature is available with Postfix 3.1 and later.
+/* .IP \fB-j\fR
+/* JSON output. Format main.cf or master.cf settings as one
+/* \fB{"\fIkey\fB": "\fIvalue\fB"}\fR object per line.
+/*
+/* This feature is available with Postfix 3.11 and later.
/* .IP \fB-l\fR
/* List the names of all supported mailbox locking methods.
/* Postfix supports the following methods:
{PCF_MAIN_PARAM, (PCF_EDIT_CONF | PCF_EDIT_EXCL | PCF_COMMENT_OUT \
|PCF_FOLD_LINE | PCF_HIDE_NAME | PCF_PARAM_CLASS \
|PCF_SHOW_EVAL | PCF_SHOW_DEFS | PCF_SHOW_NONDEF \
- |PCF_MAIN_OVER | PCF_HIDE_VALUE)},
+ |PCF_MAIN_OVER | PCF_HIDE_VALUE | PCF_SHOW_JSON)},
{PCF_MASTER_ENTRY, (PCF_EDIT_CONF | PCF_EDIT_EXCL | PCF_COMMENT_OUT \
- |PCF_FOLD_LINE | PCF_MAIN_OVER | PCF_SHOW_EVAL)},
+ |PCF_FOLD_LINE | PCF_MAIN_OVER | PCF_SHOW_EVAL \
+ |PCF_SHOW_JSON)},
{PCF_MASTER_FLD, (PCF_EDIT_CONF | PCF_FOLD_LINE | PCF_HIDE_NAME \
- |PCF_MAIN_OVER | PCF_SHOW_EVAL | PCF_HIDE_VALUE)},
+ |PCF_MAIN_OVER | PCF_SHOW_EVAL | PCF_HIDE_VALUE \
+ |PCF_SHOW_JSON)},
{PCF_MASTER_PARAM, (PCF_EDIT_CONF | PCF_EDIT_EXCL | PCF_FOLD_LINE \
|PCF_HIDE_NAME | PCF_MAIN_OVER | PCF_SHOW_EVAL \
- |PCF_HIDE_VALUE)},
+ |PCF_HIDE_VALUE | PCF_SHOW_JSON)},
+ {PCF_SHOW_JSON, (PCF_MAIN_PARAM | PCF_MASTER_ENTRY | PCF_MASTER_FLD \
+ |PCF_MASTER_PARAM | PCF_MAIN_OVER | PCF_SHOW_EVAL \
+ |PCF_SHOW_NONDEF | PCF_SHOW_DEFS)},
/* Modifiers. */
{PCF_PARAM_CLASS, (PCF_MAIN_PARAM | PCF_SHOW_DEFS | PCF_SHOW_NONDEF)},
0,
"-F", PCF_MASTER_FLD,
"-h", PCF_HIDE_NAME,
"-H", PCF_HIDE_VALUE,
+ "-j", PCF_SHOW_JSON,
"-l", PCF_SHOW_LOCKS,
"-m", PCF_SHOW_MAPS,
"-M", PCF_MASTER_ENTRY,
" [-F (master.cf fields)]"
" [-h (no names)]"
" [-H (no values)]"
+ " [-j (streaming JSON) ]"
" [-l (lock types)]"
" [-m (map types)]"
" [-M (master.cf)]"
/*
* Parse JCL.
*/
- while ((ch = GETOPT(argc, argv, "aAbc:C:deEfFhHlmMno:pPqtT:vxX#")) > 0) {
+ while ((ch = GETOPT(argc, argv, "aAbc:C:deEfFhHjlmMno:pPqtT:vxX#")) > 0) {
switch (ch) {
case 'a':
pcf_cmd_mode |= PCF_SHOW_SASL_SERV;
case 'H':
pcf_cmd_mode |= PCF_HIDE_VALUE;
break;
+ case 'j':
+ pcf_cmd_mode |= PCF_SHOW_JSON;
+ break;
case 'l':
pcf_cmd_mode |= PCF_SHOW_LOCKS;
break;
#define PCF_HIDE_VALUE (1<<20) /* hide main.cf/master.cf =value */
#define PCF_SHOW_TLS (1<<21) /* TLS support introspection */
#define PCF_WARN_UNUSED_DEPRECATED (1<<22) /* As the name says */
+#define PCF_SHOW_JSON (1 << 23) /* JSON output */
#define PCF_DEF_MODE (PCF_WARN_UNUSED_DEPRECATED)
#define PCF_PARAM_FLAG_LEGACY (1<<4) /* legacy parameter name */
#define PCF_PARAM_FLAG_READONLY (1<<5) /* legacy parameter name */
#define PCF_PARAM_FLAG_DBMS (1<<6) /* dbms-defined parameter name */
+#define PCF_PARAM_FLAG_NUMBER (1<<7) /* numeric value */
#define PCF_PARAM_MASK_CLASS \
(PCF_PARAM_FLAG_BUILTIN | PCF_PARAM_FLAG_SERVICE | PCF_PARAM_FLAG_USER)
#define PCF_LEGACY_PARAMETER(node) ((node)->flags & PCF_PARAM_FLAG_LEGACY)
#define PCF_READONLY_PARAMETER(node) ((node)->flags & PCF_PARAM_FLAG_READONLY)
#define PCF_DBMS_PARAMETER(node) ((node)->flags & PCF_PARAM_FLAG_DBMS)
+#define PCF_NUMBER_PARAMETER(mode) ((node)->flags & PCF_PARAM_FLAG_NUMBER)
/* Values for param_data. See postconf_node module for narrative text. */
#define PCF_PARAM_NO_DATA ((char *) 0)
pcf_conv_bool_parameter);
for (cit = pcf_int_table; cit->name; cit++)
PCF_PARAM_TABLE_ENTER(pcf_param_table, cit->name,
- PCF_PARAM_FLAG_BUILTIN, (void *) cit,
- pcf_conv_int_parameter);
+ PCF_PARAM_FLAG_BUILTIN | PCF_PARAM_FLAG_NUMBER,
+ (void *) cit, pcf_conv_int_parameter);
for (cst = pcf_str_table; cst->name; cst++)
PCF_PARAM_TABLE_ENTER(pcf_param_table, cst->name,
PCF_PARAM_FLAG_BUILTIN, (void *) cst,
pcf_conv_nbool_parameter);
for (lst = pcf_long_table; lst->name; lst++)
PCF_PARAM_TABLE_ENTER(pcf_param_table, lst->name,
- PCF_PARAM_FLAG_BUILTIN, (void *) lst,
- pcf_conv_long_parameter);
+ PCF_PARAM_FLAG_BUILTIN | PCF_PARAM_FLAG_NUMBER,
+ (void *) lst, pcf_conv_long_parameter);
/*
* Register legacy parameters (used as a backwards-compatible migration
msg_panic("pcf_edit_main: unknown mode %d", mode);
}
if ((cvalue = htable_find(table, pattern)) != 0) {
- msg_warn("ignoring earlier request: '%s = %s'",
- pattern, cvalue->value);
+ if (edit_value && cvalue->value
+ && strcmp(edit_value, cvalue->value) != 0)
+ msg_warn("ignoring earlier request: '%s = %s'",
+ pattern, cvalue->value);
htable_delete(table, pattern, myfree);
}
cvalue = (struct cvalue *) mymalloc(sizeof(*cvalue));
PCF_PARAM_NODE *node)
{
static VSTRING *exp_buf = 0;
+ static VSTRING *json_buf = 0;
const char *value;
- if (exp_buf == 0)
+ if (exp_buf == 0 && (mode & PCF_SHOW_EVAL))
exp_buf = vstring_alloc(100);
+ if (json_buf == 0 && (mode & PCF_SHOW_JSON))
+ json_buf = vstring_alloc(100);
/*
* Use the default or actual value.
if ((mode & PCF_SHOW_EVAL) != 0 && PCF_RAW_PARAMETER(node) == 0)
value = pcf_expand_parameter_value(exp_buf, mode, value,
(PCF_MASTER_ENT *) 0);
- if ((mode & PCF_HIDE_NAME) == 0) {
+ if (mode & PCF_SHOW_JSON) {
+ vstream_fprintf(fp, "{\"%s\": ",
+ quote_for_json(json_buf, name, -1));
+ vstream_fprintf(fp, "\"%s\"}\n",
+ quote_for_json(json_buf, value, -1));
+ } else if ((mode & PCF_HIDE_NAME) == 0) {
pcf_print_line(fp, mode, "%s = %s\n", name, value);
} else {
pcf_print_line(fp, mode, "%s\n", value);
static const char pcf_valid_bool_types[] = "yn-";
static VSTRING *pcf_exp_buf;
+static VSTRING *pcf_json_buf;
#define STR(x) vstring_str(x)
pcf_master_table[entry_count].argv = 0;
}
+/* pcf_print_master_entry_as_json - JSON formatter */
+
+static void pcf_print_master_entry_as_json(VSTREAM *fp, int mode,
+ PCF_MASTER_ENT *masterp)
+{
+ char **argv = masterp->argv->argv;
+ const char *arg;
+ const char *aval;
+ int field;
+ int in_daemon_options;
+ int need_parens;
+
+ if (pcf_exp_buf == 0 && (mode & PCF_SHOW_EVAL))
+ pcf_exp_buf = vstring_alloc(100);
+ if (pcf_json_buf == 0 && (mode & PCF_SHOW_JSON))
+ pcf_json_buf = vstring_alloc(100);
+
+ /*
+ * Output the namespace part first, so that we can reuse a buffer.
+ */
+ vstream_fprintf(fp, "{\"%s\": ",
+ quote_for_json(pcf_json_buf, masterp->name_space, -1));
+
+ /*
+ * Show the standard fields with one-space column separation.
+ */
+#define APPEND_JSON_STR(s, l) quote_for_json_append(pcf_json_buf, (s), (l))
+#define APPEND_JSON_STR0(s) APPEND_JSON_STR((s), -1)
+#define APPEND_JSON_CHAR(s) APPEND_JSON_STR((s), 1)
+
+ VSTRING_RESET(pcf_json_buf);
+ for (field = 0; field < PCF_MASTER_MIN_FIELDS; field++) {
+ arg = argv[field];
+ if (field > 0)
+ APPEND_JSON_CHAR(" ");
+ APPEND_JSON_STR0(arg);
+ }
+
+ /*
+ * Format the daemon command-line options and non-option arguments.
+ */
+ in_daemon_options = 1;
+ for ( /* void */ ; (arg = argv[field]) != 0; field++) {
+ aval = 0;
+ need_parens = 0;
+ if (in_daemon_options) {
+ if (arg[0] != '-' || strcmp(arg, "--") == 0) {
+ in_daemon_options = 0;
+ }
+
+ /*
+ * Special processing for options that require a value.
+ */
+ else if (strchr(pcf_daemon_options_expecting_value, arg[1]) != 0
+ && (aval = argv[field + 1]) != 0) {
+
+ /*
+ * Optionally, expand $name in parameter value.
+ */
+ if (strcmp(arg, "-o") == 0
+ && (mode & PCF_SHOW_EVAL) != 0)
+ aval = pcf_expand_parameter_value(pcf_exp_buf, mode,
+ aval, masterp);
+ need_parens = aval[strcspn(aval, PCF_MASTER_BLANKS)];
+ }
+ } else {
+ need_parens = arg[strcspn(arg, PCF_MASTER_BLANKS)];
+ }
+ APPEND_JSON_CHAR(" ");
+ if (in_daemon_options == 0 && need_parens)
+ APPEND_JSON_CHAR("{");
+ APPEND_JSON_STR0(arg);
+ if (in_daemon_options == 0 && need_parens)
+ APPEND_JSON_CHAR("}");
+ if (aval) {
+ APPEND_JSON_CHAR(" ");
+ if (need_parens)
+ APPEND_JSON_CHAR("{");
+ APPEND_JSON_STR0(aval);
+ if (need_parens)
+ APPEND_JSON_CHAR("}");
+ field += 1;
+ }
+ }
+ VSTRING_TERMINATE(pcf_json_buf);
+ vstream_fprintf(fp, "\"%s\"}\n", STR(pcf_json_buf));
+
+ if (msg_verbose)
+ vstream_fflush(fp);
+}
+
/* pcf_print_master_entry - print one master line */
void pcf_print_master_entry(VSTREAM *fp, int mode, PCF_MASTER_ENT *masterp)
57, /* command */
};
+ if (mode & PCF_SHOW_JSON) {
+ pcf_print_master_entry_as_json(fp, mode, masterp);
+ return;
+ }
#define ADD_TEXT(text, len) do { \
vstream_fputs(text, fp); line_len += len; } \
while (0)
}
}
+/* pcf_print_master_field_as_json - scaffolding for JSON */
+
+static void pcf_print_master_field_as_json(VSTREAM *fp, int mode,
+ PCF_MASTER_ENT *masterp,
+ int field)
+{
+ char **argv = masterp->argv->argv;
+ const char *arg;
+ const char *aval;
+ int in_daemon_options;
+ int need_parens;
+
+ if (pcf_exp_buf == 0 && (mode & PCF_SHOW_EVAL) != 0)
+ pcf_exp_buf = vstring_alloc(100);
+ if (pcf_json_buf == 0 && (mode & PCF_SHOW_JSON) != 0)
+ pcf_json_buf = vstring_alloc(100);
+
+ /*
+ * Output the name part first, so that we can reuse a buffer.
+ */
+ vstream_fprintf(fp, "{\"%s\": ",
+ quote_for_json_var(pcf_json_buf, masterp->name_space,
+ PCF_NAMESP_SEP_STR,
+ pcf_str_field_pattern(field),
+ (const char *) 0));
+
+ /*
+ * Show the field value, or the first value in the case of a multi-column
+ * field.
+ */
+ VSTRING_RESET(pcf_json_buf);
+ APPEND_JSON_STR0(argv[field]);
+
+ /*
+ * Format the daemon command-line options and non-option arguments. Here,
+ * we have no data-dependent preference for column positions, but we do
+ * have argument grouping preferences.
+ */
+ if (field == PCF_MASTER_FLD_CMD) {
+ in_daemon_options = 1;
+ for (field += 1; (arg = argv[field]) != 0; field++) {
+ aval = 0;
+ need_parens = 0;
+ if (in_daemon_options) {
+ if (arg[0] != '-' || strcmp(arg, "--") == 0) {
+ in_daemon_options = 0;
+ } else if (strchr(pcf_daemon_options_expecting_value, arg[1]) != 0
+ && (aval = argv[field + 1]) != 0) {
+
+ /*
+ * Optionally, expand $name in parameter value.
+ */
+ if (strcmp(arg, "-o") == 0
+ && (mode & PCF_SHOW_EVAL) != 0)
+ aval = pcf_expand_parameter_value(pcf_exp_buf, mode,
+ aval, masterp);
+ need_parens = aval[strcspn(aval, PCF_MASTER_BLANKS)];
+ }
+ } else {
+ need_parens = arg[strcspn(arg, PCF_MASTER_BLANKS)];
+ }
+ APPEND_JSON_CHAR(" ");
+ if (in_daemon_options == 0 && need_parens)
+ APPEND_JSON_CHAR("{");
+ APPEND_JSON_STR0(arg);
+ if (in_daemon_options == 0 && need_parens)
+ APPEND_JSON_CHAR("}");
+ if (aval) {
+ APPEND_JSON_CHAR(" ");
+ if (need_parens)
+ APPEND_JSON_CHAR("{");
+ APPEND_JSON_STR0(aval);
+ if (need_parens)
+ APPEND_JSON_CHAR("}");
+ field += 1;
+ }
+ }
+ }
+ VSTRING_TERMINATE(pcf_json_buf);
+ vstream_fprintf(fp, "\"%s\"}\n", STR(pcf_json_buf));
+
+ if (msg_verbose)
+ vstream_fflush(fp);
+}
+
/* pcf_print_master_field - scaffolding */
static void pcf_print_master_field(VSTREAM *fp, int mode,
int in_daemon_options;
int need_parens;
+ if (mode & PCF_SHOW_JSON) {
+ pcf_print_master_field_as_json(fp, mode, masterp, field);
+ return;
+ }
if (pcf_exp_buf == 0)
pcf_exp_buf = vstring_alloc(100);
const char *param_name,
const char *param_value)
{
- if (pcf_exp_buf == 0)
+ if (pcf_exp_buf == 0 && (mode & PCF_SHOW_EVAL))
pcf_exp_buf = vstring_alloc(100);
+ if (pcf_json_buf == 0 && (mode & PCF_SHOW_JSON))
+ pcf_json_buf = vstring_alloc(100);
if (mode & PCF_HIDE_VALUE) {
pcf_print_line(fp, mode, "%s%c%s\n",
if ((mode & PCF_SHOW_EVAL) != 0)
param_value = pcf_expand_parameter_value(pcf_exp_buf, mode,
param_value, masterp);
- if ((mode & PCF_HIDE_NAME) == 0) {
+ if (mode & PCF_SHOW_JSON) {
+ vstream_fprintf(fp, "{\"%s\": ",
+ quote_for_json_var(pcf_json_buf, masterp->name_space,
+ PCF_NAMESP_SEP_STR, param_name,
+ (const char *) 0));
+ vstream_fprintf(fp, "\"%s\"}\n",
+ quote_for_json(pcf_json_buf, param_value, -1));
+ } else if ((mode & PCF_HIDE_NAME) == 0) {
pcf_print_line(fp, mode, "%s%c%s = %s\n",
masterp->name_space, PCF_NAMESP_SEP_CH,
param_name, param_value);
--- /dev/null
+{"config_directory": "."}
+{"mydestination": "$whatever"}
+{"whatever": "example.com"}
--- /dev/null
+{"config_directory": "."}
+{"mydestination": "example.com"}
+{"whatever": "example.com"}
--- /dev/null
+{"foo/unix": "foo unix - n n - 0 other -o mydestination=$whatever -o whatever=example.net"}
+{"12345/inet": "12345 inet n n n - - spawn -o {command_time_limit=2 3} user=nobody argv=/bin/sh -c {sleep 1}"}
--- /dev/null
+{"foo/unix": "foo unix - n n - 0 other -o mydestination=example.net -o whatever=example.net"}
--- /dev/null
+{"foo/unix/service": "foo"}
+{"foo/unix/type": "unix"}
+{"foo/unix/private": "-"}
+{"foo/unix/unprivileged": "n"}
+{"foo/unix/chroot": "n"}
+{"foo/unix/wakeup": "-"}
+{"foo/unix/process_limit": "0"}
+{"foo/unix/command": "other -o mydestination=$whatever -o whatever=example.net"}
+{"12345/inet/service": "12345"}
+{"12345/inet/type": "inet"}
+{"12345/inet/private": "n"}
+{"12345/inet/unprivileged": "n"}
+{"12345/inet/chroot": "n"}
+{"12345/inet/wakeup": "-"}
+{"12345/inet/process_limit": "-"}
+{"12345/inet/command": "spawn -o {command_time_limit=2 3} user=nobody argv=/bin/sh -c {sleep 1}"}
--- /dev/null
+{"foo/unix/service": "foo"}
+{"foo/unix/type": "unix"}
+{"foo/unix/private": "-"}
+{"foo/unix/unprivileged": "n"}
+{"foo/unix/chroot": "n"}
+{"foo/unix/wakeup": "-"}
+{"foo/unix/process_limit": "0"}
+{"foo/unix/command": "other -o mydestination=example.net -o whatever=example.net"}
--- /dev/null
+{"foo/unix/mydestination": "$whatever"}
+{"foo/unix/whatever": "example.net"}
+{"12345/inet/command_time_limit": "2 3"}
--- /dev/null
+{"foo/unix/mydestination": "example.net"}
+{"foo/unix/whatever": "example.net"}
cp $(PROG) ../../bin
tests: test1 test2 fail_test quote_test file_test lmdb_abb_test \
- lmdb_bulk_test lmdb_incr_test cdb_bulk_test mode_conflict_test
+ lmdb_bulk_test lmdb_incr_test cdb_bulk_test mode_conflict_test \
+ json_tests
root_tests:
cmp lmdb_retry lmdb_retry.tmp
rm -f lmdb_retry lmdb_retry.tmp lmdb_retry.lmdb main.cf
+json_tests: json_query_test json_queries_test json_sequence_test \
+ json_header_test json_body_test
+
+json_query_test: $(PROG)
+ # Exercise code in postmap_query().
+ @echo ; echo RUN json_query_test
+ echo '{"foo": "fooval"}' >json_query.tmp
+ $(SHLIB_ENV) ${VALGRIND} ./$(PROG) -jq foo \
+ 'inline:{{foo = fooval}}' | diff json_query.tmp -
+ ($(SHLIB_ENV) ${VALGRIND} ./$(PROG) -jq bar \
+ 'inline:{{foo = fooval}}' || exit 0) | diff /dev/null -
+ rm -f json_query.tmp
+ @echo PASS json_query_test
+
+json_queries_test: $(PROG)
+ # Exercise json formatting in postmap_queries() with non-message input.
+ @echo ; echo RUN json_queries_test
+ echo '{"foo": "fooval"}' >json_queries.tmp
+ (echo foo; echo bar) | $(SHLIB_ENV) ${VALGRIND} \
+ ./$(PROG) -jq - 'inline:{{foo = fooval}}' | \
+ diff json_queries.tmp -
+ rm -f json_queries.tmp
+ @echo PASS json_queries_test
+
+json_sequence_test: $(PROG)
+ # Exercise json formatting in postmap_seq().
+ @echo ; echo RUN json_sequence_test
+ rm -f json_sequence.tmp
+ (echo '{"bar": "barval"}'; echo '{"foo": "fooval"}') |\
+ sort >json_sequence.tmp
+ $(SHLIB_ENV) ${VALGRIND} ./$(PROG) -js \
+ 'inline:{{foo = fooval} {bar = barval}}' | sort | \
+ diff json_sequence.tmp -
+ rm -f json_sequence.tmp
+ @echo PASS json_sequence_test
+
+json_header_test: $(PROG)
+ # Exercise json formatting in postmap_queries() in 'header' mode.
+ @echo ; echo RUN json_header_test
+ echo '{"Subject: test": "got subject"}' > json_header.tmp
+ (echo Subject: test; echo body) | $(SHLIB_ENV) ${VALGRIND} ./$(PROG) \
+ -hjq - 'regexp:{{ /Subject/ got subject} { /./ got other }}' | \
+ diff json_header.tmp -
+ rm -f json_header.tmp
+ @echo PASS json_header_test
+
+json_body_test: $(PROG)
+ # Exercise json formatting in postmap_queries() in 'body' mode.
+ @echo ; echo RUN json_body_test
+ echo '{"body": "got other"}' > json_body.tmp
+ (echo Subject: test; echo body) | $(SHLIB_ENV) ${VALGRIND} ./$(PROG) \
+ -bjq - 'regexp:{{ /Subject/ got subject} { /./ got other }}' | \
+ diff json_body.tmp -
+ rm -f json_body.tmp
+ @echo PASS json_body_test
+
clean:
rm -f *.o *core $(PROG) $(TESTPROG) *.tmp junk *.db main.cf master.cf
/* Postfix lookup table management
/* SYNOPSIS
/* .fi
-/* \fBpostmap\fR [\fB-bfFhimnNoprsuUvw\fR] [\fB-c \fIconfig_dir\fR]
+/* \fBpostmap\fR [\fB-bfFhijmnNoprsuUvw\fR] [\fB-c \fIconfig_dir\fR]
/* [\fB-d \fIkey\fR] [\fB-q \fIkey\fR]
/* [\fIfile_type\fR:]\fIfile_name\fR ...
/* DESCRIPTION
/* Incremental mode. Read entries from standard input and do not
/* truncate an existing database. By default, \fBpostmap\fR(1) creates
/* a new database from the entries in \fBfile_name\fR.
+/* .IP \fB-j\fR
+/* JSON output. Format the output from \fB-q\fR and \fB-s\fR
+/* as one \fB{"\fIkey\fB": "\fIvalue\fB"}\fR object per line.
+/* .sp
+/* This feature is available in Postfix version 3.11 and later.
/* .IP \fB-m\fR
/* Enable MIME parsing with "\fB-b\fR" and "\fB-h\fR".
/* .sp
int found; /* result */
} POSTMAP_KEY_STATE;
+ /*
+ * Global state.
+ */
+int json_output;
+VSTRING *json_key_buf;
+VSTRING *json_val_buf;
+
/* postmap - create or update mapping database */
static void postmap(char *map_type, char *path_name, int postmap_flags,
msg_warn("table %s:%s should return NO RESULT in case of NOT FOUND",
dicts[n]->type, dicts[n]->name);
}
- vstream_printf("%s %s\n", keybuf, value);
+ if (json_output == 0)
+ vstream_printf("%s %s\n", keybuf, value);
+ else
+ vstream_printf("{\"%s\": \"%s\"}\n",
+ quote_for_json(json_key_buf, keybuf, -1),
+ quote_for_json(json_val_buf, value, -1));
+
state->found = 1;
break;
}
msg_warn("table %s:%s should return NO RESULT in case of NOT FOUND",
dicts[n]->type, dicts[n]->name);
}
- vstream_printf("%s %s\n", STR(keybuf), value);
+ if (json_output == 0)
+ vstream_printf("%s %s\n", STR(keybuf), value);
+ else
+ vstream_printf("{\"%s\": \"%s\"}\n",
+ quote_for_json(json_key_buf, STR(keybuf), -1),
+ quote_for_json(json_val_buf, value, -1));
found = 1;
break;
}
msg_warn("table %s:%s should return NO RESULT in case of NOT FOUND",
map_type, map_name);
}
- vstream_printf("%s\n", value);
+ if (json_output == 0)
+ vstream_printf("%s\n", value);
+ else
+ vstream_printf("{\"%s\": \"%s\"}\n",
+ quote_for_json(json_key_buf, key, -1),
+ quote_for_json(json_val_buf, value, -1));
}
switch (dict->error) {
case 0:
}
value = STR(unb64);
}
- vstream_printf("%s %s\n", key, value);
+ if (json_output == 0)
+ vstream_printf("%s %s\n", key, value);
+ else
+ vstream_printf("{\"%s\": \"%s\"}\n",
+ quote_for_json(json_key_buf, key, -1),
+ quote_for_json(json_val_buf, value, -1));
}
if (dict->error)
msg_fatal("table %s:%s: sequence error: %m", dict->type, dict->name);
/*
* Parse JCL.
*/
- while ((ch = GETOPT(argc, argv, "bc:d:fFhimnNopq:rsuUvw")) > 0) {
+ while ((ch = GETOPT(argc, argv, "bc:d:fFhijmnNopq:rsuUvw")) > 0) {
switch (ch) {
default:
usage(argv[0]);
update = 1;
open_flags &= ~O_TRUNC;
break;
+ case 'j':
+ if (json_output == 0) {
+ json_output = 1;
+ json_key_buf = vstring_alloc(100);
+ json_val_buf = vstring_alloc(100);
+ }
+ break;
case 'm':
postmap_flags |= POSTMAP_FLAG_MIME_KEY;
break;
break;
}
}
+ if (json_output && !(sequence || query))
+ msg_fatal("option -j requires -q or -s");
mail_conf_read();
/* Enforce consistent operation of different Postfix parts. */
import_env = mail_parm_split(VAR_IMPORT_ENVIRON, var_import_environ);
/* VSTRING *result,
/* const char *in,
/* ssize_t len)
+/*
+/* char *quote_for_json_var(
+/* VSTRING *result,
+/* const char *in)
/* DESCRIPTION
/* quote_for_json() takes well-formed UTF-8 encoded text,
/* quotes that text compliant with RFC 4627, and returns a
/*
/* quote_for_json_append() appends the output to the result buffer.
/*
+/* quote_for_json_var() takes a null-terminated sequence of
+/* null-terminated arguments and formats them with quote_for_json().
+*
/* Arguments:
/* .IP result
/* Storage for the result, resized automatically.
*/
#include <sys_defs.h>
#include <ctype.h>
+#include <stdarg.h>
#include <string.h>
/*
return (quote_for_json_append(result, text, len));
}
+
+/* quote_for_json_var - quote null-terminated list of null-terminated strings */
+
+char *quote_for_json_var(VSTRING *result,...)
+{
+ VSTRING_RESET(result);
+ const char *in;
+ va_list ap;
+
+ va_start(ap, result);
+ while ((in = va_arg(ap, const char *)) != 0)
+ quote_for_json_append(result, in, -1);
+ return (STR(result));
+}
+
#ifdef TEST
/*
/*
* Utility library.
*/
+#include <argv.h>
#include <msg.h>
#include <msg_vstream.h>
typedef struct TEST_CASE {
const char *label; /* identifies test case */
- char *(*fn) (VSTRING *, const char *, ssize_t);
- const char *input; /* input string */
- ssize_t input_len; /* -1 or input length */
+ int (*action) (const struct TEST_CASE *);
+ union {
+ struct {
+ char *(*fn) (VSTRING *, const char *, ssize_t);
+ const char *input; /* input string */
+ ssize_t input_len; /* -1 or input length */
+ } fixed;
+ struct {
+ char *(*fn) (VSTRING *,...);
+ const char *input;
+ } variadic;
+ } u;
const char *exp_res; /* expected result */
} TEST_CASE;
#define PASS (0)
#define FAIL (1)
+static VSTRING *res_buf;
+
+static int run_fixed_test(const TEST_CASE *tp)
+{
+ int test_fail = 0;
+ char *res;
+
+ res = tp->u.fixed.fn(res_buf, tp->u.fixed.input, tp->u.fixed.input_len);
+ if (strcmp(res, tp->exp_res) != 0) {
+ msg_warn("test case '%s': got '%s', want '%s'",
+ tp->label, res, tp->exp_res);
+ test_fail = 1;
+ }
+ return (test_fail);
+}
+
+static int run_variadic_test(const TEST_CASE *tp)
+{
+ int test_fail = 0;
+ char *res;
+ ARGV *argv = argv_split(tp->u.variadic.input, CHARS_SPACE);
+
+ res = tp->u.variadic.fn(res_buf, argv->argv[0], argv->argv[1],
+ argv->argv[2], argv->argv[3]);
+ if (strcmp(res, tp->exp_res) != 0) {
+ msg_warn("test case '%s': got '%s', want '%s'",
+ tp->label, res, tp->exp_res);
+ test_fail = 1;
+ }
+ argv_free(argv);
+ return (test_fail);
+}
+
/*
* The test cases.
*/
static const TEST_CASE test_cases[] = {
- {"ordinary ASCII text", quote_for_json,
- " abcABC012.,[]{}/", -1, " abcABC012.,[]{}/",
+ {"ordinary ASCII text", run_fixed_test,
+ .u.fixed = {quote_for_json,
+ " abcABC012.,[]{}/", -1}, " abcABC012.,[]{}/",
},
- {"quote_for_json_append", quote_for_json_append,
- "foo", -1, " abcABC012.,[]{}/foo",
+ {"quote_for_json_append", run_fixed_test,
+ .u.fixed = {quote_for_json_append,
+ "foo", -1}, " abcABC012.,[]{}/foo",
},
- {"common control characters", quote_for_json,
- "\b\f\r\n\t", -1, "\\b\\f\\r\\n\\t",
+ {"common control characters", run_fixed_test,
+ .u.fixed = {quote_for_json,
+ "\b\f\r\n\t", -1}, "\\b\\f\\r\\n\\t",
},
- {"uncommon control characters and DEL", quote_for_json,
- "\0\01\037\040\176\177", 6, "\\u0000\\u0001\\u001F ~\\u007F",
+ {"uncommon control characters and DEL", run_fixed_test,
+ .u.fixed = {quote_for_json,
+ "\0\01\037\040\176\177", 6}, "\\u0000\\u0001\\u001F ~\\u007F",
},
- {"malformed UTF-8", quote_for_json,
- "\\*\\uasd\\u007F\x80", -1, "\\\\*\\\\uasd\\\\u007F\x80",
+ {"malformed UTF-8", run_fixed_test,
+ .u.fixed = {quote_for_json,
+ "\\*\\uasd\\u007F\x80", -1}, "\\\\*\\\\uasd\\\\u007F\x80",
+ },
+ {"multiple input strings", run_variadic_test,
+ .u.variadic = {quote_for_json_var, "one - two"},
+ "one-two",
},
0,
};
const TEST_CASE *tp;
int pass = 0;
int fail = 0;
- VSTRING *res_buf = vstring_alloc(100);
msg_vstream_init(sane_basename((VSTRING *) 0, argv[0]), VSTREAM_ERR);
+ res_buf = vstring_alloc(100);
for (tp = test_cases; tp->label != 0; tp++) {
int test_fail = 0;
- char *res;
msg_info("RUN %s", tp->label);
- res = tp->fn(res_buf, tp->input, tp->input_len);
- if (strcmp(res, tp->exp_res) != 0) {
- msg_warn("test case '%s': got '%s', want '%s'",
- tp->label, res, tp->exp_res);
- test_fail = 1;
- }
+ test_fail = tp->action(tp);
if (test_fail) {
fail++;
msg_info("FAIL %s", tp->label);
- test_fail = 1;
} else {
msg_info("PASS %s", tp->label);
pass++;
}
}
msg_info("PASS=%d FAIL=%d", pass, fail);
- vstring_free(res_buf);
exit(fail != 0);
}
extern int strncasecmp_utf8x(int, const char *, const char *, ssize_t);
extern char *quote_for_json(VSTRING *, const char *, ssize_t);
extern char *quote_for_json_append(VSTRING *, const char *, ssize_t);
+extern char *quote_for_json_var(VSTRING *,...);
extern const char *mystrerror(int);
extern char *normalize_ws(char *);