diff --git a/postfix/.indent.pro b/postfix/.indent.pro index 21572a2b3..fbd8874f6 100644 --- a/postfix/.indent.pro +++ b/postfix/.indent.pro @@ -14,6 +14,7 @@ -TBOUNCE_INFO -TBOUNCE_LOG -TBOUNCE_STAT +-TCFG_PARSER -TCLEANUP_STATE -TCLIENT_LIST -TCLNT_STREAM diff --git a/postfix/HISTORY b/postfix/HISTORY index 316d12058..132ad8b09 100644 --- a/postfix/HISTORY +++ b/postfix/HISTORY @@ -8958,6 +8958,46 @@ Apologies for any names omitted. like reject_unlisted_recipient, but will eventually be removed from Postfix. +20040102 + + Misc documentation cleanup by Loic Minier. + +20040104 + + Workaround: MacOSX dumps core on the 20030913 TZ censoring + code. We explictly set TZ=UTC, which will produce incorrect + results when "mailq" formatting is moved from the showq + daemon to the postqueue command. File: msg_syslog.c. + + Feature: after mail is requeued with "postsuper -r", the + pickup server logs the old queue ID together with the new + queue ID. Victor Duchovni. File: pickup/pickup.c. + + Feature: smtpd_sasl_application_name parameter (default: + smtpd) to control the name of the SASL configuration file + used by the Postfix SMTP server. Liviu Daia. Files: + mail_params.h, smtpd.c, smtpd_sasl_glue.c. + + Cleanup: shared config file parser for ldap, mysql, pgsql + lookup tables. Liviu Daia. Files: global/cfgparser.[hc], + global/dict_ldap.c, global/dict_mtsql.c, global/dict_pgsql.c + and documentation. + + Cleanup: moved modules with dependencies on higher-level + code from the util directory to the global directory: + util/dict_open.c, global/cfgparser.[hc], global/dict_ldap.c, + global/dict_mtsql.c, global/dict_pgsql.c, global/mail_dict.c. + + Cleanup: the new queue manager nqmgr replaces the default + queue manager qmgr, leaving behind a hard link for backwards + compatibility. The old queue manager remains available as + as oqmgr but will eventually be removed. + + Bugfix: vstring_get() etc. now return VSTREAM_EOF when they + terminate prematurely, instead of returning the last + character stored. This avoids mis-leading warnings. File: + global/vstring_vstream.c. + Open problems: Low: in the SMTP client, pass the session, request and diff --git a/postfix/INSTALL b/postfix/INSTALL index 4372df0ae..d544ddc50 100644 --- a/postfix/INSTALL +++ b/postfix/INSTALL @@ -67,11 +67,13 @@ At some point in time, a version of Postfix was supported on: Linux Debian 1.3.1 Linux Debian 2.x Linux Debian 3.x - Linux RedHat 3.x (August 2002) + Linux RedHat 3.x (Januway 2004, compile-only test) Linux RedHat 4.x Linux RedHat 5.x Linux RedHat 6.x Linux RedHat 7.x + Linux RedHat 8.x + Linux RedHat 9.x Linux Slackware 3.x Linux Slackware 4.x Linux Slackware 7.x @@ -89,8 +91,8 @@ At some point in time, a version of Postfix was supported on: OpenBSD 3.x Reliant UNIX 5.x Rhapsody 5.x - SunOS 4.1.x (December 2002) - SunOS 5.4..5.8 (Solaris 2.4..8) + SunOS 4.1.4 (January 2004, running as MTA) + SunOS 5.4..5.9 (Solaris 2.4..9) Ultrix 4.x (well, that was long ago) or something closely resemblant. diff --git a/postfix/Makefile.in b/postfix/Makefile.in index def06ceec..53aa735bc 100644 --- a/postfix/Makefile.in +++ b/postfix/Makefile.in @@ -3,10 +3,10 @@ WARN = -Wmissing-prototypes -Wformat OPTS = 'CC=$(CC)' DIRS = src/util src/global src/dns src/master src/postfix src/smtpstone \ src/sendmail src/error src/pickup src/cleanup src/smtpd src/local \ - src/lmtp src/trivial-rewrite src/qmgr src/smtp src/bounce src/pipe \ - src/showq src/postalias src/postcat src/postconf src/postdrop \ + src/lmtp src/trivial-rewrite src/qmgr src/oqmgr src/smtp src/bounce \ + 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/nqmgr src/qmqpd src/spawn src/flush src/verify \ + src/postsuper src/qmqpd src/spawn src/flush src/verify \ src/virtual src/proxymap src/anvil MANDIRS = proto man html diff --git a/postfix/README_FILES/ADDRESS_VERIFICATION_README b/postfix/README_FILES/ADDRESS_VERIFICATION_README index e14ec7ec4..1813f58af 100644 --- a/postfix/README_FILES/ADDRESS_VERIFICATION_README +++ b/postfix/README_FILES/ADDRESS_VERIFICATION_README @@ -1,3 +1,10 @@ +WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING +=============================================================== + +The sender/recipient address verification feature described in this +document is suitable only for low-traffic sites. It performs poorly +under high load. See the "Limitations" section below for details. + Address verification ==================== diff --git a/postfix/README_FILES/FILTER_README b/postfix/README_FILES/FILTER_README index 99e9e5fe3..24ea183db 100644 --- a/postfix/README_FILES/FILTER_README +++ b/postfix/README_FILES/FILTER_README @@ -6,9 +6,9 @@ then delivers it. With the external content filter described here, mail is filtered AFTER it is queued. This gives you maximal control over how many filtering processes you are willing to run in parallel. -[This is not to be confused with the approach that is described in +This is not to be confused with the approach that is described in the SMTPD_PROXY_README document, where SMTP mail is filtered BEFORE -it is queued] +it is queued. An external content filter receives unfiltered mail from Postfix and does one of the following: @@ -171,7 +171,8 @@ runs into a resource problem. This approach uses content filtering software that can receive and deliver mail via SMTP. Some Anti-virus software is built to receive and deliver mail via -SMTP and is ready to use as an advanced external content filter. +SMTP and is ready to use as an external content filter. + For non-SMTP capable content filtering software, Bennett Todd's SMTP proxy implements a nice PERL/SMTP content filtering framework. See: http://bent.latency.net/smtpprox/ @@ -203,15 +204,15 @@ Postfix via localhost port 10026. | v ....|............ : | 10025 : - : filter : + : filter : : : ................. To enable content filtering in this manner, specify in main.cf: - /etc/postfix/main.cf: - content_filter = scan:localhost:10025 - receive_override_options = no_address_mappings +/etc/postfix/main.cf: + content_filter = scan:localhost:10025 + receive_override_options = no_address_mappings The first line causes Postfix to add one extra content filtering record to each incoming mail message, with content scan:localhost:10025. @@ -223,41 +224,45 @@ so that the content filter sees the original mail addresses instead of the result of virtual alias expansion, canonical mapping, address masquerading, etc. -When a queue file has content filtering information, the queue -manager will deliver the mail to the specified content filter -regardless of its final destination. +Content filtering information is stored in queue files; this is +how Postfix keeps track of what mail needs filtering. When a queue +file contains content filter information, the queue manager will +deliver the mail to the specified content filter regardless of its +final destination. In this example, "scan" is an instance of the Postfix SMTP client with slightly different configuration parameters. This is how one would set up the service in the Postfix master.cf file: - /etc/postfix/master.cf: - scan unix - - n - 10 smtp - -o myhostname=localhost.my.domain - # -o smtp_send_xclient_command=yes - -The "-o myhostname=localhost.domain.tld" overrides main.cf and -avoids false alarms ("host greeted me with my own -hostname") that result in mail being bounced. +/etc/postfix/master.cf: + # ============================================================= + # service type private unpriv chroot wakeup maxproc command + # (yes) (yes) (yes) (never) (100) + # ============================================================= + scan unix - - n - 10 smtp + -o smtp_send_xforward_command=yes Instead of a limit of 10 concurrent processes, use whatever process limit is feasible for your machine. Content inspection software can gobble up a lot of system resources, so you don't want to have too much of it running at the same time. -Uncomment (but keep the leading white-space) the option setting: -"smtp_send_xclient_command=yes", if you would like the scan transport -to forward the original client name and IP address to the after-filter -smtpd so that filtered mail is logged with the real client name IP -address. See sample-smtp.cf and smtp(8). +With "-o smtp_send_xforward_command=yes", the scan transport will +try to forward the original client name and IP address to the +after-filter smtpd so that filtered mail is logged with the real +client name IP address. See sample-smtp.cf and smtp(8). The content filter can be set up with the Postfix spawn service, which is the Postfix equivalent of inetd. For example, to instantiate up to 10 content filtering processes on demand: - /etc/postfix/master.cf: - localhost:10025 inet n n n - 10 spawn - user=filter argv=/some/where/filter localhost 10026 +/etc/postfix/master.cf: + # =================================================================== + # service type private unpriv chroot wakeup maxproc command + # (yes) (yes) (yes) (never) (100) + # =================================================================== + localhost:10025 inet n n n - 10 spawn + user=filter argv=/some/where/filter localhost 10026 "filter" is a dedicated local user account. The user will never log in, and can be given a "*" password and non-existent shell and @@ -269,12 +274,6 @@ you want to have your filter listening on port localhost:10025 instead of Postfix, then you must run your filter as a stand-alone program. -Note: the localhost port 10025 SMTP server filter should announce -itself as "220 localhost...". Postfix aborts delivery when it -connects to an SMTP server that uses the same hostname as Postfix -("host greeted me with my own hostname"), because that -normally means you have a mail delivery loop problem. - The example here assumes that the /some/where/filter command is a PERL script. PERL has modules that make talking SMTP easy. The command-line specifies that mail should be sent back into Postfix @@ -290,15 +289,20 @@ The job of the content filter is to either bounce mail with a suitable diagnostic, or to feed the mail back into Postfix through a dedicated listener on port localhost 10026: - /etc/postfix/master.cf: - localhost:10026 inet n - n - 10 smtpd - -o content_filter= - -o receive_override_options=no_unknown_recipient_checks,no_header_body_checks - -o smtpd_helo_restrictions= - -o smtpd_client_restrictions= - -o smtpd_sender_restrictions= - -o smtpd_recipient_restrictions=permit_mynetworks,reject - -o mynetworks=127.0.0.0/8 +/etc/postfix/master.cf: + # =================================================================== + # service type private unpriv chroot wakeup maxproc command + # (yes) (yes) (yes) (never) (100) + # =================================================================== + localhost:10026 inet n - n - 10 smtpd + -o content_filter= + -o receive_override_options=no_unknown_recipient_checks,no_header_body_checks + -o smtpd_helo_restrictions= + -o smtpd_client_restrictions= + -o smtpd_sender_restrictions= + -o smtpd_recipient_restrictions=permit_mynetworks,reject + -o mynetworks=127.0.0.0/8 + -o smtpd_authorized_xforward_hosts=127.0.0.0/8 Note: do not use spaces around the "=" or "," characters. @@ -309,12 +313,18 @@ The "-o content_filter=" overrides main.cf and requests no content filtering for incoming mail. This is required or else mail will stay in the content filtering loop. -The "-o receive_override_options" line overrides main.cf and turns -off table lookups that were already done before the content filter: -attempts to find out if a recipient is unknown, and header/body -checks that can suck up lots of CPU cycles. These override options -are either implemented by the SMTP server itself, or they are passed -on to the cleanup server. +The "-o receive_override_options" overrides main.cf. It is +complementary to the the options that are specified in main.cf: + +- Disable attempts to find out if a recipient is unknown, and + disable header/body checks. This work was already done before + the content filter and repeating it would be wasteful. + +- Enable virtual alias expansion, canonical mappings, address + masquerading, and other address mappings. + +These receive override options are either implemented by the SMTP +server itself, or they are passed on to the cleanup server. The "-o smtpd_xxx_restrictions" and "-o mynetworks=127.0.0.0/8" override main.cf and turn off UCE controls that would only waste @@ -367,14 +377,12 @@ and cleanup daemons or else you will have a content filtering loop. Limitations: -- There can be only one content filter action per message. - - FILTER actions from smtpd access maps and header/body_checks take precedence over filters specified with the main.cf content_filter parameter. -- Only the last FILTER action from smtpd access maps or from - header/body_checks takes effect. +- If a message triggers more than one filter action, only the last + one takes effect. - The same content filter is applied to all the recipients of a given message. diff --git a/postfix/README_FILES/MYSQL_README b/postfix/README_FILES/MYSQL_README index c8004c1e1..3d68a5cec 100644 --- a/postfix/README_FILES/MYSQL_README +++ b/postfix/README_FILES/MYSQL_README @@ -67,6 +67,28 @@ hosts = host1.some.domain host2.some.domain unix:/file/name # end mysql config file +Alternatively, these parameters can be defined in main.cf. To that +effect, the map should be specified as "mysql:name", and the parameters +should be prefixed with the name you've given the map in its definition, +and an underscore. For example, the above settings can also be written +in main.cf as: + +# mysql definitions in main.cf + +alias_maps = mysql:mysqlsource + +mysqlsource_user = someone +mysqlsource_password = some_password +mysqlsource_dbname = customer_database +mysqlsource_table = mxaliases +mysqlsource_select_field = forw_addr +mysqlsource_where_field = alias +mysqlsource_additional_conditions = and status = 'paid' +mysqlsource_hosts = host1.some.domain host2.some.domain unix:/file/name + +# end mysql definitions + + Some notes: This configuration interface setup allows for multiple mysql diff --git a/postfix/README_FILES/PACKAGE_README b/postfix/README_FILES/PACKAGE_README index 11113515b..f92983822 100644 --- a/postfix/README_FILES/PACKAGE_README +++ b/postfix/README_FILES/PACKAGE_README @@ -27,9 +27,8 @@ Postfix Installation parameters Postfix installation is controlled by a dozen installation parameters. See the postfix-install and post-install files for details. Most -parameters have system-dependent default settings that aren't -configurable at compile time. This will hopefully be rectified in -a later release. +parameters have system-dependent default settings that are configurable +at compile time, as described in the INSTALL file. Preparing a pre-built package for distribution to other systems =============================================================== @@ -38,10 +37,9 @@ You can build a Postfix package on a machine that does not have Postfix installed on it. All you need is Postfix source code and a compilation environment that is compatible with the target system. -You can build a pre-built Postfix package as an unprivileged user, -but the result will be cleaner if you build the package as root. +You can build a pre-built Postfix package as an unprivileged user. -After successful Postfix compilation, execute: +First compile Postfix. After successful compilation, execute: % sh postfix-install @@ -73,7 +71,7 @@ Thus, to tar up the pre-built package, take the following steps: % cd INSTALL_ROOT % rm -f SOMEWHERE/outputfile - % find . \! -type d -print | xargs tar rf SOMEWHERE/outputfile + % find . \! -type d -print | xargs tar cf SOMEWHERE/outputfile % gzip SOMEWHERE/outputfile This way you will not include any directories that might cause trouble diff --git a/postfix/README_FILES/PGSQL_README b/postfix/README_FILES/PGSQL_README index 623ca3820..bdcd5e0d9 100644 --- a/postfix/README_FILES/PGSQL_README +++ b/postfix/README_FILES/PGSQL_README @@ -80,15 +80,28 @@ additional_conditions = and status = 'paid' # ($lookup is escaped so if it contains single quotes or other odd # characters, it will not cause a parse error in the sql). +# Alternatively, you can override the default SELECT statement (and the +# above table, select_field, where_field, and additional_conditions) by +# specifying the query: + +#query = select forw_addr from mxaliases where alias = '%s' and status = 'paid' + +# Before the query is actually issued, all occurences of %s are replaced +# with the address to look up, %u are replaced with the user portion, +# and %d with the domain portion. + # If you just want to use a PostgreSQL function, you can ignore the -# table name, select_field, where_field and additional_conditions, -# and just specify a database function to call: +# table name, select_field, where_field and additional_conditions, and +# just specify a database function to call: #select_function = my_lookup_user_alias -# this will result in "select my_lookup_user_alias('name')" being -# used as the SQL statement to execute. If select_function is specified -# the table-related fields above will be ignored. +# This is equivalent to: +# +#query = select my_lookup_user_alias('%s') +# +# and overrides both the query parameter and the table-related fields +# above. # # As of 25-Jun-2002, if the function returns a single row and a single # column AND that value is NULL, then the result will be treated as @@ -105,6 +118,29 @@ hosts = host1.some.domain host2.some.domain unix:/file/name # end pgsql config file +Alternatively, these parameters can be defined in main.cf. To that +effect, the map should be specified as "pgsql:name", and the parameters +should be prefixed with the name you've given the map in its definition, +and an underscore. For example, the above settings can also be written +in main.cf as: + +# pgsql definitions in main.cf + +alias_maps = pgsql:pgsqlsource + +pgsqlsource_user = someone +pgsqlsource_password = some_password +pgsqlsource_dbname = customer_database +pgsqlsource_table = mxaliases +pgsqlsource_select_field = forw_addr +pgsqlsource_where_field = alias +pgsqlsource_additional_conditions = and status = 'paid' +#pgsqlsource_select_function = my_lookup_user_alias +pgsqlsource_hosts = host1.some.domain host2.some.domain unix:/file/name + +# end mysql definitions + + Using mirrored databases ======================== diff --git a/postfix/README_FILES/SCHEDULER_README b/postfix/README_FILES/SCHEDULER_README index ebfff51e6..2d7fa28bd 100644 --- a/postfix/README_FILES/SCHEDULER_README +++ b/postfix/README_FILES/SCHEDULER_README @@ -2,12 +2,14 @@ What this file is about ======================= This is the beginning of documentation for the clever queue manager -scheduling algorithm by Patrik Rak. For too much time, this code -has been made available in the nqmgr queue manager as an optional -module. +scheduling algorithm by Patrik Rak. For a long time, this code was +made available under the name "nqmgr" (new queue manager), as an +optional module. It now become the default queue manager, which is +always called "qmgr". The old queue manager will for some time will +be available under the name of "oqmgr". -Why Postfix ships two queue managers -==================================== +Why the old Postfix queue manager was replaced +============================================== The old Postfix scheduler had several limitations due to unfortunate choices in its design. @@ -31,15 +33,6 @@ choices in its design. large delays. Patrik Rak's scheduler allows mail with fewer recipients to slip past bulk mail in an elegant manner. -If this newer scheduler is so much better, why does Postfix still -ship the old one? The problem is that there isn't a whole lot of -documentation on how Patrik's code works, so that no-one except -Patrik understands how it works. - -This document is the start of something that will help to clarify -things. Once enough documentation exists we can end the embarassing -situation of shipping two queue managers. - How the queue manager scheduler works ===================================== @@ -92,7 +85,7 @@ into core in batches and which is triggered at appropriate moments. 4) Doing things efficiently: All important things I am aware of are done in the minimum time possible (either directly or at least -when ammortized complexity is used), but to choose which job is +when amortized complexity is used), but to choose which job is the best candidate for preempting the current job requires linear search of up to all transport jobs (the worst theoretical case - the reality is much better). As this is done every time the next diff --git a/postfix/README_FILES/SMTPD_POLICY_README b/postfix/README_FILES/SMTPD_POLICY_README index e59db0fd9..b22693dbc 100644 --- a/postfix/README_FILES/SMTPD_POLICY_README +++ b/postfix/README_FILES/SMTPD_POLICY_README @@ -47,9 +47,9 @@ server sends in a delegated SMTPD access policy request: The following is specific to SMTPD delegated policy requests: - Protocol names are ESMTP or SMTP. -- Protocol states are CONNECT, EHLO, HELO, MAIL, RCPT, DATA or - ETRN; these are the SMTP protocol states where the Postfix SMTP - server makes an OK/REJECT/HOLD/etc. decision. +- Protocol states are CONNECT, EHLO, HELO, MAIL, RCPT, DATA, VRFY + or ETRN; these are the SMTP protocol states where the Postfix + SMTP server makes an OK/REJECT/HOLD/etc. decision. The policy server replies with any action that is allowed in a Postfix SMTPD access table. Example: @@ -57,8 +57,11 @@ Postfix SMTPD access table. Example: action=450 Service temporarily unavailable [empty line] -In case of trouble the server must log a warning and disconnect. -Postfix will retry the request at some later time. +This causes Postfix to reject the request with a 450 temporary +error code and with text "Service temporarily unavailable". + +In case of trouble the policy server must log a warning and +disconnect. Postfix will retry the request at some later time. CLIENT SIDE CONFIGURATION ========================= @@ -93,9 +96,10 @@ or else your system could become an open relay. NOTE: Postfix by default kills a command after 1000 seconds. This is too short for a policy daemon that may run for as long as an -SMTP client is connected to an SMTP server process. +SMTP client is connected to an SMTP server process. The default +time limit is overruled with an explicit policy_time_limit setting. -NOTE: Solaris UNIX-domain sockets do not work very well. Use TCP +NOTE: Solaris UNIX-domain sockets do not work reliably. Use TCP sockets instead: /etc/postfix/main.cf: @@ -109,15 +113,17 @@ sockets instead: Other client-side configuration parmeters: - smtpd_policy_service_max_idle (default: 300s): the amount of time - before Postfix closes an unused policy connection. The socket is - closed while the SMTP server waits for new a SMTP client. + before the Postfix SMTP server closes an unused policy client + connection. The socket is closed while the SMTP server waits + for new a SMTP client. - smtpd_policy_service_max_ttl (default: 1000s): the amount of time - before Postfix closes an active policy connection. The socket - is closed while the SMTP server waits for new a SMTP client. + before the Postfix SMTP server closes an active policy client + connection. The socket is closed while the SMTP server waits for + new a SMTP client. - smtpd_policy_service_timeout (default: 100s): the time limit to - connect to, send to or receive from a policy service. + connect to, send to or receive from a policy server. EXAMPLE: GREYLIST POLICY SERVER =============================== @@ -125,11 +131,11 @@ EXAMPLE: GREYLIST POLICY SERVER The file examples/smtpd-policy/smtpd-policy.pl in the Postfix source tree implements an example greylist policy server. This server stores a time stamp for every (client, sender, recipient) triple. -By default, mail is not accepted until a triple's time stamp is -more than 3600 seconds old. This stops junk mail with randomly -selected sender addresses, and mail that is sent through randomly -selected open proxies. It also stops junk mail from spammers that -change their IP address frequently. +By default, mail is not accepted until a time stamp is more than +60 seconds old. This stops junk mail with randomly selected sender +addresses, and mail that is sent through randomly selected open +proxies. It also stops junk mail from spammers that change their +IP address frequently. Copy examples/smtpd-policy/smtpd-policy.pl to /usr/libexec/postfix or whatever location is appropriate for your system. @@ -171,11 +177,12 @@ Greylisting mail from frequently forged domains It is relatively safe to turn on greylisting for specific domains that often appear in forged email. However, sites like AOL may -blacklist you when they find that you are probing them too often -and/or if you're probing them too often for non-existent addresses. +blacklist you when they find that you are probing them too often, +or when you're probing them too often for non-existent addresses. /etc/postfix/main.cf: smtpd_recipient_restrictions = + reject_unlisted_recipient ... reject_unauth_destination check_sender_access hash:/etc/postfix/sender_access @@ -193,6 +200,10 @@ and/or if you're probing them too often for non-existent addresses. Be sure to specify check_sender_access AFTER reject_unauth_destination or else your system could become an open mail relay. +The greylist database gets polluted quickly with bogus addresses. +It can help if you protect greylist lookups with restrictions that +reject unknown senders and/or recipients. + A list of frequently forged MAIL FROM domains can be found at http://www.monkeys.com/anti-spam/filtering/sender-domain-validate.in @@ -206,6 +217,7 @@ database relatively quickly. /etc/postfix/main.cf: smtpd_recipient_restrictions = + reject_unlisted_recipient ... reject_unauth_destination check_sender_access hash:/etc/postfix/sender_access @@ -221,6 +233,10 @@ Be sure to specify check_sender_access and check_policy_service AFTER reject_unauth_destination or else your system could become an open mail relay. +The greylist database gets polluted quickly with bogus addresses. +It can help if you protect greylist lookups with restrictions that +reject unknown senders and/or recipients. + ROUTINE MAINTENANCE =================== @@ -228,10 +244,11 @@ The greylist database grows over time, because the greylist server never removes database entries. If left unattended, the greylist database will eventually run your file system out of space. -When the status file exceeds some reasonable size you can simply -delete the file without adverse effects. In the worst case, new -mail will be delayed by one hour. To lessen the impact, delete the -file in the middle of the night during a weekend. +When the status file size exceeds some threshold you can simply +rename or remove the file without adverse effects. In the worst +case, new mail will be delayed by an hour or so. To lessen the +impact, rename or remove the file in the middle of the night at +the beginning of a weekend. SAMPLE POLICY ROUTINE ===================== @@ -245,7 +262,7 @@ This is the PERL subroutine that implements the example greylist policy. # that can run out of space. # $database_name="/var/mta/smtpd-policy.db"; -$greylist_delay=3600; +$greylist_delay=60; # # Demo SMTPD access policy routine. The result is an action just like @@ -259,8 +276,8 @@ sub smtpd_access_policy { open_database() unless $database_obj; # Lookup the time stamp for this client/sender/recipient. - $key = $attr{"client_address"}."/".$attr{"sender"}."/".$attr{"recipient"}; - $key =~ tr /A-Z/a-z/; + $key = + lc $attr{"client_address"}."/".$attr{"sender"}."/".$attr{"recipient"}; $time_stamp = read_database($key); $now = time(); @@ -278,6 +295,6 @@ sub smtpd_access_policy { if ($now - $time_stamp > $greylist_delay) { return "dunno"; } else { - return "defer_if_permit Service is unavailable"; + return "defer_if_permit Service temporarily unavailable"; } } diff --git a/postfix/README_FILES/SMTPD_PROXY_README b/postfix/README_FILES/SMTPD_PROXY_README index 3243ecee2..13065e9e6 100644 --- a/postfix/README_FILES/SMTPD_PROXY_README +++ b/postfix/README_FILES/SMTPD_PROXY_README @@ -1,3 +1,9 @@ +WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING +=============================================================== +The content filtering approach described in this document is suitable +only for low-traffic sites. It performs poorly under high load. See +the "Pros ans Cons" section below for details. + Purpose of the before-queue content filter feature ================================================== @@ -8,36 +14,19 @@ queue. The before-queue content filter is meant to be used as follows: - before / smtp - Internet -> smtpd -> queue -> smtpd -> cleanup -> queue -> local - Postfix filter Postfix \ virtual etc. - -Normally, Postfix receives mail, stores it in the mail queue and -then delivers it. The next diagram shows the normal path through -Postfix for mail that arrives via the network: - - / smtp - Internet -> smtpd -> cleanup -> queue -> local - Postfix \ virtual etc. + BEFORE / smtp + Internet -> smtpd -> QUEUE -> smtpd -> cleanup -> queue -> local + Postfix FILTER Postfix \ virtual etc. The before-queue content filter is not to be confused with the approach described in the FILTER_README document, where mail is -filtered AFTER it is stored in the Postfix mail queue. For example, -below is the FILTER_README approach with an SMTP-based after-queue -content filter: +filtered AFTER it is stored in the Postfix mail queue. - / smtp - Internet -> smtpd -> cleanup -> queue -> local - Postfix ^ v \ virtual etc. - smtpd smtp - Postfix Postfix - \ after / - queue <- - filter +Principles of operation +======================= -The before-queue content filter functions just like the after-queue -content filter. It receives unfiltered SMTP mail from Postfix and -does one of the following: +The filter receives unfiltered SMTP mail from Postfix and does one +of the following: 1 - Re-inject the mail back into Postfix via SMTP, perhaps after changing content. @@ -48,8 +37,96 @@ does one of the following: 3 - Send the mail somewhere else, or discard the mail. -Limitations -=========== +The before-queue content filter functions just like the after-queue +content filter. In many cases you can use the same content filtering +software, with some limitations as discussed in the "Pros and Cons" +section below. + +Using the SMTP pass-through proxy feature +========================================= + +In the following example, the Postfix SMTP server gives mail to a +content filter that listens on localhost port 10025, and receives +mail from the content filter via localhost port 10026. From then +on mail is processed as usual. + +The result looks as follows: + + Internet + -> Postfix SMTP server on port 25 + -> filter on localhost port 10025 + -> Postfix SMTP server on localhost port 26 + -> cleanup + -> queue + +This is configured by editing the master.cf file: + +/etc/postfix/master.cf: + # ============================================================= + # service type private unpriv chroot wakeup maxproc command + # (yes) (yes) (yes) (never) (100) + # ============================================================= + # + # Before-filter SMTP server. Receive mail from the network and + # pass it to the content filter on localhost port 10025. + # + smtp inet n - n - 20 smtpd + -o smtpd_proxy_filter=localhost:10025 + # + # After-filter SMTP server. Receive mail from the content filter + # on localhost port 10026. + # + :10026 inet n - n - - smtpd + -o smtpd_authorized_xforward_hosts=127.0.0.0/8 + -o smtpd_client_restrictions= + -o smtpd_helo_restrictions= + -o smtpd_sender_restrictions= + -o smtpd_recipient_restrictions=permit_mynetworks,reject + -o mynetworks=127.0.0.0/8 + -o receive_override_options=no_unknown_recipient_checks + +Note: do not specify spaces around the "=" or "," characters. + +The before-filter SMTP server entry is a modified version of the +default Postfix SMTP server entry that is normally configured at +the top of the master.cf file: + +- The number of SMTP sessions is reduced from the default 100 to + only 20. This prevents a burst of mail from running your system + into the ground with too many content filter processes. + +- The "-o smtpd_proxy_filter=localhost:10025" tells the before + filter SMTP server that it should give incoming mail to the + content filter that listens on localhost port 10025. + +The after-filter SMTP server is a new master.cf entry: + +- The ":10026" makes the after-filter SMTP server listen on the + localhost address only, without exposing it to the network. + NEVER expose the after-filter SMTP server to the Internet :-) + +- The "-o smtpd_authorized_xforward_hosts=127.0.0.0/8" allows the + after-filter SMTP server to receive remote SMTP client information + from the before filter SMTP server, so that the after-filter + Postfix daemons log the remote SMTP client information instead + of logging localhost[127.0.0.1]. + +- The other after-filter SMTP server settings avoid duplication of + work that is already done in the "before filter" SMTP server. + +The filter itself is not described here. You can use any filter +that is SMTP enabled. For non-SMTP capable content filtering +software, Bennett Todd's SMTP proxy implements a nice PERL/SMTP +content filtering framework. See: http://bent.latency.net/smtpprox/ + +By default, the filter has 100 seconds to do its work. If it takes +longer then Postfix gives up and reports an error to the remote +SMTP client. You can increase this time limit (see configuration +parameter section below) but doing so is pointless because you +can't control when the remote SMTP client times out. + +Pros and Cons +============= The before-queue content filter allows Postfix to reject mail before the incoming SMTP mail transfer completes, so that Postfix does @@ -62,13 +139,20 @@ load increases, fewer and fewer CPU cycles remain available to answer within the deadline, and eventually you either have to stop accepting mail or you have to stop filtering the mail. +Another problem is that content filtering software can use lots of +memory resources. In order to not run out of memory you have to +reduce the number of before-filter SMTP server processes so that +a burst of mail will not drive your system into the ground with +too many content filter processes. This, in turn, means that SMTP +clients have to wait for a long time before they receive service. + How Postfix talks to the before-queue content filter ================================================== The before-filter Postfix SMTP server connects to the content filter, delivers one message, and disconnects. While sending mail into the content filter, Postfix speaks ESMTP but uses no command -pipelining. Postfix generates its own EHLO, XCLIENT (for logging +pipelining. Postfix generates its own EHLO, XFORWARD (for logging the remote client IP address instead of localhost[127.0.0.1]), DATA and QUIT commands, and forwards unmodified copies of all the MAIL FROM and RCPT TO commands that the before-filter Postfix SMTP server @@ -110,49 +194,6 @@ smtpd_proxy_ehlo (default: $myhostname) The hostname to use when sending an EHLO command to the before-queue content filter. -Testing the SMTP pass-through proxy feature -=========================================== - -The following example sets up a null content filter, that is, the -Postfix SMTP server gives the mail directly to another Postfix SMTP -server process without intervening content filter. This useful only -for testing, of course. - -/etc/postfix/master.cf - smtp inet n - n - - smtpd - -o smtpd_proxy_filter=26 - :26 inet n - n - - smtpd - -o smtpd_authorized_xforward_hosts=127.0.0.0/8 - -o smtpd_client_restrictions= - -o smtpd_helo_restrictions= - -o smtpd_sender_restrictions= - -o smtpd_recipient_restrictions=permit_mynetworks,reject - -o mynetworks=127.0.0.0/8 - -o receive_override_options=no_unknown_recipient_checks - -Note: do not specify spaces around the "=" or "," characters. - -The ":26" causes Postfix to listen on the localhost address only. -DO NOT expose the secondary SMTP server to the Internet :-) - -The smtpd_authorized_xforward_hosts parameter allows the before -filter SMTP server to forward remote SMTP client information to -the after-filter SMTP server, so that the after-filter Postfix -daemons log the remote SMTP client information instead of logging -localhost[127.0.0.1]. - -The other parameter settings avoid duplication of effort that is -done in the "before filter" SMTP server. - -The result is as follows: - - Internet -> smtpd on port 25 -> smtpd on port 26 -> cleanup -> queue - -This configuration is sufficient for stress testing. - -Other suggestions for test configurations: use the Postfix smtp-sink -command as the proxy, or something as basic as netcat. - Transparency ============ diff --git a/postfix/README_FILES/UUCP_README b/postfix/README_FILES/UUCP_README index e6fbd56e4..6009a16c1 100644 --- a/postfix/README_FILES/UUCP_README +++ b/postfix/README_FILES/UUCP_README @@ -1,6 +1,8 @@ In order to receive mail via UUCP, your system needs to have an rmail command installed. A minimal rmail command can be found in -the "auxiliary/rmail" directory. Install the command, mode 755, in -a place that can be found by the UUCP "uuxqt" command. +the "auxiliary/rmail" directory of the Postfix source code +distribution. Install the command, mode 755, in a place that can +be found by the UUCP "uuxqt" command. -In order to send mail via UUCP, see html/faq.html. +In order to send mail via UUCP, see html/faq.html in the Postfix +source code distribution, or http://www.postfix.org/faq.html. diff --git a/postfix/README_FILES/VIRTUAL_MAILBOX_README b/postfix/README_FILES/VIRTUAL_MAILBOX_README new file mode 100644 index 000000000..44f2e1407 --- /dev/null +++ b/postfix/README_FILES/VIRTUAL_MAILBOX_README @@ -0,0 +1,372 @@ +This code was created by Andrew McNamara +and adapted to snapshot 20001121 by Xavier Beaudouin. It was merged +with mainstream Postfix for snapshot 20010128 by Wietse. + +Purpose of this software +======================== + +You can use the virtual delivery agent for mailbox delivery of some +or all domains that are handled by a machine. + +This mechanism is different from virtual alias domains. Those +are implemented by translating every recipient address into a +different address. For that, see the virtual(5) manual page. + +With the virtual delivery agent, every recipient adress can have +its own mailbox. There is no translation from recipient addresses +into different addresses. + +This is what Andrew McNamara wrote when he made the virtual delivery +agent available. + +"This code is designed for ISP's who offer virtual mail hosting. +It looks up the user mailbox location, uid and gid via separate +maps, and the mailbox location map can specify either mailbox or +maildir delivery (controlled by trailing slash on mailbox name). + +The agent does support user+foo address extensions, but does not +support aliases or .forward files (use the virtual table instead), +and therefore doesn't support file or program aliases. This choice +was made to simplify and streamline the code (it allowed me to +dispense with 70% of local's code - mostly the bits that are a +security headache) - if you need this functionality, this agent +isn't for you. + +It also doesn't support writing to a common spool as root and then +chowning the mailbox to the user - I felt this functionality didn't +fit with my overall aims." + +[End of Andrew McNamara's words] + +The result is the most secure local delivery agent that you will +find with Postfix. + +This delivery agent requires three different lookup tables in order +to define its recipients as (mailbox path, user ID, group ID). This +is because Postfix table lookups can't return multiple results. + +If your virtual mailboxes are all owned by the same user/group ID, +just specify "static" maps that always return the same result. See +below for examples. + +If your virtual mailboxes must be owned by different user/group +IDs, and if it is too inconvenient for you to maintain three parallel +tables, use an LDAP or MYSQL database (or generate the three parallel +tables from one common template). + +Configuration parameters +======================== + +virtual_mailbox_base + + Specifies a path that is prepended to all mailbox paths. This + is a safety measure to ensure an that out of control map doesn't + litter the filesystem with mailboxes (or worse). While it could + be set to "/", this isn't recommended. + +virtual_mailbox_domains + + Specifies the list of domains that should be delivered to the + $virtual_transport delivery agent (default: virtual). As of + version 2.0, Postfix is smart enough that you don't have to + list every virtual domain in a Postfix transport map. + +virtual_mailbox_maps + + Recipients are looked up in this map to determine the path to + their mailbox. If the returned path ends in a slash ("/"), + maildir-style delivery is carried out, otherwise the path is + assumed to specify a mailbox file. The virtual_mailbox_base + directory is unconditionally prepended to this path. If the + recipient is not found the mail is bounced. + + In a lookup table, specify a left-hand side of @domain.tld to + match any user in the specified domain that does not have her + own user@domain.tld entry. While searching a lookup table, an + extended address (user+foo@domain.tld) is searched before the + bare address (user@domain.tld). + + If a recipient is not found the mail is returned to the sender. + + Regular expression maps are allowed. For security reasons, + regular expression substitution of $1 etc. is disallowed, + because that would open a security hole. + + The mail administrator is expected to create and chown recipient + mailbox files or maildir directories ahead of time. + +virtual_minimum_uid + + Specifies a minimum uid that will be accepted as a return from + a virtual_uid_maps lookup. Returned values less than this will + be rejected, and the message will be deferred. + +virtual_uid_maps + + Recipients are looked up in this map to determine the UID (owner + privileges) to be used when writing to the target mailbox. + + In a lookup table, specify a left-hand side of @domain.tld to + match any user in the specified domain that does not have a + specific user@domain.tld entry. While searching a lookup table, + an address extension (user+foo@domain.tld) is ignored. + + Regular expression maps are allowed. For security reasons, + regular expression substitution of $1 etc. is disallowed, + because that would open a security hole. + + Specify a static map if all mailboxes should be owned by the same + UID. For example, to specify that all mailboxes are owned by the + UID 5000, specify: + + virtual_uid_maps = static:5000 + +virtual_gid_maps + + Recipients are looked up in this map to determine the GID (group + privileges) to be used when writing to the target mailbox. + + In a lookup table, specify a left-hand side of @domain.tld to + match any user in the specified domain that does not have a + specific user@domain.tld entry. While searching a lookup table, + an address extension (user+foo@domain.tld) is ignored. + + Regular expression maps are allowed. For security reasons, + regular expression substitution of $1 etc. is disallowed, + because that would open a security hole. + + Specify a static map if all mailboxes should be owned by the same + GID. For example, to specify that all mailboxes are owned by the + GID 5000, specify: + + virtual_gid_maps = static:5000 + +virtual_mailbox_lock + + This setting is ignored in case of maildir delivery. + + Locking method to use when updating a mailbox. Defaults to + fcntl or flock depending on the system. Depending on the POP + or IMAP server you may have to specify dotlock locking, which + requires that the recipient UID or GID has write access to the + parent directory of the mailbox file. + + Use the "postconf -l" command to find out what locking methods + Postfix supports on your system. + +virtual_mailbox_limit + + An upper limit on the size of a mailbox file or maildir file. + +Example 1: using the virtual delivery agent for all local mail +============================================================== + +This example does not use the Postfix local delivery agent at all. +With this configuration Postfix does no alias expansion, no .forward +file expansion, no lookups of recipients in /etc/passwd, and allows +but ignores user+foo address extensions. + +Instead of "hash" specify "dbm" or "btree", depending on your system +type. The command "postconf -m" displays possible lookup table +types. + + /etc/postfix/main.cf: + # Don't send mail to the local delivery agent. + mydestination = + + # All domains that are listed in $virtual_mailbox_domains + # are delivered via $virtual_transport, which is the virtual + # delivery agent by default. + virtual_mailbox_domains = + $myhostname localhost.$mydomain virtual1.domain virtual2.domain + + virtual_transport = virtual + virtual_mailbox_base = /var/mail/vhosts + virtual_mailbox_maps = hash:/etc/postfix/vmailbox + virtual_minimum_uid = 100 + virtual_uid_maps = hash:/etc/postfix/vuid + virtual_gid_maps = hash:/etc/postfix/vgid + +Define a virtual delivery agent if the entry doesn't already exist: + + /etc/postfix/master.cf: + virtual unix - n n - - virtual + +Example recipients, one UNIX-style mailbox, one qmail-style maildir: + + /etc/postfix/vmailbox: + test1@virtual1.domain test1 + test2@virtual2.domain test2/ + + /etc/postfix/vuid: + test1@virtual1.domain 5001 + test2@virtual2.domain 5002 + + /etc/postfix/vgid: + test1@virtual1.domain 5001 + test2@virtual2.domain 5002 + +Execute something like the following commands for each mailbox recipient: + + # touch /var/mail/vhosts/test1 + # chown 5001:5001 /var/mail/vhosts/test1 + +Execute something like the following commands for each maildir recipient: + + # mkdir /var/mail/vhosts/test2 + # chown 5002:5002 /var/mail/vhosts/test2 + +Be sure to make the necessary entries for root@$myhostname, +postmaster@$myhostname and for any other necessary addresses. + +Example 2: co-existing with the default local delivery agent +============================================================ + +In this example, the default Postfix local delivery agent handles +the mail for non-virtual recipients; the virtual delivery agent +handles virtual recipients, and all virtual mailboxes are owned +by user ID 5000, group ID 5000. + +Instead of "hash" specify "dbm" or "btree", depending on your system +type. The command "postconf -m" displays possible lookup table +types. + + /etc/postfix/main.cf: + # All domains and users delivered by the virtual local delivery agent. + + virtual_transport = virtual + virtual_mailbox_base = /var/mail/vhosts + virtual_mailbox_maps = hash:/etc/postfix/vmailbox + virtual_mailbox_domains = $virtual_mailbox_maps + virtual_minimum_uid = 100 + virtual_uid_maps = static:5000 + virtual_gid_maps = static:5000 + + # All domains and users delivered by the local delivery agent. + # local_recipient_maps is used by the SMTP server to reject mail + # for unknown users. + + local_transport = local + mydestination = $myhostname $localhost.$mydomain + local_recipient_maps = unix:passwd.byname $alias_maps + +Define a virtual delivery agent if the entry doesn't already exist: + + /etc/postfix/master.cf: + virtual unix - n n - - virtual + +Example recipients, one UNIX-style mailbox, one qmail-style maildir: + + /etc/postfix/vmailbox: + test1@virtual1.domain test1 + test2@virtual2.domain test2/ + + /etc/postfix/vmaildomains: + virtual1.domain required to prevent relay access denied errors + virtual2.domain required to prevent relay access denied errors + +Execute something like the following commands for each mailbox recipient: + + # touch /var/mail/vhosts/test1 + # chown 5000:5000 /var/mail/vhosts/test1 + +Execute something like the following commands for each maildir recipient: + + # mkdir /var/mail/vhosts/test2 + # chown 5000:5000 /var/mail/vhosts/test2 + +Remember that each domain is required to have a postmaster contact +address. + +Example 3: hosting many virtual users +===================================== + +Example 2 is fine if you host only a few virtual users. With many +users you will want to separate the information that changes often +(the user addresses) from the information that changes rarely (the +names of hosted domains). + +This example is the same as above, with co-existing local and +virtual domains, but it uses a separate table for specifying the +virtual domain names. + + /etc/postfix/main.cf: + # All domains and users delivered by the virtual local delivery agent. + + virtual_transport = virtual + virtual_mailbox_base = /var/mail/vhosts + virtual_mailbox_maps = hash:/etc/postfix/vmailbox + virtual_mailbox_domains = hash:/etc/postfix/vmaildomains + virtual_minimum_uid = 100 + virtual_uid_maps = static:5000 + virtual_gid_maps = static:5000 + + # All domains and users delivered by the local delivery agent. + # local_recipient_maps is used by the SMTP server to reject mail + # for unknown users. + + local_transport = local + mydestination = $myhostname $localhost.$mydomain + local_recipient_maps = unix:passwd.byname $alias_maps + +Define a virtual delivery agent if the entry doesn't already exist: + + /etc/postfix/master.cf: + virtual unix - n n - - virtual + +Example recipients, one UNIX-style mailbox, one qmail-style maildir: + + /etc/postfix/vmailbox: + test1@virtual1.domain test1 + test2@virtual2.domain test2/ + + /etc/postfix/vmaildomains: + virtual1.domain required to prevent relay access denied errors + virtual2.domain required to prevent relay access denied errors + +Execute something like the following commands for each mailbox recipient: + + # touch /var/mail/vhosts/test1 + # chown 5000:5000 /var/mail/vhosts/test1 + +Execute something like the following commands for each maildir recipient: + + # mkdir /var/mail/vhosts/test2 + # chown 5000:5000 /var/mail/vhosts/test2 + +Remember that each domain is required to have a postmaster contact +address. + +Example 4: forwarding mail for an old account to a new address +============================================================== + +In order to forward mail for a user who no longer exists, one would +set up a rule in a virtual table (please ignore the text in the +virtual configuration file about virtual domains): + + /etc/postfix/main.cf: + virtual_maps = hash:/etc/postfix/virtual + + /etc/postfix/virtual: + old_user@old.domain new_user@new.domain + +Example 5: setting up a virtual vacation autoresponder +====================================================== + +In order to set up an autoreply for virtual recipients while still +delivering mail as normal, set up a rule in a virtual table (please +ignore the text in the virtual configuration file about virtual +domains): + + /etc/postfix/main.cf: + virtual_maps = hash:/etc/postfix/virtual + + /etc/postfix/virtual: + user@domain.tld user@domain.tld, user@autoreply.domain.tld + +This delivers mail to the recipient, and sends a copy of the mail +to the address that produces automatic replies. The address can be +serviced on a different machine, or it can be serviced locally by +setting up a transport map entry that pipes all mail for the +autoreply.domain.tld into some script that sends an automatic +reply back to the sender. diff --git a/postfix/README_FILES/VIRTUAL_README b/postfix/README_FILES/VIRTUAL_README index 44f2e1407..513e77f35 100644 --- a/postfix/README_FILES/VIRTUAL_README +++ b/postfix/README_FILES/VIRTUAL_README @@ -1,372 +1,17 @@ -This code was created by Andrew McNamara -and adapted to snapshot 20001121 by Xavier Beaudouin. It was merged -with mainstream Postfix for snapshot 20010128 by Wietse. - -Purpose of this software +Porpose of this document ======================== -You can use the virtual delivery agent for mailbox delivery of some -or all domains that are handled by a machine. +Hosting multiple domains on one server -This mechanism is different from virtual alias domains. Those -are implemented by translating every recipient address into a -different address. For that, see the virtual(5) manual page. -With the virtual delivery agent, every recipient adress can have -its own mailbox. There is no translation from recipient addresses -into different addresses. +UNIX system accounts, domain shared with all users +================================================== -This is what Andrew McNamara wrote when he made the virtual delivery -agent available. +UNIX system accounts, domains not shared +======================================== -"This code is designed for ISP's who offer virtual mail hosting. -It looks up the user mailbox location, uid and gid via separate -maps, and the mailbox location map can specify either mailbox or -maildir delivery (controlled by trailing slash on mailbox name). - -The agent does support user+foo address extensions, but does not -support aliases or .forward files (use the virtual table instead), -and therefore doesn't support file or program aliases. This choice -was made to simplify and streamline the code (it allowed me to -dispense with 70% of local's code - mostly the bits that are a -security headache) - if you need this functionality, this agent -isn't for you. - -It also doesn't support writing to a common spool as root and then -chowning the mailbox to the user - I felt this functionality didn't -fit with my overall aims." - -[End of Andrew McNamara's words] - -The result is the most secure local delivery agent that you will -find with Postfix. - -This delivery agent requires three different lookup tables in order -to define its recipients as (mailbox path, user ID, group ID). This -is because Postfix table lookups can't return multiple results. - -If your virtual mailboxes are all owned by the same user/group ID, -just specify "static" maps that always return the same result. See -below for examples. - -If your virtual mailboxes must be owned by different user/group -IDs, and if it is too inconvenient for you to maintain three parallel -tables, use an LDAP or MYSQL database (or generate the three parallel -tables from one common template). - -Configuration parameters -======================== - -virtual_mailbox_base - - Specifies a path that is prepended to all mailbox paths. This - is a safety measure to ensure an that out of control map doesn't - litter the filesystem with mailboxes (or worse). While it could - be set to "/", this isn't recommended. - -virtual_mailbox_domains - - Specifies the list of domains that should be delivered to the - $virtual_transport delivery agent (default: virtual). As of - version 2.0, Postfix is smart enough that you don't have to - list every virtual domain in a Postfix transport map. - -virtual_mailbox_maps - - Recipients are looked up in this map to determine the path to - their mailbox. If the returned path ends in a slash ("/"), - maildir-style delivery is carried out, otherwise the path is - assumed to specify a mailbox file. The virtual_mailbox_base - directory is unconditionally prepended to this path. If the - recipient is not found the mail is bounced. - - In a lookup table, specify a left-hand side of @domain.tld to - match any user in the specified domain that does not have her - own user@domain.tld entry. While searching a lookup table, an - extended address (user+foo@domain.tld) is searched before the - bare address (user@domain.tld). - - If a recipient is not found the mail is returned to the sender. - - Regular expression maps are allowed. For security reasons, - regular expression substitution of $1 etc. is disallowed, - because that would open a security hole. - - The mail administrator is expected to create and chown recipient - mailbox files or maildir directories ahead of time. - -virtual_minimum_uid - - Specifies a minimum uid that will be accepted as a return from - a virtual_uid_maps lookup. Returned values less than this will - be rejected, and the message will be deferred. - -virtual_uid_maps - - Recipients are looked up in this map to determine the UID (owner - privileges) to be used when writing to the target mailbox. - - In a lookup table, specify a left-hand side of @domain.tld to - match any user in the specified domain that does not have a - specific user@domain.tld entry. While searching a lookup table, - an address extension (user+foo@domain.tld) is ignored. - - Regular expression maps are allowed. For security reasons, - regular expression substitution of $1 etc. is disallowed, - because that would open a security hole. - - Specify a static map if all mailboxes should be owned by the same - UID. For example, to specify that all mailboxes are owned by the - UID 5000, specify: - - virtual_uid_maps = static:5000 - -virtual_gid_maps - - Recipients are looked up in this map to determine the GID (group - privileges) to be used when writing to the target mailbox. - - In a lookup table, specify a left-hand side of @domain.tld to - match any user in the specified domain that does not have a - specific user@domain.tld entry. While searching a lookup table, - an address extension (user+foo@domain.tld) is ignored. - - Regular expression maps are allowed. For security reasons, - regular expression substitution of $1 etc. is disallowed, - because that would open a security hole. - - Specify a static map if all mailboxes should be owned by the same - GID. For example, to specify that all mailboxes are owned by the - GID 5000, specify: - - virtual_gid_maps = static:5000 - -virtual_mailbox_lock - - This setting is ignored in case of maildir delivery. - - Locking method to use when updating a mailbox. Defaults to - fcntl or flock depending on the system. Depending on the POP - or IMAP server you may have to specify dotlock locking, which - requires that the recipient UID or GID has write access to the - parent directory of the mailbox file. - - Use the "postconf -l" command to find out what locking methods - Postfix supports on your system. - -virtual_mailbox_limit - - An upper limit on the size of a mailbox file or maildir file. - -Example 1: using the virtual delivery agent for all local mail -============================================================== - -This example does not use the Postfix local delivery agent at all. -With this configuration Postfix does no alias expansion, no .forward -file expansion, no lookups of recipients in /etc/passwd, and allows -but ignores user+foo address extensions. - -Instead of "hash" specify "dbm" or "btree", depending on your system -type. The command "postconf -m" displays possible lookup table -types. - - /etc/postfix/main.cf: - # Don't send mail to the local delivery agent. - mydestination = - - # All domains that are listed in $virtual_mailbox_domains - # are delivered via $virtual_transport, which is the virtual - # delivery agent by default. - virtual_mailbox_domains = - $myhostname localhost.$mydomain virtual1.domain virtual2.domain - - virtual_transport = virtual - virtual_mailbox_base = /var/mail/vhosts - virtual_mailbox_maps = hash:/etc/postfix/vmailbox - virtual_minimum_uid = 100 - virtual_uid_maps = hash:/etc/postfix/vuid - virtual_gid_maps = hash:/etc/postfix/vgid - -Define a virtual delivery agent if the entry doesn't already exist: - - /etc/postfix/master.cf: - virtual unix - n n - - virtual - -Example recipients, one UNIX-style mailbox, one qmail-style maildir: - - /etc/postfix/vmailbox: - test1@virtual1.domain test1 - test2@virtual2.domain test2/ - - /etc/postfix/vuid: - test1@virtual1.domain 5001 - test2@virtual2.domain 5002 - - /etc/postfix/vgid: - test1@virtual1.domain 5001 - test2@virtual2.domain 5002 - -Execute something like the following commands for each mailbox recipient: - - # touch /var/mail/vhosts/test1 - # chown 5001:5001 /var/mail/vhosts/test1 - -Execute something like the following commands for each maildir recipient: - - # mkdir /var/mail/vhosts/test2 - # chown 5002:5002 /var/mail/vhosts/test2 - -Be sure to make the necessary entries for root@$myhostname, -postmaster@$myhostname and for any other necessary addresses. - -Example 2: co-existing with the default local delivery agent -============================================================ - -In this example, the default Postfix local delivery agent handles -the mail for non-virtual recipients; the virtual delivery agent -handles virtual recipients, and all virtual mailboxes are owned -by user ID 5000, group ID 5000. - -Instead of "hash" specify "dbm" or "btree", depending on your system -type. The command "postconf -m" displays possible lookup table -types. - - /etc/postfix/main.cf: - # All domains and users delivered by the virtual local delivery agent. - - virtual_transport = virtual - virtual_mailbox_base = /var/mail/vhosts - virtual_mailbox_maps = hash:/etc/postfix/vmailbox - virtual_mailbox_domains = $virtual_mailbox_maps - virtual_minimum_uid = 100 - virtual_uid_maps = static:5000 - virtual_gid_maps = static:5000 - - # All domains and users delivered by the local delivery agent. - # local_recipient_maps is used by the SMTP server to reject mail - # for unknown users. - - local_transport = local - mydestination = $myhostname $localhost.$mydomain - local_recipient_maps = unix:passwd.byname $alias_maps - -Define a virtual delivery agent if the entry doesn't already exist: - - /etc/postfix/master.cf: - virtual unix - n n - - virtual - -Example recipients, one UNIX-style mailbox, one qmail-style maildir: - - /etc/postfix/vmailbox: - test1@virtual1.domain test1 - test2@virtual2.domain test2/ - - /etc/postfix/vmaildomains: - virtual1.domain required to prevent relay access denied errors - virtual2.domain required to prevent relay access denied errors - -Execute something like the following commands for each mailbox recipient: - - # touch /var/mail/vhosts/test1 - # chown 5000:5000 /var/mail/vhosts/test1 - -Execute something like the following commands for each maildir recipient: - - # mkdir /var/mail/vhosts/test2 - # chown 5000:5000 /var/mail/vhosts/test2 - -Remember that each domain is required to have a postmaster contact -address. - -Example 3: hosting many virtual users +Non-UNIX accounts, domains not shared ===================================== -Example 2 is fine if you host only a few virtual users. With many -users you will want to separate the information that changes often -(the user addresses) from the information that changes rarely (the -names of hosted domains). - -This example is the same as above, with co-existing local and -virtual domains, but it uses a separate table for specifying the -virtual domain names. - - /etc/postfix/main.cf: - # All domains and users delivered by the virtual local delivery agent. - - virtual_transport = virtual - virtual_mailbox_base = /var/mail/vhosts - virtual_mailbox_maps = hash:/etc/postfix/vmailbox - virtual_mailbox_domains = hash:/etc/postfix/vmaildomains - virtual_minimum_uid = 100 - virtual_uid_maps = static:5000 - virtual_gid_maps = static:5000 - - # All domains and users delivered by the local delivery agent. - # local_recipient_maps is used by the SMTP server to reject mail - # for unknown users. - - local_transport = local - mydestination = $myhostname $localhost.$mydomain - local_recipient_maps = unix:passwd.byname $alias_maps - -Define a virtual delivery agent if the entry doesn't already exist: - - /etc/postfix/master.cf: - virtual unix - n n - - virtual - -Example recipients, one UNIX-style mailbox, one qmail-style maildir: - - /etc/postfix/vmailbox: - test1@virtual1.domain test1 - test2@virtual2.domain test2/ - - /etc/postfix/vmaildomains: - virtual1.domain required to prevent relay access denied errors - virtual2.domain required to prevent relay access denied errors - -Execute something like the following commands for each mailbox recipient: - - # touch /var/mail/vhosts/test1 - # chown 5000:5000 /var/mail/vhosts/test1 - -Execute something like the following commands for each maildir recipient: - - # mkdir /var/mail/vhosts/test2 - # chown 5000:5000 /var/mail/vhosts/test2 - -Remember that each domain is required to have a postmaster contact -address. - -Example 4: forwarding mail for an old account to a new address -============================================================== - -In order to forward mail for a user who no longer exists, one would -set up a rule in a virtual table (please ignore the text in the -virtual configuration file about virtual domains): - - /etc/postfix/main.cf: - virtual_maps = hash:/etc/postfix/virtual - - /etc/postfix/virtual: - old_user@old.domain new_user@new.domain - -Example 5: setting up a virtual vacation autoresponder -====================================================== - -In order to set up an autoreply for virtual recipients while still -delivering mail as normal, set up a rule in a virtual table (please -ignore the text in the virtual configuration file about virtual -domains): - - /etc/postfix/main.cf: - virtual_maps = hash:/etc/postfix/virtual - - /etc/postfix/virtual: - user@domain.tld user@domain.tld, user@autoreply.domain.tld - -This delivers mail to the recipient, and sends a copy of the mail -to the address that produces automatic replies. The address can be -serviced on a different machine, or it can be serviced locally by -setting up a transport map entry that pipes all mail for the -autoreply.domain.tld into some script that sends an automatic -reply back to the sender. +Aliases, mailing lists, etc. +============================ diff --git a/postfix/RELEASE_NOTES b/postfix/RELEASE_NOTES index 09a6cf81d..62b14d54d 100644 --- a/postfix/RELEASE_NOTES +++ b/postfix/RELEASE_NOTES @@ -1,13 +1,3 @@ -=============================================================== -WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING -=============================================================== -The sender/recipient address verification code is lightly documented -and has been tested lightly. The code is proof-of-concept quality -and must not be used on high-volume sites. Use at your own risk. -=============================================================== -WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING -=============================================================== - In the text below, incompatible changes are labeled with the Postfix snapshot that introduced the change. If you upgrade from a later Postfix version, then you do not have to worry about that particular @@ -26,15 +16,16 @@ Incompatible changes with Postfix snapshot 2.0.16-2004XXXX ========================================================== The SMTP server no longer accept sender addresses that match a -local, virtual or relay domain while the address is not listed in -the corresponding local, virtual or relay recipient table. +local, virtual or relay domain while the address is not listed as +valid in the corresponding local, virtual or relay recipient table. +This is not configurable. Incompatible changes with Postfix snapshot 2.0.16-20031226 ========================================================== Postfix no longer allows mail addresses with bare numeric IP -addresses (user@1.2.3.4). The form user@[ipaddress] is still -allowed. +addresses (user@1.2.3.4). This is not configurable. The form +user@[ipaddress] is still allowed. Bounce messages now have a separate queue life time. This is controlled by the bounce_queue_lifetime parameter. The default is diff --git a/postfix/conf/main.cf b/postfix/conf/main.cf index 957b18138..766f11733 100644 --- a/postfix/conf/main.cf +++ b/postfix/conf/main.cf @@ -155,9 +155,9 @@ mail_owner = postfix # # See also below, section "REJECTING MAIL FOR UNKNOWN LOCAL USERS". # -#mydestination = $myhostname, localhost.$mydomain -#mydestination = $myhostname, localhost.$mydomain $mydomain -#mydestination = $myhostname, localhost.$mydomain, $mydomain, +#mydestination = $myhostname, localhost.$mydomain, localhost +#mydestination = $myhostname, localhost.$mydomain, localhost, $mydomain +#mydestination = $myhostname, localhost.$mydomain, localhost, $mydomain, # mail.$mydomain, www.$mydomain, ftp.$mydomain # REJECTING MAIL FOR UNKNOWN LOCAL USERS diff --git a/postfix/conf/master.cf b/postfix/conf/master.cf index 765561969..154c5bc23 100644 --- a/postfix/conf/master.cf +++ b/postfix/conf/master.cf @@ -82,7 +82,7 @@ smtp inet n - n - - smtpd pickup fifo n - n 60 1 pickup cleanup unix n - n - 0 cleanup qmgr fifo n - n 300 1 qmgr -#qmgr fifo n - n 300 1 nqmgr +#qmgr fifo n - n 300 1 oqmgr rewrite unix - - n - - trivial-rewrite bounce unix - - n - 0 bounce defer unix - - n - 0 bounce diff --git a/postfix/conf/pcre_table b/postfix/conf/pcre_table index b14e334c8..c9ea78eb1 100644 --- a/postfix/conf/pcre_table +++ b/postfix/conf/pcre_table @@ -24,10 +24,27 @@ # The general form of a PCRE table is: # # /pattern/flags result +# When pattern matches the input string, use the cor- +# responding result value. # # !/pattern/flags result -# When pattern matches (does not match) a search -# string, use the corresponding result value. +# When pattern does not match the input string, use +# the corresponding result value. +# +# if /pattern/flags +# +# endif Match the input string against the patterns between +# if and endif, if and only if the input string also +# matches pattern. The if..endif can nest. +# +# Note: do not prepend whitespace to patterns inside +# if..endif. +# +# if !/pattern/flags +# +# endif Match the input string against the patterns between +# if and endif, if and only if the input string does +# not match pattern. The if..endif can nest. # # blank lines and comments # Empty lines and whitespace-only lines are ignored, @@ -39,20 +56,6 @@ # line that starts with whitespace continues a logi- # cal line. # -# if /pattern/flags -# -# endif -# -# if !/pattern/flags -# -# endif Match the search string against the patterns -# between if and endif, if and only if the search -# string matches (does not match) pattern. The -# if..endif can nest. -# -# Note: do not prepend whitespace to patterns inside -# if..endif. -# # Each pattern is a perl-like regular expression. The # expression delimiter can be any character, except whites- # pace or characters that have special meaning (tradition- @@ -126,10 +129,10 @@ # # SEARCH ORDER # Patterns are applied in the order as specified in the -# table, until a pattern is found that matches the search +# table, until a pattern is found that matches the input # string. # -# Each pattern is applied to the entire lookup key string. +# Each pattern is applied to the entire input string. # Depending on the application, that string is an entire # client hostname, an entire client IP address, or an entire # mail address. Thus, no parent domain or parent network diff --git a/postfix/conf/post-install b/postfix/conf/post-install index 6cb0c4ed3..6a46cd551 100644 --- a/postfix/conf/post-install +++ b/postfix/conf/post-install @@ -447,7 +447,7 @@ test -n "$create" && { test -n "$set_permission" && { chown $recursive $owner $path || exit 1 test -z "$group" || chgrp $recursive $group $path || exit 1 - chmod $mode $path || exit 1 + chmod $recursive $mode $path || exit 1 } done } diff --git a/postfix/conf/postfix-files b/postfix/conf/postfix-files index a2487e575..2ab28e994 100644 --- a/postfix/conf/postfix-files +++ b/postfix/conf/postfix-files @@ -22,7 +22,7 @@ # File types: # d=directory # f=regular file -# l=symlink to $sendmail_path +# l=symlink # # File flags: # No flag means the flag is not active. @@ -55,15 +55,15 @@ $queue_directory/maildrop:d:$mail_owner:$setgid_group:730:uc $queue_directory/public:d:$mail_owner:$setgid_group:710:uc $queue_directory/pid:d:root:-:755:uc $queue_directory/trace:d:$mail_owner:-:700:ucr +$daemon_directory/anvil:f:root:-:755 $daemon_directory/bounce:f:root:-:755 $daemon_directory/cleanup:f:root:-:755 -$daemon_directory/anvil:f:root:-:755 $daemon_directory/error:f:root:-:755 $daemon_directory/flush:f:root:-:755 $daemon_directory/lmtp:f:root:-:755 $daemon_directory/local:f:root:-:755 $daemon_directory/master:f:root:-:755 -$daemon_directory/nqmgr:f:root:-:755 +$daemon_directory/oqmgr:f:root:-:755 $daemon_directory/pickup:f:root:-:755 $daemon_directory/pipe:f:root:-:755 $daemon_directory/proxymap:f:root:-:755 @@ -76,6 +76,7 @@ $daemon_directory/spawn:f:root:-:755 $daemon_directory/trivial-rewrite:f:root:-:755 $daemon_directory/verify:f:root:-:755 $daemon_directory/virtual:f:root:-:755 +$nqmgr_path:l:root:-:755 $command_directory/postalias:f:root:-:755 $command_directory/postcat:f:root:-:755 $command_directory/postconf:f:root:-:755 @@ -144,7 +145,8 @@ $manpage_directory/man8/flush.8:f:root:-:644 $manpage_directory/man8/lmtp.8:f:root:-:644 $manpage_directory/man8/local.8:f:root:-:644 $manpage_directory/man8/master.8:f:root:-:644 -$manpage_directory/man8/nqmgr.8:f:root:-:644 +$manpage_directory/man8/nqmgr.8:f:root:-:644:o +$manpage_directory/man8/oqmgr.8:f:root:-:644: $manpage_directory/man8/pickup.8:f:root:-:644 $manpage_directory/man8/pipe.8:f:root:-:644 $manpage_directory/man8/proxymap.8:f:root:-:644 @@ -216,6 +218,7 @@ $readme_directory/SMTPD_PROXY_README:f:root:-:644 $readme_directory/ULTRIX_README:f:root:-:644 $readme_directory/UUCP_README:f:root:-:644 $readme_directory/VERP_README:f:root:-:644 +$readme_directory/VIRTUAL_MAILBOX_README:f:root:-:644 $readme_directory/VIRTUAL_README:f:root:-:644 $readme_directory/XCLIENT_README:f:root:-:644 $readme_directory/XFORWARD_README:f:root:-:644 diff --git a/postfix/conf/regexp_table b/postfix/conf/regexp_table index 75565b212..493142841 100644 --- a/postfix/conf/regexp_table +++ b/postfix/conf/regexp_table @@ -24,10 +24,27 @@ # The general form of a Postfix regular expression table is: # # /pattern/flags result +# When pattern matches the input string, use the cor- +# responding result value. # # !/pattern/flags result -# When pattern matches (does not match) a search -# string, use the corresponding result value. +# When pattern does not match the input string, use +# the corresponding result value. +# +# if /pattern/flags +# +# endif Match the input string against the patterns between +# if and endif, if and only if that same input string +# also matches pattern. The if..endif can nest. +# +# Note: do not prepend whitespace to patterns inside +# if..endif. +# +# if !/pattern/flags +# +# endif Match the input string against the patterns between +# if and endif, if and only if that same input string +# does not match pattern. The if..endif can nest. # # blank lines and comments # Empty lines and whitespace-only lines are ignored, @@ -39,20 +56,6 @@ # line that starts with whitespace continues a logi- # cal line. # -# if /pattern/flags -# -# endif -# -# if !/pattern/flags -# -# endif Match the search string against the patterns -# between if and endif, if and only if the search -# string matches (does not match) pattern. The -# if..endif can nest. -# -# Note: do not prepend whitespace to patterns inside -# if..endif. -# # Each pattern is a regular expression enclosed by a pair of # delimiters. The regular expression syntax is described in # re_format(7). The expression delimiter can be any charac- @@ -60,18 +63,33 @@ # meaning (traditionally the forward slash is used). The # regular expression can contain whitespace. # -# By default, matching is case-insensitive, although follow- -# ing the second slash with an `i' flag will reverse this. -# Other flags are `x' (disable extended expression syntax), -# and `m' (enable multi-line mode, that is, treat newline -# characters as special). +# By default, matching is case-insensitive, and newlines are +# not treated as special characters. The behavior is con- +# trolled by flags, which are toggled by appending one or +# more of the following characters after the pattern: +# +# i (default: on) +# Toggles the case sensitivity flag. By default, +# matching is case insensitive. +# +# x (default: on) +# Toggles the extended expression syntax flag. By +# default, support for extended expression syntax is +# enabled. +# +# m (default: off) +# Toggle the multi-line mode flag. When this flag is +# on, the ^ and $ metacharacters match immediately +# after and immediately before a newline character, +# respectively, in addition to matching at the start +# and end of the input string. # # TABLE SEARCH ORDER # Patterns are applied in the order as specified in the -# table, until a pattern is found that matches the search +# table, until a pattern is found that matches the input # string. # -# Each pattern is applied to the entire lookup key string. +# Each pattern is applied to the entire input string. # Depending on the application, that string is an entire # client hostname, an entire client IP address, or an entire # mail address. Thus, no parent domain or parent network diff --git a/postfix/conf/sample-auth.cf b/postfix/conf/sample-auth.cf index 83e22aeaf..6e2fcd3ef 100644 --- a/postfix/conf/sample-auth.cf +++ b/postfix/conf/sample-auth.cf @@ -69,6 +69,12 @@ smtpd_sasl_auth_enable = no #smtpd_sasl_security_options = noanonymous, noplaintext smtpd_sasl_security_options = noanonymous +# The application name used for SASL server initialization. This +# controls the name of the SASL configuration file. The default value +# is smtpd, corresponding to a SASL configuration file named smtpd.conf. +# +smtpd_sasl_application_name = smtpd + # The smtpd_sasl_local_domain parameter specifies the name of the # local authentication realm. # diff --git a/postfix/conf/sample-pgsql-aliases.cf b/postfix/conf/sample-pgsql-aliases.cf index a31cbccff..46a045092 100644 --- a/postfix/conf/sample-pgsql-aliases.cf +++ b/postfix/conf/sample-pgsql-aliases.cf @@ -27,15 +27,28 @@ additional_conditions = and status = 'paid' # ($lookup is escaped so if it contains single quotes or other odd # characters, it will not cause a parse error in the sql). +# Alternatively, you can override the default SELECT statement (and the +# above table, select_field, where_field, and additional_conditions) by +# specifying the query: + +#query = select forw_addr from mxaliases where alias = '%s' and status = 'paid' + +# Before the query is actually issued, all occurences of %s are replaced +# with the address to look up, %u are repleced with the user portion, +# and %d with the domain portion. + # If you just want to use a PostgreSQL function, you can ignore the -# table name, select_field, where_field and additional_conditions, -# and just specify a database function to call: +# table name, select_field, where_field and additional_conditions, and +# just specify a database function to call: #select_function = my_lookup_user_alias -# this will result in "select my_lookup_user_alias('name')" being -# used as the SQL statement to execute. If select_function is specified -# the table-related fields above will be ignored. +# This is equivalent to: +# +#query = select my_lookup_user_alias('%s') +# +# and overrides both the query parameter and the table-related fields +# above. # # As of 25-Jun-2002, if the function returns a single row and a single # column AND that value is NULL, then the result will be treated as diff --git a/postfix/conf/sample-smtpd.cf b/postfix/conf/sample-smtpd.cf index 4e421987f..6c4c1d95c 100644 --- a/postfix/conf/sample-smtpd.cf +++ b/postfix/conf/sample-smtpd.cf @@ -498,8 +498,9 @@ smtpd_helo_restrictions = # reject_unknown_sender_domain: reject sender domain without A or MX record. # +reject_unlisted_sender: reject a sender address in a local, virtual or # relay domain that is not listed as a valid address. -# This restriction is automatically added at the end if not explicitly -# specified. +# If not executed as part of smtpd_sender_restrictions, this restriction +# is automatically executed after smtpd_sender_restrictions evaluation +# completes. # reject_rhsbl_sender domain.tld: reject sender domain name if it is listed # in an A record under domain.tld. # check_sender_access maptype:mapname @@ -592,8 +593,9 @@ smtpd_sender_restrictions = # reject_unknown_recipient_domain: reject domains without A or MX record. # +reject_unlisted_recipient: reject a recipient address in a local, virtual # or relay domain that is not listed as a valid address. -# This restriction is automatically added at the end if not explicitly -# specified. +# If not executed as part of smtpd_recipient_restrictions, this +# restriction is automatically executed after completion of +# smtpd_recipient_restrictions evaluation. # +check_recipient_maps: a backwards compatibility alias for the # reject_unlisted_recipient restriction. # check_recipient_access maptype:mapname diff --git a/postfix/examples/smtpd-policy/smtpd-policy.pl b/postfix/examples/smtpd-policy/smtpd-policy.pl index f1666ff79..cee1f376a 100755 --- a/postfix/examples/smtpd-policy/smtpd-policy.pl +++ b/postfix/examples/smtpd-policy/smtpd-policy.pl @@ -93,8 +93,8 @@ sub smtpd_access_policy { open_database() unless $database_obj; # Lookup the time stamp for this client/sender/recipient. - $key = $attr{"client_address"}."/".$attr{"sender"}."/".$attr{"recipient"}; - $key =~ tr /A-Z/a-z/; + $key = + lc $attr{"client_address"}."/".$attr{"sender"}."/".$attr{"recipient"}; $time_stamp = read_database($key); $now = time(); diff --git a/postfix/html/Makefile.in b/postfix/html/Makefile.in index a2f61529d..411ed0b39 100644 --- a/postfix/html/Makefile.in +++ b/postfix/html/Makefile.in @@ -5,7 +5,7 @@ SHELL = /bin/sh DAEMONS = bounce.8.html cleanup.8.html defer.8.html error.8.html local.8.html \ lmtp.8.html master.8.html pickup.8.html pipe.8.html qmgr.8.html \ showq.8.html smtp.8.html smtpd.8.html trivial-rewrite.8.html \ - nqmgr.8.html spawn.8.html flush.8.html virtual.8.html qmqpd.8.html \ + oqmgr.8.html spawn.8.html flush.8.html virtual.8.html qmqpd.8.html \ trace.8.html verify.8.html proxymap.8.html anvil.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 \ @@ -66,11 +66,11 @@ master.8.html: ../src/master/master.c PATH=../mantools:$$PATH; \ srctoman $? | $(AWK) | nroff -man | uniq | man2html | postlink >$@ -nqmgr.8.html: ../src/nqmgr/qmgr.c +oqmgr.8.html: ../src/oqmgr/qmgr.c PATH=../mantools:$$PATH; \ - srctoman $? | sed -e 's/qmgr[^_]/n&/' \ - -e 's/qmgr$$/n&/' \ - -e 's/QMGR[^_]/N&/' | \ + srctoman $? | sed -e 's/qmgr[^_]/o&/' \ + -e 's/qmgr$$/o&/' \ + -e 's/QMGR[^_]/O&/' | \ $(AWK) | nroff -man | uniq | man2html | postlink >$@ pickup.8.html: ../src/pickup/pickup.c diff --git a/postfix/html/nqmgr.8.html b/postfix/html/oqmgr.8.html similarity index 57% rename from postfix/html/nqmgr.8.html rename to postfix/html/oqmgr.8.html index 16ead7ae5..2b054b90e 100644 --- a/postfix/html/nqmgr.8.html +++ b/postfix/html/oqmgr.8.html @@ -1,14 +1,14 @@
-NQMGR(8)                                                 NQMGR(8)
+OQMGR(8)                                                 OQMGR(8)
 
 NAME
-       nqmgr - Postfix queue manager
+       oqmgr - old Postfix queue manager
 
 SYNOPSIS
-       nqmgr [generic Postfix daemon options]
+       oqmgr [generic Postfix daemon options]
 
 DESCRIPTION
-       The  nqmgr  daemon awaits the arrival of incoming mail and
+       The  oqmgr  daemon awaits the arrival of incoming mail and
        arranges for its delivery via Postfix delivery  processes.
        The actual mail routing strategy is delegated to the triv-
        ial-rewrite(8) daemon.  This program  expects  to  be  run
@@ -19,7 +19,7 @@ NQMGR(8)                                                 NQMGR(8)
        by undeliverable bounce notifications.
 
 MAIL QUEUES
-       The nqmgr daemon maintains the following queues:
+       The oqmgr daemon maintains the following queues:
 
        incoming
               Inbound mail from the network, or mail picked up by
@@ -44,7 +44,7 @@ NQMGR(8)                                                 NQMGR(8)
               until someone sets them free.
 
 DELIVERY STATUS REPORTS
-       The nqmgr daemon keeps an eye on per-message delivery sta-
+       The oqmgr daemon keeps an eye on per-message delivery sta-
        tus reports in  the  following  directories.  Each  status
        report file has the same name as the corresponding message
        file:
@@ -57,7 +57,7 @@ NQMGR(8)                                                 NQMGR(8)
               delayed.    These   files  are  maintained  by  the
               defer(8) daemon.
 
-       The nqmgr daemon is responsible for asking  the  bounce(8)
+       The oqmgr daemon is responsible for asking  the  bounce(8)
        or defer(8) daemons to send non-delivery reports.
 
 STRATEGIES
@@ -97,28 +97,22 @@ NQMGR(8)                                                 NQMGR(8)
               attempts by  maintaining  a  short-term,  in-memory
               list of unreachable destinations.
 
-       preemptive message scheduling
-              The  queue manager attempts to minimize the average
-              per-recipient delay while still preserving the cor-
-              rect per-message delays, using a sophisticated pre-
-              emptive message scheduling.
-
 TRIGGERS
        On an idle system, the queue manager waits for the arrival
-       of  trigger  events,  or it waits for a timer to go off. A
-       trigger is a one-byte message.  Depending on  the  message
-       received,  the queue manager performs one of the following
-       actions (the message is followed by the symbolic  constant
+       of trigger events, or it waits for a timer to  go  off.  A
+       trigger  is  a one-byte message.  Depending on the message
+       received, the queue manager performs one of the  following
+       actions  (the message is followed by the symbolic constant
        used internally by the software):
 
        D (QMGR_REQ_SCAN_DEFERRED)
-              Start  a  deferred queue scan.  If a deferred queue
-              scan is already in  progress,  that  scan  will  be
+              Start a deferred queue scan.  If a  deferred  queue
+              scan  is  already  in  progress,  that scan will be
               restarted as soon as it finishes.
 
        I (QMGR_REQ_SCAN_INCOMING)
-              Start  an incoming queue scan. If an incoming queue
-              scan is already in  progress,  that  scan  will  be
+              Start an incoming queue scan. If an incoming  queue
+              scan  is  already  in  progress,  that scan will be
               restarted as soon as it finishes.
 
        A (QMGR_REQ_SCAN_ALL)
@@ -126,32 +120,32 @@ NQMGR(8)                                                 NQMGR(8)
               affects the next deferred queue scan.
 
        F (QMGR_REQ_FLUSH_DEAD)
-              Purge all information  about  dead  transports  and
+              Purge  all  information  about  dead transports and
               destinations.
 
        W (TRIGGER_REQ_WAKEUP)
-              Wakeup  call,  This is used by the master server to
-              instantiate servers that should not  go  away  for-
-              ever.  The  action  is  to  start an incoming queue
+              Wakeup call, This is used by the master  server  to
+              instantiate  servers  that  should not go away for-
+              ever. The action is  to  start  an  incoming  queue
               scan.
 
-       The nqmgr daemon reads an entire buffer worth of triggers.
-       Multiple  identical  trigger  requests  are collapsed into
-       one, and trigger requests are sorted so that A and F  pre-
-       cede  D  and  I.  Thus, in order to force a deferred queue
+       The oqmgr daemon reads an entire buffer worth of triggers.
+       Multiple identical trigger  requests  are  collapsed  into
+       one,  and trigger requests are sorted so that A and F pre-
+       cede D and I. Thus, in order to  force  a  deferred  queue
        run, one would request A F D; in order to notify the queue
        manager of the arrival of new mail one would request I.
 
 STANDARDS
-       None.  The nqmgr daemon does not interact with the outside
+       None. The oqmgr daemon does not interact with the  outside
        world.
 
 SECURITY
-       The nqmgr daemon is not security sensitive. It reads  sin-
-       gle-character  messages  from  untrusted  local users, and
-       thus may be susceptible to denial of service attacks.  The
-       nqmgr  daemon  does  not talk to the outside world, and it
-       can be run at fixed low privilege in a  chrooted  environ-
+       The  oqmgr daemon is not security sensitive. It reads sin-
+       gle-character messages from  untrusted  local  users,  and
+       thus  may be susceptible to denial of service attacks. The
+       oqmgr daemon does not talk to the outside  world,  and  it
+       can  be  run at fixed low privilege in a chrooted environ-
        ment.
 
 DIAGNOSTICS
@@ -159,34 +153,31 @@ NQMGR(8)                                                 NQMGR(8)
        Corrupted message files are saved to the corrupt queue for
        further inspection.
 
-       Depending  on the setting of the notify_classes parameter,
-       the postmaster is notified of bounces and of  other  trou-
+       Depending on the setting of the notify_classes  parameter,
+       the  postmaster  is notified of bounces and of other trou-
        ble.
 
 BUGS
-       A  single  queue  manager  process has to compete for disk
-       access with multiple front-end processes such as smtpd.  A
-       sudden  burst  of  inbound mail can negatively impact out-
+       A single queue manager process has  to  compete  for  disk
+       access  with multiple front-end processes such as smtpd. A
+       sudden burst of inbound mail can  negatively  impact  out-
        bound delivery rates.
 
 CONFIGURATION PARAMETERS
-       The following main.cf parameters are  especially  relevant
-       to  this  program. See the Postfix main.cf file for syntax
-       details and for default values.  Use  the  postfix  reload
+       The  following  main.cf parameters are especially relevant
+       to this program. See the Postfix main.cf file  for  syntax
+       details  and  for  default  values. Use the postfix reload
        command after a configuration change.
 
 Miscellaneous
        allow_min_user
-              Do  not  bounce recipient addresses that begin with
+              Do not bounce recipient addresses that  begin  with
               '-'.
 
        queue_directory
               Top-level directory of the Postfix queue.
 
 Active queue controls
-       In the text below, transport is the first field in a  mas-
-       ter.cf entry.
-
        qmgr_clog_warn_time
               Minimal delay between warnings that a specific des-
               tination is clogging up the active queue. Specify 0
@@ -201,27 +192,6 @@ NQMGR(8)                                                 NQMGR(8)
               This  parameter  also limits the size of the short-
               term, in-memory destination cache.
 
-       qmgr_message_recipient_minimum
-              Per message minimum of in-memory recipients.
-
-       default_recipient_limit
-              Default limit on the number of in-memory recipients
-              per transport.
-
-       transport_recipient_limit
-              Limit  on  the  number of in-memory recipients, for
-              the named message transport.
-
-       default_extra_recipient_limit
-              Default limit on the total number of per  transport
-              in-memory  recipients  that the preempting messages
-              can have.
-
-       transport_extra_recipient_limit
-              Limit on the number of in-memory  recipients  which
-              all  preempting messages delivered by the transport
-              transport can have.
-
 Timing controls
        minimal_backoff_time
               Minimal time in seconds between  delivery  attempts
@@ -252,6 +222,25 @@ NQMGR(8)                                                 NQMGR(8)
               ken delivery transport.
 
 Concurrency controls
+       In the text below, transport is the first field in a  mas-
+       ter.cf entry.
+
+       qmgr_fudge_factor (valid range: 10..100)
+              The  percentage  of  delivery resources that a busy
+              mail system will use up for  delivery  of  a  large
+              mailing  list  message.  With 100%, delivery of one
+              message does not begin before the previous  message
+              has  been  delivered.  This results in good perfor-
+              mance for large mailing lists, but results in  poor
+              response  time for one-to-one mail.  With less than
+              100%, response time for one-to-one  mail  improves,
+              but  large  mailing  list delivery performance suf-
+              fers. In the worst case, recipients near the begin-
+              ning  of  a  large list receive a burst of messages
+              immediately, while recipients near the end of  that
+              list  receive  that  same burst of messages a whole
+              day later.
+
        initial_destination_concurrency
               Initial per-destination concurrency level for  par-
               allel delivery to the same destination.
@@ -274,59 +263,13 @@ NQMGR(8)                                                 NQMGR(8)
               Limit  on  the  number  of  recipients  per message
               transfer, for the named message transport.
 
-Message scheduling
-       transport_delivery_slot_cost (valid range: 0,2,3...)
-              This parameter basically controls how often a  mes-
-              sage  delivered  by  transport  can be preempted by
-              another message.  An internal per-message/transport
-              counter  is  incremented  by  one  for  each trans-
-              port_delivery_slot_cost   deliveries   handled   by
-              transport.  This  counter  represents the number of
-              "available delivery slots" for use  by  other  mes-
-              sages.  Current message can be preempted by another
-              message when that other message  can  be  delivered
-              using  less  transport agents than the value of the
-              "available delivery slots" counter.
-
-              Value equal to 0 disables  the  message  preemption
-              for transport.
-
-       transport_minimum_delivery_slots
-              Message preemption is not attempted at all whenever
-              a message  that  can't  ever  accumulate  at  least
-              transport_minimum_delivery_slots available delivery
-              slots is being delivered by transport.
-
-       transport_delivery_slot_discount (valid range: 0..100)
-
-       transport_delivery_slot_loan
-              These parameters speed up the moment when a message
-              preemption  can  happen.   Instead of waiting until
-              the full  amount  of  delivery  slots  required  is
-              available,  the  preemption  can happen when trans-
-              port_delivery_slot_discount percent of the required
-              amount   plus   transport_delivery_slot_loan  still
-              remains to  be  accumulated.  Note  that  the  full
-              amount  will  still  have  to be accumulated before
-              another preemption can take place later.
-
-       default_delivery_slot_cost
-
-       default_minimum_delivery_slots
-
-       default_delivery_slot_discount
-
-       default_delivery_slot_loan
-              Default values for the transport  specific  parame-
-              ters described above.
-
 SEE ALSO
        master(8), process manager
        syslogd(8) system logging
        trivial-rewrite(8), address routing
 
 LICENSE
-       The  Secure  Mailer  license must be distributed with this
+       The Secure Mailer license must be  distributed  with  this
        software.
 
 AUTHOR(S)
@@ -335,10 +278,5 @@ NQMGR(8)                                                 NQMGR(8)
        P.O. Box 704
        Yorktown Heights, NY 10598, USA
 
-       Scheduler enhancements:
-       Patrik Rak
-       Modra 6
-       155 00, Prague, Czech Republic
-
-                                                         NQMGR(8)
+                                                         OQMGR(8)
 
diff --git a/postfix/html/pcre_table.5.html b/postfix/html/pcre_table.5.html index 3e17b5f75..2587be0ca 100644 --- a/postfix/html/pcre_table.5.html +++ b/postfix/html/pcre_table.5.html @@ -25,10 +25,27 @@ PCRE_TABLE(5) PCRE_TABLE(5) The general form of a PCRE table is: /pattern/flags result + When pattern matches the input string, use the cor- + responding result value. !/pattern/flags result - When pattern matches (does not match) a search - string, use the corresponding result value. + When pattern does not match the input string, use + the corresponding result value. + + if /pattern/flags + + endif Match the input string against the patterns between + if and endif, if and only if the input string also + matches pattern. The if..endif can nest. + + Note: do not prepend whitespace to patterns inside + if..endif. + + if !/pattern/flags + + endif Match the input string against the patterns between + if and endif, if and only if the input string does + not match pattern. The if..endif can nest. blank lines and comments Empty lines and whitespace-only lines are ignored, @@ -40,20 +57,6 @@ PCRE_TABLE(5) PCRE_TABLE(5) line that starts with whitespace continues a logi- cal line. - if /pattern/flags - - endif - - if !/pattern/flags - - endif Match the search string against the patterns - between if and endif, if and only if the search - string matches (does not match) pattern. The - if..endif can nest. - - Note: do not prepend whitespace to patterns inside - if..endif. - Each pattern is a perl-like regular expression. The expression delimiter can be any character, except whites- pace or characters that have special meaning (tradition- @@ -127,10 +130,10 @@ PCRE_TABLE(5) PCRE_TABLE(5) SEARCH ORDER Patterns are applied in the order as specified in the - table, until a pattern is found that matches the search + table, until a pattern is found that matches the input string. - Each pattern is applied to the entire lookup key string. + Each pattern is applied to the entire input string. Depending on the application, that string is an entire client hostname, an entire client IP address, or an entire mail address. Thus, no parent domain or parent network diff --git a/postfix/html/pickup.8.html b/postfix/html/pickup.8.html index a4a308f3f..dc5baedbf 100644 --- a/postfix/html/pickup.8.html +++ b/postfix/html/pickup.8.html @@ -1,4 +1,4 @@ -
+  
 PICKUP(8)                                               PICKUP(8)
 
 NAME
@@ -44,32 +44,32 @@ PICKUP(8)                                               PICKUP(8)
        command after a configuration change.
 
 Content inspection controls
-       content_filter
+       content_filter
               The  name of a mail delivery transport that filters
               mail and that either bounces mail or re-injects the
               result  back into Postfix.  This parameter uses the
               same syntax as the right-hand  side  of  a  Postfix
               transport table.
 
-       receive_override_options
+       receive_override_options
               The  following  options  override main.cf settings.
               The options are passed on to the downstream cleanup
               server.
 
-              no_address_mappings
+              no_address_mappings
                      Disable  canonical  address mapping, virtual
                      alias map expansion,  address  masquerading,
                      and  automatic  BCC recipients. Specify this
                      if address mapping etc. are to be done after
                      an external content filter.
 
-              no_header_body_checks
+              no_header_body_checks
                      Disable  header/body_checks. Specify this if
                      header/body_checks are to be done  after  an
                      external content filter.
 
 Miscellaneous
-       queue_directory
+       queue_directory
               Top-level directory of the Postfix queue.
 
 SEE ALSO
diff --git a/postfix/html/postdrop.1.html b/postfix/html/postdrop.1.html
index be2201642..34bb4c97d 100644
--- a/postfix/html/postdrop.1.html
+++ b/postfix/html/postdrop.1.html
@@ -1,11 +1,11 @@
-    
+  
 POSTDROP(1)                                           POSTDROP(1)
 
 NAME
        postdrop - Postfix mail posting utility
 
 SYNOPSIS
-       postdrop [-rv] [-c config_dir]
+       postdrop [-rv] [-c config_dir]
 
 DESCRIPTION
        The postdrop command creates a file in the maildrop direc-
@@ -48,7 +48,7 @@ POSTDROP(1)                                           POSTDROP(1)
 
               A  non-standard  directory  is  allowed only if the
               name is listed in the standard main.cf file, in the
-              alternate_config_directories  configuration parame-
+              alternate_config_directories  configuration parame-
               ter value.
 
               Only the superuser is allowed to specify  arbitrary
@@ -58,21 +58,21 @@ POSTDROP(1)                                           POSTDROP(1)
        /var/spool/postfix, mail queue
        /etc/postfix, configuration files
 
-CONFIGURATION PARAMETERS
+CONFIGURATION PARAMETERS
        See  the  Postfix  main.cf file for syntax details and for
-       default values. Use the postfix  reload  command  after  a
+       default values. Use the postfix  reload  command  after  a
        configuration change.
 
-       import_environment
+       import_environment
               List of names of environment parameters that can be
               imported from non-Postfix processes.
 
-       queue_directory
+       queue_directory
               Top-level directory of the Postfix queue.  This  is
               also the root directory of Postfix daemons that run
               chrooted.
 
-SEE ALSO
+SEE ALSO
        sendmail(1) compatibility interface
        syslogd(8) system logging
 
diff --git a/postfix/html/qmgr.8.html b/postfix/html/qmgr.8.html
index ca13a844e..b5b95fb78 100644
--- a/postfix/html/qmgr.8.html
+++ b/postfix/html/qmgr.8.html
@@ -97,22 +97,28 @@ QMGR(8)                                                   QMGR(8)
               attempts by  maintaining  a  short-term,  in-memory
               list of unreachable destinations.
 
+       preemptive message scheduling
+              The  queue manager attempts to minimize the average
+              per-recipient delay while still preserving the cor-
+              rect per-message delays, using a sophisticated pre-
+              emptive message scheduling.
+
 TRIGGERS
        On an idle system, the queue manager waits for the arrival
-       of trigger events, or it waits for a timer to  go  off.  A
-       trigger  is  a one-byte message.  Depending on the message
-       received, the queue manager performs one of the  following
-       actions  (the message is followed by the symbolic constant
+       of  trigger  events,  or it waits for a timer to go off. A
+       trigger is a one-byte message.  Depending on  the  message
+       received,  the queue manager performs one of the following
+       actions (the message is followed by the symbolic  constant
        used internally by the software):
 
        D (QMGR_REQ_SCAN_DEFERRED)
-              Start a deferred queue scan.  If a  deferred  queue
-              scan  is  already  in  progress,  that scan will be
+              Start  a  deferred queue scan.  If a deferred queue
+              scan is already in  progress,  that  scan  will  be
               restarted as soon as it finishes.
 
        I (QMGR_REQ_SCAN_INCOMING)
-              Start an incoming queue scan. If an incoming  queue
-              scan  is  already  in  progress,  that scan will be
+              Start  an incoming queue scan. If an incoming queue
+              scan is already in  progress,  that  scan  will  be
               restarted as soon as it finishes.
 
        A (QMGR_REQ_SCAN_ALL)
@@ -120,30 +126,30 @@ QMGR(8)                                                   QMGR(8)
               affects the next deferred queue scan.
 
        F (QMGR_REQ_FLUSH_DEAD)
-              Purge  all  information  about  dead transports and
+              Purge all information  about  dead  transports  and
               destinations.
 
        W (TRIGGER_REQ_WAKEUP)
-              Wakeup call, This is used by the master  server  to
-              instantiate  servers  that  should not go away for-
-              ever. The action is  to  start  an  incoming  queue
+              Wakeup  call,  This is used by the master server to
+              instantiate servers that should not  go  away  for-
+              ever.  The  action  is  to  start an incoming queue
               scan.
 
-       The  qmgr daemon reads an entire buffer worth of triggers.
-       Multiple identical trigger  requests  are  collapsed  into
-       one,  and trigger requests are sorted so that A and F pre-
-       cede D and I. Thus, in order to  force  a  deferred  queue
+       The qmgr daemon reads an entire buffer worth of  triggers.
+       Multiple  identical  trigger  requests  are collapsed into
+       one, and trigger requests are sorted so that A and F  pre-
+       cede  D  and  I.  Thus, in order to force a deferred queue
        run, one would request A F D; in order to notify the queue
        manager of the arrival of new mail one would request I.
 
 STANDARDS
-       None. The qmgr daemon does not interact with  the  outside
+       None.  The  qmgr daemon does not interact with the outside
        world.
 
 SECURITY
-       The  qmgr  daemon is not security sensitive. It reads sin-
-       gle-character messages from  untrusted  local  users,  and
-       thus  may be susceptible to denial of service attacks. The
+       The qmgr daemon is not security sensitive. It  reads  sin-
+       gle-character  messages  from  untrusted  local users, and
+       thus may be susceptible to denial of service attacks.  The
        qmgr daemon does not talk to the outside world, and it can
        be run at fixed low privilege in a chrooted environment.
 
@@ -152,31 +158,34 @@ QMGR(8)                                                   QMGR(8)
        Corrupted message files are saved to the corrupt queue for
        further inspection.
 
-       Depending  on the setting of the notify_classes parameter,
-       the postmaster is notified of bounces and of  other  trou-
+       Depending on the setting of the notify_classes  parameter,
+       the  postmaster  is notified of bounces and of other trou-
        ble.
 
 BUGS
-       A  single  queue  manager  process has to compete for disk
-       access with multiple front-end processes such as smtpd.  A
-       sudden  burst  of  inbound mail can negatively impact out-
+       A single queue manager process has  to  compete  for  disk
+       access  with multiple front-end processes such as smtpd. A
+       sudden burst of inbound mail can  negatively  impact  out-
        bound delivery rates.
 
 CONFIGURATION PARAMETERS
-       The following main.cf parameters are  especially  relevant
-       to  this  program. See the Postfix main.cf file for syntax
-       details and for default values.  Use  the  postfix  reload
+       The  following  main.cf parameters are especially relevant
+       to this program. See the Postfix main.cf file  for  syntax
+       details  and  for  default  values. Use the postfix reload
        command after a configuration change.
 
 Miscellaneous
        allow_min_user
-              Do  not  bounce recipient addresses that begin with
+              Do not bounce recipient addresses that  begin  with
               '-'.
 
        queue_directory
               Top-level directory of the Postfix queue.
 
 Active queue controls
+       In  the text below, transport is the first field in a mas-
+       ter.cf entry.
+
        qmgr_clog_warn_time
               Minimal delay between warnings that a specific des-
               tination is clogging up the active queue. Specify 0
@@ -191,6 +200,27 @@ QMGR(8)                                                   QMGR(8)
               This parameter also limits the size of  the  short-
               term, in-memory destination cache.
 
+       qmgr_message_recipient_minimum
+              Per message minimum of in-memory recipients.
+
+       default_recipient_limit
+              Default limit on the number of in-memory recipients
+              per transport.
+
+       transport_recipient_limit
+              Limit on the number of  in-memory  recipients,  for
+              the named message transport.
+
+       default_extra_recipient_limit
+              Default  limit on the total number of per transport
+              in-memory recipients that the  preempting  messages
+              can have.
+
+       transport_extra_recipient_limit
+              Limit  on  the number of in-memory recipients which
+              all preempting messages delivered by the  transport
+              transport can have.
+
 Timing controls
        minimal_backoff_time
               Minimal  time  in seconds between delivery attempts
@@ -221,25 +251,6 @@ QMGR(8)                                                   QMGR(8)
               ken delivery transport.
 
 Concurrency controls
-       In  the text below, transport is the first field in a mas-
-       ter.cf entry.
-
-       qmgr_fudge_factor (valid range: 10..100)
-              The percentage of delivery resources  that  a  busy
-              mail  system  will  use  up for delivery of a large
-              mailing list message.  With 100%, delivery  of  one
-              message  does not begin before the previous message
-              has been delivered. This results  in  good  perfor-
-              mance  for large mailing lists, but results in poor
-              response time for one-to-one mail.  With less  than
-              100%,  response  time for one-to-one mail improves,
-              but large mailing list  delivery  performance  suf-
-              fers. In the worst case, recipients near the begin-
-              ning of a large list receive a  burst  of  messages
-              immediately,  while recipients near the end of that
-              list receive that same burst of  messages  a  whole
-              day later.
-
        initial_destination_concurrency
               Initial  per-destination concurrency level for par-
               allel delivery to the same destination.
@@ -262,13 +273,59 @@ QMGR(8)                                                   QMGR(8)
               Limit on  the  number  of  recipients  per  message
               transfer, for the named message transport.
 
+Message scheduling
+       transport_delivery_slot_cost (valid range: 0,2,3...)
+              This  parameter basically controls how often a mes-
+              sage delivered by transport  can  be  preempted  by
+              another message.  An internal per-message/transport
+              counter is  incremented  by  one  for  each  trans-
+              port_delivery_slot_cost   deliveries   handled   by
+              transport. This counter represents  the  number  of
+              "available  delivery  slots"  for use by other mes-
+              sages.  Current message can be preempted by another
+              message  when  that  other message can be delivered
+              using less transport agents than the value  of  the
+              "available delivery slots" counter.
+
+              Value  equal  to  0 disables the message preemption
+              for transport.
+
+       transport_minimum_delivery_slots
+              Message preemption is not attempted at all whenever
+              a  message  that  can't  ever  accumulate  at least
+              transport_minimum_delivery_slots available delivery
+              slots is being delivered by transport.
+
+       transport_delivery_slot_discount (valid range: 0..100)
+
+       transport_delivery_slot_loan
+              These parameters speed up the moment when a message
+              preemption can happen.  Instead  of  waiting  until
+              the  full  amount  of  delivery  slots  required is
+              available, the preemption can  happen  when  trans-
+              port_delivery_slot_discount percent of the required
+              amount  plus   transport_delivery_slot_loan   still
+              remains  to  be  accumulated.  Note  that  the full
+              amount will still have  to  be  accumulated  before
+              another preemption can take place later.
+
+       default_delivery_slot_cost
+
+       default_minimum_delivery_slots
+
+       default_delivery_slot_discount
+
+       default_delivery_slot_loan
+              Default  values  for the transport specific parame-
+              ters described above.
+
 SEE ALSO
        master(8), process manager
        syslogd(8) system logging
        trivial-rewrite(8), address routing
 
 LICENSE
-       The  Secure  Mailer  license must be distributed with this
+       The Secure Mailer license must be  distributed  with  this
        software.
 
 AUTHOR(S)
@@ -277,5 +334,10 @@ QMGR(8)                                                   QMGR(8)
        P.O. Box 704
        Yorktown Heights, NY 10598, USA
 
+       Scheduler enhancements:
+       Patrik Rak
+       Modra 6
+       155 00, Prague, Czech Republic
+
                                                           QMGR(8)
 
diff --git a/postfix/html/regexp_table.5.html b/postfix/html/regexp_table.5.html index 90fb8029a..8691970bb 100644 --- a/postfix/html/regexp_table.5.html +++ b/postfix/html/regexp_table.5.html @@ -25,10 +25,27 @@ REGEXP_TABLE(5) REGEXP_TABLE(5) The general form of a Postfix regular expression table is: /pattern/flags result + When pattern matches the input string, use the cor- + responding result value. !/pattern/flags result - When pattern matches (does not match) a search - string, use the corresponding result value. + When pattern does not match the input string, use + the corresponding result value. + + if /pattern/flags + + endif Match the input string against the patterns between + if and endif, if and only if that same input string + also matches pattern. The if..endif can nest. + + Note: do not prepend whitespace to patterns inside + if..endif. + + if !/pattern/flags + + endif Match the input string against the patterns between + if and endif, if and only if that same input string + does not match pattern. The if..endif can nest. blank lines and comments Empty lines and whitespace-only lines are ignored, @@ -40,20 +57,6 @@ REGEXP_TABLE(5) REGEXP_TABLE(5) line that starts with whitespace continues a logi- cal line. - if /pattern/flags - - endif - - if !/pattern/flags - - endif Match the search string against the patterns - between if and endif, if and only if the search - string matches (does not match) pattern. The - if..endif can nest. - - Note: do not prepend whitespace to patterns inside - if..endif. - Each pattern is a regular expression enclosed by a pair of delimiters. The regular expression syntax is described in re_format(7). The expression delimiter can be any charac- @@ -61,18 +64,33 @@ REGEXP_TABLE(5) REGEXP_TABLE(5) meaning (traditionally the forward slash is used). The regular expression can contain whitespace. - By default, matching is case-insensitive, although follow- - ing the second slash with an `i' flag will reverse this. - Other flags are `x' (disable extended expression syntax), - and `m' (enable multi-line mode, that is, treat newline - characters as special). + By default, matching is case-insensitive, and newlines are + not treated as special characters. The behavior is con- + trolled by flags, which are toggled by appending one or + more of the following characters after the pattern: + + i (default: on) + Toggles the case sensitivity flag. By default, + matching is case insensitive. + + x (default: on) + Toggles the extended expression syntax flag. By + default, support for extended expression syntax is + enabled. + + m (default: off) + Toggle the multi-line mode flag. When this flag is + on, the ^ and $ metacharacters match immediately + after and immediately before a newline character, + respectively, in addition to matching at the start + and end of the input string. TABLE SEARCH ORDER Patterns are applied in the order as specified in the - table, until a pattern is found that matches the search + table, until a pattern is found that matches the input string. - Each pattern is applied to the entire lookup key string. + Each pattern is applied to the entire input string. Depending on the application, that string is an entire client hostname, an entire client IP address, or an entire mail address. Thus, no parent domain or parent network diff --git a/postfix/html/resource.html b/postfix/html/resource.html index 8255ad412..21aa9b92e 100644 --- a/postfix/html/resource.html +++ b/postfix/html/resource.html @@ -92,16 +92,6 @@ header rewriting code.

-

extract_recipient_limit (default: 10240 recipients) - -
How many recipients Postfix will extract from message headers -before it gives up. This limits the damage that a run-away program -can do with "sendmail -t". - - - -

- The following parameters restrict the use of file system storage:

diff --git a/postfix/html/smtpd.8.html b/postfix/html/smtpd.8.html index 275a510e6..4c95a132b 100644 --- a/postfix/html/smtpd.8.html +++ b/postfix/html/smtpd.8.html @@ -138,6 +138,13 @@ SMTPD(8) SMTPD(8) explicitly selected at program build time and explicitly enabled at runtime. + smtpd_sasl_application_name + The application name used for SASL server initial- + ization. This controls the name of the SASL con- + figuration file. The default value is smtpd, cor- + responding to a SASL configuration file named + smtpd.conf. + smtpd_sasl_local_domain The name of the local authentication realm. diff --git a/postfix/man/Makefile.in b/postfix/man/Makefile.in index fa91ba5e4..76a54d4df 100644 --- a/postfix/man/Makefile.in +++ b/postfix/man/Makefile.in @@ -5,7 +5,7 @@ SHELL = /bin/sh DAEMONS = man8/bounce.8 man8/defer.8 man8/cleanup.8 man8/error.8 man8/local.8 \ man8/lmtp.8 man8/master.8 man8/pickup.8 man8/pipe.8 man8/qmgr.8 \ man8/showq.8 man8/smtp.8 man8/smtpd.8 man8/trivial-rewrite.8 \ - man8/nqmgr.8 man8/spawn.8 man8/flush.8 man8/virtual.8 man8/qmqpd.8 \ + man8/oqmgr.8 man8/spawn.8 man8/flush.8 man8/virtual.8 man8/qmqpd.8 \ man8/verify.8 man8/trace.8 man8/proxymap.8 man8/anvil.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 \ @@ -57,11 +57,11 @@ man8/lmtp.8: ../src/lmtp/lmtp.c man8/master.8: ../src/master/master.c ../mantools/srctoman $? >$@ -man8/nqmgr.8: ../src/nqmgr/qmgr.c +man8/oqmgr.8: ../src/oqmgr/qmgr.c ../mantools/srctoman $? | \ - sed -e 's/qmgr[^_]/n&/' \ - -e 's/qmgr$$/n&/' \ - -e 's/QMGR[^_]/N&/' >$@ + sed -e 's/qmgr[^_]/o&/' \ + -e 's/qmgr$$/o&/' \ + -e 's/QMGR[^_]/O&/' >$@ man8/pickup.8: ../src/pickup/pickup.c ../mantools/srctoman $? >$@ diff --git a/postfix/man/man5/pcre_table.5 b/postfix/man/man5/pcre_table.5 index a4d703260..3b1dae88b 100644 --- a/postfix/man/man5/pcre_table.5 +++ b/postfix/man/man5/pcre_table.5 @@ -31,25 +31,30 @@ described in the SYNOPSIS above. .fi The general form of a PCRE table is: .IP "\fB/\fIpattern\fB/\fIflags result\fR" -.IP "\fB!/\fIpattern\fB/\fIflags result\fR" -When \fIpattern\fR matches (does not match) a search string, use +When \fIpattern\fR matches the input string, use the corresponding \fIresult\fR value. +.IP "\fB!/\fIpattern\fB/\fIflags result\fR" +When \fIpattern\fR does \fBnot\fR match the input string, use +the corresponding \fIresult\fR value. +.IP "\fBif /\fIpattern\fB/\fIflags\fR" +.IP "\fBendif\fR" +Match the input string against the patterns between \fBif\fR +and \fBendif\fR, if and only if the input string also matches +\fIpattern\fR. The \fBif\fR..\fBendif\fR can nest. +.sp +Note: do not prepend whitespace to patterns inside +\fBif\fR..\fBendif\fR. +.IP "\fBif !/\fIpattern\fB/\fIflags\fR" +.IP "\fBendif\fR" +Match the input string against the patterns between \fBif\fR +and \fBendif\fR, if and only if the input string does \fBnot\fR +match \fIpattern\fR. The \fBif\fR..\fBendif\fR can nest. .IP "blank lines and comments" Empty lines and whitespace-only lines are ignored, as are lines whose first non-whitespace character is a `#'. .IP "multi-line text" A logical line starts with non-whitespace text. A line that starts with whitespace continues a logical line. -.IP "\fBif /\fIpattern\fB/\fIflags\fR" -.IP "\fBendif\fR" -.IP "\fBif !/\fIpattern\fB/\fIflags\fR" -.IP "\fBendif\fR" -Match the search string against the patterns between \fBif\fR -and \fBendif\fR, if and only if the search string matches (does -not match) \fIpattern\fR. The \fBif\fR..\fBendif\fR can nest. -.sp -Note: do not prepend whitespace to patterns inside -\fBif\fR..\fBendif\fR. .PP Each pattern is a perl-like regular expression. The expression delimiter can be any character, except whitespace or characters @@ -113,9 +118,9 @@ error, thus reserving these combinations for future expansion. .ad .fi Patterns are applied in the order as specified in the table, until a -pattern is found that matches the search string. +pattern is found that matches the input string. -Each pattern is applied to the entire lookup key string. +Each pattern is applied to the entire input string. Depending on the application, that string is an entire client hostname, an entire client IP address, or an entire mail address. Thus, no parent domain or parent network search is done, and diff --git a/postfix/man/man5/regexp_table.5 b/postfix/man/man5/regexp_table.5 index 03bcc7cc7..7ab8acbbc 100644 --- a/postfix/man/man5/regexp_table.5 +++ b/postfix/man/man5/regexp_table.5 @@ -31,25 +31,30 @@ described in the SYNOPSIS above. .fi The general form of a Postfix regular expression table is: .IP "\fB/\fIpattern\fB/\fIflags result\fR" -.IP "\fB!/\fIpattern\fB/\fIflags result\fR" -When \fIpattern\fR matches (does not match) a search string, +When \fIpattern\fR matches the input string, use the corresponding \fIresult\fR value. +.IP "\fB!/\fIpattern\fB/\fIflags result\fR" +When \fIpattern\fR does \fBnot\fR match the input string, +use the corresponding \fIresult\fR value. +.IP "\fBif /\fIpattern\fB/\fIflags\fR" +.IP "\fBendif\fR" +Match the input string against the patterns between \fBif\fR +and \fBendif\fR, if and only if that same input string also +matches \fIpattern\fR. The \fBif\fR..\fBendif\fR can nest. +.sp +Note: do not prepend whitespace to patterns inside +\fBif\fR..\fBendif\fR. +.IP "\fBif !/\fIpattern\fB/\fIflags\fR" +.IP "\fBendif\fR" +Match the input string against the patterns between \fBif\fR +and \fBendif\fR, if and only if that same input string does +\fBnot\fR match \fIpattern\fR. The \fBif\fR..\fBendif\fR can nest. .IP "blank lines and comments" Empty lines and whitespace-only lines are ignored, as are lines whose first non-whitespace character is a `#'. .IP "multi-line text" A logical line starts with non-whitespace text. A line that starts with whitespace continues a logical line. -.IP "\fBif /\fIpattern\fB/\fIflags\fR" -.IP "\fBendif\fR" -.IP "\fBif !/\fIpattern\fB/\fIflags\fR" -.IP "\fBendif\fR" -Match the search string against the patterns between \fBif\fR -and \fBendif\fR, if and only if the search string matches (does -not match) \fIpattern\fR. The \fBif\fR..\fBendif\fR can nest. -.sp -Note: do not prepend whitespace to patterns inside -\fBif\fR..\fBendif\fR. .PP Each pattern is a regular expression enclosed by a pair of delimiters. The regular expression syntax is described in \fIre_format\fR(7). @@ -57,19 +62,30 @@ The expression delimiter can be any character, except whitespace or characters that have special meaning (traditionally the forward slash is used). The regular expression can contain whitespace. -By default, matching is case-insensitive, although following -the second slash with an `i' flag will reverse this. Other flags -are `x' (disable extended expression syntax), and `m' (enable -multi-line mode, that is, treat newline characters as special). +By default, matching is case-insensitive, and newlines are not +treated as special characters. The behavior is controlled by flags, +which are toggled by appending one or more of the following +characters after the pattern: +.IP "\fBi\fR (default: on)" +Toggles the case sensitivity flag. By default, matching is case +insensitive. +.IP "\fBx\fR (default: on)" +Toggles the extended expression syntax flag. By default, support +for extended expression syntax is enabled. +.IP "\fBm\fR (default: off)" +Toggle the multi-line mode flag. When this flag is on, the \fB^\fR +and \fB$\fR metacharacters match immediately after and immediately +before a newline character, respectively, in addition to +matching at the start and end of the input string. .SH TABLE SEARCH ORDER .na .nf .ad .fi Patterns are applied in the order as specified in the table, until a -pattern is found that matches the search string. +pattern is found that matches the input string. -Each pattern is applied to the entire lookup key string. +Each pattern is applied to the entire input string. Depending on the application, that string is an entire client hostname, an entire client IP address, or an entire mail address. Thus, no parent domain or parent network search is done, and diff --git a/postfix/man/man8/nqmgr.8 b/postfix/man/man8/oqmgr.8 similarity index 70% rename from postfix/man/man8/nqmgr.8 rename to postfix/man/man8/oqmgr.8 index 1ab7f6279..0acad100d 100644 --- a/postfix/man/man8/nqmgr.8 +++ b/postfix/man/man8/oqmgr.8 @@ -1,18 +1,18 @@ -.TH NQMGR 8 +.TH OQMGR 8 .ad .fi .SH NAME -nqmgr +oqmgr \- -Postfix queue manager +old Postfix queue manager .SH SYNOPSIS .na .nf -\fBnqmgr\fR [generic Postfix daemon options] +\fBoqmgr\fR [generic Postfix daemon options] .SH DESCRIPTION .ad .fi -The \fBnqmgr\fR daemon awaits the arrival of incoming mail +The \fBoqmgr\fR daemon awaits the arrival of incoming mail and arranges for its delivery via Postfix delivery processes. The actual mail routing strategy is delegated to the \fBtrivial-rewrite\fR(8) daemon. @@ -27,7 +27,7 @@ undeliverable bounce notifications. .nf .ad .fi -The \fBnqmgr\fR daemon maintains the following queues: +The \fBoqmgr\fR daemon maintains the following queues: .IP \fBincoming\fR Inbound mail from the network, or mail picked up by the local \fBpickup\fR agent from the \fBmaildrop\fR directory. @@ -49,7 +49,7 @@ sets them free. .nf .ad .fi -The \fBnqmgr\fR daemon keeps an eye on per-message delivery status +The \fBoqmgr\fR daemon keeps an eye on per-message delivery status reports in the following directories. Each status report file has the same name as the corresponding message file: .IP \fBbounce\fR @@ -59,7 +59,7 @@ These files are maintained by the \fBbounce\fR(8) daemon. Per-recipient status information about why mail is delayed. These files are maintained by the \fBdefer\fR(8) daemon. .PP -The \fBnqmgr\fR daemon is responsible for asking the +The \fBoqmgr\fR daemon is responsible for asking the \fBbounce\fR(8) or \fBdefer\fR(8) daemons to send non-delivery reports. .SH STRATEGIES @@ -92,10 +92,6 @@ attempt. .IP "\fBdestination status cache\fR" The queue manager avoids unnecessary delivery attempts by maintaining a short-term, in-memory list of unreachable destinations. -.IP "\fBpreemptive message scheduling\fR" -The queue manager attempts to minimize the average per-recipient delay -while still preserving the correct per-message delays, using -a sophisticated preemptive message scheduling. .SH TRIGGERS .na .nf @@ -123,7 +119,7 @@ Wakeup call, This is used by the master server to instantiate servers that should not go away forever. The action is to start an incoming queue scan. .PP -The \fBnqmgr\fR daemon reads an entire buffer worth of triggers. +The \fBoqmgr\fR daemon reads an entire buffer worth of triggers. Multiple identical trigger requests are collapsed into one, and trigger requests are sorted so that \fBA\fR and \fBF\fR precede \fBD\fR and \fBI\fR. Thus, in order to force a deferred queue run, @@ -134,15 +130,15 @@ of the arrival of new mail one would request \fBI\fR. .nf .ad .fi -None. The \fBnqmgr\fR daemon does not interact with the outside world. +None. The \fBoqmgr\fR daemon does not interact with the outside world. .SH SECURITY .na .nf .ad .fi -The \fBnqmgr\fR daemon is not security sensitive. It reads +The \fBoqmgr\fR daemon is not security sensitive. It reads single-character messages from untrusted local users, and thus may -be susceptible to denial of service attacks. The \fBnqmgr\fR daemon +be susceptible to denial of service attacks. The \fBoqmgr\fR daemon does not talk to the outside world, and it can be run at fixed low privilege in a chrooted environment. .SH DIAGNOSTICS @@ -179,8 +175,6 @@ Top-level directory of the Postfix queue. .SH "Active queue controls" .ad .fi -In the text below, \fItransport\fR is the first field in a -\fBmaster.cf\fR entry. .IP \fBqmgr_clog_warn_time\fR Minimal delay between warnings that a specific destination is clogging up the active queue. Specify 0 to disable. @@ -191,19 +185,6 @@ Limit the number of in-memory recipients. .sp This parameter also limits the size of the short-term, in-memory destination cache. -.IP \fBqmgr_message_recipient_minimum\fR -Per message minimum of in-memory recipients. -.IP \fBdefault_recipient_limit\fR -Default limit on the number of in-memory recipients per transport. -.IP \fItransport\fB_recipient_limit\fR -Limit on the number of in-memory recipients, for the named -message \fItransport\fR. -.IP \fBdefault_extra_recipient_limit\fR -Default limit on the total number of per transport in-memory -recipients that the preempting messages can have. -.IP \fItransport\fB_extra_recipient_limit\fR -Limit on the number of in-memory recipients which all preempting -messages delivered by the transport \fItransport\fR can have. .SH "Timing controls" .ad .fi @@ -231,6 +212,19 @@ delivery transport. .SH "Concurrency controls" .ad .fi +In the text below, \fItransport\fR is the first field in a +\fBmaster.cf\fR entry. +.IP "\fBqmgr_fudge_factor\fR (valid range: 10..100)" +The percentage of delivery resources that a busy mail system will +use up for delivery of a large mailing list message. +With 100%, delivery of one message does not begin before the previous +message has been delivered. This results in good performance for large +mailing lists, but results in poor response time for one-to-one mail. +With less than 100%, response time for one-to-one mail improves, +but large mailing list delivery performance suffers. In the worst +case, recipients near the beginning of a large list receive a burst +of messages immediately, while recipients near the end of that list +receive that same burst of messages a whole day later. .IP \fBinitial_destination_concurrency\fR Initial per-destination concurrency level for parallel delivery to the same destination. @@ -248,40 +242,6 @@ Default limit on the number of recipients per message transfer. .IP \fItransport\fB_destination_recipient_limit\fR Limit on the number of recipients per message transfer, for the named message \fItransport\fR. -.SH "Message scheduling" -.ad -.fi -.IP "\fItransport\fB_delivery_slot_cost\fR (valid range: 0,2,3...) -This parameter basically controls how often a message -delivered by \fItransport\fR can be preempted by another -message. -An internal per-message/transport counter is incremented by one -for each \fItransport\fB_delivery_slot_cost\fR -deliveries handled by \fItransport\fR. This counter represents -the number of "available delivery slots" for use by other messages. -Current message can be preempted by another message when that -other message can be delivered using less \fItransport\fR agents -than the value of the "available delivery slots" counter. -.sp -Value equal to 0 disables the message preemption for \fItransport\fR. -.IP \fItransport\fB_minimum_delivery_slots\fR -Message preemption is not attempted at all whenever a message -that can't ever accumulate at least \fItransport\fB_minimum_delivery_slots\fR -available delivery slots is being delivered by \fItransport\fR. -.IP "\fItransport\fB_delivery_slot_discount\fR (valid range: 0..100)" -.IP \fItransport\fB_delivery_slot_loan\fR -These parameters speed up the moment when a message preemption can happen. -Instead of waiting until the full amount of delivery slots -required is available, the preemption can happen when -\fItransport\fB_delivery_slot_discount\fR percent of the required -amount plus \fItransport\fB_delivery_slot_loan\fR still remains to -be accumulated. Note that the full amount will still have to be -accumulated before another preemption can take place later. -.IP \fBdefault_delivery_slot_cost\fR -.IP \fBdefault_minimum_delivery_slots\fR -.IP \fBdefault_delivery_slot_discount\fR -.IP \fBdefault_delivery_slot_loan\fR -Default values for the transport specific parameters described above. .SH SEE ALSO .na .nf @@ -301,8 +261,3 @@ Wietse Venema IBM T.J. Watson Research P.O. Box 704 Yorktown Heights, NY 10598, USA - -Scheduler enhancements: -Patrik Rak -Modra 6 -155 00, Prague, Czech Republic diff --git a/postfix/man/man8/qmgr.8 b/postfix/man/man8/qmgr.8 index bf66ff5bd..016aad5e0 100644 --- a/postfix/man/man8/qmgr.8 +++ b/postfix/man/man8/qmgr.8 @@ -92,6 +92,10 @@ attempt. .IP "\fBdestination status cache\fR" The queue manager avoids unnecessary delivery attempts by maintaining a short-term, in-memory list of unreachable destinations. +.IP "\fBpreemptive message scheduling\fR" +The queue manager attempts to minimize the average per-recipient delay +while still preserving the correct per-message delays, using +a sophisticated preemptive message scheduling. .SH TRIGGERS .na .nf @@ -175,6 +179,8 @@ Top-level directory of the Postfix queue. .SH "Active queue controls" .ad .fi +In the text below, \fItransport\fR is the first field in a +\fBmaster.cf\fR entry. .IP \fBqmgr_clog_warn_time\fR Minimal delay between warnings that a specific destination is clogging up the active queue. Specify 0 to disable. @@ -185,6 +191,19 @@ Limit the number of in-memory recipients. .sp This parameter also limits the size of the short-term, in-memory destination cache. +.IP \fBqmgr_message_recipient_minimum\fR +Per message minimum of in-memory recipients. +.IP \fBdefault_recipient_limit\fR +Default limit on the number of in-memory recipients per transport. +.IP \fItransport\fB_recipient_limit\fR +Limit on the number of in-memory recipients, for the named +message \fItransport\fR. +.IP \fBdefault_extra_recipient_limit\fR +Default limit on the total number of per transport in-memory +recipients that the preempting messages can have. +.IP \fItransport\fB_extra_recipient_limit\fR +Limit on the number of in-memory recipients which all preempting +messages delivered by the transport \fItransport\fR can have. .SH "Timing controls" .ad .fi @@ -212,19 +231,6 @@ delivery transport. .SH "Concurrency controls" .ad .fi -In the text below, \fItransport\fR is the first field in a -\fBmaster.cf\fR entry. -.IP "\fBqmgr_fudge_factor\fR (valid range: 10..100)" -The percentage of delivery resources that a busy mail system will -use up for delivery of a large mailing list message. -With 100%, delivery of one message does not begin before the previous -message has been delivered. This results in good performance for large -mailing lists, but results in poor response time for one-to-one mail. -With less than 100%, response time for one-to-one mail improves, -but large mailing list delivery performance suffers. In the worst -case, recipients near the beginning of a large list receive a burst -of messages immediately, while recipients near the end of that list -receive that same burst of messages a whole day later. .IP \fBinitial_destination_concurrency\fR Initial per-destination concurrency level for parallel delivery to the same destination. @@ -242,6 +248,40 @@ Default limit on the number of recipients per message transfer. .IP \fItransport\fB_destination_recipient_limit\fR Limit on the number of recipients per message transfer, for the named message \fItransport\fR. +.SH "Message scheduling" +.ad +.fi +.IP "\fItransport\fB_delivery_slot_cost\fR (valid range: 0,2,3...) +This parameter basically controls how often a message +delivered by \fItransport\fR can be preempted by another +message. +An internal per-message/transport counter is incremented by one +for each \fItransport\fB_delivery_slot_cost\fR +deliveries handled by \fItransport\fR. This counter represents +the number of "available delivery slots" for use by other messages. +Current message can be preempted by another message when that +other message can be delivered using less \fItransport\fR agents +than the value of the "available delivery slots" counter. +.sp +Value equal to 0 disables the message preemption for \fItransport\fR. +.IP \fItransport\fB_minimum_delivery_slots\fR +Message preemption is not attempted at all whenever a message +that can't ever accumulate at least \fItransport\fB_minimum_delivery_slots\fR +available delivery slots is being delivered by \fItransport\fR. +.IP "\fItransport\fB_delivery_slot_discount\fR (valid range: 0..100)" +.IP \fItransport\fB_delivery_slot_loan\fR +These parameters speed up the moment when a message preemption can happen. +Instead of waiting until the full amount of delivery slots +required is available, the preemption can happen when +\fItransport\fB_delivery_slot_discount\fR percent of the required +amount plus \fItransport\fB_delivery_slot_loan\fR still remains to +be accumulated. Note that the full amount will still have to be +accumulated before another preemption can take place later. +.IP \fBdefault_delivery_slot_cost\fR +.IP \fBdefault_minimum_delivery_slots\fR +.IP \fBdefault_delivery_slot_discount\fR +.IP \fBdefault_delivery_slot_loan\fR +Default values for the transport specific parameters described above. .SH SEE ALSO .na .nf @@ -261,3 +301,8 @@ Wietse Venema IBM T.J. Watson Research P.O. Box 704 Yorktown Heights, NY 10598, USA + +Scheduler enhancements: +Patrik Rak +Modra 6 +155 00, Prague, Czech Republic diff --git a/postfix/man/man8/smtpd.8 b/postfix/man/man8/smtpd.8 index 7a7bc3326..229092917 100644 --- a/postfix/man/man8/smtpd.8 +++ b/postfix/man/man8/smtpd.8 @@ -131,6 +131,11 @@ real-time SMTP-based content filter. Enable per-session authentication as per RFC 2554 (SASL). This functionality is available only when explicitly selected at program build time and explicitly enabled at runtime. +.IP \fBsmtpd_sasl_application_name\fR +The application name used for SASL server initialization. This +controls the name of the SASL configuration file. The default +value is \fIsmtpd\fR, corresponding to a SASL configuration file +named \fIsmtpd.conf\fR. .IP \fBsmtpd_sasl_local_domain\fR The name of the local authentication realm. .IP \fBsmtpd_sasl_security_options\fR diff --git a/postfix/postfix-install b/postfix/postfix-install index 9896c2fa6..6dd758570 100644 --- a/postfix/postfix-install +++ b/postfix/postfix-install @@ -263,6 +263,14 @@ compare_or_symlink() { } } +compare_or_hardlink() { + (cmp $1 $2 >/dev/null 2>&1 && echo Skipping $2...) || { + echo Updating $2... + rm -f $2 || exit 1 + ln $1 $2 || exit 1 + } +} + check_parent() { for path do @@ -623,6 +631,9 @@ do '$mailq_path') check_parent $MAILQ_PATH || exit 1 compare_or_symlink $SENDMAIL_PATH $MAILQ_PATH || exit 1;; + '$nqmgr_path') + compare_or_hardlink $DAEMON_DIRECTORY/qmgr \ + $DAEMON_DIRECTORY/nqmgr || exit 1;; '$newaliases_path') check_parent $NEWALIASES_PATH || exit 1 compare_or_symlink $SENDMAIL_PATH $NEWALIASES_PATH || exit 1;; diff --git a/postfix/proto/pcre_table b/postfix/proto/pcre_table index b182b54c8..e7b782c33 100644 --- a/postfix/proto/pcre_table +++ b/postfix/proto/pcre_table @@ -23,25 +23,30 @@ # .fi # The general form of a PCRE table is: # .IP "\fB/\fIpattern\fB/\fIflags result\fR" -# .IP "\fB!/\fIpattern\fB/\fIflags result\fR" -# When \fIpattern\fR matches (does not match) a search string, use +# When \fIpattern\fR matches the input string, use # the corresponding \fIresult\fR value. +# .IP "\fB!/\fIpattern\fB/\fIflags result\fR" +# When \fIpattern\fR does \fBnot\fR match the input string, use +# the corresponding \fIresult\fR value. +# .IP "\fBif /\fIpattern\fB/\fIflags\fR" +# .IP "\fBendif\fR" +# Match the input string against the patterns between \fBif\fR +# and \fBendif\fR, if and only if the input string also matches +# \fIpattern\fR. The \fBif\fR..\fBendif\fR can nest. +# .sp +# Note: do not prepend whitespace to patterns inside +# \fBif\fR..\fBendif\fR. +# .IP "\fBif !/\fIpattern\fB/\fIflags\fR" +# .IP "\fBendif\fR" +# Match the input string against the patterns between \fBif\fR +# and \fBendif\fR, if and only if the input string does \fBnot\fR +# match \fIpattern\fR. The \fBif\fR..\fBendif\fR can nest. # .IP "blank lines and comments" # Empty lines and whitespace-only lines are ignored, as # are lines whose first non-whitespace character is a `#'. # .IP "multi-line text" # A logical line starts with non-whitespace text. A line that # starts with whitespace continues a logical line. -# .IP "\fBif /\fIpattern\fB/\fIflags\fR" -# .IP "\fBendif\fR" -# .IP "\fBif !/\fIpattern\fB/\fIflags\fR" -# .IP "\fBendif\fR" -# Match the search string against the patterns between \fBif\fR -# and \fBendif\fR, if and only if the search string matches (does -# not match) \fIpattern\fR. The \fBif\fR..\fBendif\fR can nest. -# .sp -# Note: do not prepend whitespace to patterns inside -# \fBif\fR..\fBendif\fR. # .PP # Each pattern is a perl-like regular expression. The expression # delimiter can be any character, except whitespace or characters @@ -103,9 +108,9 @@ # .ad # .fi # Patterns are applied in the order as specified in the table, until a -# pattern is found that matches the search string. +# pattern is found that matches the input string. # -# Each pattern is applied to the entire lookup key string. +# Each pattern is applied to the entire input string. # Depending on the application, that string is an entire client # hostname, an entire client IP address, or an entire mail address. # Thus, no parent domain or parent network search is done, and diff --git a/postfix/proto/regexp_table b/postfix/proto/regexp_table index e12b0f789..770702d73 100644 --- a/postfix/proto/regexp_table +++ b/postfix/proto/regexp_table @@ -23,25 +23,30 @@ # .fi # The general form of a Postfix regular expression table is: # .IP "\fB/\fIpattern\fB/\fIflags result\fR" -# .IP "\fB!/\fIpattern\fB/\fIflags result\fR" -# When \fIpattern\fR matches (does not match) a search string, +# When \fIpattern\fR matches the input string, # use the corresponding \fIresult\fR value. +# .IP "\fB!/\fIpattern\fB/\fIflags result\fR" +# When \fIpattern\fR does \fBnot\fR match the input string, +# use the corresponding \fIresult\fR value. +# .IP "\fBif /\fIpattern\fB/\fIflags\fR" +# .IP "\fBendif\fR" +# Match the input string against the patterns between \fBif\fR +# and \fBendif\fR, if and only if that same input string also +# matches \fIpattern\fR. The \fBif\fR..\fBendif\fR can nest. +# .sp +# Note: do not prepend whitespace to patterns inside +# \fBif\fR..\fBendif\fR. +# .IP "\fBif !/\fIpattern\fB/\fIflags\fR" +# .IP "\fBendif\fR" +# Match the input string against the patterns between \fBif\fR +# and \fBendif\fR, if and only if that same input string does +# \fBnot\fR match \fIpattern\fR. The \fBif\fR..\fBendif\fR can nest. # .IP "blank lines and comments" # Empty lines and whitespace-only lines are ignored, as # are lines whose first non-whitespace character is a `#'. # .IP "multi-line text" # A logical line starts with non-whitespace text. A line that # starts with whitespace continues a logical line. -# .IP "\fBif /\fIpattern\fB/\fIflags\fR" -# .IP "\fBendif\fR" -# .IP "\fBif !/\fIpattern\fB/\fIflags\fR" -# .IP "\fBendif\fR" -# Match the search string against the patterns between \fBif\fR -# and \fBendif\fR, if and only if the search string matches (does -# not match) \fIpattern\fR. The \fBif\fR..\fBendif\fR can nest. -# .sp -# Note: do not prepend whitespace to patterns inside -# \fBif\fR..\fBendif\fR. # .PP # Each pattern is a regular expression enclosed by a pair of delimiters. # The regular expression syntax is described in \fIre_format\fR(7). @@ -49,17 +54,28 @@ # or characters that have special meaning (traditionally the forward # slash is used). The regular expression can contain whitespace. # -# By default, matching is case-insensitive, although following -# the second slash with an `i' flag will reverse this. Other flags -# are `x' (disable extended expression syntax), and `m' (enable -# multi-line mode, that is, treat newline characters as special). +# By default, matching is case-insensitive, and newlines are not +# treated as special characters. The behavior is controlled by flags, +# which are toggled by appending one or more of the following +# characters after the pattern: +# .IP "\fBi\fR (default: on)" +# Toggles the case sensitivity flag. By default, matching is case +# insensitive. +# .IP "\fBx\fR (default: on)" +# Toggles the extended expression syntax flag. By default, support +# for extended expression syntax is enabled. +# .IP "\fBm\fR (default: off)" +# Toggle the multi-line mode flag. When this flag is on, the \fB^\fR +# and \fB$\fR metacharacters match immediately after and immediately +# before a newline character, respectively, in addition to +# matching at the start and end of the input string. # TABLE SEARCH ORDER # .ad # .fi # Patterns are applied in the order as specified in the table, until a -# pattern is found that matches the search string. +# pattern is found that matches the input string. # -# Each pattern is applied to the entire lookup key string. +# Each pattern is applied to the entire input string. # Depending on the application, that string is an entire client # hostname, an entire client IP address, or an entire mail address. # Thus, no parent domain or parent network search is done, and diff --git a/postfix/src/global/Makefile.in b/postfix/src/global/Makefile.in index 951071b95..51f7343c9 100644 --- a/postfix/src/global/Makefile.in +++ b/postfix/src/global/Makefile.in @@ -1,72 +1,73 @@ SHELL = /bin/sh -SRCS = been_here.c bounce.c canon_addr.c cleanup_strerror.c clnt_stream.c \ - debug_peer.c debug_process.c defer.c deliver_completed.c \ - deliver_flock.c deliver_pass.c deliver_request.c domain_list.c \ - dot_lockfile.c dot_lockfile_as.c ext_prop.c file_id.c \ - header_opts.c is_header.c mail_addr.c mail_addr_crunch.c \ - mail_addr_find.c mail_addr_map.c mail_command_server.c \ - mail_command_client.c mail_conf.c mail_conf_bool.c mail_conf_int.c \ - mail_conf_raw.c mail_conf_str.c mail_connect.c mail_copy.c \ - mail_date.c mail_error.c mail_flush.c mail_open_ok.c mail_params.c \ - mail_pathname.c mail_queue.c mail_run.c \ - mail_scan_dir.c mail_stream.c mail_task.c mail_trigger.c maps.c \ - mark_corrupt.c mkmap_db.c mkmap_dbm.c mkmap_open.c mynetworks.c \ - mypwd.c namadr_list.c off_cvt.c opened.c own_inet_addr.c \ - pipe_command.c post_mail.c quote_821_local.c \ +SRCS = abounce.c anvil_clnt.c been_here.c bounce.c bounce_log.c \ + canon_addr.c cfg_parser.c cleanup_strerror.c cleanup_strflags.c \ + clnt_stream.c debug_peer.c debug_process.c defer.c \ + deliver_completed.c deliver_flock.c deliver_pass.c deliver_request.c \ + dict_ldap.c dict_mysql.c dict_pgsql.c dict_proxy.c domain_list.c \ + dot_lockfile.c dot_lockfile_as.c ext_prop.c file_id.c flush_clnt.c \ + header_opts.c header_token.c hold_message.c input_transp.c \ + is_header.c log_adhoc.c mail_addr.c mail_addr_crunch.c \ + mail_addr_find.c mail_addr_map.c mail_command_client.c \ + mail_command_server.c mail_conf.c mail_conf_bool.c mail_conf_int.c \ + mail_conf_raw.c mail_conf_str.c mail_conf_time.c mail_connect.c \ + mail_copy.c mail_date.c mail_dict.c mail_error.c mail_flush.c \ + mail_open_ok.c mail_params.c mail_pathname.c mail_queue.c \ + mail_run.c mail_scan_dir.c mail_stream.c mail_task.c mail_trigger.c \ + maps.c mark_corrupt.c match_parent_style.c mbox_conf.c \ + mbox_open.c mime_state.c mkmap_db.c mkmap_dbm.c mkmap_open.c \ + mynetworks.c mypwd.c namadr_list.c off_cvt.c opened.c \ + own_inet_addr.c pipe_command.c post_mail.c quote_821_local.c \ quote_822_local.c rec_streamlf.c rec_type.c recipient_list.c \ record.c remove.c resolve_clnt.c resolve_local.c rewrite_clnt.c \ - sent.c smtp_stream.c split_addr.c string_list.c sys_exits.c \ - timed_ipc.c tok822_find.c tok822_node.c tok822_parse.c \ - tok822_resolve.c tok822_rewrite.c tok822_tree.c xtext.c bounce_log.c \ - flush_clnt.c mail_conf_time.c mbox_conf.c mbox_open.c abounce.c \ - verp_sender.c match_parent_style.c mime_state.c header_token.c \ - strip_addr.c virtual8_maps.c hold_message.c verify_clnt.c \ - trace.c log_adhoc.c verify.c dict_proxy.c mail_dict.c input_transp.c \ - cleanup_strflags.c anvil_clnt.c -OBJS = been_here.o bounce.o canon_addr.o cleanup_strerror.o clnt_stream.o \ - debug_peer.o debug_process.o defer.o deliver_completed.o \ - deliver_flock.o deliver_pass.o deliver_request.o domain_list.o \ - dot_lockfile.o dot_lockfile_as.o ext_prop.o file_id.o \ - header_opts.o is_header.o mail_addr.o mail_addr_crunch.o \ - mail_addr_find.o mail_addr_map.o mail_command_server.o \ - mail_command_client.o mail_conf.o mail_conf_bool.o mail_conf_int.o \ - mail_conf_raw.o mail_conf_str.o mail_connect.o mail_copy.o \ - mail_date.o mail_error.o mail_flush.o mail_open_ok.o mail_params.o \ - mail_pathname.o mail_queue.o mail_run.o \ - mail_scan_dir.o mail_stream.o mail_task.o mail_trigger.o maps.o \ - mark_corrupt.o mkmap_db.o mkmap_dbm.o mkmap_open.o mynetworks.o \ - mypwd.o namadr_list.o off_cvt.o opened.o own_inet_addr.o \ - pipe_command.o post_mail.o quote_821_local.o \ + sent.c smtp_stream.c split_addr.c string_list.c strip_addr.c \ + sys_exits.c timed_ipc.c tok822_find.c tok822_node.c tok822_parse.c \ + tok822_resolve.c tok822_rewrite.c tok822_tree.c trace.c verify.c \ + verify_clnt.c verp_sender.c virtual8_maps.c xtext.c +OBJS = abounce.o anvil_clnt.o been_here.o bounce.o bounce_log.o \ + canon_addr.o cfg_parser.o cleanup_strerror.o cleanup_strflags.o \ + clnt_stream.o debug_peer.o debug_process.o defer.o \ + deliver_completed.o deliver_flock.o deliver_pass.o deliver_request.o \ + dict_ldap.o dict_mysql.o dict_pgsql.o dict_proxy.o domain_list.o \ + dot_lockfile.o dot_lockfile_as.o ext_prop.o file_id.o flush_clnt.o \ + header_opts.o header_token.o hold_message.o input_transp.o \ + is_header.o log_adhoc.o mail_addr.o mail_addr_crunch.o \ + mail_addr_find.o mail_addr_map.o mail_command_client.o \ + mail_command_server.o mail_conf.o mail_conf_bool.o mail_conf_int.o \ + mail_conf_raw.o mail_conf_str.o mail_conf_time.o mail_connect.o \ + mail_copy.o mail_date.o mail_dict.o mail_error.o mail_flush.o \ + mail_open_ok.o mail_params.o mail_pathname.o mail_queue.o \ + mail_run.o mail_scan_dir.o mail_stream.o mail_task.o mail_trigger.o \ + maps.o mark_corrupt.o match_parent_style.o mbox_conf.o \ + mbox_open.o mime_state.o mkmap_db.o mkmap_dbm.o mkmap_open.o \ + mynetworks.o mypwd.o namadr_list.o off_cvt.o opened.o \ + own_inet_addr.o pipe_command.o post_mail.o quote_821_local.o \ quote_822_local.o rec_streamlf.o rec_type.o recipient_list.o \ record.o remove.o resolve_clnt.o resolve_local.o rewrite_clnt.o \ - sent.o smtp_stream.o split_addr.o string_list.o sys_exits.o \ - timed_ipc.o tok822_find.o tok822_node.o tok822_parse.o \ - tok822_resolve.o tok822_rewrite.o tok822_tree.o xtext.o bounce_log.o \ - flush_clnt.o mail_conf_time.o mbox_conf.o mbox_open.o abounce.o \ - verp_sender.o match_parent_style.o mime_state.o header_token.o \ - strip_addr.o virtual8_maps.o hold_message.o verify_clnt.o \ - trace.o log_adhoc.o verify.o dict_proxy.o mail_dict.o input_transp.o \ - cleanup_strflags.o anvil_clnt.o -HDRS = been_here.h bounce.h canon_addr.h cleanup_user.h clnt_stream.h \ - config.h debug_peer.h debug_process.h defer.h deliver_completed.h \ - deliver_flock.h deliver_pass.h deliver_request.h domain_list.h \ - dot_lockfile.h dot_lockfile_as.h ext_prop.h file_id.h \ - header_opts.h is_header.h mail_addr.h mail_addr_crunch.h \ + sent.o smtp_stream.o split_addr.o string_list.o strip_addr.o \ + sys_exits.o timed_ipc.o tok822_find.o tok822_node.o tok822_parse.o \ + tok822_resolve.o tok822_rewrite.o tok822_tree.o trace.o verify.o \ + verify_clnt.o verp_sender.o virtual8_maps.o xtext.o +HDRS = abounce.h anvil_clnt.h been_here.h bounce.h bounce_log.h \ + canon_addr.h cfg_parser.h cleanup_user.h clnt_stream.h config.h \ + debug_peer.h debug_process.h defer.h deliver_completed.h \ + deliver_flock.h deliver_pass.h deliver_request.h dict_ldap.h \ + dict_mysql.h dict_pgsql.h dict_proxy.h domain_list.h dot_lockfile.h \ + dot_lockfile_as.h ext_prop.h file_id.h flush_clnt.h header_opts.h \ + header_token.h hold_message.h input_transp.h is_header.h \ + lex_822.h log_adhoc.h mail_addr.h mail_addr_crunch.h \ mail_addr_find.h mail_addr_map.h mail_conf.h mail_copy.h \ - mail_date.h mail_error.h mail_flush.h mail_open_ok.h mail_params.h \ - mail_proto.h mail_queue.h mail_run.h mail_scan_dir.h mail_stream.h \ - mail_task.h mail_version.h maps.h mark_corrupt.h mkmap.h \ - mynetworks.h mypwd.h namadr_list.h off_cvt.h opened.h \ - own_inet_addr.h pipe_command.h post_mail.h \ - quote_821_local.h quote_822_local.h rec_streamlf.h rec_type.h \ - recipient_list.h record.h resolve_clnt.h resolve_local.h \ - rewrite_clnt.h sent.h smtp_stream.h split_addr.h string_list.h \ - sys_exits.h timed_ipc.h tok822.h xtext.h bounce_log.h flush_clnt.h \ - mbox_conf.h mbox_open.h abounce.h qmqp_proto.h verp_sender.h \ - match_parent_style.h quote_flags.h mime_state.h header_token.h \ - lex_822.h strip_addr.h virtual8_maps.h hold_message.h verify_clnt.h \ - trace.h log_adhoc.h verify.h dict_proxy.h mail_dict.h qmgr_user.h \ - input_transp.h anvil_clnt.h + mail_date.h mail_dict.h mail_error.h mail_flush.h mail_open_ok.h \ + mail_params.h mail_proto.h mail_queue.h mail_run.h mail_scan_dir.h \ + mail_stream.h mail_task.h mail_version.h maps.h mark_corrupt.h \ + match_parent_style.h mbox_conf.h mbox_open.h mime_state.h \ + mkmap.h mynetworks.h mypwd.h namadr_list.h off_cvt.h opened.h \ + own_inet_addr.h pipe_command.h post_mail.h qmgr_user.h \ + qmqp_proto.h quote_821_local.h quote_822_local.h quote_flags.h \ + rec_streamlf.h rec_type.h recipient_list.h record.h resolve_clnt.h \ + resolve_local.h rewrite_clnt.h sent.h smtp_stream.h split_addr.h \ + string_list.h strip_addr.h sys_exits.h timed_ipc.h tok822.h \ + trace.h verify.h verify_clnt.h verp_sender.h virtual8_maps.h \ + xtext.h TESTSRC = rec2stream.c stream2rec.c recdump.c DEFS = -I. -I$(INC_DIR) -D$(SYSTYPE) CFLAGS = $(DEBUG) $(OPT) $(DEFS) @@ -442,6 +443,17 @@ canon_addr.o: ../../include/vbuf.h canon_addr.o: ../../include/mymalloc.h canon_addr.o: rewrite_clnt.h canon_addr.o: canon_addr.h +cfg_parser.o: cfg_parser.c +cfg_parser.o: ../../include/sys_defs.h +cfg_parser.o: ../../include/msg.h +cfg_parser.o: ../../include/mymalloc.h +cfg_parser.o: ../../include/vstring.h +cfg_parser.o: ../../include/vbuf.h +cfg_parser.o: ../../include/dict.h +cfg_parser.o: ../../include/vstream.h +cfg_parser.o: ../../include/argv.h +cfg_parser.o: mail_conf.h +cfg_parser.o: cfg_parser.h cleanup_strerror.o: cleanup_strerror.c cleanup_strerror.o: ../../include/sys_defs.h cleanup_strerror.o: ../../include/vstring.h @@ -546,6 +558,12 @@ deliver_request.o: ../../include/attr.h deliver_request.o: mail_open_ok.h deliver_request.o: recipient_list.h deliver_request.o: deliver_request.h +dict_ldap.o: dict_ldap.c +dict_ldap.o: ../../include/sys_defs.h +dict_mysql.o: dict_mysql.c +dict_mysql.o: ../../include/sys_defs.h +dict_pgsql.o: dict_pgsql.c +dict_pgsql.o: ../../include/sys_defs.h dict_proxy.o: dict_proxy.c dict_proxy.o: ../../include/sys_defs.h dict_proxy.o: ../../include/msg.h @@ -803,7 +821,9 @@ mail_dict.o: ../../include/vstream.h mail_dict.o: ../../include/vbuf.h mail_dict.o: ../../include/argv.h mail_dict.o: ../../include/msg.h -mail_dict.o: dict_proxy.h +mail_dict.o: dict_ldap.h +mail_dict.o: dict_mysql.h +mail_dict.o: dict_pgsql.h mail_dict.o: mail_dict.h mail_error.o: mail_error.c mail_error.o: ../../include/sys_defs.h diff --git a/postfix/src/global/cfg_parser.c b/postfix/src/global/cfg_parser.c new file mode 100644 index 000000000..58a3c4afd --- /dev/null +++ b/postfix/src/global/cfg_parser.c @@ -0,0 +1,298 @@ +/*++ +/* NAME +/* cfg_parser 3 +/* SUMMARY +/* configuration parser utilities +/* SYNOPSIS +/* #include "cfg_parser.h" +/* +/* CFG_PARSER *cfg_parser_alloc(pname) +/* const char *pname; +/* +/* CFG_PARSER *cfg_parser_free(parser) +/* CFG_PARSER *parser; +/* +/* char *cfg_get_str(parser, name, defval, min, max) +/* const CFG_PARSER *parser; +/* const char *name; +/* const char *defval; +/* int min; +/* int max; +/* +/* int cfg_get_int(parser, name, defval, min, max) +/* const CFG_PARSER *parser; +/* const char *name; +/* int defval; +/* int min; +/* int max; +/* +/* int cfg_get_bool(parser, name, defval) +/* const CFG_PARSER *parser; +/* const char *name; +/* int defval; +/* DESCRIPTION +/* This module implements utilities for parsing parameters defined +/* either as "\fIname\fR = \fBvalue\fR" in a file pointed to by +/* \fIpname\fR (the old MySQL style), or as "\fIpname\fR_\fIname\fR = +/* \fBvalue\fR" in main.cf (the old LDAP style). It unifies the +/* two styles and provides support for range checking. +/* +/* \fIcfg_parser_alloc\fR initializes the parser. +/* +/* \fIcfg_parser_free\fR releases the parser. +/* +/* \fIcfg_get_str\fR looks up a string. +/* +/* \fIcfg_get_int\fR looks up an integer. +/* +/* \fIcfg_get_bool\fR looks up a boolean value. +/* +/* \fIdefval\fR 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. +/* +/* Conveniently, \fIcfg_get_str\fR returns \fBNULL\fR if +/* \fIdefval\fR is \fBNULL\fR and no value was found. The returned +/* string has to be freed by the caller if not \fBNULL\fR. +/* DIAGNOSTICS +/* Fatal errors: bad string length, malformed numerical value, malformed +/* boolean value. +/* SEE ALSO +/* mail_conf_str(3) string-valued global configuration parameter support +/* mail_conf_int(3) integer-valued configuration parameter support +/* mail_conf_bool(3) boolean-valued configuration parameter support +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/* +/* Liviu Daia +/* Institute of Mathematics of the Romanian Academy +/* P.O. BOX 1-764 +/* RO-014700 Bucharest, ROMANIA +/*--*/ + +/* System library. */ + +#include "sys_defs.h" + +#include +#include + +/* Utility library. */ + +#include "msg.h" +#include "mymalloc.h" +#include "vstring.h" +#include "dict.h" + +/* Global library. */ + +#include "mail_conf.h" + +/* Application-specific. */ + +#include "cfg_parser.h" + +/* get string from file */ + +static char *get_dict_str(const struct CFG_PARSER *parser, + const char *name, const char *defval, + int min, int max) +{ + const char *strval; + int len; + + if ((strval = (char *) dict_lookup(parser->name, name)) == 0) + strval = defval; + + len = strlen(strval); + if (min && len < min) + msg_fatal("%s: bad string length %d < %d: %s = %s", + parser->name, len, min, name, strval); + if (max && len > max) + msg_fatal("%s: bad string length %d > %d: %s = %s", + parser->name, len, max, name, strval); + return (mystrdup(strval)); +} + +/* get string from main.cf */ + +static char *get_main_str(const struct CFG_PARSER *parser, + const char *name, const char *defval, + int min, int max) +{ + static VSTRING *buf = 0; + + if (buf == 0) + buf = vstring_alloc(15); + vstring_sprintf(buf, "%s_%s", parser->name, name); + return ((char *) get_mail_conf_str(vstring_str(buf), defval, min, max)); +} + +/* get integer from file */ + +static int get_dict_int(const struct CFG_PARSER *parser, + const char *name, int defval, int min, int max) +{ + const char *strval; + int intval; + char junk; + + if ((strval = (char *) dict_lookup(parser->name, name)) != 0) { + if (sscanf(strval, "%d%c", &intval, &junk) != 1) + msg_fatal("%s: bad numerical configuration: %s = %s", + parser->name, name, strval); + } else + intval = defval; + if (min && intval < min) + msg_fatal("%s: invalid %s parameter value %d < %d", + parser->name, name, intval, min); + if (max && intval > max) + msg_fatal("%s: invalid %s parameter value %d > %d", + parser->name, name, intval, max); + return (intval); +} + +/* get integer from main.cf */ + +static int get_main_int(const struct CFG_PARSER *parser, + const char *name, int defval, int min, int max) +{ + static VSTRING *buf = 0; + + if (buf == 0) + buf = vstring_alloc(15); + vstring_sprintf(buf, "%s_%s", parser->name, name); + return (get_mail_conf_int(vstring_str(buf), defval, min, max)); +} + +/* get boolean option from file */ + +static int get_dict_bool(const struct CFG_PARSER *parser, + const char *name, int defval) +{ + const char *strval; + int intval; + + if ((strval = (char *) dict_lookup(parser->name, name)) != 0) { + if (strcasecmp(strval, CONFIG_BOOL_YES) == 0) { + intval = 1; + } else if (strcasecmp(strval, CONFIG_BOOL_NO) == 0) { + intval = 0; + } else { + msg_fatal("%s: bad boolean configuration: %s = %s", + parser->name, name, strval); + } + } else + intval = defval; + return (intval); +} + +/* get boolean option from main.cf */ + +static int get_main_bool(const struct CFG_PARSER *parser, + const char *name, int defval) +{ + static VSTRING *buf = 0; + + if (buf == 0) + buf = vstring_alloc(15); + vstring_sprintf(buf, "%s_%s", parser->name, name); + return (get_mail_conf_bool(vstring_str(buf), defval)); +} + +/* initialize parser */ + +CFG_PARSER *cfg_parser_alloc(const char *pname) +{ + const char *myname = "cfg_parser_alloc"; + CFG_PARSER *parser; + + if (pname == 0 || *pname == 0) + msg_fatal("%s: null parser name", myname); + parser = (CFG_PARSER *) mymalloc(sizeof(*parser)); + parser->name = mystrdup(pname); + if (*parser->name == '/' || *parser->name == '.') { + dict_load_file(parser->name, parser->name); + parser->get_str = get_dict_str; + parser->get_int = get_dict_int; + parser->get_bool = get_dict_bool; + } else { + parser->get_str = get_main_str; + parser->get_int = get_main_int; + parser->get_bool = get_main_bool; + } + return (parser); +} + +/* get string */ + +char *cfg_get_str(const CFG_PARSER *parser, const char *name, + const char *defval, int min, int max) +{ + const char *myname = "cfg_get_str"; + char *strval; + + strval = parser->get_str(parser, name, (defval ? defval : ""), min, max); + if (defval == 0 && *strval == 0) { + /* the caller wants NULL instead of "" */ + myfree(strval); + strval = 0; + } + if (msg_verbose) + msg_info("%s: %s: %s = %s", myname, parser->name, name, + (strval ? strval : "")); + return (strval); +} + +/* get integer */ + +int cfg_get_int(const CFG_PARSER *parser, const char *name, int defval, + int min, int max) +{ + const char *myname = "cfg_get_int"; + int intval; + + intval = parser->get_int(parser, name, defval, min, max); + if (msg_verbose) + msg_info("%s: %s: %s = %d", myname, parser->name, name, intval); + return (intval); +} + +/* get boolean option */ + +int cfg_get_bool(const CFG_PARSER *parser, const char *name, int defval) +{ + const char *myname = "cfg_get_bool"; + int intval; + + intval = parser->get_bool(parser, name, defval); + if (msg_verbose) + msg_info("%s: %s: %s = %s", myname, parser->name, name, + (intval ? "on" : "off")); + return (intval); +} + +/* release parser */ + +CFG_PARSER *cfg_parser_free(CFG_PARSER *parser) +{ + const char *myname = "cfg_parser_free"; + + if (parser->name == 0 || *parser->name == 0) + msg_panic("%s: null parser name", myname); + if (*parser->name == '/' || *parser->name == '.') { + if (dict_handle(parser->name)) + dict_unregister(parser->name); + } + myfree(parser->name); + myfree((char *) parser); + return (0); +} diff --git a/postfix/src/global/cfg_parser.h b/postfix/src/global/cfg_parser.h new file mode 100644 index 000000000..40544a24a --- /dev/null +++ b/postfix/src/global/cfg_parser.h @@ -0,0 +1,49 @@ +#ifndef _CFG_PARSER_H_INCLUDED_ +#define _CFG_PARSER_H_INCLUDED_ + +/*++ +/* NAME +/* cfg_parser 3h +/* SUMMARY +/* configuration parser utilities +/* SYNOPSIS +/* #include "cfg_parser.h" + DESCRIPTION + .nf + + /* + * External interface. + */ + +typedef struct CFG_PARSER { + char *name; + char *(*get_str) (const struct CFG_PARSER *, const char *, const char *, + int, int); + int (*get_int) (const struct CFG_PARSER *, const char *, int, int, int); + int (*get_bool) (const struct CFG_PARSER *, const char *, int); +} CFG_PARSER; + +extern CFG_PARSER *cfg_parser_alloc(const char *); +extern char *cfg_get_str(const CFG_PARSER *, const char *, const char *, + int, int); +extern int cfg_get_int(const CFG_PARSER *, const char *, int, int, int); +extern int cfg_get_bool(const CFG_PARSER *, const char *, int); +extern CFG_PARSER *cfg_parser_free(CFG_PARSER *); + +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/* +/* Liviu Daia +/* Institute of Mathematics of the Romanian Academy +/* P.O. BOX 1-764 +/* RO-014700 Bucharest, ROMANIA +/*--*/ + +#endif diff --git a/postfix/src/util/dict_ldap.c b/postfix/src/global/dict_ldap.c similarity index 80% rename from postfix/src/util/dict_ldap.c rename to postfix/src/global/dict_ldap.c index 2a819be41..d47d2cc82 100644 --- a/postfix/src/util/dict_ldap.c +++ b/postfix/src/global/dict_ldap.c @@ -8,26 +8,26 @@ /* /* DICT *dict_ldap_open(attribute, dummy, dict_flags) /* const char *attribute; -/* int dummy; -/* int dict_flags; +/* int dummy; +/* int dict_flags; /* DESCRIPTION /* dict_ldap_open() makes LDAP user information accessible via /* the generic dictionary operations described in dict_open(3). /* /* Arguments: /* .IP ldapsource -/* Either the path to the LDAP configuration file (if it starts -/* with '/' or '.'), or the prefix which will be used to obtain -/* configuration parameters for this search. +/* Either the path to the LDAP configuration file (if it starts +/* with '/' or '.'), or the prefix which will be used to obtain +/* configuration parameters for this search. /* -/* In the first case, the configuration variables below are -/* specified in the file as \fBname\fR=\fBvalue\fR pairs. +/* In the first case, the configuration variables below are +/* specified in the file as \fBname\fR=\fBvalue\fR pairs. /* -/* In the second case, the configuration variables are prefixed -/* with the value of \fIldapsource\fR and an underscore, -/* and they are specified in main.cf. For example, if this -/* value is \fBldapone\fR, the variables would look like -/* \fBldapone_server_host\fR, \fBldapone_search_base\fR, and so on. +/* In the second case, the configuration variables are prefixed +/* with the value of \fIldapsource\fR and an underscore, +/* and they are specified in main.cf. For example, if this +/* value is \fBldapone\fR, the variables would look like +/* \fBldapone_server_host\fR, \fBldapone_search_base\fR, and so on. /* .IP dummy /* Not used; this argument exists only for compatibility with /* the dict_open(3) interface. @@ -43,7 +43,7 @@ /* The LDAP search base, for example: \fIO=organization name, C=country\fR. /* .IP domain /* If specified, only lookups ending in this value will be queried. -/* This can significantly reduce the query load on the LDAP server. +/* This can significantly reduce the query load on the LDAP server. /* .IP timeout /* Deadline for LDAP open() and LDAP search() . /* .IP query_filter @@ -57,7 +57,7 @@ /* RFC822 addresses, for example \fImaildrop\fR. /* .IP special_result_attribute /* The attribute(s) of directory entries that can contain DNs or URLs. -/* If found, a recursive subsequent search is done using their values. +/* If found, a recursive subsequent search is done using their values. /* .IP scope /* LDAP search scope: sub, base, or one. /* .IP bind @@ -119,7 +119,7 @@ /* Cipher suite to use in SSL/TLS negotiations. /* .IP debuglevel /* Debug level. See 'loglevel' option in slapd.conf(5) man page. -/* Currently only in openldap libraries (and derivatives). +/* Currently only in openldap libraries (and derivatives). /* SEE ALSO /* dict(3) generic dictionary manager /* AUTHOR(S) @@ -135,9 +135,19 @@ /* John Hensley /* john@sunislelodge.com /* -/* LaMont Jones -/* lamont@hp.com +/* Current maintainers: /* +/* LaMont Jones +/* lamont@debian.org +/* +/* Victor Duchovni +/* Morgan Stanley +/* New York, USA +/* +/* Liviu Daia +/* Institute of Mathematics of the Romanian Academy +/* P.O. BOX 1-764 +/* RO-014700 Bucharest, ROMANIA /*--*/ /* System library. */ @@ -155,6 +165,7 @@ #include #include #include +#include /* * Older APIs have weird memory freeing behavior. @@ -179,13 +190,16 @@ #include "mymalloc.h" #include "vstring.h" #include "dict.h" -#include "dict_ldap.h" #include "stringops.h" #include "binhash.h" -/* AAARGH!! */ +/* Global library. */ -#include "../global/mail_conf.h" +#include "cfg_parser.h" + +/* Application-specific. */ + +#include "dict_ldap.h" typedef struct { LDAP *conn_ld; @@ -198,6 +212,7 @@ typedef struct { */ typedef struct { DICT dict; /* generic member */ + CFG_PARSER *parser; char *ldapsource; char *server_host; int server_port; @@ -238,12 +253,6 @@ typedef struct { static BINHASH *conn_hash = 0; -typedef struct { - char *(*get_str) (const char *, const char *, const char *, int, int); - int (*get_int) (const char *, const char *, int, int, int); - int (*get_bool) (const char *, const char *, int); -} CFG_PARSER; - #if defined(LDAP_API_FEATURE_X_OPENLDAP) || !defined(LDAP_OPT_NETWORK_TIMEOUT) /* * LDAP connection timeout support. @@ -273,105 +282,6 @@ static void dict_ldap_logprint(LDAP_CONST char *data) myfree(buf); } - -static char *dict_ldap_get_dict_str(const char *opt_dict_name, - const char *name, const char *defval, - int min, int max) -{ - const char *strval; - int len; - - if ((strval = (char *) dict_lookup(opt_dict_name, name)) == 0) - strval = defval; - - len = strlen(strval); - if (min && len < min) - msg_fatal("%s: bad string length %d < %d: %s = %s", - opt_dict_name, len, min, name, strval); - if (max && len > max) - msg_fatal("%s: bad string length %d > %d: %s = %s", - opt_dict_name, len, max, name, strval); - return (mystrdup(strval)); -} - -static char *dict_ldap_get_mail_str(const char *opt_dict_name, - const char *name, const char *defval, - int min, int max) -{ - static VSTRING *buf = 0; - - if (buf == 0) - buf = vstring_alloc(15); - vstring_sprintf(buf, "%s_%s", opt_dict_name, name); - return ((char *) get_mail_conf_str(vstring_str(buf), - defval, min, max)); -} - -static int dict_ldap_get_dict_int(const char *opt_dict_name, - const char *name, int defval, int min, int max) -{ - const char *strval; - int intval; - char junk; - - if ((strval = (char *) dict_lookup(opt_dict_name, name)) != 0) { - if (sscanf(strval, "%d%c", &intval, &junk) != 1) - msg_fatal("%s: bad numerical configuration: %s = %s", - opt_dict_name, name, strval); - } else - intval = defval; - if (min && intval < min) - msg_fatal("%s: invalid %s parameter value %d < %d", - opt_dict_name, name, intval, min); - if (max && intval > max) - msg_fatal("%s: invalid %s parameter value %d > %d", - opt_dict_name, name, intval, max); - return (intval); -} - -static int dict_ldap_get_mail_int(const char *opt_dict_name, - const char *name, int defval, int min, int max) -{ - static VSTRING *buf = 0; - - if (buf == 0) - buf = vstring_alloc(15); - vstring_sprintf(buf, "%s_%s", opt_dict_name, name); - return (get_mail_conf_int(vstring_str(buf), defval, min, max)); -} - -static int dict_ldap_get_dict_bool(const char *opt_dict_name, - const char *name, int defval) -{ - const char *strval; - int intval; - - if ((strval = (char *) dict_lookup(opt_dict_name, name)) != 0) { - if (strcasecmp(strval, CONFIG_BOOL_YES) == 0) { - intval = 1; - } else if (strcasecmp(strval, CONFIG_BOOL_NO) == 0) { - intval = 0; - } else { - msg_fatal("%s: bad boolean configuration: %s = %s", - opt_dict_name, name, strval); - } - } else - intval = defval; - return (intval); -} - -static int dict_ldap_get_mail_bool(const char *opt_dict_name, - const char *name, int defval) -{ - static VSTRING *buf = 0; - - if (buf == 0) - buf = vstring_alloc(15); - vstring_sprintf(buf, "%s_%s", opt_dict_name, name); - return (get_mail_conf_bool(vstring_str(buf), defval)); -} - - static int dict_ldap_get_errno(LDAP * ld) { int rc; @@ -1243,6 +1153,7 @@ static void dict_ldap_close(DICT *dict) } binhash_delete(conn_hash, ht->key, ht->key_len, myfree); } + cfg_parser_free(dict_ldap->parser); myfree(dict_ldap->ldapsource); myfree(dict_ldap->server_host); myfree(dict_ldap->search_base); @@ -1275,7 +1186,6 @@ DICT *dict_ldap_open(const char *ldapsource, int dummy, int dict_flags) char *s; char *h; char *server_host; - CFG_PARSER parser; char *domainlist; char *scope; char *attr; @@ -1291,43 +1201,22 @@ DICT *dict_ldap_open(const char *ldapsource, int dummy, int dict_flags) dict_ldap->dict.flags = dict_flags | DICT_FLAG_FIXED; dict_ldap->ld = NULL; + dict_ldap->parser = cfg_parser_alloc(ldapsource); dict_ldap->ldapsource = mystrdup(ldapsource); - if (ldapsource[0] == '/' || ldapsource[0] == '.') { - dict_load_file(ldapsource, ldapsource); - parser.get_str = dict_ldap_get_dict_str; - parser.get_int = dict_ldap_get_dict_int; - parser.get_bool = dict_ldap_get_dict_bool; - } else { - - /* - * msg_warn("Defining LDAP attributes in main.cf is deprecated. Use - * ldap:/path/to/file instead"); - */ - parser.get_str = dict_ldap_get_mail_str; - parser.get_int = dict_ldap_get_mail_int; - parser.get_bool = dict_ldap_get_mail_bool; - } - - server_host = parser.get_str(ldapsource, "server_host", - "localhost", 1, 0); - if (msg_verbose) - msg_info("%s: %s server_host is %s", myname, ldapsource, - server_host); + server_host = cfg_get_str(dict_ldap->parser, "server_host", + "localhost", 1, 0); /* * get configured value of "server_port"; default to LDAP_PORT (389) */ dict_ldap->server_port = - parser.get_int(ldapsource, "server_port", LDAP_PORT, 0, 0); - if (msg_verbose) - msg_info("%s: %s server_port is %d", myname, ldapsource, - dict_ldap->server_port); + cfg_get_int(dict_ldap->parser, "server_port", LDAP_PORT, 0, 0); /* * Define LDAP Version. */ - dict_ldap->version = parser.get_int(ldapsource, "version", 2, 0, 0); + dict_ldap->version = cfg_get_int(dict_ldap->parser, "version", 2, 2, 0); switch (dict_ldap->version) { case 2: dict_ldap->version = LDAP_VERSION2; @@ -1402,34 +1291,26 @@ DICT *dict_ldap_open(const char *ldapsource, int dummy, int dict_flags) /* * Scope handling thanks to Carsten Hoeger of SuSE. */ - scope = parser.get_str(ldapsource, "scope", "sub", 0, 0); + scope = cfg_get_str(dict_ldap->parser, "scope", "sub", 1, 0); if (strcasecmp(scope, "one") == 0) { dict_ldap->scope = LDAP_SCOPE_ONELEVEL; - if (msg_verbose) - msg_info("%s: %s scope is LDAP_SCOPE_ONELEVEL", myname, ldapsource); - } else if (strcasecmp(scope, "base") == 0) { dict_ldap->scope = LDAP_SCOPE_BASE; - if (msg_verbose) - msg_info("%s: %s scope is LDAP_SCOPE_BASE", myname, ldapsource); - - } else { + } else if (strcasecmp(scope, "sub") == 0) { + dict_ldap->scope = LDAP_SCOPE_SUBTREE; + } else { + msg_warn("%s: %s: Unrecognized value %s specified for scope; using sub", + myname, ldapsource, scope); dict_ldap->scope = LDAP_SCOPE_SUBTREE; - if (msg_verbose) - msg_info("%s: %s scope is LDAP_SCOPE_SUBTREE", myname, ldapsource); - } myfree(scope); - dict_ldap->search_base = parser.get_str(ldapsource, "search_base", - "", 0, 0); - if (msg_verbose) - msg_info("%s: %s search_base is %s", myname, ldapsource, - dict_ldap->search_base); + dict_ldap->search_base = cfg_get_str(dict_ldap->parser, "search_base", + "", 0, 0); - domainlist = parser.get_str(ldapsource, "domain", "", 0, 0); + domainlist = cfg_get_str(dict_ldap->parser, "domain", "", 0, 0); if (*domainlist) { #ifdef MATCH_FLAG_NONE dict_ldap->domain = match_list_init(MATCH_FLAG_NONE, @@ -1438,7 +1319,8 @@ DICT *dict_ldap_open(const char *ldapsource, int dummy, int dict_flags) dict_ldap->domain = match_list_init(domainlist, 1, match_string); #endif if (dict_ldap->domain == NULL) - msg_warn("%s: domain match list creation using \"%s\" failed, will continue without it", myname, domainlist); + msg_warn("%s: domain match list creation using \"%s\" failed, will continue without it", + myname, domainlist); if (msg_verbose) msg_info("%s: domain list created using \"%s\"", myname, domainlist); @@ -1453,40 +1335,28 @@ DICT *dict_ldap_open(const char *ldapsource, int dummy, int dict_flags) * Thanks to Manuel Guesdon for spotting that this wasn't really getting * set. */ - dict_ldap->timeout = - parser.get_int(ldapsource, "timeout", 10, 0, 0); - if (msg_verbose) - msg_info("%s: %s timeout is %d", myname, ldapsource, - dict_ldap->timeout); + dict_ldap->timeout = cfg_get_int(dict_ldap->parser, "timeout", + 10, 0, 0); dict_ldap->query_filter = - parser.get_str(ldapsource, "query_filter", - "(mailacceptinggeneralid=%s)", 0, 0); - if (msg_verbose) - msg_info("%s: %s query_filter is %s", myname, ldapsource, - dict_ldap->query_filter); + cfg_get_str(dict_ldap->parser, "query_filter", + "(mailacceptinggeneralid=%s)", 0, 0); dict_ldap->result_filter = - parser.get_str(ldapsource, "result_filter", "%s", 0, 0); - if (msg_verbose) - msg_info("%s: %s result_filter is %s", myname, ldapsource, - dict_ldap->result_filter); + cfg_get_str(dict_ldap->parser, "result_filter", "%s", 0, 0); if (strcmp(dict_ldap->result_filter, "%s") == 0) { myfree(dict_ldap->result_filter); dict_ldap->result_filter = NULL; } - attr = parser.get_str(ldapsource, "result_attribute", "maildrop", 0, 0); - if (msg_verbose) - msg_info("%s: %s result_attribute is %s", myname, ldapsource, attr);; + attr = cfg_get_str(dict_ldap->parser, "result_attribute", + "maildrop", 0, 0); dict_ldap->result_attributes = argv_split(attr, " ,\t\r\n"); dict_ldap->num_attributes = dict_ldap->result_attributes->argc; myfree(attr); - attr = parser.get_str(ldapsource, "special_result_attribute", "", 0, 0); - if (msg_verbose) - msg_info("%s: %s special_result_attribute is %s", myname, ldapsource, - attr); + attr = cfg_get_str(dict_ldap->parser, "special_result_attribute", + "", 0, 0); if (*attr) { argv_split_append(dict_ldap->result_attributes, attr, " ,\t\r\n"); } @@ -1495,95 +1365,71 @@ DICT *dict_ldap_open(const char *ldapsource, int dummy, int dict_flags) /* * get configured value of "bind"; default to true */ - dict_ldap->bind = parser.get_bool(ldapsource, "bind", 1); - if (msg_verbose) - msg_info("%s: %s bind is %d", myname, ldapsource, dict_ldap->bind); + dict_ldap->bind = cfg_get_bool(dict_ldap->parser, "bind", 1); /* * get configured value of "bind_dn"; default to "" */ - dict_ldap->bind_dn = parser.get_str(ldapsource, "bind_dn", "", 0, 0); - if (msg_verbose) - msg_info("%s: %s bind_dn is %s", myname, ldapsource, - dict_ldap->bind_dn); + dict_ldap->bind_dn = cfg_get_str(dict_ldap->parser, "bind_dn", "", 0, 0); /* * get configured value of "bind_pw"; default to "" */ - dict_ldap->bind_pw = parser.get_str(ldapsource, "bind_pw", "", 0, 0); - if (msg_verbose) - msg_info("%s: %s bind_pw is %s", myname, ldapsource, - dict_ldap->bind_pw); + dict_ldap->bind_pw = cfg_get_str(dict_ldap->parser, "bind_pw", "", 0, 0); /* * get configured value of "cache"; default to false */ - tmp = parser.get_bool(ldapsource, "cache", 0); + tmp = cfg_get_bool(dict_ldap->parser, "cache", 0); if (tmp) msg_warn("%s: %s ignoring cache", myname, ldapsource); /* * get configured value of "cache_expiry"; default to 30 seconds */ - tmp = parser.get_int(ldapsource, "cache_expiry", -1, 0, 0); + tmp = cfg_get_int(dict_ldap->parser, "cache_expiry", -1, 0, 0); if (tmp >= 0) msg_warn("%s: %s ignoring cache_expiry", myname, ldapsource); /* * get configured value of "cache_size"; default to 32k */ - tmp = parser.get_int(ldapsource, "cache_size", -1, 0, 0); + tmp = cfg_get_int(dict_ldap->parser, "cache_size", -1, 0, 0); if (tmp >= 0) msg_warn("%s: %s ignoring cache_size", myname, ldapsource); /* * get configured value of "recursion_limit"; default to 1000 */ - dict_ldap->recursion_limit = parser.get_int(ldapsource, "recursion_limit", - 1000, 1, 0); - if (msg_verbose) - msg_info("%s: %s recursion_limit is %ld", myname, ldapsource, - dict_ldap->recursion_limit); + dict_ldap->recursion_limit = cfg_get_int(dict_ldap->parser, + "recursion_limit", 1000, 1, 0); /* * get configured value of "expansion_limit"; default to 0 */ - dict_ldap->expansion_limit = parser.get_int(ldapsource, "expansion_limit", - 0, 0, 0); - if (msg_verbose) - msg_info("%s: %s expansion_limit is %ld", myname, ldapsource, - dict_ldap->expansion_limit); + dict_ldap->expansion_limit = cfg_get_int(dict_ldap->parser, + "expansion_limit", 0, 0, 0); /* * get configured value of "size_limit"; default to expansion_limit */ - dict_ldap->size_limit = parser.get_int(ldapsource, "size_limit", - dict_ldap->expansion_limit, - 0, 0); - if (msg_verbose) - msg_info("%s: %s size_limit is %ld", myname, ldapsource, - dict_ldap->size_limit); + dict_ldap->size_limit = cfg_get_int(dict_ldap->parser, "size_limit", + dict_ldap->expansion_limit, + 0, 0); /* * Alias dereferencing suggested by Mike Mattice. */ - dict_ldap->dereference = parser.get_int(ldapsource, "dereference", 0, 0, - 0); + dict_ldap->dereference = cfg_get_int(dict_ldap->parser, "dereference", + 0, 0, 0); if (dict_ldap->dereference < 0 || dict_ldap->dereference > 3) { msg_warn("%s: %s Unrecognized value %d specified for dereference; using 0", myname, ldapsource, dict_ldap->dereference); dict_ldap->dereference = 0; } - if (msg_verbose) - msg_info("%s: %s dereference is %d", myname, ldapsource, - dict_ldap->dereference); - /* Referral chasing */ - dict_ldap->chase_referrals = parser.get_bool(ldapsource, "chase_referrals", - 0); - if (msg_verbose) - msg_info("%s: %s chase_referrals is %d", myname, ldapsource, - dict_ldap->chase_referrals); + dict_ldap->chase_referrals = cfg_get_bool(dict_ldap->parser, + "chase_referrals", 0); #ifdef LDAP_API_FEATURE_X_OPENLDAP @@ -1591,65 +1437,47 @@ DICT *dict_ldap_open(const char *ldapsource, int dummy, int dict_flags) * TLS options */ /* get configured value of "start_tls"; default to no */ - dict_ldap->start_tls = parser.get_bool(ldapsource, "start_tls", 0); - if (msg_verbose) - msg_info("%s: %s start_tls is %d", myname, ldapsource, - dict_ldap->start_tls); + dict_ldap->start_tls = cfg_get_bool(dict_ldap->parser, "start_tls", 0); if (dict_ldap->start_tls && dict_ldap->version < LDAP_VERSION3) { - msg_warn("%s: %s start_tls requires protocol version 3", myname, ldapsource); + msg_warn("%s: %s start_tls requires protocol version 3", + myname, ldapsource); dict_ldap->version = LDAP_VERSION3; } /* get configured value of "tls_require_cert"; default to no */ - dict_ldap->tls_require_cert = parser.get_bool(ldapsource, "tls_require_cert", 0); - if (msg_verbose) - msg_info("%s: %s tls_require_cert is %d", myname, ldapsource, - dict_ldap->tls_require_cert); + dict_ldap->tls_require_cert = cfg_get_bool(dict_ldap->parser, + "tls_require_cert", 0); + /* get configured value of "tls_ca_cert_file"; default "" */ - dict_ldap->tls_ca_cert_file = parser.get_str(ldapsource, "tls_ca_cert_file", - "", 0, 0); - if (msg_verbose) - msg_info("%s: %s tls_ca_cert_file is %s", myname, ldapsource, - dict_ldap->tls_ca_cert_file); + dict_ldap->tls_ca_cert_file = cfg_get_str(dict_ldap->parser, + "tls_ca_cert_file", "", 0, 0); + /* get configured value of "tls_ca_cert_dir"; default "" */ - dict_ldap->tls_ca_cert_dir = parser.get_str(ldapsource, "tls_ca_cert_dir", - "", 0, 0); - if (msg_verbose) - msg_info("%s: %s tls_ca_cert_dir is %s", myname, ldapsource, - dict_ldap->tls_ca_cert_dir); + dict_ldap->tls_ca_cert_dir = cfg_get_str(dict_ldap->parser, + "tls_ca_cert_dir", "", 0, 0); + /* get configured value of "tls_cert"; default "" */ - dict_ldap->tls_cert = parser.get_str(ldapsource, "tls_cert", - "", 0, 0); - if (msg_verbose) - msg_info("%s: %s tls_cert is %s", myname, ldapsource, - dict_ldap->tls_cert); + dict_ldap->tls_cert = cfg_get_str(dict_ldap->parser, "tls_cert", + "", 0, 0); + /* get configured value of "tls_key"; default "" */ - dict_ldap->tls_key = parser.get_str(ldapsource, "tls_key", - "", 0, 0); - if (msg_verbose) - msg_info("%s: %s tls_key is %s", myname, ldapsource, - dict_ldap->tls_key); + dict_ldap->tls_key = cfg_get_str(dict_ldap->parser, "tls_key", + "", 0, 0); + /* get configured value of "tls_random_file"; default "" */ - dict_ldap->tls_random_file = parser.get_str(ldapsource, - "tls_random_file", "", 0, 0); - if (msg_verbose) - msg_info("%s: %s tls_random_file is %s", myname, ldapsource, - dict_ldap->tls_random_file); + dict_ldap->tls_random_file = cfg_get_str(dict_ldap->parser, + "tls_random_file", "", 0, 0); + /* get configured value of "tls_cipher_suite"; default "" */ - dict_ldap->tls_cipher_suite = parser.get_str(ldapsource, + dict_ldap->tls_cipher_suite = cfg_get_str(dict_ldap->parser, "tls_cipher_suite", "", 0, 0); - if (msg_verbose) - msg_info("%s: %s tls_cipher_suite is %s", myname, ldapsource, - dict_ldap->tls_cipher_suite); #endif /* * Debug level. */ #if defined(LDAP_OPT_DEBUG_LEVEL) && defined(LBER_OPT_LOG_PRINT_FN) - dict_ldap->debuglevel = parser.get_int(ldapsource, "debuglevel", 0, 0, 0); - if (msg_verbose) - msg_info("%s: %s debuglevel is %d", myname, ldapsource, - dict_ldap->debuglevel); + dict_ldap->debuglevel = cfg_get_int(dict_ldap->parser, "debuglevel", + 0, 0, 0); #endif /* diff --git a/postfix/src/util/dict_ldap.h b/postfix/src/global/dict_ldap.h similarity index 100% rename from postfix/src/util/dict_ldap.h rename to postfix/src/global/dict_ldap.h diff --git a/postfix/src/util/dict_mysql.c b/postfix/src/global/dict_mysql.c similarity index 74% rename from postfix/src/util/dict_mysql.c rename to postfix/src/global/dict_mysql.c index 3c27b0023..94c0f8333 100644 --- a/postfix/src/util/dict_mysql.c +++ b/postfix/src/global/dict_mysql.c @@ -2,15 +2,14 @@ /* NAME /* dict_mysql 3 /* SUMMARY -/* dictionary manager interface to db files +/* dictionary manager interface to MySQL databases /* SYNOPSIS -/* #include /* #include /* /* DICT *dict_mysql_open(name, open_flags, dict_flags) -/* const char *name; -/* int open_flags; -/* int dict_flags; +/* const char *name; +/* int open_flags; +/* int dict_flags; /* DESCRIPTION /* dict_mysql_open() creates a dictionary of type 'mysql'. This /* dictionary is an interface for the postfix key->value mappings @@ -24,25 +23,21 @@ /* ones will be opened and used. The intent of this feature is to eliminate /* a single point of failure for mail systems that would otherwise rely /* on a single mysql server. -/* +/* .PP /* Arguments: /* .IP name -/* The path of the MySQL configuration file. The file encodes a number of -/* pieces of information: username, password, databasename, table, -/* select_field, where_field, and hosts. For example, if you want the map to -/* reference databases of the name "your_db" and execute a query like this: -/* select forw_addr from aliases where alias like '' against -/* any database called "vmailer_info" located on hosts host1.some.domain and -/* host2.some.domain, logging in as user "vmailer" and password "passwd" then -/* the configuration file should read: +/* Either the path to the MySQL configuration file (if it starts +/* with '/' or '.'), or the prefix which will be used to obtain +/* main.cf configuration parameters for this search. /* -/* user = vmailer -/* password = passwd -/* DBname = vmailer_info -/* table = aliases -/* select_field = forw_addr -/* where_field = alias -/* hosts = host1.some.domain host2.some.domain +/* In the first case, the configuration parameters below are +/* specified in the file as \fIname\fR=\fBvalue\fR pairs. +/* +/* In the second case, the configuration parameters are +/* prefixed with the value of \fIname\fR and an underscore, +/* and they are specified in main.cf. For example, if this +/* value is \fImysqlsource\fR, the parameters would look like +/* \fImysqlsource_user\fR, \fImysqlsource_table\fR, and so on. /* /* .IP other_name /* reference for outside use. @@ -50,6 +45,51 @@ /* Must be O_RDONLY. /* .IP dict_flags /* See dict_open(3). +/* .PP +/* Configuration parameters: +/* +/* The parameters encodes a number of pieces of information: +/* username, password, databasename, table, select_field, +/* where_field, and hosts: +/* .IP \fIuser\fR +/* Username for connecting to the database. +/* .IP \fIpassword\fR +/* Password for the above. +/* .IP \fIdbname\fR +/* Name of the database. +/* .IP \fItable\fR +/* Name of the table. +/* .IP \fIselect_field\fR +/* Name of the result field. +/* .IP \fIwhere_field\fR +/* Field used in the WHERE clause. +/* .IP \fIadditional_conditions\fR +/* Additional conditions to the WHERE clause. +/* .IP \fIhosts\fR +/* List of hosts to connect to. +/* .PP +/* For example, if you want the map to reference databases of +/* the name "your_db" and execute a query like this: select +/* forw_addr from aliases where alias like '' +/* against any database called "vmailer_info" located on hosts +/* host1.some.domain and host2.some.domain, logging in as user +/* "vmailer" and password "passwd" then the configuration file +/* should read: +/* .PP +/* \fIuser\fR = \fBvmailer\fR +/* .br +/* \fIpassword\fR = \fBpasswd\fR +/* .br +/* \fIdbname\fR = \fBvmailer_info\fR +/* .br +/* \fItable\fR = \fBaliases\fR +/* .br +/* \fIselect_field\fR = \fBforw_addr\fR +/* .br +/* \fIwhere_field\fR = \fBalias\fR +/* .br +/* \fIhosts\fR = \fBhost1.some.domain\fR \fBhost2.some.domain\fR +/* .PP /* SEE ALSO /* dict(3) generic dictionary manager /* AUTHOR(S) @@ -78,15 +118,23 @@ #include /* Utility library. */ + #include "dict.h" #include "msg.h" #include "mymalloc.h" -#include "dict_mysql.h" #include "argv.h" #include "vstring.h" #include "split_at.h" #include "find_inet.h" +/* Global library. */ + +#include "cfg_parser.h" + +/* Application-specific. */ + +#include "dict_mysql.h" + /* need some structs to help organize things */ typedef struct { MYSQL *db; @@ -103,6 +151,7 @@ typedef struct { } PLMYSQL; typedef struct { + CFG_PARSER *parser; char *username; char *password; char *dbname; @@ -132,7 +181,6 @@ static void plmysql_dealloc(PLMYSQL *); static void plmysql_close_host(HOST *); static void plmysql_down_host(HOST *); static void plmysql_connect_single(HOST *, char *, char *, char *); -static int plmysql_ready_reconn(HOST *); static const char *dict_mysql_lookup(DICT *, const char *); DICT *dict_mysql_open(const char *, int, int); static void dict_mysql_close(DICT *); @@ -223,9 +271,9 @@ static const char *dict_mysql_lookup(DICT *dict, const char *name) /* * plmysql_query - process a MySQL query. Return MYSQL_RES* on success. - * On failure, log failure and try other db instances. - * on failure of all db instances, return 0; - * close unnecessary active connections + * On failure, log failure and try other db instances. + * on failure of all db instances, return 0; + * close unnecessary active connections */ static MYSQL_RES *plmysql_query(PLMYSQL *PLDB, @@ -343,7 +391,7 @@ static void plmysql_close_host(HOST *host) } /* - * plmysql_down_host - close a failed connection AND set a "stay away from + * plmysql_down_host - close a failed connection AND set a "stay away from * this host" timer */ static void plmysql_down_host(HOST *host) @@ -363,14 +411,13 @@ static void plmysql_down_host(HOST *host) DICT *dict_mysql_open(const char *name, int open_flags, int dict_flags) { DICT_MYSQL *dict_mysql; - int connections; /* * Sanity checks. */ if (open_flags != O_RDONLY) - msg_fatal("%s:%s map requires O_RDONLY access mode", - DICT_TYPE_MYSQL, name); + msg_fatal("%s:%s map requires O_RDONLY access mode", + DICT_TYPE_MYSQL, name); dict_mysql = (DICT_MYSQL *) dict_alloc(DICT_TYPE_MYSQL, name, sizeof(DICT_MYSQL)); @@ -386,92 +433,50 @@ DICT *dict_mysql_open(const char *name, int open_flags, int dict_flags) } /* mysqlname_parse - parse mysql configuration file */ -static MYSQL_NAME *mysqlname_parse(const char *mysqlcf_path) +static MYSQL_NAME *mysqlname_parse(const char *mysqlcf) { + const char *myname = "mysqlname_parse"; int i; - char *nameval; char *hosts; MYSQL_NAME *name = (MYSQL_NAME *) mymalloc(sizeof(MYSQL_NAME)); ARGV *hosts_argv; - VSTRING *opt_dict_name; - /* - * setup a dict containing info in the mysql cf file. the dict has a - * name, and a path. The name must be distinct from the path, or the - * dict interface gets confused. The name must be distinct for two - * different paths, or the configuration info will cache across different - * mysql maps, which can be confusing. - */ - opt_dict_name = vstring_alloc(64); - vstring_sprintf(opt_dict_name, "mysql opt dict %s", mysqlcf_path); - dict_load_file(vstring_str(opt_dict_name), mysqlcf_path); - /* mysql username lookup */ - if ((nameval = (char *) dict_lookup(vstring_str(opt_dict_name), "user")) == NULL) - name->username = mystrdup(""); - else - name->username = mystrdup(nameval); - if (msg_verbose) - msg_info("mysqlname_parse(): set username to '%s'", name->username); - /* password lookup */ - if ((nameval = (char *) dict_lookup(vstring_str(opt_dict_name), "password")) == NULL) - name->password = mystrdup(""); - else - name->password = mystrdup(nameval); - if (msg_verbose) - msg_info("mysqlname_parse(): set password to '%s'", name->password); + /* parser */ + name->parser = cfg_parser_alloc(mysqlcf); - /* database name lookup */ - if ((nameval = (char *) dict_lookup(vstring_str(opt_dict_name), "dbname")) == NULL) - msg_fatal("%s: mysql options file does not include database name", mysqlcf_path); - else - name->dbname = mystrdup(nameval); - if (msg_verbose) - msg_info("mysqlname_parse(): set database name to '%s'", name->dbname); + /* username */ + name->username = cfg_get_str(name->parser, "user", "", 0, 0); - /* table lookup */ - if ((nameval = (char *) dict_lookup(vstring_str(opt_dict_name), "table")) == NULL) - msg_fatal("%s: mysql options file does not include table name", mysqlcf_path); - else - name->table = mystrdup(nameval); - if (msg_verbose) - msg_info("mysqlname_parse(): set table name to '%s'", name->table); + /* password */ + name->password = cfg_get_str(name->parser, "password", "", 0, 0); - /* select field lookup */ - if ((nameval = (char *) dict_lookup(vstring_str(opt_dict_name), "select_field")) == NULL) - msg_fatal("%s: mysql options file does not include select field", mysqlcf_path); - else - name->select_field = mystrdup(nameval); - if (msg_verbose) - msg_info("mysqlname_parse(): set select_field to '%s'", name->select_field); + /* database name */ + name->dbname = cfg_get_str(name->parser, "dbname", "", 1, 0); - /* where field lookup */ - if ((nameval = (char *) dict_lookup(vstring_str(opt_dict_name), "where_field")) == NULL) - msg_fatal("%s: mysql options file does not include where field", mysqlcf_path); - else - name->where_field = mystrdup(nameval); - if (msg_verbose) - msg_info("mysqlname_parse(): set where_field to '%s'", name->where_field); + /* table name */ + name->table = cfg_get_str(name->parser, "table", "", 1, 0); + + /* select field */ + name->select_field = cfg_get_str(name->parser, "select_field", "", 1, 0); + + /* where field */ + name->where_field = cfg_get_str(name->parser, "where_field", "", 1, 0); /* additional conditions */ - if ((nameval = (char *) dict_lookup(vstring_str(opt_dict_name), "additional_conditions")) == NULL) - name->additional_conditions = mystrdup(""); - else - name->additional_conditions = mystrdup(nameval); - if (msg_verbose) - msg_info("mysqlname_parse(): set additional_conditions to '%s'", name->additional_conditions); + name->additional_conditions = cfg_get_str(name->parser, + "additional_conditions", + "", 0, 0); /* mysql server hosts */ - if ((nameval = (char *) dict_lookup(vstring_str(opt_dict_name), "hosts")) == NULL) - hosts = mystrdup(""); - else - hosts = mystrdup(nameval); + hosts = cfg_get_str(name->parser, "hosts", "", 0, 0); + /* coo argv interface */ hosts_argv = argv_split(hosts, " ,\t\r\n"); - if (hosts_argv->argc == 0) { /* no hosts specified, * default to 'localhost' */ if (msg_verbose) - msg_info("mysqlname_parse(): no hostnames specified, defaulting to 'localhost'"); + msg_info("%s: %s: no hostnames specified, defaulting to 'localhost'", + myname, mysqlcf); argv_add(hosts_argv, "localhost", ARGV_END); argv_terminate(hosts_argv); } @@ -481,11 +486,10 @@ static MYSQL_NAME *mysqlname_parse(const char *mysqlcf_path) for (i = 0; hosts_argv->argv[i] != NULL; i++) { name->hostnames[i] = mystrdup(hosts_argv->argv[i]); if (msg_verbose) - msg_info("mysqlname_parse(): adding host '%s' to list of mysql server hosts", - name->hostnames[i]); + msg_info("%s: %s: adding host '%s' to list of mysql server hosts", + myname, mysqlcf, name->hostnames[i]); } myfree(hosts); - vstring_free(opt_dict_name); argv_free(hosts_argv); return name; } @@ -498,9 +502,7 @@ static MYSQL_NAME *mysqlname_parse(const char *mysqlcf_path) static PLMYSQL *plmysql_init(char *hostnames[], int len_hosts) { PLMYSQL *PLDB; - MYSQL *dbs; int i; - HOST host; if ((PLDB = (PLMYSQL *) mymalloc(sizeof(PLMYSQL))) == NULL) { msg_fatal("mymalloc of pldb failed"); @@ -537,6 +539,7 @@ static void dict_mysql_close(DICT *dict) DICT_MYSQL *dict_mysql = (DICT_MYSQL *) dict; plmysql_dealloc(dict_mysql->pldb); + cfg_parser_free(dict_mysql->name->parser); myfree(dict_mysql->name->username); myfree(dict_mysql->name->password); myfree(dict_mysql->name->dbname); diff --git a/postfix/src/util/dict_mysql.h b/postfix/src/global/dict_mysql.h similarity index 100% rename from postfix/src/util/dict_mysql.h rename to postfix/src/global/dict_mysql.h diff --git a/postfix/src/util/dict_pgsql.c b/postfix/src/global/dict_pgsql.c similarity index 75% rename from postfix/src/util/dict_pgsql.c rename to postfix/src/global/dict_pgsql.c index 4e1dc3a97..a4f69a910 100644 --- a/postfix/src/util/dict_pgsql.c +++ b/postfix/src/global/dict_pgsql.c @@ -2,14 +2,14 @@ /* NAME /* dict_pgsql 3 /* SUMMARY -/* dictionary manager interface to Postgresql files +/* dictionary manager interface to PostgreSQL databases /* SYNOPSIS /* #include /* /* DICT *dict_pgsql_open(name, open_flags, dict_flags) /* const char *name; -/* int open_flags; -/* int dict_flags; +/* int open_flags; +/* int dict_flags; /* DESCRIPTION /* dict_pgsql_open() creates a dictionary of type 'pgsql'. This /* dictionary is an interface for the postfix key->value mappings @@ -24,34 +24,83 @@ /* The intent of this feature is to eliminate a single point of /* failure for mail systems that would otherwise rely on a single /* pgsql server. -/* +/* .PP /* Arguments: /* .IP name -/* The path of the PostgreSQL configuration file. The file -/* encodes number of pieces of information: username, password, -/* databasename, table, select_field, where_field, and hosts. -/* For example, if you want the map to reference databases of -/* the name "your_db" and execute a query like this: select -/* forw_addr from aliases where alias like '' -/* against any database called "postfix_info" located on hosts -/* host1.some.domain and host2.some.domain, logging in as user -/* "postfix" and password "passwd" then the configuration file -/* should read: +/* Either the path to the PostgreSQL configuration file (if it +/* starts with '/' or '.'), or the prefix which will be used to +/* obtain main.cf configuration parameters for this search. /* -/* user = postfix -/* password = passwd -/* DBname = postfix_info -/* table = aliases -/* select_field = forw_addr -/* where_field = alias -/* hosts = host1.some.domain host2.some.domain +/* In the first case, the configuration parameters below are +/* specified in the file as \fIname\fR=\fBvalue\fR pairs. /* +/* In the second case, the configuration parameters are +/* prefixed with the value of \fIname\fR and an underscore, +/* and they are specified in main.cf. For example, if this +/* value is \fIpgsqlsource\fR, the parameters would look like +/* \fIpgsqlsource_user\fR, \fIpgsqlsource_table\fR, and so on. /* .IP other_name /* reference for outside use. /* .IP open_flags /* Must be O_RDONLY. /* .IP dict_flags /* See dict_open(3). +/* +/* .PP +/* Configuration parameters: +/* +/* The parameters encode a number of pieces of information: +/* username, password, databasename, table, select_field, +/* where_field, and hosts: +/* .IP \fIuser\fR +/* Username for connecting to the database. +/* .IP \fIpassword\fR +/* Password for the above. +/* .IP \fIdbname\fR +/* Name of the database. +/* .IP \fItable\fR +/* Name of the table. +/* .IP \fIselect_field\fR +/* Name of the result field. +/* .IP \fIwhere_field\fR +/* Field used in the WHERE clause. +/* .IP \fIadditional_conditions\fR +/* Additional conditions to the WHERE clause. +/* .IP \fIquery\fR +/* Query overriding \fItable\fR, \fIselect_field\fR, +/* \fIwhere_field\fR, and \fIadditional_conditions\fR. Before the +/* query is actually issued, all occurrences of %s are replaced +/* with the address to look up, %u are replaced with the user +/* portion, and %d with the domain portion. +/* .IP \fIselect_function\fR +/* Function to be used instead of the SELECT statement. Overrides +/* both \fIquery\fR and \fItable\fR, \fIselect_field\fR, +/* \fIwhere_field\fR, and \fIadditional_conditions\fR settings. +/* .IP \fIhosts\fR +/* List of hosts to connect to. +/* .PP +/* For example, if you want the map to reference databases of +/* the name "your_db" and execute a query like this: select +/* forw_addr from aliases where alias like '' +/* against any database called "postfix_info" located on hosts +/* host1.some.domain and host2.some.domain, logging in as user +/* "postfix" and password "passwd" then the configuration file +/* should read: +/* .PP +/* \fIuser\fR = \fBpostfix\fR +/* .br +/* \fIpassword\fR = \fBpasswd\fR +/* .br +/* \fIdbname\fR = \fBpostfix_info\fR +/* .br +/* \fItable\fR = \fBaliases\fR +/* .br +/* \fIselect_field\fR = \fBforw_addr\fR +/* .br +/* \fIwhere_field\fR = \fBalias\fR +/* .br +/* \fIhosts\fR = \fBhost1.some.domain\fR \fBhost2.some.domain\fR +/* .PP /* SEE ALSO /* dict(3) generic dictionary manager /* AUTHOR(S) @@ -70,6 +119,7 @@ /*--*/ /* System library. */ + #include "sys_defs.h" #ifdef HAS_PGSQL @@ -87,15 +137,23 @@ #include /* Utility library. */ + #include "dict.h" #include "msg.h" #include "mymalloc.h" -#include "dict_pgsql.h" #include "argv.h" #include "vstring.h" #include "split_at.h" #include "find_inet.h" +/* Global library. */ + +#include "cfg_parser.h" + +/* Application-specific. */ + +#include "dict_pgsql.h" + #define STATACTIVE 0 #define STATFAIL 1 #define STATUNTRIED 2 @@ -114,6 +172,7 @@ typedef struct { } PLPGSQL; typedef struct { + CFG_PARSER *parser; char *username; char *password; char *dbname; @@ -202,7 +261,7 @@ static void pgsql_escape_string(char *new, const char *old, unsigned int len) */ static void dict_pgsql_expand_filter(char *filter, char *value, VSTRING *out) { - char *myname = "dict_pgsql_expand_filter"; + const char *myname = "dict_pgsql_expand_filter"; char *sub, *end; @@ -285,7 +344,8 @@ static const char *dict_pgsql_lookup(DICT *dict, const char *name) } else if (dict_pgsql->name->query) { dict_pgsql_expand_filter(dict_pgsql->name->query, name_escaped, query); } else { - vstring_sprintf(query, "select %s from %s where %s = '%s' %s", dict_pgsql->name->select_field, + vstring_sprintf(query, "select %s from %s where %s = '%s' %s", + dict_pgsql->name->select_field, dict_pgsql->name->table, dict_pgsql->name->where_field, name_escaped, @@ -357,9 +417,9 @@ static const char *dict_pgsql_lookup(DICT *dict, const char *name) /* * plpgsql_query - process a PostgreSQL query. Return PGSQL_RES* on success. - * On failure, log failure and try other db instances. - * on failure of all db instances, return 0; - * close unnecessary active connections + * On failure, log failure and try other db instances. + * on failure of all db instances, return 0; + * close unnecessary active connections */ static PGSQL_RES *plpgsql_query(PLPGSQL *PLDB, @@ -516,126 +576,71 @@ DICT *dict_pgsql_open(const char *name, int open_flags, int dict_flags) } /* pgsqlname_parse - parse pgsql configuration file */ -static PGSQL_NAME *pgsqlname_parse(const char *pgsqlcf_path) +static PGSQL_NAME *pgsqlname_parse(const char *pgsqlcf) { + const char *myname = "pgsqlname_parse"; int i; - char *nameval; char *hosts; PGSQL_NAME *name = (PGSQL_NAME *) mymalloc(sizeof(PGSQL_NAME)); ARGV *hosts_argv; - VSTRING *opt_dict_name; - /* - * setup a dict containing info in the pgsql cf file. the dict has a - * name, and a path. The name must be distinct from the path, or the - * dict interface gets confused. The name must be distinct for two - * different paths, or the configuration info will cache across different - * pgsql maps, which can be confusing. - */ - opt_dict_name = vstring_alloc(64); - vstring_sprintf(opt_dict_name, "pgsql opt dict %s", pgsqlcf_path); - dict_load_file(vstring_str(opt_dict_name), pgsqlcf_path); - /* pgsql username lookup */ - if ((nameval = (char *) dict_lookup(vstring_str(opt_dict_name), "user")) == NULL) - name->username = mystrdup(""); - else - name->username = mystrdup(nameval); - if (msg_verbose) - msg_info("pgsqlname_parse(): set username to '%s'", name->username); - /* password lookup */ - if ((nameval = (char *) dict_lookup(vstring_str(opt_dict_name), "password")) == NULL) - name->password = mystrdup(""); - else - name->password = mystrdup(nameval); - if (msg_verbose) - msg_info("pgsqlname_parse(): set password to '%s'", name->password); + name->parser = cfg_parser_alloc(pgsqlcf); + + /* username */ + name->username = cfg_get_str(name->parser, "user", "", 0, 0); + + /* password */ + name->password = cfg_get_str(name->parser, "password", "", 0, 0); /* database name lookup */ - if ((nameval = (char *) dict_lookup(vstring_str(opt_dict_name), "dbname")) == NULL) - msg_fatal("%s: pgsql options file does not include database name", pgsqlcf_path); - else - name->dbname = mystrdup(nameval); - if (msg_verbose) - msg_info("pgsqlname_parse(): set database name to '%s'", name->dbname); - - /* table lookup */ - if ((nameval = (char *) dict_lookup(vstring_str(opt_dict_name), "table")) == NULL) - msg_fatal("%s: pgsql options file does not include table name", pgsqlcf_path); - else - name->table = mystrdup(nameval); - if (msg_verbose) - msg_info("pgsqlname_parse(): set table name to '%s'", name->table); - - name->select_function = NULL; - name->query = NULL; + name->dbname = cfg_get_str(name->parser, "dbname", "", 1, 0); /* * See what kind of lookup we have - a traditional 'select' or a function * call */ - if ((nameval = (char *) dict_lookup(vstring_str(opt_dict_name), "select_function")) != NULL) { + name->select_function = cfg_get_str(name->parser, "select_function", + NULL, 0, 0); + name->query = cfg_get_str(name->parser, "query", NULL, 0, 0); - /* We have a 'select %s(%s)' function call. */ - name->select_function = mystrdup(nameval); - if (msg_verbose) - msg_info("pgsqlname_parse(): set function name to '%s'", name->table); - /* query string */ - } else if ((nameval = (char *) dict_lookup(vstring_str(opt_dict_name), "query")) != NULL) { - name->query = mystrdup(nameval); - if (msg_verbose) - msg_info("pgsqlname_parse(): set query to '%s'", name->query); - } else { + if (name->select_function == 0 && name->query == 0) { /* - * We have an old style 'select %s from %s...' call, so get the - * fields + * We have an old style 'select %s from %s...' call */ - /* table lookup */ - if ((nameval = (char *) dict_lookup(vstring_str(opt_dict_name), "table")) == NULL) - msg_fatal("%s: pgsql options file does not include table name", pgsqlcf_path); - else - name->table = mystrdup(nameval); - if (msg_verbose) - msg_info("pgsqlname_parse(): set table name to '%s'", name->table); + /* table name */ + name->table = cfg_get_str(name->parser, "table", "", 1, 0); - /* select field lookup */ - if ((nameval = (char *) dict_lookup(vstring_str(opt_dict_name), "select_field")) == NULL) - msg_fatal("%s: pgsql options file does not include select field", pgsqlcf_path); - else - name->select_field = mystrdup(nameval); - if (msg_verbose) - msg_info("pgsqlname_parse(): set select_field to '%s'", name->select_field); + /* select field */ + name->select_field = cfg_get_str(name->parser, "select_field", + "", 1, 0); - /* where field lookup */ - if ((nameval = (char *) dict_lookup(vstring_str(opt_dict_name), "where_field")) == NULL) - msg_fatal("%s: pgsql options file does not include where field", pgsqlcf_path); - else - name->where_field = mystrdup(nameval); - if (msg_verbose) - msg_info("pgsqlname_parse(): set where_field to '%s'", name->where_field); + /* where field */ + name->where_field = cfg_get_str(name->parser, "where_field", + "", 1, 0); /* additional conditions */ - if ((nameval = (char *) dict_lookup(vstring_str(opt_dict_name), "additional_conditions")) == NULL) - name->additional_conditions = mystrdup(""); - else - name->additional_conditions = mystrdup(nameval); - if (msg_verbose) - msg_info("pgsqlname_parse(): set additional_conditions to '%s'", name->additional_conditions); + name->additional_conditions = cfg_get_str(name->parser, + "additional_conditions", + "", 0, 0); + } else { + name->table = 0; + name->select_field = 0; + name->where_field = 0; + name->additional_conditions = 0; } - /* pgsql server hosts */ - if ((nameval = (char *) dict_lookup(vstring_str(opt_dict_name), "hosts")) == NULL) - hosts = mystrdup(""); - else - hosts = mystrdup(nameval); + /* server hosts */ + hosts = cfg_get_str(name->parser, "hosts", "", 0, 0); + /* coo argv interface */ hosts_argv = argv_split(hosts, " ,\t\r\n"); - if (hosts_argv->argc == 0) { /* no hosts specified, * default to 'localhost' */ if (msg_verbose) - msg_info("pgsqlname_parse(): no hostnames specified, defaulting to 'localhost'"); + msg_info("%s: %s: no hostnames specified, defaulting to 'localhost'", + myname, pgsqlcf); argv_add(hosts_argv, "localhost", ARGV_END); argv_terminate(hosts_argv); } @@ -645,11 +650,10 @@ static PGSQL_NAME *pgsqlname_parse(const char *pgsqlcf_path) for (i = 0; hosts_argv->argv[i] != NULL; i++) { name->hostnames[i] = mystrdup(hosts_argv->argv[i]); if (msg_verbose) - msg_info("pgsqlname_parse(): adding host '%s' to list of pgsql server hosts", - name->hostnames[i]); + msg_info("%s: %s: adding host '%s' to list of pgsql server hosts", + myname, pgsqlcf, name->hostnames[i]); } myfree(hosts); - vstring_free(opt_dict_name); argv_free(hosts_argv); return name; } @@ -699,13 +703,22 @@ static void dict_pgsql_close(DICT *dict) DICT_PGSQL *dict_pgsql = (DICT_PGSQL *) dict; plpgsql_dealloc(dict_pgsql->pldb); + cfg_parser_free(dict_pgsql->name->parser); myfree(dict_pgsql->name->username); myfree(dict_pgsql->name->password); myfree(dict_pgsql->name->dbname); - myfree(dict_pgsql->name->table); - myfree(dict_pgsql->name->select_field); - myfree(dict_pgsql->name->where_field); - myfree(dict_pgsql->name->additional_conditions); + if (dict_pgsql->name->table) + myfree(dict_pgsql->name->table); + if (dict_pgsql->name->query) + myfree(dict_pgsql->name->query); + if (dict_pgsql->name->select_function) + myfree(dict_pgsql->name->select_function); + if (dict_pgsql->name->select_field) + myfree(dict_pgsql->name->select_field); + if (dict_pgsql->name->where_field) + myfree(dict_pgsql->name->where_field); + if (dict_pgsql->name->additional_conditions) + myfree(dict_pgsql->name->additional_conditions); for (i = 0; i < dict_pgsql->name->len_hosts; i++) { myfree(dict_pgsql->name->hostnames[i]); } diff --git a/postfix/src/util/dict_pgsql.h b/postfix/src/global/dict_pgsql.h similarity index 100% rename from postfix/src/util/dict_pgsql.h rename to postfix/src/global/dict_pgsql.h diff --git a/postfix/src/global/mail_dict.c b/postfix/src/global/mail_dict.c index a576f5894..fab1db304 100644 --- a/postfix/src/global/mail_dict.c +++ b/postfix/src/global/mail_dict.c @@ -33,6 +33,9 @@ /* Global library. */ #include +#include +#include +#include #include typedef struct { @@ -42,7 +45,15 @@ typedef struct { static DICT_OPEN_INFO dict_open_info[] = { DICT_TYPE_PROXY, dict_proxy_open, - /* XXX LDAP and MYSQL etc. should go here, too. */ +#ifdef HAS_LDAP + DICT_TYPE_LDAP, dict_ldap_open, +#endif +#ifdef HAS_MYSQL + DICT_TYPE_MYSQL, dict_mysql_open, +#endif +#ifdef HAS_PGSQL + DICT_TYPE_PGSQL, dict_pgsql_open, +#endif 0, }; diff --git a/postfix/src/global/mail_params.c b/postfix/src/global/mail_params.c index 1bd415a1c..9024498a6 100644 --- a/postfix/src/global/mail_params.c +++ b/postfix/src/global/mail_params.c @@ -445,7 +445,6 @@ void mail_params_init() VAR_MYDEST, DEF_MYDEST, &var_mydest, 0, 0, VAR_MYORIGIN, DEF_MYORIGIN, &var_myorigin, 1, 0, VAR_RELAYHOST, DEF_RELAYHOST, &var_relayhost, 0, 0, - VAR_PROGRAM_DIR, DEF_PROGRAM_DIR, &var_program_dir, 1, 0, VAR_DAEMON_DIR, DEF_DAEMON_DIR, &var_daemon_dir, 1, 0, VAR_COMMAND_DIR, DEF_COMMAND_DIR, &var_command_dir, 1, 0, VAR_QUEUE_DIR, DEF_QUEUE_DIR, &var_queue_dir, 1, 0, diff --git a/postfix/src/global/mail_params.h b/postfix/src/global/mail_params.h index 6ab8730f4..3d7e9d7b7 100644 --- a/postfix/src/global/mail_params.h +++ b/postfix/src/global/mail_params.h @@ -87,7 +87,7 @@ extern char *var_myorigin; * mail to other destinations. */ #define VAR_MYDEST "mydestination" -#define DEF_MYDEST "$myhostname, localhost.$mydomain localhost" +#define DEF_MYDEST "$myhostname, localhost.$mydomain, localhost" extern char *var_mydest; /* @@ -198,13 +198,8 @@ extern int var_smtp_mxsess_limit; extern char *var_queue_dir; /* - * Location of daemon programs. + * Location of command and daemon programs. */ -#define VAR_PROGRAM_DIR "program_directory" -#ifndef DEF_PROGRAM_DIR -#define DEF_PROGRAM_DIR "/usr/libexec/postfix" -#endif - #define VAR_DAEMON_DIR "daemon_directory" #ifndef DEF_DAEMON_DIR #define DEF_DAEMON_DIR "/usr/libexec/postfix" @@ -298,8 +293,8 @@ extern char *var_rcpt_witheld; extern bool var_strict_rfc821_env; /* - * Standards violation: send "250 AUTH=list" in order to accomodate broken - * Microsoft clients. + * Standards violation: send "250 AUTH=list" in order to accomodate clients + * that implement an old version of the protocol. */ #define VAR_BROKEN_AUTH_CLNTS "broken_sasl_auth_clients" #define DEF_BROKEN_AUTH_CLNTS 0 @@ -549,7 +544,7 @@ extern int var_max_backoff_time; extern int var_max_queue_time; #define VAR_DSN_QUEUE_TIME "bounce_queue_lifetime" -#define DEF_DSN_QUEUE_TIME "$" VAR_MAX_QUEUE_TIME +#define DEF_DSN_QUEUE_TIME "$" VAR_MAX_QUEUE_TIME /* XXX no time unit */ extern int var_dsn_queue_time; #define VAR_DELAY_WARN_TIME "delay_warning_time" @@ -913,6 +908,10 @@ extern bool var_smtpd_sasl_enable; #define DEF_SMTPD_SASL_OPTS "noanonymous" extern char *var_smtpd_sasl_opts; +#define VAR_SMTPD_SASL_APPNAME "smtpd_sasl_application_name" +#define DEF_SMTPD_SASL_APPNAME "smtpd" +extern char *var_smtpd_sasl_appname; + #define VAR_SMTPD_SASL_REALM "smtpd_sasl_local_domain" #define DEF_SMTPD_SASL_REALM "" extern char *var_smtpd_sasl_realm; @@ -1077,8 +1076,8 @@ extern int var_lmtp_quit_tmout; extern bool var_lmtp_send_xforward; /* - * Cleanup service. Header info that exceeds $header_size_limit bytes forces - * the start of the message body. + * Cleanup service. Header info that exceeds $header_size_limit bytes or + * $header_address_token_limit tokens is discarded. */ #define VAR_HOPCOUNT_LIMIT "hopcount_limit" #define DEF_HOPCOUNT_LIMIT 50 diff --git a/postfix/src/global/mail_stream.c b/postfix/src/global/mail_stream.c index 62a06b4cb..6edeea7ad 100644 --- a/postfix/src/global/mail_stream.c +++ b/postfix/src/global/mail_stream.c @@ -114,13 +114,7 @@ static VSTRING *id_buf; void mail_stream_cleanup(MAIL_STREAM *info) { - int status; - - if (info->stream) { - if ((status = info->close(info->stream)) != 0) - msg_warn("bad mail stream close status %d", status); - info->stream = 0; - } + FREE_AND_WIPE(info->close, info->stream); FREE_AND_WIPE(myfree, info->queue); FREE_AND_WIPE(myfree, info->id); FREE_AND_WIPE(myfree, info->class); diff --git a/postfix/src/global/mail_version.h b/postfix/src/global/mail_version.h index c7e7a0bba..f5424cfb0 100644 --- a/postfix/src/global/mail_version.h +++ b/postfix/src/global/mail_version.h @@ -20,7 +20,7 @@ * Patches change the patchlevel and the release date. Snapshots change the * release date only, unless they include the same bugfix as a patch release. */ -#define MAIL_RELEASE_DATE "20040101" +#define MAIL_RELEASE_DATE "20040104" #define VAR_MAIL_VERSION "mail_version" #define DEF_MAIL_VERSION "2.0.16-" MAIL_RELEASE_DATE diff --git a/postfix/src/nqmgr/.indent.pro b/postfix/src/oqmgr/.indent.pro similarity index 100% rename from postfix/src/nqmgr/.indent.pro rename to postfix/src/oqmgr/.indent.pro diff --git a/postfix/src/nqmgr/.printfck b/postfix/src/oqmgr/.printfck similarity index 100% rename from postfix/src/nqmgr/.printfck rename to postfix/src/oqmgr/.printfck diff --git a/postfix/src/nqmgr/Makefile.in b/postfix/src/oqmgr/Makefile.in similarity index 91% rename from postfix/src/nqmgr/Makefile.in rename to postfix/src/oqmgr/Makefile.in index 35f34f1ee..1e932b43e 100644 --- a/postfix/src/nqmgr/Makefile.in +++ b/postfix/src/oqmgr/Makefile.in @@ -1,11 +1,9 @@ SHELL = /bin/sh SRCS = qmgr.c qmgr_active.c qmgr_transport.c qmgr_queue.c qmgr_entry.c \ qmgr_message.c qmgr_deliver.c qmgr_move.c qmgr_rcpt_list.c \ - qmgr_job.c qmgr_peer.c \ qmgr_defer.c qmgr_enable.c qmgr_scan.c qmgr_bounce.c OBJS = qmgr.o qmgr_active.o qmgr_transport.o qmgr_queue.o qmgr_entry.o \ qmgr_message.o qmgr_deliver.o qmgr_move.o qmgr_rcpt_list.o \ - qmgr_job.o qmgr_peer.o \ qmgr_defer.o qmgr_enable.o qmgr_scan.o qmgr_bounce.o HDRS = qmgr.h TESTSRC = @@ -28,10 +26,10 @@ test: $(TESTPROG) tests: test -update: ../../libexec/n$(PROG) +update: ../../libexec/o$(PROG) -../../libexec/n$(PROG): $(PROG) - cp $(PROG) ../../libexec/n$(PROG) +../../libexec/o$(PROG): $(PROG) + cp $(PROG) ../../libexec/o$(PROG) printfck: $(OBJS) $(PROG) rm -rf printfck @@ -158,16 +156,6 @@ qmgr_entry.o: ../../include/vbuf.h qmgr_entry.o: ../../include/mail_params.h qmgr_entry.o: qmgr.h qmgr_entry.o: ../../include/scan_dir.h -qmgr_job.o: qmgr_job.c -qmgr_job.o: ../../include/sys_defs.h -qmgr_job.o: ../../include/msg.h -qmgr_job.o: ../../include/htable.h -qmgr_job.o: ../../include/mymalloc.h -qmgr_job.o: ../../include/sane_time.h -qmgr_job.o: qmgr.h -qmgr_job.o: ../../include/vstream.h -qmgr_job.o: ../../include/vbuf.h -qmgr_job.o: ../../include/scan_dir.h qmgr_message.o: qmgr_message.c qmgr_message.o: ../../include/sys_defs.h qmgr_message.o: ../../include/msg.h @@ -180,7 +168,6 @@ qmgr_message.o: ../../include/valid_hostname.h qmgr_message.o: ../../include/argv.h qmgr_message.o: ../../include/stringops.h qmgr_message.o: ../../include/myflock.h -qmgr_message.o: ../../include/sane_time.h qmgr_message.o: ../../include/dict.h qmgr_message.o: ../../include/mail_queue.h qmgr_message.o: ../../include/mail_params.h @@ -212,15 +199,6 @@ qmgr_move.o: ../../include/vbuf.h qmgr_move.o: ../../include/vstream.h qmgr_move.o: ../../include/mail_scan_dir.h qmgr_move.o: qmgr.h -qmgr_peer.o: qmgr_peer.c -qmgr_peer.o: ../../include/sys_defs.h -qmgr_peer.o: ../../include/msg.h -qmgr_peer.o: ../../include/htable.h -qmgr_peer.o: ../../include/mymalloc.h -qmgr_peer.o: qmgr.h -qmgr_peer.o: ../../include/vstream.h -qmgr_peer.o: ../../include/vbuf.h -qmgr_peer.o: ../../include/scan_dir.h qmgr_queue.o: qmgr_queue.c qmgr_queue.o: ../../include/sys_defs.h qmgr_queue.o: ../../include/msg.h diff --git a/postfix/src/nqmgr/qmgr.c b/postfix/src/oqmgr/qmgr.c similarity index 80% rename from postfix/src/nqmgr/qmgr.c rename to postfix/src/oqmgr/qmgr.c index d7bed895c..b1837bcd5 100644 --- a/postfix/src/nqmgr/qmgr.c +++ b/postfix/src/oqmgr/qmgr.c @@ -2,7 +2,7 @@ /* NAME /* qmgr 8 /* SUMMARY -/* Postfix queue manager +/* old Postfix queue manager /* SYNOPSIS /* \fBqmgr\fR [generic Postfix daemon options] /* DESCRIPTION @@ -80,10 +80,6 @@ /* .IP "\fBdestination status cache\fR" /* The queue manager avoids unnecessary delivery attempts by /* maintaining a short-term, in-memory list of unreachable destinations. -/* .IP "\fBpreemptive message scheduling\fR" -/* The queue manager attempts to minimize the average per-recipient delay -/* while still preserving the correct per-message delays, using -/* a sophisticated preemptive message scheduling. /* TRIGGERS /* .ad /* .fi @@ -141,10 +137,10 @@ /* CONFIGURATION PARAMETERS /* .ad /* .fi -/* The following \fBmain.cf\fR parameters are especially relevant to -/* this program. See the Postfix \fBmain.cf\fR file for syntax details -/* and for default values. Use the \fBpostfix reload\fR command after -/* a configuration change. +/* The following \fBmain.cf\fR parameters are especially relevant to +/* this program. See the Postfix \fBmain.cf\fR file for syntax details +/* and for default values. Use the \fBpostfix reload\fR command after +/* a configuration change. /* .SH Miscellaneous /* .ad /* .fi @@ -155,8 +151,6 @@ /* .SH "Active queue controls" /* .ad /* .fi -/* In the text below, \fItransport\fR is the first field in a -/* \fBmaster.cf\fR entry. /* .IP \fBqmgr_clog_warn_time\fR /* Minimal delay between warnings that a specific destination /* is clogging up the active queue. Specify 0 to disable. @@ -167,19 +161,6 @@ /* .sp /* This parameter also limits the size of the short-term, in-memory /* destination cache. -/* .IP \fBqmgr_message_recipient_minimum\fR -/* Per message minimum of in-memory recipients. -/* .IP \fBdefault_recipient_limit\fR -/* Default limit on the number of in-memory recipients per transport. -/* .IP \fItransport\fB_recipient_limit\fR -/* Limit on the number of in-memory recipients, for the named -/* message \fItransport\fR. -/* .IP \fBdefault_extra_recipient_limit\fR -/* Default limit on the total number of per transport in-memory -/* recipients that the preempting messages can have. -/* .IP \fItransport\fB_extra_recipient_limit\fR -/* Limit on the number of in-memory recipients which all preempting -/* messages delivered by the transport \fItransport\fR can have. /* .SH "Timing controls" /* .ad /* .fi @@ -207,6 +188,19 @@ /* .SH "Concurrency controls" /* .ad /* .fi +/* In the text below, \fItransport\fR is the first field in a +/* \fBmaster.cf\fR entry. +/* .IP "\fBqmgr_fudge_factor\fR (valid range: 10..100)" +/* The percentage of delivery resources that a busy mail system will +/* use up for delivery of a large mailing list message. +/* With 100%, delivery of one message does not begin before the previous +/* message has been delivered. This results in good performance for large +/* mailing lists, but results in poor response time for one-to-one mail. +/* With less than 100%, response time for one-to-one mail improves, +/* but large mailing list delivery performance suffers. In the worst +/* case, recipients near the beginning of a large list receive a burst +/* of messages immediately, while recipients near the end of that list +/* receive that same burst of messages a whole day later. /* .IP \fBinitial_destination_concurrency\fR /* Initial per-destination concurrency level for parallel delivery /* to the same destination. @@ -224,40 +218,6 @@ /* .IP \fItransport\fB_destination_recipient_limit\fR /* Limit on the number of recipients per message transfer, for the /* named message \fItransport\fR. -/* .SH "Message scheduling" -/* .ad -/* .fi -/* .IP "\fItransport\fB_delivery_slot_cost\fR (valid range: 0,2,3...) -/* This parameter basically controls how often a message -/* delivered by \fItransport\fR can be preempted by another -/* message. -/* An internal per-message/transport counter is incremented by one -/* for each \fItransport\fB_delivery_slot_cost\fR -/* deliveries handled by \fItransport\fR. This counter represents -/* the number of "available delivery slots" for use by other messages. -/* Current message can be preempted by another message when that -/* other message can be delivered using less \fItransport\fR agents -/* than the value of the "available delivery slots" counter. -/* .sp -/* Value equal to 0 disables the message preemption for \fItransport\fR. -/* .IP \fItransport\fB_minimum_delivery_slots\fR -/* Message preemption is not attempted at all whenever a message -/* that can't ever accumulate at least \fItransport\fB_minimum_delivery_slots\fR -/* available delivery slots is being delivered by \fItransport\fR. -/* .IP "\fItransport\fB_delivery_slot_discount\fR (valid range: 0..100)" -/* .IP \fItransport\fB_delivery_slot_loan\fR -/* These parameters speed up the moment when a message preemption can happen. -/* Instead of waiting until the full amount of delivery slots -/* required is available, the preemption can happen when -/* \fItransport\fB_delivery_slot_discount\fR percent of the required -/* amount plus \fItransport\fB_delivery_slot_loan\fR still remains to -/* be accumulated. Note that the full amount will still have to be -/* accumulated before another preemption can take place later. -/* .IP \fBdefault_delivery_slot_cost\fR -/* .IP \fBdefault_minimum_delivery_slots\fR -/* .IP \fBdefault_delivery_slot_discount\fR -/* .IP \fBdefault_delivery_slot_loan\fR -/* Default values for the transport specific parameters described above. /* SEE ALSO /* master(8), process manager /* syslogd(8) system logging @@ -271,11 +231,6 @@ /* IBM T.J. Watson Research /* P.O. Box 704 /* Yorktown Heights, NY 10598, USA -/* -/* Scheduler enhancements: -/* Patrik Rak -/* Modra 6 -/* 155 00, Prague, Czech Republic /*--*/ /* System library. */ @@ -320,21 +275,15 @@ int var_max_queue_time; int var_dsn_queue_time; int var_qmgr_active_limit; int var_qmgr_rcpt_limit; -int var_qmgr_msg_rcpt_limit; -int var_xport_rcpt_limit; -int var_stack_rcpt_limit; -int var_delivery_slot_cost; -int var_delivery_slot_loan; -int var_delivery_slot_discount; -int var_min_delivery_slots; int var_init_dest_concurrency; int var_transport_retry_time; int var_dest_con_limit; int var_dest_rcpt_limit; char *var_defer_xports; bool var_allow_min_user; -int var_local_con_lim; -int var_local_rcpt_lim; +int var_qmgr_fudge; +int var_local_rcpt_lim; /* XXX */ +int var_local_con_lim; /* XXX */ int var_proc_limit; bool var_verp_bounce_off; bool var_sender_routing; @@ -443,13 +392,16 @@ static int qmgr_loop(char *unused_name, char **unused_argv) /* * Let some new blood into the active queue when the queue size is - * smaller than some configurable limit. When the system is under heavy - * load, favor new mail over old mail. + * smaller than some configurable limit, and when the number of in-core + * recipients does not exceed some configurable limit. When the system is + * under heavy load, favor new mail over old mail. */ - if (qmgr_message_count < var_qmgr_active_limit) + if (qmgr_message_count < var_qmgr_active_limit + && qmgr_recipient_count < var_qmgr_rcpt_limit) if ((in_path = qmgr_scan_next(qmgr_incoming)) != 0) in_feed = qmgr_active_feed(qmgr_incoming, in_path); - if (qmgr_message_count < var_qmgr_active_limit) + if (qmgr_message_count < var_qmgr_active_limit + && qmgr_recipient_count < var_qmgr_rcpt_limit) if ((df_path = qmgr_scan_next(qmgr_deferred)) != 0) qmgr_active_feed(qmgr_deferred, df_path); @@ -541,16 +493,10 @@ int main(int argc, char **argv) static CONFIG_INT_TABLE int_table[] = { VAR_QMGR_ACT_LIMIT, DEF_QMGR_ACT_LIMIT, &var_qmgr_active_limit, 1, 0, VAR_QMGR_RCPT_LIMIT, DEF_QMGR_RCPT_LIMIT, &var_qmgr_rcpt_limit, 1, 0, - VAR_QMGR_MSG_RCPT_LIMIT, DEF_QMGR_MSG_RCPT_LIMIT, &var_qmgr_msg_rcpt_limit, 1, 0, - VAR_XPORT_RCPT_LIMIT, DEF_XPORT_RCPT_LIMIT, &var_xport_rcpt_limit, 0, 0, - VAR_STACK_RCPT_LIMIT, DEF_STACK_RCPT_LIMIT, &var_stack_rcpt_limit, 0, 0, - VAR_DELIVERY_SLOT_COST, DEF_DELIVERY_SLOT_COST, &var_delivery_slot_cost, 0, 0, - VAR_DELIVERY_SLOT_LOAN, DEF_DELIVERY_SLOT_LOAN, &var_delivery_slot_loan, 0, 0, - VAR_DELIVERY_SLOT_DISCOUNT, DEF_DELIVERY_SLOT_DISCOUNT, &var_delivery_slot_discount, 0, 100, - VAR_MIN_DELIVERY_SLOTS, DEF_MIN_DELIVERY_SLOTS, &var_min_delivery_slots, 0, 0, VAR_INIT_DEST_CON, DEF_INIT_DEST_CON, &var_init_dest_concurrency, 1, 0, VAR_DEST_CON_LIMIT, DEF_DEST_CON_LIMIT, &var_dest_con_limit, 0, 0, VAR_DEST_RCPT_LIMIT, DEF_DEST_RCPT_LIMIT, &var_dest_rcpt_limit, 0, 0, + VAR_QMGR_FUDGE, DEF_QMGR_FUDGE, &var_qmgr_fudge, 10, 100, VAR_LOCAL_RCPT_LIMIT, DEF_LOCAL_RCPT_LIMIT, &var_local_rcpt_lim, 0, 0, VAR_LOCAL_CON_LIMIT, DEF_LOCAL_CON_LIMIT, &var_local_con_lim, 0, 0, VAR_PROC_LIMIT, DEF_PROC_LIMIT, &var_proc_limit, 1, 0, diff --git a/postfix/src/nqmgr/qmgr.h b/postfix/src/oqmgr/qmgr.h similarity index 65% rename from postfix/src/nqmgr/qmgr.h rename to postfix/src/oqmgr/qmgr.h index 8a8f5a925..2981b4b54 100644 --- a/postfix/src/nqmgr/qmgr.h +++ b/postfix/src/oqmgr/qmgr.h @@ -22,13 +22,9 @@ typedef struct QMGR_TRANSPORT QMGR_TRANSPORT; typedef struct QMGR_QUEUE QMGR_QUEUE; typedef struct QMGR_ENTRY QMGR_ENTRY; typedef struct QMGR_MESSAGE QMGR_MESSAGE; -typedef struct QMGR_JOB QMGR_JOB; -typedef struct QMGR_PEER QMGR_PEER; typedef struct QMGR_TRANSPORT_LIST QMGR_TRANSPORT_LIST; typedef struct QMGR_QUEUE_LIST QMGR_QUEUE_LIST; typedef struct QMGR_ENTRY_LIST QMGR_ENTRY_LIST; -typedef struct QMGR_JOB_LIST QMGR_JOB_LIST; -typedef struct QMGR_PEER_LIST QMGR_PEER_LIST; typedef struct QMGR_RCPT QMGR_RCPT; typedef struct QMGR_RCPT_LIST QMGR_RCPT_LIST; typedef struct QMGR_SCAN QMGR_SCAN; @@ -36,16 +32,17 @@ typedef struct QMGR_SCAN QMGR_SCAN; /* * Hairy macros to update doubly-linked lists. */ -#define QMGR_LIST_ROTATE(head, object, peers) { \ +#define QMGR_LIST_ROTATE(head, object) { \ head.next->peers.prev = head.prev; \ head.prev->peers.next = head.next; \ head.next = object->peers.next; \ - head.next->peers.prev = 0; \ + if (object->peers.next) \ + head.next->peers.prev = 0; \ head.prev = object; \ object->peers.next = 0; \ } -#define QMGR_LIST_UNLINK(head, type, object, peers) { \ +#define QMGR_LIST_UNLINK(head, type, object) { \ type next = object->peers.next; \ type prev = object->peers.prev; \ if (prev) prev->peers.next = next; \ @@ -55,16 +52,7 @@ typedef struct QMGR_SCAN QMGR_SCAN; object->peers.next = object->peers.prev = 0; \ } -#define QMGR_LIST_LINK(head, pred, object, succ, peers) { \ - object->peers.prev = pred; \ - object->peers.next = succ; \ - if (pred) pred->peers.next = object; \ - else head.next = object; \ - if (succ) succ->peers.prev = object; \ - else head.prev = object; \ -} - -#define QMGR_LIST_PREPEND(head, object, peers) { \ +#define QMGR_LIST_APPEND(head, object) { \ object->peers.next = head.next; \ object->peers.prev = 0; \ if (head.next) { \ @@ -75,7 +63,7 @@ typedef struct QMGR_SCAN QMGR_SCAN; head.next = object; \ } -#define QMGR_LIST_APPEND(head, object, peers) { \ +#define QMGR_LIST_PREPEND(head, object) { \ object->peers.prev = head.prev; \ object->peers.next = 0; \ if (head.prev) { \ @@ -114,40 +102,14 @@ struct QMGR_QUEUE_LIST { QMGR_QUEUE *prev; }; -struct QMGR_JOB_LIST { - QMGR_JOB *next; - QMGR_JOB *prev; -}; - struct QMGR_TRANSPORT { int flags; /* blocked, etc. */ char *name; /* transport name */ int dest_concurrency_limit; /* concurrency per domain */ int init_dest_concurrency; /* init. per-domain concurrency */ int recipient_limit; /* recipients per transaction */ - int rcpt_per_stack; /* extra slots reserved for jobs put - * on the job stack */ - int rcpt_unused; /* available in-core recipient slots */ - int slot_cost; /* cost of new preemption slot (# of - * selected entries) */ - int slot_loan; /* preemption boost offset and */ - int slot_loan_factor; /* factor, see qmgr_job_preempt() */ - int min_slots; /* when preemption can take effect at - * all */ struct HTABLE *queue_byname; /* queues indexed by domain */ QMGR_QUEUE_LIST queue_list; /* queues, round robin order */ - struct HTABLE *job_byname; /* jobs indexed by queue id */ - QMGR_JOB_LIST job_list; /* list of message jobs (1 per - * message) ordered by scheduler */ - QMGR_JOB_LIST job_bytime; /* jobs ordered by time since queued */ - QMGR_JOB *job_current; /* keeps track of the current job */ - QMGR_JOB *job_next_unread; /* next job with unread recipients */ - QMGR_JOB *candidate_cache; /* cached result from - * qmgr_job_candidate() */ - QMGR_JOB *candidate_cache_current; /* current job tied to the candidate */ - time_t candidate_cache_time; /* when candidate_cache was last - * updated */ - int blocker_tag; /* for marking blocker jobs */ QMGR_TRANSPORT_LIST peers; /* linkage */ char *reason; /* why unavailable */ }; @@ -168,7 +130,9 @@ extern QMGR_TRANSPORT *qmgr_transport_find(const char *); * transactions. The "todo" queue contains messages that are to be delivered * to this next hop. When a message is elected for transmission, it is moved * from the "todo" queue to the "busy" queue. Messages are taken from the - * "todo" queue in round-robin order. + * "todo" queue in sequence. An initial destination delivery concurrency > 1 + * ensures that one problematic message will not block all other traffic to + * that next hop. */ struct QMGR_ENTRY_LIST { QMGR_ENTRY *next; @@ -186,8 +150,7 @@ struct QMGR_QUEUE { QMGR_ENTRY_LIST busy; /* messages on the wire */ QMGR_QUEUE_LIST peers; /* neighbor queues */ char *reason; /* why unavailable */ - time_t clog_time_to_warn; /* time of last warning */ - int blocker_tag; /* tagged if blocks job list */ + time_t clog_time_to_warn; /* time of next warning */ }; #define QMGR_QUEUE_TODO 1 /* waiting for service */ @@ -196,6 +159,7 @@ struct QMGR_QUEUE { extern int qmgr_queue_count; extern QMGR_QUEUE *qmgr_queue_create(QMGR_TRANSPORT *, const char *, const char *); +extern QMGR_QUEUE *qmgr_queue_select(QMGR_TRANSPORT *); extern void qmgr_queue_done(QMGR_QUEUE *); extern void qmgr_queue_throttle(QMGR_QUEUE *, const char *); extern void qmgr_queue_unthrottle(QMGR_QUEUE *); @@ -234,15 +198,13 @@ struct QMGR_ENTRY { QMGR_MESSAGE *message; /* message info */ QMGR_RCPT_LIST rcpt_list; /* as many as it takes */ QMGR_QUEUE *queue; /* parent linkage */ - QMGR_PEER *peer; /* parent linkage */ - QMGR_ENTRY_LIST queue_peers; /* per queue neighbor entries */ - QMGR_ENTRY_LIST peer_peers; /* per peer neighbor entries */ + QMGR_ENTRY_LIST peers; /* neighbor entries */ }; -extern QMGR_ENTRY *qmgr_entry_select(QMGR_PEER *); -extern void qmgr_entry_unselect(QMGR_ENTRY *); +extern QMGR_ENTRY *qmgr_entry_select(QMGR_QUEUE *); +extern void qmgr_entry_unselect(QMGR_QUEUE *, QMGR_ENTRY *); extern void qmgr_entry_done(QMGR_ENTRY *, int); -extern QMGR_ENTRY *qmgr_entry_create(QMGR_PEER *, QMGR_MESSAGE *); +extern QMGR_ENTRY *qmgr_entry_create(QMGR_QUEUE *, QMGR_MESSAGE *); /* * All common in-core information about a message is kept here. When all @@ -259,8 +221,6 @@ struct QMGR_MESSAGE { int refcount; /* queue entries */ int single_rcpt; /* send one rcpt at a time */ long arrival_time; /* time when queued */ - time_t queued_time; /* time when moved to the active - * queue */ long warn_offset; /* warning bounce flag offset */ time_t warn_time; /* time next warning to be sent */ long data_offset; /* data seek offset */ @@ -281,11 +241,6 @@ struct QMGR_MESSAGE { char *client_proto; /* client protocol */ char *client_helo; /* helo parameter */ QMGR_RCPT_LIST rcpt_list; /* complete addresses */ - int rcpt_count; /* used recipient slots */ - int rcpt_limit; /* maximum read in-core */ - int rcpt_unread; /* # of recipients left in queue file */ - QMGR_JOB_LIST job_list; /* jobs delivering this message (1 - * per transport) */ }; /* @@ -303,63 +258,6 @@ extern void qmgr_message_update_warn(QMGR_MESSAGE *); extern QMGR_MESSAGE *qmgr_message_alloc(const char *, const char *, int); extern QMGR_MESSAGE *qmgr_message_realloc(QMGR_MESSAGE *); - /* - * Sometimes it's required to access the transport queues and entries on per - * message basis. That's what the QMGR_JOB structure is for - it groups all - * per message information within each transport using a list of QMGR_PEER - * structures. These structures in turn correspond with per message - * QMGR_QUEUE structure and list all per message QMGR_ENTRY structures. - */ -struct QMGR_PEER_LIST { - QMGR_PEER *next; - QMGR_PEER *prev; -}; - -struct QMGR_JOB { - QMGR_MESSAGE *message; /* message delivered by this job */ - QMGR_TRANSPORT *transport; /* transport this job belongs to */ - QMGR_JOB_LIST message_peers; /* per message neighbor linkage */ - QMGR_JOB_LIST transport_peers; /* per transport neighbor linkage */ - QMGR_JOB_LIST time_peers; /* by time neighbor linkage */ - QMGR_JOB *stack_parent; /* stack parent */ - QMGR_JOB_LIST stack_children; /* all stack children */ - QMGR_JOB_LIST stack_siblings; /* stack children linkage */ - int stack_level; /* job stack nesting level (-1 means - * it's not on the lists at all) */ - int blocker_tag; /* tagged if blocks the job list */ - struct HTABLE *peer_byname; /* message job peers, indexed by - * domain */ - QMGR_PEER_LIST peer_list; /* list of message job peers */ - int slots_used; /* slots used during preemption */ - int slots_available; /* slots available for preemption (in - * multiples of slot_cost) */ - int selected_entries; /* # of entries selected for delivery - * so far */ - int read_entries; /* # of entries read in-core so far */ - int rcpt_count; /* used recipient slots */ - int rcpt_limit; /* available recipient slots */ -}; - -struct QMGR_PEER { - QMGR_JOB *job; /* job handling this peer */ - QMGR_QUEUE *queue; /* queue corresponding with this peer */ - int refcount; /* peer entries */ - QMGR_ENTRY_LIST entry_list; /* todo message entries queued for - * this peer */ - QMGR_PEER_LIST peers; /* neighbor linkage */ -}; - -extern QMGR_ENTRY *qmgr_job_entry_select(QMGR_TRANSPORT *); -extern QMGR_PEER *qmgr_peer_select(QMGR_JOB *); - -extern QMGR_JOB *qmgr_job_obtain(QMGR_MESSAGE *, QMGR_TRANSPORT *); -extern void qmgr_job_free(QMGR_JOB *); -extern void qmgr_job_move_limits(QMGR_JOB *); - -extern QMGR_PEER *qmgr_peer_create(QMGR_JOB *, QMGR_QUEUE *); -extern QMGR_PEER *qmgr_peer_find(QMGR_JOB *, QMGR_QUEUE *); -extern void qmgr_peer_free(QMGR_PEER *); - /* * qmgr_defer.c */ @@ -431,9 +329,4 @@ extern char *qmgr_scan_next(QMGR_SCAN *); /* IBM T.J. Watson Research /* P.O. Box 704 /* Yorktown Heights, NY 10598, USA -/* -/* Scheduler enhancements: -/* Patrik Rak -/* Modra 6 -/* 155 00, Prague, Czech Republic /*--*/ diff --git a/postfix/src/nqmgr/qmgr_active.c b/postfix/src/oqmgr/qmgr_active.c similarity index 98% rename from postfix/src/nqmgr/qmgr_active.c rename to postfix/src/oqmgr/qmgr_active.c index 3999b4a5f..bd2c9098f 100644 --- a/postfix/src/nqmgr/qmgr_active.c +++ b/postfix/src/oqmgr/qmgr_active.c @@ -364,11 +364,11 @@ static void qmgr_active_done_2_generic(QMGR_MESSAGE *message) * is full. */ if (message->tflags & (DEL_REQ_FLAG_EXPAND | DEL_REQ_FLAG_RECORD)) - message->flags |= trace_flush(message->tflags, - message->queue_name, - message->queue_id, - message->encoding, - message->sender); + message->flags |= trace_flush(message->tflags, + message->queue_name, + message->queue_id, + message->encoding, + message->sender); /* * If we get to this point we have tried all recipients for this message. diff --git a/postfix/src/nqmgr/qmgr_bounce.c b/postfix/src/oqmgr/qmgr_bounce.c similarity index 95% rename from postfix/src/nqmgr/qmgr_bounce.c rename to postfix/src/oqmgr/qmgr_bounce.c index 92196bdaa..284342d6d 100644 --- a/postfix/src/nqmgr/qmgr_bounce.c +++ b/postfix/src/oqmgr/qmgr_bounce.c @@ -35,11 +35,6 @@ /* IBM T.J. Watson Research /* P.O. Box 704 /* Yorktown Heights, NY 10598, USA -/* -/* Scheduler enhancements: -/* Patrik Rak -/* Modra 6 -/* 155 00, Prague, Czech Republic /*--*/ /* System library. */ diff --git a/postfix/src/nqmgr/qmgr_defer.c b/postfix/src/oqmgr/qmgr_defer.c similarity index 97% rename from postfix/src/nqmgr/qmgr_defer.c rename to postfix/src/oqmgr/qmgr_defer.c index 0a70df54c..405a7f476 100644 --- a/postfix/src/nqmgr/qmgr_defer.c +++ b/postfix/src/oqmgr/qmgr_defer.c @@ -60,11 +60,6 @@ /* IBM T.J. Watson Research /* P.O. Box 704 /* Yorktown Heights, NY 10598, USA -/* -/* Scheduler enhancements: -/* Patrik Rak -/* Modra 6 -/* 155 00, Prague, Czech Republic /*--*/ /* System library. */ @@ -132,7 +127,7 @@ void qmgr_defer_todo(QMGR_QUEUE *queue, const char *reason) * Proceed carefully. Queue entries will disappear as a side effect. */ for (entry = queue->todo.next; entry != 0; entry = next) { - next = entry->queue_peers.next; + next = entry->peers.next; message = entry->message; for (nrcpt = 0; nrcpt < entry->rcpt_list.len; nrcpt++) { recipient = entry->rcpt_list.info + nrcpt; diff --git a/postfix/src/nqmgr/qmgr_deliver.c b/postfix/src/oqmgr/qmgr_deliver.c similarity index 98% rename from postfix/src/nqmgr/qmgr_deliver.c rename to postfix/src/oqmgr/qmgr_deliver.c index cfdf635ff..d41064ad5 100644 --- a/postfix/src/nqmgr/qmgr_deliver.c +++ b/postfix/src/oqmgr/qmgr_deliver.c @@ -39,11 +39,6 @@ /* IBM T.J. Watson Research /* P.O. Box 704 /* Yorktown Heights, NY 10598, USA -/* -/* Scheduler enhancements: -/* Patrik Rak -/* Modra 6 -/* 155 00, Prague, Czech Republic /*--*/ /* System library. */ @@ -287,6 +282,7 @@ static void qmgr_deliver_update(int unused_event, char *context) void qmgr_deliver(QMGR_TRANSPORT *transport, VSTREAM *stream) { + QMGR_QUEUE *queue; QMGR_ENTRY *entry; /* @@ -310,7 +306,8 @@ void qmgr_deliver(QMGR_TRANSPORT *transport, VSTREAM *stream) * agent request reading routine is prepared for the queue manager to * change its mind for no apparent reason. */ - if ((entry = qmgr_job_entry_select(transport)) == 0) { + if ((queue = qmgr_queue_select(transport)) == 0 + || (entry = qmgr_entry_select(queue)) == 0) { (void) vstream_fclose(stream); return; } @@ -322,10 +319,10 @@ void qmgr_deliver(QMGR_TRANSPORT *transport, VSTREAM *stream) * while some other queue manipulation is happening. */ if (qmgr_deliver_send_request(entry, stream) < 0) { - qmgr_entry_unselect(entry); + qmgr_entry_unselect(queue, entry); qmgr_transport_throttle(transport, "mail transport unavailable"); qmgr_defer_transport(transport, transport->reason); - /* warning: entry may be a dangling pointer here */ + /* warning: entry and queue may be dangling pointers here */ (void) vstream_fclose(stream); return; } diff --git a/postfix/src/nqmgr/qmgr_enable.c b/postfix/src/oqmgr/qmgr_enable.c similarity index 100% rename from postfix/src/nqmgr/qmgr_enable.c rename to postfix/src/oqmgr/qmgr_enable.c diff --git a/postfix/src/nqmgr/qmgr_entry.c b/postfix/src/oqmgr/qmgr_entry.c similarity index 65% rename from postfix/src/nqmgr/qmgr_entry.c rename to postfix/src/oqmgr/qmgr_entry.c index c26d29325..3d3f9f461 100644 --- a/postfix/src/nqmgr/qmgr_entry.c +++ b/postfix/src/oqmgr/qmgr_entry.c @@ -6,8 +6,8 @@ /* SYNOPSIS /* #include "qmgr.h" /* -/* QMGR_ENTRY *qmgr_entry_create(peer, message) -/* QMGR_PEER *peer; +/* QMGR_ENTRY *qmgr_entry_create(queue, message) +/* QMGR_QUEUE *queue; /* QMGR_MESSAGE *message; /* /* void qmgr_entry_done(entry, which) @@ -24,8 +24,8 @@ /* These routines add/delete/manipulate per-site message /* delivery requests. /* -/* qmgr_entry_create() creates an entry for the named peer and message, -/* and appends the entry to the peer's list and its queue's todo list. +/* qmgr_entry_create() creates an entry for the named queue and +/* message, and appends the entry to the queue's todo list. /* Filling in and cleaning up the recipients is the responsibility /* of the caller. /* @@ -36,9 +36,6 @@ /* of the site's `todo' list (i.e. queue entries awaiting selection /* for actual delivery). /* -/* qmgr_entry_done() discards its peer structure when the peer -/* is not referenced anymore. -/* /* qmgr_entry_done() triggers cleanup of the per-site queue when /* the site has no pending deliveries, and the site is either /* alive, or the site is dead and the number of in-core queues @@ -50,14 +47,14 @@ /* the queue file to the deferred queue; send bounce reports to the /* message originator (see qmgr_active_done()). /* -/* qmgr_entry_select() selects first entry from the named +/* qmgr_entry_select() selects the next entry from the named /* per-site queue's `todo' list for actual delivery. The entry is /* moved to the queue's `busy' list: the list of messages being -/* delivered. The entry is also removed from its peer list. +/* delivered. /* /* qmgr_entry_unselect() takes the named entry off the named /* per-site queue's `busy' list and moves it to the queue's -/* `todo' list. The entry is also appended to its peer list again. +/* `todo' list. /* DIAGNOSTICS /* Panic: interface violations, internal inconsistencies. /* LICENSE @@ -69,11 +66,6 @@ /* IBM T.J. Watson Research /* P.O. Box 704 /* Yorktown Heights, NY 10598, USA -/* -/* Scheduler enhancements: -/* Patrik Rak -/* Modra 6 -/* 155 00, Prague, Czech Republic /*--*/ /* System library. */ @@ -99,36 +91,27 @@ /* qmgr_entry_select - select queue entry for delivery */ -QMGR_ENTRY *qmgr_entry_select(QMGR_PEER *peer) +QMGR_ENTRY *qmgr_entry_select(QMGR_QUEUE *queue) { QMGR_ENTRY *entry; - QMGR_QUEUE *queue; - if ((entry = peer->entry_list.next) != 0) { - queue = entry->queue; - QMGR_LIST_UNLINK(queue->todo, QMGR_ENTRY *, entry, queue_peers); + if ((entry = queue->todo.prev) != 0) { + QMGR_LIST_UNLINK(queue->todo, QMGR_ENTRY *, entry); queue->todo_refcount--; - QMGR_LIST_APPEND(queue->busy, entry, queue_peers); + QMGR_LIST_APPEND(queue->busy, entry); queue->busy_refcount++; - QMGR_LIST_UNLINK(peer->entry_list, QMGR_ENTRY *, entry, peer_peers); - peer->job->selected_entries++; } return (entry); } /* qmgr_entry_unselect - unselect queue entry for delivery */ -void qmgr_entry_unselect(QMGR_ENTRY *entry) +void qmgr_entry_unselect(QMGR_QUEUE *queue, QMGR_ENTRY *entry) { - QMGR_PEER *peer = entry->peer; - QMGR_QUEUE *queue = entry->queue; - - QMGR_LIST_UNLINK(queue->busy, QMGR_ENTRY *, entry, queue_peers); + QMGR_LIST_UNLINK(queue->busy, QMGR_ENTRY *, entry); queue->busy_refcount--; - QMGR_LIST_APPEND(queue->todo, entry, queue_peers); + QMGR_LIST_APPEND(queue->todo, entry); queue->todo_refcount++; - QMGR_LIST_APPEND(peer->entry_list, entry, peer_peers); - peer->job->selected_entries--; } /* qmgr_entry_done - dispose of queue entry */ @@ -137,10 +120,6 @@ void qmgr_entry_done(QMGR_ENTRY *entry, int which) { QMGR_QUEUE *queue = entry->queue; QMGR_MESSAGE *message = entry->message; - QMGR_PEER *peer = entry->peer; - QMGR_JOB *sponsor, - *job = peer->job; - QMGR_TRANSPORT *transport = job->transport; /* * Take this entry off the in-core queue. @@ -148,76 +127,24 @@ void qmgr_entry_done(QMGR_ENTRY *entry, int which) if (entry->stream != 0) msg_panic("qmgr_entry_done: file is open"); if (which == QMGR_QUEUE_BUSY) { - QMGR_LIST_UNLINK(queue->busy, QMGR_ENTRY *, entry, queue_peers); + QMGR_LIST_UNLINK(queue->busy, QMGR_ENTRY *, entry); queue->busy_refcount--; } else if (which == QMGR_QUEUE_TODO) { - QMGR_LIST_UNLINK(peer->entry_list, QMGR_ENTRY *, entry, peer_peers); - job->selected_entries++; - QMGR_LIST_UNLINK(queue->todo, QMGR_ENTRY *, entry, queue_peers); + QMGR_LIST_UNLINK(queue->todo, QMGR_ENTRY *, entry); queue->todo_refcount--; } else { msg_panic("qmgr_entry_done: bad queue spec: %d", which); } /* - * Decrease the in-core recipient counts and free the recipient list and - * the structure itself. + * Free the recipient list and decrease the in-core recipient count + * accordingly. */ - job->rcpt_count -= entry->rcpt_list.len; - message->rcpt_count -= entry->rcpt_list.len; qmgr_recipient_count -= entry->rcpt_list.len; qmgr_rcpt_list_free(&entry->rcpt_list); + myfree((char *) entry); - /* - * Make sure that the transport of any retired or finishing job that - * donated recipient slots to this message gets them back first. Then, if - * possible, pass the remaining unused recipient slots to the next job on - * the job list. - */ - for (sponsor = message->job_list.next; sponsor; sponsor = sponsor->message_peers.next) { - if (sponsor->rcpt_count >= sponsor->rcpt_limit || sponsor == job) - continue; - if (sponsor->stack_level < 0 || message->rcpt_offset == 0) - qmgr_job_move_limits(sponsor); - } - if (message->rcpt_offset == 0) { - qmgr_job_move_limits(job); - } - - /* - * 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 || queue->window == 0) - queue->blocker_tag = 0; - } - - /* - * When there are no more entries for this peer, discard the peer - * structure. - */ - peer->refcount--; - if (peer->refcount == 0) - qmgr_peer_free(peer); - /* * When the in-core queue for this site is empty and when this site is * not dead, discard the in-core queue. When this site is dead, but the @@ -234,18 +161,33 @@ void qmgr_entry_done(QMGR_ENTRY *entry, int which) /* * Update the in-core message reference count. When the in-core message * structure has no more references, dispose of the message. + * + * When the in-core recipient count falls below a threshold, and this + * message has more recipients, read more recipients now. If we read more + * recipients as soon as the recipient count falls below the in-core + * recipient limit, we do not give other messages a chance until this + * message is delivered. That's good for mailing list deliveries, bad for + * one-to-one mail. If we wait until the in-core recipient count drops + * well below the in-core recipient limit, we give other mail a chance, + * but we also allow list deliveries to become interleaved. In the worst + * case, people near the start of a mailing list get a burst of postings + * today, while people near the end of the list get that same burst of + * postings a whole day later. */ +#define FUDGE(x) ((x) * (var_qmgr_fudge / 100.0)) message->refcount--; + if (message->rcpt_offset > 0 + && qmgr_recipient_count < FUDGE(var_qmgr_rcpt_limit)) + qmgr_message_realloc(message); if (message->refcount == 0) qmgr_active_done(message); } /* qmgr_entry_create - create queue todo entry */ -QMGR_ENTRY *qmgr_entry_create(QMGR_PEER *peer, QMGR_MESSAGE *message) +QMGR_ENTRY *qmgr_entry_create(QMGR_QUEUE *queue, QMGR_MESSAGE *message) { QMGR_ENTRY *entry; - QMGR_QUEUE *queue = peer->queue; /* * Sanity check. @@ -261,11 +203,8 @@ QMGR_ENTRY *qmgr_entry_create(QMGR_PEER *peer, QMGR_MESSAGE *message) entry->message = message; qmgr_rcpt_list_init(&entry->rcpt_list); message->refcount++; - entry->peer = peer; - QMGR_LIST_APPEND(peer->entry_list, entry, peer_peers); - peer->refcount++; entry->queue = queue; - QMGR_LIST_APPEND(queue->todo, entry, queue_peers); + QMGR_LIST_APPEND(queue->todo, entry); queue->todo_refcount++; /* diff --git a/postfix/src/nqmgr/qmgr_message.c b/postfix/src/oqmgr/qmgr_message.c similarity index 87% rename from postfix/src/nqmgr/qmgr_message.c rename to postfix/src/oqmgr/qmgr_message.c index b8bf2afa0..2eb2925f7 100644 --- a/postfix/src/nqmgr/qmgr_message.c +++ b/postfix/src/oqmgr/qmgr_message.c @@ -49,10 +49,7 @@ /* qmgr_message_realloc() resumes reading recipients from the queue /* file, and updates the recipient list and \fIrcpt_offset\fR message /* structure members. A null result means that the file could not be -/* read or that the file contained incorrect information. Recipient -/* limit imposed this time is based on the position of the message -/* job(s) on corresponding transport job list(s). It's considered -/* an error to call this when the recipient slots can't be allocated. +/* read or that the file contained incorrect information. /* /* qmgr_message_free() destroys an in-core message structure and makes /* the resources available for reuse. It is an error to destroy @@ -73,11 +70,6 @@ /* IBM T.J. Watson Research /* P.O. Box 704 /* Yorktown Heights, NY 10598, USA -/* -/* Scheduler enhancements: -/* Patrik Rak -/* Modra 6 -/* 155 00, Prague, Czech Republic /*--*/ /* System library. */ @@ -106,7 +98,6 @@ #include #include #include -#include /* Global library. */ @@ -152,7 +143,6 @@ static QMGR_MESSAGE *qmgr_message_create(const char *queue_name, message->refcount = 0; message->single_rcpt = 0; message->arrival_time = 0; - message->queued_time = sane_time(); message->data_offset = 0; message->queue_id = mystrdup(queue_id); message->queue_name = mystrdup(queue_name); @@ -173,10 +163,6 @@ static QMGR_MESSAGE *qmgr_message_create(const char *queue_name, message->client_proto = 0; message->client_helo = 0; qmgr_rcpt_list_init(&message->rcpt_list); - message->rcpt_count = 0; - message->rcpt_limit = var_qmgr_msg_rcpt_limit; - message->rcpt_unread = 0; - QMGR_LIST_INIT(message->job_list); return (message); } @@ -244,7 +230,6 @@ static void qmgr_message_oldstyle_scan(QMGR_MESSAGE *message) * so we set data_size to this as well and ignore the size record itself * completely. */ - message->rcpt_unread = 0; for (;;) { rec_type = rec_get(message->fp, buf, 0); if (rec_type <= 0) @@ -255,10 +240,6 @@ static void qmgr_message_oldstyle_scan(QMGR_MESSAGE *message) msg_info("old-style scan record %c %s", rec_type, start); if (rec_type == REC_TYPE_END) break; - if (rec_type == REC_TYPE_DONE || rec_type == REC_TYPE_RCPT) { - message->rcpt_unread++; - continue; - } if (rec_type == REC_TYPE_MESG) { if (message->data_offset == 0) { if ((message->data_offset = vstream_ftell(message->fp)) < 0) @@ -297,9 +278,8 @@ static int qmgr_message_read(QMGR_MESSAGE *message) int rec_type; long curr_offset; long save_offset = message->rcpt_offset; /* save a flag */ - int save_unread = message->rcpt_unread; /* save a count */ char *start; - int recipient_limit; + int nrcpt = 0; const char *error_text; char *name; char *value; @@ -314,15 +294,6 @@ static int qmgr_message_read(QMGR_MESSAGE *message) /* * If we re-open this file, skip over on-file recipient records that we * already looked at, and refill the in-core recipient address list. - * - * For the first time, the message recipient limit is calculated from the - * global recipient limit. This is to avoid reading little recipients - * when the active queue is near empty. When the queue becomes full, only - * the necessary amount is read in core. Such priming is necessary - * because there are no message jobs yet. - * - * For the next time, the recipient limit is based solely on the message - * jobs' positions in the job lists and/or job stacks. */ if (message->rcpt_offset) { if (message->rcpt_list.len) @@ -331,29 +302,26 @@ static int qmgr_message_read(QMGR_MESSAGE *message) if (vstream_fseek(message->fp, message->rcpt_offset, SEEK_SET) < 0) msg_fatal("seek file %s: %m", VSTREAM_PATH(message->fp)); message->rcpt_offset = 0; - recipient_limit = message->rcpt_limit - message->rcpt_count; - } else { - recipient_limit = var_qmgr_rcpt_limit - qmgr_recipient_count; - if (recipient_limit < message->rcpt_limit) - recipient_limit = message->rcpt_limit; } - if (recipient_limit <= 0) - msg_panic("%s: no recipient slots available", message->queue_id); /* * Read envelope records. XXX Rely on the front-end programs to enforce - * record size limits. Read up to recipient_limit recipients from the + * record size limits. Read up to var_qmgr_rcpt_limit recipients from the * queue file, to protect against memory exhaustion. Recipient records * may appear before or after the message content, so we keep reading * from the queue file until we have enough recipients (rcpt_offset != 0) * and until we know all the non-recipient information. * - * Note that the total recipient count record is accurate only for fresh - * queue files. After some of the recipients are marked as done and the - * queue file is deferred, it can be used as upper bound estimate only. - * Fortunately, this poses no major problem on the scheduling algorithm, - * as the only impact is that the already deferred messages are not - * chosen by qmgr_job_candidate() as often as they could. + * When reading recipients from queue file, stop reading when we reach a + * per-message in-core recipient limit rather than a global in-core + * recipient limit. Use the global recipient limit only in order to stop + * opening queue files. The purpose is to achieve equal delay for + * messages with recipient counts up to var_qmgr_rcpt_limit recipients. + * + * If we would read recipients up to a global recipient limit, the average + * number of in-core recipients per message would asymptotically approach + * (global recipient limit)/(active queue size limit), which gives equal + * delay per recipient rather than equal delay per message. * * On the first open, we must examine all non-recipient records. * @@ -390,15 +358,15 @@ static int qmgr_message_read(QMGR_MESSAGE *message) */ if (rec_type == REC_TYPE_RCPT) { /* See also below for code setting orig_rcpt. */ +#define FUDGE(x) ((x) * (var_qmgr_fudge / 100.0)) if (message->rcpt_offset == 0) { - message->rcpt_unread--; qmgr_rcpt_list_add(&message->rcpt_list, curr_offset, orig_rcpt ? orig_rcpt : "", start); if (orig_rcpt) { myfree(orig_rcpt); orig_rcpt = 0; } - if (message->rcpt_list.len >= recipient_limit) { + if (message->rcpt_list.len >= FUDGE(var_qmgr_rcpt_limit)) { if ((message->rcpt_offset = vstream_ftell(message->fp)) < 0) msg_fatal("vstream_ftell %s: %m", VSTREAM_PATH(message->fp)); @@ -424,12 +392,9 @@ static int qmgr_message_read(QMGR_MESSAGE *message) continue; } if (rec_type == REC_TYPE_DONE) { - if (message->rcpt_offset == 0) { - message->rcpt_unread--; - if (orig_rcpt) { - myfree(orig_rcpt); - orig_rcpt = 0; - } + if (orig_rcpt) { + myfree(orig_rcpt); + orig_rcpt = 0; } continue; } @@ -457,7 +422,7 @@ static int qmgr_message_read(QMGR_MESSAGE *message) if (message->data_offset == 0) { if ((count = sscanf(start, "%ld %ld %d %d", &message->data_size, &message->data_offset, - &message->rcpt_unread, &message->rflags)) >= 3) { + &nrcpt, &message->rflags)) >= 3) { /* Postfix >= 1.0 (a.k.a. 20010228). */ if (message->data_offset <= 0 || message->data_size <= 0) { msg_warn("%s: invalid size record: %.100s", @@ -511,7 +476,7 @@ static int qmgr_message_read(QMGR_MESSAGE *message) if (message->sender == 0) { message->sender = mystrdup(start); opened(message->queue_id, message->sender, - message->data_size, message->rcpt_unread, + message->data_size, nrcpt, "queue %s", message->queue_name); } continue; @@ -629,12 +594,6 @@ static int qmgr_message_read(QMGR_MESSAGE *message) * Sanity checks. Verify that all required information was found, * including the queue file end marker. */ - if (message->rcpt_unread < 0 - || (message->rcpt_offset == 0 && message->rcpt_unread != 0)) { - msg_warn("%s: rcpt count mismatch (%d)", - message->queue_id, message->rcpt_unread); - message->rcpt_unread = 0; - } if (rec_type <= 0) { /* Already logged warning. */ } else if (message->arrival_time == 0) { @@ -650,9 +609,6 @@ static int qmgr_message_read(QMGR_MESSAGE *message) return (0); } message->rcpt_offset = save_offset; /* restore flag */ - message->rcpt_unread = save_unread; /* restore count */ - qmgr_rcpt_list_free(&message->rcpt_list); - qmgr_rcpt_list_init(&message->rcpt_list); return (-1); } @@ -729,7 +685,7 @@ static int qmgr_message_sort_compare(const void *p1, const void *p2) /* * Compare recipient address. */ - return (strcmp(rcpt1->address, rcpt2->address)); + return (strcasecmp(rcpt1->address, rcpt2->address)); } /* qmgr_message_sort - sort message recipient addresses by domain */ @@ -1014,8 +970,6 @@ static void qmgr_message_assign(QMGR_MESSAGE *message) QMGR_RCPT *recipient; QMGR_ENTRY *entry = 0; QMGR_QUEUE *queue; - QMGR_JOB *job = 0; - QMGR_PEER *peer = 0; /* * Try to bundle as many recipients in a delivery request as we can. When @@ -1031,87 +985,27 @@ static void qmgr_message_assign(QMGR_MESSAGE *message) for (recipient = list.info; recipient < list.info + list.len; recipient++) { if ((queue = recipient->queue) != 0) { if (message->single_rcpt || entry == 0 || entry->queue != queue - || !LIMIT_OK(queue->transport->recipient_limit, + || !LIMIT_OK(entry->queue->transport->recipient_limit, entry->rcpt_list.len)) { - - /* - * Lookup or instantiate the message job if necessary. - */ - if (job == 0 || queue->transport != job->transport) { - job = qmgr_job_obtain(message, queue->transport); - peer = 0; - } - - /* - * Lookup or instantiate job peer if necessary. - */ - if (peer == 0 || queue != peer->queue) { - if ((peer = qmgr_peer_find(job, queue)) == 0) - peer = qmgr_peer_create(job, queue); - } - - /* - * Create new peer entry. - */ - entry = qmgr_entry_create(peer, message); - job->read_entries++; + entry = qmgr_entry_create(queue, message); } - - /* - * Add the recipient to the current entry and increase all those - * recipient counters accordingly. - */ qmgr_rcpt_list_add(&entry->rcpt_list, recipient->offset, recipient->orig_rcpt, recipient->address); - job->rcpt_count++; - message->rcpt_count++; qmgr_recipient_count++; } } - - /* - * Release the message recipient list and reinitialize it for the next - * time. - */ qmgr_rcpt_list_free(&message->rcpt_list); qmgr_rcpt_list_init(&message->rcpt_list); - - /* - * Note that even if qmgr_job_obtain() reset the job candidate cache of - * all transports to which we assigned new recipients, this message may - * have other jobs which we didn't touch at all this time. But the number - * of unread recipients affecting the candidate selection might have - * changed considerably, so we must invalidate the caches if it might be - * of some use. - */ - for (job = message->job_list.next; job; job = job->message_peers.next) - if (job->selected_entries < job->read_entries - && job->blocker_tag != job->transport->blocker_tag) - job->transport->candidate_cache_current = 0; -} - -/* qmgr_message_move_limits - recycle unused recipient slots */ - -static void qmgr_message_move_limits(QMGR_MESSAGE *message) -{ - QMGR_JOB *job; - - for (job = message->job_list.next; job; job = job->message_peers.next) - qmgr_job_move_limits(job); } /* qmgr_message_free - release memory for in-core message structure */ void qmgr_message_free(QMGR_MESSAGE *message) { - QMGR_JOB *job; - if (message->refcount != 0) msg_panic("qmgr_message_free: reference len: %d", message->refcount); if (message->fp) msg_panic("qmgr_message_free: queue file is open"); - while ((job = message->job_list.next) != 0) - qmgr_job_free(job); myfree(message->queue_id); myfree(message->queue_name); if (message->encoding) @@ -1196,8 +1090,6 @@ QMGR_MESSAGE *qmgr_message_alloc(const char *queue_name, const char *queue_id, qmgr_message_sort(message); qmgr_message_assign(message); qmgr_message_close(message); - if (message->rcpt_offset == 0) - qmgr_message_move_limits(message); return (message); } } @@ -1232,8 +1124,6 @@ QMGR_MESSAGE *qmgr_message_realloc(QMGR_MESSAGE *message) qmgr_message_sort(message); qmgr_message_assign(message); qmgr_message_close(message); - if (message->rcpt_offset == 0) - qmgr_message_move_limits(message); return (message); } } diff --git a/postfix/src/nqmgr/qmgr_move.c b/postfix/src/oqmgr/qmgr_move.c similarity index 100% rename from postfix/src/nqmgr/qmgr_move.c rename to postfix/src/oqmgr/qmgr_move.c diff --git a/postfix/src/nqmgr/qmgr_queue.c b/postfix/src/oqmgr/qmgr_queue.c similarity index 87% rename from postfix/src/nqmgr/qmgr_queue.c rename to postfix/src/oqmgr/qmgr_queue.c index b86034a57..3c88b7eda 100644 --- a/postfix/src/nqmgr/qmgr_queue.c +++ b/postfix/src/oqmgr/qmgr_queue.c @@ -20,6 +20,9 @@ /* QMGR_TRANSPORT *transport; /* const char *name; /* +/* QMGR_QUEUE *qmgr_queue_select(transport) +/* QMGR_TRANSPORT *transport; +/* /* void qmgr_queue_throttle(queue, reason) /* QMGR_QUEUE *queue; /* const char *reason; @@ -49,6 +52,10 @@ /* qmgr_queue_find() looks up the named queue for the named /* transport. A null result means that the queue was not found. /* +/* qmgr_queue_select() uses a round-robin strategy to select +/* from the named transport one per-destination queue with a +/* non-empty `todo' list. +/* /* qmgr_queue_throttle() handles a delivery error, and decrements the /* concurrency limit for the destination. When the concurrency limit /* for a destination becomes zero, qmgr_queue_throttle() starts a timer @@ -60,7 +67,7 @@ /* limit specified for the transport. This routine implements /* "slow open" mode, and eliminates the "thundering herd" problem. /* DIAGNOSTICS -/* Panic: consistency check failure. +/* None /* LICENSE /* .ad /* .fi @@ -70,11 +77,6 @@ /* IBM T.J. Watson Research /* P.O. Box 704 /* Yorktown Heights, NY 10598, USA -/* -/* Scheduler enhancements: -/* Patrik Rak -/* Modra 6 -/* 155 00, Prague, Czech Republic /*--*/ /* System library. */ @@ -146,7 +148,7 @@ void qmgr_queue_unthrottle(QMGR_QUEUE *queue) */ if (transport->dest_concurrency_limit == 0 || transport->dest_concurrency_limit > queue->window) - if (queue->window < queue->busy_refcount + transport->init_dest_concurrency) + if (queue->window <= queue->busy_refcount + transport->init_dest_concurrency) queue->window++; } @@ -184,6 +186,27 @@ void qmgr_queue_throttle(QMGR_QUEUE *queue, const char *reason) } } +/* qmgr_queue_select - select in-core queue for delivery */ + +QMGR_QUEUE *qmgr_queue_select(QMGR_TRANSPORT *transport) +{ + QMGR_QUEUE *queue; + + /* + * If we find a suitable site, rotate the list to enforce round-robin + * selection. See similar selection code in qmgr_transport_select(). + */ + for (queue = transport->queue_list.next; queue; queue = queue->peers.next) { + if (queue->window > queue->busy_refcount && queue->todo.next != 0) { + QMGR_LIST_ROTATE(transport->queue_list, queue); + if (msg_verbose) + msg_info("qmgr_queue_select: %s", queue->name); + return (queue); + } + } + return (0); +} + /* qmgr_queue_done - delete in-core queue for site */ void qmgr_queue_done(QMGR_QUEUE *queue) @@ -209,7 +232,7 @@ void qmgr_queue_done(QMGR_QUEUE *queue) /* * Clean up this in-core queue. */ - QMGR_LIST_UNLINK(transport->queue_list, QMGR_QUEUE *, queue, peers); + QMGR_LIST_UNLINK(transport->queue_list, QMGR_QUEUE *, queue); htable_delete(transport->queue_byname, queue->name, (void (*) (char *)) 0); myfree(queue->name); myfree(queue->nexthop); @@ -241,8 +264,7 @@ QMGR_QUEUE *qmgr_queue_create(QMGR_TRANSPORT *transport, const char *name, QMGR_LIST_INIT(queue->busy); queue->reason = 0; queue->clog_time_to_warn = 0; - queue->blocker_tag = 0; - QMGR_LIST_APPEND(transport->queue_list, queue, peers); + QMGR_LIST_PREPEND(transport->queue_list, queue); htable_enter(transport->queue_byname, name, (char *) queue); return (queue); } diff --git a/postfix/src/nqmgr/qmgr_rcpt_list.c b/postfix/src/oqmgr/qmgr_rcpt_list.c similarity index 100% rename from postfix/src/nqmgr/qmgr_rcpt_list.c rename to postfix/src/oqmgr/qmgr_rcpt_list.c diff --git a/postfix/src/nqmgr/qmgr_scan.c b/postfix/src/oqmgr/qmgr_scan.c similarity index 100% rename from postfix/src/nqmgr/qmgr_scan.c rename to postfix/src/oqmgr/qmgr_scan.c diff --git a/postfix/src/nqmgr/qmgr_transport.c b/postfix/src/oqmgr/qmgr_transport.c similarity index 88% rename from postfix/src/nqmgr/qmgr_transport.c rename to postfix/src/oqmgr/qmgr_transport.c index 204880962..dcb4ad1fa 100644 --- a/postfix/src/nqmgr/qmgr_transport.c +++ b/postfix/src/oqmgr/qmgr_transport.c @@ -64,11 +64,6 @@ /* IBM T.J. Watson Research /* P.O. Box 704 /* Yorktown Heights, NY 10598, USA -/* -/* Scheduler enhancements: -/* Patrik Rak -/* Modra 6 -/* 155 00, Prague, Czech Republic /*--*/ /* System library. */ @@ -238,7 +233,7 @@ QMGR_TRANSPORT *qmgr_transport_select(void) /* * If we find a suitable transport, rotate the list of transports to * effectuate round-robin selection. See similar selection code in - * qmgr_peer_select(). + * qmgr_queue_select(). */ #define STAY_AWAY (QMGR_TRANSPORT_STAT_BUSY | QMGR_TRANSPORT_STAT_DEAD) @@ -247,7 +242,7 @@ QMGR_TRANSPORT *qmgr_transport_select(void) continue; for (queue = xport->queue_list.next; queue; queue = queue->peers.next) { if (queue->window > queue->busy_refcount && queue->todo.next != 0) { - QMGR_LIST_ROTATE(qmgr_transport_list, xport, peers); + QMGR_LIST_ROTATE(qmgr_transport_list, xport); if (msg_verbose) msg_info("qmgr_transport_select: %s", xport->name); return (xport); @@ -324,10 +319,10 @@ QMGR_TRANSPORT *qmgr_transport_create(const char *name) * Use global configuration settings or transport-specific settings. */ transport->dest_concurrency_limit = - get_mail_conf_int2(name, _DEST_CON_LIMIT, + get_mail_conf_int2(name, "_destination_concurrency_limit", var_dest_con_limit, 0, 0); transport->recipient_limit = - get_mail_conf_int2(name, _DEST_RCPT_LIMIT, + get_mail_conf_int2(name, "_destination_recipient_limit", var_dest_rcpt_limit, 0, 0); if (transport->dest_concurrency_limit == 0 @@ -336,36 +331,13 @@ QMGR_TRANSPORT *qmgr_transport_create(const char *name) else transport->init_dest_concurrency = transport->dest_concurrency_limit; - transport->slot_cost = get_mail_conf_int2(name, _DELIVERY_SLOT_COST, - var_delivery_slot_cost, 0, 0); - transport->slot_loan = get_mail_conf_int2(name, _DELIVERY_SLOT_LOAN, - var_delivery_slot_loan, 0, 0); - transport->slot_loan_factor = - 100 - get_mail_conf_int2(name, _DELIVERY_SLOT_DISCOUNT, - var_delivery_slot_discount, 0, 100); - transport->min_slots = get_mail_conf_int2(name, _MIN_DELIVERY_SLOTS, - var_min_delivery_slots, 0, 0); - transport->rcpt_unused = get_mail_conf_int2(name, _XPORT_RCPT_LIMIT, - var_xport_rcpt_limit, 0, 0); - transport->rcpt_per_stack = get_mail_conf_int2(name, _STACK_RCPT_LIMIT, - var_stack_rcpt_limit, 0, 0); - transport->queue_byname = htable_create(0); QMGR_LIST_INIT(transport->queue_list); - transport->job_byname = htable_create(0); - QMGR_LIST_INIT(transport->job_list); - QMGR_LIST_INIT(transport->job_bytime); - transport->job_current = 0; - transport->job_next_unread = 0; - transport->candidate_cache = 0; - transport->candidate_cache_current = 0; - transport->candidate_cache_time = (time_t) 0; - transport->blocker_tag = 1; transport->reason = 0; if (qmgr_transport_byname == 0) qmgr_transport_byname = htable_create(10); htable_enter(qmgr_transport_byname, name, (char *) transport); - QMGR_LIST_PREPEND(qmgr_transport_list, transport, peers); + QMGR_LIST_APPEND(qmgr_transport_list, transport); if (msg_verbose) msg_info("qmgr_transport_create: %s concurrency %d recipients %d", transport->name, transport->dest_concurrency_limit, diff --git a/postfix/src/pickup/pickup.c b/postfix/src/pickup/pickup.c index daa92429d..717e144fd 100644 --- a/postfix/src/pickup/pickup.c +++ b/postfix/src/pickup/pickup.c @@ -234,6 +234,7 @@ static int pickup_copy(VSTREAM *qfile, VSTREAM *cleanup, { time_t now = time((time_t *) 0); int status; + char *name; /* * Protect against time-warped time stamps. Warn about mail that has been @@ -282,8 +283,20 @@ static int pickup_copy(VSTREAM *qfile, VSTREAM *cleanup, info->id, (long) info->st.st_uid); return (REMOVE_MESSAGE_FILE); } - msg_info("%s: uid=%d from=<%s>", info->id, - (int) info->st.st_uid, info->sender); + + /* + * For messages belonging to $mail_owner also log the maildrop queue id. + * This supports message tracking for mail requeued via "postsuper -r". + */ + if (info->st.st_uid == var_owner_uid) { + msg_info("%s: uid=%d from=<%s> orig_id=%s", info->id, + (int) info->st.st_uid, info->sender, + ((name = strrchr(info->path, '/')) ? + name + 1 : info->path)); + } else { + msg_info("%s: uid=%d from=<%s>", info->id, + (int) info->st.st_uid, info->sender); + } /* * Message content segment. Send a dummy message length. Prepend a diff --git a/postfix/src/qmgr/Makefile.in b/postfix/src/qmgr/Makefile.in index a640af3dc..4926d1e40 100644 --- a/postfix/src/qmgr/Makefile.in +++ b/postfix/src/qmgr/Makefile.in @@ -1,9 +1,11 @@ SHELL = /bin/sh SRCS = qmgr.c qmgr_active.c qmgr_transport.c qmgr_queue.c qmgr_entry.c \ qmgr_message.c qmgr_deliver.c qmgr_move.c qmgr_rcpt_list.c \ + qmgr_job.c qmgr_peer.c \ qmgr_defer.c qmgr_enable.c qmgr_scan.c qmgr_bounce.c OBJS = qmgr.o qmgr_active.o qmgr_transport.o qmgr_queue.o qmgr_entry.o \ qmgr_message.o qmgr_deliver.o qmgr_move.o qmgr_rcpt_list.o \ + qmgr_job.o qmgr_peer.o \ qmgr_defer.o qmgr_enable.o qmgr_scan.o qmgr_bounce.o HDRS = qmgr.h TESTSRC = @@ -29,7 +31,7 @@ tests: test update: ../../libexec/$(PROG) ../../libexec/$(PROG): $(PROG) - cp $(PROG) ../../libexec + cp $(PROG) ../../libexec/$(PROG) printfck: $(OBJS) $(PROG) rm -rf printfck @@ -156,6 +158,16 @@ qmgr_entry.o: ../../include/vbuf.h qmgr_entry.o: ../../include/mail_params.h qmgr_entry.o: qmgr.h qmgr_entry.o: ../../include/scan_dir.h +qmgr_job.o: qmgr_job.c +qmgr_job.o: ../../include/sys_defs.h +qmgr_job.o: ../../include/msg.h +qmgr_job.o: ../../include/htable.h +qmgr_job.o: ../../include/mymalloc.h +qmgr_job.o: ../../include/sane_time.h +qmgr_job.o: qmgr.h +qmgr_job.o: ../../include/vstream.h +qmgr_job.o: ../../include/vbuf.h +qmgr_job.o: ../../include/scan_dir.h qmgr_message.o: qmgr_message.c qmgr_message.o: ../../include/sys_defs.h qmgr_message.o: ../../include/msg.h @@ -168,6 +180,7 @@ qmgr_message.o: ../../include/valid_hostname.h qmgr_message.o: ../../include/argv.h qmgr_message.o: ../../include/stringops.h qmgr_message.o: ../../include/myflock.h +qmgr_message.o: ../../include/sane_time.h qmgr_message.o: ../../include/dict.h qmgr_message.o: ../../include/mail_queue.h qmgr_message.o: ../../include/mail_params.h @@ -199,6 +212,15 @@ qmgr_move.o: ../../include/vbuf.h qmgr_move.o: ../../include/vstream.h qmgr_move.o: ../../include/mail_scan_dir.h qmgr_move.o: qmgr.h +qmgr_peer.o: qmgr_peer.c +qmgr_peer.o: ../../include/sys_defs.h +qmgr_peer.o: ../../include/msg.h +qmgr_peer.o: ../../include/htable.h +qmgr_peer.o: ../../include/mymalloc.h +qmgr_peer.o: qmgr.h +qmgr_peer.o: ../../include/vstream.h +qmgr_peer.o: ../../include/vbuf.h +qmgr_peer.o: ../../include/scan_dir.h qmgr_queue.o: qmgr_queue.c qmgr_queue.o: ../../include/sys_defs.h qmgr_queue.o: ../../include/msg.h diff --git a/postfix/src/qmgr/qmgr.c b/postfix/src/qmgr/qmgr.c index 978f37a5a..e6adb235a 100644 --- a/postfix/src/qmgr/qmgr.c +++ b/postfix/src/qmgr/qmgr.c @@ -80,6 +80,10 @@ /* .IP "\fBdestination status cache\fR" /* The queue manager avoids unnecessary delivery attempts by /* maintaining a short-term, in-memory list of unreachable destinations. +/* .IP "\fBpreemptive message scheduling\fR" +/* The queue manager attempts to minimize the average per-recipient delay +/* while still preserving the correct per-message delays, using +/* a sophisticated preemptive message scheduling. /* TRIGGERS /* .ad /* .fi @@ -137,10 +141,10 @@ /* CONFIGURATION PARAMETERS /* .ad /* .fi -/* The following \fBmain.cf\fR parameters are especially relevant to -/* this program. See the Postfix \fBmain.cf\fR file for syntax details -/* and for default values. Use the \fBpostfix reload\fR command after -/* a configuration change. +/* The following \fBmain.cf\fR parameters are especially relevant to +/* this program. See the Postfix \fBmain.cf\fR file for syntax details +/* and for default values. Use the \fBpostfix reload\fR command after +/* a configuration change. /* .SH Miscellaneous /* .ad /* .fi @@ -151,6 +155,8 @@ /* .SH "Active queue controls" /* .ad /* .fi +/* In the text below, \fItransport\fR is the first field in a +/* \fBmaster.cf\fR entry. /* .IP \fBqmgr_clog_warn_time\fR /* Minimal delay between warnings that a specific destination /* is clogging up the active queue. Specify 0 to disable. @@ -161,6 +167,19 @@ /* .sp /* This parameter also limits the size of the short-term, in-memory /* destination cache. +/* .IP \fBqmgr_message_recipient_minimum\fR +/* Per message minimum of in-memory recipients. +/* .IP \fBdefault_recipient_limit\fR +/* Default limit on the number of in-memory recipients per transport. +/* .IP \fItransport\fB_recipient_limit\fR +/* Limit on the number of in-memory recipients, for the named +/* message \fItransport\fR. +/* .IP \fBdefault_extra_recipient_limit\fR +/* Default limit on the total number of per transport in-memory +/* recipients that the preempting messages can have. +/* .IP \fItransport\fB_extra_recipient_limit\fR +/* Limit on the number of in-memory recipients which all preempting +/* messages delivered by the transport \fItransport\fR can have. /* .SH "Timing controls" /* .ad /* .fi @@ -188,19 +207,6 @@ /* .SH "Concurrency controls" /* .ad /* .fi -/* In the text below, \fItransport\fR is the first field in a -/* \fBmaster.cf\fR entry. -/* .IP "\fBqmgr_fudge_factor\fR (valid range: 10..100)" -/* The percentage of delivery resources that a busy mail system will -/* use up for delivery of a large mailing list message. -/* With 100%, delivery of one message does not begin before the previous -/* message has been delivered. This results in good performance for large -/* mailing lists, but results in poor response time for one-to-one mail. -/* With less than 100%, response time for one-to-one mail improves, -/* but large mailing list delivery performance suffers. In the worst -/* case, recipients near the beginning of a large list receive a burst -/* of messages immediately, while recipients near the end of that list -/* receive that same burst of messages a whole day later. /* .IP \fBinitial_destination_concurrency\fR /* Initial per-destination concurrency level for parallel delivery /* to the same destination. @@ -218,6 +224,40 @@ /* .IP \fItransport\fB_destination_recipient_limit\fR /* Limit on the number of recipients per message transfer, for the /* named message \fItransport\fR. +/* .SH "Message scheduling" +/* .ad +/* .fi +/* .IP "\fItransport\fB_delivery_slot_cost\fR (valid range: 0,2,3...) +/* This parameter basically controls how often a message +/* delivered by \fItransport\fR can be preempted by another +/* message. +/* An internal per-message/transport counter is incremented by one +/* for each \fItransport\fB_delivery_slot_cost\fR +/* deliveries handled by \fItransport\fR. This counter represents +/* the number of "available delivery slots" for use by other messages. +/* Current message can be preempted by another message when that +/* other message can be delivered using less \fItransport\fR agents +/* than the value of the "available delivery slots" counter. +/* .sp +/* Value equal to 0 disables the message preemption for \fItransport\fR. +/* .IP \fItransport\fB_minimum_delivery_slots\fR +/* Message preemption is not attempted at all whenever a message +/* that can't ever accumulate at least \fItransport\fB_minimum_delivery_slots\fR +/* available delivery slots is being delivered by \fItransport\fR. +/* .IP "\fItransport\fB_delivery_slot_discount\fR (valid range: 0..100)" +/* .IP \fItransport\fB_delivery_slot_loan\fR +/* These parameters speed up the moment when a message preemption can happen. +/* Instead of waiting until the full amount of delivery slots +/* required is available, the preemption can happen when +/* \fItransport\fB_delivery_slot_discount\fR percent of the required +/* amount plus \fItransport\fB_delivery_slot_loan\fR still remains to +/* be accumulated. Note that the full amount will still have to be +/* accumulated before another preemption can take place later. +/* .IP \fBdefault_delivery_slot_cost\fR +/* .IP \fBdefault_minimum_delivery_slots\fR +/* .IP \fBdefault_delivery_slot_discount\fR +/* .IP \fBdefault_delivery_slot_loan\fR +/* Default values for the transport specific parameters described above. /* SEE ALSO /* master(8), process manager /* syslogd(8) system logging @@ -231,6 +271,11 @@ /* IBM T.J. Watson Research /* P.O. Box 704 /* Yorktown Heights, NY 10598, USA +/* +/* Scheduler enhancements: +/* Patrik Rak +/* Modra 6 +/* 155 00, Prague, Czech Republic /*--*/ /* System library. */ @@ -275,15 +320,21 @@ int var_max_queue_time; int var_dsn_queue_time; int var_qmgr_active_limit; int var_qmgr_rcpt_limit; +int var_qmgr_msg_rcpt_limit; +int var_xport_rcpt_limit; +int var_stack_rcpt_limit; +int var_delivery_slot_cost; +int var_delivery_slot_loan; +int var_delivery_slot_discount; +int var_min_delivery_slots; int var_init_dest_concurrency; int var_transport_retry_time; int var_dest_con_limit; int var_dest_rcpt_limit; char *var_defer_xports; bool var_allow_min_user; -int var_qmgr_fudge; -int var_local_rcpt_lim; /* XXX */ -int var_local_con_lim; /* XXX */ +int var_local_con_lim; +int var_local_rcpt_lim; int var_proc_limit; bool var_verp_bounce_off; bool var_sender_routing; @@ -392,16 +443,13 @@ static int qmgr_loop(char *unused_name, char **unused_argv) /* * Let some new blood into the active queue when the queue size is - * smaller than some configurable limit, and when the number of in-core - * recipients does not exceed some configurable limit. When the system is - * under heavy load, favor new mail over old mail. + * smaller than some configurable limit. When the system is under heavy + * load, favor new mail over old mail. */ - if (qmgr_message_count < var_qmgr_active_limit - && qmgr_recipient_count < var_qmgr_rcpt_limit) + if (qmgr_message_count < var_qmgr_active_limit) if ((in_path = qmgr_scan_next(qmgr_incoming)) != 0) in_feed = qmgr_active_feed(qmgr_incoming, in_path); - if (qmgr_message_count < var_qmgr_active_limit - && qmgr_recipient_count < var_qmgr_rcpt_limit) + if (qmgr_message_count < var_qmgr_active_limit) if ((df_path = qmgr_scan_next(qmgr_deferred)) != 0) qmgr_active_feed(qmgr_deferred, df_path); @@ -439,9 +487,19 @@ static void pre_accept(char *unused_name, char **unused_argv) /* qmgr_post_init - post-jail initialization */ -static void qmgr_post_init(char *unused_name, char **unused_argv) +static void qmgr_post_init(char *name, char **unused_argv) { + /* + * Backwards compatibility. + */ + if (strcmp(var_procname, "nqmgr") == 0) { + msg_warn("please update the %s/%s file; the new queue manager", + var_config_dir, MASTER_CONF_FILE); + msg_warn("(old name: nqmgr) has become the standard queue manager (new name: qmgr)"); + msg_warn("support for the name old name (nqmgr) will be removed from Postfix"); + } + /* * Sanity check. */ @@ -493,10 +551,16 @@ int main(int argc, char **argv) static CONFIG_INT_TABLE int_table[] = { VAR_QMGR_ACT_LIMIT, DEF_QMGR_ACT_LIMIT, &var_qmgr_active_limit, 1, 0, VAR_QMGR_RCPT_LIMIT, DEF_QMGR_RCPT_LIMIT, &var_qmgr_rcpt_limit, 1, 0, + VAR_QMGR_MSG_RCPT_LIMIT, DEF_QMGR_MSG_RCPT_LIMIT, &var_qmgr_msg_rcpt_limit, 1, 0, + VAR_XPORT_RCPT_LIMIT, DEF_XPORT_RCPT_LIMIT, &var_xport_rcpt_limit, 0, 0, + VAR_STACK_RCPT_LIMIT, DEF_STACK_RCPT_LIMIT, &var_stack_rcpt_limit, 0, 0, + VAR_DELIVERY_SLOT_COST, DEF_DELIVERY_SLOT_COST, &var_delivery_slot_cost, 0, 0, + VAR_DELIVERY_SLOT_LOAN, DEF_DELIVERY_SLOT_LOAN, &var_delivery_slot_loan, 0, 0, + VAR_DELIVERY_SLOT_DISCOUNT, DEF_DELIVERY_SLOT_DISCOUNT, &var_delivery_slot_discount, 0, 100, + VAR_MIN_DELIVERY_SLOTS, DEF_MIN_DELIVERY_SLOTS, &var_min_delivery_slots, 0, 0, VAR_INIT_DEST_CON, DEF_INIT_DEST_CON, &var_init_dest_concurrency, 1, 0, VAR_DEST_CON_LIMIT, DEF_DEST_CON_LIMIT, &var_dest_con_limit, 0, 0, VAR_DEST_RCPT_LIMIT, DEF_DEST_RCPT_LIMIT, &var_dest_rcpt_limit, 0, 0, - VAR_QMGR_FUDGE, DEF_QMGR_FUDGE, &var_qmgr_fudge, 10, 100, VAR_LOCAL_RCPT_LIMIT, DEF_LOCAL_RCPT_LIMIT, &var_local_rcpt_lim, 0, 0, VAR_LOCAL_CON_LIMIT, DEF_LOCAL_CON_LIMIT, &var_local_con_lim, 0, 0, VAR_PROC_LIMIT, DEF_PROC_LIMIT, &var_proc_limit, 1, 0, diff --git a/postfix/src/qmgr/qmgr.h b/postfix/src/qmgr/qmgr.h index 2981b4b54..8a8f5a925 100644 --- a/postfix/src/qmgr/qmgr.h +++ b/postfix/src/qmgr/qmgr.h @@ -22,9 +22,13 @@ typedef struct QMGR_TRANSPORT QMGR_TRANSPORT; typedef struct QMGR_QUEUE QMGR_QUEUE; typedef struct QMGR_ENTRY QMGR_ENTRY; typedef struct QMGR_MESSAGE QMGR_MESSAGE; +typedef struct QMGR_JOB QMGR_JOB; +typedef struct QMGR_PEER QMGR_PEER; typedef struct QMGR_TRANSPORT_LIST QMGR_TRANSPORT_LIST; typedef struct QMGR_QUEUE_LIST QMGR_QUEUE_LIST; typedef struct QMGR_ENTRY_LIST QMGR_ENTRY_LIST; +typedef struct QMGR_JOB_LIST QMGR_JOB_LIST; +typedef struct QMGR_PEER_LIST QMGR_PEER_LIST; typedef struct QMGR_RCPT QMGR_RCPT; typedef struct QMGR_RCPT_LIST QMGR_RCPT_LIST; typedef struct QMGR_SCAN QMGR_SCAN; @@ -32,17 +36,16 @@ typedef struct QMGR_SCAN QMGR_SCAN; /* * Hairy macros to update doubly-linked lists. */ -#define QMGR_LIST_ROTATE(head, object) { \ +#define QMGR_LIST_ROTATE(head, object, peers) { \ head.next->peers.prev = head.prev; \ head.prev->peers.next = head.next; \ head.next = object->peers.next; \ - if (object->peers.next) \ - head.next->peers.prev = 0; \ + head.next->peers.prev = 0; \ head.prev = object; \ object->peers.next = 0; \ } -#define QMGR_LIST_UNLINK(head, type, object) { \ +#define QMGR_LIST_UNLINK(head, type, object, peers) { \ type next = object->peers.next; \ type prev = object->peers.prev; \ if (prev) prev->peers.next = next; \ @@ -52,7 +55,16 @@ typedef struct QMGR_SCAN QMGR_SCAN; object->peers.next = object->peers.prev = 0; \ } -#define QMGR_LIST_APPEND(head, object) { \ +#define QMGR_LIST_LINK(head, pred, object, succ, peers) { \ + object->peers.prev = pred; \ + object->peers.next = succ; \ + if (pred) pred->peers.next = object; \ + else head.next = object; \ + if (succ) succ->peers.prev = object; \ + else head.prev = object; \ +} + +#define QMGR_LIST_PREPEND(head, object, peers) { \ object->peers.next = head.next; \ object->peers.prev = 0; \ if (head.next) { \ @@ -63,7 +75,7 @@ typedef struct QMGR_SCAN QMGR_SCAN; head.next = object; \ } -#define QMGR_LIST_PREPEND(head, object) { \ +#define QMGR_LIST_APPEND(head, object, peers) { \ object->peers.prev = head.prev; \ object->peers.next = 0; \ if (head.prev) { \ @@ -102,14 +114,40 @@ struct QMGR_QUEUE_LIST { QMGR_QUEUE *prev; }; +struct QMGR_JOB_LIST { + QMGR_JOB *next; + QMGR_JOB *prev; +}; + struct QMGR_TRANSPORT { int flags; /* blocked, etc. */ char *name; /* transport name */ int dest_concurrency_limit; /* concurrency per domain */ int init_dest_concurrency; /* init. per-domain concurrency */ int recipient_limit; /* recipients per transaction */ + int rcpt_per_stack; /* extra slots reserved for jobs put + * on the job stack */ + int rcpt_unused; /* available in-core recipient slots */ + int slot_cost; /* cost of new preemption slot (# of + * selected entries) */ + int slot_loan; /* preemption boost offset and */ + int slot_loan_factor; /* factor, see qmgr_job_preempt() */ + int min_slots; /* when preemption can take effect at + * all */ struct HTABLE *queue_byname; /* queues indexed by domain */ QMGR_QUEUE_LIST queue_list; /* queues, round robin order */ + struct HTABLE *job_byname; /* jobs indexed by queue id */ + QMGR_JOB_LIST job_list; /* list of message jobs (1 per + * message) ordered by scheduler */ + QMGR_JOB_LIST job_bytime; /* jobs ordered by time since queued */ + QMGR_JOB *job_current; /* keeps track of the current job */ + QMGR_JOB *job_next_unread; /* next job with unread recipients */ + QMGR_JOB *candidate_cache; /* cached result from + * qmgr_job_candidate() */ + QMGR_JOB *candidate_cache_current; /* current job tied to the candidate */ + time_t candidate_cache_time; /* when candidate_cache was last + * updated */ + int blocker_tag; /* for marking blocker jobs */ QMGR_TRANSPORT_LIST peers; /* linkage */ char *reason; /* why unavailable */ }; @@ -130,9 +168,7 @@ extern QMGR_TRANSPORT *qmgr_transport_find(const char *); * transactions. The "todo" queue contains messages that are to be delivered * to this next hop. When a message is elected for transmission, it is moved * from the "todo" queue to the "busy" queue. Messages are taken from the - * "todo" queue in sequence. An initial destination delivery concurrency > 1 - * ensures that one problematic message will not block all other traffic to - * that next hop. + * "todo" queue in round-robin order. */ struct QMGR_ENTRY_LIST { QMGR_ENTRY *next; @@ -150,7 +186,8 @@ struct QMGR_QUEUE { QMGR_ENTRY_LIST busy; /* messages on the wire */ QMGR_QUEUE_LIST peers; /* neighbor queues */ char *reason; /* why unavailable */ - time_t clog_time_to_warn; /* time of next warning */ + time_t clog_time_to_warn; /* time of last warning */ + int blocker_tag; /* tagged if blocks job list */ }; #define QMGR_QUEUE_TODO 1 /* waiting for service */ @@ -159,7 +196,6 @@ struct QMGR_QUEUE { extern int qmgr_queue_count; extern QMGR_QUEUE *qmgr_queue_create(QMGR_TRANSPORT *, const char *, const char *); -extern QMGR_QUEUE *qmgr_queue_select(QMGR_TRANSPORT *); extern void qmgr_queue_done(QMGR_QUEUE *); extern void qmgr_queue_throttle(QMGR_QUEUE *, const char *); extern void qmgr_queue_unthrottle(QMGR_QUEUE *); @@ -198,13 +234,15 @@ struct QMGR_ENTRY { QMGR_MESSAGE *message; /* message info */ QMGR_RCPT_LIST rcpt_list; /* as many as it takes */ QMGR_QUEUE *queue; /* parent linkage */ - QMGR_ENTRY_LIST peers; /* neighbor entries */ + QMGR_PEER *peer; /* parent linkage */ + QMGR_ENTRY_LIST queue_peers; /* per queue neighbor entries */ + QMGR_ENTRY_LIST peer_peers; /* per peer neighbor entries */ }; -extern QMGR_ENTRY *qmgr_entry_select(QMGR_QUEUE *); -extern void qmgr_entry_unselect(QMGR_QUEUE *, QMGR_ENTRY *); +extern QMGR_ENTRY *qmgr_entry_select(QMGR_PEER *); +extern void qmgr_entry_unselect(QMGR_ENTRY *); extern void qmgr_entry_done(QMGR_ENTRY *, int); -extern QMGR_ENTRY *qmgr_entry_create(QMGR_QUEUE *, QMGR_MESSAGE *); +extern QMGR_ENTRY *qmgr_entry_create(QMGR_PEER *, QMGR_MESSAGE *); /* * All common in-core information about a message is kept here. When all @@ -221,6 +259,8 @@ struct QMGR_MESSAGE { int refcount; /* queue entries */ int single_rcpt; /* send one rcpt at a time */ long arrival_time; /* time when queued */ + time_t queued_time; /* time when moved to the active + * queue */ long warn_offset; /* warning bounce flag offset */ time_t warn_time; /* time next warning to be sent */ long data_offset; /* data seek offset */ @@ -241,6 +281,11 @@ struct QMGR_MESSAGE { char *client_proto; /* client protocol */ char *client_helo; /* helo parameter */ QMGR_RCPT_LIST rcpt_list; /* complete addresses */ + int rcpt_count; /* used recipient slots */ + int rcpt_limit; /* maximum read in-core */ + int rcpt_unread; /* # of recipients left in queue file */ + QMGR_JOB_LIST job_list; /* jobs delivering this message (1 + * per transport) */ }; /* @@ -258,6 +303,63 @@ extern void qmgr_message_update_warn(QMGR_MESSAGE *); extern QMGR_MESSAGE *qmgr_message_alloc(const char *, const char *, int); extern QMGR_MESSAGE *qmgr_message_realloc(QMGR_MESSAGE *); + /* + * Sometimes it's required to access the transport queues and entries on per + * message basis. That's what the QMGR_JOB structure is for - it groups all + * per message information within each transport using a list of QMGR_PEER + * structures. These structures in turn correspond with per message + * QMGR_QUEUE structure and list all per message QMGR_ENTRY structures. + */ +struct QMGR_PEER_LIST { + QMGR_PEER *next; + QMGR_PEER *prev; +}; + +struct QMGR_JOB { + QMGR_MESSAGE *message; /* message delivered by this job */ + QMGR_TRANSPORT *transport; /* transport this job belongs to */ + QMGR_JOB_LIST message_peers; /* per message neighbor linkage */ + QMGR_JOB_LIST transport_peers; /* per transport neighbor linkage */ + QMGR_JOB_LIST time_peers; /* by time neighbor linkage */ + QMGR_JOB *stack_parent; /* stack parent */ + QMGR_JOB_LIST stack_children; /* all stack children */ + QMGR_JOB_LIST stack_siblings; /* stack children linkage */ + int stack_level; /* job stack nesting level (-1 means + * it's not on the lists at all) */ + int blocker_tag; /* tagged if blocks the job list */ + struct HTABLE *peer_byname; /* message job peers, indexed by + * domain */ + QMGR_PEER_LIST peer_list; /* list of message job peers */ + int slots_used; /* slots used during preemption */ + int slots_available; /* slots available for preemption (in + * multiples of slot_cost) */ + int selected_entries; /* # of entries selected for delivery + * so far */ + int read_entries; /* # of entries read in-core so far */ + int rcpt_count; /* used recipient slots */ + int rcpt_limit; /* available recipient slots */ +}; + +struct QMGR_PEER { + QMGR_JOB *job; /* job handling this peer */ + QMGR_QUEUE *queue; /* queue corresponding with this peer */ + int refcount; /* peer entries */ + QMGR_ENTRY_LIST entry_list; /* todo message entries queued for + * this peer */ + QMGR_PEER_LIST peers; /* neighbor linkage */ +}; + +extern QMGR_ENTRY *qmgr_job_entry_select(QMGR_TRANSPORT *); +extern QMGR_PEER *qmgr_peer_select(QMGR_JOB *); + +extern QMGR_JOB *qmgr_job_obtain(QMGR_MESSAGE *, QMGR_TRANSPORT *); +extern void qmgr_job_free(QMGR_JOB *); +extern void qmgr_job_move_limits(QMGR_JOB *); + +extern QMGR_PEER *qmgr_peer_create(QMGR_JOB *, QMGR_QUEUE *); +extern QMGR_PEER *qmgr_peer_find(QMGR_JOB *, QMGR_QUEUE *); +extern void qmgr_peer_free(QMGR_PEER *); + /* * qmgr_defer.c */ @@ -329,4 +431,9 @@ extern char *qmgr_scan_next(QMGR_SCAN *); /* IBM T.J. Watson Research /* P.O. Box 704 /* Yorktown Heights, NY 10598, USA +/* +/* Scheduler enhancements: +/* Patrik Rak +/* Modra 6 +/* 155 00, Prague, Czech Republic /*--*/ diff --git a/postfix/src/qmgr/qmgr_active.c b/postfix/src/qmgr/qmgr_active.c index bd2c9098f..3999b4a5f 100644 --- a/postfix/src/qmgr/qmgr_active.c +++ b/postfix/src/qmgr/qmgr_active.c @@ -364,11 +364,11 @@ static void qmgr_active_done_2_generic(QMGR_MESSAGE *message) * is full. */ if (message->tflags & (DEL_REQ_FLAG_EXPAND | DEL_REQ_FLAG_RECORD)) - message->flags |= trace_flush(message->tflags, - message->queue_name, - message->queue_id, - message->encoding, - message->sender); + message->flags |= trace_flush(message->tflags, + message->queue_name, + message->queue_id, + message->encoding, + message->sender); /* * If we get to this point we have tried all recipients for this message. diff --git a/postfix/src/qmgr/qmgr_bounce.c b/postfix/src/qmgr/qmgr_bounce.c index 284342d6d..92196bdaa 100644 --- a/postfix/src/qmgr/qmgr_bounce.c +++ b/postfix/src/qmgr/qmgr_bounce.c @@ -35,6 +35,11 @@ /* IBM T.J. Watson Research /* P.O. Box 704 /* Yorktown Heights, NY 10598, USA +/* +/* Scheduler enhancements: +/* Patrik Rak +/* Modra 6 +/* 155 00, Prague, Czech Republic /*--*/ /* System library. */ diff --git a/postfix/src/qmgr/qmgr_defer.c b/postfix/src/qmgr/qmgr_defer.c index 405a7f476..0a70df54c 100644 --- a/postfix/src/qmgr/qmgr_defer.c +++ b/postfix/src/qmgr/qmgr_defer.c @@ -60,6 +60,11 @@ /* IBM T.J. Watson Research /* P.O. Box 704 /* Yorktown Heights, NY 10598, USA +/* +/* Scheduler enhancements: +/* Patrik Rak +/* Modra 6 +/* 155 00, Prague, Czech Republic /*--*/ /* System library. */ @@ -127,7 +132,7 @@ void qmgr_defer_todo(QMGR_QUEUE *queue, const char *reason) * Proceed carefully. Queue entries will disappear as a side effect. */ for (entry = queue->todo.next; entry != 0; entry = next) { - next = entry->peers.next; + next = entry->queue_peers.next; message = entry->message; for (nrcpt = 0; nrcpt < entry->rcpt_list.len; nrcpt++) { recipient = entry->rcpt_list.info + nrcpt; diff --git a/postfix/src/qmgr/qmgr_deliver.c b/postfix/src/qmgr/qmgr_deliver.c index d41064ad5..cfdf635ff 100644 --- a/postfix/src/qmgr/qmgr_deliver.c +++ b/postfix/src/qmgr/qmgr_deliver.c @@ -39,6 +39,11 @@ /* IBM T.J. Watson Research /* P.O. Box 704 /* Yorktown Heights, NY 10598, USA +/* +/* Scheduler enhancements: +/* Patrik Rak +/* Modra 6 +/* 155 00, Prague, Czech Republic /*--*/ /* System library. */ @@ -282,7 +287,6 @@ static void qmgr_deliver_update(int unused_event, char *context) void qmgr_deliver(QMGR_TRANSPORT *transport, VSTREAM *stream) { - QMGR_QUEUE *queue; QMGR_ENTRY *entry; /* @@ -306,8 +310,7 @@ void qmgr_deliver(QMGR_TRANSPORT *transport, VSTREAM *stream) * agent request reading routine is prepared for the queue manager to * change its mind for no apparent reason. */ - if ((queue = qmgr_queue_select(transport)) == 0 - || (entry = qmgr_entry_select(queue)) == 0) { + if ((entry = qmgr_job_entry_select(transport)) == 0) { (void) vstream_fclose(stream); return; } @@ -319,10 +322,10 @@ void qmgr_deliver(QMGR_TRANSPORT *transport, VSTREAM *stream) * while some other queue manipulation is happening. */ if (qmgr_deliver_send_request(entry, stream) < 0) { - qmgr_entry_unselect(queue, entry); + qmgr_entry_unselect(entry); qmgr_transport_throttle(transport, "mail transport unavailable"); qmgr_defer_transport(transport, transport->reason); - /* warning: entry and queue may be dangling pointers here */ + /* warning: entry may be a dangling pointer here */ (void) vstream_fclose(stream); return; } diff --git a/postfix/src/qmgr/qmgr_entry.c b/postfix/src/qmgr/qmgr_entry.c index 3d3f9f461..c26d29325 100644 --- a/postfix/src/qmgr/qmgr_entry.c +++ b/postfix/src/qmgr/qmgr_entry.c @@ -6,8 +6,8 @@ /* SYNOPSIS /* #include "qmgr.h" /* -/* QMGR_ENTRY *qmgr_entry_create(queue, message) -/* QMGR_QUEUE *queue; +/* QMGR_ENTRY *qmgr_entry_create(peer, message) +/* QMGR_PEER *peer; /* QMGR_MESSAGE *message; /* /* void qmgr_entry_done(entry, which) @@ -24,8 +24,8 @@ /* These routines add/delete/manipulate per-site message /* delivery requests. /* -/* qmgr_entry_create() creates an entry for the named queue and -/* message, and appends the entry to the queue's todo list. +/* qmgr_entry_create() creates an entry for the named peer and message, +/* and appends the entry to the peer's list and its queue's todo list. /* Filling in and cleaning up the recipients is the responsibility /* of the caller. /* @@ -36,6 +36,9 @@ /* of the site's `todo' list (i.e. queue entries awaiting selection /* for actual delivery). /* +/* qmgr_entry_done() discards its peer structure when the peer +/* is not referenced anymore. +/* /* qmgr_entry_done() triggers cleanup of the per-site queue when /* the site has no pending deliveries, and the site is either /* alive, or the site is dead and the number of in-core queues @@ -47,14 +50,14 @@ /* the queue file to the deferred queue; send bounce reports to the /* message originator (see qmgr_active_done()). /* -/* qmgr_entry_select() selects the next entry from the named +/* qmgr_entry_select() selects first entry from the named /* per-site queue's `todo' list for actual delivery. The entry is /* moved to the queue's `busy' list: the list of messages being -/* delivered. +/* delivered. The entry is also removed from its peer list. /* /* qmgr_entry_unselect() takes the named entry off the named /* per-site queue's `busy' list and moves it to the queue's -/* `todo' list. +/* `todo' list. The entry is also appended to its peer list again. /* DIAGNOSTICS /* Panic: interface violations, internal inconsistencies. /* LICENSE @@ -66,6 +69,11 @@ /* IBM T.J. Watson Research /* P.O. Box 704 /* Yorktown Heights, NY 10598, USA +/* +/* Scheduler enhancements: +/* Patrik Rak +/* Modra 6 +/* 155 00, Prague, Czech Republic /*--*/ /* System library. */ @@ -91,27 +99,36 @@ /* qmgr_entry_select - select queue entry for delivery */ -QMGR_ENTRY *qmgr_entry_select(QMGR_QUEUE *queue) +QMGR_ENTRY *qmgr_entry_select(QMGR_PEER *peer) { QMGR_ENTRY *entry; + QMGR_QUEUE *queue; - if ((entry = queue->todo.prev) != 0) { - QMGR_LIST_UNLINK(queue->todo, QMGR_ENTRY *, entry); + if ((entry = peer->entry_list.next) != 0) { + queue = entry->queue; + QMGR_LIST_UNLINK(queue->todo, QMGR_ENTRY *, entry, queue_peers); queue->todo_refcount--; - QMGR_LIST_APPEND(queue->busy, entry); + QMGR_LIST_APPEND(queue->busy, entry, queue_peers); queue->busy_refcount++; + QMGR_LIST_UNLINK(peer->entry_list, QMGR_ENTRY *, entry, peer_peers); + peer->job->selected_entries++; } return (entry); } /* qmgr_entry_unselect - unselect queue entry for delivery */ -void qmgr_entry_unselect(QMGR_QUEUE *queue, QMGR_ENTRY *entry) +void qmgr_entry_unselect(QMGR_ENTRY *entry) { - QMGR_LIST_UNLINK(queue->busy, QMGR_ENTRY *, entry); + QMGR_PEER *peer = entry->peer; + QMGR_QUEUE *queue = entry->queue; + + QMGR_LIST_UNLINK(queue->busy, QMGR_ENTRY *, entry, queue_peers); queue->busy_refcount--; - QMGR_LIST_APPEND(queue->todo, entry); + QMGR_LIST_APPEND(queue->todo, entry, queue_peers); queue->todo_refcount++; + QMGR_LIST_APPEND(peer->entry_list, entry, peer_peers); + peer->job->selected_entries--; } /* qmgr_entry_done - dispose of queue entry */ @@ -120,6 +137,10 @@ void qmgr_entry_done(QMGR_ENTRY *entry, int which) { QMGR_QUEUE *queue = entry->queue; QMGR_MESSAGE *message = entry->message; + QMGR_PEER *peer = entry->peer; + QMGR_JOB *sponsor, + *job = peer->job; + QMGR_TRANSPORT *transport = job->transport; /* * Take this entry off the in-core queue. @@ -127,24 +148,76 @@ void qmgr_entry_done(QMGR_ENTRY *entry, int which) if (entry->stream != 0) msg_panic("qmgr_entry_done: file is open"); if (which == QMGR_QUEUE_BUSY) { - QMGR_LIST_UNLINK(queue->busy, QMGR_ENTRY *, entry); + QMGR_LIST_UNLINK(queue->busy, QMGR_ENTRY *, entry, queue_peers); queue->busy_refcount--; } else if (which == QMGR_QUEUE_TODO) { - QMGR_LIST_UNLINK(queue->todo, QMGR_ENTRY *, entry); + QMGR_LIST_UNLINK(peer->entry_list, QMGR_ENTRY *, entry, peer_peers); + job->selected_entries++; + QMGR_LIST_UNLINK(queue->todo, QMGR_ENTRY *, entry, queue_peers); queue->todo_refcount--; } else { msg_panic("qmgr_entry_done: bad queue spec: %d", which); } /* - * Free the recipient list and decrease the in-core recipient count - * accordingly. + * Decrease the in-core recipient counts and free the recipient list and + * the structure itself. */ + job->rcpt_count -= entry->rcpt_list.len; + message->rcpt_count -= entry->rcpt_list.len; qmgr_recipient_count -= entry->rcpt_list.len; qmgr_rcpt_list_free(&entry->rcpt_list); - myfree((char *) entry); + /* + * Make sure that the transport of any retired or finishing job that + * donated recipient slots to this message gets them back first. Then, if + * possible, pass the remaining unused recipient slots to the next job on + * the job list. + */ + for (sponsor = message->job_list.next; sponsor; sponsor = sponsor->message_peers.next) { + if (sponsor->rcpt_count >= sponsor->rcpt_limit || sponsor == job) + continue; + if (sponsor->stack_level < 0 || message->rcpt_offset == 0) + qmgr_job_move_limits(sponsor); + } + if (message->rcpt_offset == 0) { + qmgr_job_move_limits(job); + } + + /* + * 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 || queue->window == 0) + queue->blocker_tag = 0; + } + + /* + * When there are no more entries for this peer, discard the peer + * structure. + */ + peer->refcount--; + if (peer->refcount == 0) + qmgr_peer_free(peer); + /* * When the in-core queue for this site is empty and when this site is * not dead, discard the in-core queue. When this site is dead, but the @@ -161,33 +234,18 @@ void qmgr_entry_done(QMGR_ENTRY *entry, int which) /* * Update the in-core message reference count. When the in-core message * structure has no more references, dispose of the message. - * - * When the in-core recipient count falls below a threshold, and this - * message has more recipients, read more recipients now. If we read more - * recipients as soon as the recipient count falls below the in-core - * recipient limit, we do not give other messages a chance until this - * message is delivered. That's good for mailing list deliveries, bad for - * one-to-one mail. If we wait until the in-core recipient count drops - * well below the in-core recipient limit, we give other mail a chance, - * but we also allow list deliveries to become interleaved. In the worst - * case, people near the start of a mailing list get a burst of postings - * today, while people near the end of the list get that same burst of - * postings a whole day later. */ -#define FUDGE(x) ((x) * (var_qmgr_fudge / 100.0)) message->refcount--; - if (message->rcpt_offset > 0 - && qmgr_recipient_count < FUDGE(var_qmgr_rcpt_limit)) - qmgr_message_realloc(message); if (message->refcount == 0) qmgr_active_done(message); } /* qmgr_entry_create - create queue todo entry */ -QMGR_ENTRY *qmgr_entry_create(QMGR_QUEUE *queue, QMGR_MESSAGE *message) +QMGR_ENTRY *qmgr_entry_create(QMGR_PEER *peer, QMGR_MESSAGE *message) { QMGR_ENTRY *entry; + QMGR_QUEUE *queue = peer->queue; /* * Sanity check. @@ -203,8 +261,11 @@ QMGR_ENTRY *qmgr_entry_create(QMGR_QUEUE *queue, QMGR_MESSAGE *message) entry->message = message; qmgr_rcpt_list_init(&entry->rcpt_list); message->refcount++; + entry->peer = peer; + QMGR_LIST_APPEND(peer->entry_list, entry, peer_peers); + peer->refcount++; entry->queue = queue; - QMGR_LIST_APPEND(queue->todo, entry); + QMGR_LIST_APPEND(queue->todo, entry, queue_peers); queue->todo_refcount++; /* diff --git a/postfix/src/nqmgr/qmgr_job.c b/postfix/src/qmgr/qmgr_job.c similarity index 100% rename from postfix/src/nqmgr/qmgr_job.c rename to postfix/src/qmgr/qmgr_job.c diff --git a/postfix/src/qmgr/qmgr_message.c b/postfix/src/qmgr/qmgr_message.c index 2eb2925f7..b8bf2afa0 100644 --- a/postfix/src/qmgr/qmgr_message.c +++ b/postfix/src/qmgr/qmgr_message.c @@ -49,7 +49,10 @@ /* qmgr_message_realloc() resumes reading recipients from the queue /* file, and updates the recipient list and \fIrcpt_offset\fR message /* structure members. A null result means that the file could not be -/* read or that the file contained incorrect information. +/* read or that the file contained incorrect information. Recipient +/* limit imposed this time is based on the position of the message +/* job(s) on corresponding transport job list(s). It's considered +/* an error to call this when the recipient slots can't be allocated. /* /* qmgr_message_free() destroys an in-core message structure and makes /* the resources available for reuse. It is an error to destroy @@ -70,6 +73,11 @@ /* IBM T.J. Watson Research /* P.O. Box 704 /* Yorktown Heights, NY 10598, USA +/* +/* Scheduler enhancements: +/* Patrik Rak +/* Modra 6 +/* 155 00, Prague, Czech Republic /*--*/ /* System library. */ @@ -98,6 +106,7 @@ #include #include #include +#include /* Global library. */ @@ -143,6 +152,7 @@ static QMGR_MESSAGE *qmgr_message_create(const char *queue_name, message->refcount = 0; message->single_rcpt = 0; message->arrival_time = 0; + message->queued_time = sane_time(); message->data_offset = 0; message->queue_id = mystrdup(queue_id); message->queue_name = mystrdup(queue_name); @@ -163,6 +173,10 @@ static QMGR_MESSAGE *qmgr_message_create(const char *queue_name, message->client_proto = 0; message->client_helo = 0; qmgr_rcpt_list_init(&message->rcpt_list); + message->rcpt_count = 0; + message->rcpt_limit = var_qmgr_msg_rcpt_limit; + message->rcpt_unread = 0; + QMGR_LIST_INIT(message->job_list); return (message); } @@ -230,6 +244,7 @@ static void qmgr_message_oldstyle_scan(QMGR_MESSAGE *message) * so we set data_size to this as well and ignore the size record itself * completely. */ + message->rcpt_unread = 0; for (;;) { rec_type = rec_get(message->fp, buf, 0); if (rec_type <= 0) @@ -240,6 +255,10 @@ static void qmgr_message_oldstyle_scan(QMGR_MESSAGE *message) msg_info("old-style scan record %c %s", rec_type, start); if (rec_type == REC_TYPE_END) break; + if (rec_type == REC_TYPE_DONE || rec_type == REC_TYPE_RCPT) { + message->rcpt_unread++; + continue; + } if (rec_type == REC_TYPE_MESG) { if (message->data_offset == 0) { if ((message->data_offset = vstream_ftell(message->fp)) < 0) @@ -278,8 +297,9 @@ static int qmgr_message_read(QMGR_MESSAGE *message) int rec_type; long curr_offset; long save_offset = message->rcpt_offset; /* save a flag */ + int save_unread = message->rcpt_unread; /* save a count */ char *start; - int nrcpt = 0; + int recipient_limit; const char *error_text; char *name; char *value; @@ -294,6 +314,15 @@ static int qmgr_message_read(QMGR_MESSAGE *message) /* * If we re-open this file, skip over on-file recipient records that we * already looked at, and refill the in-core recipient address list. + * + * For the first time, the message recipient limit is calculated from the + * global recipient limit. This is to avoid reading little recipients + * when the active queue is near empty. When the queue becomes full, only + * the necessary amount is read in core. Such priming is necessary + * because there are no message jobs yet. + * + * For the next time, the recipient limit is based solely on the message + * jobs' positions in the job lists and/or job stacks. */ if (message->rcpt_offset) { if (message->rcpt_list.len) @@ -302,26 +331,29 @@ static int qmgr_message_read(QMGR_MESSAGE *message) if (vstream_fseek(message->fp, message->rcpt_offset, SEEK_SET) < 0) msg_fatal("seek file %s: %m", VSTREAM_PATH(message->fp)); message->rcpt_offset = 0; + recipient_limit = message->rcpt_limit - message->rcpt_count; + } else { + recipient_limit = var_qmgr_rcpt_limit - qmgr_recipient_count; + if (recipient_limit < message->rcpt_limit) + recipient_limit = message->rcpt_limit; } + if (recipient_limit <= 0) + msg_panic("%s: no recipient slots available", message->queue_id); /* * Read envelope records. XXX Rely on the front-end programs to enforce - * record size limits. Read up to var_qmgr_rcpt_limit recipients from the + * record size limits. Read up to recipient_limit recipients from the * queue file, to protect against memory exhaustion. Recipient records * may appear before or after the message content, so we keep reading * from the queue file until we have enough recipients (rcpt_offset != 0) * and until we know all the non-recipient information. * - * When reading recipients from queue file, stop reading when we reach a - * per-message in-core recipient limit rather than a global in-core - * recipient limit. Use the global recipient limit only in order to stop - * opening queue files. The purpose is to achieve equal delay for - * messages with recipient counts up to var_qmgr_rcpt_limit recipients. - * - * If we would read recipients up to a global recipient limit, the average - * number of in-core recipients per message would asymptotically approach - * (global recipient limit)/(active queue size limit), which gives equal - * delay per recipient rather than equal delay per message. + * Note that the total recipient count record is accurate only for fresh + * queue files. After some of the recipients are marked as done and the + * queue file is deferred, it can be used as upper bound estimate only. + * Fortunately, this poses no major problem on the scheduling algorithm, + * as the only impact is that the already deferred messages are not + * chosen by qmgr_job_candidate() as often as they could. * * On the first open, we must examine all non-recipient records. * @@ -358,15 +390,15 @@ static int qmgr_message_read(QMGR_MESSAGE *message) */ if (rec_type == REC_TYPE_RCPT) { /* See also below for code setting orig_rcpt. */ -#define FUDGE(x) ((x) * (var_qmgr_fudge / 100.0)) if (message->rcpt_offset == 0) { + message->rcpt_unread--; qmgr_rcpt_list_add(&message->rcpt_list, curr_offset, orig_rcpt ? orig_rcpt : "", start); if (orig_rcpt) { myfree(orig_rcpt); orig_rcpt = 0; } - if (message->rcpt_list.len >= FUDGE(var_qmgr_rcpt_limit)) { + if (message->rcpt_list.len >= recipient_limit) { if ((message->rcpt_offset = vstream_ftell(message->fp)) < 0) msg_fatal("vstream_ftell %s: %m", VSTREAM_PATH(message->fp)); @@ -392,9 +424,12 @@ static int qmgr_message_read(QMGR_MESSAGE *message) continue; } if (rec_type == REC_TYPE_DONE) { - if (orig_rcpt) { - myfree(orig_rcpt); - orig_rcpt = 0; + if (message->rcpt_offset == 0) { + message->rcpt_unread--; + if (orig_rcpt) { + myfree(orig_rcpt); + orig_rcpt = 0; + } } continue; } @@ -422,7 +457,7 @@ static int qmgr_message_read(QMGR_MESSAGE *message) if (message->data_offset == 0) { if ((count = sscanf(start, "%ld %ld %d %d", &message->data_size, &message->data_offset, - &nrcpt, &message->rflags)) >= 3) { + &message->rcpt_unread, &message->rflags)) >= 3) { /* Postfix >= 1.0 (a.k.a. 20010228). */ if (message->data_offset <= 0 || message->data_size <= 0) { msg_warn("%s: invalid size record: %.100s", @@ -476,7 +511,7 @@ static int qmgr_message_read(QMGR_MESSAGE *message) if (message->sender == 0) { message->sender = mystrdup(start); opened(message->queue_id, message->sender, - message->data_size, nrcpt, + message->data_size, message->rcpt_unread, "queue %s", message->queue_name); } continue; @@ -594,6 +629,12 @@ static int qmgr_message_read(QMGR_MESSAGE *message) * Sanity checks. Verify that all required information was found, * including the queue file end marker. */ + if (message->rcpt_unread < 0 + || (message->rcpt_offset == 0 && message->rcpt_unread != 0)) { + msg_warn("%s: rcpt count mismatch (%d)", + message->queue_id, message->rcpt_unread); + message->rcpt_unread = 0; + } if (rec_type <= 0) { /* Already logged warning. */ } else if (message->arrival_time == 0) { @@ -609,6 +650,9 @@ static int qmgr_message_read(QMGR_MESSAGE *message) return (0); } message->rcpt_offset = save_offset; /* restore flag */ + message->rcpt_unread = save_unread; /* restore count */ + qmgr_rcpt_list_free(&message->rcpt_list); + qmgr_rcpt_list_init(&message->rcpt_list); return (-1); } @@ -685,7 +729,7 @@ static int qmgr_message_sort_compare(const void *p1, const void *p2) /* * Compare recipient address. */ - return (strcasecmp(rcpt1->address, rcpt2->address)); + return (strcmp(rcpt1->address, rcpt2->address)); } /* qmgr_message_sort - sort message recipient addresses by domain */ @@ -970,6 +1014,8 @@ static void qmgr_message_assign(QMGR_MESSAGE *message) QMGR_RCPT *recipient; QMGR_ENTRY *entry = 0; QMGR_QUEUE *queue; + QMGR_JOB *job = 0; + QMGR_PEER *peer = 0; /* * Try to bundle as many recipients in a delivery request as we can. When @@ -985,27 +1031,87 @@ static void qmgr_message_assign(QMGR_MESSAGE *message) for (recipient = list.info; recipient < list.info + list.len; recipient++) { if ((queue = recipient->queue) != 0) { if (message->single_rcpt || entry == 0 || entry->queue != queue - || !LIMIT_OK(entry->queue->transport->recipient_limit, + || !LIMIT_OK(queue->transport->recipient_limit, entry->rcpt_list.len)) { - entry = qmgr_entry_create(queue, message); + + /* + * Lookup or instantiate the message job if necessary. + */ + if (job == 0 || queue->transport != job->transport) { + job = qmgr_job_obtain(message, queue->transport); + peer = 0; + } + + /* + * Lookup or instantiate job peer if necessary. + */ + if (peer == 0 || queue != peer->queue) { + if ((peer = qmgr_peer_find(job, queue)) == 0) + peer = qmgr_peer_create(job, queue); + } + + /* + * Create new peer entry. + */ + entry = qmgr_entry_create(peer, message); + job->read_entries++; } + + /* + * Add the recipient to the current entry and increase all those + * recipient counters accordingly. + */ qmgr_rcpt_list_add(&entry->rcpt_list, recipient->offset, recipient->orig_rcpt, recipient->address); + job->rcpt_count++; + message->rcpt_count++; qmgr_recipient_count++; } } + + /* + * Release the message recipient list and reinitialize it for the next + * time. + */ qmgr_rcpt_list_free(&message->rcpt_list); qmgr_rcpt_list_init(&message->rcpt_list); + + /* + * Note that even if qmgr_job_obtain() reset the job candidate cache of + * all transports to which we assigned new recipients, this message may + * have other jobs which we didn't touch at all this time. But the number + * of unread recipients affecting the candidate selection might have + * changed considerably, so we must invalidate the caches if it might be + * of some use. + */ + for (job = message->job_list.next; job; job = job->message_peers.next) + if (job->selected_entries < job->read_entries + && job->blocker_tag != job->transport->blocker_tag) + job->transport->candidate_cache_current = 0; +} + +/* qmgr_message_move_limits - recycle unused recipient slots */ + +static void qmgr_message_move_limits(QMGR_MESSAGE *message) +{ + QMGR_JOB *job; + + for (job = message->job_list.next; job; job = job->message_peers.next) + qmgr_job_move_limits(job); } /* qmgr_message_free - release memory for in-core message structure */ void qmgr_message_free(QMGR_MESSAGE *message) { + QMGR_JOB *job; + if (message->refcount != 0) msg_panic("qmgr_message_free: reference len: %d", message->refcount); if (message->fp) msg_panic("qmgr_message_free: queue file is open"); + while ((job = message->job_list.next) != 0) + qmgr_job_free(job); myfree(message->queue_id); myfree(message->queue_name); if (message->encoding) @@ -1090,6 +1196,8 @@ QMGR_MESSAGE *qmgr_message_alloc(const char *queue_name, const char *queue_id, qmgr_message_sort(message); qmgr_message_assign(message); qmgr_message_close(message); + if (message->rcpt_offset == 0) + qmgr_message_move_limits(message); return (message); } } @@ -1124,6 +1232,8 @@ QMGR_MESSAGE *qmgr_message_realloc(QMGR_MESSAGE *message) qmgr_message_sort(message); qmgr_message_assign(message); qmgr_message_close(message); + if (message->rcpt_offset == 0) + qmgr_message_move_limits(message); return (message); } } diff --git a/postfix/src/nqmgr/qmgr_peer.c b/postfix/src/qmgr/qmgr_peer.c similarity index 100% rename from postfix/src/nqmgr/qmgr_peer.c rename to postfix/src/qmgr/qmgr_peer.c diff --git a/postfix/src/qmgr/qmgr_queue.c b/postfix/src/qmgr/qmgr_queue.c index 3c88b7eda..b86034a57 100644 --- a/postfix/src/qmgr/qmgr_queue.c +++ b/postfix/src/qmgr/qmgr_queue.c @@ -20,9 +20,6 @@ /* QMGR_TRANSPORT *transport; /* const char *name; /* -/* QMGR_QUEUE *qmgr_queue_select(transport) -/* QMGR_TRANSPORT *transport; -/* /* void qmgr_queue_throttle(queue, reason) /* QMGR_QUEUE *queue; /* const char *reason; @@ -52,10 +49,6 @@ /* qmgr_queue_find() looks up the named queue for the named /* transport. A null result means that the queue was not found. /* -/* qmgr_queue_select() uses a round-robin strategy to select -/* from the named transport one per-destination queue with a -/* non-empty `todo' list. -/* /* qmgr_queue_throttle() handles a delivery error, and decrements the /* concurrency limit for the destination. When the concurrency limit /* for a destination becomes zero, qmgr_queue_throttle() starts a timer @@ -67,7 +60,7 @@ /* limit specified for the transport. This routine implements /* "slow open" mode, and eliminates the "thundering herd" problem. /* DIAGNOSTICS -/* None +/* Panic: consistency check failure. /* LICENSE /* .ad /* .fi @@ -77,6 +70,11 @@ /* IBM T.J. Watson Research /* P.O. Box 704 /* Yorktown Heights, NY 10598, USA +/* +/* Scheduler enhancements: +/* Patrik Rak +/* Modra 6 +/* 155 00, Prague, Czech Republic /*--*/ /* System library. */ @@ -148,7 +146,7 @@ void qmgr_queue_unthrottle(QMGR_QUEUE *queue) */ if (transport->dest_concurrency_limit == 0 || transport->dest_concurrency_limit > queue->window) - if (queue->window <= queue->busy_refcount + transport->init_dest_concurrency) + if (queue->window < queue->busy_refcount + transport->init_dest_concurrency) queue->window++; } @@ -186,27 +184,6 @@ void qmgr_queue_throttle(QMGR_QUEUE *queue, const char *reason) } } -/* qmgr_queue_select - select in-core queue for delivery */ - -QMGR_QUEUE *qmgr_queue_select(QMGR_TRANSPORT *transport) -{ - QMGR_QUEUE *queue; - - /* - * If we find a suitable site, rotate the list to enforce round-robin - * selection. See similar selection code in qmgr_transport_select(). - */ - for (queue = transport->queue_list.next; queue; queue = queue->peers.next) { - if (queue->window > queue->busy_refcount && queue->todo.next != 0) { - QMGR_LIST_ROTATE(transport->queue_list, queue); - if (msg_verbose) - msg_info("qmgr_queue_select: %s", queue->name); - return (queue); - } - } - return (0); -} - /* qmgr_queue_done - delete in-core queue for site */ void qmgr_queue_done(QMGR_QUEUE *queue) @@ -232,7 +209,7 @@ void qmgr_queue_done(QMGR_QUEUE *queue) /* * Clean up this in-core queue. */ - QMGR_LIST_UNLINK(transport->queue_list, QMGR_QUEUE *, queue); + QMGR_LIST_UNLINK(transport->queue_list, QMGR_QUEUE *, queue, peers); htable_delete(transport->queue_byname, queue->name, (void (*) (char *)) 0); myfree(queue->name); myfree(queue->nexthop); @@ -264,7 +241,8 @@ QMGR_QUEUE *qmgr_queue_create(QMGR_TRANSPORT *transport, const char *name, QMGR_LIST_INIT(queue->busy); queue->reason = 0; queue->clog_time_to_warn = 0; - QMGR_LIST_PREPEND(transport->queue_list, queue); + queue->blocker_tag = 0; + QMGR_LIST_APPEND(transport->queue_list, queue, peers); htable_enter(transport->queue_byname, name, (char *) queue); return (queue); } diff --git a/postfix/src/qmgr/qmgr_transport.c b/postfix/src/qmgr/qmgr_transport.c index dcb4ad1fa..204880962 100644 --- a/postfix/src/qmgr/qmgr_transport.c +++ b/postfix/src/qmgr/qmgr_transport.c @@ -64,6 +64,11 @@ /* IBM T.J. Watson Research /* P.O. Box 704 /* Yorktown Heights, NY 10598, USA +/* +/* Scheduler enhancements: +/* Patrik Rak +/* Modra 6 +/* 155 00, Prague, Czech Republic /*--*/ /* System library. */ @@ -233,7 +238,7 @@ QMGR_TRANSPORT *qmgr_transport_select(void) /* * If we find a suitable transport, rotate the list of transports to * effectuate round-robin selection. See similar selection code in - * qmgr_queue_select(). + * qmgr_peer_select(). */ #define STAY_AWAY (QMGR_TRANSPORT_STAT_BUSY | QMGR_TRANSPORT_STAT_DEAD) @@ -242,7 +247,7 @@ QMGR_TRANSPORT *qmgr_transport_select(void) continue; for (queue = xport->queue_list.next; queue; queue = queue->peers.next) { if (queue->window > queue->busy_refcount && queue->todo.next != 0) { - QMGR_LIST_ROTATE(qmgr_transport_list, xport); + QMGR_LIST_ROTATE(qmgr_transport_list, xport, peers); if (msg_verbose) msg_info("qmgr_transport_select: %s", xport->name); return (xport); @@ -319,10 +324,10 @@ QMGR_TRANSPORT *qmgr_transport_create(const char *name) * Use global configuration settings or transport-specific settings. */ transport->dest_concurrency_limit = - get_mail_conf_int2(name, "_destination_concurrency_limit", + get_mail_conf_int2(name, _DEST_CON_LIMIT, var_dest_con_limit, 0, 0); transport->recipient_limit = - get_mail_conf_int2(name, "_destination_recipient_limit", + get_mail_conf_int2(name, _DEST_RCPT_LIMIT, var_dest_rcpt_limit, 0, 0); if (transport->dest_concurrency_limit == 0 @@ -331,13 +336,36 @@ QMGR_TRANSPORT *qmgr_transport_create(const char *name) else transport->init_dest_concurrency = transport->dest_concurrency_limit; + transport->slot_cost = get_mail_conf_int2(name, _DELIVERY_SLOT_COST, + var_delivery_slot_cost, 0, 0); + transport->slot_loan = get_mail_conf_int2(name, _DELIVERY_SLOT_LOAN, + var_delivery_slot_loan, 0, 0); + transport->slot_loan_factor = + 100 - get_mail_conf_int2(name, _DELIVERY_SLOT_DISCOUNT, + var_delivery_slot_discount, 0, 100); + transport->min_slots = get_mail_conf_int2(name, _MIN_DELIVERY_SLOTS, + var_min_delivery_slots, 0, 0); + transport->rcpt_unused = get_mail_conf_int2(name, _XPORT_RCPT_LIMIT, + var_xport_rcpt_limit, 0, 0); + transport->rcpt_per_stack = get_mail_conf_int2(name, _STACK_RCPT_LIMIT, + var_stack_rcpt_limit, 0, 0); + transport->queue_byname = htable_create(0); QMGR_LIST_INIT(transport->queue_list); + transport->job_byname = htable_create(0); + QMGR_LIST_INIT(transport->job_list); + QMGR_LIST_INIT(transport->job_bytime); + transport->job_current = 0; + transport->job_next_unread = 0; + transport->candidate_cache = 0; + transport->candidate_cache_current = 0; + transport->candidate_cache_time = (time_t) 0; + transport->blocker_tag = 1; transport->reason = 0; if (qmgr_transport_byname == 0) qmgr_transport_byname = htable_create(10); htable_enter(qmgr_transport_byname, name, (char *) transport); - QMGR_LIST_APPEND(qmgr_transport_list, transport); + QMGR_LIST_PREPEND(qmgr_transport_list, transport, peers); if (msg_verbose) msg_info("qmgr_transport_create: %s concurrency %d recipients %d", transport->name, transport->dest_concurrency_limit, diff --git a/postfix/src/smtpd/smtpd.c b/postfix/src/smtpd/smtpd.c index 63ea1c212..eefca9892 100644 --- a/postfix/src/smtpd/smtpd.c +++ b/postfix/src/smtpd/smtpd.c @@ -117,6 +117,11 @@ /* Enable per-session authentication as per RFC 2554 (SASL). /* This functionality is available only when explicitly selected /* at program build time and explicitly enabled at runtime. +/* .IP \fBsmtpd_sasl_application_name\fR +/* The application name used for SASL server initialization. This +/* controls the name of the SASL configuration file. The default +/* value is \fIsmtpd\fR, corresponding to a SASL configuration file +/* named \fIsmtpd.conf\fR. /* .IP \fBsmtpd_sasl_local_domain\fR /* The name of the local authentication realm. /* .IP \fBsmtpd_sasl_security_options\fR @@ -529,6 +534,7 @@ bool var_allow_untrust_route; int var_smtpd_junk_cmd_limit; bool var_smtpd_sasl_enable; char *var_smtpd_sasl_opts; +char *var_smtpd_sasl_appname; char *var_smtpd_sasl_realm; char *var_smtpd_sasl_exceptions_networks; char *var_filter_xport; @@ -2497,6 +2503,7 @@ int main(int argc, char **argv) VAR_ALIAS_MAPS, DEF_ALIAS_MAPS, &var_alias_maps, 0, 0, VAR_LOCAL_RCPT_MAPS, DEF_LOCAL_RCPT_MAPS, &var_local_rcpt_maps, 0, 0, VAR_SMTPD_SASL_OPTS, DEF_SMTPD_SASL_OPTS, &var_smtpd_sasl_opts, 0, 0, + VAR_SMTPD_SASL_APPNAME, DEF_SMTPD_SASL_APPNAME, &var_smtpd_sasl_appname, 1, 0, VAR_SMTPD_SASL_REALM, DEF_SMTPD_SASL_REALM, &var_smtpd_sasl_realm, 0, 0, VAR_SMTPD_SASL_EXCEPTIONS_NETWORKS, DEF_SMTPD_SASL_EXCEPTIONS_NETWORKS, &var_smtpd_sasl_exceptions_networks, 0, 0, VAR_FILTER_XPORT, DEF_FILTER_XPORT, &var_filter_xport, 0, 0, diff --git a/postfix/src/smtpd/smtpd_check.c b/postfix/src/smtpd/smtpd_check.c index 79255a53d..99ea236be 100644 --- a/postfix/src/smtpd/smtpd_check.c +++ b/postfix/src/smtpd/smtpd_check.c @@ -3577,8 +3577,8 @@ char *smtpd_check_mail(SMTPD_STATE *state, char *sender) state->defer_if_permit_sender = state->defer_if_permit.active; /* - * If the "check_recipient_maps" restriction was not applied, and if mail - * is not being rejected or discarded, validate the recipient here. + * If the "reject_unlisted_sender" restriction was not applied, and if + * mail is not being rejected or discarded, validate the sender here. */ if (status != SMTPD_CHECK_REJECT && state->sender_rcptmap_checked == 0 && state->discard == 0 && *sender) @@ -3676,8 +3676,8 @@ char *smtpd_check_rcpt(SMTPD_STATE *state, char *recipient) "%s", STR(state->defer_if_permit.reason)); /* - * If the "check_recipient_maps" restriction was not applied, and if mail - * is not being rejected or discarded, validate the recipient here. + * If the "reject_unlisted_recipient" restriction was not applied, and if + * mail is not being rejected or discarded, validate the recipient here. */ if (status != SMTPD_CHECK_REJECT && state->recipient_rcptmap_checked == 0 && state->discard == 0) @@ -3837,8 +3837,7 @@ static int check_rcpt_maps(SMTPD_STATE *state, const char *recipient, STR(reply->nexthop))); /* - * Reject mail to unknown addresses in local domains (domains that match - * $mydestination or $inet_interfaces). + * Search the recipient lookup tables of the respective address class. * * XXX Use the less expensive maps_find() (case is already folded) instead * of the baroque mail_addr_find(). But then we have to strip the domain @@ -3852,6 +3851,10 @@ static int check_rcpt_maps(SMTPD_STATE *state, const char *recipient, switch (reply->flags & RESOLVE_CLASS_MASK) { + /* + * Reject mail to unknown addresses in local domains (domains that + * match $mydestination or $inet_interfaces). + */ case RESOLVE_CLASS_LOCAL: if (*var_local_rcpt_maps /* Generated by bounce, absorbed by qmgr. */ @@ -3896,14 +3899,12 @@ static int check_rcpt_maps(SMTPD_STATE *state, const char *recipient, reply_class, var_show_unk_rcpt_table ? " in relay recipient table" : "")); break; - - /* - * Accept all other addresses - including addresses that passed the - * above tests because of some table lookup problem. - */ - default: - break; } + + /* + * Accept all other addresses - including addresses that passed the above + * tests because of some table lookup problem. + */ return (0); } @@ -3945,7 +3946,8 @@ char *smtpd_check_size(SMTPD_STATE *state, off_t size) || BLOCKS(var_message_limit) >= fsbuf.block_free / 2) { (void) smtpd_check_reject(state, MAIL_ERROR_RESOURCE, "452 Insufficient system storage"); - msg_warn("not enough free space in mail queue: %lu bytes", + msg_warn("not enough free space in mail queue: %lu bytes < " + "2*message size limit", (unsigned long) fsbuf.block_free * fsbuf.block_size); return (STR(error_text)); } diff --git a/postfix/src/smtpd/smtpd_sasl_glue.c b/postfix/src/smtpd/smtpd_sasl_glue.c index da5074655..990b706c5 100644 --- a/postfix/src/smtpd/smtpd_sasl_glue.c +++ b/postfix/src/smtpd/smtpd_sasl_glue.c @@ -211,7 +211,10 @@ void smtpd_sasl_initialize(void) /* * Initialize the library: load SASL plug-in routines, etc. */ - if (sasl_server_init(callbacks, "smtpd") != SASL_OK) + if (msg_verbose) + msg_info("smtpd_sasl_initialize: SASL config file is %s.conf", + var_smtpd_sasl_appname); + if (sasl_server_init(callbacks, var_smtpd_sasl_appname) != SASL_OK) msg_fatal("SASL per-process initialization failed"); } diff --git a/postfix/src/util/Makefile.in b/postfix/src/util/Makefile.in index 1c5f939a9..439433c25 100644 --- a/postfix/src/util/Makefile.in +++ b/postfix/src/util/Makefile.in @@ -3,8 +3,8 @@ SRCS = alldig.c argv.c argv_split.c attr_print0.c attr_print64.c \ attr_scan0.c attr_scan64.c base64_code.c basename.c binhash.c \ chroot_uid.c clean_env.c close_on_exec.c concatenate.c ctable.c \ dict.c dict_alloc.c dict_db.c dict_dbm.c dict_debug.c dict_env.c \ - dict_cidr.c dict_ht.c dict_ldap.c dict_mysql.c dict_ni.c dict_nis.c \ - dict_nisplus.c dict_open.c dict_pcre.c dict_pgsql.c dict_regexp.c \ + dict_cidr.c dict_ht.c dict_ni.c dict_nis.c \ + dict_nisplus.c dict_open.c dict_pcre.c dict_regexp.c \ dict_static.c dict_tcp.c dict_unix.c dir_forest.c doze.c \ duplex_pipe.c environ.c events.c exec_command.c fifo_listen.c \ fifo_trigger.c file_limit.c find_inet.c fsspace.c fullname.c \ @@ -33,8 +33,8 @@ OBJS = alldig.o argv.o argv_split.o attr_print0.o attr_print64.o \ attr_scan0.o attr_scan64.o base64_code.o basename.o binhash.o \ chroot_uid.o clean_env.o close_on_exec.o concatenate.o ctable.o \ dict.o dict_alloc.o dict_db.o dict_dbm.o dict_debug.o dict_env.o \ - dict_cidr.o dict_ht.o dict_ldap.o dict_mysql.o dict_ni.o dict_nis.o \ - dict_nisplus.o dict_open.o dict_pcre.o dict_pgsql.o dict_regexp.o \ + dict_cidr.o dict_ht.o dict_ni.o dict_nis.o \ + dict_nisplus.o dict_open.o dict_pcre.o dict_regexp.o \ dict_static.o dict_tcp.o dict_unix.o dir_forest.o doze.o \ duplex_pipe.o environ.o events.o exec_command.o fifo_listen.o \ fifo_trigger.o file_limit.o find_inet.o fsspace.o fullname.o \ @@ -61,8 +61,8 @@ OBJS = alldig.o argv.o argv_split.o attr_print0.o attr_print64.o \ uppercase.o HDRS = argv.h attr.h base64_code.h binhash.h chroot_uid.h clean_env.h \ connect.h ctable.h dict.h dict_db.h dict_dbm.h dict_env.h \ - dict_cidr.h dict_ht.h dict_ldap.h dict_mysql.h dict_ni.h dict_nis.h \ - dict_nisplus.h dict_pcre.h dict_pgsql.h dict_regexp.h \ + dict_cidr.h dict_ht.h dict_ni.h dict_nis.h \ + dict_nisplus.h dict_pcre.h dict_regexp.h \ dict_static.h dict_tcp.h dict_unix.h dir_forest.h events.h \ exec_command.h find_inet.h fsspace.h fullname.h get_domainname.h \ get_hostname.h hex_quote.h host_port.h htable.h inet_addr_host.h \ @@ -647,10 +647,6 @@ dict_ht.o: vstream.h dict_ht.o: vbuf.h dict_ht.o: argv.h dict_ht.o: dict_ht.h -dict_ldap.o: dict_ldap.c -dict_ldap.o: sys_defs.h -dict_mysql.o: dict_mysql.c -dict_mysql.o: sys_defs.h dict_ni.o: dict_ni.c dict_ni.o: sys_defs.h dict_nis.o: dict_nis.c @@ -689,9 +685,6 @@ dict_open.o: dict_db.h dict_open.o: dict_nis.h dict_open.o: dict_nisplus.h dict_open.o: dict_ni.h -dict_open.o: dict_ldap.h -dict_open.o: dict_mysql.h -dict_open.o: dict_pgsql.h dict_open.o: dict_pcre.h dict_open.o: dict_regexp.h dict_open.o: dict_static.h @@ -714,8 +707,6 @@ dict_pcre.o: dict.h dict_pcre.o: argv.h dict_pcre.o: dict_pcre.h dict_pcre.o: mac_parse.h -dict_pgsql.o: dict_pgsql.c -dict_pgsql.o: sys_defs.h dict_regexp.o: dict_regexp.c dict_regexp.o: sys_defs.h dict_regexp.o: mymalloc.h diff --git a/postfix/src/util/dict_open.c b/postfix/src/util/dict_open.c index 32fa0ae49..2cc1e34e9 100644 --- a/postfix/src/util/dict_open.c +++ b/postfix/src/util/dict_open.c @@ -172,9 +172,6 @@ #include #include #include -#include -#include -#include #include #include #include @@ -211,15 +208,6 @@ static DICT_OPEN_INFO dict_open_info[] = { #ifdef HAS_NETINFO DICT_TYPE_NETINFO, dict_ni_open, #endif -#ifdef HAS_LDAP - DICT_TYPE_LDAP, dict_ldap_open, -#endif -#ifdef HAS_MYSQL - DICT_TYPE_MYSQL, dict_mysql_open, -#endif -#ifdef HAS_PGSQL - DICT_TYPE_PGSQL, dict_pgsql_open, -#endif #ifdef HAS_PCRE DICT_TYPE_PCRE, dict_pcre_open, #endif diff --git a/postfix/src/util/msg_syslog.c b/postfix/src/util/msg_syslog.c index 5af7cb950..f5b2b87a2 100644 --- a/postfix/src/util/msg_syslog.c +++ b/postfix/src/util/msg_syslog.c @@ -174,7 +174,7 @@ void msg_syslog_init(const char *name, int logopt, int facility) * This scrubbing code is in the wrong place. */ if (unsafe()) - putenv("TZ="); + putenv("TZ=UTC"); tzset(); openlog(name, LOG_NDELAY | logopt, facility); if (first_call) { diff --git a/postfix/src/util/sys_defs.h b/postfix/src/util/sys_defs.h index 649c1dd55..a616e2ec0 100644 --- a/postfix/src/util/sys_defs.h +++ b/postfix/src/util/sys_defs.h @@ -257,6 +257,7 @@ extern int opterr; #define NATIVE_NEWALIAS_PATH "/usr/ucb/newaliases" #define NATIVE_COMMAND_DIR "/usr/etc" #define NATIVE_DAEMON_DIR "/usr/libexec/postfix" +#define STRCASECMP_IN_STRINGS_H #endif /* diff --git a/postfix/src/util/vstring_vstream.c b/postfix/src/util/vstring_vstream.c index 2f644bf1d..20ec1a131 100644 --- a/postfix/src/util/vstring_vstream.c +++ b/postfix/src/util/vstring_vstream.c @@ -95,7 +95,7 @@ int vstring_get(VSTRING *vp, VSTREAM *fp) break; } VSTRING_TERMINATE(vp); - return (VSTRING_GET_RESULT(vp)); + return (c == VSTREAM_EOF ? c : VSTRING_GET_RESULT(vp)); } /* vstring_get_nonl - read line from file, strip newline */ @@ -108,7 +108,7 @@ int vstring_get_nonl(VSTRING *vp, VSTREAM *fp) while ((c = VSTREAM_GETC(fp)) != VSTREAM_EOF && c != '\n') VSTRING_ADDCH(vp, c); VSTRING_TERMINATE(vp); - return (c == '\n' ? c : VSTRING_GET_RESULT(vp)); + return (c == '\n' || c == VSTREAM_EOF ? c : VSTRING_GET_RESULT(vp)); } /* vstring_get_null - read null-terminated string from file */ @@ -121,7 +121,7 @@ int vstring_get_null(VSTRING *vp, VSTREAM *fp) while ((c = VSTREAM_GETC(fp)) != VSTREAM_EOF && c != 0) VSTRING_ADDCH(vp, c); VSTRING_TERMINATE(vp); - return (c == 0 ? c : VSTRING_GET_RESULT(vp)); + return (c == 0 || c == VSTREAM_EOF ? c : VSTRING_GET_RESULT(vp)); } /* vstring_get_bound - read line from file, keep newline, up to bound */ @@ -140,7 +140,7 @@ int vstring_get_bound(VSTRING *vp, VSTREAM *fp, int bound) break; } VSTRING_TERMINATE(vp); - return (VSTRING_GET_RESULT(vp)); + return (c == VSTREAM_EOF ? c : VSTRING_GET_RESULT(vp)); } /* vstring_get_nonl_bound - read line from file, strip newline, up to bound */ @@ -156,7 +156,7 @@ int vstring_get_nonl_bound(VSTRING *vp, VSTREAM *fp, int bound) while (bound-- > 0 && (c = VSTREAM_GETC(fp)) != VSTREAM_EOF && c != '\n') VSTRING_ADDCH(vp, c); VSTRING_TERMINATE(vp); - return (c == '\n' ? c : VSTRING_GET_RESULT(vp)); + return (c == '\n' || c == VSTREAM_EOF ? c : VSTRING_GET_RESULT(vp)); } /* vstring_get_null_bound - read null-terminated string from file */ @@ -172,7 +172,7 @@ int vstring_get_null_bound(VSTRING *vp, VSTREAM *fp, int bound) while (bound-- > 0 && (c = VSTREAM_GETC(fp)) != VSTREAM_EOF && c != 0) VSTRING_ADDCH(vp, c); VSTRING_TERMINATE(vp); - return (c == 0 ? c : VSTRING_GET_RESULT(vp)); + return (c == 0 || c == VSTREAM_EOF ? c : VSTRING_GET_RESULT(vp)); } #ifdef TEST