mirror of
https://github.com/vdukhovni/postfix
synced 2025-08-30 13:48:06 +00:00
postfix-2.0.16-20040104
This commit is contained in:
committed by
Viktor Dukhovni
parent
8f5ee7e4ad
commit
8fb3e35efe
1
postfix/.indent.pro
vendored
1
postfix/.indent.pro
vendored
@@ -14,6 +14,7 @@
|
||||
-TBOUNCE_INFO
|
||||
-TBOUNCE_LOG
|
||||
-TBOUNCE_STAT
|
||||
-TCFG_PARSER
|
||||
-TCLEANUP_STATE
|
||||
-TCLIENT_LIST
|
||||
-TCLNT_STREAM
|
||||
|
@@ -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
|
||||
|
@@ -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.
|
||||
|
@@ -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
|
||||
|
||||
|
@@ -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
|
||||
====================
|
||||
|
||||
|
@@ -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/
|
||||
@@ -209,7 +210,7 @@ Postfix via localhost port 10026.
|
||||
|
||||
To enable content filtering in this manner, specify in main.cf:
|
||||
|
||||
/etc/postfix/main.cf:
|
||||
/etc/postfix/main.cf:
|
||||
content_filter = scan:localhost:10025
|
||||
receive_override_options = no_address_mappings
|
||||
|
||||
@@ -223,39 +224,43 @@ 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:
|
||||
/etc/postfix/master.cf:
|
||||
# =============================================================
|
||||
# service type private unpriv chroot wakeup maxproc command
|
||||
# (yes) (yes) (yes) (never) (100)
|
||||
# =============================================================
|
||||
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 <servername> greeted me with my own
|
||||
hostname") that result in mail being bounced.
|
||||
-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:
|
||||
/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
|
||||
|
||||
@@ -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 <servername> 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,7 +289,11 @@ 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:
|
||||
/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
|
||||
@@ -299,6 +302,7 @@ a dedicated listener on port localhost 10026:
|
||||
-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.
|
||||
|
@@ -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
|
||||
|
@@ -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
|
||||
|
@@ -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
|
||||
========================
|
||||
|
||||
|
@@ -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
|
||||
|
@@ -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";
|
||||
}
|
||||
}
|
||||
|
@@ -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
|
||||
============
|
||||
|
||||
|
@@ -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.
|
||||
|
372
postfix/README_FILES/VIRTUAL_MAILBOX_README
Normal file
372
postfix/README_FILES/VIRTUAL_MAILBOX_README
Normal file
@@ -0,0 +1,372 @@
|
||||
This code was created by Andrew McNamara <andrew@connect.com.au>
|
||||
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.
|
@@ -1,372 +1,17 @@
|
||||
This code was created by Andrew McNamara <andrew@connect.com.au>
|
||||
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.
|
||||
============================
|
||||
|
@@ -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
|
||||
|
@@ -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
|
||||
|
@@ -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
|
||||
|
@@ -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
|
||||
|
@@ -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
|
||||
}
|
||||
|
@@ -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
|
||||
|
@@ -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
|
||||
|
@@ -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.
|
||||
#
|
||||
|
@@ -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
|
||||
|
@@ -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
|
||||
|
@@ -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();
|
||||
|
||||
|
@@ -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
|
||||
|
@@ -1,14 +1,14 @@
|
||||
<html> <body> <pre>
|
||||
NQMGR(8) NQMGR(8)
|
||||
OQMGR(8) OQMGR(8)
|
||||
|
||||
<b>NAME</b>
|
||||
nqmgr - Postfix queue manager
|
||||
oqmgr - old Postfix queue manager
|
||||
|
||||
<b>SYNOPSIS</b>
|
||||
<b>nqmgr</b> [generic Postfix daemon options]
|
||||
<b>oqmgr</b> [generic Postfix daemon options]
|
||||
|
||||
<b>DESCRIPTION</b>
|
||||
The <b>nqmgr</b> daemon awaits the arrival of incoming mail and
|
||||
The <b>oqmgr</b> 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 <a href="trivial-rewrite.8.html"><b>triv-</b></a>
|
||||
<a href="trivial-rewrite.8.html"><b>ial-rewrite</b>(8)</a> daemon. This program expects to be run
|
||||
@@ -19,7 +19,7 @@ NQMGR(8) NQMGR(8)
|
||||
by undeliverable bounce notifications.
|
||||
|
||||
<b>MAIL QUEUES</b>
|
||||
The <b>nqmgr</b> daemon maintains the following queues:
|
||||
The <b>oqmgr</b> daemon maintains the following queues:
|
||||
|
||||
<b>incoming</b>
|
||||
Inbound mail from the network, or mail picked up by
|
||||
@@ -44,7 +44,7 @@ NQMGR(8) NQMGR(8)
|
||||
until someone sets them free.
|
||||
|
||||
<b>DELIVERY STATUS REPORTS</b>
|
||||
The <b>nqmgr</b> daemon keeps an eye on per-message delivery sta-
|
||||
The <b>oqmgr</b> 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
|
||||
<a href="defer.8.html"><b>defer</b>(8)</a> daemon.
|
||||
|
||||
The <b>nqmgr</b> daemon is responsible for asking the <a href="bounce.8.html"><b>bounce</b>(8)</a>
|
||||
The <b>oqmgr</b> daemon is responsible for asking the <a href="bounce.8.html"><b>bounce</b>(8)</a>
|
||||
or <a href="defer.8.html"><b>defer</b>(8)</a> daemons to send non-delivery reports.
|
||||
|
||||
<b>STRATEGIES</b>
|
||||
@@ -97,12 +97,6 @@ NQMGR(8) NQMGR(8)
|
||||
attempts by maintaining a short-term, in-memory
|
||||
list of unreachable destinations.
|
||||
|
||||
<b>preemptive message scheduling</b>
|
||||
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.
|
||||
|
||||
<b>TRIGGERS</b>
|
||||
On an idle system, the queue manager waits for the arrival
|
||||
of trigger events, or it waits for a timer to go off. A
|
||||
@@ -135,7 +129,7 @@ NQMGR(8) NQMGR(8)
|
||||
ever. The action is to start an incoming queue
|
||||
scan.
|
||||
|
||||
The <b>nqmgr</b> daemon reads an entire buffer worth of triggers.
|
||||
The <b>oqmgr</b> daemon reads an entire buffer worth of triggers.
|
||||
Multiple identical trigger requests are collapsed into
|
||||
one, and trigger requests are sorted so that <b>A</b> and <b>F</b> pre-
|
||||
cede <b>D</b> and <b>I</b>. Thus, in order to force a deferred queue
|
||||
@@ -143,14 +137,14 @@ NQMGR(8) NQMGR(8)
|
||||
manager of the arrival of new mail one would request <b>I</b>.
|
||||
|
||||
<b>STANDARDS</b>
|
||||
None. The <b>nqmgr</b> daemon does not interact with the outside
|
||||
None. The <b>oqmgr</b> daemon does not interact with the outside
|
||||
world.
|
||||
|
||||
<b>SECURITY</b>
|
||||
The <b>nqmgr</b> daemon is not security sensitive. It reads sin-
|
||||
The <b>oqmgr</b> 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
|
||||
<b>nqmgr</b> daemon does not talk to the outside world, and it
|
||||
<b>oqmgr</b> daemon does not talk to the outside world, and it
|
||||
can be run at fixed low privilege in a chrooted environ-
|
||||
ment.
|
||||
|
||||
@@ -184,9 +178,6 @@ NQMGR(8) NQMGR(8)
|
||||
Top-level directory of the Postfix queue.
|
||||
|
||||
<b>Active queue controls</b>
|
||||
In the text below, <i>transport</i> is the first field in a <b>mas-</b>
|
||||
<b>ter.cf</b> entry.
|
||||
|
||||
<b>qmgr_clog_warn_time</b>
|
||||
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.
|
||||
|
||||
<b>qmgr_message_recipient_minimum</b>
|
||||
Per message minimum of in-memory recipients.
|
||||
|
||||
<b>default_recipient_limit</b>
|
||||
Default limit on the number of in-memory recipients
|
||||
per transport.
|
||||
|
||||
<i>transport</i><b>_recipient_limit</b>
|
||||
Limit on the number of in-memory recipients, for
|
||||
the named message <i>transport</i>.
|
||||
|
||||
<b>default_extra_recipient_limit</b>
|
||||
Default limit on the total number of per transport
|
||||
in-memory recipients that the preempting messages
|
||||
can have.
|
||||
|
||||
<i>transport</i><b>_extra_recipient_limit</b>
|
||||
Limit on the number of in-memory recipients which
|
||||
all preempting messages delivered by the transport
|
||||
<i>transport</i> can have.
|
||||
|
||||
<b>Timing controls</b>
|
||||
<b>minimal_backoff_time</b>
|
||||
Minimal time in seconds between delivery attempts
|
||||
@@ -252,6 +222,25 @@ NQMGR(8) NQMGR(8)
|
||||
ken delivery transport.
|
||||
|
||||
<b>Concurrency controls</b>
|
||||
In the text below, <i>transport</i> is the first field in a <b>mas-</b>
|
||||
<b>ter.cf</b> entry.
|
||||
|
||||
<b>qmgr_fudge_factor</b> (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.
|
||||
|
||||
<b>initial_destination_concurrency</b>
|
||||
Initial per-destination concurrency level for par-
|
||||
allel delivery to the same destination.
|
||||
@@ -274,52 +263,6 @@ NQMGR(8) NQMGR(8)
|
||||
Limit on the number of recipients per message
|
||||
transfer, for the named message <i>transport</i>.
|
||||
|
||||
<b>Message scheduling</b>
|
||||
<i>transport</i><b>_delivery_slot_cost</b> (valid range: 0,2,3...)
|
||||
This parameter basically controls how often a mes-
|
||||
sage delivered by <i>transport</i> can be preempted by
|
||||
another message. An internal per-message/transport
|
||||
counter is incremented by one for each <i>trans-</i>
|
||||
<i>port</i><b>_delivery_slot_cost</b> deliveries handled by
|
||||
<i>transport</i>. 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 <i>transport</i> agents than the value of the
|
||||
"available delivery slots" counter.
|
||||
|
||||
Value equal to 0 disables the message preemption
|
||||
for <i>transport</i>.
|
||||
|
||||
<i>transport</i><b>_minimum_delivery_slots</b>
|
||||
Message preemption is not attempted at all whenever
|
||||
a message that can't ever accumulate at least
|
||||
<i>transport</i><b>_minimum_delivery_slots</b> available delivery
|
||||
slots is being delivered by <i>transport</i>.
|
||||
|
||||
<i>transport</i><b>_delivery_slot_discount</b> (valid range: 0..100)
|
||||
|
||||
<i>transport</i><b>_delivery_slot_loan</b>
|
||||
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 <i>trans-</i>
|
||||
<i>port</i><b>_delivery_slot_discount</b> percent of the required
|
||||
amount plus <i>transport</i><b>_delivery_slot_loan</b> still
|
||||
remains to be accumulated. Note that the full
|
||||
amount will still have to be accumulated before
|
||||
another preemption can take place later.
|
||||
|
||||
<b>default_delivery_slot_cost</b>
|
||||
|
||||
<b>default_minimum_delivery_slots</b>
|
||||
|
||||
<b>default_delivery_slot_discount</b>
|
||||
|
||||
<b>default_delivery_slot_loan</b>
|
||||
Default values for the transport specific parame-
|
||||
ters described above.
|
||||
|
||||
<b>SEE ALSO</b>
|
||||
<a href="master.8.html">master(8)</a>, process manager
|
||||
syslogd(8) system logging
|
||||
@@ -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)
|
||||
</pre> </body> </html>
|
@@ -25,10 +25,27 @@ PCRE_TABLE(5) PCRE_TABLE(5)
|
||||
The general form of a PCRE table is:
|
||||
|
||||
<b>/</b><i>pattern</i><b>/</b><i>flags result</i>
|
||||
When <i>pattern</i> matches the input string, use the cor-
|
||||
responding <i>result</i> value.
|
||||
|
||||
<b>!/</b><i>pattern</i><b>/</b><i>flags result</i>
|
||||
When <i>pattern</i> matches (does not match) a search
|
||||
string, use the corresponding <i>result</i> value.
|
||||
When <i>pattern</i> does <b>not</b> match the input string, use
|
||||
the corresponding <i>result</i> value.
|
||||
|
||||
<b>if /</b><i>pattern</i><b>/</b><i>flags</i>
|
||||
|
||||
<b>endif</b> Match the input string against the patterns between
|
||||
<b>if</b> and <b>endif</b>, if and only if the input string also
|
||||
matches <i>pattern</i>. The <b>if</b>..<b>endif</b> can nest.
|
||||
|
||||
Note: do not prepend whitespace to patterns inside
|
||||
<b>if</b>..<b>endif</b>.
|
||||
|
||||
<b>if !/</b><i>pattern</i><b>/</b><i>flags</i>
|
||||
|
||||
<b>endif</b> Match the input string against the patterns between
|
||||
<b>if</b> and <b>endif</b>, if and only if the input string does
|
||||
<b>not</b> match <i>pattern</i>. The <b>if</b>..<b>endif</b> 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.
|
||||
|
||||
<b>if /</b><i>pattern</i><b>/</b><i>flags</i>
|
||||
|
||||
<b>endif</b>
|
||||
|
||||
<b>if !/</b><i>pattern</i><b>/</b><i>flags</i>
|
||||
|
||||
<b>endif</b> Match the search string against the patterns
|
||||
between <b>if</b> and <b>endif</b>, if and only if the search
|
||||
string matches (does not match) <i>pattern</i>. The
|
||||
<b>if</b>..<b>endif</b> can nest.
|
||||
|
||||
Note: do not prepend whitespace to patterns inside
|
||||
<b>if</b>..<b>endif</b>.
|
||||
|
||||
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)
|
||||
|
||||
<b>SEARCH ORDER</b>
|
||||
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
|
||||
|
@@ -1,4 +1,4 @@
|
||||
<html> <head> </head> <body> <pre>
|
||||
<html> <body> <pre>
|
||||
PICKUP(8) PICKUP(8)
|
||||
|
||||
<b>NAME</b>
|
||||
@@ -44,32 +44,32 @@ PICKUP(8) PICKUP(8)
|
||||
command after a configuration change.
|
||||
|
||||
<b>Content inspection controls</b>
|
||||
<b>content</b><i>_</i><b>filter</b>
|
||||
<b>content_filter</b>
|
||||
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.
|
||||
|
||||
<b>receive</b><i>_</i><b>override</b><i>_</i><b>options</b>
|
||||
<b>receive_override_options</b>
|
||||
The following options override <b>main.cf</b> settings.
|
||||
The options are passed on to the downstream cleanup
|
||||
server.
|
||||
|
||||
<b>no</b><i>_</i><b>address</b><i>_</i><b>mappings</b>
|
||||
<b>no_address_mappings</b>
|
||||
Disable canonical address mapping, virtual
|
||||
alias map expansion, address masquerading,
|
||||
and automatic BCC recipients. Specify this
|
||||
if address mapping etc. are to be done <b>after</b>
|
||||
an external content filter.
|
||||
|
||||
<b>no</b><i>_</i><b>header</b><i>_</i><b>body</b><i>_</i><b>checks</b>
|
||||
<b>no_header_body_checks</b>
|
||||
Disable header/body_checks. Specify this if
|
||||
header/body_checks are to be done <b>after</b> an
|
||||
external content filter.
|
||||
|
||||
<b>Miscellaneous</b>
|
||||
<b>queue</b><i>_</i><b>directory</b>
|
||||
<b>queue_directory</b>
|
||||
Top-level directory of the Postfix queue.
|
||||
|
||||
<b>SEE ALSO</b>
|
||||
|
@@ -1,11 +1,11 @@
|
||||
<html> <head> </head> <body> <pre>
|
||||
<html> <body> <pre>
|
||||
POSTDROP(1) POSTDROP(1)
|
||||
|
||||
<b>NAME</b>
|
||||
postdrop - Postfix mail posting utility
|
||||
|
||||
<b>SYNOPSIS</b>
|
||||
<b>postdrop</b> [<b>-rv</b>] [<b>-c</b> <i>config_dir</i>]
|
||||
<b>postdrop</b> [<b>-rv</b>] [<b>-c</b> <i>config</i><b>_</b><i>dir</i>]
|
||||
|
||||
<b>DESCRIPTION</b>
|
||||
The <b>postdrop</b> command creates a file in the <b>maildrop</b> direc-
|
||||
@@ -48,7 +48,7 @@ POSTDROP(1) POSTDROP(1)
|
||||
|
||||
A non-standard directory is allowed only if the
|
||||
name is listed in the standard <b>main.cf</b> file, in the
|
||||
<b>alternate</b><i>_</i><b>config</b><i>_</i><b>directories</b> configuration parame-
|
||||
<b>alternate_config_directories</b> 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
|
||||
|
||||
<b>CONFIGURATION</b> <b>PARAMETERS</b>
|
||||
<b>CONFIGURATION PARAMETERS</b>
|
||||
See the Postfix <b>main.cf</b> file for syntax details and for
|
||||
default values. Use the <b>postfix</b> <b>reload</b> command after a
|
||||
default values. Use the <b>postfix reload</b> command after a
|
||||
configuration change.
|
||||
|
||||
<b>import</b><i>_</i><b>environment</b>
|
||||
<b>import_environment</b>
|
||||
List of names of environment parameters that can be
|
||||
imported from non-Postfix processes.
|
||||
|
||||
<b>queue</b><i>_</i><b>directory</b>
|
||||
<b>queue_directory</b>
|
||||
Top-level directory of the Postfix queue. This is
|
||||
also the root directory of Postfix daemons that run
|
||||
chrooted.
|
||||
|
||||
<b>SEE</b> <b>ALSO</b>
|
||||
<b>SEE ALSO</b>
|
||||
<a href="sendmail.1.html">sendmail(1)</a> compatibility interface
|
||||
syslogd(8) system logging
|
||||
|
||||
|
@@ -97,6 +97,12 @@ QMGR(8) QMGR(8)
|
||||
attempts by maintaining a short-term, in-memory
|
||||
list of unreachable destinations.
|
||||
|
||||
<b>preemptive message scheduling</b>
|
||||
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.
|
||||
|
||||
<b>TRIGGERS</b>
|
||||
On an idle system, the queue manager waits for the arrival
|
||||
of trigger events, or it waits for a timer to go off. A
|
||||
@@ -177,6 +183,9 @@ QMGR(8) QMGR(8)
|
||||
Top-level directory of the Postfix queue.
|
||||
|
||||
<b>Active queue controls</b>
|
||||
In the text below, <i>transport</i> is the first field in a <b>mas-</b>
|
||||
<b>ter.cf</b> entry.
|
||||
|
||||
<b>qmgr_clog_warn_time</b>
|
||||
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.
|
||||
|
||||
<b>qmgr_message_recipient_minimum</b>
|
||||
Per message minimum of in-memory recipients.
|
||||
|
||||
<b>default_recipient_limit</b>
|
||||
Default limit on the number of in-memory recipients
|
||||
per transport.
|
||||
|
||||
<i>transport</i><b>_recipient_limit</b>
|
||||
Limit on the number of in-memory recipients, for
|
||||
the named message <i>transport</i>.
|
||||
|
||||
<b>default_extra_recipient_limit</b>
|
||||
Default limit on the total number of per transport
|
||||
in-memory recipients that the preempting messages
|
||||
can have.
|
||||
|
||||
<i>transport</i><b>_extra_recipient_limit</b>
|
||||
Limit on the number of in-memory recipients which
|
||||
all preempting messages delivered by the transport
|
||||
<i>transport</i> can have.
|
||||
|
||||
<b>Timing controls</b>
|
||||
<b>minimal_backoff_time</b>
|
||||
Minimal time in seconds between delivery attempts
|
||||
@@ -221,25 +251,6 @@ QMGR(8) QMGR(8)
|
||||
ken delivery transport.
|
||||
|
||||
<b>Concurrency controls</b>
|
||||
In the text below, <i>transport</i> is the first field in a <b>mas-</b>
|
||||
<b>ter.cf</b> entry.
|
||||
|
||||
<b>qmgr_fudge_factor</b> (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.
|
||||
|
||||
<b>initial_destination_concurrency</b>
|
||||
Initial per-destination concurrency level for par-
|
||||
allel delivery to the same destination.
|
||||
@@ -262,6 +273,52 @@ QMGR(8) QMGR(8)
|
||||
Limit on the number of recipients per message
|
||||
transfer, for the named message <i>transport</i>.
|
||||
|
||||
<b>Message scheduling</b>
|
||||
<i>transport</i><b>_delivery_slot_cost</b> (valid range: 0,2,3...)
|
||||
This parameter basically controls how often a mes-
|
||||
sage delivered by <i>transport</i> can be preempted by
|
||||
another message. An internal per-message/transport
|
||||
counter is incremented by one for each <i>trans-</i>
|
||||
<i>port</i><b>_delivery_slot_cost</b> deliveries handled by
|
||||
<i>transport</i>. 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 <i>transport</i> agents than the value of the
|
||||
"available delivery slots" counter.
|
||||
|
||||
Value equal to 0 disables the message preemption
|
||||
for <i>transport</i>.
|
||||
|
||||
<i>transport</i><b>_minimum_delivery_slots</b>
|
||||
Message preemption is not attempted at all whenever
|
||||
a message that can't ever accumulate at least
|
||||
<i>transport</i><b>_minimum_delivery_slots</b> available delivery
|
||||
slots is being delivered by <i>transport</i>.
|
||||
|
||||
<i>transport</i><b>_delivery_slot_discount</b> (valid range: 0..100)
|
||||
|
||||
<i>transport</i><b>_delivery_slot_loan</b>
|
||||
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 <i>trans-</i>
|
||||
<i>port</i><b>_delivery_slot_discount</b> percent of the required
|
||||
amount plus <i>transport</i><b>_delivery_slot_loan</b> still
|
||||
remains to be accumulated. Note that the full
|
||||
amount will still have to be accumulated before
|
||||
another preemption can take place later.
|
||||
|
||||
<b>default_delivery_slot_cost</b>
|
||||
|
||||
<b>default_minimum_delivery_slots</b>
|
||||
|
||||
<b>default_delivery_slot_discount</b>
|
||||
|
||||
<b>default_delivery_slot_loan</b>
|
||||
Default values for the transport specific parame-
|
||||
ters described above.
|
||||
|
||||
<b>SEE ALSO</b>
|
||||
<a href="master.8.html">master(8)</a>, process manager
|
||||
syslogd(8) system logging
|
||||
@@ -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)
|
||||
</pre> </body> </html>
|
||||
|
@@ -25,10 +25,27 @@ REGEXP_TABLE(5) REGEXP_TABLE(5)
|
||||
The general form of a Postfix regular expression table is:
|
||||
|
||||
<b>/</b><i>pattern</i><b>/</b><i>flags result</i>
|
||||
When <i>pattern</i> matches the input string, use the cor-
|
||||
responding <i>result</i> value.
|
||||
|
||||
<b>!/</b><i>pattern</i><b>/</b><i>flags result</i>
|
||||
When <i>pattern</i> matches (does not match) a search
|
||||
string, use the corresponding <i>result</i> value.
|
||||
When <i>pattern</i> does <b>not</b> match the input string, use
|
||||
the corresponding <i>result</i> value.
|
||||
|
||||
<b>if /</b><i>pattern</i><b>/</b><i>flags</i>
|
||||
|
||||
<b>endif</b> Match the input string against the patterns between
|
||||
<b>if</b> and <b>endif</b>, if and only if that same input string
|
||||
also matches <i>pattern</i>. The <b>if</b>..<b>endif</b> can nest.
|
||||
|
||||
Note: do not prepend whitespace to patterns inside
|
||||
<b>if</b>..<b>endif</b>.
|
||||
|
||||
<b>if !/</b><i>pattern</i><b>/</b><i>flags</i>
|
||||
|
||||
<b>endif</b> Match the input string against the patterns between
|
||||
<b>if</b> and <b>endif</b>, if and only if that same input string
|
||||
does <b>not</b> match <i>pattern</i>. The <b>if</b>..<b>endif</b> 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.
|
||||
|
||||
<b>if /</b><i>pattern</i><b>/</b><i>flags</i>
|
||||
|
||||
<b>endif</b>
|
||||
|
||||
<b>if !/</b><i>pattern</i><b>/</b><i>flags</i>
|
||||
|
||||
<b>endif</b> Match the search string against the patterns
|
||||
between <b>if</b> and <b>endif</b>, if and only if the search
|
||||
string matches (does not match) <i>pattern</i>. The
|
||||
<b>if</b>..<b>endif</b> can nest.
|
||||
|
||||
Note: do not prepend whitespace to patterns inside
|
||||
<b>if</b>..<b>endif</b>.
|
||||
|
||||
Each pattern is a regular expression enclosed by a pair of
|
||||
delimiters. The regular expression syntax is described in
|
||||
<i>re</i><b>_</b><i>format</i>(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:
|
||||
|
||||
<b>i</b> (default: on)
|
||||
Toggles the case sensitivity flag. By default,
|
||||
matching is case insensitive.
|
||||
|
||||
<b>x</b> (default: on)
|
||||
Toggles the extended expression syntax flag. By
|
||||
default, support for extended expression syntax is
|
||||
enabled.
|
||||
|
||||
<b>m</b> (default: off)
|
||||
Toggle the multi-line mode flag. When this flag is
|
||||
on, the <b>^</b> and <b>$</b> metacharacters match immediately
|
||||
after and immediately before a newline character,
|
||||
respectively, in addition to matching at the start
|
||||
and end of the input string.
|
||||
|
||||
<b>TABLE SEARCH ORDER</b>
|
||||
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
|
||||
|
@@ -92,16 +92,6 @@ header rewriting code.
|
||||
|
||||
<p>
|
||||
|
||||
<dt> <b>extract_recipient_limit</b> (default: 10240 recipients)
|
||||
|
||||
<dd> 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".
|
||||
|
||||
</dl>
|
||||
|
||||
<p>
|
||||
|
||||
The following parameters restrict the use of file system storage:
|
||||
|
||||
<dl>
|
||||
|
@@ -138,6 +138,13 @@ SMTPD(8) SMTPD(8)
|
||||
explicitly selected at program build time and
|
||||
explicitly enabled at runtime.
|
||||
|
||||
<b>smtpd_sasl_application_name</b>
|
||||
The application name used for SASL server initial-
|
||||
ization. This controls the name of the SASL con-
|
||||
figuration file. The default value is <i>smtpd</i>, cor-
|
||||
responding to a SASL configuration file named
|
||||
<i>smtpd.conf</i>.
|
||||
|
||||
<b>smtpd_sasl_local_domain</b>
|
||||
The name of the local authentication realm.
|
||||
|
||||
|
@@ -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 $? >$@
|
||||
|
@@ -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
|
||||
|
@@ -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
|
||||
|
@@ -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
|
@@ -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
|
||||
|
@@ -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
|
||||
|
@@ -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;;
|
||||
|
@@ -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
|
||||
|
@@ -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
|
||||
|
@@ -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
|
||||
|
298
postfix/src/global/cfg_parser.c
Normal file
298
postfix/src/global/cfg_parser.c
Normal file
@@ -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 <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
/* 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 : "<NULL>"));
|
||||
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);
|
||||
}
|
49
postfix/src/global/cfg_parser.h
Normal file
49
postfix/src/global/cfg_parser.h
Normal file
@@ -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
|
@@ -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 <ldap.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <unistd.h>
|
||||
|
||||
/*
|
||||
* 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",
|
||||
server_host = cfg_get_str(dict_ldap->parser, "server_host",
|
||||
"localhost", 1, 0);
|
||||
if (msg_verbose)
|
||||
msg_info("%s: %s server_host is %s", myname, ldapsource,
|
||||
server_host);
|
||||
|
||||
/*
|
||||
* 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",
|
||||
dict_ldap->search_base = cfg_get_str(dict_ldap->parser, "search_base",
|
||||
"", 0, 0);
|
||||
if (msg_verbose)
|
||||
msg_info("%s: %s search_base is %s", myname, ldapsource,
|
||||
dict_ldap->search_base);
|
||||
|
||||
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",
|
||||
cfg_get_str(dict_ldap->parser, "query_filter",
|
||||
"(mailacceptinggeneralid=%s)", 0, 0);
|
||||
if (msg_verbose)
|
||||
msg_info("%s: %s query_filter is %s", myname, ldapsource,
|
||||
dict_ldap->query_filter);
|
||||
|
||||
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->size_limit = cfg_get_int(dict_ldap->parser, "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);
|
||||
|
||||
/*
|
||||
* 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",
|
||||
dict_ldap->tls_cert = cfg_get_str(dict_ldap->parser, "tls_cert",
|
||||
"", 0, 0);
|
||||
if (msg_verbose)
|
||||
msg_info("%s: %s tls_cert is %s", myname, ldapsource,
|
||||
dict_ldap->tls_cert);
|
||||
|
||||
/* get configured value of "tls_key"; default "" */
|
||||
dict_ldap->tls_key = parser.get_str(ldapsource, "tls_key",
|
||||
dict_ldap->tls_key = cfg_get_str(dict_ldap->parser, "tls_key",
|
||||
"", 0, 0);
|
||||
if (msg_verbose)
|
||||
msg_info("%s: %s tls_key is %s", myname, ldapsource,
|
||||
dict_ldap->tls_key);
|
||||
|
||||
/* get configured value of "tls_random_file"; default "" */
|
||||
dict_ldap->tls_random_file = parser.get_str(ldapsource,
|
||||
dict_ldap->tls_random_file = cfg_get_str(dict_ldap->parser,
|
||||
"tls_random_file", "", 0, 0);
|
||||
if (msg_verbose)
|
||||
msg_info("%s: %s tls_random_file is %s", myname, ldapsource,
|
||||
dict_ldap->tls_random_file);
|
||||
|
||||
/* 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
|
||||
|
||||
/*
|
@@ -2,9 +2,8 @@
|
||||
/* NAME
|
||||
/* dict_mysql 3
|
||||
/* SUMMARY
|
||||
/* dictionary manager interface to db files
|
||||
/* dictionary manager interface to MySQL databases
|
||||
/* SYNOPSIS
|
||||
/* #include <dict.h>
|
||||
/* #include <dict_mysql.h>
|
||||
/*
|
||||
/* DICT *dict_mysql_open(name, open_flags, dict_flags)
|
||||
@@ -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 '<some username>' 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 '<some username>'
|
||||
/* 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 <mysql.h>
|
||||
|
||||
/* 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 *);
|
||||
@@ -363,7 +411,6 @@ 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.
|
||||
@@ -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);
|
@@ -2,7 +2,7 @@
|
||||
/* NAME
|
||||
/* dict_pgsql 3
|
||||
/* SUMMARY
|
||||
/* dictionary manager interface to Postgresql files
|
||||
/* dictionary manager interface to PostgreSQL databases
|
||||
/* SYNOPSIS
|
||||
/* #include <dict_pgsql.h>
|
||||
/*
|
||||
@@ -24,12 +24,61 @@
|
||||
/* 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.
|
||||
/* 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.
|
||||
/*
|
||||
/* 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 '<some username>'
|
||||
@@ -37,21 +86,21 @@
|
||||
/* host1.some.domain and host2.some.domain, logging in as user
|
||||
/* "postfix" and password "passwd" then the configuration file
|
||||
/* should read:
|
||||
/*
|
||||
/* user = postfix
|
||||
/* password = passwd
|
||||
/* DBname = postfix_info
|
||||
/* table = aliases
|
||||
/* select_field = forw_addr
|
||||
/* where_field = alias
|
||||
/* hosts = host1.some.domain host2.some.domain
|
||||
/*
|
||||
/* .IP other_name
|
||||
/* reference for outside use.
|
||||
/* .IP open_flags
|
||||
/* Must be O_RDONLY.
|
||||
/* .IP dict_flags
|
||||
/* See dict_open(3).
|
||||
/* .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 <libpq-fe.h>
|
||||
|
||||
/* 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,
|
||||
@@ -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,12 +703,21 @@ 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);
|
||||
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]);
|
@@ -33,6 +33,9 @@
|
||||
/* Global library. */
|
||||
|
||||
#include <dict_proxy.h>
|
||||
#include <dict_ldap.h>
|
||||
#include <dict_mysql.h>
|
||||
#include <dict_pgsql.h>
|
||||
#include <mail_dict.h>
|
||||
|
||||
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,
|
||||
};
|
||||
|
||||
|
@@ -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,
|
||||
|
@@ -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
|
||||
|
@@ -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);
|
||||
|
@@ -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
|
||||
|
@@ -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
|
@@ -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
|
||||
@@ -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,
|
@@ -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; \
|
||||
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
|
||||
/*--*/
|
@@ -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. */
|
@@ -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;
|
@@ -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;
|
||||
}
|
@@ -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++;
|
||||
|
||||
/*
|
@@ -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 <argv.h>
|
||||
#include <stringops.h>
|
||||
#include <myflock.h>
|
||||
#include <sane_time.h>
|
||||
|
||||
/* 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,13 +392,10 @@ 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;
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (orig_rcpt != 0) {
|
||||
@@ -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;
|
||||
entry = qmgr_entry_create(queue, message);
|
||||
}
|
||||
|
||||
/*
|
||||
* 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)
|
||||
@@ -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);
|
||||
}
|
||||
}
|
@@ -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);
|
||||
}
|
@@ -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,
|
@@ -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);
|
||||
}
|
||||
|
||||
/*
|
||||
* 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
|
||||
|
@@ -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
|
||||
|
@@ -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
|
||||
@@ -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,
|
||||
|
@@ -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.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
|
||||
/*--*/
|
||||
|
@@ -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. */
|
||||
|
@@ -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;
|
||||
|
@@ -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;
|
||||
}
|
||||
|
@@ -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++;
|
||||
|
||||
/*
|
||||
|
@@ -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 <argv.h>
|
||||
#include <stringops.h>
|
||||
#include <myflock.h>
|
||||
#include <sane_time.h>
|
||||
|
||||
/* 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,10 +424,13 @@ 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;
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (orig_rcpt != 0) {
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
@@ -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);
|
||||
}
|
||||
|
@@ -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,
|
||||
|
@@ -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,
|
||||
|
@@ -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.
|
||||
* Accept all other addresses - including addresses that passed the above
|
||||
* tests because of some table lookup problem.
|
||||
*/
|
||||
default:
|
||||
break;
|
||||
}
|
||||
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));
|
||||
}
|
||||
|
@@ -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");
|
||||
|
||||
}
|
||||
|
@@ -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
|
||||
|
@@ -172,9 +172,6 @@
|
||||
#include <dict_nis.h>
|
||||
#include <dict_nisplus.h>
|
||||
#include <dict_ni.h>
|
||||
#include <dict_ldap.h>
|
||||
#include <dict_mysql.h>
|
||||
#include <dict_pgsql.h>
|
||||
#include <dict_pcre.h>
|
||||
#include <dict_regexp.h>
|
||||
#include <dict_static.h>
|
||||
@@ -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
|
||||
|
@@ -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) {
|
||||
|
@@ -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
|
||||
|
||||
/*
|
||||
|
@@ -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
|
||||
|
Reference in New Issue
Block a user