you create no temporary files. Each temporary file adds another
factor to the performance loss.
-We will set up a content filtering program listening on localhost
-port 10025 that receives mail via the SMTP protocol, and that
-submits mail back into Postfix via localhost port 10026.
+We will set up a content filtering program that receives SMTP mail
+via localhost port 10025, and that submits SMTP mail back into
+Postfix via localhost port 10026.
..................................
: Postfix :
smtpd and pickup servers.
When a queue file has content filtering information, the queue
-manager will deliver the mail to the specified content filtering
+manager will deliver the mail to the specified content filter
regardless of its final destination.
The content filter can be set up with the Postfix spawn service,
instead of Postfix, then you must run your filter as a stand-alone
program.
+Note: the localhost port 10025 SMTP server filter should announce
+itself as "220 localhost...", to silence warnings in the log.
+
The /some/where/filter command is most likely a PERL script. PERL
has modules that make talking SMTP easy. The command-line specifies
that mail should be sent back into Postfix via localhost port 10026.
Code cleanup: some queue/transport operations need to be
moved, after the code cleanup of the recipient/concurrency
limit handling. Patrik Rak. Files: *qmgr/qmgr_message.c.
+
+20010301
+
+ Feature: configurable name in syslog output (default:
+ "syslog_name = postfix") so that different Postfix instances
+ can be recognized by their logging. File: global/mail_task.c.
+
+20010313
+
+ Workaround for logic mismatch in nqmgr that was exposed
+ with the introduction of the asynchronous bounce client.
+ Patrik Rak.
+
+20010321
+
+ Workaround: LMTP connection caching never worked for
+ destinations starting with unix: or inet:. File:
+ lmtp/lmtp_connect.c.
+
+20010322
+
+ Portability: Solaris <2.6 does not have srandom() and
+ random() in libc. File: util/rand_sleep.c. It does not
+ have to be cryptographically strong.
+
+ Bugfix: the fast ETRN flush server could not handle [ipaddr]
+ or domain names with one-character hostname part. It should
+ be OK now. File: flush/flush.c.
and watch your syslog file for any error messages.
+ % egrep '(reject|warning|error|fatal|panic):' /some/log/file
+
When it is run for the first time, the Postfix startup shell script
will create a bunch of subdirectories below the Postfix spool
directory.
and watch your syslog file for any error messages.
+ % egrep '(reject|warning|error|fatal|panic):' /some/log/file
+
When it is run for the first time, the Postfix startup shell script
will create a bunch of subdirectories below the Postfix spool
directory.
and watch the syslog file for any complaints from the mail system.
+ % egrep '(reject|warning|error|fatal|panic):' /some/log/file
+
When it is run for the first time, the Postfix startup shell script
will create a bunch of subdirectories below the Postfix spool
directory.
+LINUX PORTABILITY
+=================
+
+On RedHat Linux 7.0, you must install the db3-devel RPM before you
+can compile the Postfix source code.
+
LINUX SYSLOGD PERFORMANCE
=========================
# octets separated by ".".
#
# ACTIONS
-# [45]XX text
+# [45]NN text
# Reject the address etc. that matches the pattern,
# and respond with the numerical code and text.
#
# OK Accept the address etc. that matches the pattern.
#
# restriction...
-# Apply the named UCE restriction (permit, reject,
+# Apply the named UCE restriction(s) (permit, reject,
# reject_unauth_destination, and so on).
#
# REGULAR EXPRESSION TABLES
# $inet_interfaces. If this parameter is defined, then the SMTP server
# will reject mail for unknown local users.
#
-# The local_recipient_maps parameter accepts tables with bare usernames
-# such as unix:passwd.byname and alias maps.
+# If you use the default Postfix local delivery agent for local
+# delivery, uncomment the definition below.
#
# Beware: if the Postfix SMTP server runs chrooted, you may have to
-# copy the passwd database into the jail. This is system dependent.
-#
-# FOR THIS TO WORK, DO NOT SPECIFY VIRTUAL DOMAINS IN MYDESTINATION.
-# MYDESTINATION MUST LIST NON-VIRTUAL DOMAINS ONLY.
+# copy the passwd (not shadow) database into the jail. This is
+# system dependent.
#
#local_recipient_maps = $alias_maps unix:passwd.byname
exit 1
esac
-LOGGER="$command_directory/postlog -t postfix-script"
+LOGGER="$command_directory/postlog -t $MAIL_LOGTAG/postfix-script"
INFO="$LOGGER -p info"
WARN="$LOGGER -p warn"
ERROR="$LOGGER -p error"
exit 1
esac
-LOGGER="$command_directory/postlog -t postfix-script"
+LOGGER="$command_directory/postlog -t $MAIL_LOGTAG/postfix-script"
INFO="$LOGGER -p info"
WARN="$LOGGER -p warn"
ERROR="$LOGGER -p error"
# This file contains example settings of Postfix configuration
# parameters that control compatibility with broken software.
+# The broken_sasl_auth_clients controls inter-operability with SMTP
+# clients that do not recognize that Postfix supports RFC 2554 (AUTH
+# command). Examples of such clients are MicroSoft Outlook Express
+# version 4 and MicroSoft Exchange version 5.0.
+#
+# Specify broken_sasl_auth_clients=yes to have Postfix advertise
+# AUTH support in a non-standard way.
+#
+broken_sasl_auth_clients = no
+
# The ignore_mx_lookup_error parameter controls what happens when a
# name server fails to respond to an MX lookup request. By default,
# Postfix defers delivery and tries again after some delay. Specify
#
syslog_facility = mail
+# The syslog_name parameter specifies the mail system name that is
+# prepended to the process name in syslog records headers, so that
+# "smtpd" becomes "postfix/smtpd".
+#
+# Beware: a non-default syslog_name setting takes effect only
+# after process initialization. Some initialization errors will be
+# logged with the default name, especially errors while parsing
+# the command line and errors while accessing the Postfix main.cf
+# configuration file.
+#
+syslog_name = postfix
+
# The trigger_timeout parameter limits the time to send a trigger to
# a Postfix daemon. This prevents programs from getting stuck when the
# mail system is under heavy load.
octets separated by ".".
<b>ACTIONS</b>
- [<b>45</b>]<i>XX</i> <i>text</i>
+ [<b>45</b>]<i>NN</i> <i>text</i>
Reject the address etc. that matches the pattern,
and respond with the numerical code and text.
<b>OK</b> Accept the address etc. that matches the pattern.
<i>restriction...</i>
- Apply the named UCE restriction (<b>permit</b>, reject,
+ Apply the named UCE restriction(s) (<b>permit</b>, reject,
<b>reject</b><i>_</i><b>unauth</b><i>_</i><b>destination</b>, and so on).
<b>REGULAR</b> <b>EXPRESSION</b> <b>TABLES</b>
<li><a href="#poppers">POP or IMAP problems</a>
+<li><a href="#systems">Problems with specific Operating Systems</a>
+
<li><a href="#warnings">Postfix warnings and error messages</a>
<li><a href="#example_config">Example configurations</a>
<li><a href="#noalias">What does "fatal: open database /etc/aliases.db" mean?</a>
+<li><a href="#noservice">What does "fatal: unknown service: smtp/tcp" mean?</a>
+
<li><a href="#nosuid">sendmail has set-uid root file permissions, or is run from a set-uid root process</a>
+<li><a href="#whoami">sendmail: unable to find out your login name</a>
+
+
+<li><a href="#unknown_virtual_loop">Mail for unknown users in
+virtual domains fails with "mail loops back to myself"</a>
+
+<li><a href="#virtual_relay">Postfix refuses mail for virtual
+domains with "relay access denied"</a>
</ul>
<p>
<li><a href="#skip_greeting">Postfix does not try all the MX addresses</a>
+<li><a href="#noservice">What does "fatal: unknown service: smtp/tcp" mean?</a>
+
</ul>
<a name="local_delivery"><h3>Local (non-virtual) delivery</h3>
</ul>
+<p>
+
+<a name="systems"><h3>Problems with specific Operating Systems</h3>
+
+<p>
+
+<ul>
+
+<li><a href="#compaq">Problems with Compaq</a>
+
+<li><a href="#irix">Problems with IRIX</a>
+
+</ul>
+
+<a name="compaq"><h3>Problems with Compaq</h3>
+
+<ul>
+
+<li><a href="#compaq-chmod">Compaq mail blackhole problem</a>
+
+</ul>
+
+<a name="irix"><h3>Problems with IRIX</h3>
+
+<ul>
+
+<li><a href="#sgistruct">IRIX problems translating IP address to string</a>
+
+</ul>
+
<hr>
<a name="poppers"><h3>POP or IMAP problems</h3>
<hr>
+<a name="whoami"><h3>sendmail: unable to find out your login name</h3>
+
+This message is logged when submitting mail from a process with a
+userid that does not exist in the UNIX password file. Postfix uses
+this information in order to set the envelope sender address.
+
+<p>
+
+The envelope sender address is also the default value for the From:
+header address, when none is specified in the message.
+
+<p>
+
+To fix, specify the envelope sender address on the sendmail command
+line:
+
+<blockquote>
+<pre>
+sendmail -f user@domain ...
+</pre>
+</blockquote>
+
+<hr>
+
<a name="moby-freebsd"><h3>Running hundreds of Postfix processes on FreeBSD</h3></a>
With hundreds of Postfix processes, the kernel will eventually
<a name="delay"><h3>Postfix responds slowly to incoming SMTP connections</h3></a>
-<dl>
+Question:
-<dt>Question:
+<blockquote>
-<dd> My Postfix server is too slow. When I telnet to the SMTP port
+My Postfix server is too slow. When I telnet to the SMTP port
(<tt>telnet hostname 25</tt>), the response comes after 40 seconds.
On the other hand, when I telnet to the the POP port (<tt>telnet
hostname 110</tt>) the response comes with no delay.
+</blockquote>
+
<p>
-<dt>Answer:
+Answer:
-<dd>
+<blockquote>
-This is a DNS configuration problem. Postfix tries to resolve the
-SMTP client IP address to a hostname. Apparently, your POP server
-does not look up POP clients.
+You have a name service problem.
<p>
-The fix is to properly configure the naming service. If you can't
-have every host in the DNS, then configure the mail server to look
-in /etc/hosts before the DNS, and specify the clients in /etc/hosts.
+Postfix calls the C library routines <b>gethostbyname()</b> and
+<b>gethostbyaddr()</b> in order to find out the SMTP client hostname.
+These library routines use several system configuration files in
+order to satisfy the request. They may in fact end up calling the
+DNS for reasons that are not under control by Postfix.
-</dl>
+<p>
+
+Depending on your system, these controlling files can be named
+<b>/etc/nsswitch.conf</b>, <b>/etc/svcorder</b>, <b>/etc/host.conf</b>
+or otherwise. Those files specify whether the C library routines
+will use local <b>/etc/hosts</b> before or after DNS.
+
+</blockquote>
<hr>
Execute the command <b>postfix reload</b> to make the change
effective immediately.
+<a name="noservice"><h3>What does "fatal: unknown service: smtp/tcp"
+mean?</h3>
+
+The Postfix <b>/etc/postfix/master.cf</b> file specifies that the
+Postfix SMTP client runs inside a <b>chroot</b> environment. However,
+the files necessary for that mode of operation are not installed
+below <b>/var/spool/postfix</b>.
+
+<p>
+
+Enabling <b>chroot</b> operation adds a non-trivial barrier for
+system penetrators.
+
+<p>
+
+Two solutions:
+
+<ul>
+
+<li> Disable the <b>chroot</b> in <b>/etc/postfix/master.cf</b>
+(and issue <b>postfix reload</b> when done).
+
+<p>
+
+<li>Install the necessary files for <b>chroot</b> operation.
+Instructions are given in the source code distribution, in the
+<b>examples/chroot-setup</b> directory.
+
+</ul>
<hr>
<a name="root"> <h3>Root's mail is delivered to nobody</h3>
<p>
+Sendmail-style virtual domains are not supported in Postfix versions
+released before 20001118.
+
+<p>
+
Be sure to follow instructions in the <a href="virtual.5.html">
virtual</a> manual page.
<hr>
+<a name="compaq-chmod"><h3>Compaq mail blackhole problem</h3>
+
+On some Compaq Tru64 UNIX configurations, Postfix will receive mail
+and then nothing happens. The mail does not even show up with the
+<b>mailq</b> command.
+
+<p>
+
+Postfix sets the execute bit on a queue file to indicate that it
+is done receiving a message. As long as a queue file does not have
+the execute bit set, Postfix will ignore it as "mail still being
+received".
+
+<p>
+
+With enhanced security enabled, Compaq Tru64 UNIX has a feature
+that disallows non-superuser attempts to set the execute bit on a
+queuefile. Unfortunately, Postfix is never informed that such
+attempts fail, and mail seems to disappear into a black hole.
+
+<p>
+
+Postfix could be modified to use some other bit than the execute
+bit, but that might equally well fail on other systems. Another
+possibility is to allow non-superusers to set the execute bit on
+files, and to mount the Postfix queue file system with the
+<b>noexec</b> option or equivalent.
+
+<hr>
+
<a href="index.html">Up one level</a> | Postfix FAQ
</body>
<b>DESCRIPTION</b>
The <b>pipe</b> daemon processes requests from the Postfix queue
- manager to deliver messages to external commands. Each
- delivery request specifies a queue file, a sender address,
- a domain or host to deliver to, and one or more recipi-
- ents. This program expects to be run from the <a href="master.8.html"><b>master</b>(8)</a>
- process manager.
+ manager to deliver messages to external commands. This
+ program expects to be run from the <a href="master.8.html"><b>master</b>(8)</a> process man-
+ ager.
The <b>pipe</b> daemon updates queue files and marks recipients
as finished, or it informs the queue manager that delivery
reports are sent to the <a href="bounce.8.html"><b>bounce</b>(8)</a> or <a href="defer.8.html"><b>defer</b>(8)</a> daemon as
appropriate.
+<b>SINGLE-RECIPIENT</b> <b>DELIVERY</b>
+ Some external commands cannot handle more than one recipi-
+ ent per delivery request. Examples of such transports are
+ pagers, fax machines, and so on.
+
+ To prevent Postfix from sending multiple recipients per
+ delivery request, specify
+
+ <i>transport_</i><b>destination</b><i>_</i><b>recipient</b><i>_</i><b>limit</b> <b>=</b> <b>1</b>
+
+ in the Postfix <b>main.cf</b> file, where <i>transport</i> is the name
+ in the first column of the Postfix <b>master.cf</b> entry for the
+ pipe-based delivery transport.
+
<b>COMMAND</b> <b>ATTRIBUTE</b> <b>SYNTAX</b>
The external command attributes are given in the <b>master.cf</b>
file at the end of a service definition. The syntax is as
follows:
<b>flags=BFR.</b>> (optional)
- Optional message processing flags. By default, a
+ Optional message processing flags. By default, a
message is copied unchanged.
- <b>B</b> Append a blank line at the end of each mes-
- sage. This is required by some mail user
- agents that recognize "<b>From</b> " lines only
+ <b>B</b> Append a blank line at the end of each mes-
+ sage. This is required by some mail user
+ agents that recognize "<b>From</b> " lines only
when preceded by a blank line.
- <b>F</b> Prepend a "<b>From</b> <i>sender</i> <i>time_stamp</i>" envelope
- header to the message content. This is
+ <b>F</b> Prepend a "<b>From</b> <i>sender</i> <i>time_stamp</i>" envelope
+ header to the message content. This is
expected by, for example, <b>UUCP</b> software.
- <b>R</b> Prepend a <b>Return-Path:</b> message header with
+ <b>R</b> Prepend a <b>Return-Path:</b> message header with
the envelope sender address.
- <b>.</b> Prepend <b>.</b> to lines starting with "<b>.</b>". This
- is needed by, for example, <b>BSMTP</b> software.
+ <b>.</b> Prepend <b>.</b> to lines starting with "<b>.</b>". This
- > Prepend > to lines starting with "<b>From</b> ".
- This is expected by, for example, <b>UUCP</b> soft-
- ware.
- <b>user</b>=<i>username</i> (required)
- <b>user</b>=<i>username</i>:<i>groupname</i>
- The external command is executed with the rights of
- the specified <i>username</i>. The software refuses to
- execute commands with root privileges, or with the
+ 1
- 1
+PIPE(8) PIPE(8)
+ is needed by, for example, <b>BSMTP</b> software.
-PIPE(8) PIPE(8)
+ > Prepend > to lines starting with "<b>From</b> ".
+ This is expected by, for example, <b>UUCP</b> soft-
+ ware.
+ <b>user</b>=<i>username</i> (required)
- privileges of the mail system owner. If <i>groupname</i>
- is specified, the corresponding group ID is used
+ <b>user</b>=<i>username</i>:<i>groupname</i>
+ The external command is executed with the rights of
+ the specified <i>username</i>. The software refuses to
+ execute commands with root privileges, or with the
+ privileges of the mail system owner. If <i>groupname</i>
+ is specified, the corresponding group ID is used
instead of the group ID of <i>username</i>.
<b>eol=string</b> (default: <b>\n</b>)
- The output record delimiter. Typically one would
- use either <b>\r\n</b> or <b>\n</b>. The usual C-style backslash
- escape sequences are recognized: <b>\a</b> <b>\b</b> <b>\f</b> <b>\n</b> <b>\r</b> <b>\t</b>
+ The output record delimiter. Typically one would
+ use either <b>\r\n</b> or <b>\n</b>. The usual C-style backslash
+ escape sequences are recognized: <b>\a</b> <b>\b</b> <b>\f</b> <b>\n</b> <b>\r</b> <b>\t</b>
<b>\v</b> <b>\</b><i>octal</i> and <b>\\</b>.
<b>size</b>=<i>size_limit</i> (optional)
will be bounced back to the sender.
<b>argv</b>=<i>command</i>... (required)
- The command to be executed. This must be specified
+ The command to be executed. This must be specified
as the last command attribute. The command is exe-
cuted directly, i.e. without interpretation of
- shell meta characters by a shell command inter-
+ shell meta characters by a shell command inter-
preter.
In the command argument vector, the following
macros are recognized and replaced with correspond-
- ing information from the Postfix queue manager
+ ing information from the Postfix queue manager
delivery request:
<b>${extension</b>}
- This macro expands to the extension part of
- a recipient address. For example, with an
+ This macro expands to the extension part of
+ a recipient address. For example, with an
address <i>user+foo@domain</i> the extension is
- <i>foo</i>. A command-line argument that contains
- <b>${extension</b>} expands into as many command-
+ <i>foo</i>.
+
+ A command-line argument that contains
+ <b>${extension</b>} expands into as many command-
line arguments as there are recipients.
<b>${mailbox</b>}
- This macro expands to the complete local
- part of a recipient address. For example,
- with an address <i>user+foo@domain</i> the mailbox
- is <i>user+foo</i>. A command-line argument that
- contains <b>${mailbox</b>} expands into as many
- command-line arguments as there are recipi-
- ents.
+ This macro expands to the complete local
+ part of a recipient address. For example,
+ with an address <i>user+foo@domain</i> the mailbox
+ is <i>user+foo</i>.
- <b>${nexthop</b>}
- This macro expands to the next-hop hostname.
-
- <b>${recipient</b>}
- This macro expands to the complete recipient
- address. A command-line argument that con-
- tains <b>${recipient</b>} expands into as many com-
- mand-line arguments as there are recipients.
-
- <b>${sender</b>}
- This macro expands to the envelope sender
PIPE(8) PIPE(8)
+ A command-line argument that contains
+ <b>${mailbox</b>} expands into as many command-line
+ arguments as there are recipients.
+
+ <b>${nexthop</b>}
+ This macro expands to the next-hop hostname.
+
+ <b>${recipient</b>}
+ This macro expands to the complete recipient
+ address.
+
+ A command-line argument that contains
+ <b>${recipient</b>} expands into as many command-
+ line arguments as there are recipients.
+
+ <b>${sender</b>}
+ This macro expands to the envelope sender
address.
<b>${size</b>}
- This macro expands to Postfix's idea of the
- message size, which is an approximation of
+ This macro expands to Postfix's idea of the
+ message size, which is an approximation of
the size of the message as delivered.
<b>${user</b>}
This macro expands to the username part of a
- recipient address. For example, with an
+ recipient address. For example, with an
address <i>user+foo@domain</i> the username part is
- <i>user</i>. A command-line argument that contains
- <b>${user</b>} expands into as many command-line
+ <i>user</i>.
+
+ A command-line argument that contains
+ <b>${user</b>} expands into as many command-line
arguments as there are recipients.
- In addition to the form ${<i>name</i>}, the forms $<i>name</i> and
- $(<i>name</i>) are also recognized. Specify <b>$$</b> where a single <b>$</b>
+ In addition to the form ${<i>name</i>}, the forms $<i>name</i> and
+ $(<i>name</i>) are also recognized. Specify <b>$$</b> where a single <b>$</b>
is wanted.
<b>DIAGNOSTICS</b>
- Command exit status codes are expected to follow the con-
+ Command exit status codes are expected to follow the con-
ventions defined in <<b>sysexits.h</b>>.
- Problems and transactions are logged to <b>syslogd</b>(8). Cor-
- rupted message files are marked so that the queue manager
+ Problems and transactions are logged to <b>syslogd</b>(8). Cor-
+ rupted message files are marked so that the queue manager
can move them to the <b>corrupt</b> queue for further inspection.
<b>SECURITY</b>
- This program needs a dual personality 1) to access the
- private Postfix queue and IPC mechanisms, and 2) to exe-
+ This program needs a dual personality 1) to access the
+ private Postfix queue and IPC mechanisms, and 2) to exe-
cute external commands as the specified user. It is there-
fore security sensitive.
<b>CONFIGURATION</b> <b>PARAMETERS</b>
- The following <b>main.cf</b> parameters are especially relevant
- to this program. See the Postfix <b>main.cf</b> file for syntax
- details and for default values. Use the <b>postfix</b> <b>reload</b>
+ The following <b>main.cf</b> parameters are especially relevant
+
+
+
+ 3
+
+
+
+
+
+PIPE(8) PIPE(8)
+
+
+ to this program. See the Postfix <b>main.cf</b> file for syntax
+ details and for default values. Use the <b>postfix</b> <b>reload</b>
command after a configuration change.
<b>Miscellaneous</b>
exported to non-Postfix processes.
<b>mail</b><i>_</i><b>owner</b>
- The process privileges used while not running an
+ The process privileges used while not running an
external command.
<b>Resource</b> <b>controls</b>
- In the text below, <i>transport</i> is the first field in a <b>mas-</b>
+ In the text below, <i>transport</i> is the first field in a <b>mas-</b>
<b>ter.cf</b> entry.
<i>transport_</i><b>destination</b><i>_</i><b>concurrency</b><i>_</i><b>limit</b>
Limit the number of parallel deliveries to the same
-
-
-
- 3
-
-
-
-
-
-PIPE(8) PIPE(8)
-
-
- destination, for delivery via the named <i>transport</i>.
- The default limit is taken from the <b>default</b><i>_</i><b>desti-</b>
- <b>nation</b><i>_</i><b>concurrency</b><i>_</i><b>limit</b> parameter. The limit is
+ destination, for delivery via the named <i>transport</i>.
+ The default limit is taken from the <b>default</b><i>_</i><b>desti-</b>
+ <b>nation</b><i>_</i><b>concurrency</b><i>_</i><b>limit</b> parameter. The limit is
enforced by the Postfix queue manager.
<i>transport_</i><b>destination</b><i>_</i><b>recipient</b><i>_</i><b>limit</b>
- Limit the number of recipients per message deliv-
- ery, for delivery via the named <i>transport</i>. The
- default limit is taken from the <b>default</b><i>_</i><b>destina-</b>
- <b>tion</b><i>_</i><b>recipient</b><i>_</i><b>limit</b> parameter. The limit is
+ Limit the number of recipients per message deliv-
+ ery, for delivery via the named <i>transport</i>. The
+ default limit is taken from the <b>default</b><i>_</i><b>destina-</b>
+ <b>tion</b><i>_</i><b>recipient</b><i>_</i><b>limit</b> parameter. The limit is
enforced by the Postfix queue manager.
<i>transport_</i><b>time</b><i>_</i><b>limit</b>
- Limit the time for delivery to external command,
- for delivery via the named <b>transport</b>. The default
- limit is taken from the <b>command</b><i>_</i><b>time</b><i>_</i><b>limit</b> parame-
- ter. The limit is enforced by the Postfix queue
+ Limit the time for delivery to external command,
+ for delivery via the named <b>transport</b>. The default
+ limit is taken from the <b>command</b><i>_</i><b>time</b><i>_</i><b>limit</b> parame-
+ ter. The limit is enforced by the Postfix queue
manager.
<b>SEE</b> <b>ALSO</b>
syslogd(8) system logging
<b>LICENSE</b>
- The Secure Mailer license must be distributed with this
+ The Secure Mailer license must be distributed with this
software.
<b>AUTHOR(S)</b>
Yorktown Heights, NY 10598, USA
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
4
.nf
.ad
.fi
-.IP "[\fB45\fR]\fIXX text\fR"
+.IP "[\fB45\fR]\fINN text\fR"
Reject the address etc. that matches the pattern, and respond with
the numerical code and text.
.IP \fBREJECT\fR
.IP \fBOK\fR
Accept the address etc. that matches the pattern.
.IP \fIrestriction...\fR
-Apply the named UCE restriction (\fBpermit\fR, \fRreject\fR,
+Apply the named UCE restriction(s) (\fBpermit\fR, \fRreject\fR,
\fBreject_unauth_destination\fR, and so on).
.SH REGULAR EXPRESSION TABLES
.na
.ad
.fi
The \fBpipe\fR daemon processes requests from the Postfix queue
-manager to deliver messages to external commands. Each delivery
-request specifies a queue file, a sender address, a domain or host
-to deliver to, and one or more recipients.
+manager to deliver messages to external commands.
This program expects to be run from the \fBmaster\fR(8) process
manager.
as finished, or it informs the queue manager that delivery should
be tried again at a later time. Delivery problem reports are sent
to the \fBbounce\fR(8) or \fBdefer\fR(8) daemon as appropriate.
+.SH SINGLE-RECIPIENT DELIVERY
+.na
+.nf
+.ad
+.fi
+Some external commands cannot handle more than one recipient
+per delivery request. Examples of such transports are pagers,
+fax machines, and so on.
+
+To prevent Postfix from sending multiple recipients per delivery
+request, specify
+
+.ti +4
+\fItransport\fB_destination_recipient_limit = 1\fR
+
+in the Postfix \fBmain.cf\fR file, where \fItransport\fR
+is the name in the first column of the Postfix \fBmaster.cf\fR
+entry for the pipe-based delivery transport.
.SH COMMAND ATTRIBUTE SYNTAX
.na
.nf
This macro expands to the extension part of a recipient address.
For example, with an address \fIuser+foo@domain\fR the extension is
\fIfoo\fR.
+.sp
A command-line argument that contains \fB${\fBextension\fR}\fR expands
into as many command-line arguments as there are recipients.
.IP \fB${\fBmailbox\fR}\fR
This macro expands to the complete local part of a recipient address.
For example, with an address \fIuser+foo@domain\fR the mailbox is
\fIuser+foo\fR.
+.sp
A command-line argument that contains \fB${\fBmailbox\fR}\fR
expands into as many command-line arguments as there are recipients.
.IP \fB${\fBnexthop\fR}\fR
This macro expands to the next-hop hostname.
.IP \fB${\fBrecipient\fR}\fR
This macro expands to the complete recipient address.
+.sp
A command-line argument that contains \fB${\fBrecipient\fR}\fR
expands into as many command-line arguments as there are recipients.
.IP \fB${\fBsender\fR}\fR
This macro expands to the username part of a recipient address.
For example, with an address \fIuser+foo@domain\fR the username
part is \fIuser\fR.
+.sp
A command-line argument that contains \fB${\fBuser\fR}\fR expands
into as many command-line arguments as there are recipients.
.RE
# ACTIONS
# .ad
# .fi
-# .IP "[\fB45\fR]\fIXX text\fR"
+# .IP "[\fB45\fR]\fINN text\fR"
# Reject the address etc. that matches the pattern, and respond with
# the numerical code and text.
# .IP \fBREJECT\fR
# .IP \fBOK\fR
# Accept the address etc. that matches the pattern.
# .IP \fIrestriction...\fR
-# Apply the named UCE restriction (\fBpermit\fR, \fRreject\fR,
+# Apply the named UCE restriction(s) (\fBpermit\fR, \fRreject\fR,
# \fBreject_unauth_destination\fR, and so on).
# REGULAR EXPRESSION TABLES
# .ad
* straightforward.
*/
if (vstream_fflush(state->dst)) {
- msg_warn("%s: write queue file: %m", state->queue_id);
if (errno == EFBIG) {
msg_warn("%s: queue file size limit exceeded", state->queue_id);
state->errs |= CLEANUP_STAT_SIZE;
#include <stdlib.h>
#include <utime.h>
#include <errno.h>
+#include <ctype.h>
/* Utility library. */
/*
* Silly little macros.
*/
-
#define STR(x) vstring_str(x)
#define STREQ(x,y) (strcmp(x,y) == 0)
+ /*
+ * Forward declarations for where we broke routines along their name space
+ * domain boundaries (actually, hostnames versus safe-to-use pathnames).
+ */
+static int flush_add_path(const char *, const char *);
+static int flush_send_path(const char *);
+
+/* flush_site_to_path - convert domain or [addr] to harmless string */
+
+static VSTRING *flush_site_to_path(VSTRING *path, const char *site)
+{
+ int ch;
+
+ /*
+ * Allocate buffer on the fly; caller still needs to clean up.
+ */
+ if (path == 0)
+ path = vstring_alloc(10);
+
+ /*
+ * Convert character values to hexadecimal.
+ */
+ while ((ch = *(unsigned const char *) site++) != 0)
+ if (ISALNUM(ch))
+ VSTRING_ADDCH(path, ch);
+ else
+ VSTRING_ADDCH(path, '_');
+ VSTRING_TERMINATE(path);
+
+ if (msg_verbose)
+ msg_info("site %s to path %s", site, STR(path));
+
+ return (path);
+}
+
/* flush_policy_ok - check logging policy */
static int flush_policy_ok(const char *site)
return (domain_list_match(flush_domains, site));
}
-/* flush_add_service - append queue ID to per-site fast flush log */
+/* flush_add_service - append queue ID to per-site fast flush logfile */
static int flush_add_service(const char *site, const char *queue_id)
{
char *myname = "flush_add_service";
- VSTREAM *log;
+ VSTRING *site_path;
+ int status;
if (msg_verbose)
msg_info("%s: site %s queue_id %s", myname, site, queue_id);
if (flush_policy_ok(site) == 0)
return (FLUSH_STAT_OK);
+ /*
+ * Map site to path and update log.
+ */
+ site_path = flush_site_to_path((VSTRING *) 0, site);
+ status = flush_add_path(STR(site_path), queue_id);
+ vstring_free(site_path);
+
+ return (status);
+}
+
+/* flush_add_path - add record to log */
+
+static int flush_add_path(const char *path, const char *queue_id)
+{
+ char *myname = "flush_add_path";
+ VSTREAM *log;
+
/*
* Open the logfile or bust.
*/
- if ((log = mail_queue_open(MAIL_QUEUE_FLUSH, site,
+ if ((log = mail_queue_open(MAIL_QUEUE_FLUSH, path,
O_CREAT | O_APPEND | O_WRONLY, 0600)) == 0)
- msg_fatal("%s: open fast flush log for site %s: %m",
- myname, site);
+ msg_fatal("%s: open fast flush logfile %s: %m", myname, path);
/*
* We must lock the logfile, so that we don't lose information due to
* will eventually take care of the problem, but it will take a while.
*/
if (myflock(vstream_fileno(log), INTERNAL_LOCK, MYFLOCK_OP_EXCLUSIVE) < 0)
- msg_fatal("%s: lock fast flush log for site %s: %m", myname, site);
+ msg_fatal("%s: lock fast flush logfile %s: %m", myname, path);
/*
* Append the queue ID. With 15 bits if microsecond time, a queue ID is
* Clean up.
*/
if (myflock(vstream_fileno(log), INTERNAL_LOCK, MYFLOCK_OP_NONE) < 0)
- msg_fatal("%s: unlock fast flush log for site %s: %m",
- myname, site);
+ msg_fatal("%s: unlock fast flush logfile %s: %m", myname, path);
if (vstream_fclose(log) != 0)
- msg_warn("write fast flush log for site %s: %m", site);
+ msg_warn("write fast flush logfile %s: %m", path);
return (FLUSH_STAT_OK);
}
static int flush_send_service(const char *site)
{
char *myname = "flush_send_service";
+ VSTRING *site_path;
+ int status;
+
+ if (msg_verbose)
+ msg_info("%s: site %s", myname, site);
+
+ /*
+ * If this site is not eligible for logging, deliver all queued mail.
+ */
+ if (flush_policy_ok(site) == 0)
+ return (mail_flush_deferred());
+
+ /*
+ * Map site name to path name and flush the log.
+ */
+ site_path = flush_site_to_path((VSTRING *) 0, site);
+ status = flush_send_path(STR(site_path));
+ vstring_free(site_path);
+
+ return (status);
+}
+
+/* flush_send_path - flush logfile file */
+
+static int flush_send_path(const char *path)
+{
+ const char *myname = "flush_send_path";
VSTRING *queue_id;
VSTRING *queue_file;
VSTREAM *log;
HTABLE *dup_filter;
int count;
- if (msg_verbose)
- msg_info("%s: site %s", myname, site);
-
- /*
- * If this site is not eligible for logging, deliver all queued mail.
- */
- if (flush_policy_ok(site) == 0)
- return (mail_flush_deferred());
-
/*
* Open the logfile. If the file does not exist, then there is no queued
* mail for this destination.
*/
- if ((log = mail_queue_open(MAIL_QUEUE_FLUSH, site, O_RDWR, 0600)) == 0) {
+ if ((log = mail_queue_open(MAIL_QUEUE_FLUSH, path, O_RDWR, 0600)) == 0) {
if (errno != ENOENT)
- msg_fatal("%s: open fast flush log for site %s: %m", myname, site);
+ msg_fatal("%s: open fast flush logfile %s: %m", myname, path);
return (FLUSH_STAT_OK);
}
* watchdog will take care of it.
*/
if (myflock(vstream_fileno(log), INTERNAL_LOCK, MYFLOCK_OP_EXCLUSIVE) < 0)
- msg_fatal("%s: lock fast flush log for site %s: %m", myname, site);
+ msg_fatal("%s: lock fast flush logfile %s: %m", myname, path);
/*
* This is the part that dominates running time: schedule the listed
tbuf.actime = tbuf.modtime = event_time();
for (count = 0; vstring_get_nonl(queue_id, log) != VSTREAM_EOF; count++) {
if (!mail_queue_id_ok(STR(queue_id))) {
- msg_warn("bad queue id \"%.30s...\" in fast flush log for site %s",
- STR(queue_id), site);
+ msg_warn("bad queue id \"%.30s...\" in fast flush logfile %s",
+ STR(queue_id), path);
continue;
}
if (dup_filter->used >= FLUSH_DUP_FILTER_SIZE
|| htable_find(dup_filter, STR(queue_id)) == 0) {
if (msg_verbose)
- msg_info("%s: site %s: update %s time stamps",
- myname, site, STR(queue_id));
+ msg_info("%s: logfile %s: update queue file %s time stamps",
+ myname, path, STR(queue_id));
if (dup_filter->used <= FLUSH_DUP_FILTER_SIZE)
htable_enter(dup_filter, STR(queue_id), 0);
}
} else {
if (msg_verbose)
- msg_info("%s: site %s: skip file %s as duplicate",
- myname, site, STR(queue_file));
+ msg_info("%s: logfile %s: skip queue file %s as duplicate",
+ myname, path, STR(queue_file));
}
}
htable_free(dup_filter, (void (*) (char *)) 0);
* Truncate the fast flush log.
*/
if (count > 0 && ftruncate(vstream_fileno(log), (off_t) 0) < 0)
- msg_fatal("%s: truncate fast flush log for site %s: %m", myname, site);
+ msg_fatal("%s: truncate fast flush logfile %s: %m", myname, path);
/*
* Request delivery and clean up.
*/
if (myflock(vstream_fileno(log), INTERNAL_LOCK, MYFLOCK_OP_NONE) < 0)
- msg_fatal("%s: unlock fast flush log for site %s: %m",
- myname, site);
+ msg_fatal("%s: unlock fast flush logfile %s: %m", myname, path);
if (vstream_fclose(log) != 0)
- msg_warn("read fast flush log for site %s: %m", site);
+ msg_warn("%s: read fast flush logfile %s: %m", myname, path);
if (count > 0) {
if (msg_verbose)
- msg_info("%s: requesting delivery for site %s", myname, site);
+ msg_info("%s: requesting delivery for logfile %s", myname, path);
mail_trigger(MAIL_CLASS_PUBLIC, MAIL_SERVICE_QUEUE,
qmgr_trigger, sizeof(qmgr_trigger));
}
{
char *myname = "flush_refresh_service";
SCAN_DIR *scan;
- char *site;
+ char *site_path;
struct stat st;
VSTRING *path = vstring_alloc(10);
scan = scan_dir_open(MAIL_QUEUE_FLUSH);
- while ((site = mail_scan_dir_next(scan)) != 0) {
- if (!mail_queue_id_ok(site))
+ while ((site_path = mail_scan_dir_next(scan)) != 0) {
+ if (!mail_queue_id_ok(site_path))
continue; /* XXX grumble. */
- mail_queue_path(path, MAIL_QUEUE_FLUSH, site);
- if (flush_policy_ok(site) == 0) {
- if (unlink(STR(path)) < 0)
- msg_warn("remove %s: %m", STR(path));
- else if (msg_verbose)
- msg_info("%s: spurious fast flush logfile name: %s",
- myname, site);
- continue;
- }
+ mail_queue_path(path, MAIL_QUEUE_FLUSH, site_path);
if (stat(STR(path), &st) < 0) {
if (errno != ENOENT)
msg_warn("%s: stat %s: %m", myname, STR(path));
msg_info("%s: unlink %s, empty and unchanged for %d days",
myname, STR(path), var_fflush_purge / 86400);
} else if (msg_verbose)
- msg_info("%s: skip site %s - empty log", myname, site);
+ msg_info("%s: skip logfile %s - empty log", myname, site_path);
} else if (st.st_atime + max_age < event_time()) {
if (msg_verbose)
- msg_info("%s: flush site %s", myname, site);
- flush_send_service(site);
+ msg_info("%s: flush logfile %s", myname, site_path);
+ flush_send_path(site_path);
} else {
if (msg_verbose)
- msg_info("%s: skip site %s, unread for <%d hours(s) ",
- myname, site, max_age / 3600);
+ msg_info("%s: skip logfile %s, unread for <%d hours(s) ",
+ myname, site_path, max_age / 3600);
}
}
scan_dir_close(scan);
site = vstring_alloc(10);
queue_id = vstring_alloc(10);
if (mail_command_read(client_stream, "%s %s", site, queue_id) == 2
- && valid_hostname(STR(site), DONT_GRIPE)
&& mail_queue_id_ok(STR(queue_id)))
status = flush_add_service(lowercase(STR(site)), STR(queue_id));
mail_print(client_stream, "%d", status);
} else if (STREQ(STR(request), FLUSH_REQ_SEND)) {
site = vstring_alloc(10);
- if (mail_command_read(client_stream, "%s", site) == 1
- && valid_hostname(STR(site), DONT_GRIPE))
+ if (mail_command_read(client_stream, "%s", site) == 1)
status = flush_send_service(lowercase(STR(site)));
mail_print(client_stream, "%d", status);
} else if (STREQ(STR(request), FLUSH_REQ_REFRESH)
mail_params.o: ../../include/stringops.h
mail_params.o: ../../include/vstring.h
mail_params.o: ../../include/vbuf.h
+mail_params.o: ../../include/safe.h
mail_params.o: mynetworks.h
mail_params.o: mail_conf.h
mail_params.o: mail_version.h
mail_task.o: ../../include/sys_defs.h
mail_task.o: ../../include/vstring.h
mail_task.o: ../../include/vbuf.h
+mail_task.o: ../../include/safe.h
mail_task.o: mail_params.h
+mail_task.o: mail_conf.h
mail_task.o: mail_task.h
mail_trigger.o: mail_trigger.c
mail_trigger.o: ../../include/sys_defs.h
*/
#define CONF_ENV_PATH "MAIL_CONFIG" /* config database */
#define CONF_ENV_VERB "MAIL_VERBOSE" /* verbose mode on */
-#define CONF_ENV_DEBUG "MAIL_DEBUG" /* verbose mode on */
+#define CONF_ENV_DEBUG "MAIL_DEBUG" /* live debugging */
+#define CONF_ENV_LOGTAG "MAIL_LOGTAG" /* instance name */
/*
* External representation for booleans.
/* char *var_transit_origin;
/* char *var_transit_dest;
/* char *var_mail_name;
+/* char *var_syslog_name;
/* char *var_mail_owner;
/* uid_t var_owner_uid;
/* gid_t var_owner_gid;
#include <sys_defs.h>
#include <unistd.h>
+#include <stdlib.h>
#include <string.h>
#include <pwd.h>
#include <time.h>
#include <get_hostname.h>
#include <valid_hostname.h>
#include <stringops.h>
+#include <safe.h>
/* Global library. */
char *var_transit_origin;
char *var_transit_dest;
char *var_mail_name;
+char *var_syslog_name;
char *var_mail_owner;
uid_t var_owner_uid;
gid_t var_owner_gid;
};
static CONFIG_STR_TABLE other_str_defaults[] = {
VAR_MAIL_NAME, DEF_MAIL_NAME, &var_mail_name, 1, 0,
+ VAR_SYSLOG_NAME, DEF_SYSLOG_NAME, &var_syslog_name, 1, 0,
VAR_MAIL_OWNER, DEF_MAIL_OWNER, &var_mail_owner, 1, 0,
VAR_MYDEST, DEF_MYDEST, &var_mydest, 0, 0,
VAR_MYORIGIN, DEF_MYORIGIN, &var_myorigin, 1, 0,
VAR_OWNREQ_SPECIAL, DEF_OWNREQ_SPECIAL, &var_ownreq_special,
0,
};
+const char *cp;
/*
* Extract syslog_facility early, so that from here on all errors are
*/
time(&var_starttime);
+ /*
+ * Export the syslog name so children can inherit and use it before they
+ * have initialized.
+ */
+ if ((cp = safe_getenv(CONF_ENV_LOGTAG)) == 0
+ || strcmp(cp, var_syslog_name) != 0)
+ if (setenv(CONF_ENV_LOGTAG, var_syslog_name, 1) < 0)
+ msg_fatal("setenv %s %s: %m", CONF_ENV_LOGTAG, var_syslog_name);
+
/*
* I have seen this happen just too often.
*/
* and what Postfix exports to the external world.
*/
#define VAR_IMPORT_ENVIRON "import_environment"
-#define DEF_IMPORT_ENVIRON "MAIL_CONFIG MAIL_DEBUG TZ XAUTHORITY DISPLAY"
+#define DEF_IMPORT_ENVIRON "MAIL_CONFIG MAIL_DEBUG MAIL_LOGTAG TZ XAUTHORITY DISPLAY"
extern char *var_import_environ;
#define VAR_EXPORT_ENVIRON "export_environment"
#define DEF_VIRT_MAILBOX_LOCK "fcntl"
extern char *var_virt_mailbox_lock;
+ /*
+ * Distinct logging tag for multiple Postfix instances.
+ */
+#define VAR_SYSLOG_NAME "syslog_name"
+#define DEF_SYSLOG_NAME "postfix"
+extern char *var_syslog_name;
+
/* LICENSE
/* .ad
/* .fi
/* mail_task() enforces consistent naming of mailer processes.
/* It strips pathname information from the process name, and
/* prepends the name of the mail system so that logfile entries
-/* are easier to recognize.
+/* are easier to recognize. The mail system name is specified
+/* with the "syslog_name" configuration parameter.
/*
/* The result is volatile. Make a copy of the result if it is
/* to be used for any appreciable amount of time.
/* Utility library. */
#include <vstring.h>
+#include <safe.h>
/* Global library. */
#include "mail_params.h"
+#include "mail_conf.h"
#include "mail_task.h"
-#define MAIL_TASK_FORMAT "postfix/%s"
-
/* mail_task - clean up and decorate the process name */
const char *mail_task(const char *argv0)
{
static VSTRING *canon_name;
const char *slash;
+ const char *tag;
if (canon_name == 0)
canon_name = vstring_alloc(10);
if ((slash = strrchr(argv0, '/')) != 0)
argv0 = slash + 1;
- vstring_sprintf(canon_name, MAIL_TASK_FORMAT, argv0);
+ if ((tag = safe_getenv(CONF_ENV_LOGTAG)) == 0)
+ tag = DEF_SYSLOG_NAME;
+ vstring_sprintf(canon_name, "%s/%s", tag, argv0);
return (vstring_str(canon_name));
}
* Version of this program.
*/
#define VAR_MAIL_VERSION "mail_version"
-#define DEF_MAIL_VERSION "Snapshot-20010228"
+#define DEF_MAIL_VERSION "Snapshot-20010323"
extern char *var_mail_version;
/* LICENSE
VSTRING_ADDCH(vp, ')');
break;
case TOK822_COMMENT_TEXT:
- tok822_copy_quoted(vp, vstring_str(tp->vstr), "()\\\r\n");
+ tok822_copy_quoted(vp, vstring_str(tp->vstr), "()\\");
break;
case TOK822_QSTRING:
VSTRING_ADDCH(vp, '"');
#include <unistd.h>
#include <vstream.h>
-#include <vstring_vstream.h>
+#include <readlline.h>
/* tok822_print - display token */
TOK822 *list;
VSTRING *buf = vstring_alloc(100);
- while (vstring_fgets_nonl(buf, VSTREAM_IN)) {
+ while (readlline(buf, VSTREAM_IN, (int *) 0, READLL_KEEPNL)) {
if (!isatty(vstream_fileno(VSTREAM_IN)))
vstream_printf(">>>%s<<<\n\n", vstring_str(buf));
list = tok822_parse(vstring_str(buf));
/*
* Disconnect if we're going to a different destination. Discard
* transcript and status information for sending QUIT.
+ *
+ * XXX Should transform nexthop into canonical form (unix:/path or
+ * inet:host:port) before doing connection cache lookup. See also the
+ * connection cache updating code in lmtp_connect.c.
*/
if (strcasecmp(state->session->dest, request->nexthop) != 0) {
lmtp_quit(state);
/* lmtp_connect_unix - connect to UNIX-domain address */
-static LMTP_SESSION *lmtp_connect_unix(const char *addr, VSTRING *why)
+static LMTP_SESSION *lmtp_connect_unix(const char *addr,
+ const char *destination, VSTRING *why)
{
#undef sun
char *myname = "lmtp_connect_unix";
msg_info("%s: trying: %s...", myname, addr);
return (lmtp_connect_sock(sock, (struct sockaddr *) & sun, sizeof(sun),
- addr, addr, addr, why));
+ addr, addr, destination, why));
}
/* lmtp_connect_addr - connect to explicit address */
* XXX Ad-hoc transport parsing and connection management. Some or all
* should be moved away to a reusable library routine so that every
* program benefits from it.
+ *
+ * XXX Should transform destination into canonical form (unix:/path or
+ * inet:host:port before entering it into the connection cache. See also
+ * the connection cache lookup code in lmtp.c.
*/
if (strncmp(destination, "unix:", 5) == 0)
- return (lmtp_connect_unix(destination + 5, why));
+ return (lmtp_connect_unix(destination + 5, destination, why));
if (strncmp(destination, "inet:", 5) == 0)
- destination += 5;
- dest_buf = lmtp_parse_destination(destination, def_service,
- &host, &port);
+ dest_buf = lmtp_parse_destination(destination + 5, def_service,
+ &host, &port);
+ else
+ dest_buf = lmtp_parse_destination(destination, def_service,
+ &host, &port);
if (msg_verbose)
msg_info("%s: connecting to %s port %d", myname, host, ntohs(port));
session = lmtp_connect_host(host, port, destination, why);
master_listen.o: ../../include/vbuf.h
master_listen.o: ../../include/inet_addr_list.h
master_listen.o: ../../include/set_eugid.h
+master_listen.o: ../../include/set_ugid.h
master_listen.o: ../../include/mail_params.h
master_listen.o: master.h
master_proto.o: master_proto.c
#include <stringops.h>
#include <inet_addr_list.h>
#include <set_eugid.h>
+#include <set_ugid.h>
#include <iostuff.h>
/* Global library. */
LOCAL_LISTEN(serv->name, serv->max_proc > var_proc_limit ?
serv->max_proc : var_proc_limit, NON_BLOCKING);
close_on_exec(serv->listen_fd[0], CLOSE_ON_EXEC);
- set_eugid(getuid(), getgid());
+ set_ugid(getuid(), getgid());
break;
/*
set_eugid(var_owner_uid, var_owner_gid);
serv->listen_fd[0] = fifo_listen(serv->name, 0622, NON_BLOCKING);
close_on_exec(serv->listen_fd[0], CLOSE_ON_EXEC);
- set_eugid(getuid(), getgid());
+ set_ugid(getuid(), getgid());
break;
/*
static void master_sigdeath(int sig)
{
- char *myname = "master_sigsetup";
+ char *myname = "master_sigdeath";
struct sigaction action;
pid_t pid = getpid();
tbuf.actime = tbuf.modtime = event_time() + delay;
path = mail_queue_path((VSTRING *) 0, queue_name, queue_id);
- if (utime(path, &tbuf) < 0)
+ if (utime(path, &tbuf) < 0 && errno != ENOENT)
msg_fatal("%s: update %s time stamps: %m", myname, path);
if (mail_queue_rename(queue_id, queue_name, dest_queue)) {
if (errno != ENOENT)
* Try reading in more recipients. Note that we do not try to read them
* as soon as possible as that would decrease the chance of per-site
* recipient grouping. We waited until reading more is really necessary.
- */
- if (message->rcpt_offset != 0 && message->rcpt_limit > message->rcpt_count) {
+ *
+ * XXX Workaround for logic mismatch. The message->refcount test needs
+ * explanation. If the refcount is zero, it means that qmgr_active_done()
+ * is beeing completed asynchronously. In such case, we can't read in
+ * more recipients as bad things would happen after qmgr_active_done()
+ * continues processing. Note that this results in the given job beeing
+ * stalled for some time, but fortunately this particular situation is so
+ * rare that it is not critical. Still we seek for better solution.
+ */
+ if (message->rcpt_offset != 0
+ && message->rcpt_limit > message->rcpt_count
+ && message->refcount > 0) {
qmgr_message_realloc(message);
if (HAS_ENTRIES(job))
return (qmgr_peer_select(job));
/* \fBpipe\fR [generic Postfix daemon options] command_attributes...
/* DESCRIPTION
/* The \fBpipe\fR daemon processes requests from the Postfix queue
-/* manager to deliver messages to external commands. Each delivery
-/* request specifies a queue file, a sender address, a domain or host
-/* to deliver to, and one or more recipients.
+/* manager to deliver messages to external commands.
/* This program expects to be run from the \fBmaster\fR(8) process
/* manager.
/*
/* as finished, or it informs the queue manager that delivery should
/* be tried again at a later time. Delivery problem reports are sent
/* to the \fBbounce\fR(8) or \fBdefer\fR(8) daemon as appropriate.
+/* SINGLE-RECIPIENT DELIVERY
+/* .ad
+/* .fi
+/* Some external commands cannot handle more than one recipient
+/* per delivery request. Examples of such transports are pagers,
+/* fax machines, and so on.
+/*
+/* To prevent Postfix from sending multiple recipients per delivery
+/* request, specify
+/*
+/* .ti +4
+/* \fItransport\fB_destination_recipient_limit = 1\fR
+/*
+/* in the Postfix \fBmain.cf\fR file, where \fItransport\fR
+/* is the name in the first column of the Postfix \fBmaster.cf\fR
+/* entry for the pipe-based delivery transport.
/* COMMAND ATTRIBUTE SYNTAX
/* .ad
/* .fi
/* This macro expands to the extension part of a recipient address.
/* For example, with an address \fIuser+foo@domain\fR the extension is
/* \fIfoo\fR.
+/* .sp
/* A command-line argument that contains \fB${\fBextension\fR}\fR expands
/* into as many command-line arguments as there are recipients.
/* .IP \fB${\fBmailbox\fR}\fR
/* This macro expands to the complete local part of a recipient address.
/* For example, with an address \fIuser+foo@domain\fR the mailbox is
/* \fIuser+foo\fR.
+/* .sp
/* A command-line argument that contains \fB${\fBmailbox\fR}\fR
/* expands into as many command-line arguments as there are recipients.
/* .IP \fB${\fBnexthop\fR}\fR
/* This macro expands to the next-hop hostname.
/* .IP \fB${\fBrecipient\fR}\fR
/* This macro expands to the complete recipient address.
+/* .sp
/* A command-line argument that contains \fB${\fBrecipient\fR}\fR
/* expands into as many command-line arguments as there are recipients.
/* .IP \fB${\fBsender\fR}\fR
/* This macro expands to the username part of a recipient address.
/* For example, with an address \fIuser+foo@domain\fR the username
/* part is \fIuser\fR.
+/* .sp
/* A command-line argument that contains \fB${\fBuser\fR}\fR expands
/* into as many command-line arguments as there are recipients.
/* .RE
#include <vstream.h>
#include <msg_vstream.h>
#include <safe.h>
+#include <events.h>
/* Global library. */
/*
* Kick the service.
*/
- if (mail_trigger(class, service, request, strlen(request)) < 0) {
+ if (mail_trigger(class, service, request, strlen(request) + 1) < 0) {
msg_warn("Cannot contact class %s service %s - perhaps the mail system is down",
class, service);
exit(1);
} else {
+ event_loop(-1);
exit(0);
}
}
tbuf.actime = tbuf.modtime = event_time() + delay;
path = mail_queue_path((VSTRING *) 0, queue_name, queue_id);
- if (utime(path, &tbuf) < 0)
+ if (utime(path, &tbuf) < 0 && errno != ENOENT)
msg_fatal("%s: update %s time stamps: %m", myname, path);
if (mail_queue_rename(queue_id, queue_name, dest_queue)) {
if (errno != ENOENT)
/* a brief connection to it and by writing the contents of the
/* named buffer.
/*
+/* The connection is closed by a background thread. Some kernels
+/* cannot handle client-side disconnect before the server has
+/* received the message.
+/*
/* Arguments:
/* .IP service
/* Name of the communication endpoint.
/* System library. */
#include <sys_defs.h>
+#include <sys/socket.h>
#include <unistd.h>
#include <string.h>
#include <msg.h>
#include <connect.h>
#include <iostuff.h>
+#include <mymalloc.h>
+#include <events.h>
#include <trigger.h>
+struct inet_trigger {
+ int fd;
+ char *service;
+};
+
+/* inet_trigger_event - disconnect from peer */
+
+static void inet_trigger_event(int event, char *context)
+{
+ struct inet_trigger *ip = (struct inet_trigger *) context;
+ static char *myname = "inet_trigger_event";
+
+ /*
+ * Disconnect.
+ */
+ if (event == EVENT_TIME)
+ msg_warn("%s: read timeout for service %s", myname, ip->service);
+ if (close(ip->fd) < 0)
+ msg_warn("%s: close %s: %m", myname, ip->service);
+ myfree(ip->service);
+ myfree((char *) ip);
+}
+
+
/* inet_trigger - wakeup INET-domain server */
int inet_trigger(const char *service, const char *buf, int len, int timeout)
{
char *myname = "inet_trigger";
+ struct inet_trigger *ip;
int fd;
if (msg_verbose > 1)
msg_warn("%s: connect to %s: %m", myname, service);
return (-1);
}
+ ip = (struct inet_trigger *) mymalloc(sizeof(*ip));
+ ip->fd = fd;
+ ip->service = mystrdup(service);
/*
* Write the request...
msg_warn("%s: write to %s: %m", myname, service);
/*
- * Disconnect.
+ * Wakeup when the peer disconnects, or when we lose patience.
*/
- if (close(fd) < 0)
- if (msg_verbose)
- msg_warn("%s: close %s: %m", myname, service);
+ if (timeout > 0)
+ event_request_timer(inet_trigger_event, (char *) ip, timeout);
+ event_enable_read(fd, inet_trigger_event, (char *) ip);
return (0);
}
* Use the semi-crappy random number generator.
*/
if (my_pid == 0)
- srandom((my_pid = getpid()) ^ time((time_t *) 0));
- usec = (delay - variation / 2) + variation * (double) random() / RAND_MAX;
+ srand((my_pid = getpid()) ^ time((time_t *) 0));
+ usec = (delay - variation / 2) + variation * (double) rand() / RAND_MAX;
doze(usec);
}
/* Utility library. */
+#include "msg.h"
#include "sane_accept.h"
/* sane_accept - sanitize accept() error returns */
EWOULDBLOCK,
0,
};
+ static int accept_warn_errors[] = {
+ ECONNABORTED,
+ 0,
+ };
int count;
int err;
int fd;
* XXX LINUX < 2.1 accept() wakes up before the three-way handshake is
* complete, so it can fail with ECONNRESET and other "false alarm"
* indications.
+ *
+ * XXX FreeBSD 4.2-STABLE accept() returns ECONNABORTED when a UNIX-domain
+ * client has disconnected in the mean time. The data that was sent with
+ * connect() write() close() is lost, even though the write() and close()
+ * reported successful completion. This was fixed shortly before FreeBSD
+ * 4.3. However, other systems may make that same mistake again, so we're
+ * adding a special warning.
*/
if ((fd = accept(sock, sa, len)) < 0) {
for (count = 0; (err = accept_ok_errors[count]) != 0; count++) {
break;
}
}
+ for (count = 0; (err = accept_warn_errors[count]) != 0; count++) {
+ if (errno == err) {
+ msg_warn("accept: %m");
+ errno = EAGAIN;
+ break;
+ }
+ }
}
return (fd);
}
/* stream_trigger() wakes up the named stream server by making
/* a brief connection to it and writing the named buffer.
/*
+/* The connection is closed by a background thread. Some kernels
+/* cannot handle client-side disconnect before the server has
+/* received the message.
+/*
/* Arguments:
/* .IP service
/* Name of the communication endpoint.
#include <msg.h>
#include <connect.h>
#include <iostuff.h>
+#include <mymalloc.h>
+#include <events.h>
#include <trigger.h>
+struct stream_trigger {
+ int fd;
+ char *service;
+};
+
+/* stream_trigger_event - disconnect from peer */
+
+static void stream_trigger_event(int event, char *context)
+{
+ struct stream_trigger *sp = (struct stream_trigger *) context;
+ static char *myname = "stream_trigger_event";
+
+ /*
+ * Disconnect.
+ */
+ if (event == EVENT_TIME)
+ msg_warn("%s: read timeout for service %s", myname, sp->service);
+ if (close(sp->fd) < 0)
+ msg_warn("%s: close %s: %m", myname, sp->service);
+ myfree(sp->service);
+ myfree((char *) sp);
+}
+
/* stream_trigger - wakeup stream server */
int stream_trigger(const char *service, const char *buf, int len, int timeout)
{
char *myname = "stream_trigger";
+ struct stream_trigger *sp;
int fd;
if (msg_verbose > 1)
return (-1);
}
+ /*
+ * Stash away context.
+ */
+ sp = (struct stream_trigger *) mymalloc(sizeof(*sp));
+ sp->fd = fd;
+ sp->service = mystrdup(service);
+
/*
* Write the request...
*/
msg_warn("%s: write to %s: %m", myname, service);
/*
- * Disconnect.
+ * Wakeup when the peer disconnects, or when we lose patience.
*/
- if (close(fd) < 0)
- if (msg_verbose)
- msg_warn("%s: close %s: %m", myname, service);
+ if (timeout > 0)
+ event_request_timer(stream_trigger_event, (char *) sp, timeout);
+ event_enable_read(fd, stream_trigger_event, (char *) sp);
return (0);
}
#define USE_STATVFS
#define STATVFS_IN_SYS_STATVFS_H
#define STRCASECMP_IN_STRINGS_H
+#if 0
extern time_t time(time_t *);
extern int seteuid(uid_t);
extern int setegid(gid_t);
extern int initgroups(const char *, int);
+#endif
#endif
/* unix_trigger() wakes up the named UNIX-domain server by making
/* a brief connection to it and writing the named buffer.
/*
+/* The connection is closed by a background thread. Some kernels
+/* cannot handle client-side disconnect before the server has
+/* received the message.
+/*
/* Arguments:
/* .IP service
/* Name of the communication endpoint.
/* System library. */
#include <sys_defs.h>
+#include <sys/socket.h>
#include <unistd.h>
#include <string.h>
#include <msg.h>
#include <connect.h>
#include <iostuff.h>
+#include <mymalloc.h>
+#include <events.h>
#include <trigger.h>
+struct unix_trigger {
+ int fd;
+ char *service;
+};
+
+/* unix_trigger_event - disconnect from peer */
+
+static void unix_trigger_event(int event, char *context)
+{
+ struct unix_trigger *up = (struct unix_trigger *) context;
+ static char *myname = "unix_trigger_event";
+
+ /*
+ * Disconnect.
+ */
+ if (event == EVENT_TIME)
+ msg_warn("%s: read timeout for service %s", myname, up->service);
+ if (close(up->fd) < 0)
+ msg_warn("%s: close %s: %m", myname, up->service);
+ myfree(up->service);
+ myfree((char *) up);
+}
+
/* unix_trigger - wakeup UNIX-domain server */
int unix_trigger(const char *service, const char *buf, int len, int timeout)
{
char *myname = "unix_trigger";
+ struct unix_trigger *up;
int fd;
if (msg_verbose > 1)
return (-1);
}
+ /*
+ * Stash away context.
+ */
+ up = (struct unix_trigger *) mymalloc(sizeof(*up));
+ up->fd = fd;
+ up->service = mystrdup(service);
+
/*
* Write the request...
*/
msg_warn("%s: write to %s: %m", myname, service);
/*
- * Disconnect.
+ * Wakeup when the peer disconnects, or when we lose patience.
*/
- if (close(fd) < 0)
- if (msg_verbose)
- msg_warn("%s: close %s: %m", myname, service);
+ if (timeout > 0)
+ event_request_timer(unix_trigger_event, (char *) up, timeout);
+ event_enable_read(fd, unix_trigger_event, (char *) up);
return (0);
}