diff --git a/postfix/FILTER_README b/postfix/FILTER_README index adb7d28fd..7190790fd 100644 --- a/postfix/FILTER_README +++ b/postfix/FILTER_README @@ -128,9 +128,9 @@ for transit mail that arrives and leaves via SMTP, provided that 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 : @@ -162,7 +162,7 @@ transport table. The content filtering records are added by the 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, @@ -183,6 +183,9 @@ you want to have your filter listening on port localhost:10025 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. diff --git a/postfix/HISTORY b/postfix/HISTORY index d51dc47eb..5e509c65c 100644 --- a/postfix/HISTORY +++ b/postfix/HISTORY @@ -4960,3 +4960,31 @@ Apologies for any names omitted. 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. diff --git a/postfix/INSTALL b/postfix/INSTALL index 20d58268c..a12ae5451 100644 --- a/postfix/INSTALL +++ b/postfix/INSTALL @@ -249,6 +249,8 @@ or, if you feel nostalgic, use the Postfix sendmail command: 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. @@ -290,6 +292,8 @@ or, if you feel nostalgic, use the Postfix sendmail program: 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. @@ -323,6 +327,8 @@ But the good old sendmail way works just as well: 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. diff --git a/postfix/LINUX_README b/postfix/LINUX_README index 1b0c41829..35a22a7a4 100644 --- a/postfix/LINUX_README +++ b/postfix/LINUX_README @@ -1,3 +1,9 @@ +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 ========================= diff --git a/postfix/conf/access b/postfix/conf/access index 78f93f188..59a855eb3 100644 --- a/postfix/conf/access +++ b/postfix/conf/access @@ -76,7 +76,7 @@ # 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. # @@ -86,7 +86,7 @@ # 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 diff --git a/postfix/conf/main.cf b/postfix/conf/main.cf index ee1c90c70..fb75a55a2 100644 --- a/postfix/conf/main.cf +++ b/postfix/conf/main.cf @@ -226,14 +226,12 @@ mail_owner = postfix # $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 diff --git a/postfix/conf/postfix-script-nosgid b/postfix/conf/postfix-script-nosgid index 2c5d43ae2..d102a0e3b 100755 --- a/postfix/conf/postfix-script-nosgid +++ b/postfix/conf/postfix-script-nosgid @@ -35,7 +35,7 @@ case $daemon_directory in 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" diff --git a/postfix/conf/postfix-script-sgid b/postfix/conf/postfix-script-sgid index a635ef679..d436e4df3 100755 --- a/postfix/conf/postfix-script-sgid +++ b/postfix/conf/postfix-script-sgid @@ -35,7 +35,7 @@ case $daemon_directory in 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" diff --git a/postfix/conf/sample-compatibility.cf b/postfix/conf/sample-compatibility.cf index 329ee4cf3..868ee5df0 100644 --- a/postfix/conf/sample-compatibility.cf +++ b/postfix/conf/sample-compatibility.cf @@ -4,6 +4,16 @@ # 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 diff --git a/postfix/conf/sample-misc.cf b/postfix/conf/sample-misc.cf index 14f9e19b2..3e0d32d4b 100644 --- a/postfix/conf/sample-misc.cf +++ b/postfix/conf/sample-misc.cf @@ -360,6 +360,18 @@ relocated_maps = # 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. diff --git a/postfix/html/access.5.html b/postfix/html/access.5.html index 0818d204c..89db2dcb3 100644 --- a/postfix/html/access.5.html +++ b/postfix/html/access.5.html @@ -87,7 +87,7 @@ ACCESS(5) ACCESS(5) 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. @@ -97,7 +97,7 @@ ACCESS(5) ACCESS(5) 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 diff --git a/postfix/html/faq.html b/postfix/html/faq.html index d48c5be2f..d65089113 100644 --- a/postfix/html/faq.html +++ b/postfix/html/faq.html @@ -24,6 +24,8 @@
  • POP or IMAP problems +
  • Problems with specific Operating Systems +
  • Postfix warnings and error messages
  • Example configurations @@ -74,8 +76,18 @@
  • What does "fatal: open database /etc/aliases.db" mean? +
  • What does "fatal: unknown service: smtp/tcp" mean? +
  • sendmail has set-uid root file permissions, or is run from a set-uid root process +
  • sendmail: unable to find out your login name + + +
  • Mail for unknown users in +virtual domains fails with "mail loops back to myself" + +
  • Postfix refuses mail for virtual +domains with "relay access denied"

    @@ -186,6 +198,8 @@ domains with "relay access denied"

  • Postfix does not try all the MX addresses +
  • What does "fatal: unknown service: smtp/tcp" mean? +

    Local (non-virtual) delivery

    @@ -311,6 +325,36 @@ mailbox
    +

    + +

    Problems with specific Operating Systems

    + +

    + +

    + +

    Problems with Compaq

    + +
    + +

    Problems with IRIX

    + +
    +

    POP or IMAP problems

    @@ -970,6 +1014,30 @@ PERMISSION_SECURITY="secure local"
    +

    sendmail: unable to find out your login name

    + +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. + +

    + +The envelope sender address is also the default value for the From: +header address, when none is specified in the message. + +

    + +To fix, specify the envelope sender address on the sendmail command +line: + +

    +
    +sendmail -f user@domain ...
    +
    +
    + +
    +

    Running hundreds of Postfix processes on FreeBSD

    With hundreds of Postfix processes, the kernel will eventually @@ -1112,32 +1180,41 @@ depending on the interface that it is supposed to handle.

    Postfix responds slowly to incoming SMTP connections

    -
    +Question: -
    Question: +
    -
    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 (telnet hostname 25), the response comes after 40 seconds. On the other hand, when I telnet to the the POP port (telnet hostname 110) the response comes with no delay. -

    - -

    Answer: - -
    - -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. +

    -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. +Answer: -

    +
    + +You have a name service problem. + +

    + +Postfix calls the C library routines gethostbyname() and +gethostbyaddr() 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. + +

    + +Depending on your system, these controlling files can be named +/etc/nsswitch.conf, /etc/svcorder, /etc/host.conf +or otherwise. Those files specify whether the C library routines +will use local /etc/hosts before or after DNS. + +


    @@ -1733,6 +1810,35 @@ use the command postconf mail_version. Execute the command postfix reload to make the change effective immediately. +

    What does "fatal: unknown service: smtp/tcp" +mean?

    + +The Postfix /etc/postfix/master.cf file specifies that the +Postfix SMTP client runs inside a chroot environment. However, +the files necessary for that mode of operation are not installed +below /var/spool/postfix. + +

    + +Enabling chroot operation adds a non-trivial barrier for +system penetrators. + +

    + +Two solutions: + +


    Root's mail is delivered to nobody

    @@ -2300,6 +2406,11 @@ virtual domain.

    +Sendmail-style virtual domains are not supported in Postfix versions +released before 20001118. + +

    + Be sure to follow instructions in the virtual manual page. @@ -2909,6 +3020,36 @@ href="http://www.isc.org/"> http://www.isc.org/.


    +

    Compaq mail blackhole problem

    + +On some Compaq Tru64 UNIX configurations, Postfix will receive mail +and then nothing happens. The mail does not even show up with the +mailq command. + +

    + +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". + +

    + +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. + +

    + +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 +noexec option or equivalent. + +


    +
    Up one level | Postfix FAQ diff --git a/postfix/html/pipe.8.html b/postfix/html/pipe.8.html index 207286715..4c79983a0 100644 --- a/postfix/html/pipe.8.html +++ b/postfix/html/pipe.8.html @@ -13,11 +13,9 @@ PIPE(8) PIPE(8) DESCRIPTION The pipe 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 master(8) - process manager. + manager to deliver messages to external commands. This + program expects to be run from the master(8) process man- + ager. The pipe daemon updates queue files and marks recipients as finished, or it informs the queue manager that delivery @@ -25,40 +23,42 @@ PIPE(8) PIPE(8) reports are sent to the bounce(8) or defer(8) daemon as appropriate. +SINGLE-RECIPIENT DELIVERY + 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 + + transport_destination_recipient_limit = 1 + + in the Postfix main.cf file, where transport is the name + in the first column of the Postfix master.cf entry for the + pipe-based delivery transport. + COMMAND ATTRIBUTE SYNTAX The external command attributes are given in the master.cf file at the end of a service definition. The syntax is as follows: flags=BFR.> (optional) - Optional message processing flags. By default, a + Optional message processing flags. By default, a message is copied unchanged. - B Append a blank line at the end of each mes- - sage. This is required by some mail user - agents that recognize "From " lines only + B Append a blank line at the end of each mes- + sage. This is required by some mail user + agents that recognize "From " lines only when preceded by a blank line. - F Prepend a "From sender time_stamp" envelope - header to the message content. This is + F Prepend a "From sender time_stamp" envelope + header to the message content. This is expected by, for example, UUCP software. - R Prepend a Return-Path: message header with + R Prepend a Return-Path: message header with the envelope sender address. - . Prepend . to lines starting with ".". This - is needed by, for example, BSMTP software. - - > Prepend > to lines starting with "From ". - This is expected by, for example, UUCP soft- - ware. - - user=username (required) - - user=username:groupname - The external command is executed with the rights of - the specified username. The software refuses to - execute commands with root privileges, or with the + . Prepend . to lines starting with ".". This @@ -71,14 +71,26 @@ PIPE(8) PIPE(8) PIPE(8) PIPE(8) - privileges of the mail system owner. If groupname - is specified, the corresponding group ID is used + is needed by, for example, BSMTP software. + + > Prepend > to lines starting with "From ". + This is expected by, for example, UUCP soft- + ware. + + user=username (required) + + user=username:groupname + The external command is executed with the rights of + the specified username. The software refuses to + execute commands with root privileges, or with the + privileges of the mail system owner. If groupname + is specified, the corresponding group ID is used instead of the group ID of username. eol=string (default: \n) - The output record delimiter. Typically one would - use either \r\n or \n. The usual C-style backslash - escape sequences are recognized: \a \b \f \n \r \t + The output record delimiter. Typically one would + use either \r\n or \n. The usual C-style backslash + escape sequences are recognized: \a \b \f \n \r \t \v \octal and \\. size=size_limit (optional) @@ -86,45 +98,33 @@ PIPE(8) PIPE(8) will be bounced back to the sender. argv=command... (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: ${extension} - 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 user+foo@domain the extension is - foo. A command-line argument that contains - ${extension} expands into as many command- + foo. + + A command-line argument that contains + ${extension} expands into as many command- line arguments as there are recipients. ${mailbox} - This macro expands to the complete local - part of a recipient address. For example, - with an address user+foo@domain the mailbox - is user+foo. A command-line argument that - contains ${mailbox} 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 user+foo@domain the mailbox + is user+foo. - ${nexthop} - This macro expands to the next-hop hostname. - - ${recipient} - This macro expands to the complete recipient - address. A command-line argument that con- - tains ${recipient} expands into as many com- - mand-line arguments as there are recipients. - - ${sender} - This macro expands to the envelope sender @@ -137,60 +137,60 @@ PIPE(8) PIPE(8) PIPE(8) PIPE(8) + A command-line argument that contains + ${mailbox} expands into as many command-line + arguments as there are recipients. + + ${nexthop} + This macro expands to the next-hop hostname. + + ${recipient} + This macro expands to the complete recipient + address. + + A command-line argument that contains + ${recipient} expands into as many command- + line arguments as there are recipients. + + ${sender} + This macro expands to the envelope sender address. ${size} - 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. ${user} This macro expands to the username part of a - recipient address. For example, with an + recipient address. For example, with an address user+foo@domain the username part is - user. A command-line argument that contains - ${user} expands into as many command-line + user. + + A command-line argument that contains + ${user} expands into as many command-line arguments as there are recipients. - In addition to the form ${name}, the forms $name and - $(name) are also recognized. Specify $$ where a single $ + In addition to the form ${name}, the forms $name and + $(name) are also recognized. Specify $$ where a single $ is wanted. DIAGNOSTICS - Command exit status codes are expected to follow the con- + Command exit status codes are expected to follow the con- ventions defined in <sysexits.h>. - Problems and transactions are logged to syslogd(8). Cor- - rupted message files are marked so that the queue manager + Problems and transactions are logged to syslogd(8). Cor- + rupted message files are marked so that the queue manager can move them to the corrupt queue for further inspection. SECURITY - 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. CONFIGURATION PARAMETERS - The following main.cf parameters are especially relevant - to this program. See the Postfix main.cf file for syntax - details and for default values. Use the postfix reload - command after a configuration change. - -Miscellaneous - export_environment - List of names of environment parameters that can be - exported to non-Postfix processes. - - mail_owner - The process privileges used while not running an - external command. - -Resource controls - In the text below, transport is the first field in a mas- - ter.cf entry. - - transport_destination_concurrency_limit - Limit the number of parallel deliveries to the same + The following main.cf parameters are especially relevant @@ -203,23 +203,42 @@ PIPE(8) PIPE(8) PIPE(8) PIPE(8) - destination, for delivery via the named transport. - The default limit is taken from the default_desti- - nation_concurrency_limit parameter. The limit is + to this program. See the Postfix main.cf file for syntax + details and for default values. Use the postfix reload + command after a configuration change. + +Miscellaneous + export_environment + List of names of environment parameters that can be + exported to non-Postfix processes. + + mail_owner + The process privileges used while not running an + external command. + +Resource controls + In the text below, transport is the first field in a mas- + ter.cf entry. + + transport_destination_concurrency_limit + Limit the number of parallel deliveries to the same + destination, for delivery via the named transport. + The default limit is taken from the default_desti- + nation_concurrency_limit parameter. The limit is enforced by the Postfix queue manager. transport_destination_recipient_limit - Limit the number of recipients per message deliv- - ery, for delivery via the named transport. The - default limit is taken from the default_destina- - tion_recipient_limit parameter. The limit is + Limit the number of recipients per message deliv- + ery, for delivery via the named transport. The + default limit is taken from the default_destina- + tion_recipient_limit parameter. The limit is enforced by the Postfix queue manager. transport_time_limit - Limit the time for delivery to external command, - for delivery via the named transport. The default - limit is taken from the command_time_limit parame- - ter. The limit is enforced by the Postfix queue + Limit the time for delivery to external command, + for delivery via the named transport. The default + limit is taken from the command_time_limit parame- + ter. The limit is enforced by the Postfix queue manager. SEE ALSO @@ -229,7 +248,7 @@ PIPE(8) PIPE(8) syslogd(8) system logging LICENSE - The Secure Mailer license must be distributed with this + The Secure Mailer license must be distributed with this software. AUTHOR(S) @@ -239,25 +258,6 @@ PIPE(8) PIPE(8) Yorktown Heights, NY 10598, USA - - - - - - - - - - - - - - - - - - - 4 diff --git a/postfix/man/man5/access.5 b/postfix/man/man5/access.5 index 851a1390f..6488c0b79 100644 --- a/postfix/man/man5/access.5 +++ b/postfix/man/man5/access.5 @@ -69,7 +69,7 @@ address is a sequence of one or more octets separated by ".". .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 @@ -78,7 +78,7 @@ error response message is generated. .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 diff --git a/postfix/man/man8/pipe.8 b/postfix/man/man8/pipe.8 index 7fb5787ba..0ab6673ef 100644 --- a/postfix/man/man8/pipe.8 +++ b/postfix/man/man8/pipe.8 @@ -13,9 +13,7 @@ Postfix delivery to external command .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. @@ -23,6 +21,24 @@ The \fBpipe\fR daemon updates queue files and marks recipients 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 @@ -82,18 +98,21 @@ manager delivery request: 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 @@ -105,6 +124,7 @@ is an approximation of the size of the message as delivered. 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 diff --git a/postfix/proto/access b/postfix/proto/access index 23b4fd960..a319352cd 100644 --- a/postfix/proto/access +++ b/postfix/proto/access @@ -57,7 +57,7 @@ # 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 @@ -66,7 +66,7 @@ # .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 diff --git a/postfix/src/cleanup/cleanup_extracted.c b/postfix/src/cleanup/cleanup_extracted.c index 1a4982fa1..243404be1 100644 --- a/postfix/src/cleanup/cleanup_extracted.c +++ b/postfix/src/cleanup/cleanup_extracted.c @@ -182,7 +182,6 @@ static void cleanup_extracted_process(CLEANUP_STATE *state, int type, char *buf, * 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; diff --git a/postfix/src/flush/flush.c b/postfix/src/flush/flush.c index d35cc5813..eb6a24dcb 100644 --- a/postfix/src/flush/flush.c +++ b/postfix/src/flush/flush.c @@ -129,6 +129,7 @@ #include #include #include +#include /* Utility library. */ @@ -182,10 +183,44 @@ static DOMAIN_LIST *flush_domains; /* * 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) @@ -193,12 +228,13 @@ 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); @@ -209,13 +245,29 @@ static int flush_add_service(const char *site, const char *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 @@ -223,7 +275,7 @@ static int flush_add_service(const char *site, const char *queue_id) * 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 @@ -237,10 +289,9 @@ static int flush_add_service(const char *site, const char *queue_id) * 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); } @@ -250,16 +301,8 @@ static int flush_add_service(const char *site, const char *queue_id) static int flush_send_service(const char *site) { char *myname = "flush_send_service"; - VSTRING *queue_id; - VSTRING *queue_file; - VSTREAM *log; - struct utimbuf tbuf; - static char qmgr_trigger[] = { - QMGR_REQ_SCAN_INCOMING, /* scan incoming queue */ - QMGR_REQ_FLUSH_DEAD, /* flush dead site/transport cache */ - }; - HTABLE *dup_filter; - int count; + VSTRING *site_path; + int status; if (msg_verbose) msg_info("%s: site %s", myname, site); @@ -270,13 +313,39 @@ static int flush_send_service(const char *site) 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; + struct utimbuf tbuf; + static char qmgr_trigger[] = { + QMGR_REQ_SCAN_INCOMING, /* scan incoming queue */ + QMGR_REQ_FLUSH_DEAD, /* flush dead site/transport cache */ + }; + HTABLE *dup_filter; + int count; + /* * 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); } @@ -287,7 +356,7 @@ static int flush_send_service(const char *site) * 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 @@ -312,15 +381,15 @@ static int flush_send_service(const char *site) 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); @@ -344,8 +413,8 @@ static int flush_send_service(const char *site) } } 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); @@ -356,19 +425,18 @@ static int flush_send_service(const char *site) * 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)); } @@ -381,23 +449,15 @@ static int flush_refresh_service(int max_age) { 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)); @@ -413,15 +473,15 @@ static int flush_refresh_service(int max_age) 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); @@ -466,14 +526,12 @@ static void flush_service(VSTREAM *client_stream, char *unused_service, 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) diff --git a/postfix/src/global/Makefile.in b/postfix/src/global/Makefile.in index 43d75aed0..c7f777ef4 100644 --- a/postfix/src/global/Makefile.in +++ b/postfix/src/global/Makefile.in @@ -601,6 +601,7 @@ mail_params.o: ../../include/valid_hostname.h 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 @@ -683,7 +684,9 @@ mail_task.o: mail_task.c 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 diff --git a/postfix/src/global/mail_conf.h b/postfix/src/global/mail_conf.h index ffa1341d7..25406e8da 100644 --- a/postfix/src/global/mail_conf.h +++ b/postfix/src/global/mail_conf.h @@ -21,7 +21,8 @@ */ #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. diff --git a/postfix/src/global/mail_params.c b/postfix/src/global/mail_params.c index ed04630aa..e0a36540b 100644 --- a/postfix/src/global/mail_params.c +++ b/postfix/src/global/mail_params.c @@ -14,6 +14,7 @@ /* 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; @@ -92,6 +93,7 @@ #include #include +#include #include #include #include @@ -107,6 +109,7 @@ #include #include #include +#include /* Global library. */ @@ -127,6 +130,7 @@ char *var_relayhost; 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; @@ -270,6 +274,7 @@ void mail_params_init() }; 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, @@ -326,6 +331,7 @@ void mail_params_init() 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 @@ -376,6 +382,15 @@ void mail_params_init() */ 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. */ diff --git a/postfix/src/global/mail_params.h b/postfix/src/global/mail_params.h index 294fd1fc5..dc7aee1ca 100644 --- a/postfix/src/global/mail_params.h +++ b/postfix/src/global/mail_params.h @@ -1192,7 +1192,7 @@ extern int var_fflush_refresh; * 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" @@ -1230,6 +1230,13 @@ extern int var_virt_mailbox_limit; #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 diff --git a/postfix/src/global/mail_task.c b/postfix/src/global/mail_task.c index 050075456..2c06f9eda 100644 --- a/postfix/src/global/mail_task.c +++ b/postfix/src/global/mail_task.c @@ -12,7 +12,8 @@ /* 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. @@ -35,25 +36,28 @@ /* Utility library. */ #include +#include /* 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)); } diff --git a/postfix/src/global/mail_version.h b/postfix/src/global/mail_version.h index f0445eecd..38259bcc5 100644 --- a/postfix/src/global/mail_version.h +++ b/postfix/src/global/mail_version.h @@ -15,7 +15,7 @@ * 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 diff --git a/postfix/src/global/tok822_parse.c b/postfix/src/global/tok822_parse.c index a07b75113..09b1685ff 100644 --- a/postfix/src/global/tok822_parse.c +++ b/postfix/src/global/tok822_parse.c @@ -252,7 +252,7 @@ VSTRING *tok822_externalize(VSTRING *vp, TOK822 *tree, int flags) 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, '"'); @@ -540,7 +540,7 @@ TOK822 *tok822_scan_addr(const char *addr) #include #include -#include +#include /* tok822_print - display token */ @@ -577,7 +577,7 @@ int main(int unused_argc, char **unused_argv) 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)); diff --git a/postfix/src/lmtp/lmtp.c b/postfix/src/lmtp/lmtp.c index 09572082a..49b89f403 100644 --- a/postfix/src/lmtp/lmtp.c +++ b/postfix/src/lmtp/lmtp.c @@ -330,6 +330,10 @@ static int deliver_message(DELIVER_REQUEST *request, char **unused_argv) /* * 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); diff --git a/postfix/src/lmtp/lmtp_connect.c b/postfix/src/lmtp/lmtp_connect.c index d48837000..cb7e24663 100644 --- a/postfix/src/lmtp/lmtp_connect.c +++ b/postfix/src/lmtp/lmtp_connect.c @@ -116,7 +116,8 @@ static LMTP_SESSION *lmtp_connect_sock(int, struct sockaddr *, int, /* 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"; @@ -156,7 +157,7 @@ static LMTP_SESSION *lmtp_connect_unix(const char *addr, VSTRING *why) 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 */ @@ -358,13 +359,19 @@ LMTP_SESSION *lmtp_connect(const char *destination, VSTRING *why) * 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); diff --git a/postfix/src/master/Makefile.in b/postfix/src/master/Makefile.in index 83d6200cc..e92df20c5 100644 --- a/postfix/src/master/Makefile.in +++ b/postfix/src/master/Makefile.in @@ -138,6 +138,7 @@ master_listen.o: ../../include/vstring.h 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 diff --git a/postfix/src/master/master_listen.c b/postfix/src/master/master_listen.c index d7020ac42..9b1d9f8a7 100644 --- a/postfix/src/master/master_listen.c +++ b/postfix/src/master/master_listen.c @@ -53,6 +53,7 @@ #include #include #include +#include #include /* Global library. */ @@ -88,7 +89,7 @@ void master_listen_init(MASTER_SERV *serv) 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; /* @@ -98,7 +99,7 @@ void master_listen_init(MASTER_SERV *serv) 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; /* diff --git a/postfix/src/master/master_sig.c b/postfix/src/master/master_sig.c index ca501e918..509a4a54d 100644 --- a/postfix/src/master/master_sig.c +++ b/postfix/src/master/master_sig.c @@ -152,7 +152,7 @@ static void master_sigchld(int sig) static void master_sigdeath(int sig) { - char *myname = "master_sigsetup"; + char *myname = "master_sigdeath"; struct sigaction action; pid_t pid = getpid(); diff --git a/postfix/src/nqmgr/qmgr_active.c b/postfix/src/nqmgr/qmgr_active.c index ec00584bd..404c4eda9 100644 --- a/postfix/src/nqmgr/qmgr_active.c +++ b/postfix/src/nqmgr/qmgr_active.c @@ -149,7 +149,7 @@ static void qmgr_active_defer(const char *queue_name, const char *queue_id, 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) diff --git a/postfix/src/nqmgr/qmgr_job.c b/postfix/src/nqmgr/qmgr_job.c index 5e604cf1a..094838b77 100644 --- a/postfix/src/nqmgr/qmgr_job.c +++ b/postfix/src/nqmgr/qmgr_job.c @@ -770,8 +770,18 @@ static QMGR_PEER *qmgr_job_peer_select(QMGR_JOB *job) * 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. + * + * 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) { + 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)); diff --git a/postfix/src/pipe/pipe.c b/postfix/src/pipe/pipe.c index ee9154c6d..cefb83716 100644 --- a/postfix/src/pipe/pipe.c +++ b/postfix/src/pipe/pipe.c @@ -7,9 +7,7 @@ /* \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. /* @@ -17,6 +15,22 @@ /* 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 @@ -74,18 +88,21 @@ /* 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 @@ -97,6 +114,7 @@ /* 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 diff --git a/postfix/src/postkick/postkick.c b/postfix/src/postkick/postkick.c index c756c14ae..12af8b712 100644 --- a/postfix/src/postkick/postkick.c +++ b/postfix/src/postkick/postkick.c @@ -80,6 +80,7 @@ #include #include #include +#include /* Global library. */ @@ -159,11 +160,12 @@ int main(int argc, char **argv) /* * 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); } } diff --git a/postfix/src/qmgr/qmgr_active.c b/postfix/src/qmgr/qmgr_active.c index ec00584bd..404c4eda9 100644 --- a/postfix/src/qmgr/qmgr_active.c +++ b/postfix/src/qmgr/qmgr_active.c @@ -149,7 +149,7 @@ static void qmgr_active_defer(const char *queue_name, const char *queue_id, 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) diff --git a/postfix/src/util/inet_trigger.c b/postfix/src/util/inet_trigger.c index e92c2d8db..ace14ab0e 100644 --- a/postfix/src/util/inet_trigger.c +++ b/postfix/src/util/inet_trigger.c @@ -16,6 +16,10 @@ /* 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. @@ -45,6 +49,7 @@ /* System library. */ #include +#include #include #include @@ -53,13 +58,40 @@ #include #include #include +#include +#include #include +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) @@ -73,6 +105,9 @@ int inet_trigger(const char *service, const char *buf, int len, int timeout) 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... @@ -82,10 +117,10 @@ int inet_trigger(const char *service, const char *buf, int len, int timeout) 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); } diff --git a/postfix/src/util/rand_sleep.c b/postfix/src/util/rand_sleep.c index 0173e9693..d119f074f 100644 --- a/postfix/src/util/rand_sleep.c +++ b/postfix/src/util/rand_sleep.c @@ -67,8 +67,8 @@ void rand_sleep(unsigned delay, unsigned variation) * 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); } diff --git a/postfix/src/util/sane_accept.c b/postfix/src/util/sane_accept.c index d9d8464d0..9ae9f6e3f 100644 --- a/postfix/src/util/sane_accept.c +++ b/postfix/src/util/sane_accept.c @@ -36,6 +36,7 @@ /* Utility library. */ +#include "msg.h" #include "sane_accept.h" /* sane_accept - sanitize accept() error returns */ @@ -55,6 +56,10 @@ int sane_accept(int sock, struct sockaddr * sa, SOCKADDR_SIZE *len) EWOULDBLOCK, 0, }; + static int accept_warn_errors[] = { + ECONNABORTED, + 0, + }; int count; int err; int fd; @@ -68,6 +73,13 @@ int sane_accept(int sock, struct sockaddr * sa, SOCKADDR_SIZE *len) * 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++) { @@ -76,6 +88,13 @@ int sane_accept(int sock, struct sockaddr * sa, SOCKADDR_SIZE *len) break; } } + for (count = 0; (err = accept_warn_errors[count]) != 0; count++) { + if (errno == err) { + msg_warn("accept: %m"); + errno = EAGAIN; + break; + } + } } return (fd); } diff --git a/postfix/src/util/stream_trigger.c b/postfix/src/util/stream_trigger.c index 585f392bf..ee59b6c28 100644 --- a/postfix/src/util/stream_trigger.c +++ b/postfix/src/util/stream_trigger.c @@ -15,6 +15,10 @@ /* 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. @@ -51,13 +55,39 @@ #include #include #include +#include +#include #include +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) @@ -72,6 +102,13 @@ int stream_trigger(const char *service, const char *buf, int len, int timeou return (-1); } + /* + * Stash away context. + */ + sp = (struct stream_trigger *) mymalloc(sizeof(*sp)); + sp->fd = fd; + sp->service = mystrdup(service); + /* * Write the request... */ @@ -80,10 +117,10 @@ int stream_trigger(const char *service, const char *buf, int len, int timeou 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); } diff --git a/postfix/src/util/sys_defs.h b/postfix/src/util/sys_defs.h index 1b1d3df54..dbad4d716 100644 --- a/postfix/src/util/sys_defs.h +++ b/postfix/src/util/sys_defs.h @@ -329,10 +329,12 @@ extern int opterr; #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 diff --git a/postfix/src/util/unix_trigger.c b/postfix/src/util/unix_trigger.c index d0f03d6c7..498a6c07a 100644 --- a/postfix/src/util/unix_trigger.c +++ b/postfix/src/util/unix_trigger.c @@ -15,6 +15,10 @@ /* 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. @@ -43,6 +47,7 @@ /* System library. */ #include +#include #include #include @@ -51,13 +56,39 @@ #include #include #include +#include +#include #include +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) @@ -72,6 +103,13 @@ int unix_trigger(const char *service, const char *buf, int len, int timeout) return (-1); } + /* + * Stash away context. + */ + up = (struct unix_trigger *) mymalloc(sizeof(*up)); + up->fd = fd; + up->service = mystrdup(service); + /* * Write the request... */ @@ -80,10 +118,10 @@ int unix_trigger(const char *service, const char *buf, int len, int timeout) 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); }