From d41ba831a244fe86b357eaad96d5d0131d366ddf Mon Sep 17 00:00:00 2001 From: Wietse Venema Date: Sat, 4 Apr 2009 00:00:00 -0500 Subject: [PATCH] postfix-2.6-20090404 --- postfix/.indent.pro | 5 + postfix/HISTORY | 97 + postfix/Makefile.in | 33 +- postfix/README_FILES/BUILTIN_FILTER_README | 7 +- postfix/README_FILES/MULTI_INSTANCE_README | 979 +++++++++ postfix/README_FILES/OVERVIEW | 4 + .../STANDARD_CONFIGURATION_README | 2 +- postfix/README_FILES/STRESS_README | 24 +- postfix/RELEASE_NOTES | 14 +- postfix/WISHLIST | 49 +- postfix/conf/postfix-files | 6 + postfix/conf/postfix-script | 5 +- postfix/conf/postmulti-script | 300 +++ postfix/html/BUILTIN_FILTER_README.html | 10 +- postfix/html/MULTI_INSTANCE_README.html | 1272 ++++++++++++ postfix/html/Makefile.in | 11 +- postfix/html/OVERVIEW.html | 5 + .../html/STANDARD_CONFIGURATION_README.html | 2 +- postfix/html/STRESS_README.html | 18 +- postfix/html/cleanup.8.html | 194 +- postfix/html/lmtp.8.html | 48 +- postfix/html/mailq.1.html | 4 +- postfix/html/newaliases.1.html | 4 +- postfix/html/postconf.5.html | 76 +- postfix/html/postfix-manuals.html | 2 +- postfix/html/postfix-wrapper.5.html | 170 +- postfix/html/postfix.1.html | 2 +- postfix/html/postmulti.1.html | 437 ++++ postfix/html/sendmail.1.html | 4 +- postfix/html/smtp.8.html | 48 +- postfix/html/verify.8.html | 2 +- postfix/makedefs | 2 + postfix/man/Makefile.in | 9 +- postfix/man/man1/postmulti.1 | 403 ++++ postfix/man/man1/sendmail.1 | 6 +- postfix/man/man5/postconf.5 | 46 +- postfix/man/man5/postfix-wrapper.5 | 153 +- postfix/man/man8/cleanup.8 | 23 +- postfix/man/man8/smtp.8 | 6 +- postfix/man/man8/verify.8 | 2 +- postfix/mantools/postlink | 9 +- postfix/proto/BUILTIN_FILTER_README.html | 6 +- postfix/proto/MULTI_INSTANCE_README.html | 1272 ++++++++++++ postfix/proto/Makefile.in | 8 + postfix/proto/OVERVIEW.html | 5 + .../proto/STANDARD_CONFIGURATION_README.html | 2 +- postfix/proto/STRESS_README.html | 18 +- postfix/proto/postconf.proto | 56 +- postfix/proto/postfix-wrapper | 153 +- postfix/src/cleanup/cleanup.c | 23 +- postfix/src/cleanup/cleanup_init.c | 3 +- postfix/src/cleanup/cleanup_message.c | 16 +- postfix/src/dns/test_dns_lookup.c | 2 +- postfix/src/global/dict_ldap.c | 49 +- postfix/src/global/mail_conf_nint.c | 12 +- postfix/src/global/mail_params.h | 35 +- postfix/src/global/mail_version.h | 2 +- postfix/src/oqmgr/qmgr_transport.c | 2 + postfix/src/postfix/postfix.c | 3 +- postfix/src/postmulti/.indent.pro | 1 + postfix/src/postmulti/Makefile.in | 83 + postfix/src/postmulti/postmulti.c | 1791 +++++++++++++++++ postfix/src/qmgr/qmgr.h | 1 + postfix/src/qmgr/qmgr_entry.c | 49 +- postfix/src/qmgr/qmgr_job.c | 41 + postfix/src/qmgr/qmgr_queue.c | 17 +- postfix/src/qmgr/qmgr_transport.c | 2 + postfix/src/sendmail/sendmail.c | 8 +- postfix/src/smtp/Makefile.in | 1 + postfix/src/smtp/lmtp_params.c | 1 + postfix/src/smtp/smtp.c | 7 +- postfix/src/smtp/smtp_rcpt.c | 30 +- postfix/src/smtpd/smtpd.c | 75 +- postfix/src/smtpd/smtpd.h | 6 +- postfix/src/smtpd/smtpd_chat.c | 5 +- postfix/src/smtpd/smtpd_check.c | 9 +- postfix/src/smtpd/smtpd_proxy.c | 12 + postfix/src/util/inet_listen.c | 5 - postfix/src/util/inet_windowsize.c | 5 +- postfix/src/verify/verify.c | 2 +- postfix/src/xsasl/xsasl_cyrus_server.c | 2 +- 81 files changed, 7727 insertions(+), 586 deletions(-) create mode 100644 postfix/README_FILES/MULTI_INSTANCE_README create mode 100644 postfix/conf/postmulti-script create mode 100644 postfix/html/MULTI_INSTANCE_README.html create mode 100644 postfix/html/postmulti.1.html create mode 100644 postfix/man/man1/postmulti.1 create mode 100644 postfix/proto/MULTI_INSTANCE_README.html create mode 120000 postfix/src/postmulti/.indent.pro create mode 100644 postfix/src/postmulti/Makefile.in create mode 100644 postfix/src/postmulti/postmulti.c diff --git a/postfix/.indent.pro b/postfix/.indent.pro index 134180a34..32dcd9910 100644 --- a/postfix/.indent.pro +++ b/postfix/.indent.pro @@ -118,6 +118,8 @@ -THTABLE_INFO -TINET_ADDR_LIST -TINET_PROTO_INFO +-TINSTANCE +-TINST_SELECTION -TINT32_TYPE -TINTV -TINT_TABLE @@ -171,6 +173,7 @@ -TMYSQL -TMYSQL_NAME -TNAMADR_LIST +-TNAME_ASSIGNMENT -TNAME_CODE -TNAME_MASK -TPEER_NAME @@ -183,6 +186,7 @@ -TPLPGSQL -TPOSTMAP_KEY_STATE -TPOST_MAIL_STATE +-TPRIVATE_STR_TABLE -TQMGR_ENTRY -TQMGR_FEEDBACK -TQMGR_JOB @@ -218,6 +222,7 @@ -TSCAN_INFO -TSCAN_OBJ -TSESSION +-TSHARED_PATH -TSINGLE_SERVER -TSINK_COMMAND -TSINK_STATE diff --git a/postfix/HISTORY b/postfix/HISTORY index 44cc0077e..790ecad50 100644 --- a/postfix/HISTORY +++ b/postfix/HISTORY @@ -14983,3 +14983,100 @@ Apologies for any names omitted. Feature: stress-dependent smtpd_timeout (normal: 300s, overload: 10s), smtpd_hard_error_limit (normal: 20, overload: 1) and smtpd_junk_command_limit (normal: 100, overload: 1). + Files: global/mail_params.h, global/mail_conf_nint.c, + master/*_server.c, smtpd/smtpd.c. + +20090213 + + Fine tuning: don't enforce smtpd_junk_command_limit for + XCLIENT and XFORWARD commands. These commands can be issued + only by authorized clients. File: src/smtpd/smtpd.c. + +20090215 + + Feature: the Postfix SMTP server hangs up after replying + with "521". This makes overload handling more effective. + See also RFC 1846. File: smtpd/smtpd.c. + + Feature: postmulti mult-instance manager command, very + lightly tested. The MULTI_INSTANCE_README still needs to + be proofread. Originally by Victor Duchovni. Files: + src/postmulti/*, proto/MULTI_INSTANCE_README.html, + conf/postmulti-script. + +20090216-24 + + Cleanup: assorted code cleanups in postmulti. File: + src/postmulti/postmulti.c. + +20090223 + + Cleanup: multiple instances of the same global. Files: + util/inet_windowsize.c, util/inet_listen.c. + +20090228 + + Cleanup: the Postfix SMTP server now maintains a per-session + "improper command pipelining detected" flag. This flag can + be tested at any time with reject_unauth_pipelining, and + is raised whenever a client command is followed by unexpected + commands or message content. Files: smtpd/smtpd.c, + smtpd/smtpd_check.c. + + Logging: the Postfix SMTP server now logs the first command + pipelining transgression as "improper command pipelining + after from []". + + Cleanup: after DATA command failure, log "(approximately + XX bytes)" only if Postfix actually accepted the DATA + command. File: smtpd/smtpd.c. + +20090303 + + Cleanup: word smithing of "sendmail -bv" probe message. + File: sendmail/sendmail.c. + + Cleanup: OpenLDAP now provides a sane solution for conflicts + with PAM ldap-over-tls. Victor Duchovni. File: global/dict_ldap.c. + +20900304 + + Cleanup: skip over suspended or throttled queues while + looking for delivery requests. File: *qmgr/qmgr_transport.c. + +20090305 + + Bugfix: in the "new queue manager", the _destination_rate_delay + code needed to postpone the job scheduler updates after + delivery completion, otherwise the scheduler could loop on + blocked jobs. Victor & Wietse. File: qmgr/qmgr_entry.c, + qmgr/qmgr_queue.c, qmgr/qmgr_job.c. + + Cleanup: report a "queue file write error", instead of + passing though bogus 2xx replies from proxy filters to SMTP + clients. File: smtpd/smtpd_proxy.c. + +20090307 + + Cleanup: with "lmtp_assume_final = yes", the Postfix LMTP + delivery agent assumes that delivery is final when talking + to an LMTP server that announces no DSN support. Otherwise, + the Postfix LMTP delivery agent assumes that delivery is + "relayed", to maintain compatibility with simple LMTP-based + content filters. Based on code by Michel Sebastien, ATOS + Origin. File: smtp/smtp_rcpt.c. + +20090310 + + Bugfix: Postfix used mumble_concurrency_failed_cohort_limit + instead of mumble_destination_concurrency_failed_cohort_limit + as documented. File: global/mail_params.h. + +20090330 + + Cleanup: add (Resent-) From:, Date:, Message-ID: or To: + headers only when clients match $local_header_rewrite_clients. + Specify "always_add_missing_headers = yes" for backwards + compatibility. Adding such headers to remote mail can break + DKIM signatures that cover headers that are not present. + File: cleanup/cleanup_message.c. diff --git a/postfix/Makefile.in b/postfix/Makefile.in index 99b798e16..87a77d8da 100644 --- a/postfix/Makefile.in +++ b/postfix/Makefile.in @@ -8,10 +8,12 @@ DIRS = src/util src/global src/dns src/tls src/xsasl src/milter src/master \ src/pipe src/showq src/postalias src/postcat src/postconf src/postdrop \ src/postkick src/postlock src/postlog src/postmap src/postqueue \ src/postsuper src/qmqpd src/spawn src/flush src/verify \ - src/virtual src/proxymap src/anvil src/scache src/discard src/tlsmgr + src/virtual src/proxymap src/anvil src/scache src/discard src/tlsmgr \ + src/postmulti MANDIRS = proto man html LIBEXEC = libexec/post-install libexec/postfix-files libexec/postfix-script \ - libexec/postfix-wrapper libexec/main.cf libexec/master.cf + libexec/postfix-wrapper libexec/main.cf libexec/master.cf \ + libexec/postmulti-script default: update @@ -33,34 +35,25 @@ update printfck tests root_tests: update: $(LIBEXEC) libexec/post-install: conf/post-install - # XXX Work around broken hardlink implementations - rm -f $@ - cp $? $@ + rm -f $@ && ln -sf ../$? $@ libexec/postfix-files: conf/postfix-files - # XXX Work around broken hardlink implementations - rm -f $@ - cp $? $@ + rm -f $@ && ln -sf ../$? $@ libexec/postfix-script: conf/postfix-script - # XXX Work around broken hardlink implementations - rm -f $@ - cp $? $@ + rm -f $@ && ln -sf ../$? $@ libexec/postfix-wrapper: conf/postfix-wrapper - # XXX Work around broken hardlink implementations - rm -f $@ - cp $? $@ + rm -f $@ && ln -sf ../$? $@ libexec/main.cf: conf/main.cf - # XXX Work around broken hardlink implementations - rm -f $@ - cp $? $@ + rm -f $@ && ln -sf ../$? $@ libexec/master.cf: conf/master.cf - # XXX Work around broken hardlink implementations - rm -f $@ - cp $? $@ + rm -f $@ && ln -sf ../$? $@ + +libexec/postmulti-script: conf/postmulti-script + rm -f $@ && ln -sf ../$? $@ manpages: set -e; for i in $(MANDIRS); do \ diff --git a/postfix/README_FILES/BUILTIN_FILTER_README b/postfix/README_FILES/BUILTIN_FILTER_README index e0c73526b..de8fa7dd8 100644 --- a/postfix/README_FILES/BUILTIN_FILTER_README +++ b/postfix/README_FILES/BUILTIN_FILTER_README @@ -213,8 +213,8 @@ all mail and complicate Postfix maintenance. CCoonnffiigguurriinngg hheeaaddeerr//bbooddyy cchheecckkss ffoorr mmaaiill ffrroomm oouuttssiiddee uusseerrss oonnllyy -The following information applies to Postfix 2.1. Earlier Postfix versions do -not support the receive_override_options feature. +The following information applies to Postfix 2.1 and later. Earlier Postfix +versions do not support the receive_override_options feature. The easiest approach is to configure ONE Postfix instance with multiple SMTP server IP addresses in master.cf: @@ -235,6 +235,9 @@ server IP addresses in master.cf: pickup fifo n - n 60 1 pickup -o receive_override_options=no_header_body_checks + * Add some firewall rule to prevent access to 1.2.3.4:smtp from the outside + world. + * One SMTP server address for mail from outside users with header/body filtering turned on via main.cf. diff --git a/postfix/README_FILES/MULTI_INSTANCE_README b/postfix/README_FILES/MULTI_INSTANCE_README new file mode 100644 index 000000000..80a086c2b --- /dev/null +++ b/postfix/README_FILES/MULTI_INSTANCE_README @@ -0,0 +1,979 @@ +MMaannaaggiinngg mmuullttiippllee PPoossttffiixx iinnssttaanncceess oonn aa ssiinnggllee hhoosstt + +------------------------------------------------------------------------------- + +OOvveerrvviieeww + +This document is a guide to managing multiple Postfix instances on a single +host using the postmulti(1) instance manager. Multi-instance support is +available with Postfix version 2.6 and later. See the postfix-wrapper(5) manual +page for background on the instance management framework, and on how to deploy +a custom instance manager. + +Topics covered in this document: + + * Why multiple Postfix instances + * Null-client instances versus service instances + * Multi-instance walk-through + * Components of a Postfix system + * The default Postfix instance + * Instance groups + * Multi-instance configuration parameters + * Using the postmulti(1) command + * Credits + +WWhhyy mmuullttiippllee PPoossttffiixx iinnssttaanncceess + +Postfix is a general-purpose mail system that can be configured to serve a +variety of needs. Examples of Postfix applications are: + + * Local mail submission for shell users and system processes. + + * Incoming (MX host) email from the Internet. + + * Outbound mail relay for a corporate network. + + * Authenticated submission for roaming users. + + * Before/after content-filter mail. + +A single Postfix configuration can provide many or all of these services, but a +complex interplay of settings may be required, for example with master.cf +options overriding main.cf settings. In this document we take the view that +multiple Postfix instances may be a simpler way to configure a multi-function +Postfix system. With multiple Postfix instances, each instance has its own +directories for configuration, queue and data files, but it shares all Postfix +program and documentation files with other instances. + +Since there is no single right way to configure your system, we recommend that +you choose what makes you most comfortable. If different Postfix services don't +involve incompatible main.cf or master.cf settings, and if they can be combined +together without complex tricks, then a single monolithic configuration may be +the simplest approach. + +The purpose of multi-instance support in Postfix is not to force you to create +multiple Postfix instances, but rather to give you a choice. Multiple instances +give you the freedom to tune each Postfix instance to a single task that it +does well and to combine instances into complete systems. + +With the introduction of the postmulti(1) utility and the reduction of the per- +instance configuration footprint of a secondary Postfix instance to just a +main.cf and master.cf file (other files are now in shared locations), we hope +that multiple instances will be easier to use than ever before. + +NNuullll--cclliieenntt iinnssttaanncceess vveerrssuuss sseerrvviiccee iinnssttaanncceess + +In the multi-instance approach to configuring Postfix, the first simplification +is with the default local-submission Postfix instance. + +Most UNIX systems require support for email submission with the sendmail(1) +command so that system processes such as cron jobs can send status reports, and +so that system users can send email with command-line utilities. Such email can +be handled with a null-client Postfix configuration that forwards all mail to a +central mail hub. The null client will typically either not run an SMTP +listener at all (master_service_disable = inet), or it will listen only on the +loopback interface (inet_interfaces = loopback-only). + +When implementing specialized servers for inbound Internet email, outbound +MTAs, internal mail hubs, and so on, we recommend using a null client for local +submission and creating single-function secondary Postfix instances to serve +the specialized needs. + + Note: usually, you need to use different "myhostname" settings when you run + multiple instances on the same host. Otherwise, there will be false "mail + loops back to myself" alarms when one instance tries to send mail into + another instance. Typically, the null-client instance will use the system's + hostname, and other instances will use their own dedicated "myhostname" + settings. Different names are not needed when instances send mail to each + other with a protocol other than SMTP, or with SMTP over a TCP port other + than 25 as is usual with SMTP-based content filters. + +MMuullttii--iinnssttaannccee wwaallkk--tthhrroouugghh + +Before discussing the fine details of multi-instance operation we first show +the steps for creating a border mail server. This server has with a null-client +Postfix instance for local submission, an input Postfix instance to receive +mail from the Internet, plus an advanced SMTP content-filter and an output +Postfix instance to deliver filtered email to its internal destination. + +SSeettttiinngg uupp tthhee nnuullll--cclliieenntt PPoossttffiixx iinnssttaannccee + +On a border mail hub, while mail from the Internet requires a great deal of +scrutiny, locally submitted messages are typically limited to mail from cron +jobs and other system services. In this regard the border MTA is not different +from other Unix hosts in your environment. For this reason, it will submit +locally-generated email to the internal mail hub. We start the construction of +the border mail server with the default instance, which will be a local- +submission null client: + + /etc/postfix/main.cf: + # We are mta1.example.com + # + myhostname = mta1.example.com + mydomain = example.com + + # Flat user-account namespace in example.com: + # + # user@example.com not user@host.example.com + # + myorigin = $mydomain + + # Postfix 2.6+, disable inet services, specifically disable smtpd(8) + # + master_service_disable = inet + + # No local delivery: + # + mydestination = + local_transport = error:5.1.1 Mailbox unavailable + alias_database = + alias_maps = + local_recipient_maps = + + # Send everything to the internal mailhub + # + relayhost = [mailhub.example.com] + + # Indexed table macro: + # (use "hash", ... when cdb is not available) + # + default_database_type = cdb + indexed = ${default_database_type}:${config_directory}/ + + # Expose origin host of mail from "root", ... + # + smtp_generic_maps = ${indexed}generic + + # Send messages addressed to "root", ... to the MTA support team + # + virtual_alias_maps = ${indexed}virtual + + /etc/postfix/generic: + # The smarthost supports "+" addressing (recipient_delimiter = +). + # Mail from "root" exposes the origin host, without replies + # and bounces going back to the same host. + # + # On clustered MTAs this file is typically machine-built from + # a template file. The build process expands the template into + # "mtaadmin+root=mta1" + # + root mtaadmin+root=mta1 + + /etc/postfix/virtual: + # Caretaker aliases: + # + root mtaadmin + postmaster root + +You would typically also add a Makefile, to automatically run postmap(1) +commands when source files change. This Makefile also creates a "generic" +database when none exists. + + /etc/postfix/Makefile: + MTAADMIN=mtaadmin + + all: virtual.cdb generic.cdb + + generic: Makefile + @echo Creating $@ + @rm -f $@.tmp + @printf '%s\t%s+root=%s\n' root $MTAADMIN `uname -n` > $@.tmp + @mv $@.tmp generic + + %.cdb: % + postmap cdb:$< + +Construct the "virtual" and "generic" databases (the latter is created by +running "make"), then start and test the null-client: + + # cd /etc/postfix; make + # postfix start + # sendmail -i -f root -t < line. + Major changes with snapshot 20090212 ==================================== -Stress-depdent behavior by default. Under conditions of overload, +Stress-dependent behavior by default. Under conditions of overload, smtpd_timeout is reduced from 300s to to 10s, smtpd_hard_error_limit is reduced from 20 to 1, and smtpd_junk_command_limit is reduced from 100 to 1. This will reduce the delays for most legitimate mail. diff --git a/postfix/WISHLIST b/postfix/WISHLIST index 821ecf894..84f31dd8b 100644 --- a/postfix/WISHLIST +++ b/postfix/WISHLIST @@ -1,5 +1,49 @@ Wish list: + "postconf -N" option to print user-defined parameter names + (these have no defaults, since they exist only when + specified in main.cf or with "-o name=value"). + + Make the "unknown recipient" test configurable as + first|last|never, with "yes"=="last" for backwards + compatibility. The "first" setting is good for performance + (stress=yes) when all users are defined in local files. + + Make the double-bounce address time-dependent (with 24-hour + grace period). Spammers appear to use this address to avoid + DATA command rejects. Avoiding DATA rejects means they can + pipeline the entire SMTP session without triggering huge + numbers of protocol errors. They can still trigger "improper + command pipelining after DATA" alarms, but that requires + non-default main.cf settings. + + Cleanup: make DNSBL query format configurable beyond the + client's reversed IP address. + + With 'final delivery' in the LMTP client, need an option + to also add delivered-to and other pipe(8) features. + This requires making mail_copy() more generic. + + To work around historical AWK's limit of 10 open files, + pipe all output into a shell and have the shell open files. + It's too much pain to find out whose AWK is old and where + if any they keep the XPG4 compliant version. + + Cleanup: modernize the "add missing From: header" code, to + ``phrase '' form. Most likely, quote the entire phrase + if it contains any text that is special, then rfc822_externalize + the whole thing. + + SMTP server: make the server_addr and server_port port + available to policy server, Dovecot, and perhaps Milters. + + Maybe change maps_rbl_reject_code default to 521, and + update wording in STRESS_README. + + reject_unlisted_recipient = (yes | late | early | no) with + yes===late, for backwards compatibility. Ditto for + reject_unlisted_sender. + Set a flag when a remote SMTP client speaks before the Postfix SMTP server sends the 220 greeting. @@ -190,11 +234,6 @@ Wish list: playing with the soft_error test in the smtp_trouble.c module, and avoiding delivery to backup MX hosts. - In the SMTP server, set a "pipelining detected" flag at the - start of a session and at protocol synchronization points, - so that reject_unauth_pipelining can be specified in any - access rule. - Centralize main.cf parameter input so that defaults work consistently. What about parameter names that are prefixed with mail delivery transport names? diff --git a/postfix/conf/postfix-files b/postfix/conf/postfix-files index 617ebface..7ff93eafa 100644 --- a/postfix/conf/postfix-files +++ b/postfix/conf/postfix-files @@ -81,6 +81,7 @@ $daemon_directory/post-install:f:root:-:755 $daemon_directory/postfix-files:f:root:-:644 $daemon_directory/postfix-script:f:root:-:755 $daemon_directory/postfix-wrapper:f:root:-:755 +$daemon_directory/postmulti-script:f:root:-:755 $daemon_directory/proxymap:f:root:-:755 $daemon_directory/qmgr:f:root:-:755 $daemon_directory/qmqpd:f:root:-:755 @@ -103,6 +104,7 @@ $command_directory/postkick:f:root:-:755 $command_directory/postlock:f:root:-:755 $command_directory/postlog:f:root:-:755 $command_directory/postmap:f:root:-:755 +$command_directory/postmulti:f:root:-:755 $command_directory/postsuper:f:root:-:755 $command_directory/postdrop:f:root:$setgid_group:2755:u $command_directory/postqueue:f:root:$setgid_group:2755:u @@ -146,6 +148,7 @@ $manpage_directory/man1/postkick.1:f:root:-:644 $manpage_directory/man1/postlock.1:f:root:-:644 $manpage_directory/man1/postlog.1:f:root:-:644 $manpage_directory/man1/postmap.1:f:root:-:644 +$manpage_directory/man1/postmulti.1:f:root:-:644 $manpage_directory/man1/postqueue.1:f:root:-:644 $manpage_directory/man1/postsuper.1:f:root:-:644 $manpage_directory/man1/sendmail.1:f:root:-:644 @@ -255,6 +258,7 @@ $readme_directory/LOCAL_RECIPIENT_README:f:root:-:644 $readme_directory/MACOSX_README:f:root:-:644:o $readme_directory/MAILDROP_README:f:root:-:644 $readme_directory/MILTER_README:f:root:-:644 +$readme_directory/MULTI_INSTANCE_README:f:root:-:644 $readme_directory/MYSQL_README:f:root:-:644 $readme_directory/NFS_README:f:root:-:644 $readme_directory/OVERVIEW:f:root:-:644 @@ -304,6 +308,7 @@ $html_directory/LINUX_README.html:f:root:-:644 $html_directory/LOCAL_RECIPIENT_README.html:f:root:-:644 $html_directory/MAILDROP_README.html:f:root:-:644 $html_directory/MILTER_README.html:f:root:-:644 +$html_directory/MULTI_INSTANCE_README.html:f:root:-:644 $html_directory/MYSQL_README.html:f:root:-:644 $html_directory/NFS_README.html:f:root:-:644 $html_directory/OVERVIEW.html:f:root:-:644 @@ -371,6 +376,7 @@ $html_directory/postkick.1.html:f:root:-:644 $html_directory/postlock.1.html:f:root:-:644 $html_directory/postlog.1.html:f:root:-:644 $html_directory/postmap.1.html:f:root:-:644 +$html_directory/postmulti.1.html:f:root:-:644 $html_directory/postqueue.1.html:f:root:-:644 $html_directory/postsuper.1.html:f:root:-:644 $html_directory/qshape.1.html:f:root:-:644 diff --git a/postfix/conf/postfix-script b/postfix/conf/postfix-script index 7f9926100..30ddf1cbc 100644 --- a/postfix/conf/postfix-script +++ b/postfix/conf/postfix-script @@ -75,7 +75,8 @@ def_config_directory=`$command_directory/postconf -dh config_directory` || { # If this is a secondary instance, don't touch shared files. -instances=`$command_directory/postconf -c $def_config_directory \ +instances=`test ! -f $def_config_directory/main.cf || + $command_directory/postconf -c $def_config_directory \ -h multi_instance_directories | sed 's/,/ /'` || { $FATAL cannot execute $command_directory/postconf! exit 1 @@ -337,7 +338,7 @@ post-install) ;; *) - + $ERROR "unknown command: '$1'" $FATAL "usage: postfix start (or stop, reload, abort, flush, check, status, set-permissions, upgrade-configuration)" exit 1 ;; diff --git a/postfix/conf/postmulti-script b/postfix/conf/postmulti-script new file mode 100644 index 000000000..3f1333be3 --- /dev/null +++ b/postfix/conf/postmulti-script @@ -0,0 +1,300 @@ +#! /bin/sh + +umask 022 + +# postmulti(1) contract: +# +# Arguments: +# postmulti-script -e +# +# Environment: +# +# All actions: +# +# MAIL_CONFIG - config_directory of primary instance +# command_directory - From primary instance +# daemon_directory - From primary instance +# config_directroy - config_directory of target instance +# queue_directory - queue_directory of target instance +# data_directory - data_directory of target instance +# +# Create, destroy, import and deport: +# +# multi_instance_directories - New value for primary instance +# +# Create, import and assign (unset == nochange, "-" == clear): +# +# multi_instance_group - New value for target instance +# multi_instance_name - New value for target instance + +: ${MAIL_CONFIG:?"do not invoke this command directly"} +: ${command_directory:?"do not invoke this command directly"} +: ${daemon_directory:?"do not invoke this command directly"} + +USAGE="$0 -e create|destroy|import|deport|enable|disable|assign|init" +usage() { echo "$0: Error: Usage: $USAGE" >&2; exit 1; } + +TAG="$MAIL_LOGTAG/postmulti-script" +fatal() { postlog -p fatal -t "$TAG" "$1"; exit 1; } + +# args: add|del $dir +# +update_cfdirs() { + op=$1 + dir=$2 + + alt=`postconf -h alternate_config_directories` || return 1 + + shift $# # Needed on SunOS where bare "set --" is NOP! + IFS="$IFS,"; set -- $alt; IFS="$BACKUP_IFS" + keep= + found= + # Portability: SunOS "sh" needs 'in "$@"' for one-line for-loop. + for d in "$@"; do [ "$d" = "$dir" ] && found=1 || keep="$keep $d"; done + + set -- "multi_instance_directories = $multi_instance_directories" + + case $op in + add) test -n "$found" || + set -- "$@" "alternate_config_directories =$keep $dir";; + del) test -n "$found" && + set -- "$@" "alternate_config_directories =$keep";; + *) return 1;; # XXX: Internal error + esac + postconf -e "$@" || return 1 +} + +assign_names() { + # Set the instance name and group + # + test -n "$multi_instance_name" && { + test "$multi_instance_name" = "-" && multi_instance_name= + set -- "$@" "multi_instance_name = $multi_instance_name" + } + test -n "$multi_instance_group" && { + test "$multi_instance_group" = "-" && multi_instance_group= + set -- "$@" "multi_instance_group = $multi_instance_group" + } + test $# -eq 0 || postconf -c "$config_directory" -e "$@" || return 1 +} + +# Process command-line options and parameter settings. Work around +# brain damaged shells. "IFS=value command" should not make the +# IFS=value setting permanent. But some broken standard allows it. + +BACKUP_IFS="$IFS" +action= + +while getopts ":e:" opt +do + case $opt in + e) action="$OPTARG";; + *) usage;; + esac +done +shift `expr $OPTIND - 1` + +# Check for valid action and required instance name +case "$action" in + create|import|destroy|deport|enable|disable|assign|init) ;; + *) usage;; +esac +test $# -eq 0 || usage + +case $action in +init) + postconf -e \ + 'multi_instance_wrapper = ${command_directory}/postmulti -p --' \ + 'multi_instance_enable = yes' + exit $? ;; +esac + +: ${config_directory:?"Invalid empty target instance config_directory"} + +case $action in +create|import) + + # Atomically install stock main.cf/master.cf files. We install the + # master.cf file last. Once it is present the instance is complete. + # + test -f $config_directory/main.cf -a \ + -f $config_directory/master.cf || { + + test "$action" = "create" || { + test -f $config_directory/main.cf || + fatal "'$config_directory' lacks a main.cf file" + test -f $config_directory/master.cf || + fatal "'$config_directory' lacks a master.cf file" + } + + # Create instance-specific directories + # + test -d $config_directory || + { (umask 022; mkdir -p $config_directory) || exit 1; } + test -d $queue_directory || + { (umask 022; mkdir -p $queue_directory) || exit 1; } + test -d $data_directory || + { (umask 077; mkdir -p $data_directory) || exit 1; } + + tmpdir=$config_directory/.tmp + (umask 077; mkdir -p $tmpdir) || exit 1 + cp -p $daemon_directory/main.cf $tmpdir/main.cf || exit 1 + + # Shared install parameters are cloned from user-specified values in + # the default instance, but only if explicitly set there. Otherwise, + # they are commented out in the new main.cf file. + # + SHARED_PARAMETERS=" + command_directory + daemon_directory + mail_owner + setgid_group + sendmail_path + mailq_path + newaliases_path + html_directory + manpage_directory + sample_directory + readme_directory + " + + shift $# # Needed on SunOS where bare "set --" is NOP! + comment_out= + for p in $SHARED_PARAMETERS; do + val=`postconf -nh $p` || exit 1 + test -n "$val" && { set -- "$@" "$p = $val"; continue; } + comment_out="$comment_out $p" + done + + # First comment-out any parameters that take default values + test -n "$comment_out" && { + postconf -c $tmpdir -# $comment_out || exit 1 + } + + # Now add instance-specific and non-default values. + # By default, disable inet services and local submission + # in new instances + # + postconf -c $tmpdir -e \ + "queue_directory = $queue_directory" \ + "data_directory = $data_directory" \ + "authorized_submit_users =" \ + "master_service_disable = inet" \ + "$@" || exit 1 + + + cp -p $daemon_directory/master.cf $tmpdir/master.cf || exit 1 + mv $tmpdir/main.cf $config_directory/main.cf || exit 1 + mv $tmpdir/master.cf $config_directory/master.cf || exit 1 + rmdir $tmpdir 2>/dev/null + } + + # Set instance name and group + # + assign_names || exit 1 + + # Update multi_instance_directories + # and drop from alternate_config_directories + # + # XXX: Must happen before set-permissions below, otherwise instance + # is treated as a non-slave instance by post-install via postfix(1). + # + update_cfdirs del $config_directory || exit 1 + + # Update permissions of private files. Verifies existence of + # queue_directory and data_directory, ... + # + # XXX: Must happen after instance list updates above, otherwise instance + # is treated as a non-slave instance by post-install via postfix(1). + # + postfix -c $config_directory set-permissions || exit 1 + ;; + +deport) + # Deporting an already deleted instance? + # + [ -f "$config_directory/main.cf" ] || { + update_cfdirs del $config_directory + exit $? + } + + postfix -c "$config_directory" status >/dev/null 2>&1 && + fatal "Instance '$config_directory' is not stopped" + + # Update multi_instance_directories + # and add to alternate_config_directories + # + update_cfdirs add $config_directory || exit 1 + ;; + +destroy) + # Locate the target instance + # + [ -f "$config_directory/main.cf" ] || + fatal "$config_directory/main.cf file not found" + + postfix -c "$config_directory" status >/dev/null 2>&1 && + fatal "Instance '$config_directory' is not stopped" + + # XXX: Internal "postfix /some/cmd" interface via /bin/env for execvp(). + # + for q in maildrop incoming active deferred hold + do + postfix -c "$config_directory" /bin/env \ + find "$q" ! -name "$q" ! -name "?" -perm 0700 -print | + grep "^" >/dev/null && + fatal "Instance '$config_directory' $q queue is not empty" + done + + # Update multi_instance directories + # and also (just in case) drop from alternate_config_directories + # + update_cfdirs del $config_directory || exit 1 + + # Change default personalities: + MAIL_CONFIG="$config_directory"; export MAIL_CONFIG + + # Full steam ahead, instance will be at least partly destroyed! + + # Try to remove data_directory, but not sub-directories. + # Note: care with "$TAG" insertion into sh -c 'script'. + # + postfix /bin/sh -c \ + 'cd $data_directory; rm -f -- *; cd ..; rmdir $data_directory; \ + PATH=$command_directory:$PATH; export PATH; \ + test -d $data_directory && \ + postlog -p warn -t "'"$TAG"'" \ + "$data_directory partly removed" 2>&1' 2>/dev/null + + # Remove Postfix-owned files in the queue directory. + # Remove all files in the "pid" sub-directory. + # Remove empty directories. + # Note: care with "$TAG" insertion into sh -c 'script'. + postfix /bin/sh -c \ + 'find . -user $mail_owner ! -type d -exec rm -f -- "{}" ";"; \ + find . -depth -user $mail_owner -type d -exec rmdir -- "{}" ";"; \ + rm -f -- pid/*; rmdir *; cd ..; rmdir $queue_directory; \ + PATH=$command_directory:$PATH; export PATH; \ + test -d $queue_directory && \ + postlog -p warn -t "'"$TAG"'" \ + "$queue_directory partly removed" 2>&1' 2>/dev/null + + # In the configuration directory remove just the main.cf and master.cf + # files. + rm -f -- "$MAIL_CONFIG/master.cf" "$MAIL_CONFIG/main.cf" 2>/dev/null + rmdir -- "$MAIL_CONFIG" 2>/dev/null + test -d "$MAIL_CONFIG" && \ + postlog -p warn -t "$TAG" \ + "$MAIL_CONFIG partly removed" 2>&1 + ;; +enable) + postconf -c "$config_directory" -e \ + "multi_instance_enable = yes" || exit 1;; +disable) + postconf -c "$config_directory" -e \ + "multi_instance_enable = no" || exit 1;; +assign) + assign_names || exit 1;; +esac + +exit 0 diff --git a/postfix/html/BUILTIN_FILTER_README.html b/postfix/html/BUILTIN_FILTER_README.html index 91a9aeeee..22418cdce 100644 --- a/postfix/html/BUILTIN_FILTER_README.html +++ b/postfix/html/BUILTIN_FILTER_README.html @@ -218,9 +218,9 @@ header/body checks for mail to some domains only".

  • Header/body checks do not decode message headers or message body content. For example, if text in the message body is BASE64 -encoded (RFC 2045) then your regular expressions will have to match +encoded (RFC 2045) then your regular expressions will have to match the BASE64 encoded form. Likewise, message headers with encoded -non-ASCII characters (RFC 2047) need to be matched in their encoded +non-ASCII characters (RFC 2047) need to be matched in their encoded form.

  • Header/body checks cannot filter on a combination of @@ -348,7 +348,8 @@ Such rules slow down all mail and complicate Postfix maintenance.

    Configuring header/body checks for mail from outside users only

    -

    The following information applies to Postfix 2.1. Earlier +

    The following information applies to Postfix 2.1 and later. +Earlier Postfix versions do not support the receive_override_options feature.

    @@ -375,6 +376,9 @@ service with header/body filtering turned off.

    -o receive_override_options=no_header_body_checks +
  • Add some firewall rule to prevent access to 1.2.3.4:smtp +from the outside world.

    +
  • One SMTP server address for mail from outside users with header/body filtering turned on via main.cf.

    diff --git a/postfix/html/MULTI_INSTANCE_README.html b/postfix/html/MULTI_INSTANCE_README.html new file mode 100644 index 000000000..502b22da4 --- /dev/null +++ b/postfix/html/MULTI_INSTANCE_README.html @@ -0,0 +1,1272 @@ + + + + + + +Managing multiple Postfix instances on a single host + + + + + + + +

    Managing +multiple Postfix instances on a single host

    + +
    + +

    Overview

    + +

    This document is a guide to managing multiple Postfix instances +on a single host using the postmulti(1) instance manager. Multi-instance +support is available with Postfix version 2.6 and later. See the +postfix-wrapper(5) manual page for background on the instance +management framework, and on how to deploy a custom instance manager. +

    + +

    Topics covered in this document:

    + + + +

    Why multiple Postfix instances

    + +

    Postfix is a general-purpose mail system that can be configured +to serve a variety of needs. Examples of Postfix applications are:

    + +
      + +
    • Local mail submission for shell users and system processes.

      + +
    • Incoming (MX host) email from the Internet.

      + +
    • Outbound mail relay for a corporate network.

      + +
    • Authenticated submission for roaming users.

      + +
    • Before/after content-filter mail.

      + +
    + +

    A single Postfix configuration can provide many or all of these +services, but a complex interplay of settings may be required, for +example with master.cf options overriding main.cf settings. In this +document we take the view that multiple Postfix instances may be a +simpler way to configure a multi-function Postfix system. With +multiple Postfix instances, each instance has its own directories +for configuration, queue and data files, but it shares all Postfix +program and documentation files with other instances.

    + +

    Since there is no single right way to configure your system, +we recommend that you choose what makes you most comfortable. If +different Postfix services don't involve incompatible main.cf or +master.cf settings, and if they can be combined together without +complex tricks, then a single monolithic configuration may be the +simplest approach.

    + +

    The purpose of multi-instance support in Postfix is not to force +you to create multiple Postfix instances, but rather to give you a +choice. Multiple instances give you the freedom to tune each Postfix +instance to a single task that it does well and to combine instances +into complete systems.

    + +

    With the introduction of the postmulti(1) utility and the reduction +of the per-instance configuration footprint of a secondary Postfix +instance to just a main.cf and master.cf file (other files are now in +shared locations), we hope that multiple instances will be easier to +use than ever before.

    + +

    Null-client instances versus service instances

    + +

    In the multi-instance approach to configuring Postfix, the first +simplification is with the default local-submission Postfix instance. +

    + +

    Most UNIX systems require support for email submission with the +sendmail(1) command so that system processes such as cron jobs can +send status reports, and so that system users can send email with +command-line utilities. Such email can be handled with a null-client +Postfix configuration that forwards all mail to a central mail hub. +The null client will typically either not run an SMTP listener at +all (master_service_disable = inet), or it will listen only on the +loopback interface (inet_interfaces = loopback-only).

    + +

    When implementing specialized servers for inbound Internet +email, outbound MTAs, internal mail hubs, and so on, we recommend +using a null client for local submission and creating single-function +secondary Postfix instances to serve the specialized needs.

    + +
    + +

    Note: usually, you need to use different "myhostname" settings +when you run multiple instances on the same host. Otherwise, there +will be false "mail loops back to myself" alarms when one instance +tries to send mail into another instance. Typically, the null-client +instance will use the system's hostname, and other instances will +use their own dedicated "myhostname" settings. Different names are +not needed when instances send mail to each other with a protocol +other than SMTP, or with SMTP over a TCP port other than 25 as is +usual with SMTP-based content filters.

    + +
    + +

    Multi-instance walk-through

    + +

    Before discussing the fine details of multi-instance operation +we first show the steps for creating a border mail server. This +server has with a null-client Postfix instance for local submission, +an input Postfix instance to receive mail from the Internet, plus +an advanced SMTP +content-filter and an output Postfix instance to deliver filtered +email to its internal destination.

    + +

    Setting up the null-client Postfix instance

    + +

    On a border mail hub, while mail from the Internet requires a +great deal of scrutiny, locally submitted messages are typically +limited to mail from cron jobs and other system services. In this +regard the border MTA is not different from other Unix hosts in +your environment. For this reason, it will submit locally-generated +email to the internal mail hub. We start the construction of the +border mail server with the default +instance, which will be a local-submission null client: +

    + +
    +
    +/etc/postfix/main.cf:
    +    # We are mta1.example.com
    +    #
    +    myhostname = mta1.example.com
    +    mydomain = example.com
    +
    +    # Flat user-account namespace in example.com:
    +    #
    +    #   user@example.com not user@host.example.com
    +    #
    +    myorigin = $mydomain
    +
    +    # Postfix 2.6+, disable inet services, specifically disable smtpd(8)
    +    #
    +    master_service_disable = inet
    +
    +    # No local delivery:
    +    #
    +    mydestination =
    +    local_transport = error:5.1.1 Mailbox unavailable
    +    alias_database =
    +    alias_maps =
    +    local_recipient_maps =
    +
    +    # Send everything to the internal mailhub
    +    #
    +    relayhost = [mailhub.example.com]
    +
    +    # Indexed table macro:
    +    # (use "hash", ... when cdb is not available)
    +    #
    +    default_database_type = cdb
    +    indexed = ${default_database_type}:${config_directory}/
    +
    +    # Expose origin host of mail from "root", ...
    +    #
    +    smtp_generic_maps = ${indexed}generic
    +
    +    # Send messages addressed to "root", ... to the MTA support team
    +    #
    +    virtual_alias_maps = ${indexed}virtual
    +
    +/etc/postfix/generic:
    +    # The smarthost supports "+" addressing (recipient_delimiter = +).
    +    # Mail from "root" exposes the origin host, without replies
    +    # and bounces going back to the same host.
    +    #
    +    # On clustered MTAs this file is typically machine-built from
    +    # a template file. The build process expands the template into
    +    # "mtaadmin+root=mta1"
    +    #
    +    root    	mtaadmin+root=mta1
    +
    +/etc/postfix/virtual:
    +    # Caretaker aliases:
    +    #
    +    root    	mtaadmin
    +    postmaster	root
    +
    +
    + +

    You would typically also add a Makefile, to automatically run +postmap(1) commands when source files change. This Makefile also +creates a "generic" database when none exists.

    + +
    +
    +/etc/postfix/Makefile:
    +    MTAADMIN=mtaadmin
    +
    +    all: virtual.cdb generic.cdb
    +
    +    generic: Makefile
    +	    @echo Creating $@
    +	    @rm -f $@.tmp
    +	    @printf '%s\t%s+root=%s\n' root $MTAADMIN `uname -n` > $@.tmp
    +	    @mv $@.tmp generic
    +
    +    %.cdb: %
    +	    postmap cdb:$<
    +
    +
    + +

    Construct the "virtual" and "generic" databases (the latter is +created by running "make"), then start and test the null-client: +

    + +
    +
    +# cd /etc/postfix; make
    +# postfix start
    +# sendmail -i -f root -t <<EOF
    +From: root
    +To: root
    +Subject: test
    +
    +testing
    +EOF
    +
    +
    + +

    The test message should be delivered the members of the "mtaadmin" +address group (or whatever address group you choose) with the +following headers:

    + +
    +
    +From: mtaadmin+root=mta1@example.com
    +To: mtadmin+root=mta1@example.com
    +Subject: test
    +
    +
    + +

    Setting up the "output" Postfix instance

    + +

    With the null-client instance out of the way, we can create the +MTA "output" instance that will deliver filtered mail to the inside +network. We add the "output" instance first, because the output +instance needs to be up and running before the input instance can +be fully tested, and when the system boots, the "output" instance +must start before the input instance. We will put the output and +input instances into a single instance group named "mta".

    + +

    Just once, when adding the first secondary instance, enable +multi-instance support in the default (null-client) instance:

    + +
    +
    +# postmulti -e init
    +
    +
    + +

    Then create the output instance:

    + +

    +
    +# postmulti -I postfix-out -G mta -e create
    +
    +
    + +

    The instance configuration directory defaults to /etc/postfix-out, +more precisely, the "postfix-out" subdirectory of the parent directory +of the default-instance configuration directory. The new instance will +be created in a "disabled" state:

    + +
    +
    +/etc/postfix-out/main.cf
    +    #
    +    # ... "stock" main.cf settings ...
    +    #
    +    multi_instance_name = postfix-out
    +    queue_directory = /var/spool/postfix-out
    +    data_directory = /var/lib/postfix-out
    +    #
    +    multi_instance_enable = no
    +    master_service_disable = inet
    +    authorized_submit_users =
    +
    +
    + +

    This instance has a "stock" master.cf file, and its queue and +data directories, also named "postfix-out", will be located in the +same parent directories as the corresponding directories of the +default instance (e.g., /var/spool/postfix-out and /var/lib/postfix-out). +

    + +

    While this instance is immediately safe to start, it is not yet +usefully configured. It needs to be customized to fit the role of a +post-filter re-injection SMTP service. Typical additions include:

    + +
    +
    +/etc/postfix-out/master.cf:
    +    # Replace default "smtp inet" entry with one listening on port 10026.
    +    127.0.0.1:10026     inet  n       -       n       -       -       smtpd
    +
    +/etc/postfix-out/main.cf
    +    # ...
    +
    +    # Comment out if you don't use IPv6 internally
    +    # inet_protocols = ipv4
    +    inet_interfaces = loopback-only
    +    mynetworks_style = host
    +    smtpd_authorized_xforward_hosts = $mynetworks
    +
    +    # Don't anvil(8) control the re-injection port.
    +    #
    +    smtpd_client_connection_count_limit = 0
    +    smtpd_client_event_limit_exceptions = $mynetworks
    +
    +    # Best practice when inet_interfaces is set, as this is not a
    +    # "secondary IP personality" configuration.
    +    #
    +    smtp_bind_address = 0.0.0.0
    +
    +    # All header rewriting happens upstream
    +    #
    +    local_header_rewrite_clients =
    +
    +    # No local delivery on border gateway
    +    #
    +    mydestination =
    +    alias_maps =
    +    alias_database =
    +    local_recipient_maps =
    +    local_transport = error:5.1.1 Mailbox unavailable
    +
    +    # May need a recipient_delimiter for per-user transport lookups:
    +    #
    +    recipient_delimiter = +
    +
    +    # Only one (unrestricted client)
    +    # With multiple instances, rarely need "-o param=value" overrides
    +    # in master.cf, each instance gets its own main.cf file.
    +    #
    +    smtpd_recipient_restrictions = permit_mynetworks, reject
    +
    +    # Tolerate occasional high latency in the  content filter.
    +    #
    +    smtpd_timeout = 1200s
    +
    +    # Best when empty, with all parent domain matches explicit.
    +    #
    +    parent_domain_matches_subdomains =
    +
    +    # Use the "relay" transport for inbound mail, and the default
    +    # "smtp" transport for outbound mail (bounces, ...). The latter
    +    # won't starve the former of delivery agent slots.
    +    #
    +    relay_domains = example.com, .example.com
    +
    +    # With xforward, match the input instance setting, if you
    +    # want "yes", set both to "yes".
    +    #
    +    smtpd_client_port_logging = no
    +
    +    # Transport settings ...
    +    # Message size limit
    +    # Concurrency tuning for "relay" and "smtp" transport
    +    # ...
    +
    +
    + +

    With the "output" configuration in place, enable and start the +instance:

    + +
    +
    +1 # postmulti -i postfix-out -x postconf -e \
    +2     "master_service_disable =" "authorized_submit_users = root"
    +3 # postmulti -i postfix-out -e enable
    +4 # postmulti -i postfix-out -p start
    +
    +
    + +

    This uses the postmulti(1) command to invoke postconf(1) in the +context (MAIL_CONFIG=/etc/postfix-out) of the output instance.

    + +
      + +
    • Lines 1-2: With "authorized_submit_users = root", the +superuser can test the postix-out instance with "postmulti -i +postfix-out -x sendmail -bv recipient...", but otherwise local +submission remains disabled.

      + +
    • Lines 1-2: With "master_service_disable =", the "inet" +listeners are re-enabled.

      + +
    • Line 3: The output instance is enabled for multi-instance +start/stop.

      + +
    • Line 4: The output instance is started.

      + +
    + +

    Test the output instance by submitting probe messages via "sendmail +-bv" and "telnet". For production systems, in-depth configuration tests +should be done on a lab system. The simple tests just suggested will only +confirm successful deployment of a configuration that should already be +known good.

    + +

    Setting up the content-filter proxy

    + +

    With the output instance ready, deploy your content-filter +proxy. Most proxies will need their own /etc/rc* start/stop script. +Some proxies, however, are started on demand by the Postfix spawn(8) +service, in which case you need to add the relevant spawn(8) entry +to the output instance master.cf file.

    + +

    Configure the proxy to listen on 127.0.0.1:10025 and to re-inject +filtered email to 127.0.0.1:10026. Start the proxy service if +necessary, then test the proxy via "telnet" or automated SMTP +injectors. The proxy should support the following ESMTP features: +DSN, 8BITMIME, and XFORWARD. In addition, the proxy should support +multiple mail deliveries within an SMTP session.

    + +

    Setting up the input Postfix instance

    + +

    The input Postfix instance receives mail from the network and +sends it through the content filter. Now we create the input instance, +also part of the "mta" instance group:

    + +
    +
    +# postmulti -I postfix-in -G mta -e create
    +
    +
    + +

    The new instance configuration directory defaults to /etc/postfix-in, +more precisely, the "postfix-in" subdirectory of the parent directory +of the default-instance configuration directory. The new instance will +be created in a "disabled" state:

    + +
    +
    +/etc/postfix-in/main.cf
    +    #
    +    # ... "stock" main.cf settings ...
    +    #
    +    multi_instance_name = postfix-in
    +    queue_directory = /var/spool/postfix-in
    +    data_directory = /var/lib/postfix-in
    +    #
    +    multi_instance_enable = no
    +    master_service_disable = inet
    +    authorized_submit_users =
    +
    +
    + +

    As before, make appropriate changes to main.cf and master.cf to +make the instance production ready. Consider setting "soft_bounce = yes" +during the first few hours of deployment, so you can iron-out any unexpected +"kinks".

    + +

    Manual testing can start with: + +

    +
    +/etc/postfix-in/main.cf
    +    # Accept only local traffic, but allow impersonation:
    +    inet_interfaces = 127.0.0.1
    +    smtpd_authorized_xclient_hosts = 127.0.0.1
    +
    +
    + +

    This allows you to use the Postfix-specific XCLIENT SMTP command to safely +simulate connections from remote systems before any remote systems +are able to connect. If the test results look good, revert the above +settings to the required production values. Typical settings in the +pre-filter input instance include:

    + +
    +
    +/etc/postfix-in/main.cf
    +    #
    +    # ... 
    +    #
    +
    +    # No local delivery on border gateway
    +    #
    +    mydestination =
    +    alias_maps =
    +    alias_database =
    +    local_recipient_maps =
    +    local_transport = error:5.1.1 Mailbox unavailable
    +
    +    # Don't rewrite remote headers
    +    #
    +    local_header_rewrite_clients =
    +
    +    # All recipients of not yet filtered email go to the same filter together.
    +    #
    +    # With multiple instances, the content-filter is specified
    +    # via transport settings not the "content_filter" transport
    +    # switch override! Here the filter listens on local port 10025.
    +    #
    +    # If you need to route some users or recipient domains directly to the
    +    # output instance bypassing the filter, just define a transport table
    +    # with suitable entries.
    +    #
    +    default_transport = smtp:[127.0.0.1]:10025
    +    relay_transport = $default_transport
    +    virtual_transport = $default_transport
    +    transport_maps =
    +
    +    # Pass original client log information through the filter.
    +    #
    +    smtp_send_xforward_command = yes
    +
    +    # Avoid splitting the envelope and scanning messages multiple times.
    +    # Match the re-injection server's recipient limit.
    +    #
    +	smtp_destination_recipient_limit = 1000
    +
    +    # Tolerate occasional high latency in the content filter.
    +    #
    +    smtp_data_done_timeout = 1200s
    +
    +    # With xforward, match the output instance setting, if you
    +    # want "yes", set both to "yes".
    +    #
    +    smtpd_client_port_logging = no
    +
    +    # ... Lots of settings for inbound MX host ...
    +
    +
    + +

    With the "input" instance configured, enable and start it:

    + +
    +
    +# postmulti -i postfix-in -x postconf -e \
    +    "master_service_disable =" "authorized_submit_users = root"
    +# postmulti -i postfix-in -e enable
    +# postmulti -i postfix-in -p start
    +
    +
    + +

    That's it. You now have a 3-instance configuration. A null-client +sending all locally submitted mail to the internal mail hub and a pair of +"mta" instances that receive mail from the Internet, pass it through a +content-filter, and then deliver it to the internal destination.

    + +

    Running "postfix start" or "postfix stop" will now start/stop all +three Postfix instances. You can use "postfix -c /config/path start" +to start just one instance, or use the instance name (or instance +group name) via postmulti(1):

    + +
    +
    +# postmulti -i - -p stop
    +# postmulti -g mta -p status
    +# postmulti -i postfix-out -p flush
    +# postmulti -i postfix-in -p reload
    +# ...
    +
    +
    + +

    This example ends the multi-instance "walk through". The remainder +of this document provides background information on Postfix +multi-instance support features and options.

    + +

    Components of a Postfix system

    + +

    A Postfix system consists of the following components:

    + +

    Shared among all instances:

    + + + +

    Private to each instance:

    + + + +

    The Postfix configuration parameters mentioned above are +collectively referred to as "installation parameters". Their default +values are set when the Postfix software is built from source, and +all but one may be optionally set to a non-default value via the +main.cf file. The one parameter that (catch-22) cannot be set in +main.cf is $config_directory, as this defines the location of the +main.cf file itself.

    + +

    Though config_directory cannot be set in main.cf, postfix(1) and +most of the other command-line Postfix utilities allow you to specify a +non-default configuration directory via a command line option (typically +-c) or via the MAIL_CONFIG environment variable. In this way, +it is possible to have multiple configuration directories on the same +machine, and to have multiple running master(8) daemons each with its +own configuration files, queue directory and data directory.

    + +

    These multiple running copies of master(8) share the base Postfix +software. They do not (and cannot) share their configuration +directories, queue directories or data directories.

    + +

    Each combination of configuration directory, together with the queue +directory and data directory (specified in the corresponding main.cf file) +make up a Postfix instance.

    + +

    The default Postfix instance

    + +

    One Postfix instance is special: this is the instance whose +configuration directory is the default one compiled into the Postfix +utilities. The location of the default configuration directory is +typically /etc/postfix, and can be queried via the "postconf -d +config_directory" command. We call the instance with this configuration +directory the "default instance".

    + +

    The default instance is responsible for local mail submission. The +setgid postdrop(1) utility is used by the sendmail(1) local submission +program to spool messages into the maildrop sub-directory of the +queue directory of the default instance.

    + +

    Even in the rare case when "sendmail -C" is used to submit local mail +into a non-default Postfix instance, for security reasons, postdrop(1) +will consult the default main.cf file to check the validity of the +requested non-default configuration directory.

    + +

    So, while in most other respects, all instances are equal, the +default instance is "more equal than others". You may choose to create +additional instances, but you must have at least the default instance, +with its configuration directory in the default compiled-in location.

    + +

    Instance groups

    + +

    The postmulti(1) multi-instance manager supports the notion of an +instance "group". Typically, the member instances of an instance group +constitute a logical service, and are expected to all be running or all +be stopped.

    + +

    In many cases a single Postfix instance will be a complete logical +"service". You should define such instances as stand-alone instances +that are not members of any instance "group". The null-client +instance is an example of a non-group instance.

    + +

    When a logical service consists of multiple Postfix instances, +often a pair of pre-filter and post-filter instances with a content +filter proxy between them, the related instances should be members +of a single instance group (however, the content filter usually has +its own start/stop procedure that is separate from any Postfix +instance).

    + +

    The default instance main.cf file's $multi_instance_directories +configuration parameter lists the configuration directories of all +secondary (non-default) instances. Together with the default instance, +these secondary instances are managed by the multi-instance manager. +Instances are started in the order listed, and stopped in the +opposite order. For instances that are members of a service "group", +you should arrange to start the service back-to-front, with the +output stages started and ready to receive mail before the input +stages are started.

    + +

    Multi-instance configuration parameters

    + +
    + +
    multi_instance_wrapper
    + +

    This default-instance configuration parameter must be set +to a suitable multi-instance manager's "wrapper" program that +controls the starting, stopping, etc. of a multi-instance Postfix +system. To use the postmulti(1) manager described in this document, +this parameter should be set with the "postmulti +-e init" command.

    + +
    multi_instance_directories
    + +

    This default-instance configuration parameter specifies +an optional list of the secondary instances controlled via the +multi-instance manager. Instances are listed in their "start" order, +with the default instance always started first (if enabled). If +$multi_instance_directories is left empty, the postfix(1) command +runs with multi-instance support turned off, and none of the +multi_instance_ configuration parameters will have any effect.

    + +

    Do not assign a non-empty list of secondary instance configuration +directories to multi_instance_directories until you have configured a +suitable multi_instance_wrapper setting! This is best accomplished via +the "postmulti -e init" command. +

    + +
    multi_instance_name
    + +

    Each Postfix instance may be assigned a distinct name (with +"postfix -e create/import/assign -I name..."). This name can +be used with the postmulti(1) command-line utility to perform tasks +on the instance by name (rather than the full pathname of its +configuration directory). Choose a name that concisely captures the +role of the instance (it must start with "postfix-"). It is an +error for two instances to have the same $multi_instance_name. You +can leave an instance "nameless" by leaving this parameter at the +default empty setting.

    + +

    To avoid confusion in your logs, if you don't assign each +secondary instance a non-empty (distinct) $multi_instance_name, you +should make sure that the $syslog_name setting is different for +each instance. The $syslog_name parameter defaults to $multi_instance_name +when the latter is non-empty. If at all possible, the syslog_name +should start with "postfix-", this helps log parsers to identify +log entries from secondary Postfix instances.

    + +
    multi_instance_group
    + +

    Each Postfix instance may be assigned an "instance group" +name (with "postfix -e create/import/assign -G name..."). +The default (empty) value of multi_instance_group parameter indicates +a stand-alone instance that is not part of any group. The group +name can be used with the postmulti(1) command-line utility to +perform a task on the members of a group by name. Choose a single-word +group name that concisely captures the role of the group.

    +
    + +
    multi_instance_enable
    + +

    This parameter controls whether a Postfix instance will +be started by a Postfix multi-instance manager. The default value +is "no". The instance can be started explicitly with "postfix -c +/path/to/config/directory"; this is useful for testing.

    + +

    When an instance is disabled, the postfix(1) "start" command +is replaced by "check".

    + +

    Some postfix(1) commands (such as "stop", "flush", ...) require +a running Postfix instance, and skip instances that are disabled. +

    + +

    Other postfix(1) commands (such as "status", "set-permissions", +"upgrade-configuration", ...) do not require a running Postfix +system, and apply to all instances whether enabled or not.

    +
    + +
    + +

    The postmulti(1) utility can be used to create (or destroy) instances. +It can also be used to "import" or "deport" existing instances into or +from the list of managed instances. When using postmulti(1) to manage +instances, the above configuration parameters are managed for you +automatically. See below.

    + +

    Using the postmulti(1) command

    + + + +

    Initializing the multi-instance manager

    + +

    Before postmulti(1) is used for the first time, you must install +it as the multi_instance_wrapper for your Postfix system and enable +multi-instance operation of the default Postfix instance. You can then +proceed to add new or existing +instances to the multi-instance configuration. This initial installation +is accomplished as follows:

    + +
    +
    +    # postmulti -e init
    +
    +
    + +

    This updates the default instance main.cf file as follows:

    + +
    +
    +    # Use postmulti(1) as a postfix-wrapper(5)
    +    #
    +    multi_instance_wrapper = ${command_directory}/postmulti -p --
    +
    +    # Configure the default instance to start when in multi-instance mode
    +    #
    +    multi_instance_enable = yes
    +
    +
    + +

    If you prefer, you can make these changes by editing the default +main.cf directly, or by using "postconf -e".

    + +

    Listing managed instances

    + +

    The list of managed instances consists of the default instance and +the additional instances whose configuration directories are listed +(in start order) under the multi_instance_directories parameter of the +default main.cf configuration file.

    + +

    You can list selected instances, groups of instances or all +instances by specifying only the instance matching options with the +"-l" option. The "-a" option is assumed if no other instance +selection options are specified (this behavior changes with the +"-e" option). As a special case, even if it has an explicit name, +the default instance can always be selected via "-i -".

    + +
    +
    +# postmulti -l -a
    +# postmulti -l -g a_group
    +# postmulti -l -i an_instance
    +
    +
    + +

    The output is one line per instance (in "postfix start" order): +

    + +
    + + + + + + +
    name group enabled config_directory
    - - yes /etc/postfix + +
    mta-out mta yes /etc/postfix/mta-out + +
    mta-in mta yes /etc/postfix-mta-in + +
    msa-out msa yes /etc/postfix-msa-out + +
    msa-in msa yes /etc/postfix-msa-in + +
    test - no /etc/postfix-test + +
    + +
    + +

    The first line showing the column headings is not part of the +output. When either the instance name or the instance group is not +set, it is shown as a "-".

    + +

    When selecting an existing instance via the "-i" option, you +can always use the full pathname of its configuration directory +instead of the instance (short) name. This is the only way to select +a non-default nameless instance. The default instance can be selected +via "-i -", whether it has a name or not.

    + +

    To list instances in reverse start order, include the "-R" +option together with the instance selection options.

    + +

    Starting or stopping a multi-instance system +

    + +

    To start, stop, reload, etc. the complete (already configured as +above) multi-instance system just use postfix(1) as you would with a +single-instance system. The Postfix multi-instance wrapper framework +insulates Postfix init.d start and package upgrade scripts from the +details of multi-instance management!

    + +

    The -p option of postmulti(1) turns on postfix(1) compatibility +mode. With this option the remaining arguments are exactly those supported +by postfix(1), but commands are applied to all instances or all enabled +instances as appropriate. As described above, this switch is required +when using postmulti(1) as the multi_instance_wrapper.

    + +

    If you want to specify a subset of instances by name, or group name, +or run arbitrary commands (not just "postfix stop/start/etc. in the +context (MAIL_CONFIG environment variable setting) of a particular +instance or group of instances, then you can use the instance-aware +postmulti(1) utility directly.

    + +

    Ad-hoc multi-instance operations

    + +

    The postmulti(1) command can be used by the administrator to run arbitrary +commands in the context of one or more Postfix instances. The most common +use-case is stopping or starting a group of Postfix instances:

    + +
    +
    +# postmulti -g mygroup -p start
    +# postmulti -g mygroup -p flush
    +# postmulti -g mygroup -p reload
    +# postmulti -g mygroup -p status
    +# postmulti -g mygroup -p stop
    +# postmulti -g mygroup -p upgrade-configuration
    +
    +
    + +

    The -p option is essentially a short-hand for a leading +postfix command argument, but with appropriate additional options +turned on depending on the first argument. In the case of "start", +disabled instances are "checked" (postfix check) rather than simply +skipped.

    + +

    The resulting command is executed for each candidate instance with +the MAIL_CONFIG environment variable set to the configuration +directory of the corresponding Postfix instance.

    + +

    The postmulti(1) utility is able to launch commands other than +postfix(1), Use the -x option to ask postmulti to execute an +ad-hoc command for all instances, a group of instances, or just one +instance. With ad-hoc commands the multi_instance_enable parameter +is ignored: the command is unconditionally executed for the instances +selected via -a, -g or -i. In addition to MAIL_CONFIG, the following +instance parameters are exported into the command environment:

    + +
    +
    +command_directory=$command_directory
    +daemon_directory=$daemon_directory
    +config_directory=$config_directory
    +queue_directory=$queue_directory
    +data_directory=$data_directory
    +multi_instance_name=$multi_instance_name
    +multi_instance_group=$multi_instance_group
    +multi_instance_enable=$multi_instance_enable
    +
    +
    + +

    The config_directory setting is of course the same as MAIL_CONFIG, +and is arguably redundant, but leaving it in is less surprising. If +you want to skip disabled instances, just check multi_instance_enable +environment variable and exit if it is set to "no".

    + +

    The ability to run ad-hoc commands opens up a wealth of additional +possibilities:

    + +
      + +
    • Specify an instance by name rather than configuration directory +when using sendmail(1) to send a verification probe:

      + +
      +
      +$ postmulti -i postfix-myinst -x sendmail -bv test@example.net
      +
      +
      + +
    • Display non-default main.cf settings of all Postfix instances. +This uses an inline shell script to package together multiple shell +commands to execute for each instance:

      + +
      +
      +$ postmulti -x sh -c 'echo "-- $MAIL_CONFIG"; postconf -n'
      +
      +
      + +
    • Put all mail in enabled member instances of a group on hold:

      + +
      +
      +# postmulti -g group_name -x \
      +    sh -c 'test $multi_instance_enable = yes && postsuper -h ALL'
      +
      +
      + +
    • Show top 10 domains in the deferred queue of all instances: +

      + +
      +
      +# postmulti -x sh -c 'echo "-- $MAIL_CONFIG"; qshape deferred | head -12'
      +
      +
      + +
    + +

    Creating a new Postfix instance

    + +

    The postmulti(1) command can be used to create additional Postfix +instances. New instances are created with local submission and all "inet" +services disabled via the following non-default parameter settings in +the main.cf file:

    + +
    +
    +authorized_submit_users =
    +master_service_disable = inet
    +
    +
    + +

    The above settings ensure that new instances are safe to start +immediately: they will not conflict with inet listeners in existing +Postfix instances. They will also not accept any mail until they are +fully configured, at which point you can do away with one or both of +the above safety measures.

    + +

    The postmulti(1) command encourages a preferred way of organizing +the configuration directories, queue directories and data directories +of non-default instances. If the default instance settings are:

    + +
    +
    +config_directory = /conf-path/postfix
    +queue_directory = /queue-path/postfix
    +data_directory = /data-path/postfix
    +
    +
    + +

    A newly-created instance named postfix-myinst will by default +have:

    + +
    +
    +multi_instance_enable = no
    +multi_instance_name = postfix-myinst
    +config_directory = /conf-path/postfix-myinst
    +queue_directory = /queue-path/postfix-myinst
    +data_directory = /data-path/postfix-myinst
    +
    +
    + +

    You can override any of these defaults when creating the instance, +but unless you want to spread instance queue directories over multiple +file-systems, use the default naming strategy. It keeps the multiple +instances organized in a uniform, predictable fashion.

    + +

    When specifying the instance name later, you can refer to it +either as "postfix-myinst", or via the full path of the configuration +directory.

    + +

    To create a new instance just use the -e create option:

    + +
    +
    +# postmulti -I postfix-myinst -e create
    +
    +
    + +

    If the new instance is to belong to a group of related instances that +implement a single logical service, assign it to a group:

    + +
    +
    +# postmulti -I postfix-myinst -G mygroup -e create
    +
    +
    + +

    If you want to override the conventional values of the instance +installation parameters, specify their values on the command-line:

    + +
    +
    +# postmulti [-I postfix-myinst] [-G mygroup] -e create \
    +	"config_directory = /path/to/config_directory" \
    +	"queue_directory = /path/to/queue_directory" \
    +	"data_directory = /path/to/data_directory"
    +
    +
    + +

    A note on the -I and -G options above. These are always +used to assign a name or group name to an instance, while the -i +and -g options always select existing instances. By default, +the configuration directories of newly managed instances are appended +to the instance list. You can use the "-i" or "-g" or "-a" options to +insert the new instance before the specified instance or group, or at +the beginning of the instance list (multi_instance_directories parameter +of the default instance).

    + +

    If you do specify a name (use "-I" with a name that is not "-") +for the new instance, you may omit any of the 3 instance installation +parameters whose instance-name based value is acceptable. Otherwise, all +three instance installation parameters are required. You should set the +"syslog_name" explicitly in the main.cf file of a "nameless" instance, +in order to avoid confusion in the mail logs when multiple instances +are in use.

    + +

    Destroying a Postfix instance

    + +

    If you no longer need an instance, you can destroy it via:

    + +
    +
    +# postmulti -i postfix-myinst -p stop
    +# postmulti -i postfix-myinst -e disable
    +# postmulti -i postfix-myinst -e destroy
    +
    +
    + +

    The instance must be stopped, disabled and have no queued messages. +This is expected to fully delete a just created instance that has never +been used. If the instance is not freshly created, files added after +the instance was created will remain in the configuration, queue or +data directories, in which case the corresponding directory may not +be fully removed and a warning to that effect will be displayed. You +can complete the destruction of the instance manually by removing any +unwanted remnants of the instance-specific "private" directories.

    + +

    Importing an existing Postfix instance

    + +

    If you already have an existing secondary Postfix instance that is +not yet managed via postmulti(1), you can "import" it into the list +of managed instances. If your instance is already using the default +configuration directory naming scheme, just specify the corresponding +instance name (the multi_instance_name parameter in its configuration +file will be adjusted to match this name if necessary):

    + +
    +
    +# postmulti -I postfix-myinst [-G mygroup] -e import
    +
    +
    + +

    Otherwise, you must specify the location of its configuration +directory:

    + +
    +
    +# postmulti [-I postfix-myinst] [-G mygroup] -e import \
    +	"config_directory = /path/of/config_directory"
    +
    +
    + +

    When the instance is imported, you can assign a name or a group. As +with "create", you can control the placement of the +new instance in the start order by using "-i", "-g" or "-a" to prepend +before the selected instance or instances.

    + +

    An imported instance is usually not multi-instance "enabled", +unless it was part of a multi-instance configuration at an earlier +time. If it is fully configured and ready to run, don't forget +to enable it and if necessary start it. When +other enabled instances are already running, new instances need to +be started individually when they are first created or imported. +

    + +

    To find out what instances are running, use:

    + +
    +
    +# postfix status
    +
    +
    + +

    Deporting a managed Postfix instance

    + +

    You can "deport" an existing instance from the list of managed +instances. This does not destroy the instance, rather the instance +just becomes a stand-alone Postfix instance not registered with the +multi-instance manager. postmulti(1) will refuse to "deport" an +instance that is not stopped and disabled.

    + +
    +
    +# postmulti -i postfix-myinst -p stop
    +# postmulti -i postfix-myinst -e disable
    +# postmulti -i postfix-myinst -e deport
    +
    +
    + +

    Assigning a new name or group name

    + +

    You can assign a new name or new group to a managed instance. +Use "-" as the new value to assign the instance to no group or make it +nameless. To specify a nameless secondary instance use the configuration +directory path instead of the old name:

    + +
    +
    +# postmulti -i postfix-old [-I postfix-new] [-G newgroup] -e assign
    +
    +
    + +

    Enabling/disabling managed instances

    + +

    You can enable or disable a managed instance. As documented in +postfix-wrapper(5), disabled instances are skipped with actions +that start, +stop or control running +Postfix instances.

    + +
    +
    +# postmulti -i postfix-myinst -e enable
    +# postmulti -i postfix-myinst -e disable
    +
    +
    + +

    Credits

    + +

    Wietse Venema created Postfix, designed and implemented the +multi-instance wrapper framework and provided design feedback that made +the postmulti(1) utility much more general and useful than originally +envisioned.

    + +

    The postmulti(1) utility was developed by Victor Duchovni of Morgan +Stanley, who also wrote the initial version of this document.

    + + diff --git a/postfix/html/Makefile.in b/postfix/html/Makefile.in index fadc13b6a..7d20df6bb 100644 --- a/postfix/html/Makefile.in +++ b/postfix/html/Makefile.in @@ -10,9 +10,10 @@ DAEMONS = bounce.8.html cleanup.8.html defer.8.html error.8.html local.8.html \ scache.8.html discard.8.html tlsmgr.8.html COMMANDS= mailq.1.html newaliases.1.html postalias.1.html postcat.1.html \ postconf.1.html postfix.1.html postkick.1.html postlock.1.html \ - postlog.1.html postdrop.1.html postmap.1.html sendmail.1.html \ - postqueue.1.html postsuper.1.html smtp-source.1.html \ - smtp-sink.1.html qmqp-source.1.html qmqp-sink.1.html \ + postlog.1.html postdrop.1.html postmap.1.html postmulti.1.html \ + postqueue.1.html postsuper.1.html sendmail.1.html \ + smtp-source.1.html smtp-sink.1.html \ + qmqp-source.1.html qmqp-sink.1.html \ qshape.1.html CONFIG = access.5.html aliases.5.html canonical.5.html relocated.5.html \ transport.5.html virtual.5.html pcre_table.5.html regexp_table.5.html \ @@ -177,6 +178,10 @@ postmap.1.html: ../src/postmap/postmap.c PATH=../mantools:$$PATH; \ srctoman $? | $(AWK) | nroff -man | uniq | $(MAN2HTML) | postlink >$@ +postmulti.1.html: ../src/postmulti/postmulti.c + PATH=../mantools:$$PATH; \ + srctoman $? | $(AWK) | nroff -man | uniq | $(MAN2HTML) | postlink >$@ + postqueue.1.html: ../src/postqueue/postqueue.c PATH=../mantools:$$PATH; \ srctoman $? | $(AWK) | nroff -man | uniq | $(MAN2HTML) | postlink >$@ diff --git a/postfix/html/OVERVIEW.html b/postfix/html/OVERVIEW.html index 5c8ff198f..7a332d9c1 100644 --- a/postfix/html/OVERVIEW.html +++ b/postfix/html/OVERVIEW.html @@ -728,6 +728,11 @@ for shell scripts.

    such as canonical(5), virtual(5) and others. It is a cousin of the UNIX makemap command.

    +
  • The postmulti(1) command repeats the "postfix start" etc. +command for each Postfix instance, and supports creation, deletion +etc. of Postfix instances. For a tutorial, see MULTI_INSTANCE_README. +

    +
  • The postqueue(1) command is the privileged command that is run by Postfix sendmail(1) and mailq(1) in order to flush or list the diff --git a/postfix/html/STANDARD_CONFIGURATION_README.html b/postfix/html/STANDARD_CONFIGURATION_README.html index ef7365d86..12d22ca06 100644 --- a/postfix/html/STANDARD_CONFIGURATION_README.html +++ b/postfix/html/STANDARD_CONFIGURATION_README.html @@ -111,7 +111,7 @@ their default settings.

    1 /etc/postfix/main.cf: 2 myorigin = $mydomain 3 relayhost = $mydomain -4 inet_interfaces = 127.0.0.1 +4 inet_interfaces = loopback-only 5 local_transport = error:local delivery is disabled 6 7 /etc/postfix/master.cf: diff --git a/postfix/html/STRESS_README.html b/postfix/html/STRESS_README.html index 809f1c4f3..0a090a2c3 100644 --- a/postfix/html/STRESS_README.html +++ b/postfix/html/STRESS_README.html @@ -226,10 +226,11 @@ clients get a chance to talk to Postfix.

      -
    • Use "421" reply codes for botnet-related RBLs or for -selected non-RBL restrictions. This causes Postfix 2.3 and later -to disconnect immediately without waiting for the remote SMTP -client to send a QUIT command.

      +
    • Use "521" reply codes (Postfix 2.6 and later) for +botnet-related RBLs or for selected non-RBL restrictions. With +Postfix 2.3-2.5 use "421" for a similar result. The Postfix SMTP +server will disconnect immediately without waiting for the remote +SMTP client to send a QUIT command.

      You can set individual reject codes for RBLs, and for individual responses from a specific RBL. We'll use zen.spamhaus.org as an @@ -237,7 +238,7 @@ example; by the time you read this document, details may have changed. Right now, their documents say that a response of 127.0.0.10 or 127.0.0.11 indicates a dynamic client IP address, which means that the machine is probably running a bot of some kind. To give -a 421 response instead of the default 554 response, use something +a 521 response instead of the default 554 response, use something like:

      @@ -251,11 +252,11 @@ like: 

      8 rbl_reply_maps = hash:/etc/postfix/rbl_reply_maps 9 10 /etc/postfix/rbl_reply_maps: -11 zen.spamhaus.org=127.0.0.10 421 4.7.1 Service unavailable; +11 zen.spamhaus.org=127.0.0.10 521 4.7.1 Service unavailable; 12 $rbl_class [$rbl_what] blocked using 13 $rbl_domain${rbl_reason?; $rbl_reason} 14 -15 zen.spamhaus.org=127.0.0.11 421 4.7.1 Service unavailable; +15 zen.spamhaus.org=127.0.0.11 521 4.7.1 Service unavailable; 16 $rbl_class [$rbl_what] blocked using 17 $rbl_domain${rbl_reason?; $rbl_reason}
      @@ -264,7 +265,8 @@ like:

      will still only do a single DNS query, so the performance difference is negligible.

      -

      The down-side of sending 421 instead of the default 554 is that +

      With Postfix 2.3-2.5, use 421 (reply code 521 will not cause +Postfix to disconnect). The down-side of sending 421 is that it works only for zombies and other malware. If the client is running a real MTA, then it may connect again several times until the mail expires in its queue. When this is a problem, stick with the default diff --git a/postfix/html/cleanup.8.html b/postfix/html/cleanup.8.html index 57236fc7c..4eabdae18 100644 --- a/postfix/html/cleanup.8.html +++ b/postfix/html/cleanup.8.html @@ -95,41 +95,47 @@ CLEANUP(8) CLEANUP(8) and is always turned on with older Postfix ver- sions). + Available in Postfix version 2.6 and later: + + always_add_missing_headers (no) + Always add (Resent-) From:, To:, Date: or Message- + ID headers when not present. + BUILT-IN CONTENT FILTERING CONTROLS - Postfix built-in content filtering is meant to stop a - flood of worms or viruses. It is not a general content + Postfix built-in content filtering is meant to stop a + flood of worms or viruses. It is not a general content filter. body_checks (empty) - Optional lookup tables for content inspection as + Optional lookup tables for content inspection as specified in the body_checks(5) manual page. header_checks (empty) - Optional lookup tables for content inspection of - primary non-MIME message headers, as specified in + Optional lookup tables for content inspection of + primary non-MIME message headers, as specified in the header_checks(5) manual page. Available in Postfix version 2.0 and later: body_checks_size_limit (51200) How much text in a message body segment (or attach- - ment, if you prefer to use that term) is subjected + ment, if you prefer to use that term) is subjected to body_checks inspection. mime_header_checks ($header_checks) - Optional lookup tables for content inspection of - MIME related message headers, as described in the + Optional lookup tables for content inspection of + MIME related message headers, as described in the header_checks(5) manual page. nested_header_checks ($header_checks) - Optional lookup tables for content inspection of - non-MIME message headers in attached messages, as + Optional lookup tables for content inspection of + non-MIME message headers in attached messages, as described in the header_checks(5) manual page. Available in Postfix version 2.3 and later: message_reject_characters (empty) - The set of characters that Postfix will reject in + The set of characters that Postfix will reject in message content. message_strip_characters (empty) @@ -138,23 +144,23 @@ CLEANUP(8) CLEANUP(8) BEFORE QUEUE MILTER CONTROLS As of version 2.3, Postfix supports the Sendmail version 8 - Milter (mail filter) protocol. When mail is not received - via the smtpd(8) server, the cleanup(8) server will simu- - late SMTP events to the extent that this is possible. For + Milter (mail filter) protocol. When mail is not received + via the smtpd(8) server, the cleanup(8) server will simu- + late SMTP events to the extent that this is possible. For details see the MILTER_README document. non_smtpd_milters (empty) A list of Milter (mail filter) applications for new - mail that does not arrive via the Postfix smtpd(8) + mail that does not arrive via the Postfix smtpd(8) server. milter_protocol (2) - The mail filter protocol version and optional pro- - tocol extensions for communication with a Milter + The mail filter protocol version and optional pro- + tocol extensions for communication with a Milter (mail filter) application. milter_default_action (tempfail) - The default action when a Milter (mail filter) + The default action when a Milter (mail filter) application is unavailable or mis-configured. milter_macro_daemon_name ($myhostname) @@ -166,55 +172,55 @@ CLEANUP(8) CLEANUP(8) cations. milter_connect_timeout (30s) - The time limit for connecting to a Milter (mail - filter) application, and for negotiating protocol + The time limit for connecting to a Milter (mail + filter) application, and for negotiating protocol options. milter_command_timeout (30s) - The time limit for sending an SMTP command to a + The time limit for sending an SMTP command to a Milter (mail filter) application, and for receiving the response. milter_content_timeout (300s) - The time limit for sending message content to a + The time limit for sending message content to a Milter (mail filter) application, and for receiving the response. - milter_connect_macros (see postconf -n output) - The macros that are sent to Milter (mail filter) - applications after completion of an SMTP connec- + milter_connect_macros (see 'postconf -d' output) + The macros that are sent to Milter (mail filter) + applications after completion of an SMTP connec- tion. - milter_helo_macros (see postconf -n output) - The macros that are sent to Milter (mail filter) + milter_helo_macros (see 'postconf -d' output) + The macros that are sent to Milter (mail filter) applications after the SMTP HELO or EHLO command. - milter_mail_macros (see postconf -n output) - The macros that are sent to Milter (mail filter) + milter_mail_macros (see 'postconf -d' output) + The macros that are sent to Milter (mail filter) applications after the SMTP MAIL FROM command. - milter_rcpt_macros (see postconf -n output) - The macros that are sent to Milter (mail filter) + milter_rcpt_macros (see 'postconf -d' output) + The macros that are sent to Milter (mail filter) applications after the SMTP RCPT TO command. - milter_data_macros (see postconf -n output) - The macros that are sent to version 4 or higher - Milter (mail filter) applications after the SMTP + milter_data_macros (see 'postconf -d' output) + The macros that are sent to version 4 or higher + Milter (mail filter) applications after the SMTP DATA command. - milter_unknown_command_macros (see postconf -n output) - The macros that are sent to version 3 or higher - Milter (mail filter) applications after an unknown + milter_unknown_command_macros (see 'postconf -d' output) + The macros that are sent to version 3 or higher + Milter (mail filter) applications after an unknown SMTP command. - milter_end_of_data_macros (see postconf -n output) - The macros that are sent to Milter (mail filter) + milter_end_of_data_macros (see 'postconf -d' output) + The macros that are sent to Milter (mail filter) applications after the message end-of-data. Available in Postfix version 2.5 and later: - milter_end_of_header_macros (see postconf -n output) - The macros that are sent to Milter (mail filter) + milter_end_of_header_macros (see 'postconf -d' output) + The macros that are sent to Milter (mail filter) applications after the end of the message header. MIME PROCESSING CONTROLS @@ -232,27 +238,27 @@ CLEANUP(8) CLEANUP(8) will handle. strict_8bitmime (no) - Enable both strict_7bit_headers and strict_8bit- + Enable both strict_7bit_headers and strict_8bit- mime_body. strict_7bit_headers (no) Reject mail with 8-bit text in message headers. strict_8bitmime_body (no) - Reject 8-bit message body text without 8-bit MIME + Reject 8-bit message body text without 8-bit MIME content encoding information. strict_mime_encoding_domain (no) Reject mail with invalid Content-Transfer-Encoding: - information for the message/* or multipart/* MIME + information for the message/* or multipart/* MIME content types. Available in Postfix version 2.5 and later: detect_8bit_encoding_header (yes) Automatically detect 8BITMIME body content by look- - ing at Content-Transfer-Encoding: message headers; - historically, this behavior was hard-coded to be + ing at Content-Transfer-Encoding: message headers; + historically, this behavior was hard-coded to be "always on". AUTOMATIC BCC RECIPIENT CONTROLS @@ -260,31 +266,31 @@ CLEANUP(8) CLEANUP(8) mail enters the mail system: always_bcc (empty) - Optional address that receives a "blind carbon + Optional address that receives a "blind carbon copy" of each message that is received by the Post- fix mail system. Available in Postfix version 2.1 and later: sender_bcc_maps (empty) - Optional BCC (blind carbon-copy) address lookup + Optional BCC (blind carbon-copy) address lookup tables, indexed by sender address. recipient_bcc_maps (empty) - Optional BCC (blind carbon-copy) address lookup + Optional BCC (blind carbon-copy) address lookup tables, indexed by recipient address. ADDRESS TRANSFORMATION CONTROLS - Address rewriting is delegated to the trivial-rewrite(8) - daemon. The cleanup(8) server implements table driven + Address rewriting is delegated to the trivial-rewrite(8) + daemon. The cleanup(8) server implements table driven address mapping. empty_address_recipient (MAILER-DAEMON) - The recipient of mail addressed to the null + The recipient of mail addressed to the null address. canonical_maps (empty) - Optional address mapping lookup tables for message + Optional address mapping lookup tables for message headers and envelopes. recipient_canonical_maps (empty) @@ -295,49 +301,49 @@ CLEANUP(8) CLEANUP(8) Optional address mapping lookup tables for envelope and header sender addresses. - masquerade_classes (envelope_sender, header_sender, + masquerade_classes (envelope_sender, header_sender, header_recipient) What addresses are subject to address masquerading. masquerade_domains (empty) - Optional list of domains whose subdomain structure + Optional list of domains whose subdomain structure will be stripped off in email addresses. masquerade_exceptions (empty) - Optional list of user names that are not subjected - to address masquerading, even when their address + Optional list of user names that are not subjected + to address masquerading, even when their address matches $masquerade_domains. propagate_unmatched_extensions (canonical, virtual) - What address lookup tables copy an address exten- + What address lookup tables copy an address exten- sion from the lookup key to the lookup result. Available before Postfix version 2.0: virtual_maps (empty) Optional lookup tables with a) names of domains for - which all addresses are aliased to addresses in - other local or remote domains, and b) addresses - that are aliased to addresses in other local or + which all addresses are aliased to addresses in + other local or remote domains, and b) addresses + that are aliased to addresses in other local or remote domains. Available in Postfix version 2.0 and later: virtual_alias_maps ($virtual_maps) - Optional lookup tables that alias specific mail - addresses or domains to other local or remote + Optional lookup tables that alias specific mail + addresses or domains to other local or remote address. Available in Postfix version 2.2 and later: - canonical_classes (envelope_sender, envelope_recipient, + canonical_classes (envelope_sender, envelope_recipient, header_sender, header_recipient) - What addresses are subject to canonical_maps + What addresses are subject to canonical_maps address mapping. recipient_canonical_classes (envelope_recipient, header_recipient) - What addresses are subject to recipient_canoni- + What addresses are subject to recipient_canoni- cal_maps address mapping. sender_canonical_classes (envelope_sender, header_sender) @@ -345,15 +351,15 @@ CLEANUP(8) CLEANUP(8) address mapping. remote_header_rewrite_domain (empty) - Don't rewrite message headers from remote clients + Don't rewrite message headers from remote clients at all when this parameter is empty; otherwise, re- - write message headers and append the specified + write message headers and append the specified domain name to incomplete addresses. RESOURCE AND RATE CONTROLS duplicate_filter_limit (1000) - The maximal number of addresses remembered by the - address duplicate filter for aliases(5) or vir- + The maximal number of addresses remembered by the + address duplicate filter for aliases(5) or vir- tual(5) alias expansion, or for showq(8) queue dis- plays. @@ -362,16 +368,16 @@ CLEANUP(8) CLEANUP(8) message header. hopcount_limit (50) - The maximal number of Received: message headers + The maximal number of Received: message headers that is allowed in the primary message headers. in_flow_delay (1s) - Time to pause before accepting a new message, when + Time to pause before accepting a new message, when the message arrival rate exceeds the message deliv- ery rate. message_size_limit (10240000) - The maximal size in bytes of a message, including + The maximal size in bytes of a message, including envelope information. Available in Postfix version 2.0 and later: @@ -389,35 +395,35 @@ CLEANUP(8) CLEANUP(8) will handle. queue_file_attribute_count_limit (100) - The maximal number of (name=value) attributes that + The maximal number of (name=value) attributes that may be stored in a Postfix queue file. Available in Postfix version 2.1 and later: virtual_alias_expansion_limit (1000) - The maximal number of addresses that virtual alias + The maximal number of addresses that virtual alias expansion produces from each original recipient. virtual_alias_recursion_limit (1000) - The maximal nesting depth of virtual alias expan- + The maximal nesting depth of virtual alias expan- sion. MISCELLANEOUS CONTROLS config_directory (see 'postconf -d' output) - The default location of the Postfix main.cf and + The default location of the Postfix main.cf and master.cf configuration files. daemon_timeout (18000s) - How much time a Postfix daemon process may take to - handle a request before it is terminated by a + How much time a Postfix daemon process may take to + handle a request before it is terminated by a built-in watchdog timer. delay_logging_resolution_limit (2) - The maximal number of digits after the decimal + The maximal number of digits after the decimal point when logging sub-second delay values. delay_warning_time (0h) - The time after which the sender receives the mes- + The time after which the sender receives the mes- sage headers of mail that is still queued. ipc_timeout (3600s) @@ -425,13 +431,13 @@ CLEANUP(8) CLEANUP(8) over an internal communication channel. max_idle (100s) - The maximum amount of time that an idle Postfix - daemon process waits for an incoming connection + The maximum amount of time that an idle Postfix + daemon process waits for an incoming connection before terminating voluntarily. max_use (100) - The maximal number of incoming connections that a - Postfix daemon process will service before termi- + The maximal number of incoming connections that a + Postfix daemon process will service before termi- nating voluntarily. myhostname (see 'postconf -d' output) @@ -439,19 +445,19 @@ CLEANUP(8) CLEANUP(8) myorigin ($myhostname) The domain name that locally-posted mail appears to - come from, and that locally posted mail is deliv- + come from, and that locally posted mail is deliv- ered to. process_id (read-only) - The process ID of a Postfix command or daemon + The process ID of a Postfix command or daemon process. process_name (read-only) - The process name of a Postfix command or daemon + The process name of a Postfix command or daemon process. queue_directory (see 'postconf -d' output) - The location of the Postfix top-level queue direc- + The location of the Postfix top-level queue direc- tory. soft_bounce (no) @@ -461,15 +467,15 @@ CLEANUP(8) CLEANUP(8) syslog_facility (mail) The syslog facility of Postfix logging. - syslog_name (postfix) - The mail system name that is prepended to the - process name in syslog records, so that "smtpd" + syslog_name (see 'postconf -d' output) + The mail system name that is prepended to the + process name in syslog records, so that "smtpd" becomes, for example, "postfix/smtpd". Available in Postfix version 2.1 and later: enable_original_recipient (yes) - Enable support for the X-Original-To message + Enable support for the X-Original-To message header. FILES @@ -493,7 +499,7 @@ CLEANUP(8) CLEANUP(8) CONTENT_INSPECTION_README content inspection LICENSE - The Secure Mailer license must be distributed with this + The Secure Mailer license must be distributed with this software. AUTHOR(S) diff --git a/postfix/html/lmtp.8.html b/postfix/html/lmtp.8.html index 77d062520..1588febfa 100644 --- a/postfix/html/lmtp.8.html +++ b/postfix/html/lmtp.8.html @@ -730,76 +730,82 @@ SMTP(8) SMTP(8) The time limit for sending or receiving information over an internal communication channel. + lmtp_assume_final (no) + When an LMTP server announces no DSN support, + assume that the server performs final delivery, and + send "delivered" delivery status notifications + instead of "relayed". + lmtp_tcp_port (24) - The default TCP port that the Postfix LMTP client + The default TCP port that the Postfix LMTP client connects to. max_idle (100s) - The maximum amount of time that an idle Postfix - daemon process waits for an incoming connection + The maximum amount of time that an idle Postfix + daemon process waits for an incoming connection before terminating voluntarily. max_use (100) - The maximal number of incoming connections that a - Postfix daemon process will service before termi- + The maximal number of incoming connections that a + Postfix daemon process will service before termi- nating voluntarily. process_id (read-only) - The process ID of a Postfix command or daemon + The process ID of a Postfix command or daemon process. process_name (read-only) - The process name of a Postfix command or daemon + The process name of a Postfix command or daemon process. proxy_interfaces (empty) The network interface addresses that this mail sys- - tem receives mail on by way of a proxy or network + tem receives mail on by way of a proxy or network address translation unit. smtp_bind_address (empty) - An optional numerical network address that the - Postfix SMTP client should bind to when making an + An optional numerical network address that the + Postfix SMTP client should bind to when making an IPv4 connection. smtp_bind_address6 (empty) - An optional numerical network address that the - Postfix SMTP client should bind to when making an + An optional numerical network address that the + Postfix SMTP client should bind to when making an IPv6 connection. smtp_helo_name ($myhostname) - The hostname to send in the SMTP EHLO or HELO com- + The hostname to send in the SMTP EHLO or HELO com- mand. lmtp_lhlo_name ($myhostname) The hostname to send in the LMTP LHLO command. smtp_host_lookup (dns) - What mechanisms when the Postfix SMTP client uses + What mechanisms when the Postfix SMTP client uses to look up a host's IP address. smtp_randomize_addresses (yes) - Randomize the order of equal-preference MX host + Randomize the order of equal-preference MX host addresses. syslog_facility (mail) The syslog facility of Postfix logging. - syslog_name (postfix) - The mail system name that is prepended to the - process name in syslog records, so that "smtpd" + syslog_name (see 'postconf -d' output) + The mail system name that is prepended to the + process name in syslog records, so that "smtpd" becomes, for example, "postfix/smtpd". Available with Postfix 2.2 and earlier: fallback_relay (empty) - Optional list of relay hosts for SMTP destinations + Optional list of relay hosts for SMTP destinations that can't be found or that are unreachable. Available with Postfix 2.3 and later: smtp_fallback_relay ($fallback_relay) - Optional list of relay hosts for SMTP destinations + Optional list of relay hosts for SMTP destinations that can't be found or that are unreachable. SEE ALSO @@ -820,7 +826,7 @@ SMTP(8) SMTP(8) TLS_README, Postfix STARTTLS howto LICENSE - The Secure Mailer license must be distributed with this + The Secure Mailer license must be distributed with this software. AUTHOR(S) diff --git a/postfix/html/mailq.1.html b/postfix/html/mailq.1.html index ce6de4e40..c4efe7302 100644 --- a/postfix/html/mailq.1.html +++ b/postfix/html/mailq.1.html @@ -450,7 +450,7 @@ SENDMAIL(1) SENDMAIL(1) syslog_facility (mail) The syslog facility of Postfix logging. - syslog_name (postfix) + syslog_name (see 'postconf -d' output) The mail system name that is prepended to the process name in syslog records, so that "smtpd" becomes, for example, "postfix/smtpd". @@ -477,7 +477,7 @@ SENDMAIL(1) SENDMAIL(1) VERP_README, Postfix VERP howto LICENSE - The Secure Mailer license must be distributed with this + The Secure Mailer license must be distributed with this software. AUTHOR(S) diff --git a/postfix/html/newaliases.1.html b/postfix/html/newaliases.1.html index ce6de4e40..c4efe7302 100644 --- a/postfix/html/newaliases.1.html +++ b/postfix/html/newaliases.1.html @@ -450,7 +450,7 @@ SENDMAIL(1) SENDMAIL(1) syslog_facility (mail) The syslog facility of Postfix logging. - syslog_name (postfix) + syslog_name (see 'postconf -d' output) The mail system name that is prepended to the process name in syslog records, so that "smtpd" becomes, for example, "postfix/smtpd". @@ -477,7 +477,7 @@ SENDMAIL(1) SENDMAIL(1) VERP_README, Postfix VERP howto LICENSE - The Secure Mailer license must be distributed with this + The Secure Mailer license must be distributed with this software. AUTHOR(S) diff --git a/postfix/html/postconf.5.html b/postfix/html/postconf.5.html index 8caab334c..487e5abb8 100644 --- a/postfix/html/postconf.5.html +++ b/postfix/html/postconf.5.html @@ -677,6 +677,18 @@ and postdrop(1).

      + + +
      always_add_missing_headers +(default: no)
      + +

      Always add (Resent-) From:, To:, Date: or Message-ID: headers +when not present. Postfix 2.6 and later add these headers only +when clients match the local_header_rewrite_clients parameter +setting. Earlier Postfix versions always add these headers; this +may break DKIM signatures that cover non-existent headers.

      + +
      always_bcc @@ -3432,6 +3444,18 @@ This feature is available in Postfix 2.1 and later. this length; upon delivery, long lines are reconstructed.

      + + +
      lmtp_assume_final +(default: no)
      + +

      When an LMTP server announces no DSN support, assume that the +server performs final delivery, and send "delivered" delivery status +notifications instead of "relayed". The default setting is backwards +compatible to avoid the infinetisimal possibility of breaking +existing LMTP-based content filters.

      + +
      lmtp_bind_address @@ -5810,11 +5834,16 @@ Postfix instance, and that are started, stopped, etc., together with the default Postfix instance. Specify a list of pathnames separated by comma or whitespace.

      -

      When the list of non-default Postfix configuration directories -is non-empty, the postfix(1) command will invoke the multi-instance -manager specified with the multi_instance_wrapper parameter to -execute commands on the default instance and on all additional -Postfix instances.

      +

      When $multi_instance_directories is empty, the postfix(1) command +runs in single-instance mode and operates on a single Postfix +instance only. Otherwise, the postfix(1) command runs in multi-instance +mode and invokes the multi-instance manager specified with the +multi_instance_wrapper parameter. The multi-instance manager in +turn executes postfix(1) commands for the default instance and for +all Postfix instances in $multi_instance_directories.

      + +

      Currently, this parameter setting is ignored except for the +default main.cf file.

      This feature is available in Postfix 2.6 and later.

      @@ -6320,6 +6349,43 @@ is rejected by the reject_

      This feature is available in Postfix 2.3 and later.

      + + +
      postmulti_control_commands +(default: reload flush)
      + +

      The postfix(1) commands that the postmulti(1) instance manager +treats as "control" commands, that operate on running instances. For +these commands, disabled instances are skipped.

      + +

      This feature is available in Postfix 2.6 and later.

      + + +
      + +
      postmulti_start_commands +(default: start)
      + +

      The postfix(1) commands that the postmulti(1) instance manager treats +as "start" commands. For these commands, disabled instances are "checked" +rather than "started", and failure to "start" a member instance of an +instance group will abort the start-up of later instances.

      + +

      This feature is available in Postfix 2.6 and later.

      + + +
      + +
      postmulti_stop_commands +(default: see "postconf -d" output)
      + +

      The postfix(1) commands that the postmulti(1) instance manager treats +as "stop" commands. For these commands, disabled instances are skipped, +and enabled instances are processed in reverse order.

      + +

      This feature is available in Postfix 2.6 and later.

      + +
      prepend_delivered_header diff --git a/postfix/html/postfix-manuals.html b/postfix/html/postfix-manuals.html index b1c5efcf7..92fc12202 100644 --- a/postfix/html/postfix-manuals.html +++ b/postfix/html/postfix-manuals.html @@ -94,7 +94,7 @@ the following convention:

    • postmap(1), Postfix lookup table manager -
    • postmulti(1), Postfix multi-instance manager +
    • postmulti(1), Postfix multi-instance manager
    • postqueue(1), Postfix mail queue control diff --git a/postfix/html/postfix-wrapper.5.html b/postfix/html/postfix-wrapper.5.html index 8f829f3da..645ab28ca 100644 --- a/postfix/html/postfix-wrapper.5.html +++ b/postfix/html/postfix-wrapper.5.html @@ -17,36 +17,31 @@ POSTFIX-WRAPPER(5) POSTFIX-WRAPPER(5) This document describes how the familiar "postfix start" etc. user interface can be used to manage one or multiple - Postfix instances, and gives details of an API that allows - the postfix(1) command to coordinate activities with a + Postfix instances, and gives details of an API to coordi- + nate activities between the postfix(1) command and a multi-instance manager program. - A trivial but useful multi-instance manager implementation - is described below, and can be found in the file $dae- - mon_directory/postfix-wrapper. The latter file also con- - tains instructions for setting up multiple instances. - - With multi-instance support, the default Postfix instance - is required. The location of its configuration files is - specified by the built-in default value for the con- - fig_directory parameter. + With multi-instance support, the default Postfix instance + is always required. The config_directory parameter's + default value specifies that instance's configuration file + location. GENERAL OPERATION - Multi-instance support is backwards compatible: when there - is only one Postfix instance, commands such as "postfix - start" keep doing what they have always done. + Multi-instance support is backwards compatible: when you + run only one Postfix instance, commands such as "postfix + start" will not change behavior at all. - Even after multi-instance support has been set up through - the mechanisms discussed later, sites can continue to use - the familiar postfix commands in boot scripts, upgrade - procedures, and other places. + Even with multiple Postfix instances, you can keep using + the same postfix commands in boot scripts, upgrade proce- + dures, and other places. The commands do more work, but + humans are not forced to learn new tricks. - To start all applicable Postfix instances, use: + For example, to start all Postfix instances, use: # postfix start Other postfix(1) commands also work as expected. For exam- - ple, to find out what Postfix instances exist in a multi- + ple, to find out what Postfix instances exist in a multi- instance configuration, use: # postfix status @@ -55,51 +50,64 @@ POSTFIX-WRAPPER(5) POSTFIX-WRAPPER(5) a multi-instance configuration. MANAGING AN INDIVIDUAL POSTFIX INSTANCE - To operate on a specific Postfix instance, specify its - configuration directory on the postfix(1) command line: + To manage a specific Postfix instance, specify its config- + uration directory on the postfix(1) command line: # postfix -c /path/to/config_directory command - Alternatively, the postfix(1) command accepts the - instance's configuration directory via the MAIL_CONFIG + Alternatively, the postfix(1) command accepts the + instance's configuration directory via the MAIL_CONFIG environment variable (the -c command-line option has higher precedence). - When no Postfix instance information is specified, the - postfix(1) command will operate on all applicable Postfix - instances. + When no Postfix instance information is specified, the + postfix(1) command will operate on all Postfix instances. -MULTI-INSTANCE MANAGER IMPLEMENTATION - Historically, the postfix(1) command invokes the postfix- - script file (currently installed in the daemon directory). - This file contains the commands that start or stop Post- - fix, upgrade the configuration and so on. +ENABLING POSTFIX(1) MULTI-INSTANCE MODE + By default, the postfix(1) command operates in single- + instance mode. In this mode the command invokes the post- + fix-script file directly (currently installed in the dae- + mon directory). This file contains the commands that + start or stop one Postfix instance, that upgrade the con- + figuration of one Postfix instance, and so on. - When multi-instance support is turned on, the postfix(1) - command needs to execute these commands for each applica- - ble Postfix instance. This multiplication of commands is - handled by a multi-instance manager program. + When the postfix(1) command operates in multi-instance + mode as discussed below, the command needs to execute + start, stop, etc. commands for each Postfix instance. + This multiplication of commands is handled by a multi- + instance manager program. - Turning on multi-instance support goes as follows: update - the default Postfix instance's main.cf file, and populate - the multi_instance_directories parameter with the configu- - ration directory pathnames of additional Postfix - instances. + Turning on postfix(1) multi-instance mode goes as follows: + in the default Postfix instance's main.cf file, 1) specify + the pathname of a multi-instance manager program with the + multi_instance_wrapper parameter; 2) populate the + multi_instance_directories parameter with the configura- + tion directory pathnames of additional Postfix instances. + For example: - With multi-instance support turned on, the postfix(1) com- - mand invokes a multi-instance manager command instead of - the postfix-script file. The multi-instance manager exe- - cutes the postfix(1) command for each applicable Postfix - instance. The pathname of the multi-instance manager is - specified in the default main.cf file with the - multi_instance_wrapper parameter. + /etc/postfix/main.cf: + multi_instance_wrapper = $daemon_directory/postfix-wrapper + multi_instance_directories = /etc/postfix-test + + The $daemon_directory/postfix-wrapper file implements a + simple manager and contains instructions for creating + Postfix instances by hand. The postmulti(1) command pro- + vides a more extensive implementation including support + for life-cycle management. The multi_instance_directories and other main.cf parame- ters are listed below in the CONFIGURATION PARAMETERS sec- tion. - A useful multi-instance manager implementation can be as - simple as: + In multi-instance mode, the postfix(1) command invokes the + $multi_instance_wrapper command instead of the postfix- + script file. This multi-instance manager in turn executes + the postfix(1) command in single-instance mode for each + Postfix instance. + + To illustrate the main ideas behind multi-instance opera- + tion, below is an example of a simple but useful multi- + instance manager implementation: #!/bin/sh @@ -129,47 +137,43 @@ POSTFIX-WRAPPER(5) POSTFIX-WRAPPER(5) exit $err - A sample implementation, with instructions, can be found - in $daemon_directory/postfix-wrapper. - - The postmulti(1) command implements a more sophisticated - approach, based on a combination of C code and scripting. - -ENABLING A SPECIFIC INSTANCE FOR MULTI-INSTANCE OPERATION +PER-INSTANCE MULTI-INSTANCE MANAGER CONTROLS Each Postfix instance has its own main.cf file with param- - eters that control multi-instance operation. The most - important settings are discussed here. + eters that control how the multi-instance manager operates + on that instance. This section discusses the most impor- + tant settings. The setting "multi_instance_enable = yes" allows the - multi-instance manager to start (and stop) the correspond- - ing Postfix instance. For safety reasons, this setting is - not the default. + multi-instance manager to start (stop, etc.) the corre- + sponding Postfix instance. For safety reasons, this set- + ting is not the default. - The setting "multi_instance_enable = no" is useful for - manual testing. With this, the multi-instance manager - will not start the Postfix instance, and it will skip com- - mands such as "stop" or "flush" that require a running - Postfix instance. The multi-instance manager will execute - commands such as "check", "set-permissions" or "upgrade- - configuration", and it will replace "start" by "check" so - that problems will be reported even when the instance is - disabled. + The default setting "multi_instance_enable = no" is useful + for manual testing with "postfix -c /path/name start" etc. + The multi-instance manager will not start such an + instance, and it will skip commands such as "stop" or + "flush" that require a running Postfix instance. The + multi-instance manager will execute commands such as + "check", "set-permissions" or "upgrade-configuration", and + it will replace "start" by "check" so that problems will + be reported even when the instance is disabled. -SHARED VERSUS NON-SHARED FILES - Some files are shared between Postfix instances, such as +MAINTAINING SHARED AND NON-SHARED FILES + Some files are shared between Postfix instances, such as executables and manpages, and some files are per-instance, - such as the queue directory. See the NON-SHARED FILES - section below for a list of per-instance files. + such as configuration files, mail queue files, and data + files. See the NON-SHARED FILES section below for a list + of per-instance files. Before Postfix multi-instance support was implemented, the - executables, manpages, etc., have always been checked or - updated as part of the default Postfix instance. With - multi-instance support, we simply continue to do this. + executables, manpages, etc., have always been maintained + as part of the default Postfix instance. - Specifically, Postfix instances will not check or update - shared files when their config_directory value is listed - with the default main.cf's multi_instance_directories - parameter. + With multi-instance support, we simply continue to do + this. Specifically, a Postfix instance will not check or + update shared files when that instance's config_directory + value is listed with the default main.cf file's + multi_instance_directories parameter. The consequence of this approach is that the default Post- fix instance should be checked and updated before any @@ -275,7 +279,7 @@ POSTFIX-WRAPPER(5) POSTFIX-WRAPPER(5) SEE ALSO postfix(1) Postfix control program - postmulti(1) full-blown multi-instance manager + postmulti(1) full-blown multi-instance manager $daemon_directory/postfix-wrapper simple multi-instance manager LICENSE diff --git a/postfix/html/postfix.1.html b/postfix/html/postfix.1.html index ccef49f0c..2339142f0 100644 --- a/postfix/html/postfix.1.html +++ b/postfix/html/postfix.1.html @@ -270,7 +270,7 @@ POSTFIX(1) POSTFIX(1) postlock(1), Postfix-compatible locking postlog(1), Postfix-compatible logging postmap(1), Postfix lookup table manager - postmulti(1), Postfix multi-instance manager + postmulti(1), Postfix multi-instance manager postqueue(1), Postfix mail queue control postsuper(1), Postfix housekeeping mailq(1), Sendmail compatibility interface diff --git a/postfix/html/postmulti.1.html b/postfix/html/postmulti.1.html new file mode 100644 index 000000000..896106ea8 --- /dev/null +++ b/postfix/html/postmulti.1.html @@ -0,0 +1,437 @@ + + + + Postfix manual - postmulti(1) +
      +POSTMULTI(1)                                                      POSTMULTI(1)
      +
      +NAME
      +       postmulti - Postfix multi-instance manager
      +
      +SYNOPSIS
      +       postmulti -l [-aRv] [-g group] [-i name]
      +
      +       postmulti -p [-av] [-g group] [-i name] command...
      +
      +       postmulti -x [-aRv] [-g group] [-i name] command...
      +
      +       postmulti -e init [-v]
      +
      +       postmulti -e create [-av] [-g group] [-i name] [-G group]
      +       [-I name] [param=value ...]
      +
      +       postmulti -e import [-av] [-g group] [-i name] [-G group]
      +       [-I name] [config_directory=/path]
      +
      +       postmulti -e destroy [-v] -i name
      +
      +       postmulti -e deport [-v] -i name
      +
      +       postmulti -e enable [-v] -i name
      +
      +       postmulti -e disable [-v] -i name
      +
      +       postmulti -e assign [-v] -i name [-I name] [-G group]
      +
      +DESCRIPTION
      +       The postmulti(1) command allows a Postfix administrator to
      +       manage multiple Postfix instances on a single host.
      +
      +       postmulti(1) implements two fundamental  modes  of  opera-
      +       tion.   In iterator mode, it executes the same command for
      +       multiple  Postfix  instances.   In  life-cycle  management
      +       mode,  it  adds  or  deletes  one instance, or changes the
      +       multi-instance status of one instance.
      +
      +       Each mode of operation has its  own  command  syntax.  For
      +       this  reason, each mode is documented in separate sections
      +       below.
      +
      +BACKGROUND
      +       A multi-instance configuration  consists  of  one  primary
      +       Postfix  instance,  and  one  or  more secondary instances
      +       whose configuration directory pathnames  are  recorded  in
      +       the  primary  instance's  main.cf  file. Postfix instances
      +       share program files and documentation, but have their  own
      +       configuration, queue and data directories.
      +
      +       Currently,  only  the default Postfix instance can be used
      +       as primary instance in a multi-instance configuration. The
      +       postmulti(1)  command  does  not  currently  support  a -c
      +       option to select  an  alternative  primary  instance,  and
      +       exits  with  a  fatal error if the MAIL_CONFIG environment
      +       variable is set to a non-default configuration  directory.
      +
      +       See the MULTI_INSTANCE_README tutorial for a more detailed
      +       discussion of multi-instance management with postmulti(1).
      +
      +ITERATOR MODE
      +       In iterator mode, postmulti performs the same operation on
      +       all Postfix instances in turn.
      +
      +       If multi-instance support is not  enabled,  the  requested
      +       command is performed just for the primary instance.
      +
      +       Iterator mode implements the following command options:
      +
      +Instance selection
      +       -a     Perform the operation on all instances. This is the
      +              default.
      +
      +       -g group
      +              Perform the operation only for members of the named
      +              group.
      +
      +       -i name
      +              Perform  the  operation  only for the instance with
      +              the specified name.  You  can  specify  either  the
      +              instance  name  or  the  absolute  pathname  of the
      +              instance's configuration directory.  Specify "-" to
      +              select the primary Postfix instance.
      +
      +       -R     Reverse  the iteration order. This may be appropri-
      +              ate when updating a  multi-instance  system,  where
      +              "sink"   instances   are  started  before  "source"
      +              instances.
      +
      +              This option cannot be used with -p.
      +
      +List mode
      +       -l     List Postfix instances with  their  instance  name,
      +              instance group name, enable/disable status and con-
      +              figuration directory.
      +
      +Postfix-wrapper mode
      +       -p     Invoke postfix(1) to execute the specified command.
      +              This   option   implements  the  postfix-wrapper(5)
      +              interface.
      +
      +              o      With "start"-like commands, "postfix  check"
      +                     is  executed  for  instances  that  are  not
      +                     enabled. The full list of commands is speci-
      +                     fied   with   the   postmulti_start_commands
      +                     parameter.
      +
      +              o      With  "stop"-like  commands,  the  iteration
      +                     order  is  reversed,  and disabled instances
      +                     are skipped. The full list  of  commands  is
      +                     specified  with  the postmulti_stop_commands
      +                     parameter.
      +
      +              o      With  "reload"  and  other   commands   that
      +                     require   a   started   instance,   disabled
      +                     instances are skipped. The full list of com-
      +                     mands  is  specified with the postmulti_con-
      +                     trol_commands parameter.
      +
      +              o      With "status" and other commands that  don't
      +                     require  a  started instance, the command is
      +                     executed for all instances.
      +
      +              The -p option can also  be  used  interactively  to
      +              start/stop/etc.    a  named  instance  or  instance
      +              group. For example, to start just the instances  in
      +              the group "msa", invoke postmulti(1) as follows:
      +
      +                     # postmulti -g msa -p start
      +
      +Command mode
      +       -x     Execute  the  specified  command  for  all  Postfix
      +              instances.  The command runs with appropriate envi-
      +              ronment  settings  for  MAIL_CONFIG, command_direc-
      +              tory,      daemon_directory,      config_directory,
      +              queue_directory,                    data_directory,
      +              multi_instance_name,    multi_instance_group    and
      +              multi_instance_enable.
      +
      +Other options
      +       -v     Enable verbose logging for debugging purposes. Mul-
      +              tiple -v options  make  the  software  increasingly
      +              verbose.
      +
      +LIFE-CYCLE MANAGEMENT MODE
      +       With  the  -e  option  postmulti(1)  can be used to add or
      +       delete a  Postfix  instance,  and  to  manage  the  multi-
      +       instance status of an existing instance.
      +
      +       The following options are implemented:
      +
      +Existing instance selection
      +       -a     When  creating  or importing an instance, place the
      +              new instance at the front of the secondary instance
      +              list.
      +
      +       -g group
      +              When  creating  or importing an instance, place the
      +              new instance before the  first  secondary  instance
      +              that is a member of the specified group.
      +
      +       -i name
      +              When  creating  or importing an instance, place the
      +              new  instance   before   the   matching   secondary
      +              instance.
      +
      +              With  other life-cycle operations, apply the opera-
      +              tion to the named existing instance.   Specify  "-"
      +              to select the primary Postfix instance.
      +
      +New or existing instance name assignment
      +       -I name
      +              Assign  the  specified instance name to an existing
      +              instance  or  to  a  newly  created   or   imported
      +              instance.   Instance names other than "-"    (which
      +              makes the  instance  "nameless")  must  start  with
      +              "postfix-".   This  restriction reduces the likeli-
      +              hood of name collisions with system files.
      +
      +       -G group
      +              Assign the specified  group  name  to  an  existing
      +              instance   or   to  a  newly  created  or  imported
      +              instance.
      +
      +Instance creation/deletion/status change
      +       -e action
      +              "Edit" managed instances. The following actions are
      +              supported:
      +
      +              init   This command is required before postmulti(1)
      +                     can be used  to  manage  Postfix  instances.
      +                     The  "postmulti -e init" command updates the
      +                     primary instance's main.cf file by setting:
      +
      +                            multi_instance_wrapper =
      +                                    ${command_directory}/postmulti -p --
      +                            multi_instance_enable = yes
      +
      +                     You can set these by other means if you pre-
      +                     fer.
      +
      +              create Create  a new Postfix instance and add it to
      +                     the multi_instance_directories parameter  of
      +                     the  primary instance.  The "-I name" option
      +                     is recommended to give the instance a  short
      +                     name  that is used to construct default val-
      +                     ues for the private directories of  the  new
      +                     instance. The "-G group" option may be spec-
      +                     ified to assign the  instance  to  a  group,
      +                     otherwise,  the new instance is not a member
      +                     of any groups.
      +
      +                     The  new  instance  main.cf  is  the   stock
      +                     main.cf with the parameters that specify the
      +                     locations of shared files  cloned  from  the
      +                     primary instance.  For "nameless" instances,
      +                     you should manually adjust "syslog_name"  to
      +                     yield a unique "logtag" starting with "post-
      +                     fix-"  that  will  uniquely   identify   the
      +                     instance  in the mail logs. It is simpler to
      +                     assign the instance a short  name  with  the
      +                     "-I name" option.
      +
      +                     Optional  "name=value" arguments specify the
      +                     instance  config_directory,  queue_directory
      +                     and data_directory.  For example:
      +
      +                            # postmulti -I postfix-mumble \
      +                                    -G mygroup -e create \
      +                                    config_directory=/my/config/dir \
      +                                    queue_directory=/my/queue/dir \
      +                                    data_directory=/my/data/dir
      +
      +                     If  any  of these pathnames is not supplied,
      +                     the program attempts to generate  the  path-
      +                     name  by  taking  the  corresponding primary
      +                     instance pathname, and by replacing the last
      +                     pathname  component  by  the value of the -I
      +                     option.
      +
      +                     If  the  instance  configuration   directory
      +                     already  exists, and contains both a main.cf
      +                     and master.cf file, create will "import" the
      +                     instance as-is. For existing instances, cre-
      +                     ate and import are identical.
      +
      +              import Import an existing instance into the list of
      +                     instances managed by the postmulti(1) multi-
      +                     instance manager.  This adds the instance to
      +                     the  multi_instance_directories  list of the
      +                     primary instance.  If the "-I  name"  option
      +                     is  provided  it  specifies the new name for
      +                     the instance and is used to define a default
      +                     location   for  the  instance  configuration
      +                     directory (as with create above).   The  "-G
      +                     group"  option  may  be  used  to assign the
      +                     instance to a group.  Add  a  "config_direc-
      +                     tory=/path"  argument  to override a default
      +                     pathname based on "-I name".
      +
      +              destroy
      +                     Destroy a secondary Postfix instance. To  be
      +                     a candidate for destruction an instance must
      +                     be disabled, stopped and its queue must  not
      +                     contain  any  messages.  Attempts to destroy
      +                     the primary Postfix instance trigger a fatal
      +                     error, without destroying the instance.
      +
      +                     The  instance  is  removed  from the primary
      +                     instance   main.cf   file's   alternate_con-
      +                     fig_directories   parameter  and  its  data,
      +                     queue  and  configuration  directories   are
      +                     cleaned  of files and directories created by
      +                     the Postfix system.  The  main.cf  and  mas-
      +                     ter.cf files are removed from the configura-
      +                     tion directory even if they have been  modi-
      +                     fied  since  initial  creation. Finally, the
      +                     instance is "deported" from the list of man-
      +                     aged instances.
      +
      +                     If  other files are present in instance pri-
      +                     vate directories, the directories may not be
      +                     fully  removed, a warning is logged to alert
      +                     the administrator. It is  expected  that  an
      +                     instance built using "fresh" directories via
      +                     the create action will be fully  removed  by
      +                     the  destroy  action (if first disabled). If
      +                     the instance configuration and queue  direc-
      +                     tories  are  populated with additional files
      +                     (access and rewriting  tables,  chroot  jail
      +                     content, etc.) the instance directories will
      +                     not be fully removed.
      +
      +                     The destroy action triggers potentially dan-
      +                     gerous  file  removal  operations. Make sure
      +                     the instance's data, queue and configuration
      +                     directories  are  set  correctly  and do not
      +                     contain any valuable files.
      +
      +              deport Deport a secondary instance from the list of
      +                     managed instances. This deletes the instance
      +                     configuration  directory  from  the  primary
      +                     instance's  multi_instance_directories list,
      +                     but does not remove any  files  or  directo-
      +                     ries.
      +
      +              assign Assign  a  new  instance name or a new group
      +                     name to the selected instance.  Use  "-G  -"
      +                     to  specify "no group" and "-I -" to specify
      +                     "no  name".   If  you  choose  to  make   an
      +                     instance  "nameless",  set  a  suitable sys-
      +                     log_name in the corresponding main.cf  file.
      +
      +              enable Mark  the selected instance as enabled. This
      +                     just sets the multi_instance_enable  parame-
      +                     ter to "yes" in the instance's main.cf file.
      +
      +              disable
      +                     Mark the selected instance as disabled. This
      +                     means  that the instance will not be started
      +                     etc. with  "postfix  start",  "postmulti  -p
      +                     start"  and so on. The instance can still be
      +                     started etc. with "postfix -c  config-direc-
      +                     tory start".
      +
      +Other options
      +       -v     Enable verbose logging for debugging purposes. Mul-
      +              tiple -v options  make  the  software  increasingly
      +              verbose.
      +
      +ENVIRONMENT
      +       The postmulti(1) command exports the following environment
      +       variables before executing the  requested  command  for  a
      +       given instance:
      +
      +       MAIL_VERBOSE
      +              This  is  set  when  the  -v command-line option is
      +              present.
      +
      +       MAIL_CONFIG
      +              The location of the configuration directory of  the
      +              instance.
      +
      +CONFIGURATION PARAMETERS
      +       config_directory (see 'postconf -d' output)
      +              The  default  location  of  the Postfix main.cf and
      +              master.cf configuration files.
      +
      +       daemon_directory (see 'postconf -d' output)
      +              The directory with  Postfix  support  programs  and
      +              daemon programs.
      +
      +       import_environment (see 'postconf -d' output)
      +              The  list  of environment parameters that a Postfix
      +              process  will  import  from  a  non-Postfix  parent
      +              process.
      +
      +       multi_instance_directories (empty)
      +              An  optional list of non-default Postfix configura-
      +              tion directories; these directories belong to addi-
      +              tional  Postfix  instances  that  share the Postfix
      +              executable files and documentation with the default
      +              Postfix  instance,  and  that are started, stopped,
      +              etc., together with the default Postfix instance.
      +
      +       multi_instance_group (empty)
      +              The optional instance group name  of  this  Postfix
      +              instance.
      +
      +       multi_instance_name (empty)
      +              The   optional   instance   name  of  this  Postfix
      +              instance.
      +
      +       multi_instance_enable (no)
      +              Allow this Postfix instance to be started, stopped,
      +              etc., by a multi-instance manager.
      +
      +       postmulti_start_commands (start)
      +              The   postfix(1)  commands  that  the  postmulti(1)
      +              instance manager treats as "start" commands.
      +
      +       postmulti_stop_commands (see 'postconf -d' output)
      +              The  postfix(1)  commands  that  the   postmulti(1)
      +              instance manager treats as "stop" commands.
      +
      +       postmulti_control_commands (reload flush)
      +              The   postfix(1)  commands  that  the  postmulti(1)
      +              instance manager treats as "control" commands, that
      +              operate on running instances.
      +
      +       syslog_facility (mail)
      +              The syslog facility of Postfix logging.
      +
      +       syslog_name (see 'postconf -d' output)
      +              The  mail  system  name  that  is  prepended to the
      +              process name in syslog  records,  so  that  "smtpd"
      +              becomes, for example, "postfix/smtpd".
      +
      +FILES
      +       $daemon_directory/main.cf, stock configuration file
      +       $daemon_directory/master.cf, stock configuration file
      +       $daemon_directory/postmulti-script, life-cycle helper program
      +
      +SEE ALSO
      +       postfix(1), Postfix control program
      +       postfix-wrapper(5), Postfix multi-instance API
      +
      +README FILES
      +       MULTI_INSTANCE_README, Postfix multi-instance management
      +
      +HISTORY
      +       The  postmulti(1) command was introduced with Postfix ver-
      +       sion 2.6.
      +
      +LICENSE
      +       The Secure Mailer license must be  distributed  with  this
      +       software.
      +
      +AUTHOR(S)
      +       Victor Duchovni
      +       Morgan Stanley
      +
      +       Wietse Venema
      +       IBM T.J. Watson Research
      +       P.O. Box 704
      +       Yorktown Heights, NY 10598, USA
      +
      +                                                                  POSTMULTI(1)
      +
      diff --git a/postfix/html/sendmail.1.html b/postfix/html/sendmail.1.html index ce6de4e40..c4efe7302 100644 --- a/postfix/html/sendmail.1.html +++ b/postfix/html/sendmail.1.html @@ -450,7 +450,7 @@ SENDMAIL(1) SENDMAIL(1) syslog_facility (mail) The syslog facility of Postfix logging. - syslog_name (postfix) + syslog_name (see 'postconf -d' output) The mail system name that is prepended to the process name in syslog records, so that "smtpd" becomes, for example, "postfix/smtpd". @@ -477,7 +477,7 @@ SENDMAIL(1) SENDMAIL(1) VERP_README, Postfix VERP howto LICENSE - The Secure Mailer license must be distributed with this + The Secure Mailer license must be distributed with this software. AUTHOR(S) diff --git a/postfix/html/smtp.8.html b/postfix/html/smtp.8.html index 77d062520..1588febfa 100644 --- a/postfix/html/smtp.8.html +++ b/postfix/html/smtp.8.html @@ -730,76 +730,82 @@ SMTP(8) SMTP(8) The time limit for sending or receiving information over an internal communication channel. + lmtp_assume_final (no) + When an LMTP server announces no DSN support, + assume that the server performs final delivery, and + send "delivered" delivery status notifications + instead of "relayed". + lmtp_tcp_port (24) - The default TCP port that the Postfix LMTP client + The default TCP port that the Postfix LMTP client connects to. max_idle (100s) - The maximum amount of time that an idle Postfix - daemon process waits for an incoming connection + The maximum amount of time that an idle Postfix + daemon process waits for an incoming connection before terminating voluntarily. max_use (100) - The maximal number of incoming connections that a - Postfix daemon process will service before termi- + The maximal number of incoming connections that a + Postfix daemon process will service before termi- nating voluntarily. process_id (read-only) - The process ID of a Postfix command or daemon + The process ID of a Postfix command or daemon process. process_name (read-only) - The process name of a Postfix command or daemon + The process name of a Postfix command or daemon process. proxy_interfaces (empty) The network interface addresses that this mail sys- - tem receives mail on by way of a proxy or network + tem receives mail on by way of a proxy or network address translation unit. smtp_bind_address (empty) - An optional numerical network address that the - Postfix SMTP client should bind to when making an + An optional numerical network address that the + Postfix SMTP client should bind to when making an IPv4 connection. smtp_bind_address6 (empty) - An optional numerical network address that the - Postfix SMTP client should bind to when making an + An optional numerical network address that the + Postfix SMTP client should bind to when making an IPv6 connection. smtp_helo_name ($myhostname) - The hostname to send in the SMTP EHLO or HELO com- + The hostname to send in the SMTP EHLO or HELO com- mand. lmtp_lhlo_name ($myhostname) The hostname to send in the LMTP LHLO command. smtp_host_lookup (dns) - What mechanisms when the Postfix SMTP client uses + What mechanisms when the Postfix SMTP client uses to look up a host's IP address. smtp_randomize_addresses (yes) - Randomize the order of equal-preference MX host + Randomize the order of equal-preference MX host addresses. syslog_facility (mail) The syslog facility of Postfix logging. - syslog_name (postfix) - The mail system name that is prepended to the - process name in syslog records, so that "smtpd" + syslog_name (see 'postconf -d' output) + The mail system name that is prepended to the + process name in syslog records, so that "smtpd" becomes, for example, "postfix/smtpd". Available with Postfix 2.2 and earlier: fallback_relay (empty) - Optional list of relay hosts for SMTP destinations + Optional list of relay hosts for SMTP destinations that can't be found or that are unreachable. Available with Postfix 2.3 and later: smtp_fallback_relay ($fallback_relay) - Optional list of relay hosts for SMTP destinations + Optional list of relay hosts for SMTP destinations that can't be found or that are unreachable. SEE ALSO @@ -820,7 +826,7 @@ SMTP(8) SMTP(8) TLS_README, Postfix STARTTLS howto LICENSE - The Secure Mailer license must be distributed with this + The Secure Mailer license must be distributed with this software. AUTHOR(S) diff --git a/postfix/html/verify.8.html b/postfix/html/verify.8.html index 744a5bb71..38db746f5 100644 --- a/postfix/html/verify.8.html +++ b/postfix/html/verify.8.html @@ -173,7 +173,7 @@ VERIFY(8) VERIFY(8) syslog_facility (mail) The syslog facility of Postfix logging. - syslog_name (postfix) + syslog_name (see 'postconf -d' output) The mail system name that is prepended to the process name in syslog records, so that "smtpd" becomes, for example, "postfix/smtpd". diff --git a/postfix/makedefs b/postfix/makedefs index 9ec17877c..ee4643610 100644 --- a/postfix/makedefs +++ b/postfix/makedefs @@ -180,6 +180,8 @@ case "$SYSTEM.$RELEASE" in SunOS.5*) SYSTYPE=SUNOS5 RANLIB=echo SYSLIBS="-lresolv -lsocket -lnsl" + # Stock awk breaks with >10 files. + test -x /usr/xpg4/bin/awk && AWK=/usr/xpg4/bin/awk # Solaris 2.5 added usleep() and POSIX regular expressions case $RELEASE in 5.[0-4]) CCARGS="$CCARGS -DMISSING_USLEEP -DNO_POSIX_REGEXP";; diff --git a/postfix/man/Makefile.in b/postfix/man/Makefile.in index 094ed3e2a..c9753a286 100644 --- a/postfix/man/Makefile.in +++ b/postfix/man/Makefile.in @@ -10,8 +10,8 @@ DAEMONS = man8/bounce.8 man8/defer.8 man8/cleanup.8 man8/error.8 man8/local.8 \ man8/scache.8 man8/discard.8 man8/tlsmgr.8 COMMANDS= man1/postalias.1 man1/postcat.1 man1/postconf.1 man1/postfix.1 \ man1/postkick.1 man1/postlock.1 man1/postlog.1 man1/postdrop.1 \ - man1/postmap.1 man1/sendmail.1 man1/mailq.1 man1/newaliases.1 \ - man1/postqueue.1 man1/postsuper.1 + man1/postmap.1 man1/postmulti.1 man1/postqueue.1 man1/postsuper.1 \ + man1/sendmail.1 man1/mailq.1 man1/newaliases.1 CONFIG = man5/access.5 man5/aliases.5 man5/canonical.5 man5/relocated.5 \ man5/transport.5 man5/virtual.5 man5/pcre_table.5 man5/regexp_table.5 \ man5/cidr_table.5 man5/tcp_table.5 man5/header_checks.5 \ @@ -203,6 +203,11 @@ man1/postmap.1: ../src/postmap/postmap.c (cmp -s junk $? || mv junk $?) && rm -f junk ../mantools/srctoman $? >$@ +man1/postmulti.1: ../src/postmulti/postmulti.c + ../mantools/fixman ../proto/postconf.proto $? >junk && \ + (cmp -s junk $? || mv junk $?) && rm -f junk + ../mantools/srctoman $? >$@ + man1/postqueue.1: ../src/postqueue/postqueue.c ../mantools/fixman ../proto/postconf.proto $? >junk && \ (cmp -s junk $? || mv junk $?) && rm -f junk diff --git a/postfix/man/man1/postmulti.1 b/postfix/man/man1/postmulti.1 new file mode 100644 index 000000000..36439722f --- /dev/null +++ b/postfix/man/man1/postmulti.1 @@ -0,0 +1,403 @@ +.TH POSTMULTI 1 +.ad +.fi +.SH NAME +postmulti +\- +Postfix multi-instance manager +.SH "SYNOPSIS" +.na +.nf +.fi +\fBpostmulti\fR \fB-l\fR [\fB-aRv\fR] [\fB-g \fIgroup\fR] +[\fB-i \fIname\fR] + +\fBpostmulti\fR \fB-p\fR [\fB-av\fR] [\fB-g \fIgroup\fR] +[\fB-i \fIname\fR] \fIcommand...\fR + +\fBpostmulti\fR \fB-x\fR [\fB-aRv\fR] [\fB-g \fIgroup\fR] +[\fB-i \fIname\fR] \fIcommand...\fR + +\fBpostmulti\fR \fB-e init\fR [\fB-v\fR] + +\fBpostmulti\fR \fB-e create\fR [\fB-av\fR] +[\fB-g \fIgroup\fR] [\fB-i \fIname\fR] [\fB-G \fIgroup\fR] +[\fB-I \fIname\fR] [\fIparam=value\fR ...] + +\fBpostmulti\fR \fB-e import\fR [\fB-av\fR] +[\fB-g \fIgroup\fR] [\fB-i \fIname\fR] [\fB-G \fIgroup\fR] +[\fB-I \fIname\fR] [\fBconfig_directory=\fI/path\fR] + +\fBpostmulti\fR \fB-e destroy\fR [\fB-v\fR] \fB-i \fIname\fR + +\fBpostmulti\fR \fB-e deport\fR [\fB-v\fR] \fB-i \fIname\fR + +\fBpostmulti\fR \fB-e enable\fR [\fB-v\fR] \fB-i \fIname\fR + +\fBpostmulti\fR \fB-e disable\fR [\fB-v\fR] \fB-i \fIname\fR + +\fBpostmulti\fR \fB-e assign\fR [\fB-v\fR] \fB-i \fIname\fR +[\fB-I \fIname\fR] [-G \fIgroup\fR] +.SH DESCRIPTION +.ad +.fi +The \fBpostmulti\fR(1) command allows a Postfix administrator +to manage multiple Postfix instances on a single host. + +\fBpostmulti\fR(1) implements two fundamental modes of +operation. In \fBiterator\fR mode, it executes the same +command for multiple Postfix instances. In \fBlife-cycle +management\fR mode, it adds or deletes one instance, or +changes the multi-instance status of one instance. + +Each mode of operation has its own command syntax. For this +reason, each mode is documented in separate sections below. +.SH "BACKGROUND" +.na +.nf +.ad +.fi +A multi-instance configuration consists of one primary +Postfix instance, and one or more secondary instances whose +configuration directory pathnames are recorded in the primary +instance's main.cf file. Postfix instances share program +files and documentation, but have their own configuration, +queue and data directories. + +Currently, only the default Postfix instance can be used +as primary instance in a multi-instance configuration. The +\fBpostmulti\fR(1) command does not currently support a \fB-c\fR +option to select an alternative primary instance, and exits +with a fatal error if the \fBMAIL_CONFIG\fR environment +variable is set to a non-default configuration directory. + +See the MULTI_INSTANCE_README tutorial for a more detailed +discussion of multi-instance management with \fBpostmulti\fR(1). +.SH "ITERATOR MODE" +.na +.nf +.ad +.fi +In iterator mode, \fBpostmulti\fR performs the same operation +on all Postfix instances in turn. + +If multi-instance support is not enabled, the requested +command is performed just for the primary instance. +.PP +Iterator mode implements the following command options: +.SH "Instance selection" +.IP \fB-a\fR +Perform the operation on all instances. This is the default. +.IP "\fB-g \fIgroup\fR" +Perform the operation only for members of the named \fIgroup\fR. +.IP "\fB-i \fIname\fR" +Perform the operation only for the instance with the specified +\fIname\fR. You can specify either the instance name +or the absolute pathname of the instance's configuration +directory. Specify "-" to select the primary Postfix instance. +.IP \fB-R\fR +Reverse the iteration order. This may be appropriate when +updating a multi-instance system, where "sink" instances +are started before "source" instances. +.sp +This option cannot be used with \fB-p\fR. +.SH "List mode" +.IP \fB-l\fR +List Postfix instances with their instance name, instance +group name, enable/disable status and configuration directory. +.SH "Postfix-wrapper mode" +.IP \fB-p\fR +Invoke \fBpostfix(1)\fR to execute the specified \fIcommand\fR. +This option implements the \fBpostfix-wrapper\fR(5) interface. +.RS +.IP \(bu +With "start"-like commands, "postfix check" is executed for +instances that are not enabled. The full list of commands +is specified with the postmulti_start_commands parameter. +.IP \(bu +With "stop"-like commands, the iteration order is reversed, +and disabled instances are skipped. The full list of commands +is specified with the postmulti_stop_commands parameter. +.IP \(bu +With "reload" and other commands that require a started +instance, disabled instances are skipped. The full list of +commands is specified with the postmulti_control_commands +parameter. +.IP \(bu +With "status" and other commands that don't require a started +instance, the command is executed for all instances. +.RE +.IP +The \fB-p\fR option can also be used interactively to +start/stop/etc. a named instance or instance group. For +example, to start just the instances in the group "msa", +invoke \fBpostmulti\fR(1) as follows: +.RS +.IP +# postmulti -g msa -p start +.RE +.SH "Command mode" +.IP \fB-x\fR +Execute the specified \fIcommand\fR for all Postfix instances. +The command runs with appropriate environment settings for +MAIL_CONFIG, command_directory, daemon_directory, +config_directory, queue_directory, data_directory, +multi_instance_name, multi_instance_group and +multi_instance_enable. +.SH "Other options" +.IP \fB-v\fR +Enable verbose logging for debugging purposes. Multiple +\fB-v\fR options make the software increasingly verbose. +.SH "LIFE-CYCLE MANAGEMENT MODE" +.na +.nf +.ad +.fi +With the \fB-e\fR option \fBpostmulti\fR(1) can be used to +add or delete a Postfix instance, and to manage the +multi-instance status of an existing instance. +.PP +The following options are implemented: +.SH "Existing instance selection" +.IP \fB-a\fR +When creating or importing an instance, place the new +instance at the front of the secondary instance list. +.IP "\fB-g \fIgroup\fR" +When creating or importing an instance, place the new +instance before the first secondary instance that is a +member of the specified group. +.IP "\fB-i \fIname\fR" +When creating or importing an instance, place the new +instance before the matching secondary instance. +.sp +With other life-cycle operations, apply the operation to +the named existing instance. Specify "-" to select the +primary Postfix instance. +.SH "New or existing instance name assignment" +.IP "\fB-I \fIname\fR" +Assign the specified instance \fIname\fR to an existing +instance or to a newly created or imported instance. Instance +names other than "-" (which makes the instance "nameless") +must start with "postfix-". This restriction reduces the +likelihood of name collisions with system files. +.IP "\fB-G \fIgroup\fR" +Assign the specified \fIgroup\fR name to an existing instance +or to a newly created or imported instance. +.SH "Instance creation/deletion/status change" +.IP "\fB-e \fIaction\fR" +"Edit" managed instances. The following actions are supported: +.RS +.IP \fBinit\fR +This command is required before \fBpostmulti\fR(1) can be +used to manage Postfix instances. The "postmulti -e init" +command updates the primary instance's main.cf file by +setting: +.RS +.IP +.nf +multi_instance_wrapper = + ${command_directory}/postmulti -p -- +multi_instance_enable = yes +.fi +.RE +.IP +You can set these by other means if you prefer. +.IP \fBcreate\fR +Create a new Postfix instance and add it to the +multi_instance_directories parameter of the primary instance. +The "\fB-I \fIname\fR" option is recommended to give the +instance a short name that is used to construct default +values for the private directories of the new instance. The +"\fB-G \fIgroup\fR" option may be specified to assign the +instance to a group, otherwise, the new instance is not a +member of any groups. +.sp +The new instance main.cf is the stock main.cf with the +parameters that specify the locations of shared files cloned +from the primary instance. For "nameless" instances, you +should manually adjust "syslog_name" to yield a unique +"logtag" starting with "postfix-" that will uniquely identify +the instance in the mail logs. It is simpler to assign the +instance a short name with the "\fB-I \fIname\fR" option. +.sp +Optional "name=value" arguments specify the instance +config_directory, queue_directory and data_directory. +For example: +.RS +.IP +.nf +# postmulti -I postfix-mumble \e + -G mygroup -e create \e + config_directory=/my/config/dir \e + queue_directory=/my/queue/dir \e + data_directory=/my/data/dir +.fi +.RE +.IP +If any of these pathnames is not supplied, the program +attempts to generate the pathname by taking the corresponding +primary instance pathname, and by replacing the last pathname +component by the value of the \fB-I\fR option. +.sp +If the instance configuration directory already exists, and +contains both a main.cf and master.cf file, \fBcreate\fR +will "import" the instance as-is. For existing instances, +\fBcreate\fR and \fBimport\fR are identical. +.IP \fBimport\fR +Import an existing instance into the list of instances +managed by the \fBpostmulti\fR(1) multi-instance manager. +This adds the instance to the multi_instance_directories +list of the primary instance. If the "\fB-I \fIname\fR" +option is provided it specifies the new name for the instance +and is used to define a default location for the instance +configuration directory (as with \fBcreate\fR above). The +"\fB-G \fIgroup\fR" option may be used to assign the instance +to a group. Add a "\fBconfig_directory=\fI/path\fR" argument +to override a default pathname based on "\fB-I \fIname\fR". +.IP \fBdestroy\fR +Destroy a secondary Postfix instance. To be a candidate for +destruction an instance must be disabled, stopped and its +queue must not contain any messages. Attempts to destroy +the primary Postfix instance trigger a fatal error, without +destroying the instance. +.sp +The instance is removed from the primary instance main.cf +file's alternate_config_directories parameter and its data, +queue and configuration directories are cleaned of files +and directories created by the Postfix system. The main.cf +and master.cf files are removed from the configuration +directory even if they have been modified since initial +creation. Finally, the instance is "deported" from the list +of managed instances. +.sp +If other files are present in instance private directories, +the directories may not be fully removed, a warning is +logged to alert the administrator. It is expected that an +instance built using "fresh" directories via the \fBcreate\fR +action will be fully removed by the \fBdestroy\fR action +(if first disabled). If the instance configuration and queue +directories are populated with additional files (access and +rewriting tables, chroot jail content, etc.) the instance +directories will not be fully removed. +.sp +The \fBdestroy\fR action triggers potentially dangerous +file removal operations. Make sure the instance's data, +queue and configuration directories are set correctly and +do not contain any valuable files. +.IP \fBdeport\fR +Deport a secondary instance from the list of managed +instances. This deletes the instance configuration directory +from the primary instance's multi_instance_directories list, +but does not remove any files or directories. +.IP \fBassign\fR +Assign a new instance name or a new group name to the +selected instance. Use "\fB-G -\fR" to specify "no group" +and "\fB-I -\fR" to specify "no name". If you choose to +make an instance "nameless", set a suitable syslog_name in +the corresponding main.cf file. +.IP \fBenable\fR +Mark the selected instance as enabled. This just sets the +multi_instance_enable parameter to "yes" in the instance's +main.cf file. +.IP \fBdisable\fR +Mark the selected instance as disabled. This means that +the instance will not be started etc. with "postfix start", +"postmulti -p start" and so on. The instance can still be +started etc. with "postfix -c config-directory start". +.SH "Other options" +.IP \fB-v\fR +Enable verbose logging for debugging purposes. Multiple +\fB-v\fR options make the software increasingly verbose. +.RE +.SH "ENVIRONMENT" +.na +.nf +.ad +.fi +The \fBpostmulti\fR(1) command exports the following environment +variables before executing the requested \fIcommand\fR for a given +instance: +.IP \fBMAIL_VERBOSE\fR +This is set when the -v command-line option is present. +.IP \fBMAIL_CONFIG\fR +The location of the configuration directory of the instance. +.SH "CONFIGURATION PARAMETERS" +.na +.nf +.ad +.fi +.IP "\fBconfig_directory (see 'postconf -d' output)\fR" +The default location of the Postfix main.cf and master.cf +configuration files. +.IP "\fBdaemon_directory (see 'postconf -d' output)\fR" +The directory with Postfix support programs and daemon programs. +.IP "\fBimport_environment (see 'postconf -d' output)\fR" +The list of environment parameters that a Postfix process will +import from a non-Postfix parent process. +.IP "\fBmulti_instance_directories (empty)\fR" +An optional list of non-default Postfix configuration directories; +these directories belong to additional Postfix instances that share +the Postfix executable files and documentation with the default +Postfix instance, and that are started, stopped, etc., together +with the default Postfix instance. +.IP "\fBmulti_instance_group (empty)\fR" +The optional instance group name of this Postfix instance. +.IP "\fBmulti_instance_name (empty)\fR" +The optional instance name of this Postfix instance. +.IP "\fBmulti_instance_enable (no)\fR" +Allow this Postfix instance to be started, stopped, etc., by a +multi-instance manager. +.IP "\fBpostmulti_start_commands (start)\fR" +The \fBpostfix\fR(1) commands that the \fBpostmulti\fR(1) instance manager treats +as "start" commands. +.IP "\fBpostmulti_stop_commands (see 'postconf -d' output)\fR" +The \fBpostfix\fR(1) commands that the \fBpostmulti\fR(1) instance manager treats +as "stop" commands. +.IP "\fBpostmulti_control_commands (reload flush)\fR" +The \fBpostfix\fR(1) commands that the \fBpostmulti\fR(1) instance manager +treats as "control" commands, that operate on running instances. +.IP "\fBsyslog_facility (mail)\fR" +The syslog facility of Postfix logging. +.IP "\fBsyslog_name (see 'postconf -d' output)\fR" +The mail system name that is prepended to the process name in syslog +records, so that "smtpd" becomes, for example, "postfix/smtpd". +.SH "FILES" +.na +.nf +$daemon_directory/main.cf, stock configuration file +$daemon_directory/master.cf, stock configuration file +$daemon_directory/postmulti-script, life-cycle helper program +.SH "SEE ALSO" +.na +.nf +postfix(1), Postfix control program +postfix-wrapper(5), Postfix multi-instance API +.SH "README FILES" +.na +.nf +Use "\fBpostconf readme_directory\fR" or "\fBpostconf +html_directory\fR" to locate this information. +MULTI_INSTANCE_README, Postfix multi-instance management +.SH "HISTORY" +.na +.nf +.ad +.fi +The \fBpostmulti\fR(1) command was introduced with Postfix +version 2.6. +.SH "LICENSE" +.na +.nf +.ad +.fi +The Secure Mailer license must be distributed with this software. +.SH "AUTHOR(S)" +.na +.nf +Victor Duchovni +Morgan Stanley + +Wietse Venema +IBM T.J. Watson Research +P.O. Box 704 +Yorktown Heights, NY 10598, USA diff --git a/postfix/man/man1/sendmail.1 b/postfix/man/man1/sendmail.1 index ed7bc5aca..b7abb31a0 100644 --- a/postfix/man/man1/sendmail.1 +++ b/postfix/man/man1/sendmail.1 @@ -383,7 +383,7 @@ this parameter is empty; otherwise, rewrite message headers and append the specified domain name to incomplete addresses. .IP "\fBsyslog_facility (mail)\fR" The syslog facility of Postfix logging. -.IP "\fBsyslog_name (postfix)\fR" +.IP "\fBsyslog_name (see 'postconf -d' output)\fR" The mail system name that is prepended to the process name in syslog records, so that "smtpd" becomes, for example, "postfix/smtpd". .SH "FILES" @@ -407,8 +407,12 @@ syslogd(8), system logging .SH "README_FILES" .na .nf +.ad +.fi Use "\fBpostconf readme_directory\fR" or "\fBpostconf html_directory\fR" to locate this information. +.na +.nf DEBUG_README, Postfix debugging howto ETRN_README, Postfix ETRN howto VERP_README, Postfix VERP howto diff --git a/postfix/man/man5/postconf.5 b/postfix/man/man5/postconf.5 index 36690a5b7..00d394280 100644 --- a/postfix/man/man5/postconf.5 +++ b/postfix/man/man5/postconf.5 @@ -376,6 +376,12 @@ via the MAIL_CONFIG environment parameter. This list must be specified in the default Postfix configuration directory, and is used by set-gid Postfix commands such as \fBpostqueue\fR(1) and \fBpostdrop\fR(1). +.SH always_add_missing_headers (default: no) +Always add (Resent-) From:, To:, Date: or Message-ID: headers +when not present. Postfix 2.6 and later add these headers only +when clients match the local_header_rewrite_clients parameter +setting. Earlier Postfix versions always add these headers; this +may break DKIM signatures that cover non-existent headers. .SH always_bcc (default: empty) Optional address that receives a "blind carbon copy" of each message that is received by the Postfix mail system. @@ -1906,6 +1912,12 @@ This feature is available in Postfix 2.1 and later. .SH line_length_limit (default: 2048) Upon input, long lines are chopped up into pieces of at most this length; upon delivery, long lines are reconstructed. +.SH lmtp_assume_final (default: no) +When an LMTP server announces no DSN support, assume that the +server performs final delivery, and send "delivered" delivery status +notifications instead of "relayed". The default setting is backwards +compatible to avoid the infinetisimal possibility of breaking +existing LMTP-based content filters. .SH lmtp_bind_address (default: empty) The LMTP-specific version of the smtp_bind_address configuration parameter. See there for details. @@ -3219,11 +3231,16 @@ Postfix instance, and that are started, stopped, etc., together with the default Postfix instance. Specify a list of pathnames separated by comma or whitespace. .PP -When the list of non-default Postfix configuration directories -is non-empty, the \fBpostfix\fR(1) command will invoke the multi-instance -manager specified with the multi_instance_wrapper parameter to -execute commands on the default instance and on all additional -Postfix instances. +When $multi_instance_directories is empty, the \fBpostfix\fR(1) command +runs in single-instance mode and operates on a single Postfix +instance only. Otherwise, the \fBpostfix\fR(1) command runs in multi-instance +mode and invokes the multi-instance manager specified with the +multi_instance_wrapper parameter. The multi-instance manager in +turn executes \fBpostfix\fR(1) commands for the default instance and for +all Postfix instances in $multi_instance_directories. +.PP +Currently, this parameter setting is ignored except for the +default main.cf file. .PP This feature is available in Postfix 2.6 and later. .SH multi_instance_enable (default: no) @@ -3532,6 +3549,25 @@ The numerical Postfix SMTP server response code when a request is rejected by the \fBreject_plaintext_session\fR restriction. .PP This feature is available in Postfix 2.3 and later. +.SH postmulti_control_commands (default: reload flush) +The \fBpostfix\fR(1) commands that the \fBpostmulti\fR(1) instance manager +treats as "control" commands, that operate on running instances. For +these commands, disabled instances are skipped. +.PP +This feature is available in Postfix 2.6 and later. +.SH postmulti_start_commands (default: start) +The \fBpostfix\fR(1) commands that the \fBpostmulti\fR(1) instance manager treats +as "start" commands. For these commands, disabled instances are "checked" +rather than "started", and failure to "start" a member instance of an +instance group will abort the start-up of later instances. +.PP +This feature is available in Postfix 2.6 and later. +.SH postmulti_stop_commands (default: see "postconf -d" output) +The \fBpostfix\fR(1) commands that the \fBpostmulti\fR(1) instance manager treats +as "stop" commands. For these commands, disabled instances are skipped, +and enabled instances are processed in reverse order. +.PP +This feature is available in Postfix 2.6 and later. .SH prepend_delivered_header (default: command, file, forward) The message delivery contexts where the Postfix \fBlocal\fR(8) delivery agent prepends a Delivered-To: message header with the address diff --git a/postfix/man/man5/postfix-wrapper.5 b/postfix/man/man5/postfix-wrapper.5 index 0e18e4a91..f2a1a71a8 100644 --- a/postfix/man/man5/postfix-wrapper.5 +++ b/postfix/man/man5/postfix-wrapper.5 @@ -15,34 +15,28 @@ queue and data files. This document describes how the familiar "postfix start" etc. user interface can be used to manage one or multiple -Postfix instances, and gives details of an API that allows -the postfix(1) command to coordinate activities with a -multi-instance manager program. - -A trivial but useful multi-instance manager implementation -is described below, and can be found in the file -$daemon_directory/postfix-wrapper. The latter file also -contains instructions for setting up multiple instances. +Postfix instances, and gives details of an API to coordinate +activities between the postfix(1) command and a multi-instance +manager program. With multi-instance support, the default Postfix instance -is required. The location of its configuration files is -specified by the built-in default value for the config_directory -parameter. +is always required. The config_directory parameter's default +value specifies that instance's configuration file location. .SH "GENERAL OPERATION" .na .nf .ad .fi -Multi-instance support is backwards compatible: when there -is only one Postfix instance, commands such as "postfix -start" keep doing what they have always done. +Multi-instance support is backwards compatible: when you +run only one Postfix instance, commands such as "postfix +start" will not change behavior at all. -Even after multi-instance support has been set up through -the mechanisms discussed later, sites can continue to use -the familiar postfix commands in boot scripts, upgrade -procedures, and other places. +Even with multiple Postfix instances, you can keep using +the same postfix commands in boot scripts, upgrade procedures, +and other places. The commands do more work, but humans are +not forced to learn new tricks. -To start all applicable Postfix instances, use: +For example, to start all Postfix instances, use: .IP # postfix start .PP @@ -59,8 +53,8 @@ a multi-instance configuration. .nf .ad .fi -To operate on a specific Postfix instance, specify its -configuration directory on the postfix(1) command line: +To manage a specific Postfix instance, specify its configuration +directory on the postfix(1) command line: .IP # postfix -c \fI/path/to/config_directory command\fR .PP @@ -69,41 +63,57 @@ configuration directory via the MAIL_CONFIG environment variable (the -c command-line option has higher precedence). When no Postfix instance information is specified, the -postfix(1) command will operate on all applicable Postfix -instances. -.SH "MULTI-INSTANCE MANAGER IMPLEMENTATION" +postfix(1) command will operate on all Postfix instances. +.SH "ENABLING POSTFIX(1) MULTI-INSTANCE MODE" .na .nf .ad .fi -Historically, the postfix(1) command invokes the postfix-script -file (currently installed in the daemon directory). This -file contains the commands that start or stop Postfix, -upgrade the configuration and so on. +By default, the postfix(1) command operates in single-instance +mode. In this mode the command invokes the postfix-script +file directly (currently installed in the daemon directory). +This file contains the commands that start or stop one +Postfix instance, that upgrade the configuration of one +Postfix instance, and so on. -When multi-instance support is turned on, the postfix(1) -command needs to execute these commands for each applicable -Postfix instance. This multiplication of commands is handled -by a multi-instance manager program. +When the postfix(1) command operates in multi-instance mode +as discussed below, the command needs to execute start, +stop, etc. commands for each Postfix instance. This +multiplication of commands is handled by a multi-instance +manager program. -Turning on multi-instance support goes as follows: update -the default Postfix instance's main.cf file, and populate -the multi_instance_directories parameter with the configuration -directory pathnames of additional Postfix instances. - -With multi-instance support turned on, the postfix(1) command -invokes a multi-instance manager command instead of the -postfix-script file. The multi-instance manager executes -the postfix(1) command for each applicable Postfix instance. -The pathname of the multi-instance manager is specified in -the default main.cf file with the multi_instance_wrapper -parameter. +Turning on postfix(1) multi-instance mode goes as follows: +in the default Postfix instance's main.cf file, 1) specify +the pathname of a multi-instance manager program with the +multi_instance_wrapper parameter; 2) populate the +multi_instance_directories parameter with the configuration +directory pathnames of additional Postfix instances. For +example: +.IP +.nf +/etc/postfix/main.cf: + multi_instance_wrapper = $daemon_directory/postfix-wrapper + multi_instance_directories = /etc/postfix-test +.fi +.PP +The $daemon_directory/postfix-wrapper file implements a +simple manager and contains instructions for creating Postfix +instances by hand. The postmulti(1) command provides a +more extensive implementation including support for life-cycle +management. The multi_instance_directories and other main.cf parameters are listed below in the CONFIGURATION PARAMETERS section. -A useful multi-instance manager implementation can be as -simple as: +In multi-instance mode, the postfix(1) command invokes the +$multi_instance_wrapper command instead of the postfix-script +file. This multi-instance manager in turn executes the +postfix(1) command in single-instance mode for each Postfix +instance. + +To illustrate the main ideas behind multi-instance operation, +below is an example of a simple but useful multi-instance +manager implementation: .IP .nf #!/bin/sh @@ -134,52 +144,49 @@ done exit $err .fi -.PP -A sample implementation, with instructions, can be found -in $daemon_directory/postfix-wrapper. - -The postmulti(1) command implements a more sophisticated -approach, based on a combination of C code and scripting. -.SH "ENABLING A SPECIFIC INSTANCE FOR MULTI-INSTANCE OPERATION" +.SH "PER-INSTANCE MULTI-INSTANCE MANAGER CONTROLS" .na .nf .ad .fi Each Postfix instance has its own main.cf file with parameters -that control multi-instance operation. The most important -settings are discussed here. +that control how the multi-instance manager operates on +that instance. This section discusses the most important +settings. The setting "multi_instance_enable = yes" allows the -multi-instance manager to start (and stop) the corresponding +multi-instance manager to start (stop, etc.) the corresponding Postfix instance. For safety reasons, this setting is not the default. -The setting "multi_instance_enable = no" is useful for -manual testing. With this, the multi-instance manager will -not start the Postfix instance, and it will skip commands -such as "stop" or "flush" that require a running Postfix -instance. The multi-instance manager will execute commands -such as "check", "set-permissions" or "upgrade-configuration", -and it will replace "start" by "check" so that problems -will be reported even when the instance is disabled. -.SH "SHARED VERSUS NON-SHARED FILES" +The default setting "multi_instance_enable = no" is useful +for manual testing with "postfix -c \fI/path/name\fR start" +etc. The multi-instance manager will not start such an +instance, and it will skip commands such as "stop" or "flush" +that require a running Postfix instance. The multi-instance +manager will execute commands such as "check", "set-permissions" +or "upgrade-configuration", and it will replace "start" by +"check" so that problems will be reported even when the +instance is disabled. +.SH "MAINTAINING SHARED AND NON-SHARED FILES" .na .nf .ad .fi Some files are shared between Postfix instances, such as executables and manpages, and some files are per-instance, -such as the queue directory. See the NON-SHARED FILES -section below for a list of per-instance files. +such as configuration files, mail queue files, and data +files. See the NON-SHARED FILES section below for a list +of per-instance files. Before Postfix multi-instance support was implemented, the -executables, manpages, etc., have always been checked or -updated as part of the default Postfix instance. With -multi-instance support, we simply continue to do this. +executables, manpages, etc., have always been maintained +as part of the default Postfix instance. -Specifically, Postfix instances will not check or update -shared files when their config_directory value is listed -with the default main.cf's multi_instance_directories +With multi-instance support, we simply continue to do this. +Specifically, a Postfix instance will not check or update +shared files when that instance's config_directory value is +listed with the default main.cf file's multi_instance_directories parameter. The consequence of this approach is that the default Postfix diff --git a/postfix/man/man8/cleanup.8 b/postfix/man/man8/cleanup.8 index 412c3e1eb..cb6a47b61 100644 --- a/postfix/man/man8/cleanup.8 +++ b/postfix/man/man8/cleanup.8 @@ -93,6 +93,11 @@ non-standard Errors-To: message header, instead of the envelope sender address (this feature is removed with Postfix version 2.2, is turned off by default with Postfix version 2.1, and is always turned on with older Postfix versions). +.PP +Available in Postfix version 2.6 and later: +.IP "\fBalways_add_missing_headers (no)\fR" +Always add (Resent-) From:, To:, Date: or Message-ID headers +when not present. .SH "BUILT-IN CONTENT FILTERING CONTROLS" .na .nf @@ -158,30 +163,30 @@ filter) application, and for receiving the response. .IP "\fBmilter_content_timeout (300s)\fR" The time limit for sending message content to a Milter (mail filter) application, and for receiving the response. -.IP "\fBmilter_connect_macros (see postconf -n output)\fR" +.IP "\fBmilter_connect_macros (see 'postconf -d' output)\fR" The macros that are sent to Milter (mail filter) applications after completion of an SMTP connection. -.IP "\fBmilter_helo_macros (see postconf -n output)\fR" +.IP "\fBmilter_helo_macros (see 'postconf -d' output)\fR" The macros that are sent to Milter (mail filter) applications after the SMTP HELO or EHLO command. -.IP "\fBmilter_mail_macros (see postconf -n output)\fR" +.IP "\fBmilter_mail_macros (see 'postconf -d' output)\fR" The macros that are sent to Milter (mail filter) applications after the SMTP MAIL FROM command. -.IP "\fBmilter_rcpt_macros (see postconf -n output)\fR" +.IP "\fBmilter_rcpt_macros (see 'postconf -d' output)\fR" The macros that are sent to Milter (mail filter) applications after the SMTP RCPT TO command. -.IP "\fBmilter_data_macros (see postconf -n output)\fR" +.IP "\fBmilter_data_macros (see 'postconf -d' output)\fR" The macros that are sent to version 4 or higher Milter (mail filter) applications after the SMTP DATA command. -.IP "\fBmilter_unknown_command_macros (see postconf -n output)\fR" +.IP "\fBmilter_unknown_command_macros (see 'postconf -d' output)\fR" The macros that are sent to version 3 or higher Milter (mail filter) applications after an unknown SMTP command. -.IP "\fBmilter_end_of_data_macros (see postconf -n output)\fR" +.IP "\fBmilter_end_of_data_macros (see 'postconf -d' output)\fR" The macros that are sent to Milter (mail filter) applications after the message end-of-data. .PP Available in Postfix version 2.5 and later: -.IP "\fBmilter_end_of_header_macros (see postconf -n output)\fR" +.IP "\fBmilter_end_of_header_macros (see 'postconf -d' output)\fR" The macros that are sent to Milter (mail filter) applications after the end of the message header. .SH "MIME PROCESSING CONTROLS" @@ -365,7 +370,7 @@ Safety net to keep mail queued that would otherwise be returned to the sender. .IP "\fBsyslog_facility (mail)\fR" The syslog facility of Postfix logging. -.IP "\fBsyslog_name (postfix)\fR" +.IP "\fBsyslog_name (see 'postconf -d' output)\fR" The mail system name that is prepended to the process name in syslog records, so that "smtpd" becomes, for example, "postfix/smtpd". .PP diff --git a/postfix/man/man8/smtp.8 b/postfix/man/man8/smtp.8 index 720f55859..b15d3aacd 100644 --- a/postfix/man/man8/smtp.8 +++ b/postfix/man/man8/smtp.8 @@ -580,6 +580,10 @@ or accepting connections. .IP "\fBipc_timeout (3600s)\fR" The time limit for sending or receiving information over an internal communication channel. +.IP "\fBlmtp_assume_final (no)\fR" +When an LMTP server announces no DSN support, assume that the +server performs final delivery, and send "delivered" delivery status +notifications instead of "relayed". .IP "\fBlmtp_tcp_port (24)\fR" The default TCP port that the Postfix LMTP client connects to. .IP "\fBmax_idle (100s)\fR" @@ -612,7 +616,7 @@ address. Randomize the order of equal-preference MX host addresses. .IP "\fBsyslog_facility (mail)\fR" The syslog facility of Postfix logging. -.IP "\fBsyslog_name (postfix)\fR" +.IP "\fBsyslog_name (see 'postconf -d' output)\fR" The mail system name that is prepended to the process name in syslog records, so that "smtpd" becomes, for example, "postfix/smtpd". .PP diff --git a/postfix/man/man8/verify.8 b/postfix/man/man8/verify.8 index fa8c05ec9..0d9a852d7 100644 --- a/postfix/man/man8/verify.8 +++ b/postfix/man/man8/verify.8 @@ -161,7 +161,7 @@ The process name of a Postfix command or daemon process. The location of the Postfix top-level queue directory. .IP "\fBsyslog_facility (mail)\fR" The syslog facility of Postfix logging. -.IP "\fBsyslog_name (postfix)\fR" +.IP "\fBsyslog_name (see 'postconf -d' output)\fR" The mail system name that is prepended to the process name in syslog records, so that "smtpd" becomes, for example, "postfix/smtpd". .SH "SEE ALSO" diff --git a/postfix/mantools/postlink b/postfix/mantools/postlink index 20b3db5a3..5fea129b3 100755 --- a/postfix/mantools/postlink +++ b/postfix/mantools/postlink @@ -93,7 +93,8 @@ while (<>) { s;\ballow_min_user\b;$&;g; s;\ballow_percent_hack\b;$&;g; s;\ballow_untrusted_routing\b;$&;g; - s;\balternate_config_direc[-]*\n*[ ]*tories\b;$&;g; + s;\balternate_con[-]*\n*[ ]*fig_direc[-]*\n*[ ]*tories\b;$&;g; + s;\balways_add_missing_headers\b;$&;g; s;\balways_bcc\b;$&;g; s;\banvil_rate_time_unit\b;$&;g; s;\bappend_at_myorigin\b;$&;g; @@ -119,7 +120,7 @@ while (<>) { s;\bcommand_execu[-]*\n* *[]*tion_direc[-]*\n* *[]*tory\b;$&;g; s;\bexecu[-]*\n* *[]*tion_directory_expansion_filter\b;$&;g; s;\banvil_status_update_time\b;$&;g; - s;\bcommand_directory\b;$&;g; + s;\bcommand_direc[-]*\n* *[]*tory\b;$&;g; s;\bcommand_expan[-]*\n* *[]*sion_filter\b;$&;g; s;\bcommand_time_limit\b;$&;g; s;\bconfig_direc[-]*\n*[ ]*tory\b;$&;g; @@ -333,6 +334,9 @@ while (<>) { s;\bpermit_mx_backup_networks\b;$&;g; s;\bpickup_service_name\b;$&;g; s;\bplaintext_reject_code\b;$&;g; + s;\bpostmulti_start_commands\b;$&;g; + s;\bpostmulti_stop_commands\b;$&;g; + s;\bpostmulti_con[-]*\n* *[]*trol_commands\b;$&;g; s;\bprepend_delivered_header\b;$&;g; s;\bprocess_id\b;$&;g; s;\bprocess_id_directory\b;$&;g; @@ -725,6 +729,7 @@ while (<>) { s/[]*postlock[<\/bB>]*\(1\)/$&<\/a>/g; s/[]*postlog[<\/bB>]*\(1\)/$&<\/a>/g; s/[]*postmap[<\/bB>]*\(1\)/$&<\/a>/g; + s/[]*postmulti[<\/bB>]*\(1\)/$&<\/a>/g; s/[]*postqueue[<\/bB>]*\(1\)/$&<\/a>/g; s/[]*postsuper[<\/bB>]*\(1\)/$&<\/a>/g; s/[]*send[-<\/bB>]*\n*[ ]*mail[<\/bB>]*\(1\)/$&<\/a>/g; diff --git a/postfix/proto/BUILTIN_FILTER_README.html b/postfix/proto/BUILTIN_FILTER_README.html index dbda9519c..af959d298 100644 --- a/postfix/proto/BUILTIN_FILTER_README.html +++ b/postfix/proto/BUILTIN_FILTER_README.html @@ -348,7 +348,8 @@ Such rules slow down all mail and complicate Postfix maintenance.

      Configuring header/body checks for mail from outside users only

      -

      The following information applies to Postfix 2.1. Earlier +

      The following information applies to Postfix 2.1 and later. +Earlier Postfix versions do not support the receive_override_options feature.

      @@ -375,6 +376,9 @@ service with header/body filtering turned off.

      -o receive_override_options=no_header_body_checks +
    • Add some firewall rule to prevent access to 1.2.3.4:smtp +from the outside world.

      +
    • One SMTP server address for mail from outside users with header/body filtering turned on via main.cf.

      diff --git a/postfix/proto/MULTI_INSTANCE_README.html b/postfix/proto/MULTI_INSTANCE_README.html new file mode 100644 index 000000000..1e5196adb --- /dev/null +++ b/postfix/proto/MULTI_INSTANCE_README.html @@ -0,0 +1,1272 @@ + + + + + + +Managing multiple Postfix instances on a single host + + + + + + + +

      Managing +multiple Postfix instances on a single host

      + +
      + +

      Overview

      + +

      This document is a guide to managing multiple Postfix instances +on a single host using the postmulti(1) instance manager. Multi-instance +support is available with Postfix version 2.6 and later. See the +postfix-wrapper(5) manual page for background on the instance +management framework, and on how to deploy a custom instance manager. +

      + +

      Topics covered in this document:

      + + + +

      Why multiple Postfix instances

      + +

      Postfix is a general-purpose mail system that can be configured +to serve a variety of needs. Examples of Postfix applications are:

      + +
        + +
      • Local mail submission for shell users and system processes.

        + +
      • Incoming (MX host) email from the Internet.

        + +
      • Outbound mail relay for a corporate network.

        + +
      • Authenticated submission for roaming users.

        + +
      • Before/after content-filter mail.

        + +
      + +

      A single Postfix configuration can provide many or all of these +services, but a complex interplay of settings may be required, for +example with master.cf options overriding main.cf settings. In this +document we take the view that multiple Postfix instances may be a +simpler way to configure a multi-function Postfix system. With +multiple Postfix instances, each instance has its own directories +for configuration, queue and data files, but it shares all Postfix +program and documentation files with other instances.

      + +

      Since there is no single right way to configure your system, +we recommend that you choose what makes you most comfortable. If +different Postfix services don't involve incompatible main.cf or +master.cf settings, and if they can be combined together without +complex tricks, then a single monolithic configuration may be the +simplest approach.

      + +

      The purpose of multi-instance support in Postfix is not to force +you to create multiple Postfix instances, but rather to give you a +choice. Multiple instances give you the freedom to tune each Postfix +instance to a single task that it does well and to combine instances +into complete systems.

      + +

      With the introduction of the postmulti(1) utility and the reduction +of the per-instance configuration footprint of a secondary Postfix +instance to just a main.cf and master.cf file (other files are now in +shared locations), we hope that multiple instances will be easier to +use than ever before.

      + +

      Null-client instances versus service instances

      + +

      In the multi-instance approach to configuring Postfix, the first +simplification is with the default local-submission Postfix instance. +

      + +

      Most UNIX systems require support for email submission with the +sendmail(1) command so that system processes such as cron jobs can +send status reports, and so that system users can send email with +command-line utilities. Such email can be handled with a null-client +Postfix configuration that forwards all mail to a central mail hub. +The null client will typically either not run an SMTP listener at +all (master_service_disable = inet), or it will listen only on the +loopback interface (inet_interfaces = loopback-only).

      + +

      When implementing specialized servers for inbound Internet +email, outbound MTAs, internal mail hubs, and so on, we recommend +using a null client for local submission and creating single-function +secondary Postfix instances to serve the specialized needs.

      + +
      + +

      Note: usually, you need to use different "myhostname" settings +when you run multiple instances on the same host. Otherwise, there +will be false "mail loops back to myself" alarms when one instance +tries to send mail into another instance. Typically, the null-client +instance will use the system's hostname, and other instances will +use their own dedicated "myhostname" settings. Different names are +not needed when instances send mail to each other with a protocol +other than SMTP, or with SMTP over a TCP port other than 25 as is +usual with SMTP-based content filters.

      + +
      + +

      Multi-instance walk-through

      + +

      Before discussing the fine details of multi-instance operation +we first show the steps for creating a border mail server. This +server has with a null-client Postfix instance for local submission, +an input Postfix instance to receive mail from the Internet, plus +an advanced SMTP +content-filter and an output Postfix instance to deliver filtered +email to its internal destination.

      + +

      Setting up the null-client Postfix instance

      + +

      On a border mail hub, while mail from the Internet requires a +great deal of scrutiny, locally submitted messages are typically +limited to mail from cron jobs and other system services. In this +regard the border MTA is not different from other Unix hosts in +your environment. For this reason, it will submit locally-generated +email to the internal mail hub. We start the construction of the +border mail server with the default +instance, which will be a local-submission null client: +

      + +
      +
      +/etc/postfix/main.cf:
      +    # We are mta1.example.com
      +    #
      +    myhostname = mta1.example.com
      +    mydomain = example.com
      +
      +    # Flat user-account namespace in example.com:
      +    #
      +    #   user@example.com not user@host.example.com
      +    #
      +    myorigin = $mydomain
      +
      +    # Postfix 2.6+, disable inet services, specifically disable smtpd(8)
      +    #
      +    master_service_disable = inet
      +
      +    # No local delivery:
      +    #
      +    mydestination =
      +    local_transport = error:5.1.1 Mailbox unavailable
      +    alias_database =
      +    alias_maps =
      +    local_recipient_maps =
      +
      +    # Send everything to the internal mailhub
      +    #
      +    relayhost = [mailhub.example.com]
      +
      +    # Indexed table macro:
      +    # (use "hash", ... when cdb is not available)
      +    #
      +    default_database_type = cdb
      +    indexed = ${default_database_type}:${config_directory}/
      +
      +    # Expose origin host of mail from "root", ...
      +    #
      +    smtp_generic_maps = ${indexed}generic
      +
      +    # Send messages addressed to "root", ... to the MTA support team
      +    #
      +    virtual_alias_maps = ${indexed}virtual
      +
      +/etc/postfix/generic:
      +    # The smarthost supports "+" addressing (recipient_delimiter = +).
      +    # Mail from "root" exposes the origin host, without replies
      +    # and bounces going back to the same host.
      +    #
      +    # On clustered MTAs this file is typically machine-built from
      +    # a template file. The build process expands the template into
      +    # "mtaadmin+root=mta1"
      +    #
      +    root    	mtaadmin+root=mta1
      +
      +/etc/postfix/virtual:
      +    # Caretaker aliases:
      +    #
      +    root    	mtaadmin
      +    postmaster	root
      +
      +
      + +

      You would typically also add a Makefile, to automatically run +postmap(1) commands when source files change. This Makefile also +creates a "generic" database when none exists.

      + +
      +
      +/etc/postfix/Makefile:
      +    MTAADMIN=mtaadmin
      +
      +    all: virtual.cdb generic.cdb
      +
      +    generic: Makefile
      +	    @echo Creating $@
      +	    @rm -f $@.tmp
      +	    @printf '%s\t%s+root=%s\n' root $MTAADMIN `uname -n` > $@.tmp
      +	    @mv $@.tmp generic
      +
      +    %.cdb: %
      +	    postmap cdb:$<
      +
      +
      + +

      Construct the "virtual" and "generic" databases (the latter is +created by running "make"), then start and test the null-client: +

      + +
      +
      +# cd /etc/postfix; make
      +# postfix start
      +# sendmail -i -f root -t <<EOF
      +From: root
      +To: root
      +Subject: test
      +
      +testing
      +EOF
      +
      +
      + +

      The test message should be delivered the members of the "mtaadmin" +address group (or whatever address group you choose) with the +following headers:

      + +
      +
      +From: mtaadmin+root=mta1@example.com
      +To: mtadmin+root=mta1@example.com
      +Subject: test
      +
      +
      + +

      Setting up the "output" Postfix instance

      + +

      With the null-client instance out of the way, we can create the +MTA "output" instance that will deliver filtered mail to the inside +network. We add the "output" instance first, because the output +instance needs to be up and running before the input instance can +be fully tested, and when the system boots, the "output" instance +must start before the input instance. We will put the output and +input instances into a single instance group named "mta".

      + +

      Just once, when adding the first secondary instance, enable +multi-instance support in the default (null-client) instance:

      + +
      +
      +# postmulti -e init
      +
      +
      + +

      Then create the output instance:

      + +

      +
      +# postmulti -I postfix-out -G mta -e create
      +
      +
      + +

      The instance configuration directory defaults to /etc/postfix-out, +more precisely, the "postfix-out" subdirectory of the parent directory +of the default-instance configuration directory. The new instance will +be created in a "disabled" state:

      + +
      +
      +/etc/postfix-out/main.cf
      +    #
      +    # ... "stock" main.cf settings ...
      +    #
      +    multi_instance_name = postfix-out
      +    queue_directory = /var/spool/postfix-out
      +    data_directory = /var/lib/postfix-out
      +    #
      +    multi_instance_enable = no
      +    master_service_disable = inet
      +    authorized_submit_users =
      +
      +
      + +

      This instance has a "stock" master.cf file, and its queue and +data directories, also named "postfix-out", will be located in the +same parent directories as the corresponding directories of the +default instance (e.g., /var/spool/postfix-out and /var/lib/postfix-out). +

      + +

      While this instance is immediately safe to start, it is not yet +usefully configured. It needs to be customized to fit the role of a +post-filter re-injection SMTP service. Typical additions include:

      + +
      +
      +/etc/postfix-out/master.cf:
      +    # Replace default "smtp inet" entry with one listening on port 10026.
      +    127.0.0.1:10026     inet  n       -       n       -       -       smtpd
      +
      +/etc/postfix-out/main.cf
      +    # ...
      +
      +    # Comment out if you don't use IPv6 internally
      +    # inet_protocols = ipv4
      +    inet_interfaces = loopback-only
      +    mynetworks_style = host
      +    smtpd_authorized_xforward_hosts = $mynetworks
      +
      +    # Don't anvil(8) control the re-injection port.
      +    #
      +    smtpd_client_connection_count_limit = 0
      +    smtpd_client_event_limit_exceptions = $mynetworks
      +
      +    # Best practice when inet_interfaces is set, as this is not a
      +    # "secondary IP personality" configuration.
      +    #
      +    smtp_bind_address = 0.0.0.0
      +
      +    # All header rewriting happens upstream
      +    #
      +    local_header_rewrite_clients =
      +
      +    # No local delivery on border gateway
      +    #
      +    mydestination =
      +    alias_maps =
      +    alias_database =
      +    local_recipient_maps =
      +    local_transport = error:5.1.1 Mailbox unavailable
      +
      +    # May need a recipient_delimiter for per-user transport lookups:
      +    #
      +    recipient_delimiter = +
      +
      +    # Only one (unrestricted client)
      +    # With multiple instances, rarely need "-o param=value" overrides
      +    # in master.cf, each instance gets its own main.cf file.
      +    #
      +    smtpd_recipient_restrictions = permit_mynetworks, reject
      +
      +    # Tolerate occasional high latency in the  content filter.
      +    #
      +    smtpd_timeout = 1200s
      +
      +    # Best when empty, with all parent domain matches explicit.
      +    #
      +    parent_domain_matches_subdomains =
      +
      +    # Use the "relay" transport for inbound mail, and the default
      +    # "smtp" transport for outbound mail (bounces, ...). The latter
      +    # won't starve the former of delivery agent slots.
      +    #
      +    relay_domains = example.com, .example.com
      +
      +    # With xforward, match the input instance setting, if you
      +    # want "yes", set both to "yes".
      +    #
      +    smtpd_client_port_logging = no
      +
      +    # Transport settings ...
      +    # Message size limit
      +    # Concurrency tuning for "relay" and "smtp" transport
      +    # ...
      +
      +
      + +

      With the "output" configuration in place, enable and start the +instance:

      + +
      +
      +1 # postmulti -i postfix-out -x postconf -e \
      +2     "master_service_disable =" "authorized_submit_users = root"
      +3 # postmulti -i postfix-out -e enable
      +4 # postmulti -i postfix-out -p start
      +
      +
      + +

      This uses the postmulti(1) command to invoke postconf(1) in the +context (MAIL_CONFIG=/etc/postfix-out) of the output instance.

      + +
        + +
      • Lines 1-2: With "authorized_submit_users = root", the +superuser can test the postix-out instance with "postmulti -i +postfix-out -x sendmail -bv recipient...", but otherwise local +submission remains disabled.

        + +
      • Lines 1-2: With "master_service_disable =", the "inet" +listeners are re-enabled.

        + +
      • Line 3: The output instance is enabled for multi-instance +start/stop.

        + +
      • Line 4: The output instance is started.

        + +
      + +

      Test the output instance by submitting probe messages via "sendmail +-bv" and "telnet". For production systems, in-depth configuration tests +should be done on a lab system. The simple tests just suggested will only +confirm successful deployment of a configuration that should already be +known good.

      + +

      Setting up the content-filter proxy

      + +

      With the output instance ready, deploy your content-filter +proxy. Most proxies will need their own /etc/rc* start/stop script. +Some proxies, however, are started on demand by the Postfix spawn(8) +service, in which case you need to add the relevant spawn(8) entry +to the output instance master.cf file.

      + +

      Configure the proxy to listen on 127.0.0.1:10025 and to re-inject +filtered email to 127.0.0.1:10026. Start the proxy service if +necessary, then test the proxy via "telnet" or automated SMTP +injectors. The proxy should support the following ESMTP features: +DSN, 8BITMIME, and XFORWARD. In addition, the proxy should support +multiple mail deliveries within an SMTP session.

      + +

      Setting up the input Postfix instance

      + +

      The input Postfix instance receives mail from the network and +sends it through the content filter. Now we create the input instance, +also part of the "mta" instance group:

      + +
      +
      +# postmulti -I postfix-in -G mta -e create
      +
      +
      + +

      The new instance configuration directory defaults to /etc/postfix-in, +more precisely, the "postfix-in" subdirectory of the parent directory +of the default-instance configuration directory. The new instance will +be created in a "disabled" state:

      + +
      +
      +/etc/postfix-in/main.cf
      +    #
      +    # ... "stock" main.cf settings ...
      +    #
      +    multi_instance_name = postfix-in
      +    queue_directory = /var/spool/postfix-in
      +    data_directory = /var/lib/postfix-in
      +    #
      +    multi_instance_enable = no
      +    master_service_disable = inet
      +    authorized_submit_users =
      +
      +
      + +

      As before, make appropriate changes to main.cf and master.cf to +make the instance production ready. Consider setting "soft_bounce = yes" +during the first few hours of deployment, so you can iron-out any unexpected +"kinks".

      + +

      Manual testing can start with: + +

      +
      +/etc/postfix-in/main.cf
      +    # Accept only local traffic, but allow impersonation:
      +    inet_interfaces = 127.0.0.1
      +    smtpd_authorized_xclient_hosts = 127.0.0.1
      +
      +
      + +

      This allows you to use the Postfix-specific XCLIENT SMTP command to safely +simulate connections from remote systems before any remote systems +are able to connect. If the test results look good, revert the above +settings to the required production values. Typical settings in the +pre-filter input instance include:

      + +
      +
      +/etc/postfix-in/main.cf
      +    #
      +    # ... 
      +    #
      +
      +    # No local delivery on border gateway
      +    #
      +    mydestination =
      +    alias_maps =
      +    alias_database =
      +    local_recipient_maps =
      +    local_transport = error:5.1.1 Mailbox unavailable
      +
      +    # Don't rewrite remote headers
      +    #
      +    local_header_rewrite_clients =
      +
      +    # All recipients of not yet filtered email go to the same filter together.
      +    #
      +    # With multiple instances, the content-filter is specified
      +    # via transport settings not the "content_filter" transport
      +    # switch override! Here the filter listens on local port 10025.
      +    #
      +    # If you need to route some users or recipient domains directly to the
      +    # output instance bypassing the filter, just define a transport table
      +    # with suitable entries.
      +    #
      +    default_transport = smtp:[127.0.0.1]:10025
      +    relay_transport = $default_transport
      +    virtual_transport = $default_transport
      +    transport_maps =
      +
      +    # Pass original client log information through the filter.
      +    #
      +    smtp_send_xforward_command = yes
      +
      +    # Avoid splitting the envelope and scanning messages multiple times.
      +    # Match the re-injection server's recipient limit.
      +    #
      +	smtp_destination_recipient_limit = 1000
      +
      +    # Tolerate occasional high latency in the content filter.
      +    #
      +    smtp_data_done_timeout = 1200s
      +
      +    # With xforward, match the output instance setting, if you
      +    # want "yes", set both to "yes".
      +    #
      +    smtpd_client_port_logging = no
      +
      +    # ... Lots of settings for inbound MX host ...
      +
      +
      + +

      With the "input" instance configured, enable and start it:

      + +
      +
      +# postmulti -i postfix-in -x postconf -e \
      +    "master_service_disable =" "authorized_submit_users = root"
      +# postmulti -i postfix-in -e enable
      +# postmulti -i postfix-in -p start
      +
      +
      + +

      That's it. You now have a 3-instance configuration. A null-client +sending all locally submitted mail to the internal mail hub and a pair of +"mta" instances that receive mail from the Internet, pass it through a +content-filter, and then deliver it to the internal destination.

      + +

      Running "postfix start" or "postfix stop" will now start/stop all +three Postfix instances. You can use "postfix -c /config/path start" +to start just one instance, or use the instance name (or instance +group name) via postmulti(1):

      + +
      +
      +# postmulti -i - -p stop
      +# postmulti -g mta -p status
      +# postmulti -i postfix-out -p flush
      +# postmulti -i postfix-in -p reload
      +# ...
      +
      +
      + +

      This example ends the multi-instance "walk through". The remainder +of this document provides background information on Postfix +multi-instance support features and options.

      + +

      Components of a Postfix system

      + +

      A Postfix system consists of the following components:

      + +

      Shared among all instances:

      + +
        + +
      • Command-line utilities for administrators and users installed in +$command_directory, $sendmail_path, $mailq_path and $newaliases_path.

        + +
      • Daemon executables, and run-time support files installed in +$daemon_directory.

        + +
      • Bundled documentation, installed in $html_directory, +$manpage_directory and $readme_directory.

        + +
      • Entries in /etc/passwd and /etc/group for the $mail_owner user and +$setgid_group group. The the $mail_owner user provides the mail system +with a protected (non-root) execution context. The $setgid_group group +is used exclusively to support the setgid postdrop(1) and postqueue(1) +utilities (it must not be the primary group or secondary group +of any users, including the $mail_owner user).

        + +
      + +

      Private to each instance:

      + +
        + +
      • The main.cf, master.cf (and other optional) configuration +files in $config_directory.

        + +
      • The maildrop, incoming, active, deferred and hold queues +in $queue_directory (which contains additional directories needed +by Postfix, and which optionally doubles as a chroot jail for Postfix +daemon processes).

        + +
      • Various caches (TLS session, address verification, ...) +in $data_directory.

        + +
      + +

      The Postfix configuration parameters mentioned above are +collectively referred to as "installation parameters". Their default +values are set when the Postfix software is built from source, and +all but one may be optionally set to a non-default value via the +main.cf file. The one parameter that (catch-22) cannot be set in +main.cf is $config_directory, as this defines the location of the +main.cf file itself.

      + +

      Though config_directory cannot be set in main.cf, postfix(1) and +most of the other command-line Postfix utilities allow you to specify a +non-default configuration directory via a command line option (typically +-c) or via the MAIL_CONFIG environment variable. In this way, +it is possible to have multiple configuration directories on the same +machine, and to have multiple running master(8) daemons each with its +own configuration files, queue directory and data directory.

      + +

      These multiple running copies of master(8) share the base Postfix +software. They do not (and cannot) share their configuration +directories, queue directories or data directories.

      + +

      Each combination of configuration directory, together with the queue +directory and data directory (specified in the corresponding main.cf file) +make up a Postfix instance.

      + +

      The default Postfix instance

      + +

      One Postfix instance is special: this is the instance whose +configuration directory is the default one compiled into the Postfix +utilities. The location of the default configuration directory is +typically /etc/postfix, and can be queried via the "postconf -d +config_directory" command. We call the instance with this configuration +directory the "default instance".

      + +

      The default instance is responsible for local mail submission. The +setgid postdrop(1) utility is used by the sendmail(1) local submission +program to spool messages into the maildrop sub-directory of the +queue directory of the default instance.

      + +

      Even in the rare case when "sendmail -C" is used to submit local mail +into a non-default Postfix instance, for security reasons, postdrop(1) +will consult the default main.cf file to check the validity of the +requested non-default configuration directory.

      + +

      So, while in most other respects, all instances are equal, the +default instance is "more equal than others". You may choose to create +additional instances, but you must have at least the default instance, +with its configuration directory in the default compiled-in location.

      + +

      Instance groups

      + +

      The postmulti(1) multi-instance manager supports the notion of an +instance "group". Typically, the member instances of an instance group +constitute a logical service, and are expected to all be running or all +be stopped.

      + +

      In many cases a single Postfix instance will be a complete logical +"service". You should define such instances as stand-alone instances +that are not members of any instance "group". The null-client +instance is an example of a non-group instance.

      + +

      When a logical service consists of multiple Postfix instances, +often a pair of pre-filter and post-filter instances with a content +filter proxy between them, the related instances should be members +of a single instance group (however, the content filter usually has +its own start/stop procedure that is separate from any Postfix +instance).

      + +

      The default instance main.cf file's $multi_instance_directories +configuration parameter lists the configuration directories of all +secondary (non-default) instances. Together with the default instance, +these secondary instances are managed by the multi-instance manager. +Instances are started in the order listed, and stopped in the +opposite order. For instances that are members of a service "group", +you should arrange to start the service back-to-front, with the +output stages started and ready to receive mail before the input +stages are started.

      + +

      Multi-instance configuration parameters

      + +
      + +
      multi_instance_wrapper
      + +

      This default-instance configuration parameter must be set +to a suitable multi-instance manager's "wrapper" program that +controls the starting, stopping, etc. of a multi-instance Postfix +system. To use the postmulti(1) manager described in this document, +this parameter should be set with the "postmulti +-e init" command.

      + +
      multi_instance_directories
      + +

      This default-instance configuration parameter specifies +an optional list of the secondary instances controlled via the +multi-instance manager. Instances are listed in their "start" order, +with the default instance always started first (if enabled). If +$multi_instance_directories is left empty, the postfix(1) command +runs with multi-instance support turned off, and none of the +multi_instance_ configuration parameters will have any effect.

      + +

      Do not assign a non-empty list of secondary instance configuration +directories to multi_instance_directories until you have configured a +suitable multi_instance_wrapper setting! This is best accomplished via +the "postmulti -e init" command. +

      + +
      multi_instance_name
      + +

      Each Postfix instance may be assigned a distinct name (with +"postfix -e create/import/assign -I name..."). This name can +be used with the postmulti(1) command-line utility to perform tasks +on the instance by name (rather than the full pathname of its +configuration directory). Choose a name that concisely captures the +role of the instance (it must start with "postfix-"). It is an +error for two instances to have the same $multi_instance_name. You +can leave an instance "nameless" by leaving this parameter at the +default empty setting.

      + +

      To avoid confusion in your logs, if you don't assign each +secondary instance a non-empty (distinct) $multi_instance_name, you +should make sure that the $syslog_name setting is different for +each instance. The $syslog_name parameter defaults to $multi_instance_name +when the latter is non-empty. If at all possible, the syslog_name +should start with "postfix-", this helps log parsers to identify +log entries from secondary Postfix instances.

      + +
      multi_instance_group
      + +

      Each Postfix instance may be assigned an "instance group" +name (with "postfix -e create/import/assign -G name..."). +The default (empty) value of multi_instance_group parameter indicates +a stand-alone instance that is not part of any group. The group +name can be used with the postmulti(1) command-line utility to +perform a task on the members of a group by name. Choose a single-word +group name that concisely captures the role of the group.

      +
      + +
      multi_instance_enable
      + +

      This parameter controls whether a Postfix instance will +be started by a Postfix multi-instance manager. The default value +is "no". The instance can be started explicitly with "postfix -c +/path/to/config/directory"; this is useful for testing.

      + +

      When an instance is disabled, the postfix(1) "start" command +is replaced by "check".

      + +

      Some postfix(1) commands (such as "stop", "flush", ...) require +a running Postfix instance, and skip instances that are disabled. +

      + +

      Other postfix(1) commands (such as "status", "set-permissions", +"upgrade-configuration", ...) do not require a running Postfix +system, and apply to all instances whether enabled or not.

      +
      + +
      + +

      The postmulti(1) utility can be used to create (or destroy) instances. +It can also be used to "import" or "deport" existing instances into or +from the list of managed instances. When using postmulti(1) to manage +instances, the above configuration parameters are managed for you +automatically. See below.

      + +

      Using the postmulti(1) command

      + + + +

      Initializing the multi-instance manager

      + +

      Before postmulti(1) is used for the first time, you must install +it as the multi_instance_wrapper for your Postfix system and enable +multi-instance operation of the default Postfix instance. You can then +proceed to add new or existing +instances to the multi-instance configuration. This initial installation +is accomplished as follows:

      + +
      +
      +    # postmulti -e init
      +
      +
      + +

      This updates the default instance main.cf file as follows:

      + +
      +
      +    # Use postmulti(1) as a postfix-wrapper(5)
      +    #
      +    multi_instance_wrapper = ${command_directory}/postmulti -p --
      +
      +    # Configure the default instance to start when in multi-instance mode
      +    #
      +    multi_instance_enable = yes
      +
      +
      + +

      If you prefer, you can make these changes by editing the default +main.cf directly, or by using "postconf -e".

      + +

      Listing managed instances

      + +

      The list of managed instances consists of the default instance and +the additional instances whose configuration directories are listed +(in start order) under the multi_instance_directories parameter of the +default main.cf configuration file.

      + +

      You can list selected instances, groups of instances or all +instances by specifying only the instance matching options with the +"-l" option. The "-a" option is assumed if no other instance +selection options are specified (this behavior changes with the +"-e" option). As a special case, even if it has an explicit name, +the default instance can always be selected via "-i -".

      + +
      +
      +# postmulti -l -a
      +# postmulti -l -g a_group
      +# postmulti -l -i an_instance
      +
      +
      + +

      The output is one line per instance (in "postfix start" order): +

      + +
      + + + + + + +
      name group enabled config_directory
      - - yes /etc/postfix + +
      mta-out mta yes /etc/postfix/mta-out + +
      mta-in mta yes /etc/postfix-mta-in + +
      msa-out msa yes /etc/postfix-msa-out + +
      msa-in msa yes /etc/postfix-msa-in + +
      test - no /etc/postfix-test + +
      + +
      + +

      The first line showing the column headings is not part of the +output. When either the instance name or the instance group is not +set, it is shown as a "-".

      + +

      When selecting an existing instance via the "-i" option, you +can always use the full pathname of its configuration directory +instead of the instance (short) name. This is the only way to select +a non-default nameless instance. The default instance can be selected +via "-i -", whether it has a name or not.

      + +

      To list instances in reverse start order, include the "-R" +option together with the instance selection options.

      + +

      Starting or stopping a multi-instance system +

      + +

      To start, stop, reload, etc. the complete (already configured as +above) multi-instance system just use postfix(1) as you would with a +single-instance system. The Postfix multi-instance wrapper framework +insulates Postfix init.d start and package upgrade scripts from the +details of multi-instance management!

      + +

      The -p option of postmulti(1) turns on postfix(1) compatibility +mode. With this option the remaining arguments are exactly those supported +by postfix(1), but commands are applied to all instances or all enabled +instances as appropriate. As described above, this switch is required +when using postmulti(1) as the multi_instance_wrapper.

      + +

      If you want to specify a subset of instances by name, or group name, +or run arbitrary commands (not just "postfix stop/start/etc. in the +context (MAIL_CONFIG environment variable setting) of a particular +instance or group of instances, then you can use the instance-aware +postmulti(1) utility directly.

      + +

      Ad-hoc multi-instance operations

      + +

      The postmulti(1) command can be used by the administrator to run arbitrary +commands in the context of one or more Postfix instances. The most common +use-case is stopping or starting a group of Postfix instances:

      + +
      +
      +# postmulti -g mygroup -p start
      +# postmulti -g mygroup -p flush
      +# postmulti -g mygroup -p reload
      +# postmulti -g mygroup -p status
      +# postmulti -g mygroup -p stop
      +# postmulti -g mygroup -p upgrade-configuration
      +
      +
      + +

      The -p option is essentially a short-hand for a leading +postfix command argument, but with appropriate additional options +turned on depending on the first argument. In the case of "start", +disabled instances are "checked" (postfix check) rather than simply +skipped.

      + +

      The resulting command is executed for each candidate instance with +the MAIL_CONFIG environment variable set to the configuration +directory of the corresponding Postfix instance.

      + +

      The postmulti(1) utility is able to launch commands other than +postfix(1), Use the -x option to ask postmulti to execute an +ad-hoc command for all instances, a group of instances, or just one +instance. With ad-hoc commands the multi_instance_enable parameter +is ignored: the command is unconditionally executed for the instances +selected via -a, -g or -i. In addition to MAIL_CONFIG, the following +instance parameters are exported into the command environment:

      + +
      +
      +command_directory=$command_directory
      +daemon_directory=$daemon_directory
      +config_directory=$config_directory
      +queue_directory=$queue_directory
      +data_directory=$data_directory
      +multi_instance_name=$multi_instance_name
      +multi_instance_group=$multi_instance_group
      +multi_instance_enable=$multi_instance_enable
      +
      +
      + +

      The config_directory setting is of course the same as MAIL_CONFIG, +and is arguably redundant, but leaving it in is less surprising. If +you want to skip disabled instances, just check multi_instance_enable +environment variable and exit if it is set to "no".

      + +

      The ability to run ad-hoc commands opens up a wealth of additional +possibilities:

      + +
        + +
      • Specify an instance by name rather than configuration directory +when using sendmail(1) to send a verification probe:

        + +
        +
        +$ postmulti -i postfix-myinst -x sendmail -bv test@example.net
        +
        +
        + +
      • Display non-default main.cf settings of all Postfix instances. +This uses an inline shell script to package together multiple shell +commands to execute for each instance:

        + +
        +
        +$ postmulti -x sh -c 'echo "-- $MAIL_CONFIG"; postconf -n'
        +
        +
        + +
      • Put all mail in enabled member instances of a group on hold:

        + +
        +
        +# postmulti -g group_name -x \
        +    sh -c 'test $multi_instance_enable = yes && postsuper -h ALL'
        +
        +
        + +
      • Show top 10 domains in the deferred queue of all instances: +

        + +
        +
        +# postmulti -x sh -c 'echo "-- $MAIL_CONFIG"; qshape deferred | head -12'
        +
        +
        + +
      + +

      Creating a new Postfix instance

      + +

      The postmulti(1) command can be used to create additional Postfix +instances. New instances are created with local submission and all "inet" +services disabled via the following non-default parameter settings in +the main.cf file:

      + +
      +
      +authorized_submit_users =
      +master_service_disable = inet
      +
      +
      + +

      The above settings ensure that new instances are safe to start +immediately: they will not conflict with inet listeners in existing +Postfix instances. They will also not accept any mail until they are +fully configured, at which point you can do away with one or both of +the above safety measures.

      + +

      The postmulti(1) command encourages a preferred way of organizing +the configuration directories, queue directories and data directories +of non-default instances. If the default instance settings are:

      + +
      +
      +config_directory = /conf-path/postfix
      +queue_directory = /queue-path/postfix
      +data_directory = /data-path/postfix
      +
      +
      + +

      A newly-created instance named postfix-myinst will by default +have:

      + +
      +
      +multi_instance_enable = no
      +multi_instance_name = postfix-myinst
      +config_directory = /conf-path/postfix-myinst
      +queue_directory = /queue-path/postfix-myinst
      +data_directory = /data-path/postfix-myinst
      +
      +
      + +

      You can override any of these defaults when creating the instance, +but unless you want to spread instance queue directories over multiple +file-systems, use the default naming strategy. It keeps the multiple +instances organized in a uniform, predictable fashion.

      + +

      When specifying the instance name later, you can refer to it +either as "postfix-myinst", or via the full path of the configuration +directory.

      + +

      To create a new instance just use the -e create option:

      + +
      +
      +# postmulti -I postfix-myinst -e create
      +
      +
      + +

      If the new instance is to belong to a group of related instances that +implement a single logical service, assign it to a group:

      + +
      +
      +# postmulti -I postfix-myinst -G mygroup -e create
      +
      +
      + +

      If you want to override the conventional values of the instance +installation parameters, specify their values on the command-line:

      + +
      +
      +# postmulti [-I postfix-myinst] [-G mygroup] -e create \
      +	"config_directory = /path/to/config_directory" \
      +	"queue_directory = /path/to/queue_directory" \
      +	"data_directory = /path/to/data_directory"
      +
      +
      + +

      A note on the -I and -G options above. These are always +used to assign a name or group name to an instance, while the -i +and -g options always select existing instances. By default, +the configuration directories of newly managed instances are appended +to the instance list. You can use the "-i" or "-g" or "-a" options to +insert the new instance before the specified instance or group, or at +the beginning of the instance list (multi_instance_directories parameter +of the default instance).

      + +

      If you do specify a name (use "-I" with a name that is not "-") +for the new instance, you may omit any of the 3 instance installation +parameters whose instance-name based value is acceptable. Otherwise, all +three instance installation parameters are required. You should set the +"syslog_name" explicitly in the main.cf file of a "nameless" instance, +in order to avoid confusion in the mail logs when multiple instances +are in use.

      + +

      Destroying a Postfix instance

      + +

      If you no longer need an instance, you can destroy it via:

      + +
      +
      +# postmulti -i postfix-myinst -p stop
      +# postmulti -i postfix-myinst -e disable
      +# postmulti -i postfix-myinst -e destroy
      +
      +
      + +

      The instance must be stopped, disabled and have no queued messages. +This is expected to fully delete a just created instance that has never +been used. If the instance is not freshly created, files added after +the instance was created will remain in the configuration, queue or +data directories, in which case the corresponding directory may not +be fully removed and a warning to that effect will be displayed. You +can complete the destruction of the instance manually by removing any +unwanted remnants of the instance-specific "private" directories.

      + +

      Importing an existing Postfix instance

      + +

      If you already have an existing secondary Postfix instance that is +not yet managed via postmulti(1), you can "import" it into the list +of managed instances. If your instance is already using the default +configuration directory naming scheme, just specify the corresponding +instance name (the multi_instance_name parameter in its configuration +file will be adjusted to match this name if necessary):

      + +
      +
      +# postmulti -I postfix-myinst [-G mygroup] -e import
      +
      +
      + +

      Otherwise, you must specify the location of its configuration +directory:

      + +
      +
      +# postmulti [-I postfix-myinst] [-G mygroup] -e import \
      +	"config_directory = /path/of/config_directory"
      +
      +
      + +

      When the instance is imported, you can assign a name or a group. As +with "create", you can control the placement of the +new instance in the start order by using "-i", "-g" or "-a" to prepend +before the selected instance or instances.

      + +

      An imported instance is usually not multi-instance "enabled", +unless it was part of a multi-instance configuration at an earlier +time. If it is fully configured and ready to run, don't forget +to enable it and if necessary start it. When +other enabled instances are already running, new instances need to +be started individually when they are first created or imported. +

      + +

      To find out what instances are running, use:

      + +
      +
      +# postfix status
      +
      +
      + +

      Deporting a managed Postfix instance

      + +

      You can "deport" an existing instance from the list of managed +instances. This does not destroy the instance, rather the instance +just becomes a stand-alone Postfix instance not registered with the +multi-instance manager. postmulti(1) will refuse to "deport" an +instance that is not stopped and disabled.

      + +
      +
      +# postmulti -i postfix-myinst -p stop
      +# postmulti -i postfix-myinst -e disable
      +# postmulti -i postfix-myinst -e deport
      +
      +
      + +

      Assigning a new name or group name

      + +

      You can assign a new name or new group to a managed instance. +Use "-" as the new value to assign the instance to no group or make it +nameless. To specify a nameless secondary instance use the configuration +directory path instead of the old name:

      + +
      +
      +# postmulti -i postfix-old [-I postfix-new] [-G newgroup] -e assign
      +
      +
      + +

      Enabling/disabling managed instances

      + +

      You can enable or disable a managed instance. As documented in +postfix-wrapper(5), disabled instances are skipped with actions +that start, +stop or control running +Postfix instances.

      + +
      +
      +# postmulti -i postfix-myinst -e enable
      +# postmulti -i postfix-myinst -e disable
      +
      +
      + +

      Credits

      + +

      Wietse Venema created Postfix, designed and implemented the +multi-instance wrapper framework and provided design feedback that made +the postmulti(1) utility much more general and useful than originally +envisioned.

      + +

      The postmulti(1) utility was developed by Victor Duchovni of Morgan +Stanley, who also wrote the initial version of this document.

      + + diff --git a/postfix/proto/Makefile.in b/postfix/proto/Makefile.in index 9b1398ce6..886e7fc8b 100644 --- a/postfix/proto/Makefile.in +++ b/postfix/proto/Makefile.in @@ -24,6 +24,7 @@ HTML = ../html/ADDRESS_CLASS_README.html \ ../html/LINUX_README.html \ ../html/LOCAL_RECIPIENT_README.html ../html/MAILDROP_README.html \ ../html/MILTER_README.html \ + ../html/MULTI_INSTANCE_README.html \ ../html/MYSQL_README.html ../html/NFS_README.html \ ../html/OVERVIEW.html \ ../html/PACKAGE_README.html ../html/PCRE_README.html \ @@ -61,6 +62,7 @@ README = ../README_FILES/ADDRESS_CLASS_README \ ../README_FILES/LINUX_README \ ../README_FILES/LOCAL_RECIPIENT_README ../README_FILES/MAILDROP_README \ ../README_FILES/MILTER_README \ + ../README_FILES/MULTI_INSTANCE_README \ ../README_FILES/MYSQL_README ../README_FILES/NFS_README \ ../README_FILES/OVERVIEW \ ../README_FILES/PACKAGE_README ../README_FILES/PCRE_README \ @@ -196,6 +198,9 @@ clobber: ../html/MILTER_README.html: MILTER_README.html $(POSTLINK) $? >$@ +../html/MULTI_INSTANCE_README.html: MULTI_INSTANCE_README.html + $(POSTLINK) $? >$@ + ../html/MYSQL_README.html: MYSQL_README.html $(POSTLINK) $? >$@ @@ -343,6 +348,9 @@ clobber: ../README_FILES/MILTER_README: MILTER_README.html $(HT2READ) $? >$@ +../README_FILES/MULTI_INSTANCE_README: MULTI_INSTANCE_README.html + $(HT2READ) $? >$@ + ../README_FILES/MYSQL_README: MYSQL_README.html $(HT2READ) $? >$@ diff --git a/postfix/proto/OVERVIEW.html b/postfix/proto/OVERVIEW.html index 6e25b064d..ac8bbd476 100644 --- a/postfix/proto/OVERVIEW.html +++ b/postfix/proto/OVERVIEW.html @@ -728,6 +728,11 @@ for shell scripts.

      such as canonical(5), virtual(5) and others. It is a cousin of the UNIX makemap command.

      +
    • The postmulti(1) command repeats the "postfix start" etc. +command for each Postfix instance, and supports creation, deletion +etc. of Postfix instances. For a tutorial, see MULTI_INSTANCE_README. +

      +
    • The postqueue(1) command is the privileged command that is run by Postfix sendmail(1) and mailq(1) in order to flush or list the diff --git a/postfix/proto/STANDARD_CONFIGURATION_README.html b/postfix/proto/STANDARD_CONFIGURATION_README.html index ff5b64f56..4edc339f1 100644 --- a/postfix/proto/STANDARD_CONFIGURATION_README.html +++ b/postfix/proto/STANDARD_CONFIGURATION_README.html @@ -111,7 +111,7 @@ their default settings.

      1 /etc/postfix/main.cf: 2 myorigin = $mydomain 3 relayhost = $mydomain -4 inet_interfaces = 127.0.0.1 +4 inet_interfaces = loopback-only 5 local_transport = error:local delivery is disabled 6 7 /etc/postfix/master.cf: diff --git a/postfix/proto/STRESS_README.html b/postfix/proto/STRESS_README.html index 32744ffd2..6df8cb662 100644 --- a/postfix/proto/STRESS_README.html +++ b/postfix/proto/STRESS_README.html @@ -226,10 +226,11 @@ clients get a chance to talk to Postfix.

        -
      • Use "421" reply codes for botnet-related RBLs or for -selected non-RBL restrictions. This causes Postfix 2.3 and later -to disconnect immediately without waiting for the remote SMTP -client to send a QUIT command.

        +
      • Use "521" reply codes (Postfix 2.6 and later) for +botnet-related RBLs or for selected non-RBL restrictions. With +Postfix 2.3-2.5 use "421" for a similar result. The Postfix SMTP +server will disconnect immediately without waiting for the remote +SMTP client to send a QUIT command.

        You can set individual reject codes for RBLs, and for individual responses from a specific RBL. We'll use zen.spamhaus.org as an @@ -237,7 +238,7 @@ example; by the time you read this document, details may have changed. Right now, their documents say that a response of 127.0.0.10 or 127.0.0.11 indicates a dynamic client IP address, which means that the machine is probably running a bot of some kind. To give -a 421 response instead of the default 554 response, use something +a 521 response instead of the default 554 response, use something like:

        @@ -251,11 +252,11 @@ like: 

        8 rbl_reply_maps = hash:/etc/postfix/rbl_reply_maps 9 10 /etc/postfix/rbl_reply_maps: -11 zen.spamhaus.org=127.0.0.10 421 4.7.1 Service unavailable; +11 zen.spamhaus.org=127.0.0.10 521 4.7.1 Service unavailable; 12 $rbl_class [$rbl_what] blocked using 13 $rbl_domain${rbl_reason?; $rbl_reason} 14 -15 zen.spamhaus.org=127.0.0.11 421 4.7.1 Service unavailable; +15 zen.spamhaus.org=127.0.0.11 521 4.7.1 Service unavailable; 16 $rbl_class [$rbl_what] blocked using 17 $rbl_domain${rbl_reason?; $rbl_reason}
        @@ -264,7 +265,8 @@ like:

        will still only do a single DNS query, so the performance difference is negligible.

        -

        The down-side of sending 421 instead of the default 554 is that +

        With Postfix 2.3-2.5, use 421 (reply code 521 will not cause +Postfix to disconnect). The down-side of sending 421 is that it works only for zombies and other malware. If the client is running a real MTA, then it may connect again several times until the mail expires in its queue. When this is a problem, stick with the default diff --git a/postfix/proto/postconf.proto b/postfix/proto/postconf.proto index 628e4e618..5c55409e3 100644 --- a/postfix/proto/postconf.proto +++ b/postfix/proto/postconf.proto @@ -12065,11 +12065,16 @@ Postfix instance, and that are started, stopped, etc., together with the default Postfix instance. Specify a list of pathnames separated by comma or whitespace.

        -

        When the list of non-default Postfix configuration directories -is non-empty, the postfix(1) command will invoke the multi-instance -manager specified with the multi_instance_wrapper parameter to -execute commands on the default instance and on all additional -Postfix instances.

        +

        When $multi_instance_directories is empty, the postfix(1) command +runs in single-instance mode and operates on a single Postfix +instance only. Otherwise, the postfix(1) command runs in multi-instance +mode and invokes the multi-instance manager specified with the +multi_instance_wrapper parameter. The multi-instance manager in +turn executes postfix(1) commands for the default instance and for +all Postfix instances in $multi_instance_directories.

        + +

        Currently, this parameter setting is ignored except for the +default main.cf file.

        This feature is available in Postfix 2.6 and later.

        @@ -12175,3 +12180,44 @@ for opportunities to reject mail, and defers the client request only if it would otherwise be accepted.

        This feature is available in Postfix 2.6 and later.

        + +%PARAM postmulti_start_commands start + +

        The postfix(1) commands that the postmulti(1) instance manager treats +as "start" commands. For these commands, disabled instances are "checked" +rather than "started", and failure to "start" a member instance of an +instance group will abort the start-up of later instances.

        + +

        This feature is available in Postfix 2.6 and later.

        + +%PARAM postmulti_stop_commands see "postconf -d" output + +

        The postfix(1) commands that the postmulti(1) instance manager treats +as "stop" commands. For these commands, disabled instances are skipped, +and enabled instances are processed in reverse order.

        + +

        This feature is available in Postfix 2.6 and later.

        + +%PARAM postmulti_control_commands reload flush + +

        The postfix(1) commands that the postmulti(1) instance manager +treats as "control" commands, that operate on running instances. For +these commands, disabled instances are skipped.

        + +

        This feature is available in Postfix 2.6 and later.

        + +%PARAM lmtp_assume_final no + +

        When an LMTP server announces no DSN support, assume that the +server performs final delivery, and send "delivered" delivery status +notifications instead of "relayed". The default setting is backwards +compatible to avoid the infinetisimal possibility of breaking +existing LMTP-based content filters.

        + +%PARAM always_add_missing_headers no + +

        Always add (Resent-) From:, To:, Date: or Message-ID: headers +when not present. Postfix 2.6 and later add these headers only +when clients match the local_header_rewrite_clients parameter +setting. Earlier Postfix versions always add these headers; this +may break DKIM signatures that cover non-existent headers.

        diff --git a/postfix/proto/postfix-wrapper b/postfix/proto/postfix-wrapper index 982daafbb..8856448fc 100644 --- a/postfix/proto/postfix-wrapper +++ b/postfix/proto/postfix-wrapper @@ -11,32 +11,26 @@ # # This document describes how the familiar "postfix start" # etc. user interface can be used to manage one or multiple -# Postfix instances, and gives details of an API that allows -# the postfix(1) command to coordinate activities with a -# multi-instance manager program. -# -# A trivial but useful multi-instance manager implementation -# is described below, and can be found in the file -# $daemon_directory/postfix-wrapper. The latter file also -# contains instructions for setting up multiple instances. +# Postfix instances, and gives details of an API to coordinate +# activities between the postfix(1) command and a multi-instance +# manager program. # # With multi-instance support, the default Postfix instance -# is required. The location of its configuration files is -# specified by the built-in default value for the config_directory -# parameter. +# is always required. The config_directory parameter's default +# value specifies that instance's configuration file location. # GENERAL OPERATION # .ad # .fi -# Multi-instance support is backwards compatible: when there -# is only one Postfix instance, commands such as "postfix -# start" keep doing what they have always done. +# Multi-instance support is backwards compatible: when you +# run only one Postfix instance, commands such as "postfix +# start" will not change behavior at all. # -# Even after multi-instance support has been set up through -# the mechanisms discussed later, sites can continue to use -# the familiar postfix commands in boot scripts, upgrade -# procedures, and other places. +# Even with multiple Postfix instances, you can keep using +# the same postfix commands in boot scripts, upgrade procedures, +# and other places. The commands do more work, but humans are +# not forced to learn new tricks. # -# To start all applicable Postfix instances, use: +# For example, to start all Postfix instances, use: # .IP # # postfix start # .PP @@ -51,8 +45,8 @@ # MANAGING AN INDIVIDUAL POSTFIX INSTANCE # .ad # .fi -# To operate on a specific Postfix instance, specify its -# configuration directory on the postfix(1) command line: +# To manage a specific Postfix instance, specify its configuration +# directory on the postfix(1) command line: # .IP # # postfix -c \fI/path/to/config_directory command\fR # .PP @@ -61,39 +55,55 @@ # variable (the -c command-line option has higher precedence). # # When no Postfix instance information is specified, the -# postfix(1) command will operate on all applicable Postfix -# instances. -# MULTI-INSTANCE MANAGER IMPLEMENTATION +# postfix(1) command will operate on all Postfix instances. +# ENABLING POSTFIX(1) MULTI-INSTANCE MODE # .ad # .fi -# Historically, the postfix(1) command invokes the postfix-script -# file (currently installed in the daemon directory). This -# file contains the commands that start or stop Postfix, -# upgrade the configuration and so on. +# By default, the postfix(1) command operates in single-instance +# mode. In this mode the command invokes the postfix-script +# file directly (currently installed in the daemon directory). +# This file contains the commands that start or stop one +# Postfix instance, that upgrade the configuration of one +# Postfix instance, and so on. # -# When multi-instance support is turned on, the postfix(1) -# command needs to execute these commands for each applicable -# Postfix instance. This multiplication of commands is handled -# by a multi-instance manager program. +# When the postfix(1) command operates in multi-instance mode +# as discussed below, the command needs to execute start, +# stop, etc. commands for each Postfix instance. This +# multiplication of commands is handled by a multi-instance +# manager program. # -# Turning on multi-instance support goes as follows: update -# the default Postfix instance's main.cf file, and populate -# the multi_instance_directories parameter with the configuration -# directory pathnames of additional Postfix instances. -# -# With multi-instance support turned on, the postfix(1) command -# invokes a multi-instance manager command instead of the -# postfix-script file. The multi-instance manager executes -# the postfix(1) command for each applicable Postfix instance. -# The pathname of the multi-instance manager is specified in -# the default main.cf file with the multi_instance_wrapper -# parameter. +# Turning on postfix(1) multi-instance mode goes as follows: +# in the default Postfix instance's main.cf file, 1) specify +# the pathname of a multi-instance manager program with the +# multi_instance_wrapper parameter; 2) populate the +# multi_instance_directories parameter with the configuration +# directory pathnames of additional Postfix instances. For +# example: +# .IP +# .nf +# /etc/postfix/main.cf: +# multi_instance_wrapper = $daemon_directory/postfix-wrapper +# multi_instance_directories = /etc/postfix-test +# .fi +# .PP +# The $daemon_directory/postfix-wrapper file implements a +# simple manager and contains instructions for creating Postfix +# instances by hand. The postmulti(1) command provides a +# more extensive implementation including support for life-cycle +# management. # # The multi_instance_directories and other main.cf parameters # are listed below in the CONFIGURATION PARAMETERS section. # -# A useful multi-instance manager implementation can be as -# simple as: +# In multi-instance mode, the postfix(1) command invokes the +# $multi_instance_wrapper command instead of the postfix-script +# file. This multi-instance manager in turn executes the +# postfix(1) command in single-instance mode for each Postfix +# instance. +# +# To illustrate the main ideas behind multi-instance operation, +# below is an example of a simple but useful multi-instance +# manager implementation: # .IP # .nf # #!/bin/sh @@ -124,48 +134,45 @@ # # exit $err # .fi -# .PP -# A sample implementation, with instructions, can be found -# in $daemon_directory/postfix-wrapper. -# -# The postmulti(1) command implements a more sophisticated -# approach, based on a combination of C code and scripting. -# ENABLING A SPECIFIC INSTANCE FOR MULTI-INSTANCE OPERATION +# PER-INSTANCE MULTI-INSTANCE MANAGER CONTROLS # .ad # .fi # Each Postfix instance has its own main.cf file with parameters -# that control multi-instance operation. The most important -# settings are discussed here. +# that control how the multi-instance manager operates on +# that instance. This section discusses the most important +# settings. # # The setting "multi_instance_enable = yes" allows the -# multi-instance manager to start (and stop) the corresponding +# multi-instance manager to start (stop, etc.) the corresponding # Postfix instance. For safety reasons, this setting is not # the default. # -# The setting "multi_instance_enable = no" is useful for -# manual testing. With this, the multi-instance manager will -# not start the Postfix instance, and it will skip commands -# such as "stop" or "flush" that require a running Postfix -# instance. The multi-instance manager will execute commands -# such as "check", "set-permissions" or "upgrade-configuration", -# and it will replace "start" by "check" so that problems -# will be reported even when the instance is disabled. -# SHARED VERSUS NON-SHARED FILES +# The default setting "multi_instance_enable = no" is useful +# for manual testing with "postfix -c \fI/path/name\fR start" +# etc. The multi-instance manager will not start such an +# instance, and it will skip commands such as "stop" or "flush" +# that require a running Postfix instance. The multi-instance +# manager will execute commands such as "check", "set-permissions" +# or "upgrade-configuration", and it will replace "start" by +# "check" so that problems will be reported even when the +# instance is disabled. +# MAINTAINING SHARED AND NON-SHARED FILES # .ad # .fi # Some files are shared between Postfix instances, such as # executables and manpages, and some files are per-instance, -# such as the queue directory. See the NON-SHARED FILES -# section below for a list of per-instance files. +# such as configuration files, mail queue files, and data +# files. See the NON-SHARED FILES section below for a list +# of per-instance files. # # Before Postfix multi-instance support was implemented, the -# executables, manpages, etc., have always been checked or -# updated as part of the default Postfix instance. With -# multi-instance support, we simply continue to do this. +# executables, manpages, etc., have always been maintained +# as part of the default Postfix instance. # -# Specifically, Postfix instances will not check or update -# shared files when their config_directory value is listed -# with the default main.cf's multi_instance_directories +# With multi-instance support, we simply continue to do this. +# Specifically, a Postfix instance will not check or update +# shared files when that instance's config_directory value is +# listed with the default main.cf file's multi_instance_directories # parameter. # # The consequence of this approach is that the default Postfix diff --git a/postfix/src/cleanup/cleanup.c b/postfix/src/cleanup/cleanup.c index a281ed553..688b6860d 100644 --- a/postfix/src/cleanup/cleanup.c +++ b/postfix/src/cleanup/cleanup.c @@ -77,6 +77,11 @@ /* sender address (this feature is removed with Postfix version 2.2, is /* turned off by default with Postfix version 2.1, and is always turned on /* with older Postfix versions). +/* .PP +/* Available in Postfix version 2.6 and later: +/* .IP "\fBalways_add_missing_headers (no)\fR" +/* Always add (Resent-) From:, To:, Date: or Message-ID headers +/* when not present. /* BUILT-IN CONTENT FILTERING CONTROLS /* .ad /* .fi @@ -138,30 +143,30 @@ /* .IP "\fBmilter_content_timeout (300s)\fR" /* The time limit for sending message content to a Milter (mail /* filter) application, and for receiving the response. -/* .IP "\fBmilter_connect_macros (see postconf -n output)\fR" +/* .IP "\fBmilter_connect_macros (see 'postconf -d' output)\fR" /* The macros that are sent to Milter (mail filter) applications /* after completion of an SMTP connection. -/* .IP "\fBmilter_helo_macros (see postconf -n output)\fR" +/* .IP "\fBmilter_helo_macros (see 'postconf -d' output)\fR" /* The macros that are sent to Milter (mail filter) applications /* after the SMTP HELO or EHLO command. -/* .IP "\fBmilter_mail_macros (see postconf -n output)\fR" +/* .IP "\fBmilter_mail_macros (see 'postconf -d' output)\fR" /* The macros that are sent to Milter (mail filter) applications /* after the SMTP MAIL FROM command. -/* .IP "\fBmilter_rcpt_macros (see postconf -n output)\fR" +/* .IP "\fBmilter_rcpt_macros (see 'postconf -d' output)\fR" /* The macros that are sent to Milter (mail filter) applications /* after the SMTP RCPT TO command. -/* .IP "\fBmilter_data_macros (see postconf -n output)\fR" +/* .IP "\fBmilter_data_macros (see 'postconf -d' output)\fR" /* The macros that are sent to version 4 or higher Milter (mail /* filter) applications after the SMTP DATA command. -/* .IP "\fBmilter_unknown_command_macros (see postconf -n output)\fR" +/* .IP "\fBmilter_unknown_command_macros (see 'postconf -d' output)\fR" /* The macros that are sent to version 3 or higher Milter (mail /* filter) applications after an unknown SMTP command. -/* .IP "\fBmilter_end_of_data_macros (see postconf -n output)\fR" +/* .IP "\fBmilter_end_of_data_macros (see 'postconf -d' output)\fR" /* The macros that are sent to Milter (mail filter) applications /* after the message end-of-data. /* .PP /* Available in Postfix version 2.5 and later: -/* .IP "\fBmilter_end_of_header_macros (see postconf -n output)\fR" +/* .IP "\fBmilter_end_of_header_macros (see 'postconf -d' output)\fR" /* The macros that are sent to Milter (mail filter) applications /* after the end of the message header. /* MIME PROCESSING CONTROLS @@ -335,7 +340,7 @@ /* the sender. /* .IP "\fBsyslog_facility (mail)\fR" /* The syslog facility of Postfix logging. -/* .IP "\fBsyslog_name (postfix)\fR" +/* .IP "\fBsyslog_name (see 'postconf -d' output)\fR" /* The mail system name that is prepended to the process name in syslog /* records, so that "smtpd" becomes, for example, "postfix/smtpd". /* .PP diff --git a/postfix/src/cleanup/cleanup_init.c b/postfix/src/cleanup/cleanup_init.c index bb3983c4d..67a27a8d9 100644 --- a/postfix/src/cleanup/cleanup_init.c +++ b/postfix/src/cleanup/cleanup_init.c @@ -162,6 +162,7 @@ char *var_milt_eod_macros; /* end-of-data macros */ char *var_milt_unk_macros; /* unknown command macros */ char *var_cleanup_milters; /* non-SMTP mail */ int var_auto_8bit_enc_hdr; /* auto-detect 8bit encoding header */ +int var_always_add_hdrs; /* always add missing headers */ CONFIG_INT_TABLE cleanup_int_table[] = { VAR_HOPCOUNT_LIMIT, DEF_HOPCOUNT_LIMIT, &var_hopcount_limit, 1, 0, @@ -177,6 +178,7 @@ CONFIG_BOOL_TABLE cleanup_bool_table[] = { VAR_ENABLE_ORCPT, DEF_ENABLE_ORCPT, &var_enable_orcpt, VAR_VERP_BOUNCE_OFF, DEF_VERP_BOUNCE_OFF, &var_verp_bounce_off, VAR_AUTO_8BIT_ENC_HDR, DEF_AUTO_8BIT_ENC_HDR, &var_auto_8bit_enc_hdr, + VAR_ALWAYS_ADD_HDRS, DEF_ALWAYS_ADD_HDRS, &var_always_add_hdrs, 0, }; @@ -318,7 +320,6 @@ void cleanup_pre_jail(char *unused_name, char **unused_argv) CANON_CLASS_HDR_RCPT, CLEANUP_CANON_FLAG_HDR_RCPT, 0, }; - static const NAME_MASK masq_class_table[] = { MASQ_CLASS_ENV_FROM, CLEANUP_MASQ_FLAG_ENV_FROM, MASQ_CLASS_ENV_RCPT, CLEANUP_MASQ_FLAG_ENV_RCPT, diff --git a/postfix/src/cleanup/cleanup_message.c b/postfix/src/cleanup/cleanup_message.c index 4d1273222..236db5e11 100644 --- a/postfix/src/cleanup/cleanup_message.c +++ b/postfix/src/cleanup/cleanup_message.c @@ -625,7 +625,8 @@ static void cleanup_header_done_callback(void *context) * the message ID matches the queue ID creation time, as long as we use * the queue ID in the message ID. */ - if ((state->headers_seen & (1 << (state->resent[0] ? + if ((state->hdr_rewrite_context || var_always_add_hdrs) + && (state->headers_seen & (1 << (state->resent[0] ? HDR_RESENT_MESSAGE_ID : HDR_MESSAGE_ID))) == 0) { tv = state->handle->ctime.tv_sec; tp = gmtime(&tv); @@ -641,8 +642,9 @@ static void cleanup_header_done_callback(void *context) * Add a missing (Resent-)Date: header. The date is in local time units, * with the GMT offset at the end. */ - if ((state->headers_seen & (1 << (state->resent[0] ? - HDR_RESENT_DATE : HDR_DATE))) == 0) { + if ((state->hdr_rewrite_context || var_always_add_hdrs) + && (state->headers_seen & (1 << (state->resent[0] ? + HDR_RESENT_DATE : HDR_DATE))) == 0) { cleanup_out_format(state, REC_TYPE_NORM, "%sDate: %s", state->resent, mail_date(state->arrival_time.tv_sec)); } @@ -650,8 +652,9 @@ static void cleanup_header_done_callback(void *context) /* * Add a missing (Resent-)From: header. */ - if ((state->headers_seen & (1 << (state->resent[0] ? - HDR_RESENT_FROM : HDR_FROM))) == 0) { + if ((state->hdr_rewrite_context || var_always_add_hdrs) + && (state->headers_seen & (1 << (state->resent[0] ? + HDR_RESENT_FROM : HDR_FROM))) == 0) { quote_822_local(state->temp1, *state->sender ? state->sender : MAIL_ADDR_MAIL_DAEMON); vstring_sprintf(state->temp2, "%sFrom: %s", @@ -695,7 +698,8 @@ static void cleanup_header_done_callback(void *context) #define VISIBLE_RCPT ((1 << HDR_TO) | (1 << HDR_RESENT_TO) \ | (1 << HDR_CC) | (1 << HDR_RESENT_CC)) - if ((state->headers_seen & VISIBLE_RCPT) == 0 && *var_rcpt_witheld) { + if ((state->hdr_rewrite_context || var_always_add_hdrs) + && (state->headers_seen & VISIBLE_RCPT) == 0 && *var_rcpt_witheld) { if (!is_header(var_rcpt_witheld)) { msg_warn("bad %s header text \"%s\" -- " "need \"headername: headervalue\"", diff --git a/postfix/src/dns/test_dns_lookup.c b/postfix/src/dns/test_dns_lookup.c index ac61287ac..b34edaeac 100644 --- a/postfix/src/dns/test_dns_lookup.c +++ b/postfix/src/dns/test_dns_lookup.c @@ -80,7 +80,7 @@ static void print_rr(DNS_RR *rr) int main(int argc, char **argv) { ARGV *types_argv; - int *types; + unsigned *types; char *name; VSTRING *fqdn = vstring_alloc(100); VSTRING *why = vstring_alloc(100); diff --git a/postfix/src/global/dict_ldap.c b/postfix/src/global/dict_ldap.c index 4877e863d..935f194ab 100644 --- a/postfix/src/global/dict_ldap.c +++ b/postfix/src/global/dict_ldap.c @@ -485,10 +485,19 @@ static int dict_ldap_set_tls_options(DICT_LDAP *dict_ldap) const char *myname = "dict_ldap_set_tls_options"; int rc; +#ifdef LDAP_OPT_X_TLS_NEWCTX + int am_server = 0; + LDAP *ld = dict_ldap->ld; + +#else + LDAP *ld = 0; + +#endif + if (dict_ldap->start_tls || dict_ldap->ldap_ssl) { if (*dict_ldap->tls_random_file) { - if ((rc = ldap_set_option(NULL, LDAP_OPT_X_TLS_RANDOM_FILE, - dict_ldap->tls_random_file)) != LDAP_SUCCESS) { + if ((rc = ldap_set_option(ld, LDAP_OPT_X_TLS_RANDOM_FILE, + dict_ldap->tls_random_file)) != LDAP_SUCCESS) { msg_warn("%s: Unable to set tls_random_file to %s: %d: %s", myname, dict_ldap->tls_random_file, rc, ldap_err2string(rc)); @@ -496,8 +505,8 @@ static int dict_ldap_set_tls_options(DICT_LDAP *dict_ldap) } } if (*dict_ldap->tls_ca_cert_file) { - if ((rc = ldap_set_option(NULL, LDAP_OPT_X_TLS_CACERTFILE, - dict_ldap->tls_ca_cert_file)) != LDAP_SUCCESS) { + if ((rc = ldap_set_option(ld, LDAP_OPT_X_TLS_CACERTFILE, + dict_ldap->tls_ca_cert_file)) != LDAP_SUCCESS) { msg_warn("%s: Unable to set tls_ca_cert_file to %s: %d: %s", myname, dict_ldap->tls_ca_cert_file, rc, ldap_err2string(rc)); @@ -505,8 +514,8 @@ static int dict_ldap_set_tls_options(DICT_LDAP *dict_ldap) } } if (*dict_ldap->tls_ca_cert_dir) { - if ((rc = ldap_set_option(NULL, LDAP_OPT_X_TLS_CACERTDIR, - dict_ldap->tls_ca_cert_dir)) != LDAP_SUCCESS) { + if ((rc = ldap_set_option(ld, LDAP_OPT_X_TLS_CACERTDIR, + dict_ldap->tls_ca_cert_dir)) != LDAP_SUCCESS) { msg_warn("%s: Unable to set tls_ca_cert_dir to %s: %d: %s", myname, dict_ldap->tls_ca_cert_dir, rc, ldap_err2string(rc)); @@ -514,8 +523,8 @@ static int dict_ldap_set_tls_options(DICT_LDAP *dict_ldap) } } if (*dict_ldap->tls_cert) { - if ((rc = ldap_set_option(NULL, LDAP_OPT_X_TLS_CERTFILE, - dict_ldap->tls_cert)) != LDAP_SUCCESS) { + if ((rc = ldap_set_option(ld, LDAP_OPT_X_TLS_CERTFILE, + dict_ldap->tls_cert)) != LDAP_SUCCESS) { msg_warn("%s: Unable to set tls_cert to %s: %d: %s", myname, dict_ldap->tls_cert, rc, ldap_err2string(rc)); @@ -523,7 +532,7 @@ static int dict_ldap_set_tls_options(DICT_LDAP *dict_ldap) } } if (*dict_ldap->tls_key) { - if ((rc = ldap_set_option(NULL, LDAP_OPT_X_TLS_KEYFILE, + if ((rc = ldap_set_option(ld, LDAP_OPT_X_TLS_KEYFILE, dict_ldap->tls_key)) != LDAP_SUCCESS) { msg_warn("%s: Unable to set tls_key to %s: %d: %s", myname, dict_ldap->tls_key, @@ -532,21 +541,29 @@ static int dict_ldap_set_tls_options(DICT_LDAP *dict_ldap) } } if (*dict_ldap->tls_cipher_suite) { - if ((rc = ldap_set_option(NULL, LDAP_OPT_X_TLS_CIPHER_SUITE, - dict_ldap->tls_cipher_suite)) != LDAP_SUCCESS) { + if ((rc = ldap_set_option(ld, LDAP_OPT_X_TLS_CIPHER_SUITE, + dict_ldap->tls_cipher_suite)) != LDAP_SUCCESS) { msg_warn("%s: Unable to set tls_cipher_suite to %s: %d: %s", myname, dict_ldap->tls_cipher_suite, rc, ldap_err2string(rc)); return (-1); } } - if ((rc = ldap_set_option(NULL, LDAP_OPT_X_TLS_REQUIRE_CERT, - &(dict_ldap->tls_require_cert))) != LDAP_SUCCESS) { + if ((rc = ldap_set_option(ld, LDAP_OPT_X_TLS_REQUIRE_CERT, + &(dict_ldap->tls_require_cert))) != LDAP_SUCCESS) { msg_warn("%s: Unable to set tls_require_cert to %d: %d: %s", myname, dict_ldap->tls_require_cert, rc, ldap_err2string(rc)); return (-1); } +#ifdef LDAP_OPT_X_TLS_NEWCTX + if ((rc = ldap_set_option(ld, LDAP_OPT_X_TLS_NEWCTX, &am_server)) + != LDAP_SUCCESS) { + msg_warn("%s: Unable to allocate new TLS context %d: %s", + myname, rc, ldap_err2string(rc)); + return (-1); + } +#endif } return (0); } @@ -592,10 +609,6 @@ static int dict_ldap_connect(DICT_LDAP *dict_ldap) #ifdef LDAP_OPT_NETWORK_TIMEOUT #ifdef LDAP_API_FEATURE_X_OPENLDAP - if (dict_ldap_set_tls_options(dict_ldap) != 0) { - dict_errno = DICT_ERR_RETRY; - return (-1); - } ldap_initialize(&(dict_ldap->ld), dict_ldap->server_host); #else dict_ldap->ld = ldap_init(dict_ldap->server_host, @@ -700,6 +713,8 @@ static int dict_ldap_connect(DICT_LDAP *dict_ldap) #endif #ifdef LDAP_API_FEATURE_X_OPENLDAP + if (dict_ldap_set_tls_options(dict_ldap) != 0) + DICT_LDAP_UNBIND_RETURN(dict_ldap->ld, DICT_ERR_RETRY, -1); if (dict_ldap->start_tls) { if ((saved_alarm = signal(SIGALRM, dict_ldap_timeout)) == SIG_ERR) { msg_warn("%s: Error setting signal handler for STARTTLS timeout: %m", diff --git a/postfix/src/global/mail_conf_nint.c b/postfix/src/global/mail_conf_nint.c index 8accd9f16..6884859ee 100644 --- a/postfix/src/global/mail_conf_nint.c +++ b/postfix/src/global/mail_conf_nint.c @@ -43,27 +43,27 @@ /* for integer values. The default value can be a macro /* expression ($name, ${name?value} and ${name:value}). /* -/* get_mail_conf_int() looks up the named entry in the global +/* get_mail_conf_nint() looks up the named entry in the global /* configuration dictionary. The default value is returned /* when no value was found. /* \fImin\fR is zero or specifies a lower limit on the integer /* value or string length; \fImax\fR is zero or specifies an /* upper limit on the integer value or string length. /* -/* get_mail_conf_int_fn() is similar but specifies a function that +/* get_mail_conf_nint_fn() is similar but specifies a function that /* provides the default value. The function is called only /* when the default value is needed. /* -/* set_mail_conf_int() updates the named entry in the global +/* set_mail_conf_nint() updates the named entry in the global /* configuration dictionary. This has no effect on values that /* have been looked up earlier via the get_mail_conf_XXX() routines. /* -/* get_mail_conf_int_table() and get_mail_conf_int_fn_table() initialize +/* get_mail_conf_nint_table() and get_mail_conf_nint_fn_table() initialize /* lists of variables, as directed by their table arguments. A table /* must be terminated by a null entry. /* -/* get_mail_conf_int2() concatenates the two names and is otherwise -/* identical to get_mail_conf_int(). +/* get_mail_conf_nint2() concatenates the two names and is otherwise +/* identical to get_mail_conf_nint(). /* DIAGNOSTICS /* Fatal errors: malformed numerical value. /* SEE ALSO diff --git a/postfix/src/global/mail_params.h b/postfix/src/global/mail_params.h index d952a7ce5..516d91735 100644 --- a/postfix/src/global/mail_params.h +++ b/postfix/src/global/mail_params.h @@ -332,6 +332,14 @@ extern char *var_always_bcc; #define DEF_RCPT_WITHELD "To: undisclosed-recipients:;" extern char *var_rcpt_witheld; + /* + * Add missing headers. Postfix 2.6 no longer adds headers to remote mail by + * default. + */ +#define VAR_ALWAYS_ADD_HDRS "always_add_missing_headers" +#define DEF_ALWAYS_ADD_HDRS 0 +extern bool var_always_add_hdrs; + /* * Standards violation: allow/permit RFC 822-style addresses in SMTP * commands. @@ -1105,7 +1113,7 @@ extern int var_smtpd_tmout; extern int var_smtpd_starttls_tmout; #define VAR_SMTPD_RCPT_LIMIT "smtpd_recipient_limit" -#define DEF_SMTPD_RCPT_LIMIT "1000" +#define DEF_SMTPD_RCPT_LIMIT 1000 extern int var_smtpd_rcpt_limit; #define VAR_SMTPD_SOFT_ERLIM "smtpd_soft_error_limit" @@ -1660,6 +1668,10 @@ extern int var_smtp_sasl_auth_cache_time; #define DEF_LMTP_TCP_PORT "24" extern char *var_lmtp_tcp_port; +#define VAR_LMTP_ASSUME_FINAL "lmtp_assume_final" +#define DEF_LMTP_ASSUME_FINAL 0 +extern bool var_lmtp_assume_final; + #define VAR_LMTP_CACHE_CONN "lmtp_cache_connection" #define DEF_LMTP_CACHE_CONN 1 extern bool var_lmtp_cache_conn; @@ -3019,12 +3031,12 @@ extern char *var_smtp_body_chks; * Scheduler concurrency feedback algorithms. */ #define VAR_CONC_POS_FDBACK "default_destination_concurrency_positive_feedback" -#define _CONC_POS_FDBACK "_concurrency_positive_feedback" +#define _CONC_POS_FDBACK "_destination_concurrency_positive_feedback" #define DEF_CONC_POS_FDBACK "1" extern char *var_conc_pos_feedback; #define VAR_CONC_NEG_FDBACK "default_destination_concurrency_negative_feedback" -#define _CONC_NEG_FDBACK "_concurrency_negative_feedback" +#define _CONC_NEG_FDBACK "_destination_concurrency_negative_feedback" #define DEF_CONC_NEG_FDBACK "1" extern char *var_conc_neg_feedback; @@ -3032,7 +3044,7 @@ extern char *var_conc_neg_feedback; #define CONC_FDBACK_NAME_SQRT_WIN "sqrt_concurrency" #define VAR_CONC_COHORT_LIM "default_destination_concurrency_failed_cohort_limit" -#define _CONC_COHORT_LIM "_concurrency_failed_cohort_limit" +#define _CONC_COHORT_LIM "_destination_concurrency_failed_cohort_limit" #define DEF_CONC_COHORT_LIM 1 extern int var_conc_cohort_limit; @@ -3090,6 +3102,21 @@ extern char *var_multi_group; #define DEF_MULTI_ENABLE 0 extern bool var_multi_enable; + /* + * postmulti(1) instance manager + */ +#define VAR_MULTI_START_CMDS "postmulti_start_commands" +#define DEF_MULTI_START_CMDS "start" +extern char *var_multi_start_cmds; + +#define VAR_MULTI_STOP_CMDS "postmulti_stop_commands" +#define DEF_MULTI_STOP_CMDS "stop abort drain quick-stop" +extern char *var_multi_stop_cmds; + +#define VAR_MULTI_CNTRL_CMDS "postmulti_control_commands" +#define DEF_MULTI_CNTRL_CMDS "reload flush" +extern char *var_multi_cntrl_cmds; + /* LICENSE /* .ad /* .fi diff --git a/postfix/src/global/mail_version.h b/postfix/src/global/mail_version.h index dfad21eee..00e1c4011 100644 --- a/postfix/src/global/mail_version.h +++ b/postfix/src/global/mail_version.h @@ -20,7 +20,7 @@ * Patches change both the patchlevel and the release date. Snapshots have no * patchlevel; they change the release date only. */ -#define MAIL_RELEASE_DATE "20090212" +#define MAIL_RELEASE_DATE "20090404" #define MAIL_VERSION_NUMBER "2.6" #ifdef SNAPSHOT diff --git a/postfix/src/oqmgr/qmgr_transport.c b/postfix/src/oqmgr/qmgr_transport.c index 8006873ec..b117564e1 100644 --- a/postfix/src/oqmgr/qmgr_transport.c +++ b/postfix/src/oqmgr/qmgr_transport.c @@ -286,6 +286,8 @@ QMGR_TRANSPORT *qmgr_transport_select(void) continue; need = xport->pending + 1; for (queue = xport->queue_list.next; queue; queue = queue->peers.next) { + if (QMGR_QUEUE_READY(queue) == 0) + continue; if ((need -= MIN5af51743e4eef(queue->window - queue->busy_refcount, queue->todo_refcount)) <= 0) { QMGR_LIST_ROTATE(qmgr_transport_list, xport); diff --git a/postfix/src/postfix/postfix.c b/postfix/src/postfix/postfix.c index 3747e7800..2cdccebe3 100644 --- a/postfix/src/postfix/postfix.c +++ b/postfix/src/postfix/postfix.c @@ -520,7 +520,8 @@ int main(int argc, char **argv) /* * Run the management script. */ - if (force_single_instance || *var_multi_conf_dirs == 0) { + if (force_single_instance + || argv_split(var_multi_conf_dirs, "\t\r\n, ")->argc == 0) { script = concatenate(var_daemon_dir, "/postfix-script", (char *) 0); if (optind < 1) msg_panic("bad optind value"); diff --git a/postfix/src/postmulti/.indent.pro b/postfix/src/postmulti/.indent.pro new file mode 120000 index 000000000..5c837eca6 --- /dev/null +++ b/postfix/src/postmulti/.indent.pro @@ -0,0 +1 @@ +../../.indent.pro \ No newline at end of file diff --git a/postfix/src/postmulti/Makefile.in b/postfix/src/postmulti/Makefile.in new file mode 100644 index 000000000..b071ec942 --- /dev/null +++ b/postfix/src/postmulti/Makefile.in @@ -0,0 +1,83 @@ +SHELL = /bin/sh +SRCS = postmulti.c +OBJS = postmulti.o +HDRS = +TESTSRC = +DEFS = -I. -I$(INC_DIR) -D$(SYSTYPE) +CFLAGS = $(DEBUG) $(OPT) $(DEFS) +FILES = Makefile $(SRCS) $(HDRS) +INC_DIR = ../../include +TESTPROG= +PROG = postmulti +LIBS = ../../lib/libglobal.a ../../lib/libutil.a + +.c.o:; $(CC) $(CFLAGS) -c $*.c + +$(PROG): $(OBJS) $(LIBS) + $(CC) $(CFLAGS) -o $@ $(OBJS) $(LIBS) $(SYSLIBS) + +$(OBJS): ../../conf/makedefs.out + +Makefile: Makefile.in + cat ../../conf/makedefs.out $? >$@ + +test: $(TESTPROG) + +tests: + +root_tests: + +update: ../../bin/$(PROG) + +../../bin/$(PROG): $(PROG) + cp $(PROG) ../../bin + +printfck: $(OBJS) $(PROG) + rm -rf printfck + mkdir printfck + sed '1,/^# do not edit/!d' Makefile >printfck/Makefile + set -e; for i in *.c; do printfck -f .printfck $$i >printfck/$$i; done + cd printfck; make "INC_DIR=../../../include" `cd ..; ls *.o` + +shar: + @shar $(FILES) + +lint: + lint $(SRCS) + +clean: + rm -f *.o *core $(PROG) $(TESTPROG) junk + rm -rf printfck + +tidy: clean + +depend: $(MAKES) + (sed '1,/^# do not edit/!d' Makefile.in; \ + set -e; for i in [a-z][a-z0-9]*.c; do \ + $(CC) -E $(DEFS) $(INCL) $$i | grep -v '[<>]' | sed -n -e '/^# *1 *"\([^"]*\)".*/{' \ + -e 's//'`echo $$i|sed 's/c$$/o/'`': \1/' \ + -e 's/o: \.\//o: /' -e p -e '}' ; \ + done | sort -u) | grep -v '[.][o][:][ ][/]' >$$$$ && mv $$$$ Makefile.in + @$(EXPORT) make -f Makefile.in Makefile 1>&2 + +# do not edit below this line - it is generated by 'make depend' +postmulti.o: ../../include/argv.h +postmulti.o: ../../include/clean_env.h +postmulti.o: ../../include/htable.h +postmulti.o: ../../include/mail_conf.h +postmulti.o: ../../include/mail_params.h +postmulti.o: ../../include/mail_version.h +postmulti.o: ../../include/msg.h +postmulti.o: ../../include/msg_syslog.h +postmulti.o: ../../include/msg_vstream.h +postmulti.o: ../../include/mymalloc.h +postmulti.o: ../../include/name_code.h +postmulti.o: ../../include/ring.h +postmulti.o: ../../include/safe.h +postmulti.o: ../../include/stringops.h +postmulti.o: ../../include/sys_defs.h +postmulti.o: ../../include/vbuf.h +postmulti.o: ../../include/vstream.h +postmulti.o: ../../include/vstring.h +postmulti.o: ../../include/vstring_vstream.h +postmulti.o: postmulti.c diff --git a/postfix/src/postmulti/postmulti.c b/postfix/src/postmulti/postmulti.c new file mode 100644 index 000000000..353eefde7 --- /dev/null +++ b/postfix/src/postmulti/postmulti.c @@ -0,0 +1,1791 @@ +/*++ +/* NAME +/* postmulti 1 +/* SUMMARY +/* Postfix multi-instance manager +/* SYNOPSIS +/* .fi +/* \fBpostmulti\fR \fB-l\fR [\fB-aRv\fR] [\fB-g \fIgroup\fR] +/* [\fB-i \fIname\fR] +/* +/* \fBpostmulti\fR \fB-p\fR [\fB-av\fR] [\fB-g \fIgroup\fR] +/* [\fB-i \fIname\fR] \fIcommand...\fR +/* +/* \fBpostmulti\fR \fB-x\fR [\fB-aRv\fR] [\fB-g \fIgroup\fR] +/* [\fB-i \fIname\fR] \fIcommand...\fR +/* +/* \fBpostmulti\fR \fB-e init\fR [\fB-v\fR] +/* +/* \fBpostmulti\fR \fB-e create\fR [\fB-av\fR] +/* [\fB-g \fIgroup\fR] [\fB-i \fIname\fR] [\fB-G \fIgroup\fR] +/* [\fB-I \fIname\fR] [\fIparam=value\fR ...] +/* +/* \fBpostmulti\fR \fB-e import\fR [\fB-av\fR] +/* [\fB-g \fIgroup\fR] [\fB-i \fIname\fR] [\fB-G \fIgroup\fR] +/* [\fB-I \fIname\fR] [\fBconfig_directory=\fI/path\fR] +/* +/* \fBpostmulti\fR \fB-e destroy\fR [\fB-v\fR] \fB-i \fIname\fR +/* +/* \fBpostmulti\fR \fB-e deport\fR [\fB-v\fR] \fB-i \fIname\fR +/* +/* \fBpostmulti\fR \fB-e enable\fR [\fB-v\fR] \fB-i \fIname\fR +/* +/* \fBpostmulti\fR \fB-e disable\fR [\fB-v\fR] \fB-i \fIname\fR +/* +/* \fBpostmulti\fR \fB-e assign\fR [\fB-v\fR] \fB-i \fIname\fR +/* [\fB-I \fIname\fR] [-G \fIgroup\fR] +/* DESCRIPTION +/* The \fBpostmulti\fR(1) command allows a Postfix administrator +/* to manage multiple Postfix instances on a single host. +/* +/* \fBpostmulti\fR(1) implements two fundamental modes of +/* operation. In \fBiterator\fR mode, it executes the same +/* command for multiple Postfix instances. In \fBlife-cycle +/* management\fR mode, it adds or deletes one instance, or +/* changes the multi-instance status of one instance. +/* +/* Each mode of operation has its own command syntax. For this +/* reason, each mode is documented in separate sections below. +/* BACKGROUND +/* .ad +/* .fi +/* A multi-instance configuration consists of one primary +/* Postfix instance, and one or more secondary instances whose +/* configuration directory pathnames are recorded in the primary +/* instance's main.cf file. Postfix instances share program +/* files and documentation, but have their own configuration, +/* queue and data directories. +/* +/* Currently, only the default Postfix instance can be used +/* as primary instance in a multi-instance configuration. The +/* \fBpostmulti\fR(1) command does not currently support a \fB-c\fR +/* option to select an alternative primary instance, and exits +/* with a fatal error if the \fBMAIL_CONFIG\fR environment +/* variable is set to a non-default configuration directory. +/* +/* See the MULTI_INSTANCE_README tutorial for a more detailed +/* discussion of multi-instance management with \fBpostmulti\fR(1). +/* ITERATOR MODE +/* .ad +/* .fi +/* In iterator mode, \fBpostmulti\fR performs the same operation +/* on all Postfix instances in turn. +/* +/* If multi-instance support is not enabled, the requested +/* command is performed just for the primary instance. +/* .PP +/* Iterator mode implements the following command options: +/* .SH "Instance selection" +/* .IP \fB-a\fR +/* Perform the operation on all instances. This is the default. +/* .IP "\fB-g \fIgroup\fR" +/* Perform the operation only for members of the named \fIgroup\fR. +/* .IP "\fB-i \fIname\fR" +/* Perform the operation only for the instance with the specified +/* \fIname\fR. You can specify either the instance name +/* or the absolute pathname of the instance's configuration +/* directory. Specify "-" to select the primary Postfix instance. +/* .IP \fB-R\fR +/* Reverse the iteration order. This may be appropriate when +/* updating a multi-instance system, where "sink" instances +/* are started before "source" instances. +/* .sp +/* This option cannot be used with \fB-p\fR. +/* .SH "List mode" +/* .IP \fB-l\fR +/* List Postfix instances with their instance name, instance +/* group name, enable/disable status and configuration directory. +/* .SH "Postfix-wrapper mode" +/* .IP \fB-p\fR +/* Invoke \fBpostfix(1)\fR to execute the specified \fIcommand\fR. +/* This option implements the \fBpostfix-wrapper\fR(5) interface. +/* .RS +/* .IP \(bu +/* With "start"-like commands, "postfix check" is executed for +/* instances that are not enabled. The full list of commands +/* is specified with the postmulti_start_commands parameter. +/* .IP \(bu +/* With "stop"-like commands, the iteration order is reversed, +/* and disabled instances are skipped. The full list of commands +/* is specified with the postmulti_stop_commands parameter. +/* .IP \(bu +/* With "reload" and other commands that require a started +/* instance, disabled instances are skipped. The full list of +/* commands is specified with the postmulti_control_commands +/* parameter. +/* .IP \(bu +/* With "status" and other commands that don't require a started +/* instance, the command is executed for all instances. +/* .RE +/* .IP +/* The \fB-p\fR option can also be used interactively to +/* start/stop/etc. a named instance or instance group. For +/* example, to start just the instances in the group "msa", +/* invoke \fBpostmulti\fR(1) as follows: +/* .RS +/* .IP +/* # postmulti -g msa -p start +/* .RE +/* .SH "Command mode" +/* .IP \fB-x\fR +/* Execute the specified \fIcommand\fR for all Postfix instances. +/* The command runs with appropriate environment settings for +/* MAIL_CONFIG, command_directory, daemon_directory, +/* config_directory, queue_directory, data_directory, +/* multi_instance_name, multi_instance_group and +/* multi_instance_enable. +/* .SH "Other options" +/* .IP \fB-v\fR +/* Enable verbose logging for debugging purposes. Multiple +/* \fB-v\fR options make the software increasingly verbose. +/* LIFE-CYCLE MANAGEMENT MODE +/* .ad +/* .fi +/* With the \fB-e\fR option \fBpostmulti\fR(1) can be used to +/* add or delete a Postfix instance, and to manage the +/* multi-instance status of an existing instance. +/* .PP +/* The following options are implemented: +/* .SH "Existing instance selection" +/* .IP \fB-a\fR +/* When creating or importing an instance, place the new +/* instance at the front of the secondary instance list. +/* .IP "\fB-g \fIgroup\fR" +/* When creating or importing an instance, place the new +/* instance before the first secondary instance that is a +/* member of the specified group. +/* .IP "\fB-i \fIname\fR" +/* When creating or importing an instance, place the new +/* instance before the matching secondary instance. +/* .sp +/* With other life-cycle operations, apply the operation to +/* the named existing instance. Specify "-" to select the +/* primary Postfix instance. +/* .SH "New or existing instance name assignment" +/* .IP "\fB-I \fIname\fR" +/* Assign the specified instance \fIname\fR to an existing +/* instance or to a newly created or imported instance. Instance +/* names other than "-" (which makes the instance "nameless") +/* must start with "postfix-". This restriction reduces the +/* likelihood of name collisions with system files. +/* .IP "\fB-G \fIgroup\fR" +/* Assign the specified \fIgroup\fR name to an existing instance +/* or to a newly created or imported instance. +/* .SH "Instance creation/deletion/status change" +/* .IP "\fB-e \fIaction\fR" +/* "Edit" managed instances. The following actions are supported: +/* .RS +/* .IP \fBinit\fR +/* This command is required before \fBpostmulti\fR(1) can be +/* used to manage Postfix instances. The "postmulti -e init" +/* command updates the primary instance's main.cf file by +/* setting: +/* .RS +/* .IP +/* .nf +/* multi_instance_wrapper = +/* ${command_directory}/postmulti -p -- +/* multi_instance_enable = yes +/* .fi +/* .RE +/* .IP +/* You can set these by other means if you prefer. +/* .IP \fBcreate\fR +/* Create a new Postfix instance and add it to the +/* multi_instance_directories parameter of the primary instance. +/* The "\fB-I \fIname\fR" option is recommended to give the +/* instance a short name that is used to construct default +/* values for the private directories of the new instance. The +/* "\fB-G \fIgroup\fR" option may be specified to assign the +/* instance to a group, otherwise, the new instance is not a +/* member of any groups. +/* .sp +/* The new instance main.cf is the stock main.cf with the +/* parameters that specify the locations of shared files cloned +/* from the primary instance. For "nameless" instances, you +/* should manually adjust "syslog_name" to yield a unique +/* "logtag" starting with "postfix-" that will uniquely identify +/* the instance in the mail logs. It is simpler to assign the +/* instance a short name with the "\fB-I \fIname\fR" option. +/* .sp +/* Optional "name=value" arguments specify the instance +/* config_directory, queue_directory and data_directory. +/* For example: +/* .RS +/* .IP +/* .nf +/* # postmulti -I postfix-mumble \e +/* -G mygroup -e create \e +/* config_directory=/my/config/dir \e +/* queue_directory=/my/queue/dir \e +/* data_directory=/my/data/dir +/* .fi +/* .RE +/* .IP +/* If any of these pathnames is not supplied, the program +/* attempts to generate the pathname by taking the corresponding +/* primary instance pathname, and by replacing the last pathname +/* component by the value of the \fB-I\fR option. +/* .sp +/* If the instance configuration directory already exists, and +/* contains both a main.cf and master.cf file, \fBcreate\fR +/* will "import" the instance as-is. For existing instances, +/* \fBcreate\fR and \fBimport\fR are identical. +/* .IP \fBimport\fR +/* Import an existing instance into the list of instances +/* managed by the \fBpostmulti\fR(1) multi-instance manager. +/* This adds the instance to the multi_instance_directories +/* list of the primary instance. If the "\fB-I \fIname\fR" +/* option is provided it specifies the new name for the instance +/* and is used to define a default location for the instance +/* configuration directory (as with \fBcreate\fR above). The +/* "\fB-G \fIgroup\fR" option may be used to assign the instance +/* to a group. Add a "\fBconfig_directory=\fI/path\fR" argument +/* to override a default pathname based on "\fB-I \fIname\fR". +/* .IP \fBdestroy\fR +/* Destroy a secondary Postfix instance. To be a candidate for +/* destruction an instance must be disabled, stopped and its +/* queue must not contain any messages. Attempts to destroy +/* the primary Postfix instance trigger a fatal error, without +/* destroying the instance. +/* .sp +/* The instance is removed from the primary instance main.cf +/* file's alternate_config_directories parameter and its data, +/* queue and configuration directories are cleaned of files +/* and directories created by the Postfix system. The main.cf +/* and master.cf files are removed from the configuration +/* directory even if they have been modified since initial +/* creation. Finally, the instance is "deported" from the list +/* of managed instances. +/* .sp +/* If other files are present in instance private directories, +/* the directories may not be fully removed, a warning is +/* logged to alert the administrator. It is expected that an +/* instance built using "fresh" directories via the \fBcreate\fR +/* action will be fully removed by the \fBdestroy\fR action +/* (if first disabled). If the instance configuration and queue +/* directories are populated with additional files (access and +/* rewriting tables, chroot jail content, etc.) the instance +/* directories will not be fully removed. +/* .sp +/* The \fBdestroy\fR action triggers potentially dangerous +/* file removal operations. Make sure the instance's data, +/* queue and configuration directories are set correctly and +/* do not contain any valuable files. +/* .IP \fBdeport\fR +/* Deport a secondary instance from the list of managed +/* instances. This deletes the instance configuration directory +/* from the primary instance's multi_instance_directories list, +/* but does not remove any files or directories. +/* .IP \fBassign\fR +/* Assign a new instance name or a new group name to the +/* selected instance. Use "\fB-G -\fR" to specify "no group" +/* and "\fB-I -\fR" to specify "no name". If you choose to +/* make an instance "nameless", set a suitable syslog_name in +/* the corresponding main.cf file. +/* .IP \fBenable\fR +/* Mark the selected instance as enabled. This just sets the +/* multi_instance_enable parameter to "yes" in the instance's +/* main.cf file. +/* .IP \fBdisable\fR +/* Mark the selected instance as disabled. This means that +/* the instance will not be started etc. with "postfix start", +/* "postmulti -p start" and so on. The instance can still be +/* started etc. with "postfix -c config-directory start". +/* .SH "Other options" +/* .IP \fB-v\fR +/* Enable verbose logging for debugging purposes. Multiple +/* \fB-v\fR options make the software increasingly verbose. +/* .RE +/* ENVIRONMENT +/* .ad +/* .fi +/* The \fBpostmulti\fR(1) command exports the following environment +/* variables before executing the requested \fIcommand\fR for a given +/* instance: +/* .IP \fBMAIL_VERBOSE\fR +/* This is set when the -v command-line option is present. +/* .IP \fBMAIL_CONFIG\fR +/* The location of the configuration directory of the instance. +/* CONFIGURATION PARAMETERS +/* .ad +/* .fi +/* .IP "\fBconfig_directory (see 'postconf -d' output)\fR" +/* The default location of the Postfix main.cf and master.cf +/* configuration files. +/* .IP "\fBdaemon_directory (see 'postconf -d' output)\fR" +/* The directory with Postfix support programs and daemon programs. +/* .IP "\fBimport_environment (see 'postconf -d' output)\fR" +/* The list of environment parameters that a Postfix process will +/* import from a non-Postfix parent process. +/* .IP "\fBmulti_instance_directories (empty)\fR" +/* An optional list of non-default Postfix configuration directories; +/* these directories belong to additional Postfix instances that share +/* the Postfix executable files and documentation with the default +/* Postfix instance, and that are started, stopped, etc., together +/* with the default Postfix instance. +/* .IP "\fBmulti_instance_group (empty)\fR" +/* The optional instance group name of this Postfix instance. +/* .IP "\fBmulti_instance_name (empty)\fR" +/* The optional instance name of this Postfix instance. +/* .IP "\fBmulti_instance_enable (no)\fR" +/* Allow this Postfix instance to be started, stopped, etc., by a +/* multi-instance manager. +/* .IP "\fBpostmulti_start_commands (start)\fR" +/* The \fBpostfix\fR(1) commands that the \fBpostmulti\fR(1) instance manager treats +/* as "start" commands. +/* .IP "\fBpostmulti_stop_commands (see 'postconf -d' output)\fR" +/* The \fBpostfix\fR(1) commands that the \fBpostmulti\fR(1) instance manager treats +/* as "stop" commands. +/* .IP "\fBpostmulti_control_commands (reload flush)\fR" +/* The \fBpostfix\fR(1) commands that the \fBpostmulti\fR(1) instance manager +/* treats as "control" commands, that operate on running instances. +/* .IP "\fBsyslog_facility (mail)\fR" +/* The syslog facility of Postfix logging. +/* .IP "\fBsyslog_name (see 'postconf -d' output)\fR" +/* The mail system name that is prepended to the process name in syslog +/* records, so that "smtpd" becomes, for example, "postfix/smtpd". +/* FILES +/* $daemon_directory/main.cf, stock configuration file +/* $daemon_directory/master.cf, stock configuration file +/* $daemon_directory/postmulti-script, life-cycle helper program +/* SEE ALSO +/* postfix(1), Postfix control program +/* postfix-wrapper(5), Postfix multi-instance API +/* README FILES +/* Use "\fBpostconf readme_directory\fR" or "\fBpostconf +/* html_directory\fR" to locate this information. +/* MULTI_INSTANCE_README, Postfix multi-instance management +/* HISTORY +/* .ad +/* .fi +/* The \fBpostmulti\fR(1) command was introduced with Postfix +/* version 2.6. +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Victor Duchovni +/* Morgan Stanley +/* +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/*--*/ + +/* System library. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef USE_PATHS_H +#include +#endif +#include + +/* Utility library. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Global library. */ + +#include +#include +#include + +/* Application-specific. */ + + /* + * Configuration parameters, specific to postmulti(1). + */ +char *var_multi_start_cmds; +char *var_multi_stop_cmds; +char *var_multi_cntrl_cmds; + + /* + * Shared directory pathnames. + */ +typedef struct { + const char *param_name; + char **param_value; +} SHARED_PATH; + +static SHARED_PATH shared_dir_table[] = { + VAR_COMMAND_DIR, &var_command_dir, + VAR_DAEMON_DIR, &var_daemon_dir, + 0, +}; + + /* + * Actions. + */ +#define ITER_CMD_POSTFIX (1<<0) /* postfix(1) iterator mode */ +#define ITER_CMD_LIST (1<<1) /* listing iterator mode */ +#define ITER_CMD_GENERIC (1<<2) /* generic command iterator mode */ + +#define ITER_CMD_MASK_ALL \ + (ITER_CMD_POSTFIX | ITER_CMD_LIST | ITER_CMD_GENERIC) + +#define EDIT_CMD_CREATE (1<<4) /* create new instance */ +#define EDIT_CMD_IMPORT (1<<5) /* import existing instance */ +#define EDIT_CMD_DESTROY (1<<6) /* destroy instance */ +#define EDIT_CMD_DEPORT (1<<7) /* export instance */ +#define EDIT_CMD_ENABLE (1<<8) /* enable start/stop */ +#define EDIT_CMD_DISABLE (1<<9) /* disable start/stop */ +#define EDIT_CMD_ASSIGN (1<<10) /* assign name/group */ +#define EDIT_CMD_INIT (1<<11) /* hook into main.cf */ + +#define EDIT_CMD_MASK_ADD (EDIT_CMD_CREATE | EDIT_CMD_IMPORT) +#define EDIT_CMD_MASK_DEL (EDIT_CMD_DESTROY | EDIT_CMD_DEPORT) +#define EDIT_CMD_MASK_ASSIGN (EDIT_CMD_MASK_ADD | EDIT_CMD_ASSIGN) +#define EDIT_CMD_MASK_ENB (EDIT_CMD_ENABLE | EDIT_CMD_DISABLE) +#define EDIT_CMD_MASK_ALL \ + (EDIT_CMD_MASK_ASSIGN | EDIT_CMD_MASK_DEL | EDIT_CMD_MASK_ENB | \ + EDIT_CMD_INIT) + + /* + * Edit command to number mapping, and vice versa. + */ +static NAME_CODE edit_command_table[] = { + "create", EDIT_CMD_CREATE, + "import", EDIT_CMD_IMPORT, + "destroy", EDIT_CMD_DESTROY, + "deport", EDIT_CMD_DEPORT, + "enable", EDIT_CMD_ENABLE, + "disable", EDIT_CMD_DISABLE, + "assign", EDIT_CMD_ASSIGN, + "init", EDIT_CMD_INIT, + 0, -1, +}; + +#define EDIT_CMD_CODE(str) \ + name_code(edit_command_table, NAME_CODE_FLAG_STRICT_CASE, (str)) +#define EDIT_CMD_STR(code) str_name_code(edit_command_table, (code)) + + /* + * Mandatory prefix for non-empty instance names. + */ +#ifndef NAME_PREFIX +#define NAME_PREFIX "postfix-" +#endif +#define HAS_NAME_PREFIX(name) \ + (strncmp((name), NAME_PREFIX, sizeof(NAME_PREFIX)-1) == 0) +#define NEED_NAME_PREFIX(name) \ + ((name) != 0 && strcmp((name), "-") != 0 && !HAS_NAME_PREFIX(name)) +#define NAME_SUFFIX(name) ((name) + sizeof(NAME_PREFIX) - 1) + + /* + * In-core instance structure. Only private information is kept here. + */ +typedef struct instance { + RING ring; /* linkage. */ + char *config_dir; /* private */ + char *queue_dir; /* private */ + char *data_dir; /* private */ + char *name; /* null or name */ + char *gname; /* null or group */ + int enabled; /* start/stop enable */ + int primary; /* special */ +} INSTANCE; + + /* + * Managed instance list (edit mode and iterator mode). + */ +static RING instance_hd[1]; /* instance list head */ + +#define RING_TO_INSTANCE(ring_ptr) RING_TO_APPL(ring_ptr, INSTANCE, ring) +#define RING_PTR_OF(x) (&((x)->ring)) + +#define FOREACH_INSTANCE(entry) \ + for ((entry) = instance_hd; \ + ((entry) = ring_succ(entry)) != instance_hd;) + +#define FOREACH_SECONDARY_INSTANCE(entry) \ + for ((entry) = ring_succ(instance_hd); \ + ((entry) = ring_succ(entry)) != instance_hd;) + +#define NEXT_ITERATOR_INSTANCE(flags, entry) \ + (((flags) & ITER_FLAG_REVERSE) ? ring_pred(entry) : ring_succ(entry)) + +#define FOREACH_ITERATOR_INSTANCE(flags, entry) \ + for ((entry) = instance_hd; \ + ((entry) = NEXT_ITERATOR_INSTANCE(flags, (entry))) != instance_hd;) + + /* + * Instance selection. One can either select all instances, select by + * instance name, or select by instance group. + */ +typedef struct { + int type; /* see below */ + char *name; /* undefined or name */ +} INST_SELECTION; + +#define INST_SEL_NONE 0 /* default: no selection */ +#define INST_SEL_ALL 1 /* select all instances */ +#define INST_SEL_NAME 2 /* select instance name */ +#define INST_SEL_GROUP 3 /* select instance group */ + + /* + * Instance name assignment. Each instance may be assigned an instance name + * (this must be globally unique within a multi-instance cluster) or an + * instance group name (this is intended to be shared). Externally, empty + * names may be represented as "-". Internally, we use "" only, to simplify + * the code. + */ +typedef struct { + char *name; /* null or assigned instance name */ + char *gname; /* null or assigned group name */ +} NAME_ASSIGNMENT; + + /* + * Iterator controls for non-edit commands. One can reverse the iteration + * order, or give special treatment to disabled instances. + */ +#define ITER_FLAG_DEFAULT 0 /* default setting */ +#define ITER_FLAG_REVERSE (1<<0) /* reverse iteration order */ +#define ITER_FLAG_CHECK_DISABLED (1<<1) /* check disabled instances */ +#define ITER_FLAG_SKIP_DISABLED (1<<2) /* skip disabled instances */ + + /* + * Environment export controls for edit commands. postmulti(1) exports only + * things that need to be updated. + */ +#define EXP_FLAG_MULTI_DIRS (1<<0) /* export multi_instance_directories */ +#define EXP_FLAG_MULTI_NAME (1<<1) /* export multi_instance_name */ +#define EXP_FLAG_MULTI_GROUP (1<<2) /* export multi_instance_group */ + + /* + * To detect conflicts, each instance name and each shared or private + * pathname is registered in one place, with its owner. Everyone must + * register their claims when they join, and will be rejected in case of + * conlict. + * + * Each claim value involves a parameter value (either a directory name or an + * instance name). Each claim owner is the config_directory pathname plus + * the parameter name. + * + * XXX: No multi.cf lock file, so this is not race-free. + */ +static HTABLE *claim_table; + +#define IS_CLAIMED_BY(name) \ + (claim_table ? htable_find(claim_table, (name)) : 0) + + /* + * Forward references. + */ +static int iterate_command(int, int, char **, INST_SELECTION *); +static int match_instance_selection(INSTANCE *, INST_SELECTION *); + + /* + * Convenience. + */ +#define INSTANCE_NAME(i) ((i)->name ? (i)->name : (i)->config_dir) +#define STR(buf) vstring_str(buf) + +/* register_claim - register claim or bust */ + +static void register_claim(const char *instance_path, const char *param_name, + const char *param_value) +{ + const char *myname = "register_claim"; + char *requestor; + const char *owner; + + /* + * Sanity checks. + */ + if (instance_path == 0 || *instance_path == 0) + msg_panic("%s: no or empty instance pathname", myname); + if (param_name == 0 || *param_name == 0) + msg_panic("%s: no or empty parameter name", myname); + if (param_value == 0) + msg_panic("%s: no parameter value", myname); + + /* + * Make a claim or report a conflict. + */ + if (claim_table == 0) + claim_table = htable_create(100); + requestor = concatenate(instance_path, ", ", param_name, (char *) 0); + if ((owner = htable_find(claim_table, param_value)) == 0) { + (void) htable_enter(claim_table, param_value, requestor); + } else if (strcmp(owner, requestor) == 0) { + myfree(requestor); + } else { + msg_fatal("instance %s, %s=%s conflicts with instance %s=%s", + instance_path, param_name, param_value, owner, param_value); + } +} + +/* claim_instance_attributes - claim multiple private instance attributes */ + +static void claim_instance_attributes(INSTANCE *ip) +{ + + /* + * Detect instance name or pathname conflicts between this instance and + * other instances. XXX: No multi.cf lock file, so this is not race-free. + */ + if (ip->name) + register_claim(ip->config_dir, VAR_MULTI_NAME, ip->name); + register_claim(ip->config_dir, VAR_CONFIG_DIR, ip->config_dir); + register_claim(ip->config_dir, VAR_QUEUE_DIR, ip->queue_dir); + register_claim(ip->config_dir, VAR_DATA_DIR, ip->data_dir); +} + +/* alloc_instance - allocate a single instance object */ + +static INSTANCE *alloc_instance(const char *config_dir) +{ + INSTANCE *ip = (INSTANCE *) mymalloc(sizeof(INSTANCE)); + + ring_init(RING_PTR_OF(ip)); + ip->config_dir = config_dir ? mystrdup(config_dir) : 0; + ip->queue_dir = 0; + ip->data_dir = 0; + ip->name = 0; + ip->gname = 0; + ip->enabled = 0; + ip->primary = 0; + + return (ip); +} + +#if 0 + +/* free_instance - free a single instance object */ + +static void free_instance(INSTANCE *ip) +{ + + /* + * If we continue after secondary main.cf file read error, we must be + * prepared for the case that some parameters may be missing. + */ + if (ip->name) + myfree(ip->name); + if (ip->gname) + myfree(ip->gname); + if (ip->config_dir) + myfree(ip->config_dir); + if (ip->queue_dir) + myfree(ip->queue_dir); + if (ip->data_dir) + myfree(ip->data_dir); + myfree((char *) ip); +} + +#endif + +/* insert_instance - insert instance before selected location, claim names */ + +static void insert_instance(INSTANCE *ip, INST_SELECTION *selection) +{ + RING *old; + +#define append_instance(ip) insert_instance((ip), (INST_SELECTION *) 0) + + /* + * Insert instance before the selected site. + */ + claim_instance_attributes(ip); + if (ring_succ(instance_hd) == 0) + ring_init(instance_hd); + if (selection && selection->type != INST_SEL_NONE) { + FOREACH_SECONDARY_INSTANCE(old) { + if (match_instance_selection(RING_TO_INSTANCE(old), selection)) { + ring_prepend(old, RING_PTR_OF(ip)); + return; + } + } + if (selection->type != INST_SEL_ALL) + msg_fatal("No matching secondary instances"); + } + ring_prepend(instance_hd, RING_PTR_OF(ip)); +} + +/* create_primary_instance - synthetic entry for primary instance */ + +static INSTANCE *create_primary_instance(void) +{ + INSTANCE *ip = alloc_instance(var_config_dir); + + /* + * There is no need to load primary instance paramater settings from + * file. We already have the main.cf parameters of interest in memory. + */ +#define SAVE_INSTANCE_NAME(val) (*(val) ? mystrdup(val) : 0) + + ip->name = SAVE_INSTANCE_NAME(var_multi_name); + ip->gname = SAVE_INSTANCE_NAME(var_multi_group); + ip->enabled = var_multi_enable; + ip->queue_dir = mystrdup(var_queue_dir); + ip->data_dir = mystrdup(var_data_dir); + ip->primary = 1; + return (ip); +} + +/* load_instance - read instance parameters from config_dir/main.cf */ + +static INSTANCE *load_instance(INSTANCE *ip) +{ + VSTREAM *pipe; + VSTRING *buf; + char *name; + char *value; + ARGV *cmd; + int count = 0; + static NAME_CODE bool_code[] = { + CONFIG_BOOL_YES, 1, + CONFIG_BOOL_NO, 0, + 0, -1, + }; + + /* + * XXX: We could really use a "postconf -E" to expand values in the + * context of the target main.cf! + */ +#define REQUEST_PARAM_COUNT 5 /* # of requested parameters */ + + cmd = argv_alloc(REQUEST_PARAM_COUNT + 3); + name = concatenate(var_command_dir, "/", "postconf", (char *) 0); + argv_add(cmd, name, "-c", ip->config_dir, + VAR_QUEUE_DIR, VAR_DATA_DIR, + VAR_MULTI_NAME, VAR_MULTI_GROUP, VAR_MULTI_ENABLE, + (char *) 0); + myfree(name); + pipe = vstream_popen(O_RDONLY, VSTREAM_POPEN_ARGV, cmd->argv, + VSTREAM_POPEN_END); + argv_free(cmd); + if (pipe == 0) + msg_fatal("Cannot parse %s/main.cf file: %m", ip->config_dir); + + /* + * Read parameter settings from postconf. See also comments below on + * whether we should continue or skip groups after error instead of + * bailing out immediately. + */ + buf = vstring_alloc(100); + while (vstring_get_nonl(buf, pipe) != VSTREAM_EOF) { + if (split_nameval(STR(buf), &name, &value)) + msg_fatal("Invalid %s/main.cf parameter: %s", + ip->config_dir, STR(buf)); + if (strcmp(name, VAR_QUEUE_DIR) == 0 && ++count) + ip->queue_dir = mystrdup(value); + else if (strcmp(name, VAR_DATA_DIR) == 0 && ++count) + ip->data_dir = mystrdup(value); + else if (strcmp(name, VAR_MULTI_NAME) == 0 && ++count) + ip->name = SAVE_INSTANCE_NAME(value); + else if (strcmp(name, VAR_MULTI_GROUP) == 0 && ++count) + ip->gname = SAVE_INSTANCE_NAME(value); + else if (strcmp(name, VAR_MULTI_ENABLE) == 0 && ++count) { + /* mail_conf_bool(3) is case insensitive! */ + ip->enabled = name_code(bool_code, NAME_CODE_FLAG_NONE, value); + if (ip->enabled < 0) + msg_fatal("Unexpected %s/main.cf entry: %s = %s", + ip->config_dir, VAR_MULTI_ENABLE, value); + } + } + vstring_free(buf); + + /* + * XXX We should not bail out while reading a bad secondary main.cf file. + * When we manage dozens or more instances, the likelihood increases that + * some file will be damaged or missing after a system crash. That is not + * a good reason to prevent undamaged Postfix instances from starting. + */ + if (count != REQUEST_PARAM_COUNT) + msg_fatal("Failed to obtain all required %s/main.cf parameters", + ip->config_dir); + + if (vstream_pclose(pipe)) + msg_fatal("Cannot parse %s/main.cf file", ip->config_dir); + return (ip); +} + +/* load_all_instances - compute list of Postfix instances */ + +static void load_all_instances(void) +{ + INSTANCE *primary_instance; + char **cpp; + ARGV *secondary_names; + + /* + * Avoid unexpected behavior when $multi_instance_directories contains + * only comma characters. Count the actual number of elements, before we + * decide that the list is empty. + */ + secondary_names = argv_split(var_multi_conf_dirs, "\t\n\r, "); + + /* + * First, the primary instance. This is synthesized out of thin air. + */ + primary_instance = create_primary_instance(); + if (secondary_names->argc == 0) + primary_instance->enabled = 1; /* Single-instance mode */ + append_instance(primary_instance); + + /* + * Next, instances defined in $multi_instance_directories. Note: + * load_instance() has side effects on the global config dictionary, but + * this does not affect the values that have already been extracted into + * C variables. + */ + for (cpp = secondary_names->argv; *cpp != 0; cpp++) + append_instance(load_instance(alloc_instance(*cpp))); + + argv_free(secondary_names); +} + +/* match_instance_selection - match all/name/group constraints */ + +static int match_instance_selection(INSTANCE *ip, INST_SELECTION *selection) +{ + char *iname; + char *name; + + /* + * When selecting (rather than assigning names) an instance, we match by + * the instance name, config_directory path, or the instance name suffix + * (name without mandatory prefix). Selecting "-" selects the primary + * instance. + */ + switch (selection->type) { + case INST_SEL_NONE: + return (0); + case INST_SEL_ALL: + return (1); + case INST_SEL_GROUP: + return (ip->gname != 0 && strcmp(selection->name, ip->gname) == 0); + case INST_SEL_NAME: + name = selection->name; + if (*name == '/' || ip->name == 0) + iname = ip->config_dir; + else if (!HAS_NAME_PREFIX(name) && HAS_NAME_PREFIX(ip->name)) + iname = NAME_SUFFIX(ip->name); + else + iname = ip->name; + return (strcmp(name, iname) == 0 + || (ip->primary && strcmp(name, "-") == 0)); + default: + msg_panic("match_instance_selection: unknown selection type: %d", + selection->type); + } +} + +/* check_setenv - setenv() with extreme prejudice */ + +static void check_setenv(const char *name, const char *value) +{ +#define CLOBBER 1 + if (setenv(name, value, CLOBBER) < 0) + msg_fatal("setenv: %m"); +} + +/* prepend_command_path - prepend command_directory to PATH */ + +static void prepend_command_path(void) +{ + char *cmd_path; + + /* + * Carefully prepend "$command_directory:" to PATH. We can free the + * buffer after check_setenv(), since the value is copied there. + */ + cmd_path = safe_getenv("PATH"); + cmd_path = concatenate(var_command_dir, ":", (cmd_path && *cmd_path) ? + cmd_path : ROOT_PATH, (char *) 0); + check_setenv("PATH", cmd_path); + myfree(cmd_path); +} + +/* check_shared_dir_status - check and claim shared directories */ + +static void check_shared_dir_status(void) +{ + struct stat st; + const SHARED_PATH *sp; + + for (sp = shared_dir_table; sp->param_name; ++sp) { + if (stat(sp->param_value[0], &st) < 0) + msg_fatal("%s = '%s': directory not found: %m", + sp->param_name, sp->param_value[0]); + if (!S_ISDIR(st.st_mode)) + msg_fatal("%s = '%s' is not a directory", + sp->param_name, sp->param_value[0]); + register_claim(var_config_dir, sp->param_name, sp->param_value[0]); + } +} + +/* check_safe_name - allow instance or group name with only "safe" characters */ + +static int check_safe_name(const char *s) +{ +#define SAFE_PUNCT "!@%-_=+:./" + if (*s == 0) + return (0); + for (; *s; ++s) { + if (!ISALNUM(*s) && !strchr(SAFE_PUNCT, *s)) + return (0); + } + return (1); +} + +/* check_name_assignments - Check validity of assigned instance or group name */ + +static void check_name_assignments(NAME_ASSIGNMENT *assignment) +{ + + /* + * Syntax check the assigned instance name. This name is also used to + * generate directory pathnames, so we must not allow "/" characters. + * + * The value "" will clear the name and is always valid. The command-line + * parser has already converted "-" into "", to simplify implementation. + */ + if (assignment->name && *assignment->name) { + if (!check_safe_name(assignment->name)) + msg_fatal("Unsafe characters in new instance name: '%s'", + assignment->name); + if (strchr(assignment->name, '/')) + msg_fatal("Illegal '/' character in new instance name: '%s'", + assignment->name); + if (NEED_NAME_PREFIX(assignment->name)) + msg_fatal("New instance name must start with '%s'", + NAME_PREFIX); + } + + /* + * Syntax check the assigned group name. + */ + if (assignment->gname && *assignment->gname) { + if (!check_safe_name(assignment->gname)) + msg_fatal("Unsafe characters in '-G %s'", assignment->gname); + } +} + +/* do_name_assignments - assign instance/group names */ + +static int do_name_assignments(INSTANCE *target, NAME_ASSIGNMENT *assignment) +{ + int export_flags = 0; + + /* + * The command-line parser has already converted "-" into "", to simplify + * implementation. + */ + if (assignment->name + && strcmp(assignment->name, target->name ? target->name : "")) { + register_claim(target->config_dir, VAR_MULTI_NAME, assignment->name); + if (target->name) + myfree(target->name); + target->name = SAVE_INSTANCE_NAME(assignment->name); + export_flags |= EXP_FLAG_MULTI_NAME; + } + if (assignment->gname + && strcmp(assignment->gname, target->gname ? target->gname : "")) { + if (target->gname) + myfree(target->gname); + target->gname = SAVE_INSTANCE_NAME(assignment->gname); + export_flags |= EXP_FLAG_MULTI_GROUP; + } + return (export_flags); +} + +/* make_private_path - generate secondary pathname using primary as template */ + +static char *make_private_path(const char *param_name, + const char *primary_value, + NAME_ASSIGNMENT *assignment) +{ + char *path; + char *base; + char *end; + + /* + * The command-line parser has already converted "-" into "", to simplify + * implementation. + */ + if (assignment->name == 0 || *assignment->name == 0) + msg_fatal("Missing %s parameter value", param_name); + + if (*primary_value != '/') + msg_fatal("Invalid default %s parameter value: '%s': " + "specify an absolute pathname", + param_name, primary_value); + + base = mystrdup(primary_value); + if ((end = strrchr(base, '/')) != 0) { + /* Drop trailing slashes */ + if (end[1] == '\0') { + while (--end > base && *end == '/') + *end = '\0'; + end = strrchr(base, '/'); + } + /* Drop last path component */ + while (end > base && *end == '/') + *end-- = '\0'; + } + path = concatenate(base[1] ? base : "", "/", + assignment->name, (char *) 0); + myfree(base); + return (path); +} + +/* assign_new_parameter - assign new instance private name=value */ + +static void assign_new_parameter(INSTANCE *new, int edit_cmd, + const char *arg) +{ + char *saved_arg; + char *name; + char *value; + char *end; + char **target = 0; + + /* + * With "import", only config_directory is specified on the command line + * (either explicitly as config_directory=/path/name, or implicitly as + * instance name). The other private directory pathnames are taken from + * the existing instance's main.cf file. + * + * With "create", all private pathname parameters are specified on the + * command line, or generated from an instance name. + */ + saved_arg = mystrdup(arg); + if (split_nameval(saved_arg, &name, &value)) + msg_fatal("Malformed parameter setting '%s'", arg); + + if (strcmp(VAR_CONFIG_DIR, name) == 0) { + target = &new->config_dir; + } else if (edit_cmd != EDIT_CMD_IMPORT) { + if (strcmp(VAR_QUEUE_DIR, name) == 0) { + target = &new->queue_dir; + } else if (strcmp(VAR_DATA_DIR, name) == 0) { + target = &new->data_dir; + } + } + if (target == 0) + msg_fatal("Parameter '%s' not valid with action %s", + name, EDIT_CMD_STR(edit_cmd)); + + /* + * Extract and assign the parameter value. We do a limited number of + * checks here. Conflicts between instances are checked by the caller. + * More checks may be implemented in the helper script if inspired. + */ + if (*value != '/') + msg_fatal("Parameter setting '%s' is not an absolute path", name); + + /* Tolerate+trim trailing "/" from readline completion */ + for (end = value + strlen(value) - 1; end > value && *end == '/'; --end) + *end = 0; + + /* No checks here for "/." or other shoot-foot silliness. */ + if (end == value) + msg_fatal("Parameter setting '%s' is the root directory", name); + + if (*target) + myfree(*target); + *target = mystrdup(value); + + /* + * Cleanup. + */ + myfree(saved_arg); +} + +/* assign_new_parameters - initialize new instance private parameters */ + +static void assign_new_parameters(INSTANCE *new, int edit_cmd, + char **argv, NAME_ASSIGNMENT *assignment) +{ + const char *owner; + + /* + * Sanity check the explicit parameter settings. More stringent checks + * may take place in the helper script. + */ + while (*argv) + assign_new_parameter(new, edit_cmd, *argv++); + + /* + * Initialize any missing private directory pathnames, using the primary + * configuration directory parameter values as a template, and using the + * assigned instance name to fill in the blanks. + * + * When importing an existing instance, load private directory pathnames + * from its main.cf file. + */ + if (new->config_dir == 0) + new->config_dir = + make_private_path(VAR_CONFIG_DIR, var_config_dir, assignment); + /* Needed for better-quality error message. */ + if ((owner = IS_CLAIMED_BY(new->config_dir)) != 0) + msg_fatal("new %s=%s is already in use by instance %s=%s", + VAR_CONFIG_DIR, new->config_dir, owner, new->config_dir); + if (edit_cmd != EDIT_CMD_IMPORT) { + if (new->queue_dir == 0) + new->queue_dir = + make_private_path(VAR_QUEUE_DIR, var_queue_dir, assignment); + if (new->data_dir == 0) + new->data_dir = + make_private_path(VAR_DATA_DIR, var_data_dir, assignment); + } else { + load_instance(new); + } +} + +/* export_helper_environment - update environment settings for helper command */ + +static void export_helper_environment(INSTANCE *target, int export_flags) +{ + ARGV *import_env; + VSTRING *multi_dirs; + const SHARED_PATH *sp; + RING *entry; + + /* + * Environment import filter, to enforce consistent behavior whether this + * command is started by hand, or at system boot time. This is necessary + * because some shell scripts use environment settings to override + * main.cf settings. + */ + import_env = argv_split(var_import_environ, ", \t\r\n"); + clean_env(import_env->argv); + argv_free(import_env); + + /* + * Prepend $command_directory: to PATH. This supposedly ensures that + * naive programs will execute commands from the right Postfix version. + */ + prepend_command_path(); + + /* + * The following ensures that Postfix's own programs will target the + * primary instance. + */ + check_setenv(CONF_ENV_PATH, var_config_dir); + + /* + * Export the parameter settings that are shared between instances. + */ + for (sp = shared_dir_table; sp->param_name; ++sp) + check_setenv(sp->param_name, sp->param_value[0]); + + /* + * Export the target instance's private directory locations. + */ + check_setenv(VAR_CONFIG_DIR, target->config_dir); + check_setenv(VAR_QUEUE_DIR, target->queue_dir); + check_setenv(VAR_DATA_DIR, target->data_dir); + + /* + * With operations that add or delete a secondary instance, we export the + * modified multi_instance_directories parameter value for the primary + * Postfix instance. + */ + if (export_flags & EXP_FLAG_MULTI_DIRS) { + multi_dirs = vstring_alloc(100); + FOREACH_SECONDARY_INSTANCE(entry) { + if (VSTRING_LEN(multi_dirs) > 0) + VSTRING_ADDCH(multi_dirs, ' '); + vstring_strcat(multi_dirs, RING_TO_INSTANCE(entry)->config_dir); + } + check_setenv(VAR_MULTI_CONF_DIRS, STR(multi_dirs)); + vstring_free(multi_dirs); + } + + /* + * Export updates for the instance name and group. Empty value (or no + * export) means don't update, "-" means clear. + */ + if (export_flags & EXP_FLAG_MULTI_NAME) + check_setenv(VAR_MULTI_NAME, target->name && *target->name ? + target->name : "-"); + + if (export_flags & EXP_FLAG_MULTI_GROUP) + check_setenv(VAR_MULTI_GROUP, target->gname && *target->gname ? + target->gname : "-"); + + /* + * If we would implement enable/disable commands by exporting the updated + * parameter value, then we could skip commands that have no effect, just + * like we can skip "assign" commands that make no change. + */ +} + +/* install_new_instance - install and return newly created instance */ + +static INSTANCE *install_new_instance(int edit_cmd, char **argv, + INST_SELECTION *selection, + NAME_ASSIGNMENT *assignment, + int *export_flags) +{ + INSTANCE *new; + + new = alloc_instance((char *) 0); + check_name_assignments(assignment); + assign_new_parameters(new, edit_cmd, argv, assignment); + *export_flags |= + (do_name_assignments(new, assignment) | EXP_FLAG_MULTI_DIRS); + insert_instance(new, selection); + return (new); +} + +/* update_instance - update existing instance, return export flags */ + +static int update_instance(INSTANCE *target, NAME_ASSIGNMENT *assignment) +{ + int export_flags; + + check_name_assignments(assignment); + export_flags = do_name_assignments(target, assignment); + return (export_flags); +} + +/* select_existing_instance - return instance selected for management */ + +static INSTANCE *select_existing_instance(INST_SELECTION *selection, + int unlink_flag, + int *export_flags) +{ + INSTANCE *selected = 0; + RING *entry; + INSTANCE *ip; + +#define DONT_UNLINK 0 +#define DO_UNLINK 1 + + if (selection->type != INST_SEL_NAME) + msg_fatal("Select an instance via '-i name'"); + + /* Find the selected instance and its predecessor */ + FOREACH_INSTANCE(entry) { + if (match_instance_selection(ip = RING_TO_INSTANCE(entry), selection)) { + selected = ip; + break; + } + } + + if (selected == 0) + msg_fatal("No instance named %s", selection->name); + + if (unlink_flag) { + /* Splice the target instance out of the list */ + if (ring_pred(entry) == instance_hd) + msg_fatal("Cannot remove the primary instance"); + if (selected->enabled) + msg_fatal("Cannot remove enabled instances"); + ring_detach(entry); + if (export_flags == 0) + msg_panic("select_existing_instance: no export flags"); + *export_flags |= EXP_FLAG_MULTI_DIRS; + } + return (selected); +} + +/* manage - create/destroy/... manage instances */ + +static NORETURN manage(int edit_cmd, int argc, char **argv, + INST_SELECTION *selection, + NAME_ASSIGNMENT *assignment) +{ + char *cmd; + INSTANCE *target; + int export_flags; + + /* + * Edit mode is not subject to iterator controls. + */ +#define NO_EXPORT_FLAGS ((int *) 0) + export_flags = 0; + + switch (edit_cmd) { + case EDIT_CMD_INIT: + target = create_primary_instance(); + break; + + case EDIT_CMD_CREATE: + case EDIT_CMD_IMPORT: + load_all_instances(); + target = install_new_instance(edit_cmd, argv, selection, + assignment, &export_flags); + break; + + case EDIT_CMD_ASSIGN: + load_all_instances(); + target = + select_existing_instance(selection, DONT_UNLINK, NO_EXPORT_FLAGS); + export_flags |= update_instance(target, assignment); + if (export_flags == 0) + exit(0); + break; + + case EDIT_CMD_DESTROY: + case EDIT_CMD_DEPORT: + load_all_instances(); + target = select_existing_instance(selection, DO_UNLINK, &export_flags); + break; + + default: + load_all_instances(); + target = + select_existing_instance(selection, DONT_UNLINK, NO_EXPORT_FLAGS); + break; + } + + /* + * Set up the helper script's process environment, and execute the helper + * script. + */ +#define HELPER "postmulti-script" + + export_helper_environment(target, export_flags); + cmd = concatenate(var_daemon_dir, "/" HELPER, (char *) 0); + execl(cmd, cmd, "-e", EDIT_CMD_STR(edit_cmd), (char *) 0); + msg_fatal("%s: %m", cmd); +} + +/* run_user_command - execute external command with requested MAIL_CONFIG env */ + +static int run_user_command(INSTANCE *ip, int iter_cmd, int iter_flags, + char **argv) +{ + WAIT_STATUS_T status; + int pid; + int wpid; + + /* + * Set up a process environment. The postfix(1) command needs MAIL_CONFIG + * (or the equivalent command-line option); it overrides everything else. + * + * postmulti(1) typically runs various Postfix utilities (postsuper, ...) in + * the context of one or more instances. It can also run various scripts + * on the users PATH. So we can't clobber the user's PATH, but do want to + * make sure that the utilities in $command_directory are always found in + * the right place (or at all). + */ + switch (pid = fork()) { + case -1: + msg_warn("fork %s: %m", argv[0]); + return -1; + case 0: + check_setenv(CONF_ENV_PATH, ip->config_dir); + if (iter_cmd != ITER_CMD_POSTFIX) { + check_setenv(VAR_DAEMON_DIR, var_daemon_dir); + check_setenv(VAR_COMMAND_DIR, var_command_dir); + check_setenv(VAR_CONFIG_DIR, ip->config_dir); + check_setenv(VAR_QUEUE_DIR, ip->queue_dir); + check_setenv(VAR_DATA_DIR, ip->data_dir); + check_setenv(VAR_MULTI_NAME, ip->name ? ip->name : ""); + check_setenv(VAR_MULTI_GROUP, ip->gname ? ip->gname : ""); + check_setenv(VAR_MULTI_ENABLE, ip->enabled ? + CONFIG_BOOL_YES : CONFIG_BOOL_NO); + prepend_command_path(); + } + + /* + * Replace: postfix -- start ... With: postfix -- check ... + */ + if (iter_cmd == ITER_CMD_POSTFIX + && (iter_flags & ITER_FLAG_CHECK_DISABLED) && !ip->enabled) + argv[2] = "check"; + + execvp(argv[0], argv); + msg_fatal("execvp %s: %m", argv[0]); + default: + do { + wpid = waitpid(pid, &status, 0); + } while (wpid == -1 && errno == EINTR); + return (wpid == -1 ? -1 : + WIFEXITED(status) ? WEXITSTATUS(status) : 1); + } +} + +/* word_in_list - look up command in start, stop, or control list */ + +static int word_in_list(char *cmdlist, const char *cmd) +{ + char *saved; + char *cp; + char *elem; + + cp = saved = mystrdup(cmdlist); + while ((elem = mystrtok(&cp, "\t\n\r, ")) != 0 && strcmp(elem, cmd) != 0) + /* void */ ; + myfree(saved); + return (elem != 0); +} + +/* iterate_postfix_command - execute postfix(1) command */ + +static int iterate_postfix_command(int iter_cmd, int argc, char **argv, + INST_SELECTION *selection) +{ + int exit_status; + char *cmd; + ARGV *my_argv; + int iter_flags; + + /* + * Override the iterator controls. + */ + if (word_in_list(var_multi_start_cmds, argv[0])) { + iter_flags = ITER_FLAG_CHECK_DISABLED; + } else if (word_in_list(var_multi_stop_cmds, argv[0])) { + iter_flags = ITER_FLAG_SKIP_DISABLED | ITER_FLAG_REVERSE; + } else if (word_in_list(var_multi_cntrl_cmds, argv[0])) { + iter_flags = ITER_FLAG_SKIP_DISABLED; + } else { + iter_flags = 0; + } + + /* + * Override the command line in a straightforward manner: prepend + * "postfix --" to the command arguments. Other overrides (environment, + * start -> check) are implemented below the iterator. + */ +#define POSTFIX_CMD "postfix" + + my_argv = argv_alloc(argc + 2); + cmd = concatenate(var_command_dir, "/" POSTFIX_CMD, (char *) 0); + argv_add(my_argv, cmd, "--", (char *) 0); + myfree(cmd); + while (*argv) + argv_add(my_argv, *argv++, (char *) 0); + + /* + * Execute the command for all applicable Postfix instances. + */ + exit_status = + iterate_command(iter_cmd, iter_flags, my_argv->argv, selection); + + argv_free(my_argv); + return (exit_status); +} + +/* list_instances - list all selected instances */ + +static void list_instances(int iter_flags, INST_SELECTION *selection) +{ + RING *entry; + INSTANCE *ip; + + /* + * Iterate over the selected instances. + */ + FOREACH_ITERATOR_INSTANCE(iter_flags, entry) { + ip = RING_TO_INSTANCE(entry); + if (match_instance_selection(ip, selection)) + vstream_printf("%-15s %-15s %-9s %s\n", + ip->name ? ip->name : "-", + ip->gname ? ip->gname : "-", + ip->enabled ? "y" : "n", + ip->config_dir); + } + if (vstream_fflush(VSTREAM_OUT)) + msg_fatal("error writing output: %m"); +} + +/* iterate_command - execute command for selected instances */ + +static int iterate_command(int iter_cmd, int iter_flags, char **argv, + INST_SELECTION *selection) +{ + int exit_status = 0; + int matched = 0; + RING *entry; + INSTANCE *ip; + + /* + * Iterate over the selected instances. + */ + FOREACH_ITERATOR_INSTANCE(iter_flags, entry) { + ip = RING_TO_INSTANCE(entry); + if (!match_instance_selection(ip, selection)) + continue; + matched = 1; + + /* Run the requested command */ + if (run_user_command(ip, iter_cmd, iter_flags, argv) != 0) + exit_status = 1; + } + if (matched == 0) + msg_fatal("No matching instances"); + + return (exit_status); +} + +/* iterate - Iterate over all or selected instances */ + +static NORETURN iterate(int iter_cmd, int iter_flags, int argc, char **argv, + INST_SELECTION *selection) +{ + int exit_status; + + /* + * In iterator mode, no selection means wild-card selection. + */ + if (selection->type == INST_SEL_NONE) + selection->type = INST_SEL_ALL; + + /* + * Load the in-memory instance table from main.cf files. + */ + load_all_instances(); + + /* + * Iterate over the selected instances. + */ + switch (iter_cmd) { + case ITER_CMD_POSTFIX: + exit_status = iterate_postfix_command(iter_cmd, argc, argv, selection); + break; + case ITER_CMD_LIST: + list_instances(iter_flags, selection); + exit_status = 0; + break; + case ITER_CMD_GENERIC: + exit_status = iterate_command(iter_cmd, iter_flags, argv, selection); + break; + default: + msg_panic("iterate: unknown mode: %d", iter_cmd); + } + exit(exit_status); +} + +static NORETURN usage(const char *progname) +{ + msg_fatal("Usage:" + "%s -l [-v] [-a] [-g group] [-i instance] | " + "%s -p [-v] [-a] [-g group] [-i instance] command... | " + "%s -x [-v] [-a] [-i name] [-g group] command... | " + "%s -e action [-v] [-a] [-i name] [-g group] [-I name] " + "[-G group] [param=value ...]", + progname, progname, progname, progname); +} + +MAIL_VERSION_STAMP_DECLARE; + +/* main - iterate commands over multiple instance or manage instances */ + +int main(int argc, char **argv) +{ + int fd; + struct stat st; + char *slash; + char *config_dir; + int ch; + static const CONFIG_STR_TABLE str_table[] = { + VAR_MULTI_START_CMDS, DEF_MULTI_START_CMDS, &var_multi_start_cmds, 0, 0, + VAR_MULTI_STOP_CMDS, DEF_MULTI_STOP_CMDS, &var_multi_stop_cmds, 0, 0, + VAR_MULTI_CNTRL_CMDS, DEF_MULTI_CNTRL_CMDS, &var_multi_cntrl_cmds, 0, 0, + 0, + }; + int instance_select_count = 0; + int command_mode_count = 0; + INST_SELECTION selection; + NAME_ASSIGNMENT assignment; + int iter_flags = ITER_FLAG_DEFAULT; + int cmd_mode = 0; + int code; + + selection.type = INST_SEL_NONE; + assignment.name = assignment.gname = 0; + + /* + * Fingerprint executables and core dumps. + */ + MAIL_VERSION_STAMP_ALLOCATE; + + /* + * Be consistent with file permissions. + */ + umask(022); + + /* + * To minimize confusion, make sure that the standard file descriptors + * are open before opening anything else. XXX Work around for 44BSD where + * fstat can return EBADF on an open file descriptor. + */ + for (fd = 0; fd < 3; fd++) + if (fstat(fd, &st) == -1 + && (close(fd), open("/dev/null", O_RDWR, 0)) != fd) + msg_fatal("open /dev/null: %m"); + + /* + * Set up diagnostics. XXX What if stdin is the system console during + * boot time? It seems a bad idea to log startup errors to the console. + * This is UNIX, a system that can run without hand holding. + */ + if ((slash = strrchr(argv[0], '/')) != 0 && slash[1]) + argv[0] = slash + 1; + if (isatty(STDERR_FILENO)) + msg_vstream_init(argv[0], VSTREAM_ERR); + msg_syslog_init(argv[0], LOG_PID, LOG_FACILITY); + + if ((config_dir = getenv(CONF_ENV_PATH)) != 0 + && strcmp(config_dir, DEF_CONFIG_DIR) != 0) + msg_fatal("Non-default configuration directory: %s=%s", + CONF_ENV_PATH, config_dir); + + /* + * Parse switches. + */ + while ((ch = GETOPT(argc, argv, "ae:g:i:G:I:lpRvx")) > 0) { + switch (ch) { + default: + usage(argv[0]); + /* NOTREACHED */ + case 'a': + if (selection.type != INST_SEL_ALL) + instance_select_count++; + selection.type = INST_SEL_ALL; + break; + case 'e': + if ((code = EDIT_CMD_CODE(optarg)) < 0) + msg_fatal("Invalid '-e' edit action '%s'. Specify '%s', " + "'%s', '%s', '%s', '%s', '%s', '%s', '%s' or '%s'", + optarg, + EDIT_CMD_STR(EDIT_CMD_CREATE), + EDIT_CMD_STR(EDIT_CMD_DESTROY), + EDIT_CMD_STR(EDIT_CMD_IMPORT), + EDIT_CMD_STR(EDIT_CMD_DEPORT), + EDIT_CMD_STR(EDIT_CMD_ENABLE), + EDIT_CMD_STR(EDIT_CMD_DISABLE), + EDIT_CMD_STR(EDIT_CMD_ASSIGN), + EDIT_CMD_STR(EDIT_CMD_INIT), + optarg); + if (cmd_mode != code) + command_mode_count++; + cmd_mode = code; + break; + case 'g': + instance_select_count++; + selection.type = INST_SEL_GROUP; + selection.name = optarg; + break; + case 'i': + instance_select_count++; + selection.type = INST_SEL_NAME; + selection.name = optarg; + break; + case 'G': + if (assignment.gname != 0) + msg_fatal("Specify at most one '-G' option"); + assignment.gname = strcmp(optarg, "-") == 0 ? "" : optarg; + break; + case 'I': + if (assignment.name != 0) + msg_fatal("Specify at most one '-I' option"); + assignment.name = strcmp(optarg, "-") == 0 ? "" : optarg; + break; + case 'l': + if (cmd_mode != ITER_CMD_LIST) + command_mode_count++; + cmd_mode = ITER_CMD_LIST; + break; + case 'p': + if (cmd_mode != ITER_CMD_POSTFIX) + command_mode_count++; + cmd_mode = ITER_CMD_POSTFIX; + break; + case 'R': + iter_flags ^= ITER_FLAG_REVERSE; + break; + case 'v': + msg_verbose++; + check_setenv(CONF_ENV_VERB, ""); + break; + case 'x': + if (cmd_mode != ITER_CMD_GENERIC) + command_mode_count++; + cmd_mode = ITER_CMD_GENERIC; + break; + } + } + + /* + * Report missing arguments, or wrong arguments in the wrong context. + */ + if (instance_select_count > 1) + msg_fatal("Specity no more than one of '-a', '-g', '-i'"); + + if (command_mode_count != 1) + msg_fatal("Specify exactly one of '-e', '-l', '-p', '-x'"); + + if (cmd_mode == ITER_CMD_LIST && argc > optind) + msg_fatal("Command not allowed with '-l'"); + + if (cmd_mode == ITER_CMD_POSTFIX || cmd_mode == ITER_CMD_GENERIC) + if (argc == optind) + msg_fatal("Command required with '-p' or '-x' option"); + + if (cmd_mode == ITER_CMD_POSTFIX || (cmd_mode & EDIT_CMD_MASK_ALL)) + if (iter_flags != ITER_FLAG_DEFAULT) + msg_fatal("The '-p' and '-e' options preclude the use of '-R'"); + + if ((cmd_mode & EDIT_CMD_MASK_ASSIGN) == 0 + && (assignment.name || assignment.gname)) { + if ((cmd_mode & EDIT_CMD_MASK_ALL) == 0) + msg_fatal("Cannot assign instance name or group without '-e %s'", + EDIT_CMD_STR(EDIT_CMD_ASSIGN)); + else + msg_fatal("Cannot assign instance name or group with '-e %s'", + EDIT_CMD_STR(cmd_mode)); + } + if (cmd_mode & EDIT_CMD_MASK_ALL) { + if (cmd_mode == EDIT_CMD_ASSIGN + && (assignment.name == 0 && assignment.gname == 0)) + msg_fatal("Specify new instance name or group with '-e %s'", + EDIT_CMD_STR(cmd_mode)); + + if ((cmd_mode & ~EDIT_CMD_MASK_ADD) != 0 && argc > optind) + msg_fatal("Parameter overrides not valid with '-e %s'", + EDIT_CMD_STR(cmd_mode)); + } + + /* + * Proces main.cf parameters. + */ + mail_conf_read(); + get_mail_conf_str_table(str_table); + + /* + * Sanity checks. + */ + check_shared_dir_status(); + + /* + * Iterate over selected instances, or manipulate one instance. + */ + if (cmd_mode & ITER_CMD_MASK_ALL) + iterate(cmd_mode, iter_flags, argc - optind, argv + optind, &selection); + else + manage(cmd_mode, argc - optind, argv + optind, &selection, &assignment); +} diff --git a/postfix/src/qmgr/qmgr.h b/postfix/src/qmgr/qmgr.h index df8b980ad..52f0327e3 100644 --- a/postfix/src/qmgr/qmgr.h +++ b/postfix/src/qmgr/qmgr.h @@ -436,6 +436,7 @@ struct QMGR_PEER { extern QMGR_ENTRY *qmgr_job_entry_select(QMGR_TRANSPORT *); extern QMGR_PEER *qmgr_peer_select(QMGR_JOB *); +extern void qmgr_job_blocker_update(QMGR_QUEUE *); extern QMGR_JOB *qmgr_job_obtain(QMGR_MESSAGE *, QMGR_TRANSPORT *); extern void qmgr_job_free(QMGR_JOB *); diff --git a/postfix/src/qmgr/qmgr_entry.c b/postfix/src/qmgr/qmgr_entry.c index 9f89e0a73..71eba9f0a 100644 --- a/postfix/src/qmgr/qmgr_entry.c +++ b/postfix/src/qmgr/qmgr_entry.c @@ -299,29 +299,25 @@ void qmgr_entry_done(QMGR_ENTRY *entry, int which) } /* - * If the queue was blocking some of the jobs on the job list, check if - * the concurrency limit has lifted. If there are still some pending - * deliveries, give it a try and unmark all transport blockers at once. - * The qmgr_job_entry_select() will do the rest. In either case make sure - * the queue is not marked as a blocker anymore, with extra handling of - * queues which were declared dead. + * We implement a rate-limited queue by emulating a slow delivery + * channel. We insert the artificial delays with qmgr_queue_suspend(). * - * Note that changing the blocker status also affects the candidate cache. - * Most of the cases would be automatically recognized by the current job - * change, but we play safe and reset the cache explicitly below. - * - * Keeping the transport blocker tag odd is an easy way to make sure the tag - * never matches jobs that are not explicitly marked as blockers. + * When a queue is suspended, we must postpone any job scheduling decisions + * until the queue is resumed. Otherwise, we make those decisions now. + * The job scheduling decisions are made by qmgr_job_blocker_update(). */ - if (queue->blocker_tag == transport->blocker_tag) { - if (queue->window > queue->busy_refcount && queue->todo.next != 0) { - transport->blocker_tag += 2; - transport->job_current = transport->job_list.next; - transport->candidate_cache_current = 0; - } - if (queue->window > queue->busy_refcount || QMGR_QUEUE_THROTTLED(queue)) - queue->blocker_tag = 0; + if (which == QMGR_QUEUE_BUSY && transport->rate_delay > 0) { + if (queue->window > 1) + msg_panic("%s: queue %s/%s: window %d > 1 on rate-limited service", + myname, transport->name, queue->name, queue->window); + if (QMGR_QUEUE_THROTTLED(queue)) /* XXX */ + qmgr_queue_unthrottle(queue); + if (QMGR_QUEUE_READY(queue)) + qmgr_queue_suspend(queue, transport->rate_delay); } + if (!QMGR_QUEUE_SUSPENDED(queue) + && queue->blocker_tag == transport->blocker_tag) + qmgr_job_blocker_update(queue); /* * When there are no more entries for this peer, discard the peer @@ -337,19 +333,6 @@ void qmgr_entry_done(QMGR_ENTRY *entry, int which) if (which == QMGR_QUEUE_BUSY) queue->last_done = event_time(); - /* - * Suspend a rate-limited queue, so that mail trickles out. - */ - if (which == QMGR_QUEUE_BUSY && transport->rate_delay > 0) { - if (queue->window > 1) - msg_panic("%s: queue %s/%s: window %d > 1 on rate-limited service", - myname, transport->name, queue->name, queue->window); - if (QMGR_QUEUE_THROTTLED(queue)) /* XXX */ - qmgr_queue_unthrottle(queue); - if (QMGR_QUEUE_READY(queue)) - qmgr_queue_suspend(queue, transport->rate_delay); - } - /* * When the in-core queue for this site is empty and when this site is * not dead or suspended, discard the in-core queue. When this site is diff --git a/postfix/src/qmgr/qmgr_job.c b/postfix/src/qmgr/qmgr_job.c index ad727f43a..7de70f25c 100644 --- a/postfix/src/qmgr/qmgr_job.c +++ b/postfix/src/qmgr/qmgr_job.c @@ -18,6 +18,9 @@ /* /* QMGR_ENTRY *qmgr_job_entry_select(transport) /* QMGR_TRANSPORT *transport; +/* +/* void qmgr_job_blocker_update(queue) +/* QMGR_QUEUE *queue; /* DESCRIPTION /* These routines add/delete/manipulate per-transport jobs. /* Each job corresponds to a specific transport and message. @@ -38,6 +41,11 @@ /* If necessary, an attempt to read more recipients into core is made. /* This can result in creation of more job, queue and entry structures. /* +/* qmgr_job_blocker_update() updates the status of blocked +/* jobs after a decrease in the queue's concurrency level, +/* after the queue is throttled, or after the queue is resumed +/* from suspension. +/* /* qmgr_job_move_limits() takes care of proper distribution of the /* per-transport recipients limit among the per-transport jobs. /* Should be called whenever a job's recipient slot becomes available. @@ -937,3 +945,36 @@ QMGR_ENTRY *qmgr_job_entry_select(QMGR_TRANSPORT *transport) transport->job_current = 0; return (0); } + +/* qmgr_job_blocker_update - update "blocked job" status */ + +void qmgr_job_blocker_update(QMGR_QUEUE *queue) +{ + QMGR_TRANSPORT *transport = queue->transport; + + /* + * If the queue was blocking some of the jobs on the job list, check if + * the concurrency limit has lifted. If there are still some pending + * deliveries, give it a try and unmark all transport blockers at once. + * The qmgr_job_entry_select() will do the rest. In either case make sure + * the queue is not marked as a blocker anymore, with extra handling of + * queues which were declared dead. + * + * Note that changing the blocker status also affects the candidate cache. + * Most of the cases would be automatically recognized by the current job + * change, but we play safe and reset the cache explicitly below. + * + * Keeping the transport blocker tag odd is an easy way to make sure the tag + * never matches jobs that are not explicitly marked as blockers. + */ + if (queue->blocker_tag == transport->blocker_tag) { + if (queue->window > queue->busy_refcount && queue->todo.next != 0) { + transport->blocker_tag += 2; + transport->job_current = transport->job_list.next; + transport->candidate_cache_current = 0; + } + if (queue->window > queue->busy_refcount || QMGR_QUEUE_THROTTLED(queue)) + queue->blocker_tag = 0; + } +} + diff --git a/postfix/src/qmgr/qmgr_queue.c b/postfix/src/qmgr/qmgr_queue.c index 3bc6782b2..d35b7db0c 100644 --- a/postfix/src/qmgr/qmgr_queue.c +++ b/postfix/src/qmgr/qmgr_queue.c @@ -66,7 +66,11 @@ /* "slow open" mode, and eliminates the "thundering herd" problem. /* /* qmgr_queue_suspend() suspends delivery for this destination -/* briefly. +/* briefly. This function invalidates any scheduling decisions +/* that are based on the present queue's concurrency window. +/* To compensate for work skipped by qmgr_entry_done(), the +/* status of blocker jobs is re-evaluated after the queue is +/* resumed. /* DIAGNOSTICS /* Panic: consistency check failure. /* LICENSE @@ -152,9 +156,20 @@ static void qmgr_queue_resume(int event, char *context) /* * Every event handler that leaves a queue in the "ready" state should * remove the queue when it is empty. + * + * XXX Do not omit the redundant test below. It is here to simplify code + * consistency checks. The check is trivially eliminated by the compiler + * optimizer. There is no need to sacrifice code clarity for the sake of + * performance. + * + * XXX Do not expose the blocker job logic here. Rate-limited queues are not + * a performance-critical feature. Here, too, there is no need to sacrifice + * code clarity for the sake of performance. */ if (QMGR_QUEUE_READY(queue) && queue->todo.next == 0 && queue->busy.next == 0) qmgr_queue_done(queue); + else + qmgr_job_blocker_update(queue); } /* qmgr_queue_suspend - briefly suspend a destination */ diff --git a/postfix/src/qmgr/qmgr_transport.c b/postfix/src/qmgr/qmgr_transport.c index 29a1f53e1..434d75efc 100644 --- a/postfix/src/qmgr/qmgr_transport.c +++ b/postfix/src/qmgr/qmgr_transport.c @@ -291,6 +291,8 @@ QMGR_TRANSPORT *qmgr_transport_select(void) continue; need = xport->pending + 1; for (queue = xport->queue_list.next; queue; queue = queue->peers.next) { + if (QMGR_QUEUE_READY(queue) == 0) + continue; if ((need -= MIN5af51743e4eef(queue->window - queue->busy_refcount, queue->todo_refcount)) <= 0) { QMGR_LIST_ROTATE(qmgr_transport_list, xport, peers); diff --git a/postfix/src/sendmail/sendmail.c b/postfix/src/sendmail/sendmail.c index e5b147248..14bd673d7 100644 --- a/postfix/src/sendmail/sendmail.c +++ b/postfix/src/sendmail/sendmail.c @@ -357,7 +357,7 @@ /* append the specified domain name to incomplete addresses. /* .IP "\fBsyslog_facility (mail)\fR" /* The syslog facility of Postfix logging. -/* .IP "\fBsyslog_name (postfix)\fR" +/* .IP "\fBsyslog_name (see 'postconf -d' output)\fR" /* The mail system name that is prepended to the process name in syslog /* records, so that "smtpd" becomes, for example, "postfix/smtpd". /* FILES @@ -375,8 +375,12 @@ /* postqueue(1), mail queue control /* syslogd(8), system logging /* README_FILES +/* .ad +/* .fi /* Use "\fBpostconf readme_directory\fR" or /* "\fBpostconf html_directory\fR" to locate this information. +/* .na +/* .nf /* DEBUG_README, Postfix debugging howto /* ETRN_README, Postfix ETRN howto /* VERP_README, Postfix VERP howto @@ -754,7 +758,7 @@ static void enqueue(const int flags, const char *encoding, rec_fprintf(dst, REC_TYPE_NORM, "From: %s", saved_sender); rec_fprintf(dst, REC_TYPE_NORM, "Subject: probe"); if (recipients) { - rec_fprintf(dst, REC_TYPE_NORM, "To:"); + rec_fprintf(dst, REC_TYPE_CONT, "To:"); for (cpp = recipients; *cpp != 0; cpp++) { rec_fprintf(dst, REC_TYPE_NORM, " %s%s", *cpp, cpp[1] ? "," : ""); diff --git a/postfix/src/smtp/Makefile.in b/postfix/src/smtp/Makefile.in index 2d24a069c..64b8ffbb8 100644 --- a/postfix/src/smtp/Makefile.in +++ b/postfix/src/smtp/Makefile.in @@ -355,6 +355,7 @@ smtp_rcpt.o: ../../include/dsn_mask.h smtp_rcpt.o: ../../include/header_body_checks.h smtp_rcpt.o: ../../include/header_opts.h smtp_rcpt.o: ../../include/htable.h +smtp_rcpt.o: ../../include/mail_params.h smtp_rcpt.o: ../../include/maps.h smtp_rcpt.o: ../../include/match_list.h smtp_rcpt.o: ../../include/match_ops.h diff --git a/postfix/src/smtp/lmtp_params.c b/postfix/src/smtp/lmtp_params.c index b202fd219..645d5ae2a 100644 --- a/postfix/src/smtp/lmtp_params.c +++ b/postfix/src/smtp/lmtp_params.c @@ -103,5 +103,6 @@ VAR_LMTP_SENDER_AUTH, DEF_LMTP_SENDER_AUTH, &var_smtp_sender_auth, VAR_LMTP_CNAME_OVERR, DEF_LMTP_CNAME_OVERR, &var_smtp_cname_overr, VAR_LMTP_SASL_AUTH_SOFT_BOUNCE, DEF_LMTP_SASL_AUTH_SOFT_BOUNCE, &var_smtp_sasl_auth_soft_bounce, + VAR_LMTP_ASSUME_FINAL, DEF_LMTP_ASSUME_FINAL, &var_lmtp_assume_final, 0, }; diff --git a/postfix/src/smtp/smtp.c b/postfix/src/smtp/smtp.c index d4c7314bd..7546bcf5b 100644 --- a/postfix/src/smtp/smtp.c +++ b/postfix/src/smtp/smtp.c @@ -542,6 +542,10 @@ /* .IP "\fBipc_timeout (3600s)\fR" /* The time limit for sending or receiving information over an internal /* communication channel. +/* .IP "\fBlmtp_assume_final (no)\fR" +/* When an LMTP server announces no DSN support, assume that the +/* server performs final delivery, and send "delivered" delivery status +/* notifications instead of "relayed". /* .IP "\fBlmtp_tcp_port (24)\fR" /* The default TCP port that the Postfix LMTP client connects to. /* .IP "\fBmax_idle (100s)\fR" @@ -574,7 +578,7 @@ /* Randomize the order of equal-preference MX host addresses. /* .IP "\fBsyslog_facility (mail)\fR" /* The syslog facility of Postfix logging. -/* .IP "\fBsyslog_name (postfix)\fR" +/* .IP "\fBsyslog_name (see 'postconf -d' output)\fR" /* The mail system name that is prepended to the process name in syslog /* records, so that "smtpd" becomes, for example, "postfix/smtpd". /* .PP @@ -781,6 +785,7 @@ char *var_smtp_head_chks; char *var_smtp_mime_chks; char *var_smtp_nest_chks; char *var_smtp_body_chks; +bool var_lmtp_assume_final; /* Special handling of 535 AUTH errors. */ char *var_smtp_sasl_auth_cache_name; diff --git a/postfix/src/smtp/smtp_rcpt.c b/postfix/src/smtp/smtp_rcpt.c index 31599464b..ebfb5e328 100644 --- a/postfix/src/smtp/smtp_rcpt.c +++ b/postfix/src/smtp/smtp_rcpt.c @@ -116,6 +116,7 @@ /* Global library. */ +#include #include /* smtp_rcpt_done */ #include /* smtp_rcpt_done */ #include /* smtp_rcpt_done */ @@ -132,19 +133,36 @@ void smtp_rcpt_done(SMTP_STATE *state, SMTP_RESP *resp, RECIPIENT *rcpt) DELIVER_REQUEST *request = state->request; SMTP_SESSION *session = state->session; DSN_BUF *why = state->why; + const char *dsn_action = "relayed"; int status; /* - * Report success and delete the recipient from the delivery request. - * Defer if the success can't be reported. Don't send a DSN "SUCCESS" - * notification if the receiving site announced DSN support. - * - * Note: the DSN action is ignored in case of address probes. + * Assume this was intermediate delivery when the server announced DSN + * support, and don't send a DSN "SUCCESS" notification. */ if (session->features & SMTP_FEATURE_DSN) rcpt->dsn_notify &= ~DSN_NOTIFY_SUCCESS; - dsb_update(why, resp->dsn, "relayed", DSB_MTYPE_DNS, session->host, + /* + * Assume this was final delivery when the LMTP server announced no DSN + * support. In backwards compatibility mode, send a "relayed" instead of + * a "delivered" DSN "SUCCESS" notification. Do not attempt to "simplify" + * the expression. The redundancy is for clarity. It is trivially + * eliminated by the compiler. There is no need to sacrifice clarity for + * the sake of "performance". + */ + if ((session->features & SMTP_FEATURE_DSN) == 0 + && (state->misc_flags & SMTP_MISC_FLAG_USE_LMTP) != 0 + && var_lmtp_assume_final != 0) + dsn_action = "delivered"; + + /* + * Report success and delete the recipient from the delivery request. + * Defer if the success can't be reported. + * + * Note: the DSN action is ignored in case of address probes. + */ + dsb_update(why, resp->dsn, dsn_action, DSB_MTYPE_DNS, session->host, DSB_DTYPE_SMTP, resp->str, "%s", resp->str); status = sent(DEL_REQ_TRACE_FLAGS(request->flags), diff --git a/postfix/src/smtpd/smtpd.c b/postfix/src/smtpd/smtpd.c index 5c9e78f4f..c5dcc8921 100644 --- a/postfix/src/smtpd/smtpd.c +++ b/postfix/src/smtpd/smtpd.c @@ -2248,6 +2248,7 @@ static void mail_reset(SMTPD_STATE *state) { state->msg_size = 0; state->act_size = 0; + state->flags &= SMTPD_MASK_MAIL_KEEP; /* * Unceremoniously close the pipe to the cleanup service. The cleanup @@ -2861,6 +2862,7 @@ static int data_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *unused_argv) #endif } smtpd_chat_reply(state, "354 End data with ."); + state->where = SMTPD_AFTER_DATA; /* * Copy the message content. If the cleanup process has a problem, keep @@ -4119,6 +4121,7 @@ typedef struct SMTPD_CMD { #define SMTPD_CMD_FLAG_LIMIT (1<<0) /* limit usage */ #define SMTPD_CMD_FLAG_PRE_TLS (1<<1) /* allow before STARTTLS */ +#define SMTPD_CMD_FLAG_LAST (1<<2) /* last in PIPELINING command group */ static SMTPD_CMD smtpd_cmd_table[] = { SMTPD_CMD_HELO, helo_cmd, SMTPD_CMD_FLAG_LIMIT | SMTPD_CMD_FLAG_PRE_TLS, @@ -4131,14 +4134,14 @@ static SMTPD_CMD smtpd_cmd_table[] = { #endif SMTPD_CMD_MAIL, mail_cmd, 0, SMTPD_CMD_RCPT, rcpt_cmd, 0, - SMTPD_CMD_DATA, data_cmd, 0, + SMTPD_CMD_DATA, data_cmd, SMTPD_CMD_FLAG_LAST, SMTPD_CMD_RSET, rset_cmd, SMTPD_CMD_FLAG_LIMIT, SMTPD_CMD_NOOP, noop_cmd, SMTPD_CMD_FLAG_LIMIT | SMTPD_CMD_FLAG_PRE_TLS, SMTPD_CMD_VRFY, vrfy_cmd, SMTPD_CMD_FLAG_LIMIT, SMTPD_CMD_ETRN, etrn_cmd, SMTPD_CMD_FLAG_LIMIT, SMTPD_CMD_QUIT, quit_cmd, SMTPD_CMD_FLAG_PRE_TLS, - SMTPD_CMD_XCLIENT, xclient_cmd, SMTPD_CMD_FLAG_LIMIT, - SMTPD_CMD_XFORWARD, xforward_cmd, SMTPD_CMD_FLAG_LIMIT, + SMTPD_CMD_XCLIENT, xclient_cmd, 0, + SMTPD_CMD_XFORWARD, xforward_cmd, 0, 0, }; @@ -4321,7 +4324,55 @@ static void smtpd_proto(SMTPD_STATE *state) smtpd_chat_reply(state, "421 %s Service unavailable - try again later", var_myhostname); /* Not: state->error_count++; */ +#ifdef notdef + } else if (strcmp(state->name, "unknown") == 0) { + static char *greet_chunks[] = { + "220 ", 0, " ESMTP ", 0, 0, + }; + char **cpp; + char *cp; + + greet_chunks[1] = var_myhostname; + greet_chunks[3] = var_mail_name; + for (cpp = greet_chunks; *cpp; cpp++) { + for (cp = *cpp; *cp; cp++) + smtp_fputc(*(unsigned char *) cp, state->client); + smtp_flush(state->client); + if (read_wait(vstream_fileno(state->client), 2) == 0) { + smtpd_chat_query(state); + msg_info("PREGREET from %s: %s", + state->namaddr, vstring_str(state->buffer)); + state->error_mask |= MAIL_ERROR_POLICY; + smtpd_chat_reply(state, + "521 %s ESMTP not accepting connections", + var_myhostname); + /* Not: state->error_count++; */ + break; + } + } + smtp_fputs("", 0, state->client); + smtp_flush(state->client); +#endif } else { +#ifdef PREGREET + if (*var_stress == 0 && strcmp(state->name, "unknown") == 0) { + smtpd_chat_reply(state, "220-%s", var_smtpd_banner); + smtp_flush(state->client); + if (read_wait(vstream_fileno(state->client), 1) == 0) { + int n = peekfd(vstream_fileno(state->client)); + + smtpd_chat_query(state); + msg_info("PREGREET %d from %s: %s", + n, state->namaddr, vstring_str(state->buffer)); + state->error_mask |= MAIL_ERROR_POLICY; + smtpd_chat_reply(state, + "521 %s ESMTP not accepting connections", + var_myhostname); + /* Not: state->error_count++; */ + break; + } + } +#endif smtpd_chat_reply(state, "220 %s", var_smtpd_banner); } } @@ -4427,6 +4478,16 @@ static void smtpd_proto(SMTPD_STATE *state) } #endif state->where = cmdp->name; + if (SMTPD_STAND_ALONE(state) == 0 + && (strcasecmp(state->protocol, MAIL_PROTO_ESMTP) != 0 + || (cmdp->flags & SMTPD_CMD_FLAG_LAST)) + && (state->flags & SMTPD_FLAG_ILL_PIPELINING) == 0 + && (vstream_peek(state->client) > 0 + || peekfd(vstream_fileno(state->client)) > 0)) { + msg_info("improper command pipelining after %s from %s", + cmdp->name, state->namaddr); + state->flags |= SMTPD_FLAG_ILL_PIPELINING; + } if (cmdp->action(state, argc, argv) != 0) state->error_count++; if ((cmdp->flags & SMTPD_CMD_FLAG_LIMIT) @@ -4463,9 +4524,9 @@ static void smtpd_proto(SMTPD_STATE *state) * troubles. */ if (state->reason && state->where) { - if (strcmp(state->where, SMTPD_CMD_DATA) == 0) { - msg_info("%s after %s (approximately %lu bytes) from %s", - state->reason, state->where, + if (strcmp(state->where, SMTPD_AFTER_DATA) == 0) { + msg_info("%s after %s (%lu bytes) from %s", /* 2.5 compat */ + state->reason, SMTPD_CMD_DATA, /* 2.5 compat */ (long) (state->act_size + vstream_peek(state->client)), state->namaddr); } else if (strcmp(state->where, SMTPD_AFTER_DOT) @@ -4829,13 +4890,13 @@ MAIL_VERSION_STAMP_DECLARE; int main(int argc, char **argv) { static const CONFIG_NINT_TABLE nint_table[] = { - VAR_SMTPD_RCPT_LIMIT, DEF_SMTPD_RCPT_LIMIT, &var_smtpd_rcpt_limit, 1, 0, VAR_SMTPD_SOFT_ERLIM, DEF_SMTPD_SOFT_ERLIM, &var_smtpd_soft_erlim, 1, 0, VAR_SMTPD_HARD_ERLIM, DEF_SMTPD_HARD_ERLIM, &var_smtpd_hard_erlim, 1, 0, VAR_SMTPD_JUNK_CMD, DEF_SMTPD_JUNK_CMD, &var_smtpd_junk_cmd_limit, 1, 0, 0, }; static const CONFIG_INT_TABLE int_table[] = { + VAR_SMTPD_RCPT_LIMIT, DEF_SMTPD_RCPT_LIMIT, &var_smtpd_rcpt_limit, 1, 0, VAR_QUEUE_MINFREE, DEF_QUEUE_MINFREE, &var_queue_minfree, 0, 0, VAR_UNK_CLIENT_CODE, DEF_UNK_CLIENT_CODE, &var_unk_client_code, 0, 0, VAR_BAD_NAME_CODE, DEF_BAD_NAME_CODE, &var_bad_name_code, 0, 0, diff --git a/postfix/src/smtpd/smtpd.h b/postfix/src/smtpd/smtpd.h index cef00062f..fc7ac56ed 100644 --- a/postfix/src/smtpd/smtpd.h +++ b/postfix/src/smtpd/smtpd.h @@ -180,7 +180,10 @@ typedef struct SMTPD_STATE { ssize_t milter_argc; } SMTPD_STATE; -#define SMTPD_FLAG_HANGUP (1<<0) /* disconnect */ +#define SMTPD_FLAG_HANGUP (1<<0) /* 421/521 disconnect */ +#define SMTPD_FLAG_ILL_PIPELINING (1<<1) /* inappropriate pipelining */ + +#define SMTPD_MASK_MAIL_KEEP ~0 /* keep all after MAIL reset */ #define SMTPD_STATE_XFORWARD_INIT (1<<0) /* xforward preset done */ #define SMTPD_STATE_XFORWARD_NAME (1<<1) /* client name received */ @@ -204,6 +207,7 @@ extern void smtpd_state_reset(SMTPD_STATE *); * diagnostics. */ #define SMTPD_AFTER_CONNECT "CONNECT" +#define SMTPD_AFTER_DATA "DATA content" #define SMTPD_AFTER_DOT "END-OF-MESSAGE" /* diff --git a/postfix/src/smtpd/smtpd_chat.c b/postfix/src/smtpd/smtpd_chat.c index f39eaf1c6..e78f5936d 100644 --- a/postfix/src/smtpd/smtpd_chat.c +++ b/postfix/src/smtpd/smtpd_chat.c @@ -200,9 +200,10 @@ void smtpd_chat_reply(SMTPD_STATE *state, const char *format,...) vstream_longjmp(state->client, SMTP_ERR_EOF); /* - * Orderly disconnect in case of 421 reply. + * Orderly disconnect in case of 421 or 521 reply. */ - if (strncmp(STR(state->buffer), "421", 3) == 0) + if (strncmp(STR(state->buffer), "421", 3) == 0 + || strncmp(STR(state->buffer), "521", 3) == 0) state->flags |= SMTPD_FLAG_HANGUP; } diff --git a/postfix/src/smtpd/smtpd_check.c b/postfix/src/smtpd/smtpd_check.c index 70428d546..cbe8c69ed 100644 --- a/postfix/src/smtpd/smtpd_check.c +++ b/postfix/src/smtpd/smtpd_check.c @@ -1421,17 +1421,12 @@ static int reject_unauth_pipelining(SMTPD_STATE *state, if (msg_verbose) msg_info("%s: %s", myname, state->where); - if (state->client != 0 - && SMTPD_STAND_ALONE(state) == 0 - && (vstream_peek(state->client) > 0 - || peekfd(vstream_fileno(state->client)) > 0) - && (strcasecmp(state->protocol, MAIL_PROTO_ESMTP) != 0 - || strcasecmp(state->where, SMTPD_CMD_DATA) == 0)) { + if (state->flags & SMTPD_FLAG_ILL_PIPELINING) return (smtpd_check_reject(state, MAIL_ERROR_PROTOCOL, 503, "5.5.0", "<%s>: %s rejected: Improper use of SMTP command pipelining", reply_name, reply_class)); - } + return (SMTPD_CHECK_DUNNO); } diff --git a/postfix/src/smtpd/smtpd_proxy.c b/postfix/src/smtpd/smtpd_proxy.c index c2fa84102..cfefe7e3d 100644 --- a/postfix/src/smtpd/smtpd_proxy.c +++ b/postfix/src/smtpd/smtpd_proxy.c @@ -564,11 +564,23 @@ int smtpd_proxy_cmd(SMTPD_STATE *state, int expect, const char *fmt,...) /* * Log a warning in case the proxy does not send the expected response. * Silently accept any response when the client expressed no expectation. + * + * Don't pass through misleading 2xx replies. it confuses naive users and + * SMTP clients, and creates support problems. */ if (expect != SMTPD_PROX_WANT_ANY && expect != *STR(state->proxy_buffer)) { va_start(ap, fmt); smtpd_proxy_cmd_error(state, fmt, ap); va_end(ap); + if (*STR(state->proxy_buffer) == SMTPD_PROX_WANT_OK + || *STR(state->proxy_buffer) == SMTPD_PROX_WANT_MORE) { + state->error_mask |= MAIL_ERROR_SOFTWARE; + state->err |= CLEANUP_STAT_PROXY; + detail = cleanup_stat_detail(CLEANUP_STAT_PROXY); + vstring_sprintf(state->proxy_buffer, + "%d %s Error: %s", + detail->smtp, detail->dsn, detail->text); + } return (-1); } else { return (0); diff --git a/postfix/src/util/inet_listen.c b/postfix/src/util/inet_listen.c index a9153ac75..ad6dd82c0 100644 --- a/postfix/src/util/inet_listen.c +++ b/postfix/src/util/inet_listen.c @@ -77,11 +77,6 @@ #include "sock_addr.h" #include "inet_proto.h" - /* - * Tunable to work around broken routers. - */ -int inet_windowsize = 0; - /* inet_listen - create TCP listener */ int inet_listen(const char *addr, int backlog, int block_mode) diff --git a/postfix/src/util/inet_windowsize.c b/postfix/src/util/inet_windowsize.c index cbe3a8b93..ae2b170f4 100644 --- a/postfix/src/util/inet_windowsize.c +++ b/postfix/src/util/inet_windowsize.c @@ -53,7 +53,10 @@ /* Application storage. */ -int inet_windowsize; + /* + * Tunable to work around broken routers. + */ +int inet_windowsize = 0; /* set_inet_windowsize - set TCP send/receive window size */ diff --git a/postfix/src/verify/verify.c b/postfix/src/verify/verify.c index bf1680cc2..803998428 100644 --- a/postfix/src/verify/verify.c +++ b/postfix/src/verify/verify.c @@ -141,7 +141,7 @@ /* The location of the Postfix top-level queue directory. /* .IP "\fBsyslog_facility (mail)\fR" /* The syslog facility of Postfix logging. -/* .IP "\fBsyslog_name (postfix)\fR" +/* .IP "\fBsyslog_name (see 'postconf -d' output)\fR" /* The mail system name that is prepended to the process name in syslog /* records, so that "smtpd" becomes, for example, "postfix/smtpd". /* SEE ALSO diff --git a/postfix/src/xsasl/xsasl_cyrus_server.c b/postfix/src/xsasl/xsasl_cyrus_server.c index d3b802107..a86602d65 100644 --- a/postfix/src/xsasl/xsasl_cyrus_server.c +++ b/postfix/src/xsasl/xsasl_cyrus_server.c @@ -307,7 +307,7 @@ static XSASL_SERVER *xsasl_cyrus_server_create(XSASL_SERVER_IMPL *unused_impl, /* * Don't give any IP address information to SASL. SASLv1 doesn't use it, - * and in SASLv2 this will disable any mechaniams that do. + * and in SASLv2 this will disable any mechanisms that do. */ server_address = 0; client_address = 0;